From 23458f66f0eac153dd97a5a933da4361ab618d34 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 4 May 2020 18:29:01 +0900 Subject: [PATCH 01/82] Get copy of SequenceReader of corefx. --- src/MsgPack.Abstraction/BlockReader`1.cs | 455 +++++++++++++++++++++++ 1 file changed, 455 insertions(+) create mode 100644 src/MsgPack.Abstraction/BlockReader`1.cs diff --git a/src/MsgPack.Abstraction/BlockReader`1.cs b/src/MsgPack.Abstraction/BlockReader`1.cs new file mode 100644 index 000000000..f7a1b8efd --- /dev/null +++ b/src/MsgPack.Abstraction/BlockReader`1.cs @@ -0,0 +1,455 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; +using Internal.Runtime.CompilerServices; + +namespace System.Buffers +{ + public ref partial struct SequenceReader where T : unmanaged, IEquatable + { + private SequencePosition _currentPosition; + private SequencePosition _nextPosition; + private bool _moreData; + private readonly long _length; + + /// + /// Create a over the given . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SequenceReader(ReadOnlySequence sequence) + { + CurrentSpanIndex = 0; + Consumed = 0; + Sequence = sequence; + _currentPosition = sequence.Start; + _length = -1; + + sequence.GetFirstSpan(out ReadOnlySpan first, out _nextPosition); + CurrentSpan = first; + _moreData = first.Length > 0; + + if (!_moreData && !sequence.IsSingleSegment) + { + _moreData = true; + GetNextSpan(); + } + } + + /// + /// True when there is no more data in the . + /// + public readonly bool End => !_moreData; + + /// + /// The underlying for the reader. + /// + public readonly ReadOnlySequence Sequence { get; } + + /// + /// Gets the unread portion of the . + /// + /// + /// The unread portion of the . + /// + public readonly ReadOnlySequence UnreadSequence => Sequence.Slice(Position); + + /// + /// The current position in the . + /// + public readonly SequencePosition Position + => Sequence.GetPosition(CurrentSpanIndex, _currentPosition); + + /// + /// The current segment in the as a span. + /// + public ReadOnlySpan CurrentSpan { readonly get; private set; } + + /// + /// The index in the . + /// + public int CurrentSpanIndex { readonly get; private set; } + + /// + /// The unread portion of the . + /// + public readonly ReadOnlySpan UnreadSpan + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => CurrentSpan.Slice(CurrentSpanIndex); + } + + /// + /// The total number of 's processed by the reader. + /// + public long Consumed { readonly get; private set; } + + /// + /// Remaining 's in the reader's . + /// + public readonly long Remaining => Length - Consumed; + + /// + /// Count of in the reader's . + /// + public readonly long Length + { + get + { + if (_length < 0) + { + // Cast-away readonly to initialize lazy field + Volatile.Write(ref Unsafe.AsRef(_length), Sequence.Length); + } + return _length; + } + } + + /// + /// Peeks at the next value without advancing the reader. + /// + /// The next value or default if at the end. + /// False if at the end of the reader. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool TryPeek(out T value) + { + if (_moreData) + { + value = CurrentSpan[CurrentSpanIndex]; + return true; + } + else + { + value = default; + return false; + } + } + + /// + /// Peeks at the next value at specific offset without advancing the reader. + /// + /// The offset from current position. + /// The next value, or the default value if at the end of the reader. + /// true if the reader is not at its end and the peek operation succeeded; false if at the end of the reader. + public readonly bool TryPeek(long offset, out T value) + { + if (offset < 0) + ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange(); + + // If we've got data and offset is not out of bounds + if (!_moreData || Remaining <= offset) + { + value = default; + return false; + } + + // Sum CurrentSpanIndex + offset could overflow as is but the value of offset should be very large + // because we check Remaining <= offset above so to overflow we should have a ReadOnlySequence close to 8 exabytes + Debug.Assert(CurrentSpanIndex + offset >= 0); + + // If offset doesn't fall inside current segment move to next until we find correct one + if ((CurrentSpanIndex + offset) <= CurrentSpan.Length - 1) + { + Debug.Assert(offset <= int.MaxValue); + + value = CurrentSpan[CurrentSpanIndex + (int)offset]; + return true; + } + else + { + long remainingOffset = offset - (CurrentSpan.Length - CurrentSpanIndex); + SequencePosition nextPosition = _nextPosition; + ReadOnlyMemory currentMemory = default; + + while (Sequence.TryGet(ref nextPosition, out currentMemory, advance: true)) + { + // Skip empty segment + if (currentMemory.Length > 0) + { + if (remainingOffset >= currentMemory.Length) + { + // Subtract current non consumed data + remainingOffset -= currentMemory.Length; + } + else + { + break; + } + } + } + + value = currentMemory.Span[(int)remainingOffset]; + return true; + } + } + + /// + /// Read the next value and advance the reader. + /// + /// The next value or default if at the end. + /// False if at the end of the reader. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryRead(out T value) + { + if (End) + { + value = default; + return false; + } + + value = CurrentSpan[CurrentSpanIndex]; + CurrentSpanIndex++; + Consumed++; + + if (CurrentSpanIndex >= CurrentSpan.Length) + { + GetNextSpan(); + } + + return true; + } + + /// + /// Move the reader back the specified number of items. + /// + /// + /// Thrown if trying to rewind a negative amount or more than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Rewind(long count) + { + if ((ulong)count > (ulong)Consumed) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); + } + + Consumed -= count; + + if (CurrentSpanIndex >= count) + { + CurrentSpanIndex -= (int)count; + _moreData = true; + } + else + { + // Current segment doesn't have enough data, scan backward through segments + RetreatToPreviousSpan(Consumed); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void RetreatToPreviousSpan(long consumed) + { + ResetReader(); + Advance(consumed); + } + + private void ResetReader() + { + CurrentSpanIndex = 0; + Consumed = 0; + _currentPosition = Sequence.Start; + _nextPosition = _currentPosition; + + if (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory memory, advance: true)) + { + _moreData = true; + + if (memory.Length == 0) + { + CurrentSpan = default; + // No data in the first span, move to one with data + GetNextSpan(); + } + else + { + CurrentSpan = memory.Span; + } + } + else + { + // No data in any spans and at end of sequence + _moreData = false; + CurrentSpan = default; + } + } + + /// + /// Get the next segment with available data, if any. + /// + private void GetNextSpan() + { + if (!Sequence.IsSingleSegment) + { + SequencePosition previousNextPosition = _nextPosition; + while (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory memory, advance: true)) + { + _currentPosition = previousNextPosition; + if (memory.Length > 0) + { + CurrentSpan = memory.Span; + CurrentSpanIndex = 0; + return; + } + else + { + CurrentSpan = default; + CurrentSpanIndex = 0; + previousNextPosition = _nextPosition; + } + } + } + _moreData = false; + } + + /// + /// Move the reader ahead the specified number of items. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Advance(long count) + { + const long TooBigOrNegative = unchecked((long)0xFFFFFFFF80000000); + if ((count & TooBigOrNegative) == 0 && CurrentSpan.Length - CurrentSpanIndex > (int)count) + { + CurrentSpanIndex += (int)count; + Consumed += count; + } + else + { + // Can't satisfy from the current span + AdvanceToNextSpan(count); + } + } + + /// + /// Unchecked helper to avoid unnecessary checks where you know count is valid. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AdvanceCurrentSpan(long count) + { + Debug.Assert(count >= 0); + + Consumed += count; + CurrentSpanIndex += (int)count; + if (CurrentSpanIndex >= CurrentSpan.Length) + GetNextSpan(); + } + + /// + /// Only call this helper if you know that you are advancing in the current span + /// with valid count and there is no need to fetch the next one. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AdvanceWithinSpan(long count) + { + Debug.Assert(count >= 0); + + Consumed += count; + CurrentSpanIndex += (int)count; + + Debug.Assert(CurrentSpanIndex < CurrentSpan.Length); + } + + private void AdvanceToNextSpan(long count) + { + if (count < 0) + { + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); + } + + Consumed += count; + while (_moreData) + { + int remaining = CurrentSpan.Length - CurrentSpanIndex; + + if (remaining > count) + { + CurrentSpanIndex += (int)count; + count = 0; + break; + } + + // As there may not be any further segments we need to + // push the current index to the end of the span. + CurrentSpanIndex += remaining; + count -= remaining; + Debug.Assert(count >= 0); + + GetNextSpan(); + + if (count == 0) + { + break; + } + } + + if (count != 0) + { + // Not enough data left- adjust for where we actually ended and throw + Consumed -= count; + ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); + } + } + + /// + /// Copies data from the current to the given span if there + /// is enough data to fill it. + /// + /// + /// This API is used to copy a fixed amount of data out of the sequence if possible. It does not advance + /// the reader. To look ahead for a specific stream of data can be used. + /// + /// Destination span to copy to. + /// True if there is enough data to completely fill the span. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool TryCopyTo(Span destination) + { + // This API doesn't advance to facilitate conditional advancement based on the data returned. + // We don't provide an advance option to allow easier utilizing of stack allocated destination spans. + // (Because we can make this method readonly we can guarantee that we won't capture the span.) + + ReadOnlySpan firstSpan = UnreadSpan; + if (firstSpan.Length >= destination.Length) + { + firstSpan.Slice(0, destination.Length).CopyTo(destination); + return true; + } + + // Not enough in the current span to satisfy the request, fall through to the slow path + return TryCopyMultisegment(destination); + } + + internal readonly bool TryCopyMultisegment(Span destination) + { + // If we don't have enough to fill the requested buffer, return false + if (Remaining < destination.Length) + return false; + + ReadOnlySpan firstSpan = UnreadSpan; + Debug.Assert(firstSpan.Length < destination.Length); + firstSpan.CopyTo(destination); + int copied = firstSpan.Length; + + SequencePosition next = _nextPosition; + while (Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) + { + if (nextSegment.Length > 0) + { + ReadOnlySpan nextSpan = nextSegment.Span; + int toCopy = Math.Min(nextSpan.Length, destination.Length - copied); + nextSpan.Slice(0, toCopy).CopyTo(destination.Slice(copied)); + copied += toCopy; + if (copied >= destination.Length) + { + break; + } + } + } + + return true; + } + } +} \ No newline at end of file From 8b5e1ee4a73a8075acdbdb3fd7fdb701465c617f Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 4 May 2020 19:00:07 +0900 Subject: [PATCH 02/82] Format code before import MsgPack C# customization. --- src/MsgPack.Abstraction/BlockReader`1.cs | 174 +++++++++++------------ 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/MsgPack.Abstraction/BlockReader`1.cs b/src/MsgPack.Abstraction/BlockReader`1.cs index f7a1b8efd..d46e95ec4 100644 --- a/src/MsgPack.Abstraction/BlockReader`1.cs +++ b/src/MsgPack.Abstraction/BlockReader`1.cs @@ -22,27 +22,27 @@ namespace System.Buffers [MethodImpl(MethodImplOptions.AggressiveInlining)] public SequenceReader(ReadOnlySequence sequence) { - CurrentSpanIndex = 0; - Consumed = 0; - Sequence = sequence; - _currentPosition = sequence.Start; - _length = -1; + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this.Sequence = sequence; + this._currentPosition = sequence.Start; + this._length = -1; - sequence.GetFirstSpan(out ReadOnlySpan first, out _nextPosition); - CurrentSpan = first; - _moreData = first.Length > 0; + sequence.GetFirstSpan(out ReadOnlySpan first, out this._nextPosition); + this.CurrentSpan = first; + this._moreData = first.Length > 0; - if (!_moreData && !sequence.IsSingleSegment) + if (!this._moreData && !sequence.IsSingleSegment) { - _moreData = true; - GetNextSpan(); + this._moreData = true; + this.GetNextSpan(); } } /// /// True when there is no more data in the . /// - public readonly bool End => !_moreData; + public readonly bool End => !this._moreData; /// /// The underlying for the reader. @@ -55,13 +55,13 @@ public SequenceReader(ReadOnlySequence sequence) /// /// The unread portion of the . /// - public readonly ReadOnlySequence UnreadSequence => Sequence.Slice(Position); + public readonly ReadOnlySequence UnreadSequence => this.Sequence.Slice(this.Position); /// /// The current position in the . /// public readonly SequencePosition Position - => Sequence.GetPosition(CurrentSpanIndex, _currentPosition); + => this.Sequence.GetPosition(this.CurrentSpanIndex, this._currentPosition); /// /// The current segment in the as a span. @@ -79,7 +79,7 @@ public readonly SequencePosition Position public readonly ReadOnlySpan UnreadSpan { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => CurrentSpan.Slice(CurrentSpanIndex); + get => this.CurrentSpan.Slice(this.CurrentSpanIndex); } /// @@ -90,7 +90,7 @@ public readonly ReadOnlySpan UnreadSpan /// /// Remaining 's in the reader's . /// - public readonly long Remaining => Length - Consumed; + public readonly long Remaining => this.Length - this.Consumed; /// /// Count of in the reader's . @@ -99,12 +99,12 @@ public readonly long Length { get { - if (_length < 0) + if (this._length < 0) { // Cast-away readonly to initialize lazy field - Volatile.Write(ref Unsafe.AsRef(_length), Sequence.Length); + Volatile.Write(ref Unsafe.AsRef(this._length), this.Sequence.Length); } - return _length; + return this._length; } } @@ -116,9 +116,9 @@ public readonly long Length [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly bool TryPeek(out T value) { - if (_moreData) + if (this._moreData) { - value = CurrentSpan[CurrentSpanIndex]; + value = this.CurrentSpan[this.CurrentSpanIndex]; return true; } else @@ -140,7 +140,7 @@ public readonly bool TryPeek(long offset, out T value) ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange(); // If we've got data and offset is not out of bounds - if (!_moreData || Remaining <= offset) + if (!this._moreData || this.Remaining <= offset) { value = default; return false; @@ -148,23 +148,23 @@ public readonly bool TryPeek(long offset, out T value) // Sum CurrentSpanIndex + offset could overflow as is but the value of offset should be very large // because we check Remaining <= offset above so to overflow we should have a ReadOnlySequence close to 8 exabytes - Debug.Assert(CurrentSpanIndex + offset >= 0); + Debug.Assert(this.CurrentSpanIndex + offset >= 0); // If offset doesn't fall inside current segment move to next until we find correct one - if ((CurrentSpanIndex + offset) <= CurrentSpan.Length - 1) + if ((this.CurrentSpanIndex + offset) <= this.CurrentSpan.Length - 1) { Debug.Assert(offset <= int.MaxValue); - value = CurrentSpan[CurrentSpanIndex + (int)offset]; + value = this.CurrentSpan[this.CurrentSpanIndex + (int)offset]; return true; } else { - long remainingOffset = offset - (CurrentSpan.Length - CurrentSpanIndex); - SequencePosition nextPosition = _nextPosition; + long remainingOffset = offset - (this.CurrentSpan.Length - this.CurrentSpanIndex); + SequencePosition nextPosition = this._nextPosition; ReadOnlyMemory currentMemory = default; - while (Sequence.TryGet(ref nextPosition, out currentMemory, advance: true)) + while (this.Sequence.TryGet(ref nextPosition, out currentMemory, advance: true)) { // Skip empty segment if (currentMemory.Length > 0) @@ -194,19 +194,19 @@ public readonly bool TryPeek(long offset, out T value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryRead(out T value) { - if (End) + if (this.End) { value = default; return false; } - value = CurrentSpan[CurrentSpanIndex]; - CurrentSpanIndex++; - Consumed++; + value = this.CurrentSpan[this.CurrentSpanIndex]; + this.CurrentSpanIndex++; + this.Consumed++; - if (CurrentSpanIndex >= CurrentSpan.Length) + if (this.CurrentSpanIndex >= this.CurrentSpan.Length) { - GetNextSpan(); + this.GetNextSpan(); } return true; @@ -221,59 +221,59 @@ public bool TryRead(out T value) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Rewind(long count) { - if ((ulong)count > (ulong)Consumed) + if ((ulong)count > (ulong)this.Consumed) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); } - Consumed -= count; + this.Consumed -= count; - if (CurrentSpanIndex >= count) + if (this.CurrentSpanIndex >= count) { - CurrentSpanIndex -= (int)count; - _moreData = true; + this.CurrentSpanIndex -= (int)count; + this._moreData = true; } else { // Current segment doesn't have enough data, scan backward through segments - RetreatToPreviousSpan(Consumed); + this.RetreatToPreviousSpan(Consumed); } } [MethodImpl(MethodImplOptions.NoInlining)] private void RetreatToPreviousSpan(long consumed) { - ResetReader(); - Advance(consumed); + this.ResetReader(); + this.Advance(consumed); } private void ResetReader() { - CurrentSpanIndex = 0; - Consumed = 0; - _currentPosition = Sequence.Start; - _nextPosition = _currentPosition; + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this._currentPosition = this.Sequence.Start; + this._nextPosition = this._currentPosition; - if (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory memory, advance: true)) + if (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) { - _moreData = true; + this._moreData = true; if (memory.Length == 0) { - CurrentSpan = default; + this.CurrentSpan = default; // No data in the first span, move to one with data - GetNextSpan(); + this.GetNextSpan(); } else { - CurrentSpan = memory.Span; + this.CurrentSpan = memory.Span; } } else { // No data in any spans and at end of sequence - _moreData = false; - CurrentSpan = default; + this._moreData = false; + this.CurrentSpan = default; } } @@ -282,27 +282,27 @@ private void ResetReader() /// private void GetNextSpan() { - if (!Sequence.IsSingleSegment) + if (!this.Sequence.IsSingleSegment) { - SequencePosition previousNextPosition = _nextPosition; - while (Sequence.TryGet(ref _nextPosition, out ReadOnlyMemory memory, advance: true)) + SequencePosition previousNextPosition = this._nextPosition; + while (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) { - _currentPosition = previousNextPosition; + this._currentPosition = previousNextPosition; if (memory.Length > 0) { - CurrentSpan = memory.Span; - CurrentSpanIndex = 0; + this.CurrentSpan = memory.Span; + this.CurrentSpanIndex = 0; return; } else { - CurrentSpan = default; - CurrentSpanIndex = 0; - previousNextPosition = _nextPosition; + this.CurrentSpan = default; + this.CurrentSpanIndex = 0; + previousNextPosition = this._nextPosition; } } } - _moreData = false; + this._moreData = false; } /// @@ -312,15 +312,15 @@ private void GetNextSpan() public void Advance(long count) { const long TooBigOrNegative = unchecked((long)0xFFFFFFFF80000000); - if ((count & TooBigOrNegative) == 0 && CurrentSpan.Length - CurrentSpanIndex > (int)count) + if ((count & TooBigOrNegative) == 0 && this.CurrentSpan.Length - this.CurrentSpanIndex > (int)count) { - CurrentSpanIndex += (int)count; - Consumed += count; + this.CurrentSpanIndex += (int)count; + this.Consumed += count; } else { // Can't satisfy from the current span - AdvanceToNextSpan(count); + this.AdvanceToNextSpan(count); } } @@ -332,10 +332,10 @@ internal void AdvanceCurrentSpan(long count) { Debug.Assert(count >= 0); - Consumed += count; - CurrentSpanIndex += (int)count; - if (CurrentSpanIndex >= CurrentSpan.Length) - GetNextSpan(); + this.Consumed += count; + this.CurrentSpanIndex += (int)count; + if (this.CurrentSpanIndex >= this.CurrentSpan.Length) + this.GetNextSpan(); } /// @@ -347,10 +347,10 @@ internal void AdvanceWithinSpan(long count) { Debug.Assert(count >= 0); - Consumed += count; - CurrentSpanIndex += (int)count; + this.Consumed += count; + this.CurrentSpanIndex += (int)count; - Debug.Assert(CurrentSpanIndex < CurrentSpan.Length); + Debug.Assert(this.CurrentSpanIndex < this.CurrentSpan.Length); } private void AdvanceToNextSpan(long count) @@ -360,25 +360,25 @@ private void AdvanceToNextSpan(long count) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); } - Consumed += count; - while (_moreData) + this.Consumed += count; + while (this._moreData) { - int remaining = CurrentSpan.Length - CurrentSpanIndex; + int remaining = this.CurrentSpan.Length - this.CurrentSpanIndex; if (remaining > count) { - CurrentSpanIndex += (int)count; + this.CurrentSpanIndex += (int)count; count = 0; break; } // As there may not be any further segments we need to // push the current index to the end of the span. - CurrentSpanIndex += remaining; + this.CurrentSpanIndex += remaining; count -= remaining; Debug.Assert(count >= 0); - GetNextSpan(); + this.GetNextSpan(); if (count == 0) { @@ -389,7 +389,7 @@ private void AdvanceToNextSpan(long count) if (count != 0) { // Not enough data left- adjust for where we actually ended and throw - Consumed -= count; + this.Consumed -= count; ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); } } @@ -411,7 +411,7 @@ public readonly bool TryCopyTo(Span destination) // We don't provide an advance option to allow easier utilizing of stack allocated destination spans. // (Because we can make this method readonly we can guarantee that we won't capture the span.) - ReadOnlySpan firstSpan = UnreadSpan; + ReadOnlySpan firstSpan = this.UnreadSpan; if (firstSpan.Length >= destination.Length) { firstSpan.Slice(0, destination.Length).CopyTo(destination); @@ -419,22 +419,22 @@ public readonly bool TryCopyTo(Span destination) } // Not enough in the current span to satisfy the request, fall through to the slow path - return TryCopyMultisegment(destination); + return this.TryCopyMultisegment(destination); } internal readonly bool TryCopyMultisegment(Span destination) { // If we don't have enough to fill the requested buffer, return false - if (Remaining < destination.Length) + if (this.Remaining < destination.Length) return false; - ReadOnlySpan firstSpan = UnreadSpan; + ReadOnlySpan firstSpan = this.UnreadSpan; Debug.Assert(firstSpan.Length < destination.Length); firstSpan.CopyTo(destination); int copied = firstSpan.Length; - SequencePosition next = _nextPosition; - while (Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) + SequencePosition next = this._nextPosition; + while (this.Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) { if (nextSegment.Length > 0) { From b3b8dfb23eadd8449b20803a856c39607ec30a9c Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 4 May 2020 19:01:55 +0900 Subject: [PATCH 03/82] Make SequenceReader accepting ReadOnlyMemory This code is borrowed from MessagePack C# --- src/MsgPack.Abstraction/BlockReader`1.cs | 348 +++++++++++++---------- 1 file changed, 194 insertions(+), 154 deletions(-) diff --git a/src/MsgPack.Abstraction/BlockReader`1.cs b/src/MsgPack.Abstraction/BlockReader`1.cs index d46e95ec4..7dde28695 100644 --- a/src/MsgPack.Abstraction/BlockReader`1.cs +++ b/src/MsgPack.Abstraction/BlockReader`1.cs @@ -1,110 +1,175 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +/* Licensed to the .NET Foundation under one or more agreements. + * The .NET Foundation licenses this file to you under the MIT license. + * See the LICENSE file in the project root for more information. */ using System.Diagnostics; using System.Runtime.CompilerServices; -using System.Threading; -using Internal.Runtime.CompilerServices; namespace System.Buffers { - public ref partial struct SequenceReader where T : unmanaged, IEquatable + internal ref partial struct SequenceReader + where T : unmanaged, IEquatable { - private SequencePosition _currentPosition; - private SequencePosition _nextPosition; - private bool _moreData; - private readonly long _length; + /// + /// A value indicating whether we're using (as opposed to . + /// + private bool usingSequence; /// - /// Create a over the given . + /// Backing for the entire sequence when we're not using . + /// + private ReadOnlySequence sequence; + + /// + /// The position at the start of the . + /// + private SequencePosition currentPosition; + + /// + /// The position at the end of the . + /// + private SequencePosition nextPosition; + + /// + /// Backing for the entire sequence when we're not using . + /// + private ReadOnlyMemory memory; + + /// + /// A value indicating whether there is unread data remaining. + /// + private bool moreData; + + /// + /// The total number of elements in the sequence. + /// + private long length; + + /// + /// Initializes a new instance of the struct + /// over the given . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SequenceReader(ReadOnlySequence sequence) + public SequenceReader(in ReadOnlySequence sequence) { + this.usingSequence = true; this.CurrentSpanIndex = 0; this.Consumed = 0; - this.Sequence = sequence; - this._currentPosition = sequence.Start; - this._length = -1; + this.sequence = sequence; + this.memory = default; + this.currentPosition = sequence.Start; + this.length = -1; - sequence.GetFirstSpan(out ReadOnlySpan first, out this._nextPosition); + ReadOnlySpan first = sequence.First.Span; + this.nextPosition = sequence.GetPosition(first.Length); this.CurrentSpan = first; - this._moreData = first.Length > 0; + this.moreData = first.Length > 0; - if (!this._moreData && !sequence.IsSingleSegment) + if (!this.moreData && !sequence.IsSingleSegment) { - this._moreData = true; + this.moreData = true; this.GetNextSpan(); } } /// - /// True when there is no more data in the . + /// Initializes a new instance of the struct + /// over the given . /// - public readonly bool End => !this._moreData; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SequenceReader(ReadOnlyMemory memory) + { + this.usingSequence = false; + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this.memory = memory; + this.CurrentSpan = memory.Span; + this.length = memory.Length; + this.moreData = memory.Length > 0; + + this.currentPosition = default; + this.nextPosition = default; + this.sequence = default; + } /// - /// The underlying for the reader. + /// Gets a value indicating whether there is no more data in the . /// - public readonly ReadOnlySequence Sequence { get; } + public bool End => !this.moreData; /// - /// Gets the unread portion of the . + /// Gets the underlying for the reader. /// - /// - /// The unread portion of the . - /// - public readonly ReadOnlySequence UnreadSequence => this.Sequence.Slice(this.Position); + public ReadOnlySequence Sequence + { + get + { + if (this.sequence.IsEmpty && !this.memory.IsEmpty) + { + // We're in memory mode (instead of sequence mode). + // Lazily fill in the sequence data. + this.sequence = new ReadOnlySequence(this.memory); + this.currentPosition = this.sequence.Start; + this.nextPosition = this.sequence.End; + } + + return this.sequence; + } + } /// - /// The current position in the . + /// Gets the current position in the . /// - public readonly SequencePosition Position - => this.Sequence.GetPosition(this.CurrentSpanIndex, this._currentPosition); + public SequencePosition Position + => this.Sequence.GetPosition(this.CurrentSpanIndex, this.currentPosition); /// - /// The current segment in the as a span. + /// Gets the current segment in the as a span. /// - public ReadOnlySpan CurrentSpan { readonly get; private set; } + public ReadOnlySpan CurrentSpan { get; private set; } /// - /// The index in the . + /// Gets the index in the . /// - public int CurrentSpanIndex { readonly get; private set; } + public int CurrentSpanIndex { get; private set; } /// - /// The unread portion of the . + /// Gets the unread portion of the . /// - public readonly ReadOnlySpan UnreadSpan + public ReadOnlySpan UnreadSpan { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => this.CurrentSpan.Slice(this.CurrentSpanIndex); } /// - /// The total number of 's processed by the reader. + /// Gets the total number of 's processed by the reader. /// - public long Consumed { readonly get; private set; } + public long Consumed { get; private set; } /// - /// Remaining 's in the reader's . + /// Gets remaining 's in the reader's . /// - public readonly long Remaining => this.Length - this.Consumed; + public long Remaining => this.Length - this.Consumed; /// - /// Count of in the reader's . + /// Gets count of in the reader's . /// - public readonly long Length + public long Length { + [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if (this._length < 0) + if (this.length < 0) { - // Cast-away readonly to initialize lazy field - Volatile.Write(ref Unsafe.AsRef(this._length), this.Sequence.Length); + // Cache the length + this.length = this.Sequence.Length; } - return this._length; + + return this.length; } } @@ -114,9 +179,9 @@ public readonly long Length /// The next value or default if at the end. /// False if at the end of the reader. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool TryPeek(out T value) + public bool TryPeek(out T value) { - if (this._moreData) + if (this.moreData) { value = this.CurrentSpan[this.CurrentSpanIndex]; return true; @@ -128,64 +193,6 @@ public readonly bool TryPeek(out T value) } } - /// - /// Peeks at the next value at specific offset without advancing the reader. - /// - /// The offset from current position. - /// The next value, or the default value if at the end of the reader. - /// true if the reader is not at its end and the peek operation succeeded; false if at the end of the reader. - public readonly bool TryPeek(long offset, out T value) - { - if (offset < 0) - ThrowHelper.ThrowArgumentOutOfRangeException_OffsetOutOfRange(); - - // If we've got data and offset is not out of bounds - if (!this._moreData || this.Remaining <= offset) - { - value = default; - return false; - } - - // Sum CurrentSpanIndex + offset could overflow as is but the value of offset should be very large - // because we check Remaining <= offset above so to overflow we should have a ReadOnlySequence close to 8 exabytes - Debug.Assert(this.CurrentSpanIndex + offset >= 0); - - // If offset doesn't fall inside current segment move to next until we find correct one - if ((this.CurrentSpanIndex + offset) <= this.CurrentSpan.Length - 1) - { - Debug.Assert(offset <= int.MaxValue); - - value = this.CurrentSpan[this.CurrentSpanIndex + (int)offset]; - return true; - } - else - { - long remainingOffset = offset - (this.CurrentSpan.Length - this.CurrentSpanIndex); - SequencePosition nextPosition = this._nextPosition; - ReadOnlyMemory currentMemory = default; - - while (this.Sequence.TryGet(ref nextPosition, out currentMemory, advance: true)) - { - // Skip empty segment - if (currentMemory.Length > 0) - { - if (remainingOffset >= currentMemory.Length) - { - // Subtract current non consumed data - remainingOffset -= currentMemory.Length; - } - else - { - break; - } - } - } - - value = currentMemory.Span[(int)remainingOffset]; - return true; - } - } - /// /// Read the next value and advance the reader. /// @@ -206,7 +213,14 @@ public bool TryRead(out T value) if (this.CurrentSpanIndex >= this.CurrentSpan.Length) { - this.GetNextSpan(); + if (this.usingSequence) + { + this.GetNextSpan(); + } + else + { + this.moreData = false; + } } return true; @@ -215,15 +229,12 @@ public bool TryRead(out T value) /// /// Move the reader back the specified number of items. /// - /// - /// Thrown if trying to rewind a negative amount or more than . - /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Rewind(long count) { - if ((ulong)count > (ulong)this.Consumed) + if (count < 0) { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); + throw new ArgumentOutOfRangeException(nameof(count)); } this.Consumed -= count; @@ -231,36 +242,43 @@ public void Rewind(long count) if (this.CurrentSpanIndex >= count) { this.CurrentSpanIndex -= (int)count; - this._moreData = true; + this.moreData = true; } - else + else if (this.usingSequence) { // Current segment doesn't have enough data, scan backward through segments - this.RetreatToPreviousSpan(Consumed); + this.RetreatToPreviousSpan(this.Consumed); + } + else + { + throw new ArgumentOutOfRangeException("Rewind went past the start of the memory."); } } [MethodImpl(MethodImplOptions.NoInlining)] private void RetreatToPreviousSpan(long consumed) { + Debug.Assert(this.usingSequence, "usingSequence"); this.ResetReader(); this.Advance(consumed); } private void ResetReader() { + Debug.Assert(this.usingSequence, "usingSequence"); this.CurrentSpanIndex = 0; this.Consumed = 0; - this._currentPosition = this.Sequence.Start; - this._nextPosition = this._currentPosition; + this.currentPosition = this.Sequence.Start; + this.nextPosition = this.currentPosition; - if (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) + if (this.Sequence.TryGet(ref this.nextPosition, out ReadOnlyMemory memory, advance: true)) { - this._moreData = true; + this.moreData = true; if (memory.Length == 0) { this.CurrentSpan = default; + // No data in the first span, move to one with data this.GetNextSpan(); } @@ -272,7 +290,7 @@ private void ResetReader() else { // No data in any spans and at end of sequence - this._moreData = false; + this.moreData = false; this.CurrentSpan = default; } } @@ -282,12 +300,13 @@ private void ResetReader() /// private void GetNextSpan() { + Debug.Assert(this.usingSequence, "usingSequence"); if (!this.Sequence.IsSingleSegment) { - SequencePosition previousNextPosition = this._nextPosition; - while (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) + SequencePosition previousNextPosition = this.nextPosition; + while (this.Sequence.TryGet(ref this.nextPosition, out ReadOnlyMemory memory, advance: true)) { - this._currentPosition = previousNextPosition; + this.currentPosition = previousNextPosition; if (memory.Length > 0) { this.CurrentSpan = memory.Span; @@ -298,11 +317,12 @@ private void GetNextSpan() { this.CurrentSpan = default; this.CurrentSpanIndex = 0; - previousNextPosition = this._nextPosition; + previousNextPosition = this.nextPosition; } } } - this._moreData = false; + + this.moreData = false; } /// @@ -317,11 +337,21 @@ public void Advance(long count) this.CurrentSpanIndex += (int)count; this.Consumed += count; } - else + else if (this.usingSequence) { // Can't satisfy from the current span this.AdvanceToNextSpan(count); } + else if (this.CurrentSpan.Length - this.CurrentSpanIndex == (int)count) + { + this.CurrentSpanIndex += (int)count; + this.Consumed += count; + this.moreData = false; + } + else + { + throw new ArgumentOutOfRangeException(nameof(count)); + } } /// @@ -330,12 +360,14 @@ public void Advance(long count) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void AdvanceCurrentSpan(long count) { - Debug.Assert(count >= 0); + Debug.Assert(count >= 0, "count >= 0"); this.Consumed += count; this.CurrentSpanIndex += (int)count; - if (this.CurrentSpanIndex >= this.CurrentSpan.Length) + if (this.usingSequence && this.CurrentSpanIndex >= this.CurrentSpan.Length) + { this.GetNextSpan(); + } } /// @@ -345,23 +377,40 @@ internal void AdvanceCurrentSpan(long count) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void AdvanceWithinSpan(long count) { - Debug.Assert(count >= 0); + Debug.Assert(count >= 0, "count >= 0"); this.Consumed += count; this.CurrentSpanIndex += (int)count; - Debug.Assert(this.CurrentSpanIndex < this.CurrentSpan.Length); + Debug.Assert(this.CurrentSpanIndex < this.CurrentSpan.Length, "this.CurrentSpanIndex < this.CurrentSpan.Length"); + } + + /// + /// Move the reader ahead the specified number of items + /// if there are enough elements remaining in the sequence. + /// + /// true if there were enough elements to advance; otherwise false. + internal bool TryAdvance(long count) + { + if (this.Remaining < count) + { + return false; + } + + this.Advance(count); + return true; } private void AdvanceToNextSpan(long count) { + Debug.Assert(this.usingSequence, "usingSequence"); if (count < 0) { - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); + throw new ArgumentOutOfRangeException(nameof(count)); } this.Consumed += count; - while (this._moreData) + while (this.moreData) { int remaining = this.CurrentSpan.Length - this.CurrentSpanIndex; @@ -376,7 +425,7 @@ private void AdvanceToNextSpan(long count) // push the current index to the end of the span. this.CurrentSpanIndex += remaining; count -= remaining; - Debug.Assert(count >= 0); + Debug.Assert(count >= 0, "count >= 0"); this.GetNextSpan(); @@ -390,27 +439,18 @@ private void AdvanceToNextSpan(long count) { // Not enough data left- adjust for where we actually ended and throw this.Consumed -= count; - ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count); + throw new ArgumentOutOfRangeException(nameof(count)); } } /// - /// Copies data from the current to the given span if there - /// is enough data to fill it. + /// Copies data from the current to the given span. /// - /// - /// This API is used to copy a fixed amount of data out of the sequence if possible. It does not advance - /// the reader. To look ahead for a specific stream of data can be used. - /// - /// Destination span to copy to. - /// True if there is enough data to completely fill the span. + /// Destination to copy to. + /// True if there is enough data to copy to the . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public readonly bool TryCopyTo(Span destination) + public bool TryCopyTo(Span destination) { - // This API doesn't advance to facilitate conditional advancement based on the data returned. - // We don't provide an advance option to allow easier utilizing of stack allocated destination spans. - // (Because we can make this method readonly we can guarantee that we won't capture the span.) - ReadOnlySpan firstSpan = this.UnreadSpan; if (firstSpan.Length >= destination.Length) { @@ -418,22 +458,22 @@ public readonly bool TryCopyTo(Span destination) return true; } - // Not enough in the current span to satisfy the request, fall through to the slow path return this.TryCopyMultisegment(destination); } - internal readonly bool TryCopyMultisegment(Span destination) + internal bool TryCopyMultisegment(Span destination) { - // If we don't have enough to fill the requested buffer, return false if (this.Remaining < destination.Length) + { return false; + } ReadOnlySpan firstSpan = this.UnreadSpan; - Debug.Assert(firstSpan.Length < destination.Length); + Debug.Assert(firstSpan.Length < destination.Length, "firstSpan.Length < destination.Length"); firstSpan.CopyTo(destination); int copied = firstSpan.Length; - SequencePosition next = this._nextPosition; + SequencePosition next = this.nextPosition; while (this.Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) { if (nextSegment.Length > 0) @@ -452,4 +492,4 @@ internal readonly bool TryCopyMultisegment(Span destination) return true; } } -} \ No newline at end of file +} From 808cb93668862948dcc4775448cb15af45f40c51 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 4 May 2020 19:07:18 +0900 Subject: [PATCH 04/82] Fix code format before more customization. --- src/MsgPack.Abstraction/BlockReader`1.cs | 122 +++++++++++------------ 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/src/MsgPack.Abstraction/BlockReader`1.cs b/src/MsgPack.Abstraction/BlockReader`1.cs index 7dde28695..4ecd9ccfe 100644 --- a/src/MsgPack.Abstraction/BlockReader`1.cs +++ b/src/MsgPack.Abstraction/BlockReader`1.cs @@ -14,39 +14,39 @@ internal ref partial struct SequenceReader where T : unmanaged, IEquatable { /// - /// A value indicating whether we're using (as opposed to . + /// A value indicating whether we're using (as opposed to . /// - private bool usingSequence; + private readonly bool _usingSequence; /// - /// Backing for the entire sequence when we're not using . + /// Backing for the entire sequence when we're not using . /// - private ReadOnlySequence sequence; + private ReadOnlySequence _sequence; /// /// The position at the start of the . /// - private SequencePosition currentPosition; + private SequencePosition _currentPosition; /// /// The position at the end of the . /// - private SequencePosition nextPosition; + private SequencePosition _nextPosition; /// - /// Backing for the entire sequence when we're not using . + /// Backing for the entire sequence when we're not using . /// - private ReadOnlyMemory memory; + private readonly ReadOnlyMemory _memory; /// /// A value indicating whether there is unread data remaining. /// - private bool moreData; + private bool _moreData; /// /// The total number of elements in the sequence. /// - private long length; + private long _length; /// /// Initializes a new instance of the struct @@ -55,22 +55,22 @@ internal ref partial struct SequenceReader [MethodImpl(MethodImplOptions.AggressiveInlining)] public SequenceReader(in ReadOnlySequence sequence) { - this.usingSequence = true; + this._usingSequence = true; this.CurrentSpanIndex = 0; this.Consumed = 0; - this.sequence = sequence; - this.memory = default; - this.currentPosition = sequence.Start; - this.length = -1; + this._sequence = sequence; + this._memory = default; + this._currentPosition = sequence.Start; + this._length = -1; ReadOnlySpan first = sequence.First.Span; - this.nextPosition = sequence.GetPosition(first.Length); + this._nextPosition = sequence.GetPosition(first.Length); this.CurrentSpan = first; - this.moreData = first.Length > 0; + this._moreData = first.Length > 0; - if (!this.moreData && !sequence.IsSingleSegment) + if (!this._moreData && !sequence.IsSingleSegment) { - this.moreData = true; + this._moreData = true; this.GetNextSpan(); } } @@ -82,23 +82,23 @@ public SequenceReader(in ReadOnlySequence sequence) [MethodImpl(MethodImplOptions.AggressiveInlining)] public SequenceReader(ReadOnlyMemory memory) { - this.usingSequence = false; + this._usingSequence = false; this.CurrentSpanIndex = 0; this.Consumed = 0; - this.memory = memory; + this._memory = memory; this.CurrentSpan = memory.Span; - this.length = memory.Length; - this.moreData = memory.Length > 0; + this._length = memory.Length; + this._moreData = memory.Length > 0; - this.currentPosition = default; - this.nextPosition = default; - this.sequence = default; + this._currentPosition = default; + this._nextPosition = default; + this._sequence = default; } /// /// Gets a value indicating whether there is no more data in the . /// - public bool End => !this.moreData; + public bool End => !this._moreData; /// /// Gets the underlying for the reader. @@ -107,16 +107,16 @@ public ReadOnlySequence Sequence { get { - if (this.sequence.IsEmpty && !this.memory.IsEmpty) + if (this._sequence.IsEmpty && !this._memory.IsEmpty) { // We're in memory mode (instead of sequence mode). // Lazily fill in the sequence data. - this.sequence = new ReadOnlySequence(this.memory); - this.currentPosition = this.sequence.Start; - this.nextPosition = this.sequence.End; + this._sequence = new ReadOnlySequence(this._memory); + this._currentPosition = this._sequence.Start; + this._nextPosition = this._sequence.End; } - return this.sequence; + return this._sequence; } } @@ -124,7 +124,7 @@ public ReadOnlySequence Sequence /// Gets the current position in the . /// public SequencePosition Position - => this.Sequence.GetPosition(this.CurrentSpanIndex, this.currentPosition); + => this.Sequence.GetPosition(this.CurrentSpanIndex, this._currentPosition); /// /// Gets the current segment in the as a span. @@ -163,13 +163,13 @@ public long Length [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - if (this.length < 0) + if (this._length < 0) { // Cache the length - this.length = this.Sequence.Length; + this._length = this.Sequence.Length; } - return this.length; + return this._length; } } @@ -181,7 +181,7 @@ public long Length [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryPeek(out T value) { - if (this.moreData) + if (this._moreData) { value = this.CurrentSpan[this.CurrentSpanIndex]; return true; @@ -213,13 +213,13 @@ public bool TryRead(out T value) if (this.CurrentSpanIndex >= this.CurrentSpan.Length) { - if (this.usingSequence) + if (this._usingSequence) { this.GetNextSpan(); } else { - this.moreData = false; + this._moreData = false; } } @@ -242,9 +242,9 @@ public void Rewind(long count) if (this.CurrentSpanIndex >= count) { this.CurrentSpanIndex -= (int)count; - this.moreData = true; + this._moreData = true; } - else if (this.usingSequence) + else if (this._usingSequence) { // Current segment doesn't have enough data, scan backward through segments this.RetreatToPreviousSpan(this.Consumed); @@ -258,22 +258,22 @@ public void Rewind(long count) [MethodImpl(MethodImplOptions.NoInlining)] private void RetreatToPreviousSpan(long consumed) { - Debug.Assert(this.usingSequence, "usingSequence"); + Debug.Assert(this._usingSequence, "usingSequence"); this.ResetReader(); this.Advance(consumed); } private void ResetReader() { - Debug.Assert(this.usingSequence, "usingSequence"); + Debug.Assert(this._usingSequence, "usingSequence"); this.CurrentSpanIndex = 0; this.Consumed = 0; - this.currentPosition = this.Sequence.Start; - this.nextPosition = this.currentPosition; + this._currentPosition = this.Sequence.Start; + this._nextPosition = this._currentPosition; - if (this.Sequence.TryGet(ref this.nextPosition, out ReadOnlyMemory memory, advance: true)) + if (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) { - this.moreData = true; + this._moreData = true; if (memory.Length == 0) { @@ -290,7 +290,7 @@ private void ResetReader() else { // No data in any spans and at end of sequence - this.moreData = false; + this._moreData = false; this.CurrentSpan = default; } } @@ -300,13 +300,13 @@ private void ResetReader() /// private void GetNextSpan() { - Debug.Assert(this.usingSequence, "usingSequence"); + Debug.Assert(this._usingSequence, "usingSequence"); if (!this.Sequence.IsSingleSegment) { - SequencePosition previousNextPosition = this.nextPosition; - while (this.Sequence.TryGet(ref this.nextPosition, out ReadOnlyMemory memory, advance: true)) + SequencePosition previousNextPosition = this._nextPosition; + while (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) { - this.currentPosition = previousNextPosition; + this._currentPosition = previousNextPosition; if (memory.Length > 0) { this.CurrentSpan = memory.Span; @@ -317,12 +317,12 @@ private void GetNextSpan() { this.CurrentSpan = default; this.CurrentSpanIndex = 0; - previousNextPosition = this.nextPosition; + previousNextPosition = this._nextPosition; } } } - this.moreData = false; + this._moreData = false; } /// @@ -337,7 +337,7 @@ public void Advance(long count) this.CurrentSpanIndex += (int)count; this.Consumed += count; } - else if (this.usingSequence) + else if (this._usingSequence) { // Can't satisfy from the current span this.AdvanceToNextSpan(count); @@ -346,7 +346,7 @@ public void Advance(long count) { this.CurrentSpanIndex += (int)count; this.Consumed += count; - this.moreData = false; + this._moreData = false; } else { @@ -364,7 +364,7 @@ internal void AdvanceCurrentSpan(long count) this.Consumed += count; this.CurrentSpanIndex += (int)count; - if (this.usingSequence && this.CurrentSpanIndex >= this.CurrentSpan.Length) + if (this._usingSequence && this.CurrentSpanIndex >= this.CurrentSpan.Length) { this.GetNextSpan(); } @@ -403,14 +403,14 @@ internal bool TryAdvance(long count) private void AdvanceToNextSpan(long count) { - Debug.Assert(this.usingSequence, "usingSequence"); + Debug.Assert(this._usingSequence, "usingSequence"); if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } this.Consumed += count; - while (this.moreData) + while (this._moreData) { int remaining = this.CurrentSpan.Length - this.CurrentSpanIndex; @@ -473,7 +473,7 @@ internal bool TryCopyMultisegment(Span destination) firstSpan.CopyTo(destination); int copied = firstSpan.Length; - SequencePosition next = this.nextPosition; + SequencePosition next = this._nextPosition; while (this.Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) { if (nextSegment.Length > 0) @@ -492,4 +492,4 @@ internal bool TryCopyMultisegment(Span destination) return true; } } -} +} \ No newline at end of file From fd8b126d2a77db97d27ac97d9122487abd451e1b Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 4 May 2020 19:08:46 +0900 Subject: [PATCH 05/82] Clarify licensing terms --- src/MsgPack.Abstraction/BlockReader`1.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/MsgPack.Abstraction/BlockReader`1.cs b/src/MsgPack.Abstraction/BlockReader`1.cs index 4ecd9ccfe..8ec92b2f1 100644 --- a/src/MsgPack.Abstraction/BlockReader`1.cs +++ b/src/MsgPack.Abstraction/BlockReader`1.cs @@ -1,6 +1,12 @@ -// Copyright (c) All contributors. All rights reserved. +// This file is originated from MessagePack C# and .NET Core Libraries + +// License term of MessagePack C#: + +// Copyright (c) All contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +// License term of .NET Core Libraries: + /* Licensed to the .NET Foundation under one or more agreements. * The .NET Foundation licenses this file to you under the MIT license. * See the LICENSE file in the project root for more information. */ From 4b0bf12db42e81612e5de6a27fdb9433ccf9ef0d Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 4 May 2020 19:17:00 +0900 Subject: [PATCH 06/82] Code formatting --- src/MsgPack.Abstraction/BlockReader`1.cs | 966 +++++++++++------------ 1 file changed, 483 insertions(+), 483 deletions(-) diff --git a/src/MsgPack.Abstraction/BlockReader`1.cs b/src/MsgPack.Abstraction/BlockReader`1.cs index 8ec92b2f1..be0b27f21 100644 --- a/src/MsgPack.Abstraction/BlockReader`1.cs +++ b/src/MsgPack.Abstraction/BlockReader`1.cs @@ -16,486 +16,486 @@ namespace System.Buffers { - internal ref partial struct SequenceReader - where T : unmanaged, IEquatable - { - /// - /// A value indicating whether we're using (as opposed to . - /// - private readonly bool _usingSequence; - - /// - /// Backing for the entire sequence when we're not using . - /// - private ReadOnlySequence _sequence; - - /// - /// The position at the start of the . - /// - private SequencePosition _currentPosition; - - /// - /// The position at the end of the . - /// - private SequencePosition _nextPosition; - - /// - /// Backing for the entire sequence when we're not using . - /// - private readonly ReadOnlyMemory _memory; - - /// - /// A value indicating whether there is unread data remaining. - /// - private bool _moreData; - - /// - /// The total number of elements in the sequence. - /// - private long _length; - - /// - /// Initializes a new instance of the struct - /// over the given . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SequenceReader(in ReadOnlySequence sequence) - { - this._usingSequence = true; - this.CurrentSpanIndex = 0; - this.Consumed = 0; - this._sequence = sequence; - this._memory = default; - this._currentPosition = sequence.Start; - this._length = -1; - - ReadOnlySpan first = sequence.First.Span; - this._nextPosition = sequence.GetPosition(first.Length); - this.CurrentSpan = first; - this._moreData = first.Length > 0; - - if (!this._moreData && !sequence.IsSingleSegment) - { - this._moreData = true; - this.GetNextSpan(); - } - } - - /// - /// Initializes a new instance of the struct - /// over the given . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SequenceReader(ReadOnlyMemory memory) - { - this._usingSequence = false; - this.CurrentSpanIndex = 0; - this.Consumed = 0; - this._memory = memory; - this.CurrentSpan = memory.Span; - this._length = memory.Length; - this._moreData = memory.Length > 0; - - this._currentPosition = default; - this._nextPosition = default; - this._sequence = default; - } - - /// - /// Gets a value indicating whether there is no more data in the . - /// - public bool End => !this._moreData; - - /// - /// Gets the underlying for the reader. - /// - public ReadOnlySequence Sequence - { - get - { - if (this._sequence.IsEmpty && !this._memory.IsEmpty) - { - // We're in memory mode (instead of sequence mode). - // Lazily fill in the sequence data. - this._sequence = new ReadOnlySequence(this._memory); - this._currentPosition = this._sequence.Start; - this._nextPosition = this._sequence.End; - } - - return this._sequence; - } - } - - /// - /// Gets the current position in the . - /// - public SequencePosition Position - => this.Sequence.GetPosition(this.CurrentSpanIndex, this._currentPosition); - - /// - /// Gets the current segment in the as a span. - /// - public ReadOnlySpan CurrentSpan { get; private set; } - - /// - /// Gets the index in the . - /// - public int CurrentSpanIndex { get; private set; } - - /// - /// Gets the unread portion of the . - /// - public ReadOnlySpan UnreadSpan - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.CurrentSpan.Slice(this.CurrentSpanIndex); - } - - /// - /// Gets the total number of 's processed by the reader. - /// - public long Consumed { get; private set; } - - /// - /// Gets remaining 's in the reader's . - /// - public long Remaining => this.Length - this.Consumed; - - /// - /// Gets count of in the reader's . - /// - public long Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (this._length < 0) - { - // Cache the length - this._length = this.Sequence.Length; - } - - return this._length; - } - } - - /// - /// Peeks at the next value without advancing the reader. - /// - /// The next value or default if at the end. - /// False if at the end of the reader. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryPeek(out T value) - { - if (this._moreData) - { - value = this.CurrentSpan[this.CurrentSpanIndex]; - return true; - } - else - { - value = default; - return false; - } - } - - /// - /// Read the next value and advance the reader. - /// - /// The next value or default if at the end. - /// False if at the end of the reader. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryRead(out T value) - { - if (this.End) - { - value = default; - return false; - } - - value = this.CurrentSpan[this.CurrentSpanIndex]; - this.CurrentSpanIndex++; - this.Consumed++; - - if (this.CurrentSpanIndex >= this.CurrentSpan.Length) - { - if (this._usingSequence) - { - this.GetNextSpan(); - } - else - { - this._moreData = false; - } - } - - return true; - } - - /// - /// Move the reader back the specified number of items. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Rewind(long count) - { - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - this.Consumed -= count; - - if (this.CurrentSpanIndex >= count) - { - this.CurrentSpanIndex -= (int)count; - this._moreData = true; - } - else if (this._usingSequence) - { - // Current segment doesn't have enough data, scan backward through segments - this.RetreatToPreviousSpan(this.Consumed); - } - else - { - throw new ArgumentOutOfRangeException("Rewind went past the start of the memory."); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void RetreatToPreviousSpan(long consumed) - { - Debug.Assert(this._usingSequence, "usingSequence"); - this.ResetReader(); - this.Advance(consumed); - } - - private void ResetReader() - { - Debug.Assert(this._usingSequence, "usingSequence"); - this.CurrentSpanIndex = 0; - this.Consumed = 0; - this._currentPosition = this.Sequence.Start; - this._nextPosition = this._currentPosition; - - if (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) - { - this._moreData = true; - - if (memory.Length == 0) - { - this.CurrentSpan = default; - - // No data in the first span, move to one with data - this.GetNextSpan(); - } - else - { - this.CurrentSpan = memory.Span; - } - } - else - { - // No data in any spans and at end of sequence - this._moreData = false; - this.CurrentSpan = default; - } - } - - /// - /// Get the next segment with available data, if any. - /// - private void GetNextSpan() - { - Debug.Assert(this._usingSequence, "usingSequence"); - if (!this.Sequence.IsSingleSegment) - { - SequencePosition previousNextPosition = this._nextPosition; - while (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) - { - this._currentPosition = previousNextPosition; - if (memory.Length > 0) - { - this.CurrentSpan = memory.Span; - this.CurrentSpanIndex = 0; - return; - } - else - { - this.CurrentSpan = default; - this.CurrentSpanIndex = 0; - previousNextPosition = this._nextPosition; - } - } - } - - this._moreData = false; - } - - /// - /// Move the reader ahead the specified number of items. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Advance(long count) - { - const long TooBigOrNegative = unchecked((long)0xFFFFFFFF80000000); - if ((count & TooBigOrNegative) == 0 && this.CurrentSpan.Length - this.CurrentSpanIndex > (int)count) - { - this.CurrentSpanIndex += (int)count; - this.Consumed += count; - } - else if (this._usingSequence) - { - // Can't satisfy from the current span - this.AdvanceToNextSpan(count); - } - else if (this.CurrentSpan.Length - this.CurrentSpanIndex == (int)count) - { - this.CurrentSpanIndex += (int)count; - this.Consumed += count; - this._moreData = false; - } - else - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - } - - /// - /// Unchecked helper to avoid unnecessary checks where you know count is valid. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AdvanceCurrentSpan(long count) - { - Debug.Assert(count >= 0, "count >= 0"); - - this.Consumed += count; - this.CurrentSpanIndex += (int)count; - if (this._usingSequence && this.CurrentSpanIndex >= this.CurrentSpan.Length) - { - this.GetNextSpan(); - } - } - - /// - /// Only call this helper if you know that you are advancing in the current span - /// with valid count and there is no need to fetch the next one. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AdvanceWithinSpan(long count) - { - Debug.Assert(count >= 0, "count >= 0"); - - this.Consumed += count; - this.CurrentSpanIndex += (int)count; - - Debug.Assert(this.CurrentSpanIndex < this.CurrentSpan.Length, "this.CurrentSpanIndex < this.CurrentSpan.Length"); - } - - /// - /// Move the reader ahead the specified number of items - /// if there are enough elements remaining in the sequence. - /// - /// true if there were enough elements to advance; otherwise false. - internal bool TryAdvance(long count) - { - if (this.Remaining < count) - { - return false; - } - - this.Advance(count); - return true; - } - - private void AdvanceToNextSpan(long count) - { - Debug.Assert(this._usingSequence, "usingSequence"); - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - this.Consumed += count; - while (this._moreData) - { - int remaining = this.CurrentSpan.Length - this.CurrentSpanIndex; - - if (remaining > count) - { - this.CurrentSpanIndex += (int)count; - count = 0; - break; - } - - // As there may not be any further segments we need to - // push the current index to the end of the span. - this.CurrentSpanIndex += remaining; - count -= remaining; - Debug.Assert(count >= 0, "count >= 0"); - - this.GetNextSpan(); - - if (count == 0) - { - break; - } - } - - if (count != 0) - { - // Not enough data left- adjust for where we actually ended and throw - this.Consumed -= count; - throw new ArgumentOutOfRangeException(nameof(count)); - } - } - - /// - /// Copies data from the current to the given span. - /// - /// Destination to copy to. - /// True if there is enough data to copy to the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryCopyTo(Span destination) - { - ReadOnlySpan firstSpan = this.UnreadSpan; - if (firstSpan.Length >= destination.Length) - { - firstSpan.Slice(0, destination.Length).CopyTo(destination); - return true; - } - - return this.TryCopyMultisegment(destination); - } - - internal bool TryCopyMultisegment(Span destination) - { - if (this.Remaining < destination.Length) - { - return false; - } - - ReadOnlySpan firstSpan = this.UnreadSpan; - Debug.Assert(firstSpan.Length < destination.Length, "firstSpan.Length < destination.Length"); - firstSpan.CopyTo(destination); - int copied = firstSpan.Length; - - SequencePosition next = this._nextPosition; - while (this.Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) - { - if (nextSegment.Length > 0) - { - ReadOnlySpan nextSpan = nextSegment.Span; - int toCopy = Math.Min(nextSpan.Length, destination.Length - copied); - nextSpan.Slice(0, toCopy).CopyTo(destination.Slice(copied)); - copied += toCopy; - if (copied >= destination.Length) - { - break; - } - } - } - - return true; - } - } -} \ No newline at end of file + internal ref partial struct SequenceReader + where T : unmanaged, IEquatable + { + /// + /// A value indicating whether we're using (as opposed to . + /// + private readonly bool _usingSequence; + + /// + /// Backing for the entire sequence when we're not using . + /// + private ReadOnlySequence _sequence; + + /// + /// The position at the start of the . + /// + private SequencePosition _currentPosition; + + /// + /// The position at the end of the . + /// + private SequencePosition _nextPosition; + + /// + /// Backing for the entire sequence when we're not using . + /// + private readonly ReadOnlyMemory _memory; + + /// + /// A value indicating whether there is unread data remaining. + /// + private bool _moreData; + + /// + /// The total number of elements in the sequence. + /// + private long _length; + + /// + /// Initializes a new instance of the struct + /// over the given . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SequenceReader(in ReadOnlySequence sequence) + { + this._usingSequence = true; + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this._sequence = sequence; + this._memory = default; + this._currentPosition = sequence.Start; + this._length = -1; + + ReadOnlySpan first = sequence.First.Span; + this._nextPosition = sequence.GetPosition(first.Length); + this.CurrentSpan = first; + this._moreData = first.Length > 0; + + if (!this._moreData && !sequence.IsSingleSegment) + { + this._moreData = true; + this.GetNextSpan(); + } + } + + /// + /// Initializes a new instance of the struct + /// over the given . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public SequenceReader(ReadOnlyMemory memory) + { + this._usingSequence = false; + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this._memory = memory; + this.CurrentSpan = memory.Span; + this._length = memory.Length; + this._moreData = memory.Length > 0; + + this._currentPosition = default; + this._nextPosition = default; + this._sequence = default; + } + + /// + /// Gets a value indicating whether there is no more data in the . + /// + public bool End => !this._moreData; + + /// + /// Gets the underlying for the reader. + /// + public ReadOnlySequence Sequence + { + get + { + if (this._sequence.IsEmpty && !this._memory.IsEmpty) + { + // We're in memory mode (instead of sequence mode). + // Lazily fill in the sequence data. + this._sequence = new ReadOnlySequence(this._memory); + this._currentPosition = this._sequence.Start; + this._nextPosition = this._sequence.End; + } + + return this._sequence; + } + } + + /// + /// Gets the current position in the . + /// + public SequencePosition Position + => this.Sequence.GetPosition(this.CurrentSpanIndex, this._currentPosition); + + /// + /// Gets the current segment in the as a span. + /// + public ReadOnlySpan CurrentSpan { get; private set; } + + /// + /// Gets the index in the . + /// + public int CurrentSpanIndex { get; private set; } + + /// + /// Gets the unread portion of the . + /// + public ReadOnlySpan UnreadSpan + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.CurrentSpan.Slice(this.CurrentSpanIndex); + } + + /// + /// Gets the total number of 's processed by the reader. + /// + public long Consumed { get; private set; } + + /// + /// Gets remaining 's in the reader's . + /// + public long Remaining => this.Length - this.Consumed; + + /// + /// Gets count of in the reader's . + /// + public long Length + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (this._length < 0) + { + // Cache the length + this._length = this.Sequence.Length; + } + + return this._length; + } + } + + /// + /// Peeks at the next value without advancing the reader. + /// + /// The next value or default if at the end. + /// False if at the end of the reader. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryPeek(out T value) + { + if (this._moreData) + { + value = this.CurrentSpan[this.CurrentSpanIndex]; + return true; + } + else + { + value = default; + return false; + } + } + + /// + /// Read the next value and advance the reader. + /// + /// The next value or default if at the end. + /// False if at the end of the reader. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryRead(out T value) + { + if (this.End) + { + value = default; + return false; + } + + value = this.CurrentSpan[this.CurrentSpanIndex]; + this.CurrentSpanIndex++; + this.Consumed++; + + if (this.CurrentSpanIndex >= this.CurrentSpan.Length) + { + if (this._usingSequence) + { + this.GetNextSpan(); + } + else + { + this._moreData = false; + } + } + + return true; + } + + /// + /// Move the reader back the specified number of items. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Rewind(long count) + { + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + this.Consumed -= count; + + if (this.CurrentSpanIndex >= count) + { + this.CurrentSpanIndex -= (int)count; + this._moreData = true; + } + else if (this._usingSequence) + { + // Current segment doesn't have enough data, scan backward through segments + this.RetreatToPreviousSpan(this.Consumed); + } + else + { + throw new ArgumentOutOfRangeException("Rewind went past the start of the memory."); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void RetreatToPreviousSpan(long consumed) + { + Debug.Assert(this._usingSequence, "usingSequence"); + this.ResetReader(); + this.Advance(consumed); + } + + private void ResetReader() + { + Debug.Assert(this._usingSequence, "usingSequence"); + this.CurrentSpanIndex = 0; + this.Consumed = 0; + this._currentPosition = this.Sequence.Start; + this._nextPosition = this._currentPosition; + + if (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) + { + this._moreData = true; + + if (memory.Length == 0) + { + this.CurrentSpan = default; + + // No data in the first span, move to one with data + this.GetNextSpan(); + } + else + { + this.CurrentSpan = memory.Span; + } + } + else + { + // No data in any spans and at end of sequence + this._moreData = false; + this.CurrentSpan = default; + } + } + + /// + /// Get the next segment with available data, if any. + /// + private void GetNextSpan() + { + Debug.Assert(this._usingSequence, "usingSequence"); + if (!this.Sequence.IsSingleSegment) + { + SequencePosition previousNextPosition = this._nextPosition; + while (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) + { + this._currentPosition = previousNextPosition; + if (memory.Length > 0) + { + this.CurrentSpan = memory.Span; + this.CurrentSpanIndex = 0; + return; + } + else + { + this.CurrentSpan = default; + this.CurrentSpanIndex = 0; + previousNextPosition = this._nextPosition; + } + } + } + + this._moreData = false; + } + + /// + /// Move the reader ahead the specified number of items. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Advance(long count) + { + const long TooBigOrNegative = unchecked((long)0xFFFFFFFF80000000); + if ((count & TooBigOrNegative) == 0 && this.CurrentSpan.Length - this.CurrentSpanIndex > (int)count) + { + this.CurrentSpanIndex += (int)count; + this.Consumed += count; + } + else if (this._usingSequence) + { + // Can't satisfy from the current span + this.AdvanceToNextSpan(count); + } + else if (this.CurrentSpan.Length - this.CurrentSpanIndex == (int)count) + { + this.CurrentSpanIndex += (int)count; + this.Consumed += count; + this._moreData = false; + } + else + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + } + + /// + /// Unchecked helper to avoid unnecessary checks where you know count is valid. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AdvanceCurrentSpan(long count) + { + Debug.Assert(count >= 0, "count >= 0"); + + this.Consumed += count; + this.CurrentSpanIndex += (int)count; + if (this._usingSequence && this.CurrentSpanIndex >= this.CurrentSpan.Length) + { + this.GetNextSpan(); + } + } + + /// + /// Only call this helper if you know that you are advancing in the current span + /// with valid count and there is no need to fetch the next one. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void AdvanceWithinSpan(long count) + { + Debug.Assert(count >= 0, "count >= 0"); + + this.Consumed += count; + this.CurrentSpanIndex += (int)count; + + Debug.Assert(this.CurrentSpanIndex < this.CurrentSpan.Length, "this.CurrentSpanIndex < this.CurrentSpan.Length"); + } + + /// + /// Move the reader ahead the specified number of items + /// if there are enough elements remaining in the sequence. + /// + /// true if there were enough elements to advance; otherwise false. + internal bool TryAdvance(long count) + { + if (this.Remaining < count) + { + return false; + } + + this.Advance(count); + return true; + } + + private void AdvanceToNextSpan(long count) + { + Debug.Assert(this._usingSequence, "usingSequence"); + if (count < 0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + this.Consumed += count; + while (this._moreData) + { + int remaining = this.CurrentSpan.Length - this.CurrentSpanIndex; + + if (remaining > count) + { + this.CurrentSpanIndex += (int)count; + count = 0; + break; + } + + // As there may not be any further segments we need to + // push the current index to the end of the span. + this.CurrentSpanIndex += remaining; + count -= remaining; + Debug.Assert(count >= 0, "count >= 0"); + + this.GetNextSpan(); + + if (count == 0) + { + break; + } + } + + if (count != 0) + { + // Not enough data left- adjust for where we actually ended and throw + this.Consumed -= count; + throw new ArgumentOutOfRangeException(nameof(count)); + } + } + + /// + /// Copies data from the current to the given span. + /// + /// Destination to copy to. + /// True if there is enough data to copy to the . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryCopyTo(Span destination) + { + ReadOnlySpan firstSpan = this.UnreadSpan; + if (firstSpan.Length >= destination.Length) + { + firstSpan.Slice(0, destination.Length).CopyTo(destination); + return true; + } + + return this.TryCopyMultisegment(destination); + } + + internal bool TryCopyMultisegment(Span destination) + { + if (this.Remaining < destination.Length) + { + return false; + } + + ReadOnlySpan firstSpan = this.UnreadSpan; + Debug.Assert(firstSpan.Length < destination.Length, "firstSpan.Length < destination.Length"); + firstSpan.CopyTo(destination); + int copied = firstSpan.Length; + + SequencePosition next = this._nextPosition; + while (this.Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) + { + if (nextSegment.Length > 0) + { + ReadOnlySpan nextSpan = nextSegment.Span; + int toCopy = Math.Min(nextSpan.Length, destination.Length - copied); + nextSpan.Slice(0, toCopy).CopyTo(destination.Slice(copied)); + copied += toCopy; + if (copied >= destination.Length) + { + break; + } + } + } + + return true; + } + } +} From f59ad28f0a5d5fffcc0d198729a4c6d9b2e9a9b3 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Wed, 6 May 2020 16:16:18 +0900 Subject: [PATCH 07/82] Cancel BlockReader --- src/MsgPack.Abstraction/BlockReader`1.cs | 501 ----------------------- 1 file changed, 501 deletions(-) delete mode 100644 src/MsgPack.Abstraction/BlockReader`1.cs diff --git a/src/MsgPack.Abstraction/BlockReader`1.cs b/src/MsgPack.Abstraction/BlockReader`1.cs deleted file mode 100644 index be0b27f21..000000000 --- a/src/MsgPack.Abstraction/BlockReader`1.cs +++ /dev/null @@ -1,501 +0,0 @@ -// This file is originated from MessagePack C# and .NET Core Libraries - -// License term of MessagePack C#: - -// Copyright (c) All contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -// License term of .NET Core Libraries: - -/* Licensed to the .NET Foundation under one or more agreements. - * The .NET Foundation licenses this file to you under the MIT license. - * See the LICENSE file in the project root for more information. */ - -using System.Diagnostics; -using System.Runtime.CompilerServices; - -namespace System.Buffers -{ - internal ref partial struct SequenceReader - where T : unmanaged, IEquatable - { - /// - /// A value indicating whether we're using (as opposed to . - /// - private readonly bool _usingSequence; - - /// - /// Backing for the entire sequence when we're not using . - /// - private ReadOnlySequence _sequence; - - /// - /// The position at the start of the . - /// - private SequencePosition _currentPosition; - - /// - /// The position at the end of the . - /// - private SequencePosition _nextPosition; - - /// - /// Backing for the entire sequence when we're not using . - /// - private readonly ReadOnlyMemory _memory; - - /// - /// A value indicating whether there is unread data remaining. - /// - private bool _moreData; - - /// - /// The total number of elements in the sequence. - /// - private long _length; - - /// - /// Initializes a new instance of the struct - /// over the given . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SequenceReader(in ReadOnlySequence sequence) - { - this._usingSequence = true; - this.CurrentSpanIndex = 0; - this.Consumed = 0; - this._sequence = sequence; - this._memory = default; - this._currentPosition = sequence.Start; - this._length = -1; - - ReadOnlySpan first = sequence.First.Span; - this._nextPosition = sequence.GetPosition(first.Length); - this.CurrentSpan = first; - this._moreData = first.Length > 0; - - if (!this._moreData && !sequence.IsSingleSegment) - { - this._moreData = true; - this.GetNextSpan(); - } - } - - /// - /// Initializes a new instance of the struct - /// over the given . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SequenceReader(ReadOnlyMemory memory) - { - this._usingSequence = false; - this.CurrentSpanIndex = 0; - this.Consumed = 0; - this._memory = memory; - this.CurrentSpan = memory.Span; - this._length = memory.Length; - this._moreData = memory.Length > 0; - - this._currentPosition = default; - this._nextPosition = default; - this._sequence = default; - } - - /// - /// Gets a value indicating whether there is no more data in the . - /// - public bool End => !this._moreData; - - /// - /// Gets the underlying for the reader. - /// - public ReadOnlySequence Sequence - { - get - { - if (this._sequence.IsEmpty && !this._memory.IsEmpty) - { - // We're in memory mode (instead of sequence mode). - // Lazily fill in the sequence data. - this._sequence = new ReadOnlySequence(this._memory); - this._currentPosition = this._sequence.Start; - this._nextPosition = this._sequence.End; - } - - return this._sequence; - } - } - - /// - /// Gets the current position in the . - /// - public SequencePosition Position - => this.Sequence.GetPosition(this.CurrentSpanIndex, this._currentPosition); - - /// - /// Gets the current segment in the as a span. - /// - public ReadOnlySpan CurrentSpan { get; private set; } - - /// - /// Gets the index in the . - /// - public int CurrentSpanIndex { get; private set; } - - /// - /// Gets the unread portion of the . - /// - public ReadOnlySpan UnreadSpan - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => this.CurrentSpan.Slice(this.CurrentSpanIndex); - } - - /// - /// Gets the total number of 's processed by the reader. - /// - public long Consumed { get; private set; } - - /// - /// Gets remaining 's in the reader's . - /// - public long Remaining => this.Length - this.Consumed; - - /// - /// Gets count of in the reader's . - /// - public long Length - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if (this._length < 0) - { - // Cache the length - this._length = this.Sequence.Length; - } - - return this._length; - } - } - - /// - /// Peeks at the next value without advancing the reader. - /// - /// The next value or default if at the end. - /// False if at the end of the reader. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryPeek(out T value) - { - if (this._moreData) - { - value = this.CurrentSpan[this.CurrentSpanIndex]; - return true; - } - else - { - value = default; - return false; - } - } - - /// - /// Read the next value and advance the reader. - /// - /// The next value or default if at the end. - /// False if at the end of the reader. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryRead(out T value) - { - if (this.End) - { - value = default; - return false; - } - - value = this.CurrentSpan[this.CurrentSpanIndex]; - this.CurrentSpanIndex++; - this.Consumed++; - - if (this.CurrentSpanIndex >= this.CurrentSpan.Length) - { - if (this._usingSequence) - { - this.GetNextSpan(); - } - else - { - this._moreData = false; - } - } - - return true; - } - - /// - /// Move the reader back the specified number of items. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Rewind(long count) - { - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - this.Consumed -= count; - - if (this.CurrentSpanIndex >= count) - { - this.CurrentSpanIndex -= (int)count; - this._moreData = true; - } - else if (this._usingSequence) - { - // Current segment doesn't have enough data, scan backward through segments - this.RetreatToPreviousSpan(this.Consumed); - } - else - { - throw new ArgumentOutOfRangeException("Rewind went past the start of the memory."); - } - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private void RetreatToPreviousSpan(long consumed) - { - Debug.Assert(this._usingSequence, "usingSequence"); - this.ResetReader(); - this.Advance(consumed); - } - - private void ResetReader() - { - Debug.Assert(this._usingSequence, "usingSequence"); - this.CurrentSpanIndex = 0; - this.Consumed = 0; - this._currentPosition = this.Sequence.Start; - this._nextPosition = this._currentPosition; - - if (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) - { - this._moreData = true; - - if (memory.Length == 0) - { - this.CurrentSpan = default; - - // No data in the first span, move to one with data - this.GetNextSpan(); - } - else - { - this.CurrentSpan = memory.Span; - } - } - else - { - // No data in any spans and at end of sequence - this._moreData = false; - this.CurrentSpan = default; - } - } - - /// - /// Get the next segment with available data, if any. - /// - private void GetNextSpan() - { - Debug.Assert(this._usingSequence, "usingSequence"); - if (!this.Sequence.IsSingleSegment) - { - SequencePosition previousNextPosition = this._nextPosition; - while (this.Sequence.TryGet(ref this._nextPosition, out ReadOnlyMemory memory, advance: true)) - { - this._currentPosition = previousNextPosition; - if (memory.Length > 0) - { - this.CurrentSpan = memory.Span; - this.CurrentSpanIndex = 0; - return; - } - else - { - this.CurrentSpan = default; - this.CurrentSpanIndex = 0; - previousNextPosition = this._nextPosition; - } - } - } - - this._moreData = false; - } - - /// - /// Move the reader ahead the specified number of items. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Advance(long count) - { - const long TooBigOrNegative = unchecked((long)0xFFFFFFFF80000000); - if ((count & TooBigOrNegative) == 0 && this.CurrentSpan.Length - this.CurrentSpanIndex > (int)count) - { - this.CurrentSpanIndex += (int)count; - this.Consumed += count; - } - else if (this._usingSequence) - { - // Can't satisfy from the current span - this.AdvanceToNextSpan(count); - } - else if (this.CurrentSpan.Length - this.CurrentSpanIndex == (int)count) - { - this.CurrentSpanIndex += (int)count; - this.Consumed += count; - this._moreData = false; - } - else - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - } - - /// - /// Unchecked helper to avoid unnecessary checks where you know count is valid. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AdvanceCurrentSpan(long count) - { - Debug.Assert(count >= 0, "count >= 0"); - - this.Consumed += count; - this.CurrentSpanIndex += (int)count; - if (this._usingSequence && this.CurrentSpanIndex >= this.CurrentSpan.Length) - { - this.GetNextSpan(); - } - } - - /// - /// Only call this helper if you know that you are advancing in the current span - /// with valid count and there is no need to fetch the next one. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void AdvanceWithinSpan(long count) - { - Debug.Assert(count >= 0, "count >= 0"); - - this.Consumed += count; - this.CurrentSpanIndex += (int)count; - - Debug.Assert(this.CurrentSpanIndex < this.CurrentSpan.Length, "this.CurrentSpanIndex < this.CurrentSpan.Length"); - } - - /// - /// Move the reader ahead the specified number of items - /// if there are enough elements remaining in the sequence. - /// - /// true if there were enough elements to advance; otherwise false. - internal bool TryAdvance(long count) - { - if (this.Remaining < count) - { - return false; - } - - this.Advance(count); - return true; - } - - private void AdvanceToNextSpan(long count) - { - Debug.Assert(this._usingSequence, "usingSequence"); - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - this.Consumed += count; - while (this._moreData) - { - int remaining = this.CurrentSpan.Length - this.CurrentSpanIndex; - - if (remaining > count) - { - this.CurrentSpanIndex += (int)count; - count = 0; - break; - } - - // As there may not be any further segments we need to - // push the current index to the end of the span. - this.CurrentSpanIndex += remaining; - count -= remaining; - Debug.Assert(count >= 0, "count >= 0"); - - this.GetNextSpan(); - - if (count == 0) - { - break; - } - } - - if (count != 0) - { - // Not enough data left- adjust for where we actually ended and throw - this.Consumed -= count; - throw new ArgumentOutOfRangeException(nameof(count)); - } - } - - /// - /// Copies data from the current to the given span. - /// - /// Destination to copy to. - /// True if there is enough data to copy to the . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryCopyTo(Span destination) - { - ReadOnlySpan firstSpan = this.UnreadSpan; - if (firstSpan.Length >= destination.Length) - { - firstSpan.Slice(0, destination.Length).CopyTo(destination); - return true; - } - - return this.TryCopyMultisegment(destination); - } - - internal bool TryCopyMultisegment(Span destination) - { - if (this.Remaining < destination.Length) - { - return false; - } - - ReadOnlySpan firstSpan = this.UnreadSpan; - Debug.Assert(firstSpan.Length < destination.Length, "firstSpan.Length < destination.Length"); - firstSpan.CopyTo(destination); - int copied = firstSpan.Length; - - SequencePosition next = this._nextPosition; - while (this.Sequence.TryGet(ref next, out ReadOnlyMemory nextSegment, true)) - { - if (nextSegment.Length > 0) - { - ReadOnlySpan nextSpan = nextSegment.Span; - int toCopy = Math.Min(nextSpan.Length, destination.Length - copied); - nextSpan.Slice(0, toCopy).CopyTo(destination.Slice(copied)); - copied += toCopy; - if (copied >= destination.Length) - { - break; - } - } - } - - return true; - } - } -} From 73c59ad49dddf0d970533eefcb7878e95c4ae833 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Wed, 6 May 2020 16:18:00 +0900 Subject: [PATCH 08/82] Port .NET 5 EncodingExtensions --- LICENSE.DotNetFoundation.txt | 23 + .../System.Text/EncodingExtensions.cs | 633 ++++++++++++++++++ 2 files changed, 656 insertions(+) create mode 100644 LICENSE.DotNetFoundation.txt create mode 100644 src/MsgPack.Core/System.Text/EncodingExtensions.cs diff --git a/LICENSE.DotNetFoundation.txt b/LICENSE.DotNetFoundation.txt new file mode 100644 index 000000000..a616ed188 --- /dev/null +++ b/LICENSE.DotNetFoundation.txt @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/src/MsgPack.Core/System.Text/EncodingExtensions.cs b/src/MsgPack.Core/System.Text/EncodingExtensions.cs new file mode 100644 index 000000000..b204df465 --- /dev/null +++ b/src/MsgPack.Core/System.Text/EncodingExtensions.cs @@ -0,0 +1,633 @@ +// This file is licensed under MIT license. +// See the LICENSE.DotNetFoundation in the project root for more information. + +// This file is ported from .NET 5 https://github.com/dotnet/runtime/blob/a4c050ebf45a9745e2941863a8d7ae9c70ee88b2/src/libraries/System.Memory/src/System/Text/EncodingExtensions.cs#L1 + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Text +{ + public static class EncodingExtensions + { + /// + /// The maximum number of input elements after which we'll begin to chunk the input. + /// + /// + /// The reason for this chunking is that the existing Encoding / Encoder / Decoder APIs + /// like GetByteCount / GetCharCount will throw if an integer overflow occurs. Since + /// we may be working with large inputs in these extension methods, we don't want to + /// risk running into this issue. While it's technically possible even for 1 million + /// input elements to result in an overflow condition, such a scenario is unrealistic, + /// so we won't worry about it. + /// + private const int MaxInputElementsPerIteration = 1 * 1024 * 1024; + + /// + /// Encodes the specified to s using the specified + /// and writes the result to . + /// + /// The which represents how the data in should be encoded. + /// The to encode to s. + /// The buffer to which the encoded bytes will be written. + /// Thrown if contains data that cannot be encoded and is configured + /// to throw an exception when such data is seen. + public static long GetBytes(this Encoding encoding, ReadOnlySpan chars, IBufferWriter writer) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } + + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (chars.Length <= MaxInputElementsPerIteration) + { + // The input span is small enough where we can one-shot this. + + int byteCount = encoding.GetByteCount(chars); + Span scratchBuffer = writer.GetSpan(byteCount); + + int actualBytesWritten = encoding.GetBytes(chars, scratchBuffer); + + writer.Advance(actualBytesWritten); + return actualBytesWritten; + } + else + { + // Allocate a stateful Encoder instance and chunk this. + + Convert(encoding.GetEncoder(), chars, writer, flush: true, out long totalBytesWritten, out bool completed); + return totalBytesWritten; + } + } + + /// + /// Decodes the specified to s using the specified + /// and writes the result to . + /// + /// The which represents how the data in should be encoded. + /// The whose contents should be encoded. + /// The buffer to which the encoded bytes will be written. + /// The number of bytes written to . + /// Thrown if contains data that cannot be encoded and is configured + /// to throw an exception when such data is seen. + public static long GetBytes(this Encoding encoding, in ReadOnlySequence chars, IBufferWriter writer) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } + + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + // Delegate to the Span-based method if possible. + // If that doesn't work, allocate the Encoder instance and run a loop. + + if (chars.IsSingleSegment) + { + return GetBytes(encoding, chars.FirstSpan, writer); + } + else + { + Convert(encoding.GetEncoder(), chars, writer, flush: true, out long bytesWritten, out bool completed); + return bytesWritten; + } + } + + /// + /// Encodes the specified to s using the specified + /// and outputs the result to . + /// + /// The which represents how the data in should be encoded. + /// The to encode to s. + /// The destination buffer to which the encoded bytes will be written. + /// The number of bytes written to . + /// Thrown if is not large enough to contain the encoded form of . + /// Thrown if contains data that cannot be encoded and is configured + /// to throw an exception when such data is seen. + public static int GetBytes(this Encoding encoding, in ReadOnlySequence chars, Span bytes) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } + + if (chars.IsSingleSegment) + { + // If the incoming sequence is single-segment, one-shot this. + + return encoding.GetBytes(chars.FirstSpan, bytes); + } + else + { + // If the incoming sequence is multi-segment, create a stateful Encoder + // and use it as the workhorse. On the final iteration we'll pass flush=true. + + ReadOnlySequence remainingChars = chars; + int originalBytesLength = bytes.Length; + Encoder encoder = encoding.GetEncoder(); + bool isFinalSegment; + + do + { + remainingChars.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + isFinalSegment = remainingChars.IsSingleSegment; + + int bytesWrittenJustNow = encoder.GetBytes(firstSpan, bytes, flush: isFinalSegment); + bytes = bytes.Slice(bytesWrittenJustNow); + remainingChars = remainingChars.Slice(next); + } while (!isFinalSegment); + + return originalBytesLength - bytes.Length; // total number of bytes we wrote + } + } + + /// + /// Encodes the specified into a array using the specified . + /// + /// The which represents how the data in should be encoded. + /// The to encode to s. + /// A array which represents the encoded contents of . + /// Thrown if contains data that cannot be encoded and is configured + /// to throw an exception when such data is seen. + public static byte[] GetBytes(this Encoding encoding, in ReadOnlySequence chars) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } + + if (chars.IsSingleSegment) + { + // If the incoming sequence is single-segment, one-shot this. + + ReadOnlySpan span = chars.FirstSpan; + + byte[] retVal = new byte[encoding.GetByteCount(span)]; + encoding.GetBytes(span, retVal); + return retVal; + } + else + { + // If the incoming sequence is multi-segment, create a stateful Encoder + // and use it as the workhorse. On the final iteration we'll pass flush=true. + + Encoder encoder = encoding.GetEncoder(); + + // Maintain a list of all the segments we'll need to concat together. + // These will be released back to the pool at the end of the method. + + List<(byte[], int)> listOfSegments = new List<(byte[], int)>(); + int totalByteCount = 0; + + ReadOnlySequence remainingChars = chars; + bool isFinalSegment; + + do + { + remainingChars.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + isFinalSegment = remainingChars.IsSingleSegment; + + int byteCountThisIteration = encoder.GetByteCount(firstSpan, flush: isFinalSegment); + byte[] rentedArray = ArrayPool.Shared.Rent(byteCountThisIteration); + int actualBytesWrittenThisIteration = encoder.GetBytes(firstSpan, rentedArray, flush: isFinalSegment); // could throw ArgumentException if overflow would occur + listOfSegments.Add((rentedArray, actualBytesWrittenThisIteration)); + + totalByteCount += actualBytesWrittenThisIteration; + if (totalByteCount < 0) + { + // If we overflowed, call the array ctor, passing int.MaxValue. + // This will end up throwing the expected OutOfMemoryException + // since arrays are limited to under int.MaxValue elements in length. + + totalByteCount = int.MaxValue; + break; + } + + remainingChars = remainingChars.Slice(next); + } while (!isFinalSegment); + + // Now build up the byte[] to return, then release all of our scratch buffers + // back to the shared pool. + + byte[] retVal = new byte[totalByteCount]; + Span remainingBytes = retVal; + + foreach ((byte[] array, int length) in listOfSegments) + { + array.AsSpan(0, length).CopyTo(remainingBytes); + ArrayPool.Shared.Return(array); + remainingBytes = remainingBytes.Slice(length); + } + + Debug.Assert(remainingBytes.IsEmpty, "Over-allocated the byte[] instance?"); + + return retVal; + } + } + + /// + /// Decodes the specified to s using the specified + /// and writes the result to . + /// + /// The which represents how the data in should be decoded. + /// The whose bytes should be decoded. + /// The buffer to which the decoded chars will be written. + /// The number of chars written to . + /// Thrown if contains data that cannot be decoded and is configured + /// to throw an exception when such data is seen. + public static long GetChars(this Encoding encoding, ReadOnlySpan bytes, IBufferWriter writer) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } + + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + if (bytes.Length <= MaxInputElementsPerIteration) + { + // The input span is small enough where we can one-shot this. + + int charCount = encoding.GetCharCount(bytes); + Span scratchBuffer = writer.GetSpan(charCount); + + int actualCharsWritten = encoding.GetChars(bytes, scratchBuffer); + + writer.Advance(actualCharsWritten); + return actualCharsWritten; + } + else + { + // Allocate a stateful Decoder instance and chunk this. + + Convert(encoding.GetDecoder(), bytes, writer, flush: true, out long totalCharsWritten, out bool completed); + return totalCharsWritten; + } + } + + /// + /// Decodes the specified to s using the specified + /// and writes the result to . + /// + /// The which represents how the data in should be decoded. + /// The whose bytes should be decoded. + /// The buffer to which the decoded chars will be written. + /// The number of chars written to . + /// Thrown if contains data that cannot be decoded and is configured + /// to throw an exception when such data is seen. + public static long GetChars(this Encoding encoding, in ReadOnlySequence bytes, IBufferWriter writer) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } + + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + // Delegate to the Span-based method if possible. + // If that doesn't work, allocate the Encoder instance and run a loop. + + if (bytes.IsSingleSegment) + { + return GetChars(encoding, bytes.FirstSpan, writer); + } + else + { + Convert(encoding.GetDecoder(), bytes, writer, flush: true, out long charsWritten, out bool completed); + return charsWritten; + } + } + + /// + /// Decodes the specified to s using the specified + /// and outputs the result to . + /// + /// The which represents how the data in is encoded. + /// The to decode to characters. + /// The destination buffer to which the decoded characters will be written. + /// The number of chars written to . + /// Thrown if is not large enough to contain the encoded form of . + /// Thrown if contains data that cannot be decoded and is configured + /// to throw an exception when such data is seen. + public static int GetChars(this Encoding encoding, in ReadOnlySequence bytes, Span chars) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } + + if (bytes.IsSingleSegment) + { + // If the incoming sequence is single-segment, one-shot this. + + return encoding.GetChars(bytes.FirstSpan, chars); + } + else + { + // If the incoming sequence is multi-segment, create a stateful Decoder + // and use it as the workhorse. On the final iteration we'll pass flush=true. + + ReadOnlySequence remainingBytes = bytes; + int originalCharsLength = chars.Length; + Decoder decoder = encoding.GetDecoder(); + bool isFinalSegment; + + do + { + remainingBytes.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + isFinalSegment = remainingBytes.IsSingleSegment; + + int charsWrittenJustNow = decoder.GetChars(firstSpan, chars, flush: isFinalSegment); + chars = chars.Slice(charsWrittenJustNow); + remainingBytes = remainingBytes.Slice(next); + } while (!isFinalSegment); + + return originalCharsLength - chars.Length; // total number of chars we wrote + } + } + + /// + /// Decodes the specified into a using the specified . + /// + /// The which represents how the data in is encoded. + /// The to decode into characters. + /// A which represents the decoded contents of . + /// Thrown if contains data that cannot be decoded and is configured + /// to throw an exception when such data is seen. + public static string GetString(this Encoding encoding, in ReadOnlySequence bytes) + { + if (encoding is null) + { + throw new ArgumentNullException(nameof(encoding)); + } + + if (bytes.IsSingleSegment) + { + // If the incoming sequence is single-segment, one-shot this. + + return encoding.GetString(bytes.FirstSpan); + } + else + { + // If the incoming sequence is multi-segment, create a stateful Decoder + // and use it as the workhorse. On the final iteration we'll pass flush=true. + + Decoder decoder = encoding.GetDecoder(); + + // Maintain a list of all the segments we'll need to concat together. + // These will be released back to the pool at the end of the method. + + List<(char[], int)> listOfSegments = new List<(char[], int)>(); + int totalCharCount = 0; + + ReadOnlySequence remainingBytes = bytes; + bool isFinalSegment; + + do + { + remainingBytes.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + isFinalSegment = remainingBytes.IsSingleSegment; + + int charCountThisIteration = decoder.GetCharCount(firstSpan, flush: isFinalSegment); // could throw ArgumentException if overflow would occur + char[] rentedArray = ArrayPool.Shared.Rent(charCountThisIteration); + int actualCharsWrittenThisIteration = decoder.GetChars(firstSpan, rentedArray, flush: isFinalSegment); + listOfSegments.Add((rentedArray, actualCharsWrittenThisIteration)); + + totalCharCount += actualCharsWrittenThisIteration; + if (totalCharCount < 0) + { + // If we overflowed, call string.Create, passing int.MaxValue. + // This will end up throwing the expected OutOfMemoryException + // since strings are limited to under int.MaxValue elements in length. + + totalCharCount = int.MaxValue; + break; + } + + remainingBytes = remainingBytes.Slice(next); + } while (!isFinalSegment); + + // Now build up the string to return, then release all of our scratch buffers + // back to the shared pool. + + return string.Create(totalCharCount, listOfSegments, (span, listOfSegments) => + { + foreach ((char[] array, int length) in listOfSegments) + { + array.AsSpan(0, length).CopyTo(span); + ArrayPool.Shared.Return(array); + span = span.Slice(length); + } + + Debug.Assert(span.IsEmpty, "Over-allocated the string instance?"); + }); + } + } + + /// + /// Converts a to bytes using and writes the result to . + /// + /// The instance which can convert s to s. + /// A sequence of characters to encode. + /// The buffer to which the encoded bytes will be written. + /// to indicate no further data is to be converted; otherwise . + /// When this method returns, contains the count of s which were written to . + /// + /// When this method returns, contains if contains no partial internal state; otherwise, . + /// If is , this will always be set to when the method returns. + /// + /// Thrown if contains data that cannot be encoded and is configured + /// to throw an exception when such data is seen. + public static void Convert(this Encoder encoder, ReadOnlySpan chars, IBufferWriter writer, bool flush, out long bytesUsed, out bool completed) + { + if (encoder is null) + { + throw new ArgumentNullException(nameof(encoder)); + } + + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + // We need to perform at least one iteration of the loop since the encoder could have internal state. + + long totalBytesWritten = 0; + + do + { + // If our remaining input is very large, instead truncate it and tell the encoder + // that there'll be more data after this call. This truncation is only for the + // purposes of getting the required byte count. Since the writer may give us a span + // larger than what we asked for, we'll pass the entirety of the remaining data + // to the transcoding routine, since it may be able to make progress beyond what + // was initially computed for the truncated input data. + + int byteCountForThisSlice = (chars.Length <= MaxInputElementsPerIteration) + ? encoder.GetByteCount(chars, flush) + : encoder.GetByteCount(chars.Slice(0, MaxInputElementsPerIteration), flush: false /* this isn't the end of the data */); + + Span scratchBuffer = writer.GetSpan(byteCountForThisSlice); + + encoder.Convert(chars, scratchBuffer, flush, out int charsUsedJustNow, out int bytesWrittenJustNow, out completed); + + chars = chars.Slice(charsUsedJustNow); + writer.Advance(bytesWrittenJustNow); + totalBytesWritten += bytesWrittenJustNow; + } while (!chars.IsEmpty); + + bytesUsed = totalBytesWritten; + } + + /// + /// Converts a to encoded bytes and writes the result to . + /// + /// The instance which can convert s to s. + /// A sequence of characters to encode. + /// The buffer to which the encoded bytes will be written. + /// to indicate no further data is to be converted; otherwise . + /// When this method returns, contains the count of s which were written to . + /// When this method returns, contains if all input up until was + /// converted; otherwise, . If is , this will always be set to + /// when the method returns. + /// Thrown if contains data that cannot be encoded and is configured + /// to throw an exception when such data is seen. + public static void Convert(this Encoder encoder, in ReadOnlySequence chars, IBufferWriter writer, bool flush, out long bytesUsed, out bool completed) + { + // Parameter null checks will be performed by the workhorse routine. + + ReadOnlySequence remainingChars = chars; + long totalBytesWritten = 0; + bool isFinalSegment; + + do + { + // Process each segment individually. We need to run at least one iteration of the loop in case + // the Encoder has internal state. + + remainingChars..GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + isFinalSegment = remainingChars.IsSingleSegment; + + Convert(encoder, firstSpan, writer, flush && isFinalSegment, out long bytesWrittenThisIteration, out completed); + + totalBytesWritten += bytesWrittenThisIteration; + remainingChars = remainingChars.Slice(next); + } while (!isFinalSegment); + + bytesUsed = totalBytesWritten; + } + + /// + /// Converts a to chars using and writes the result to . + /// + /// The instance which can convert s to s. + /// A sequence of bytes to decode. + /// The buffer to which the decoded chars will be written. + /// to indicate no further data is to be converted; otherwise . + /// When this method returns, contains the count of s which were written to . + /// + /// When this method returns, contains if contains no partial internal state; otherwise, . + /// If is , this will always be set to when the method returns. + /// + /// Thrown if contains data that cannot be encoded and is configured + /// to throw an exception when such data is seen. + public static void Convert(this Decoder decoder, ReadOnlySpan bytes, IBufferWriter writer, bool flush, out long charsUsed, out bool completed) + { + if (decoder is null) + { + throw new ArgumentNullException(nameof(decoder)); + } + + if (writer is null) + { + throw new ArgumentNullException(nameof(writer)); + } + + // We need to perform at least one iteration of the loop since the decoder could have internal state. + + long totalCharsWritten = 0; + + do + { + // If our remaining input is very large, instead truncate it and tell the decoder + // that there'll be more data after this call. This truncation is only for the + // purposes of getting the required char count. Since the writer may give us a span + // larger than what we asked for, we'll pass the entirety of the remaining data + // to the transcoding routine, since it may be able to make progress beyond what + // was initially computed for the truncated input data. + + int charCountForThisSlice = (bytes.Length <= MaxInputElementsPerIteration) + ? decoder.GetCharCount(bytes, flush) + : decoder.GetCharCount(bytes.Slice(0, MaxInputElementsPerIteration), flush: false /* this isn't the end of the data */); + + Span scratchBuffer = writer.GetSpan(charCountForThisSlice); + + decoder.Convert(bytes, scratchBuffer, flush, out int bytesUsedJustNow, out int charsWrittenJustNow, out completed); + + bytes = bytes.Slice(bytesUsedJustNow); + writer.Advance(charsWrittenJustNow); + totalCharsWritten += charsWrittenJustNow; + } while (!bytes.IsEmpty); + + charsUsed = totalCharsWritten; + } + + /// + /// Converts a to UTF-16 encoded characters and writes the result to . + /// + /// The instance which can convert s to s. + /// A sequence of bytes to decode. + /// The buffer to which the decoded characters will be written. + /// to indicate no further data is to be converted; otherwise . + /// When this method returns, contains the count of s which were written to . + /// + /// When this method returns, contains if contains no partial internal state; otherwise, . + /// If is , this will always be set to when the method returns. + /// + /// Thrown if contains data that cannot be decoded and is configured + /// to throw an exception when such data is seen. + public static void Convert(this Decoder decoder, in ReadOnlySequence bytes, IBufferWriter writer, bool flush, out long charsUsed, out bool completed) + { + // Parameter null checks will be performed by the workhorse routine. + + ReadOnlySequence remainingBytes = bytes; + long totalCharsWritten = 0; + bool isFinalSegment; + + do + { + // Process each segment individually. We need to run at least one iteration of the loop in case + // the Decoder has internal state. + + remainingBytes.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + isFinalSegment = remainingBytes.IsSingleSegment; + + Convert(decoder, firstSpan, writer, flush && isFinalSegment, out long charsWrittenThisIteration, out completed); + + totalCharsWritten += charsWrittenThisIteration; + remainingBytes = remainingBytes.Slice(next); + } while (!isFinalSegment); + + charsUsed = totalCharsWritten; + } + } +} From 8536ac64435191583f5a6b00c3873db376f3239d Mon Sep 17 00:00:00 2001 From: yfakariya Date: Wed, 6 May 2020 16:52:54 +0900 Subject: [PATCH 09/82] Port .NET 5 EncodingExtensions to .NET Core 3.1 * Add psuedo GetFirstSpan() internal method. * Fix code format to compliant project's rule. --- .../System.Text/EncodingExtensions.cs | 64 +++++++++++++++---- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/src/MsgPack.Core/System.Text/EncodingExtensions.cs b/src/MsgPack.Core/System.Text/EncodingExtensions.cs index b204df465..9cc34cbd3 100644 --- a/src/MsgPack.Core/System.Text/EncodingExtensions.cs +++ b/src/MsgPack.Core/System.Text/EncodingExtensions.cs @@ -1,3 +1,4 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under MIT license. // See the LICENSE.DotNetFoundation in the project root for more information. @@ -10,10 +11,13 @@ using System.Buffers; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using MsgPack.Internal; namespace System.Text { - public static class EncodingExtensions + internal static class EncodingExtensions { /// /// The maximum number of input elements after which we'll begin to chunk the input. @@ -142,7 +146,7 @@ public static int GetBytes(this Encoding encoding, in ReadOnlySequence cha do { - remainingChars.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + GetFirstSpan(remainingChars, out ReadOnlySpan firstSpan, out SequencePosition next); isFinalSegment = remainingChars.IsSingleSegment; int bytesWrittenJustNow = encoder.GetBytes(firstSpan, bytes, flush: isFinalSegment); @@ -155,11 +159,11 @@ public static int GetBytes(this Encoding encoding, in ReadOnlySequence cha } /// - /// Encodes the specified into a array using the specified . + /// Encodes the specified into a array using the specified . /// /// The which represents how the data in should be encoded. /// The to encode to s. - /// A array which represents the encoded contents of . + /// A array which represents the encoded contents of . /// Thrown if contains data that cannot be encoded and is configured /// to throw an exception when such data is seen. public static byte[] GetBytes(this Encoding encoding, in ReadOnlySequence chars) @@ -197,7 +201,7 @@ public static byte[] GetBytes(this Encoding encoding, in ReadOnlySequence do { - remainingChars.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + GetFirstSpan(remainingChars, out ReadOnlySpan firstSpan, out SequencePosition next); isFinalSegment = remainingChars.IsSingleSegment; int byteCountThisIteration = encoder.GetByteCount(firstSpan, flush: isFinalSegment); @@ -212,7 +216,7 @@ public static byte[] GetBytes(this Encoding encoding, in ReadOnlySequence // This will end up throwing the expected OutOfMemoryException // since arrays are limited to under int.MaxValue elements in length. - totalByteCount = int.MaxValue; + totalByteCount = Int32.MaxValue; break; } @@ -353,7 +357,7 @@ public static int GetChars(this Encoding encoding, in ReadOnlySequence byt do { - remainingBytes.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + GetFirstSpan(remainingBytes, out ReadOnlySpan firstSpan, out SequencePosition next); isFinalSegment = remainingBytes.IsSingleSegment; int charsWrittenJustNow = decoder.GetChars(firstSpan, chars, flush: isFinalSegment); @@ -366,11 +370,11 @@ public static int GetChars(this Encoding encoding, in ReadOnlySequence byt } /// - /// Decodes the specified into a using the specified . + /// Decodes the specified into a using the specified . /// /// The which represents how the data in is encoded. /// The to decode into characters. - /// A which represents the decoded contents of . + /// A which represents the decoded contents of . /// Thrown if contains data that cannot be decoded and is configured /// to throw an exception when such data is seen. public static string GetString(this Encoding encoding, in ReadOnlySequence bytes) @@ -404,7 +408,7 @@ public static string GetString(this Encoding encoding, in ReadOnlySequence do { - remainingBytes.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + GetFirstSpan(remainingBytes, out ReadOnlySpan firstSpan, out SequencePosition next); isFinalSegment = remainingBytes.IsSingleSegment; int charCountThisIteration = decoder.GetCharCount(firstSpan, flush: isFinalSegment); // could throw ArgumentException if overflow would occur @@ -419,7 +423,7 @@ public static string GetString(this Encoding encoding, in ReadOnlySequence // This will end up throwing the expected OutOfMemoryException // since strings are limited to under int.MaxValue elements in length. - totalCharCount = int.MaxValue; + totalCharCount = Int32.MaxValue; break; } @@ -429,7 +433,7 @@ public static string GetString(this Encoding encoding, in ReadOnlySequence // Now build up the string to return, then release all of our scratch buffers // back to the shared pool. - return string.Create(totalCharCount, listOfSegments, (span, listOfSegments) => + return String.Create(totalCharCount, listOfSegments, (span, listOfSegments) => { foreach ((char[] array, int length) in listOfSegments) { @@ -524,7 +528,7 @@ public static void Convert(this Encoder encoder, in ReadOnlySequence chars // Process each segment individually. We need to run at least one iteration of the loop in case // the Encoder has internal state. - remainingChars..GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + GetFirstSpan(remainingChars, out ReadOnlySpan firstSpan, out SequencePosition next); isFinalSegment = remainingChars.IsSingleSegment; Convert(encoder, firstSpan, writer, flush && isFinalSegment, out long bytesWrittenThisIteration, out completed); @@ -618,7 +622,7 @@ public static void Convert(this Decoder decoder, in ReadOnlySequence bytes // Process each segment individually. We need to run at least one iteration of the loop in case // the Decoder has internal state. - remainingBytes.GetFirstSpan(out ReadOnlySpan firstSpan, out SequencePosition next); + GetFirstSpan(remainingBytes, out ReadOnlySpan firstSpan, out SequencePosition next); isFinalSegment = remainingBytes.IsSingleSegment; Convert(decoder, firstSpan, writer, flush && isFinalSegment, out long charsWrittenThisIteration, out completed); @@ -629,5 +633,37 @@ public static void Convert(this Decoder decoder, in ReadOnlySequence bytes charsUsed = totalCharsWritten; } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static void GetFirstSpan(ReadOnlySequence sequence, out ReadOnlySpan first, out SequencePosition next) + { + // Port from https://github.com/dotnet/runtime/blob/a4c050ebf45a9745e2941863a8d7ae9c70ee88b2/src/libraries/System.Memory/src/System/Buffers/ReadOnlySequence.Helpers.cs#L635 + next = default; + + if (SequenceMarshal.TryGetArray(sequence, out var arraySegment)) + { + first = arraySegment; + } + else if(SequenceMarshal.TryGetReadOnlySequenceSegment(sequence, out var segment, out var startIndex, out _, out var endIndex)) + { + // In this pass, startIndex and endIndex should be same as internal _startInteger and _endInteger. + first = segment.Memory.Span; + if (!sequence.IsSingleSegment) + { + first = first.Slice(startIndex); + next = new SequencePosition(segment.Next, 0); + } + else + { + first = first.Slice(startIndex, endIndex - startIndex); + } + } + else + { + // This should fallback to "Slow-Pass" and default value handling. + // And they must be single segment, so next will be default. + first = sequence.FirstSpan; + } + } } } From 0e508c938d0d2acac2bb2bfc1a395b144652a1cb Mon Sep 17 00:00:00 2001 From: yfakariya Date: Fri, 8 May 2020 02:34:22 +0900 Subject: [PATCH 10/82] Configure editorconfig --- .editorconfig | 140 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 122 insertions(+), 18 deletions(-) diff --git a/.editorconfig b/.editorconfig index 73050d175..c28041544 100644 --- a/.editorconfig +++ b/.editorconfig @@ -42,18 +42,17 @@ csharp_style_var_when_type_is_apparent = true:suggestion csharp_style_var_elsewhere = true:suggestion dotnet_sort_system_directives_first = true csharp_space_between_method_declaration_name_and_open_parenthesis = false -csharp_space_between_method_declaration_parameter_list_parentheses = true +csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_parameter_list_parentheses = true +csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_call_empty_parameter_list_parentheses = false -csharp_space_between_parentheses = expressions,type_casts,control_flow_statements csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_cast = false csharp_space_around_declaration_statements = true csharp_space_before_open_square_brackets = false csharp_space_between_empty_square_brackets = false -csharp_space_between_square_brackets = true +csharp_space_between_square_brackets = false csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_comma = true csharp_space_after_dot = false @@ -62,7 +61,7 @@ csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false csharp_space_before_semicolon_in_for_statement = false -csharp_space_around_binary_operators = true +csharp_space_around_binary_operators = false csharp_indent_braces = false csharp_indent_block_contents = true csharp_indent_switch_labels = true @@ -75,17 +74,122 @@ csharp_preserve_single_line_statements = true csharp_new_line_before_open_brace = all csharp_new_line_between_query_expression_clauses = true -# Disabled for Unity compatibility until 2017.1 RTM [*.cs] -dotnet_style_null_propagation = false:warning -csharp_style_expression_bodied_methods = false:warning -csharp_style_expression_bodied_constructors = false:warning -csharp_style_expression_bodied_operators = false:warning -csharp_style_expression_bodied_properties = false:warning -csharp_style_expression_bodied_indexers = false:warning -csharp_style_expression_bodied_accessors = false:warning -csharp_style_pattern_matching_over_is_with_cast_check = false:warning -csharp_style_pattern_matching_over_as_with_null_check = false:warning -csharp_style_inlined_variable_declaration = false:warning -csharp_style_throw_expression = false:warning -csharp_style_conditional_delegate_call = false:warning +dotnet_style_null_propagation = true:suggestion +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# From https://github.com/dotnet/roslyn/blob/master/.editorconfig + +# Non-private static fields are PascalCase +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style + +dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_static_fields.required_modifiers = static + +dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case + +# Non-private readonly fields are PascalCase +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.symbols = non_private_readonly_fields +dotnet_naming_rule.non_private_readonly_fields_should_be_pascal_case.style = non_private_readonly_field_style + +dotnet_naming_symbols.non_private_readonly_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_readonly_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected +dotnet_naming_symbols.non_private_readonly_fields.required_modifiers = readonly + +dotnet_naming_style.non_private_readonly_field_style.capitalization = pascal_case + +# Constants are PascalCase +dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants +dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_style.constant_style.capitalization = pascal_case + +# Static fields are camelCase and start with s_ +dotnet_naming_rule.static_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.static_fields_should_be_camel_case.symbols = static_fields +dotnet_naming_rule.static_fields_should_be_camel_case.style = static_field_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_field_style.capitalization = camel_case +dotnet_naming_style.static_field_style.required_prefix = s_ + +# Instance fields are camelCase and start with _ +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style + +dotnet_naming_symbols.instance_fields.applicable_kinds = field + +dotnet_naming_style.instance_field_style.capitalization = camel_case +dotnet_naming_style.instance_field_style.required_prefix = _ + +# Locals and parameters are camelCase +dotnet_naming_rule.locals_should_be_camel_case.severity = suggestion +dotnet_naming_rule.locals_should_be_camel_case.symbols = locals_and_parameters +dotnet_naming_rule.locals_should_be_camel_case.style = camel_case_style + +dotnet_naming_symbols.locals_and_parameters.applicable_kinds = parameter, local + +dotnet_naming_style.camel_case_style.capitalization = camel_case + +# Local functions are PascalCase +dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function + +dotnet_naming_style.local_function_style.capitalization = pascal_case + +# By default, name items with PascalCase +dotnet_naming_rule.members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.members_should_be_pascal_case.symbols = all_members +dotnet_naming_rule.members_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.all_members.applicable_kinds = * + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Blocks are allowed +csharp_prefer_braces = true:silent +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +# IDE0005: Remove unnecessary usings/imports +dotnet_diagnostic.IDE0005.severity = warning + +# IDE0036: Order modifiers +dotnet_diagnostic.IDE0036.severity = warning + +# IDE0040: Add accessibility modifiers +dotnet_diagnostic.IDE0040.severity = warning + +# IDE0044: Make field readonly +dotnet_diagnostic.IDE0044.severity = warning + +# CONSIDER: Are IDE0051 and IDE0052 too noisy to be warnings for IDE editing scenarios? Should they be made build-only warnings? +# IDE0051: Remove unused private member +dotnet_diagnostic.IDE0051.severity = warning + +# IDE0052: Remove unread private member +dotnet_diagnostic.IDE0052.severity = warning \ No newline at end of file From ad8839b3a113dea2da8b1252d434a0f9dc0d0f7e Mon Sep 17 00:00:00 2001 From: yfakariya Date: Fri, 8 May 2020 02:37:42 +0900 Subject: [PATCH 11/82] Fix Directory.Build.props encoding --- Directory.Build.props | Bin 716 -> 349 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 06e5a5521db7ba78d58e9b84e279a45c124c1f41..b87abed37ea7bc47c78afd73dbfaa95f89daacbc 100644 GIT binary patch literal 349 zcmZvY!3u*g5Jc~O#S(<5P;GBLBuY`}u|o0Ii>*;oG?6qZ_3s;tf=XczyEE)#7?OOt z7T!#~xD*r}!aV{e9g^TL9X5gjfWaG}3bq-3u5XGKeLAFWxD(dSBvG1nY(#E?Wa z7Q8BBu6THLst_Yf-(|&`Td%BHZmEKD;t4YK%BiJ#Ql&uU2r=V`Z$|1k<%F|Mv@NqM zg%kJ4Ywg}q->GP3>XD1@{6KayN1UbbRGCOC`S%xBB8P2?sBfpak+*A6Bs_SY-K<@T zdvz~FADx)Wm1-}G`O}`!RnE3EcfQL1^gXeb+KW`P*>0q|_vE#?JJ9#IInCep?#)!D O3H!5uOB&7Z9s2>;mvIjO From db229ae9f959ab08226bacb79f2fcda39550e9d3 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Fri, 8 May 2020 02:38:07 +0900 Subject: [PATCH 12/82] Define common properties --- Directory.Build.props | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Directory.Build.props b/Directory.Build.props index b87abed37..58a34002b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,5 +4,12 @@ Debug AnyCPU ..\..\ + enable + + + $(DefineConstants);FEATURE_ASYNC_DISPOSABLE + + + $(DefineConstants);FEATURE_ASYNC_DISPOSABLE;FEATURE_ENCODING_EXTENSION;FEATURE_UTF8STRING From 51b0a935ccc43bca9b5ade62308d2ade9799855c Mon Sep 17 00:00:00 2001 From: yfakariya Date: Fri, 8 May 2020 02:39:24 +0900 Subject: [PATCH 13/82] WIP: initial v2 commit This commit also includes _SampleObject to prove API concept. --- doc/v2.md | 49 + .../InsufficientInputException.cs | 23 + .../Internal/CollectionContext.cs | 52 + .../Internal/CollectionItemIterator.cs | 84 ++ .../Internal/CollectionType.cs | 67 + .../Internal/Decoder.Primitives.cs | 1019 ++++++++++++++++ .../Internal/Decoder.Primitives.tt | 130 ++ .../Internal/Decoder.Strings.cs | 315 +++++ .../Internal/Decoder.Strings.tt | 194 +++ src/MsgPack.Abstraction/Internal/Decoder.cs | 226 ++++ .../Internal/DecoderOptions.cs | 47 + .../Internal/DecoderOptionsBuilder.cs | 93 ++ .../Internal/ElementType.cs | 25 + .../Internal/Encoder.Primitives.cs | 228 ++++ .../Internal/Encoder.Primitives.tt | 65 + src/MsgPack.Abstraction/Internal/Encoder.cs | 175 +++ .../Internal/EncoderOptions.cs | 35 + .../Internal/EncoderOptionsBuilder.cs | 61 + src/MsgPack.Abstraction/Internal/Ensure.cs | 45 + .../Internal/FormatFeatures.cs | 20 + .../Internal/FormatFeaturesBuilder.cs | 15 + .../Internal/MethodImplOptionsShim.cs | 22 + .../Internal/OptionsDefaults.cs | 22 + .../Internal/StreamBufferWriter.cs | 112 ++ .../Internal/StreamReadOnlyMemoryProvider.cs | 57 + src/MsgPack.Abstraction/Internal/Throw.cs | 150 +++ .../Internal/Utf8EncodingNonBom.cs | 21 + .../LimitExceededException.cs | 21 + .../MsgPack.Abstraction.csproj | 49 + .../Properties/AssemblyInfo.cs | 6 + .../Serialization/DeserializationOptions.cs | 38 + .../DeserializationOptionsBuilder.cs | 58 + .../DeserializationOperationContext.cs | 65 + .../Internal/IObjectSerializer`1.cs | 20 + .../Internal/SerializationOperationContext.cs | 50 + .../Serialization/SerializationOptions.cs | 26 + .../SerializationOptionsBuilder.cs | 24 + .../Serialization/Serializer`1.cs | 112 ++ .../MsgPack.Compatibility.csproj | 7 + .../Internal/CurrentMessagePackEncoder.cs | 163 +++ .../Internal/LegacyMessagePackEncoder.cs | 45 + src/MsgPack.Core/Internal/MemoryExtensions.cs | 34 + .../MessagePackDecoder.CollectionHeaders.cs | 171 +++ .../Internal/MessagePackDecoder.DecodeItem.cs | 355 ++++++ .../Internal/MessagePackDecoder.Extension.cs | 138 +++ .../Internal/MessagePackDecoder.Integers.cs | 449 +++++++ .../Internal/MessagePackDecoder.Integers.tt | 262 ++++ .../Internal/MessagePackDecoder.Iteration.cs | 67 + .../Internal/MessagePackDecoder.Nullables.cs | 161 +++ .../Internal/MessagePackDecoder.Nullables.tt | 55 + .../MessagePackDecoder.Number.ttinclude | 32 + .../Internal/MessagePackDecoder.Reals.cs | 223 ++++ .../Internal/MessagePackDecoder.Reals.tt | 100 ++ .../Internal/MessagePackDecoder.SkipDrain.cs | 281 +++++ .../Internal/MessagePackDecoder.Strings.cs | 283 +++++ .../Internal/MessagePackDecoder.cs | 184 +++ .../Internal/MessagePackDecoderOptions.cs | 12 + .../MessagePackDecoderOptionsBuilder.cs | 13 + .../Internal/MessagePackEncoder.Integers.cs | 190 +++ .../Internal/MessagePackEncoder.Integers.tt | 137 +++ .../Internal/MessagePackEncoder.Strings.cs | 272 +++++ .../Internal/MessagePackEncoder.Strings.tt | 173 +++ .../Internal/MessagePackEncoder.cs | 249 ++++ .../Internal/MessagePackEncoderOptions.cs | 11 + .../MessagePackEncoderOptionsBuilder.cs | 13 + src/MsgPack.Core/Internal/MessagePackThrow.cs | 45 + .../Internal/MsgPackStringTrie`1.cs | 299 +++++ src/MsgPack.Core/MsgPack.Core.csproj | 71 ++ .../System.Text/EncodingExtensions.cs | 94 +- src/MsgPack.Core/_SampleObject.cs | 1085 +++++++++++++++++ .../MsgPack.Extensions.csproj | 7 + src/MsgPack.Json/Json/JsonEncoder.cs | 784 ++++++++++++ src/MsgPack.Json/MsgPack.Json.csproj | 21 + .../MsgPack.Serialization.Extensions.csproj | 7 + .../MsgPack.Serialization.Reflection.csproj | 7 + ...Pack.Serialization.SourceGeneration.csproj | 7 + .../MsgPack.Serialization.csproj | 15 + 77 files changed, 10301 insertions(+), 42 deletions(-) create mode 100644 doc/v2.md create mode 100644 src/MsgPack.Abstraction/InsufficientInputException.cs create mode 100644 src/MsgPack.Abstraction/Internal/CollectionContext.cs create mode 100644 src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs create mode 100644 src/MsgPack.Abstraction/Internal/CollectionType.cs create mode 100644 src/MsgPack.Abstraction/Internal/Decoder.Primitives.cs create mode 100644 src/MsgPack.Abstraction/Internal/Decoder.Primitives.tt create mode 100644 src/MsgPack.Abstraction/Internal/Decoder.Strings.cs create mode 100644 src/MsgPack.Abstraction/Internal/Decoder.Strings.tt create mode 100644 src/MsgPack.Abstraction/Internal/Decoder.cs create mode 100644 src/MsgPack.Abstraction/Internal/DecoderOptions.cs create mode 100644 src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs create mode 100644 src/MsgPack.Abstraction/Internal/ElementType.cs create mode 100644 src/MsgPack.Abstraction/Internal/Encoder.Primitives.cs create mode 100644 src/MsgPack.Abstraction/Internal/Encoder.Primitives.tt create mode 100644 src/MsgPack.Abstraction/Internal/Encoder.cs create mode 100644 src/MsgPack.Abstraction/Internal/EncoderOptions.cs create mode 100644 src/MsgPack.Abstraction/Internal/EncoderOptionsBuilder.cs create mode 100644 src/MsgPack.Abstraction/Internal/Ensure.cs create mode 100644 src/MsgPack.Abstraction/Internal/FormatFeatures.cs create mode 100644 src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs create mode 100644 src/MsgPack.Abstraction/Internal/MethodImplOptionsShim.cs create mode 100644 src/MsgPack.Abstraction/Internal/OptionsDefaults.cs create mode 100644 src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs create mode 100644 src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs create mode 100644 src/MsgPack.Abstraction/Internal/Throw.cs create mode 100644 src/MsgPack.Abstraction/Internal/Utf8EncodingNonBom.cs create mode 100644 src/MsgPack.Abstraction/LimitExceededException.cs create mode 100644 src/MsgPack.Abstraction/MsgPack.Abstraction.csproj create mode 100644 src/MsgPack.Abstraction/Properties/AssemblyInfo.cs create mode 100644 src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs create mode 100644 src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext.cs create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`1.cs create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext.cs create mode 100644 src/MsgPack.Abstraction/Serialization/SerializationOptions.cs create mode 100644 src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs create mode 100644 src/MsgPack.Abstraction/Serialization/Serializer`1.cs create mode 100644 src/MsgPack.Compatibility/MsgPack.Compatibility.csproj create mode 100644 src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs create mode 100644 src/MsgPack.Core/Internal/LegacyMessagePackEncoder.cs create mode 100644 src/MsgPack.Core/Internal/MemoryExtensions.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Integers.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Integers.tt create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Iteration.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.tt create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Number.ttinclude create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Reals.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Reals.tt create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.SkipDrain.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.Strings.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoder.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackDecoderOptionsBuilder.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackEncoder.Integers.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackEncoder.Integers.tt create mode 100644 src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackEncoder.Strings.tt create mode 100644 src/MsgPack.Core/Internal/MessagePackEncoder.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackEncoderOptionsBuilder.cs create mode 100644 src/MsgPack.Core/Internal/MessagePackThrow.cs create mode 100644 src/MsgPack.Core/Internal/MsgPackStringTrie`1.cs create mode 100644 src/MsgPack.Core/MsgPack.Core.csproj create mode 100644 src/MsgPack.Core/_SampleObject.cs create mode 100644 src/MsgPack.Extensions/MsgPack.Extensions.csproj create mode 100644 src/MsgPack.Json/Json/JsonEncoder.cs create mode 100644 src/MsgPack.Json/MsgPack.Json.csproj create mode 100644 src/MsgPack.Serialization.Extensions/MsgPack.Serialization.Extensions.csproj create mode 100644 src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj create mode 100644 src/MsgPack.Serialization.SourceGeneration/MsgPack.Serialization.SourceGeneration.csproj create mode 100644 src/MsgPack.Serialization/MsgPack.Serialization.csproj diff --git a/doc/v2.md b/doc/v2.md new file mode 100644 index 000000000..7f1c3ddd7 --- /dev/null +++ b/doc/v2.md @@ -0,0 +1,49 @@ +MessagePack for CLI v2 +=== + +Goals +--- + +* Supports .NET Framework 3.5 and 4.8, .NET Core 2.1 and 3.1, Xamarin Android and iOS, UWP, Unity. +* Supports stream based serialization/deserialization for large blobs like v1. +* Supports various format as possible including JSON, CBOF, protbuf, etc. +* Keep high level API compatiblity with v1. +* Improve performance. + +Non-Goals +--- + +* 100% API compatibility with v1. + * Custom `Packer` and custom `Unpacker` will not be supported. +* ABI compatibility with v1. +* Keep performance for compatibility layer espetially custom serializer, IPackable/IUnpackable, and async operation. +* Legacy platform support including Silverlight, .NET Standard 1.x, etc. +* High performance in old platform such as .NET Framework and .NET Core 2.1. +* 100% Unity stability. + +Status +--- + +* [ ] Encoder/Decoder layer utlizing `ReadOnlySequence` and `IBufferWriter` +* [ ] Simple JSON serializer with some tweak points for design verification. \[in progress] +* [ ] Debugging features +* [ ] Reflection based object serializer +* [ ] Code generator w/ CodDOM or Roslyn +* [ ] ILGenerator based object serializer +* [ ] `SerializationContext` +* [ ] Known serializers +* [ ] Timestamp +* [ ] Multi demensional array +* [ ] Polymorphism +* [ ] v1 compatiblity layer + * [ ] `MessagePackSerializer` + * [ ] `MessagePackObject` + * [ ] `Packer` and `Unpacker` + * [ ] `IPackable` and `IUnpackable` + * other stuff... +* [ ] msgpack for C# compatiblity such as LZ4 support +* [ ] More formats + * [ ] Completes JSON serializer + * [ ] CBOF serializer + * [ ] Protobuf serializer + * other formats... diff --git a/src/MsgPack.Abstraction/InsufficientInputException.cs b/src/MsgPack.Abstraction/InsufficientInputException.cs new file mode 100644 index 000000000..ea02c15ae --- /dev/null +++ b/src/MsgPack.Abstraction/InsufficientInputException.cs @@ -0,0 +1,23 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack +{ + /// + /// An exception thrown if there are no more inputs when be requested. + /// + public sealed class InsufficientInputException : Exception + { + public InsufficientInputException() + : this("There are no more inputs.") { } + + public InsufficientInputException(string? message) + : base(message) { } + + public InsufficientInputException(string? message, Exception? innerException) + : base(message, innerException) { } + } +} diff --git a/src/MsgPack.Abstraction/Internal/CollectionContext.cs b/src/MsgPack.Abstraction/Internal/CollectionContext.cs new file mode 100644 index 000000000..8b2cdb5d5 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/CollectionContext.cs @@ -0,0 +1,52 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using MsgPack.Serialization; + +namespace MsgPack.Internal +{ + public partial struct CollectionContext + { + public static CollectionContext Default => + new CollectionContext( + DeserializationOptionsBuilder.DefaultMaxArrayLength, + DeserializationOptionsBuilder.DefaultMaxMapCount, + DeserializationOptionsBuilder.DefaultMaxDepth, + currentDepth: 0 + ); + + public int MaxArrayLength { get; } + public int MaxMapCount { get; } + public int MaxDepth { get; } + public int CurrentDepth { get; private set; } + + internal CollectionContext(int maxArrayLength, int maxMapCount, int maxDepth, int currentDepth) + { + this.MaxArrayLength = maxArrayLength; + this.MaxMapCount = maxMapCount; + this.MaxDepth = Ensure.IsNotLessThan(maxDepth, 1); + this.CurrentDepth = currentDepth; + } + + public int IncrementDepth() + { + if (this.CurrentDepth == this.MaxDepth) + { + Throw.DepthExeeded(this.CurrentDepth, this.MaxDepth); + } + + return this.CurrentDepth++; + } + + public int DecrementDepth() + { + if (this.CurrentDepth == 0) + { + Throw.DepthUnderflow(); + } + + return this.CurrentDepth--; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs b/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs new file mode 100644 index 000000000..ddf24be47 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs @@ -0,0 +1,84 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + public struct CollectionItemIterator + { + public delegate bool CollectionEndDetection(in SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint); + + private readonly long _itemsCount; + private long _nextItemIndex; + private readonly CollectionEndDetection _collectionEnds; + + public CollectionItemIterator( + CollectionEndDetection moveNext, + long itemsCount + ) + { + this._collectionEnds = Ensure.NotNull(moveNext); + this._itemsCount = itemsCount; + this._nextItemIndex = 0; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public bool CollectionEnds(in SequenceReader source) + { + var result = this._collectionEnds(source, ref this._nextItemIndex, this._itemsCount, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInputForDetectCollectionEnds(source.Consumed, requestHint); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public bool CollectionEnds(in SequenceReader source, out int requestHint) + => this._collectionEnds(source, ref this._nextItemIndex, this._itemsCount, out requestHint); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public bool CollectionEnds(ReadOnlyMemory source, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(source)); + return this.CollectionEnds(reader, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void Drain(in SequenceReader source) + { + if (!this.Drain(source, out var requestHint)) + { + Throw.InsufficientInputForDrainCollectionItems(source.Consumed, requestHint); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public bool Drain(in SequenceReader source, out int requestHint) + { + while (!this.CollectionEnds(source, out requestHint)) + { + if (requestHint != 0) + { + return false; + } + } + + return true; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public bool Drain(ref ReadOnlyMemory source, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(source)); + var ends = this.Drain(reader, out requestHint); + source = source.Slice((int)reader.Consumed); + return ends; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/CollectionType.cs b/src/MsgPack.Abstraction/Internal/CollectionType.cs new file mode 100644 index 000000000..1875b8472 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/CollectionType.cs @@ -0,0 +1,67 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + /// + /// Represents collection type. + /// + public readonly struct CollectionType : IEquatable + { + public static readonly CollectionType None = default; + public static readonly CollectionType Array = new CollectionType(1); + public static readonly CollectionType Map = new CollectionType(2); + + private readonly int _type; + + public bool IsArray + { + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + get => this._type == 1; + } + + public bool IsMap + { + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + get => this._type == 2; + } + + public bool IsNone + { + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + get => this._type == 0; + } + + private CollectionType(int type) + { + this._type = type; + } + + public override bool Equals(object? obj) + => obj is CollectionType other ? this.Equals(other) : false; + + public bool Equals(CollectionType other) + => this._type == other._type; + + public override int GetHashCode() + => this._type; + + public override string ToString() + => this._type switch + { + 1 => "Array", + 2 => "Map", + _ => String.Empty + }; + + public static bool operator ==(CollectionType left, CollectionType right) + => left.Equals(right); + + public static bool operator !=(CollectionType left, CollectionType right) + => !left.Equals(right); + } +} diff --git a/src/MsgPack.Abstraction/Internal/Decoder.Primitives.cs b/src/MsgPack.Abstraction/Internal/Decoder.Primitives.cs new file mode 100644 index 000000000..9d4066b1a --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Decoder.Primitives.cs @@ -0,0 +1,1019 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class Decoder + { + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Byte DecodeByte(in SequenceReader source) + { + var result = this.DecodeByte(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Byte), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract Byte DecodeByte(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Byte? DecodeNullableByte(in SequenceReader source) + { + var result = this.DecodeNullableByte(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Byte), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract Byte? DecodeNullableByte(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Int16 DecodeInt16(in SequenceReader source) + { + var result = this.DecodeInt16(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Int16), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract Int16 DecodeInt16(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Int16? DecodeNullableInt16(in SequenceReader source) + { + var result = this.DecodeNullableInt16(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Int16), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract Int16? DecodeNullableInt16(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Int32 DecodeInt32(in SequenceReader source) + { + var result = this.DecodeInt32(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Int32), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract Int32 DecodeInt32(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Int32? DecodeNullableInt32(in SequenceReader source) + { + var result = this.DecodeNullableInt32(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Int32), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract Int32? DecodeNullableInt32(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Int64 DecodeInt64(in SequenceReader source) + { + var result = this.DecodeInt64(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Int64), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract Int64 DecodeInt64(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Int64? DecodeNullableInt64(in SequenceReader source) + { + var result = this.DecodeNullableInt64(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Int64), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract Int64? DecodeNullableInt64(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public SByte DecodeSByte(in SequenceReader source) + { + var result = this.DecodeSByte(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(SByte), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract SByte DecodeSByte(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public SByte? DecodeNullableSByte(in SequenceReader source) + { + var result = this.DecodeNullableSByte(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(SByte), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract SByte? DecodeNullableSByte(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public UInt16 DecodeUInt16(in SequenceReader source) + { + var result = this.DecodeUInt16(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(UInt16), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract UInt16 DecodeUInt16(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public UInt16? DecodeNullableUInt16(in SequenceReader source) + { + var result = this.DecodeNullableUInt16(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(UInt16), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract UInt16? DecodeNullableUInt16(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public UInt32 DecodeUInt32(in SequenceReader source) + { + var result = this.DecodeUInt32(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(UInt32), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract UInt32 DecodeUInt32(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public UInt32? DecodeNullableUInt32(in SequenceReader source) + { + var result = this.DecodeNullableUInt32(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(UInt32), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract UInt32? DecodeNullableUInt32(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public UInt64 DecodeUInt64(in SequenceReader source) + { + var result = this.DecodeUInt64(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(UInt64), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract UInt64 DecodeUInt64(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public UInt64? DecodeNullableUInt64(in SequenceReader source) + { + var result = this.DecodeNullableUInt64(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(UInt64), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract UInt64? DecodeNullableUInt64(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Single DecodeSingle(in SequenceReader source) + { + var result = this.DecodeSingle(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Single), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract Single DecodeSingle(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Single? DecodeNullableSingle(in SequenceReader source) + { + var result = this.DecodeNullableSingle(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Single), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract Single? DecodeNullableSingle(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Double DecodeDouble(in SequenceReader source) + { + var result = this.DecodeDouble(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Double), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract Double DecodeDouble(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Double? DecodeNullableDouble(in SequenceReader source) + { + var result = this.DecodeNullableDouble(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Double), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract Double? DecodeNullableDouble(in SequenceReader source, out int requestHint); + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Boolean DecodeBoolean(in SequenceReader source) + { + var result = this.DecodeBoolean(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Boolean), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract Boolean DecodeBoolean(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Boolean? DecodeNullableBoolean(in SequenceReader source) + { + var result = this.DecodeNullableBoolean(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(Boolean), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract Boolean? DecodeNullableBoolean(in SequenceReader source, out int requestHint); + + } +} diff --git a/src/MsgPack.Abstraction/Internal/Decoder.Primitives.tt b/src/MsgPack.Abstraction/Internal/Decoder.Primitives.tt new file mode 100644 index 000000000..4a4be9e47 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Decoder.Primitives.tt @@ -0,0 +1,130 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class Decoder + { +<# +foreach (var outputType in new [] { + "Byte", + "Int16", + "Int32", + "Int64", + "SByte", + "UInt16", + "UInt32", + "UInt64", + "Single", + "Double", + "Boolean" +}) +{ +#> + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public <#= outputType #> Decode<#= outputType #>(in SequenceReader source) + { + var result = this.Decode<#= outputType #>(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(<#= outputType #>), requestHint); + } + + return result; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract <#= outputType #> Decode<#= outputType #>(in SequenceReader source, out int requestHint); + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public <#= outputType #>? DecodeNullable<#= outputType #>(in SequenceReader source) + { + var result = this.DecodeNullable<#= outputType #>(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(<#= outputType #>), requestHint); + } + + return result; + } + + /// + /// Encodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract <#= outputType #>? DecodeNullable<#= outputType #>(in SequenceReader source, out int requestHint); + +<# +} +#> + } +} diff --git a/src/MsgPack.Abstraction/Internal/Decoder.Strings.cs b/src/MsgPack.Abstraction/Internal/Decoder.Strings.cs new file mode 100644 index 000000000..78a9715d2 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Decoder.Strings.cs @@ -0,0 +1,315 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; + +namespace MsgPack.Internal +{ + partial class Decoder + { + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM. + /// to cancel long running operation. This value can be omitted. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public String DecodeString(in SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + var result = this.DecodeString(source, out var requestHint, encoding, cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInputForString(source.Consumed, typeof(String), encoding, requestHint); + } + + return result!; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM. + /// to cancel long running operation. This value can be omitted. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract String? DecodeString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); + + /// + /// Decodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public String? DecodeNullableString(in SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + var result = this.DecodeNullableString(source, out var requestHint, encoding, cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInputForString(source.Consumed, typeof(String), encoding, requestHint); + } + + return result; + } + + /// + /// Decodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract String? DecodeNullableString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); + + +#if FEATURE_UTF8STRING + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM. + /// to cancel long running operation. This value can be omitted. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Utf8String DecodeUtf8String(in SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + var result = this.DecodeUtf8String(source, out var requestHint, encoding, cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInputForUtf8String(source.Consumed, typeof(Utf8String), encoding, requestHint); + } + + return result!; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM. + /// to cancel long running operation. This value can be omitted. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract Utf8String? DecodeUtf8String(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); + + /// + /// Decodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Utf8String? DecodeNullableUtf8String(in SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + var result = this.DecodeNullableUtf8String(source, out var requestHint, encoding, cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInputForUtf8String(source.Consumed, typeof(Utf8String), encoding, requestHint); + } + + return result; + } + + /// + /// Decodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract Utf8String? DecodeNullableUtf8String(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); + + +#endif // FEATURE_UTF8STRING + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// to cancel long running operation. This value can be omitted. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public byte[] DecodeBinary(in SequenceReader source, CancellationToken cancellationToken = default) + { + var result = this.DecodeBinary(source, out var requestHint, cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(byte[]), requestHint); + } + + return result!; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// to cancel long running operation. This value can be omitted. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract byte[]? DecodeBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default); + + /// + /// Decodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public byte[]? DecodeNullableBinary(in SequenceReader source, CancellationToken cancellationToken = default) + { + var result = this.DecodeNullableBinary(source, out var requestHint, cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInput(source.Consumed, typeof(byte[]), requestHint); + } + + return result; + } + + /// + /// Decodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract byte[]? DecodeNullableBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default); + + } +} diff --git a/src/MsgPack.Abstraction/Internal/Decoder.Strings.tt b/src/MsgPack.Abstraction/Internal/Decoder.Strings.tt new file mode 100644 index 000000000..4b7a3da9e --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Decoder.Strings.tt @@ -0,0 +1,194 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; + +namespace MsgPack.Internal +{ + partial class Decoder + { +<# +foreach (var spec in new [] { + new + { + Name = "String", Type = "String", ThrowSuffix = "ForString", + ExtraParameters = "Encoding? encoding = null, ", + ExtraParameterDocs = new [] { "Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM." }, + ExtraArguments = "encoding, ", IfDef = String.Empty + }, + new + { + Name = "Utf8String", Type = "Utf8String", ThrowSuffix = "ForUtf8String", + ExtraParameters = "Encoding? encoding = null, ", + ExtraParameterDocs = new [] { "Specify charactor encoding. This value can be omitted, and default is UTF-8 without BOM." }, + ExtraArguments = "encoding, ", IfDef = "FEATURE_UTF8STRING" + }, + new + { + Name = "Binary", Type = "byte[]", ThrowSuffix = String.Empty, + ExtraParameters = String.Empty, + ExtraParameterDocs = Array.Empty(), + ExtraArguments = String.Empty, IfDef = String.Empty + }, +}) +{ + if (!String.IsNullOrEmpty(spec.IfDef)) + { +#> + +#if <#= spec.IfDef #> + +<# + } +#> + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. +<# + foreach (var doc in spec.ExtraParameterDocs) + { +#> + /// <#= doc #> +<# + } +#> + /// to cancel long running operation. This value can be omitted. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public <#= spec.Type #> Decode<#= spec.Name #>(in SequenceReader source, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default) + { + var result = this.Decode<#= spec.Name #>(source, out var requestHint, <#= spec.ExtraArguments #>cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInput<#= spec.ThrowSuffix #>(source.Consumed, typeof(<#= spec.Type #>), <#= spec.ExtraArguments #>requestHint); + } + + return result!; + } + + /// + /// Decodes value from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// +<# + foreach (var doc in spec.ExtraParameterDocs) + { +#> + /// <#= doc #> +<# + } +#> + /// to cancel long running operation. This value can be omitted. + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + public abstract <#= spec.Type #>? Decode<#= spec.Name #>(in SequenceReader source, out int requestHint, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default); + + /// + /// Decodes value or null from specified sequence. + /// + /// SequenceReader<byte>. +<# + foreach (var doc in spec.ExtraParameterDocs) + { +#> + /// <#= doc #> +<# + } +#> + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public <#= spec.Type #>? DecodeNullable<#= spec.Name #>(in SequenceReader source, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default) + { + var result = this.DecodeNullable<#= spec.Name #>(source, out var requestHint, <#= spec.ExtraArguments #>cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInput<#= spec.ThrowSuffix #>(source.Consumed, typeof(<#= spec.Type #>), <#= spec.ExtraArguments #>requestHint); + } + + return result; + } + + /// + /// Decodes value or null from specified sequence. + /// + /// SequenceReader<byte>. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// +<# + foreach (var doc in spec.ExtraParameterDocs) + { +#> + /// <#= doc #> +<# + } +#> + /// + /// Decoded value if this method succeeds to decode value; Default value when does not contain enough bytes to decode. + /// If this value is default, will not be advanced. + /// + /// + /// The caller must concatinate old remaining sequence and new sequence when this method returns false and then recall. + /// + /// The underlying format value is not compatible to type. + /// The underlying format does not suppor this type. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract <#= spec.Type #>? DecodeNullable<#= spec.Name #>(in SequenceReader source, out int requestHint, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default); + +<# + if (!String.IsNullOrEmpty(spec.IfDef)) + { +#> + +#endif // <#= spec.IfDef #> + +<# + } +} +#> + } +} diff --git a/src/MsgPack.Abstraction/Internal/Decoder.cs b/src/MsgPack.Abstraction/Internal/Decoder.cs new file mode 100644 index 000000000..1b5c814da --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Decoder.cs @@ -0,0 +1,226 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace MsgPack.Internal +{ + public abstract partial class Decoder +#warning TODO: Use 'Try' prefix for published APIs which can return 'requestHint'. + + { + public FormatFeatures FormatFeatures { get; } + + public DecoderOptions Options { get; } + + protected Decoder(DecoderOptions options, FormatFeatures formatFeatures) + { + this.Options = Ensure.NotNull(options); + this.FormatFeatures = Ensure.NotNull(formatFeatures); + } + + public void Skip(in SequenceReader source, in CollectionContext collectionContext, CancellationToken cancellationToken = default) + { + this.Skip(source, collectionContext, out var requestHint, cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInputForSkip(source.Consumed, requestHint); + } + } + + public abstract void Skip(in SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public bool TryPeek(in SequenceReader source, out byte value) => source.TryRead(out value); + + public abstract ElementType DecodeItem(in SequenceReader source, out ReadOnlySequence valueOrLength, out int requestHint, CancellationToken cancellationToken = default); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void GetRawString(in SequenceReader source, out ReadOnlySpan rawString, CancellationToken cancellationToken = default) + { + if (!this.GetRawString(source, out rawString, out var requestHint, cancellationToken)) + { + Throw.InsufficientInputForRawString(source.Consumed, requestHint); + } + } + + public abstract bool GetRawString(in SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default); + + /// + /// Decodes current data as array or map header, and returns the items count if known. + /// + /// Reader of source byte sequence. If and only if this method succeeds, the reader will be advanced. + /// Items count if known; -1 if underlying format does not contain any count information; 0 if underlying format is not an array nor a map. + /// + /// for array, for map (dictionary). + /// This method does not return anything else, but may throw an exception. + /// + /// The decoded value is not an array nor a map. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out long itemsCount) + { + var result = this.DecodeArrayOrMapHeader(source, out itemsCount, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInputForDecodeArrayOrMapHeader(source.Consumed, requestHint); + } + + return result; + } + + /// + /// Decodes current data as array or map header, and returns the items count if known. + /// + /// Reader of source byte sequence. If and only if this method succeeds, the reader will be advanced. + /// Items count if known; -1 if underlying format does not contain any count information; 0 if underlying format is not an array nor a map. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// for array, for map (dictionary), or if there were not enough bytes to decode. + /// This method does not return anything else, but may throw an exception. + /// + /// The decoded value is not an array nor a map. + public abstract CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out long itemsCount, out int requestHint); + + /// + /// Decodes current data as array header, and returns the items count if known. + /// + /// Reader of source byte sequence. If and only if this method succeeds, the reader will be advanced. + /// + /// Items count if known; -1 if underlying format does not contain any count information. + /// Note that 0 is valid value when the array is empty. + /// + /// The decoded value is not an array. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public long DecodeArrayHeader(in SequenceReader source) + { + var result = this.DecodeArrayHeader(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInputForDecodeArrayHeader(source.Consumed, requestHint); + } + + return result; + } + + /// + /// Decodes current data as array header, and returns the items count if known. + /// + /// Reader of source byte sequence. If and only if this method succeeds, the reader will be advanced. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Items count if known; -1 if underlying format does not contain any count information. + /// Note that 0 is valid value when the array is empty. + /// + /// The decoded value is not an array. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public abstract long DecodeArrayHeader(in SequenceReader source, out int requestHint); + + /// + /// Decodes current data as map header, and returns the items count if known. + /// + /// Reader of source byte sequence. If and only if this method succeeds, the reader will be advanced. + /// + /// Items count if known; -1 if underlying format does not contain any count information. + /// Note that 0 is valid value when the map is empty. + /// + /// The decoded value is not a map. + /// does not contain enough bytes to decode. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public long DecodeMapHeader(in SequenceReader source) + { + var result = this.DecodeMapHeader(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInputForDecodeMapHeader(source.Consumed, requestHint); + } + + return result; + } + + /// + /// Decodes current data as map header, and returns the items count if known. + /// + /// Reader of source byte sequence. If and only if this method succeeds, the reader will be advanced. + /// + /// 0 if this method succeeds to decode value; Positive integer when does not contain enough bytes to decode, and required memory bytes hint is stored. + /// Note that -1 represents unknown size. If so, caller must supply new buffer with most efficient size. + /// + /// + /// Items count if known; -1 if underlying format does not contain any count information. + /// Note that 0 is valid value when the map is empty. + /// + /// The decoded value is not a map. + public abstract long DecodeMapHeader(in SequenceReader source, out int requestHint); + + public virtual void DecodeExtension(in SequenceReader source, out byte typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) + { + Throw.ExtensionsIsNotSupported(); + // never + body = default; + requestHint = -1; + typeCode = default; + } + + public CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator) + { + var result = this.DecodeArrayOrMap(source, out iterator, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInputForDecodeArrayOrMapHeader(source.Consumed, requestHint); + } + + return result; + } + + public abstract CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator, out int requestHint); + + public CollectionItemIterator DecodeArray(in SequenceReader source) + { + var result = this.DecodeArray(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInputForDecodeArrayHeader(source.Consumed, requestHint); + } + + return result; + } + + public abstract CollectionItemIterator DecodeArray(in SequenceReader source, out int requestHint); + + public CollectionItemIterator DecodeMap(in SequenceReader source) + { + var result = this.DecodeMap(source, out var requestHint); + if (requestHint != 0) + { + Throw.InsufficientInputForDecodeMapHeader(source.Consumed, requestHint); + } + + return result; + } + + public abstract CollectionItemIterator DecodeMap(in SequenceReader source, out int requestHint); + + public void Drain(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, CancellationToken cancellationToken = default) + { + this.Drain(source, collectionContext, itemsCount, out var requestHint, cancellationToken); + if (requestHint != 0) + { + Throw.InsufficientInputForDecodeMapHeader(source.Consumed, requestHint); + } + } + + public abstract void Drain(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default); + } +} diff --git a/src/MsgPack.Abstraction/Internal/DecoderOptions.cs b/src/MsgPack.Abstraction/Internal/DecoderOptions.cs new file mode 100644 index 000000000..9e333bc2c --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/DecoderOptions.cs @@ -0,0 +1,47 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Internal +{ + public abstract class DecoderOptions + { + public bool CanTreatRealAsInteger { get; } + + public int CancellationSupportThreshold { get; } + + public ArrayPool ByteBufferPool { get; } + + public ArrayPool CharBufferPool { get; } + + public int MaxByteBufferLength { get; } + + public int MaxCharBufferLength { get; } + + public int MaxNumberLengthInBytes { get; } + + public int MaxStringLengthInBytes { get; } + + public int MaxBinaryLengthInBytes { get; } + + public bool ClearsBuffer { get; } + + protected DecoderOptions(DecoderOptionsBuilder builder) + { + builder = Ensure.NotNull(builder); + + this.CanTreatRealAsInteger = builder.CanTreatRealAsInteger; + this.CancellationSupportThreshold = builder.CancellationSupportThreshold; + this.ClearsBuffer = builder.ClearsBuffer; + this.ByteBufferPool = builder.ByteBufferPool; + this.CharBufferPool = builder.CharBufferPool; + this.MaxByteBufferLength = builder.MaxByteBufferLength; + this.MaxCharBufferLength = builder.MaxCharBufferLength; + this.MaxNumberLengthInBytes = builder.MaxNumberLengthInBytes; + this.MaxStringLengthInBytes = builder.MaxStringLengthInBytes; + this.MaxBinaryLengthInBytes = builder.MaxBinaryLengthInBytes; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs b/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs new file mode 100644 index 000000000..893cff502 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs @@ -0,0 +1,93 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Internal +{ + public abstract class DecoderOptionsBuilder + { + public bool CanTreatRealAsInteger { get; set; } = OptionsDefaults.CanTreatRealAsInteger; + + private int _cancellationSupportThreshold = OptionsDefaults.CancellationSupportThreshold; + + public int CancellationSupportThreshold + { + get => this._cancellationSupportThreshold; + set => this._cancellationSupportThreshold = Ensure.IsNotLessThan(value, 1); + } + + private int _maxNumberLengthInBytes = OptionsDefaults.MaxNumberLengthInBytes; + + public int MaxNumberLengthInBytes + { + get => this._maxNumberLengthInBytes; + set => this._maxNumberLengthInBytes = Ensure.IsNotLessThan(value, 1); + } + + private int _maxStringLengthInBytes = OptionsDefaults.MaxStringLengthInBytes; + + public int MaxStringLengthInBytes + { + get => this._maxStringLengthInBytes; + set => this._maxStringLengthInBytes = Ensure.IsNotLessThan(value, 1); + } + + private int _naxBinaryLengthInBytes = OptionsDefaults.MaxBinaryLengthInBytes; + + public int MaxBinaryLengthInBytes + { + get => this._naxBinaryLengthInBytes; + set => this._naxBinaryLengthInBytes = Ensure.IsNotLessThan(value, 1); + } + + private int _maxByteBufferLength = OptionsDefaults.MaxByteBufferLength; + + public int MaxByteBufferLength + { + get => this._maxByteBufferLength; + set => this._maxByteBufferLength = Ensure.IsNotLessThan(value, 4); + } + + private int _maxCharBufferLength = OptionsDefaults.MaxCharBufferLength; + + public int MaxCharBufferLength + { + get => this._maxCharBufferLength; + set => this._maxCharBufferLength = Ensure.IsNotLessThan(value, 2); + } + + private ArrayPool _byteBufferPool = OptionsDefaults.ByteBufferPool; + + public ArrayPool ByteBufferPool + { + get => this._byteBufferPool; + set => this._byteBufferPool = Ensure.NotNull(value); + } + + private ArrayPool _charBufferPool = OptionsDefaults.CharBufferPool; + + public ArrayPool CharBufferPool + { + get => this._charBufferPool; + set => this._charBufferPool = Ensure.NotNull(value); + } + + public bool ClearsBuffer { get; set; } = OptionsDefaults.ClearsBuffer; + + protected DecoderOptionsBuilder() { } + + public DecoderOptionsBuilder WithoutBufferClear() + { + this.ClearsBuffer = false; + return this; + } + + public DecoderOptionsBuilder ProhibitTreatRealAsInteger() + { + this.CanTreatRealAsInteger = false; + return this; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/ElementType.cs b/src/MsgPack.Abstraction/Internal/ElementType.cs new file mode 100644 index 000000000..4c69521a5 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/ElementType.cs @@ -0,0 +1,25 @@ +using System; + +namespace MsgPack.Internal +{ + public enum ElementType + { + None = 0, + Int32 = 1, + Int64 = 2, + UInt64 = 3, + Single = 4, + Double = 5, + True = 6, + False = 7, + Null = 8, + Array = 0x11, + Map = 0x12, + String = 0x31, + Binary = 0x32, + Extension = 0x41, + Whitespace = 0x51, + Comment = 0x52, + OtherTrivia = 0x5F + } +} diff --git a/src/MsgPack.Abstraction/Internal/Encoder.Primitives.cs b/src/MsgPack.Abstraction/Internal/Encoder.Primitives.cs new file mode 100644 index 000000000..3b66f1243 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Encoder.Primitives.cs @@ -0,0 +1,228 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class Encoder + { + /// + /// Encodes value to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + /// is null. + /// The underlying format does not suppor this type. + public abstract void EncodeInt32(Int32 value, IBufferWriter buffer); + + /// + /// Encodes value or null to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeInt32(Int32? value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + if (value == null) + { + this.EncodeNull(buffer); + } + else + { + this.EncodeInt32(value.GetValueOrDefault(), buffer); + } + } + /// + /// Encodes value to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + /// is null. + /// The underlying format does not suppor this type. + public abstract void EncodeInt64(Int64 value, IBufferWriter buffer); + + /// + /// Encodes value or null to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeInt64(Int64? value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + if (value == null) + { + this.EncodeNull(buffer); + } + else + { + this.EncodeInt64(value.GetValueOrDefault(), buffer); + } + } + /// + /// Encodes value to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + /// is null. + /// The underlying format does not suppor this type. + public abstract void EncodeUInt32(UInt32 value, IBufferWriter buffer); + + /// + /// Encodes value or null to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeUInt32(UInt32? value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + if (value == null) + { + this.EncodeNull(buffer); + } + else + { + this.EncodeUInt32(value.GetValueOrDefault(), buffer); + } + } + /// + /// Encodes value to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + /// is null. + /// The underlying format does not suppor this type. + public abstract void EncodeUInt64(UInt64 value, IBufferWriter buffer); + + /// + /// Encodes value or null to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeUInt64(UInt64? value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + if (value == null) + { + this.EncodeNull(buffer); + } + else + { + this.EncodeUInt64(value.GetValueOrDefault(), buffer); + } + } + /// + /// Encodes value to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + /// is null. + /// The underlying format does not suppor this type. + public abstract void EncodeSingle(Single value, IBufferWriter buffer); + + /// + /// Encodes value or null to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeSingle(Single? value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + if (value == null) + { + this.EncodeNull(buffer); + } + else + { + this.EncodeSingle(value.GetValueOrDefault(), buffer); + } + } + /// + /// Encodes value to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + /// is null. + /// The underlying format does not suppor this type. + public abstract void EncodeDouble(Double value, IBufferWriter buffer); + + /// + /// Encodes value or null to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeDouble(Double? value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + if (value == null) + { + this.EncodeNull(buffer); + } + else + { + this.EncodeDouble(value.GetValueOrDefault(), buffer); + } + } + /// + /// Encodes value to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + /// is null. + /// The underlying format does not suppor this type. + public abstract void EncodeBoolean(Boolean value, IBufferWriter buffer); + + /// + /// Encodes value or null to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeBoolean(Boolean? value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + if (value == null) + { + this.EncodeNull(buffer); + } + else + { + this.EncodeBoolean(value.GetValueOrDefault(), buffer); + } + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/Encoder.Primitives.tt b/src/MsgPack.Abstraction/Internal/Encoder.Primitives.tt new file mode 100644 index 000000000..c94109f3e --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Encoder.Primitives.tt @@ -0,0 +1,65 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class Encoder + { +<# +foreach (var inputType in new [] { + "Int32", + "Int64", + "UInt32", + "UInt64", + "Single", + "Double", + "Boolean" +}) +{ +#> + /// + /// Encodes value to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + /// is null. + /// The underlying format does not suppor this type. + public abstract void Encode<#= inputType #>(<#= inputType #> value, IBufferWriter buffer); + + /// + /// Encodes value or null to specified buffer. + /// The implementation will choose most compact format. + /// + /// Value to be encoded. + /// IBufferWriter<byte>. + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void Encode<#= inputType #>(<#= inputType #>? value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + if (value == null) + { + this.EncodeNull(buffer); + } + else + { + this.Encode<#= inputType #>(value.GetValueOrDefault(), buffer); + } + } +<# +} +#> + } +} diff --git a/src/MsgPack.Abstraction/Internal/Encoder.cs b/src/MsgPack.Abstraction/Internal/Encoder.cs new file mode 100644 index 000000000..6ef3a7360 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Encoder.cs @@ -0,0 +1,175 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; + +namespace MsgPack.Internal +{ + /// + /// Defines an interface and basic functionarity of stateless . + /// + /// + /// The is stateless, so caller (serializer, reader, etc.) can cache the instance for performance. + /// + public abstract partial class Encoder + { + public EncoderOptions Options { get; } + + protected Encoder(EncoderOptions options) + { + this.Options = Ensure.NotNull(options); + } + + public abstract void EncodeNull(IBufferWriter buffer); + +#if FEATURE_UTF8STRING + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeString(Utf8String? value, IBufferWriter buffer) + => this.EncodeString(value.AsBytes(), buffer); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeString(Utf8Span value, IBufferWriter buffer) + => this.EncodeString(value.AsBytes(), buffer); +#endif // FEATURE_UTF8STRING + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeString(string? value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + if (value == null) + { + this.EncodeNull(buffer); + return; + } + + this.EncodeString(value.AsSpan(), buffer, encoding, cancellationToken); + } + + public abstract void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default); + + public abstract void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default); + + public abstract void EncodeString(ReadOnlySpan encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default); + + public abstract void EncodeString(in ReadOnlySequence encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default); + + public abstract void EncodeBinary(ReadOnlySpan value, IBufferWriter buffer, CancellationToken cancellationToken = default); + + public abstract void EncodeBinary(in ReadOnlySequence value, IBufferWriter buffer, CancellationToken cancellationToken = default); + + public abstract void EncodeArrayStart(int length, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeArrayEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeArrayItemStart(int index, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeArrayItemEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeMapStart(int length, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeMapEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeMapKeyStart(int index, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeMapKeyEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeMapValueStart(int index, IBufferWriter buffer, in CollectionContext collectionContext); + + public abstract void EncodeMapValueEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext); + + public virtual void EncodeExtension(sbyte typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) + => Throw.ExtensionsIsNotSupported(); + + public virtual void EncodeExtension(sbyte typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) + => Throw.ExtensionsIsNotSupported(); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void WriteRaw(ReadOnlySpan value, IBufferWriter buffer, CancellationToken cancellationToken = default) + { + if (value.Length <= this.Options.CancellationSupportThreshold) + { + buffer.Write(value); + } + else + { + WriteRawSlow(buffer, value, buffer.GetSpan(), cancellationToken); + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void WriteRawSlow(IBufferWriter buffer, in ReadOnlySpan source, Span destination, CancellationToken cancellationToken) + { + // https://github.com/dotnet/runtime/blob/af1db3eccbc238745e1d163458c92c1bfa650fbd/src/libraries/System.Memory/src/System/Buffers/BuffersExtensions.cs#L133 + ReadOnlySpan input = source; + while (true) + { + if (destination.IsEmpty) + { + ThrowBufferWriterReturnsEmptyBuffer(nameof(buffer)); + } + + var writeSize = Math.Min(destination.Length, input.Length); + input.Slice(0, writeSize).CopyTo(destination); + buffer.Advance(writeSize); + input = input.Slice(writeSize); + if (input.Length > 0) + { + destination = buffer.GetSpan(); + + cancellationToken.ThrowIfCancellationRequested(); + continue; + } + + return; + } + } + + private static void ThrowBufferWriterReturnsEmptyBuffer(string paramName) + => throw new ArgumentOutOfRangeException(paramName); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void WriteRaw(in ReadOnlySequence value, IBufferWriter buffer, CancellationToken cancellationToken = default) + { + buffer = EnsureNotNull(buffer); + + if (value.Length <= this.Options.MaxByteBufferLength) + { + var length32 = unchecked((int)value.Length); + var span = buffer.GetSpan(length32); + value.CopyTo(span); + buffer.Advance(length32); + return; + } + + WriteRawSlow(value, buffer, cancellationToken); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void WriteRawSlow(in ReadOnlySequence value, IBufferWriter buffer, CancellationToken cancellationToken = default) + { + var reader = new SequenceReader(value); + while (!reader.End) + { + cancellationToken.ThrowIfCancellationRequested(); + + var sink = buffer.GetSpan(); + if (sink.IsEmpty) + { + ThrowBufferWriterReturnsEmptyBuffer(nameof(buffer)); + } + + var source = reader.UnreadSpan.Slice(0, Math.Min(sink.Length, reader.UnreadSpan.Length)); + source.CopyTo(sink); + buffer.Advance(source.Length); + reader.Advance(source.Length); + } + } + + private protected static IBufferWriter EnsureNotNull(IBufferWriter buffer) + => buffer ?? throw new ArgumentNullException(nameof(buffer)); + } +} diff --git a/src/MsgPack.Abstraction/Internal/EncoderOptions.cs b/src/MsgPack.Abstraction/Internal/EncoderOptions.cs new file mode 100644 index 000000000..8ec9bcd3b --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/EncoderOptions.cs @@ -0,0 +1,35 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Internal +{ + public abstract class EncoderOptions + { + public int CancellationSupportThreshold { get; } + + public int MaxByteBufferLength { get; } + + public int MaxCharBufferLength { get; } + + public ArrayPool ByteBufferPool { get; } + + public ArrayPool CharBufferPool { get; } + + public bool ClearsBuffer { get; } + + protected EncoderOptions(EncoderOptionsBuilder builder) + { + builder = Ensure.NotNull(builder); + + this.CancellationSupportThreshold = builder.CancellationSupportThreshold; + this.MaxByteBufferLength = builder.MaxByteBufferLength; + this.MaxCharBufferLength = builder.MaxCharBufferLength; + this.ByteBufferPool = builder.ByteBufferPool; + this.CharBufferPool = builder.CharBufferPool; + this.ClearsBuffer = builder.ClearsBuffer; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/EncoderOptionsBuilder.cs b/src/MsgPack.Abstraction/Internal/EncoderOptionsBuilder.cs new file mode 100644 index 000000000..7fa2d2ef6 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/EncoderOptionsBuilder.cs @@ -0,0 +1,61 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Internal +{ + public abstract class EncoderOptionsBuilder + { + private int _cancellationSupportThreshold = OptionsDefaults.CancellationSupportThreshold; + + public int CancellationSupportThreshold + { + get => this._cancellationSupportThreshold; + set => this._cancellationSupportThreshold = Ensure.IsNotLessThan(value, 1); + } + + private int _maxByteBufferLength = OptionsDefaults.MaxByteBufferLength; + + public int MaxByteBufferLength + { + get => this._maxByteBufferLength; + set => this._maxByteBufferLength = Ensure.IsNotLessThan(value, 4); + } + + private int _maxCharBufferLength = OptionsDefaults.MaxCharBufferLength; + + public int MaxCharBufferLength + { + get => this._maxCharBufferLength; + set => this._maxCharBufferLength = Ensure.IsNotLessThan(value, 2); + } + + private ArrayPool _byteBufferPool = OptionsDefaults.ByteBufferPool; + + public ArrayPool ByteBufferPool + { + get => this._byteBufferPool; + set => this._byteBufferPool = Ensure.NotNull(value); + } + + private ArrayPool _charBufferPool = OptionsDefaults.CharBufferPool; + + public ArrayPool CharBufferPool + { + get => this._charBufferPool; + set => this._charBufferPool = Ensure.NotNull(value); + } + + public bool ClearsBuffer { get; set; } = OptionsDefaults.ClearsBuffer; + + protected EncoderOptionsBuilder() { } + + public EncoderOptionsBuilder WithoutBufferClear() + { + this.ClearsBuffer = false; + return this; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/Ensure.cs b/src/MsgPack.Abstraction/Internal/Ensure.cs new file mode 100644 index 000000000..3927e712f --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Ensure.cs @@ -0,0 +1,45 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + internal static class Ensure + { + [return: NotNull] + public static T NotNull([NotNull]T value, [CallerArgumentExpression("value")]string paramName = null!) + { + if (value == null) + { + throw new ArgumentNullException(paramName); + } + + return value; + } + + public static int IsNotNegative(int value, [CallerArgumentExpression("value")]string paramName = null!) + { + if (value < 0) + { + throw new ArgumentOutOfRangeException(paramName, "Value cannot be negative number."); + } + + return value; + } + + public static T IsNotLessThan(T value, T minInclusive, [CallerArgumentExpression("value")] string paramName = null!) + where T : struct, IComparable + { + if (value.CompareTo(minInclusive) < 0) + { + throw new ArgumentOutOfRangeException(paramName, $"Value cannot be less than {minInclusive}."); + } + + return value; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/FormatFeatures.cs b/src/MsgPack.Abstraction/Internal/FormatFeatures.cs new file mode 100644 index 000000000..f20f4a578 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/FormatFeatures.cs @@ -0,0 +1,20 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Internal +{ + public sealed class FormatFeatures + { + public bool IsContextful { get; } + public bool CanCountCollectionItems { get; } + public bool CanSpecifyStringEncoding { get; } + + internal FormatFeatures(FormatFeaturesBuilder builder) + { + this.IsContextful = builder.IsContextful; + this.CanCountCollectionItems = builder.CanCountCollectionItems; + this.CanSpecifyStringEncoding = builder.CanSpecifyStringEncoding; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs b/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs new file mode 100644 index 000000000..0a5d3ab2b --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs @@ -0,0 +1,15 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Internal +{ + public sealed class FormatFeaturesBuilder + { + public bool IsContextful { get; set; } + public bool CanCountCollectionItems { get; set; } + public bool CanSpecifyStringEncoding { get; set; } + + public FormatFeatures Build() => new FormatFeatures(this); + } +} diff --git a/src/MsgPack.Abstraction/Internal/MethodImplOptionsShim.cs b/src/MsgPack.Abstraction/Internal/MethodImplOptionsShim.cs new file mode 100644 index 000000000..07bf05070 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/MethodImplOptionsShim.cs @@ -0,0 +1,22 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + /// + /// Defines compatibility shims for legacy runtime. + /// + internal static class MethodImplOptionsShim + { + /// + /// Tells JIT to inline aggressively. + /// + /// + /// This value is not defined in .NET Framework 3.5, but the runtime will ignore this flag value. + /// + public const MethodImplOptions AggressiveInlining = (MethodImplOptions)256; + } +} diff --git a/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs b/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs new file mode 100644 index 000000000..c8fa9fbed --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs @@ -0,0 +1,22 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Internal +{ + internal static class OptionsDefaults + { + public static readonly int CancellationSupportThreshold = 128 * 1024 * 1024; // About 0.1 sec in desktop, more for IoT + public static readonly int MaxNumberLengthInBytes = 32; + public static readonly int MaxStringLengthInBytes = 256 * 1024 * 1024; + public static readonly int MaxBinaryLengthInBytes = 256 * 1024 * 1024; + public static readonly int MaxByteBufferLength = 2 * 1024 * 1024; + public static readonly int MaxCharBufferLength = 2 * 1024 * 1024; + public static readonly ArrayPool ByteBufferPool = ArrayPool.Shared; + public static readonly ArrayPool CharBufferPool = ArrayPool.Shared; + public static readonly bool ClearsBuffer = true; + public static readonly bool CanTreatRealAsInteger = true; + } +} diff --git a/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs b/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs new file mode 100644 index 000000000..e320035ad --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs @@ -0,0 +1,112 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.IO; +using System.Threading.Tasks; + +namespace MsgPack.Internal +{ + internal sealed class StreamBufferWriter : IBufferWriter, IAsyncDisposable + { + private readonly Stream _underlying; + private readonly bool _ownsStream; + + private readonly ArrayPool _arrayPool; + private readonly bool _cleansBuffer; + + private byte[] _buffer = null!; + private int _offset; + + private ValueTask _flushTask; + private byte[]? _flushingBuffer; + + public int Remaining => this._buffer.Length - this._offset; + + public StreamBufferWriter( + Stream underlying, + bool ownsStream, + ArrayPool arrayPool, + bool cleansBuffer + ) + { + this._underlying = underlying; + this._ownsStream = ownsStream; + this._arrayPool = arrayPool; + this._cleansBuffer = cleansBuffer; + } + + public async ValueTask DisposeAsync() + { + await this._flushTask.ConfigureAwait(false); + this.CleanUpAsyncWork(); + await this.FlushToStreamAsync().ConfigureAwait(false); + await this._underlying.FlushAsync().ConfigureAwait(false); + this._arrayPool.Return(this._buffer, this._cleansBuffer); + this._buffer = null!; + this._offset = 0; + if (this._ownsStream) + { + await this._underlying.DisposeAsync(); + } + } + + public void Advance(int count) + => this._offset++; + + public Memory GetMemory(int sizeHint = 0) + { + this.EnsureBuffer(sizeHint); + return this._buffer.AsMemory(this._offset); + } + + public Span GetSpan(int sizeHint = 0) + { + this.EnsureBuffer(sizeHint); + return this._buffer.AsSpan(this._offset); + } + + private void EnsureBuffer(int sizeHint) + { + if (this._buffer == null) + { + throw new ObjectDisposedException(this.GetType().FullName); + } + + if (this.Remaining > 0 && this.Remaining >= sizeHint) + { + return; + } + + if (!this._flushTask.IsCompletedSuccessfully) + { + this._flushTask.ConfigureAwait(false).GetAwaiter().GetResult(); + this.CleanUpAsyncWork(); + } + + this._flushTask = this.FlushToStreamAsync(); + if (!this._flushTask.IsCompletedSuccessfully) + { + this._flushingBuffer = this._buffer; + this._buffer = this._arrayPool.Rent(this._buffer.Length); + } + + this._offset = 0; + } + + private ValueTask FlushToStreamAsync() + => this._underlying.WriteAsync(this._buffer.AsMemory(0, this._offset)); + + private void CleanUpAsyncWork() + { + if (this._flushingBuffer != null) + { + this._arrayPool.Return(this._flushingBuffer!, this._cleansBuffer); + this._flushingBuffer = null; + this._flushTask = default; + } + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs new file mode 100644 index 000000000..c09f44bc8 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs @@ -0,0 +1,57 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MsgPack.Internal +{ + internal sealed class StreamReadOnlyMemoryProvider + { + private readonly Stream _stream; + private readonly byte[] _buffer; + + public StreamReadOnlyMemoryProvider(Stream stream, byte[] buffer) + { + this._stream = stream; + this._buffer = buffer; + } + + public async ValueTask> GetNextAsync(ReadOnlyMemory previous, int requestHint, CancellationToken cancellationToken) + { + var required = (long)previous.Length + requestHint; + if (required > UInt32.MaxValue) + { +#warning TODO: + throw new Exception("Too large"); + } + + Memory buffer; + if (required < this._buffer.Length) + { + if (previous.Length > 0) + { + this._buffer.AsSpan().CopyTo(this._buffer.AsSpan(previous.Length)); + } + + buffer = this._buffer.AsMemory(previous.Length); + if (requestHint > 0) + { + buffer = buffer.Slice(0, requestHint); + } + } + else + { + buffer = new byte[required]; + previous.CopyTo(buffer); + buffer = buffer.Slice(previous.Length); + } + + await this._stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + return buffer; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/Throw.cs b/src/MsgPack.Abstraction/Internal/Throw.cs new file mode 100644 index 000000000..91f780428 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Throw.cs @@ -0,0 +1,150 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Text; + +namespace MsgPack.Internal +{ + internal static class Throw + { + public static void ExtensionsIsNotSupported() + => throw new NotSupportedException($"Extension type is not supported in this encoder."); + + public static void TooLargeCharLength(long size) + => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It is {size:#,0} chars, but it must be lessor than {Int32.MaxValue:#,0} chars. Use EncodeLargeString instead."); + + public static void DepthExeeded(long position, int maxDepth) + => throw new LimitExceededException($"The depth of collection exceeds max depth {maxDepth:#,0} at {position:#,0}."); + + public static void StringLengthExceeded(long position, long length, int maxLength) + => throw new LimitExceededException($"The byte length of encoded string ({length:#,0}) exceeds max length ({maxLength:#,0}) at {position:#,0}."); + + public static void BinaryLengthExceeded(long position, long length, int maxLength) + => throw new LimitExceededException($"The byte length of encoded binary ({length:#,0}) exceeds max length ({maxLength:#,0}) at {position:#,0}."); + + public static void DepthUnderflow() + => throw new InvalidOperationException("CurrentDepth is 0."); + + public static void TooLargeByteLength(long size, string encodingName) + => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It will be encoded to {size:#,0} bytes with '{encodingName}' encoding, but it must be less than or equal to {UInt32.MaxValue:#,0} bytes. Use EncodeLargeString instead."); + + public static void TooLargeByteLength(Exception innerException, string encodingName) + => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It will be encoded to larger than {Int32.MaxValue:#,0} bytes with '{encodingName}' encoding, but it must be less than or equal to {UInt32.MaxValue:#,0} bytes. Use EncodeLargeString instead.", innerException); + + public static void TooLargeByteLengthForString(string encodingName) + => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It will be decoded to larger than maximum System.String length with '{encodingName}' encoding."); + + internal static void TooLargePropertyKey(long position, int length, int maxPropertyKeyLength) + => throw new InvalidOperationException($"Property key is too large. The size {length:#,0} is larger than configured limit {maxPropertyKeyLength:#,0} at {position:#,0}."); + + public static void InsufficientInput(long position, Type targetType, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to decode {targetType} value at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to decode {targetType} value at {position:#,0}."); + } + } + + public static void InsufficientInputForDecodeArrayOrMapHeader(long position, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to decode array or map header at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to decode array or map header at {position:#,0}."); + } + } + + public static void InsufficientInputForDecodeArrayHeader(long position, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to decode array header at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to decode array header at {position:#,0}."); + } + } + + public static void InsufficientInputForDecodeMapHeader(long position, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to decode map header at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to decode map header at {position:#,0}."); + } + } + + public static void InsufficientInputForString(long position, Type type, Encoding? encoding, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to decode string as {type} with '{encoding}' encoding at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to decode string as {type} with '{encoding}' encoding at {position:#,0}."); + } + } + + public static void InsufficientInputForRawString(long position, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to fetch raw string at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to fetch raw string at {position:#,0}."); + } + } + + public static void InsufficientInputForSkip(long position, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to skip current subtree at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to skip current subtree at {position:#,0}."); + } + } + + public static void InsufficientInputForDetectCollectionEnds(long position, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to detect whether current collection is end at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to detect whether current collection is end at {position:#,0}."); + } + } + + internal static void InsufficientInputForDrainCollectionItems(long position, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to drain current collection items at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to current collection items at {position:#,0}."); + } + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/Utf8EncodingNonBom.cs b/src/MsgPack.Abstraction/Internal/Utf8EncodingNonBom.cs new file mode 100644 index 000000000..0cf4999fc --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/Utf8EncodingNonBom.cs @@ -0,0 +1,21 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Text; + +namespace MsgPack.Internal +{ + /// + /// Defines sealed UTF-8 class which cannot emit BOM. + /// + /// + /// Sealed class typed static constant is important for JIT devirtualization optimization. + /// + internal sealed class Utf8EncodingNonBom : UTF8Encoding + { + public static Utf8EncodingNonBom Instance { get; } = new Utf8EncodingNonBom(); + + private Utf8EncodingNonBom() : base(encoderShouldEmitUTF8Identifier: false) { } + } +} diff --git a/src/MsgPack.Abstraction/LimitExceededException.cs b/src/MsgPack.Abstraction/LimitExceededException.cs new file mode 100644 index 000000000..a49484da8 --- /dev/null +++ b/src/MsgPack.Abstraction/LimitExceededException.cs @@ -0,0 +1,21 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack +{ +#warning TODO: Quota? Limit? + public sealed class LimitExceededException : Exception + { + public LimitExceededException() + : this("Sime quota is exeeded.") { } + + public LimitExceededException(string? message) + : base(message) { } + + public LimitExceededException(string? message, Exception? innerException) + : base(message, innerException) { } + } +} diff --git a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj new file mode 100644 index 000000000..1d0ea90cf --- /dev/null +++ b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj @@ -0,0 +1,49 @@ + + + + netcoreapp3.1 + + + + + + + + + + TextTemplatingFileGenerator + Decoder.Strings.cs + + + Encoder.Primitives.cs + TextTemplatingFileGenerator + + + TextTemplatingFileGenerator + Decoder.Primitives.cs + + + + + + + + + + True + True + Decoder.Primitives.tt + + + True + True + Decoder.Strings.tt + + + True + True + Encoder.Primitives.tt + + + + diff --git a/src/MsgPack.Abstraction/Properties/AssemblyInfo.cs b/src/MsgPack.Abstraction/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..4c671542f --- /dev/null +++ b/src/MsgPack.Abstraction/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +#warning TODO:HEADER + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("MsgPack.Core")] +[assembly: InternalsVisibleTo("MsgPack.Json")] diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs new file mode 100644 index 000000000..7469d883d --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs @@ -0,0 +1,38 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Text; + +namespace MsgPack.Serialization +{ + public sealed class DeserializationOptions + { + public static DeserializationOptions Default { get; } = new DeserializationOptionsBuilder().Create(); + + public int MaxArrayLength { get; } + public int MaxMapCount { get; } + public int MaxPropertyKeyLength { get; } + public int MaxDepth { get; } + public Encoding? StringEncoding { get; } + public ArrayPool ArrayPool { get; } + + internal DeserializationOptions( + int maxArrayLength, + int maxMapCount, + int maxPropertyKeyLength, + int maxDepth, + Encoding? stringEncoding, + ArrayPool arrayPool + ) + { + this.MaxArrayLength =maxArrayLength; + this.MaxMapCount = maxMapCount; + this.MaxPropertyKeyLength = maxPropertyKeyLength; + this.MaxDepth = maxDepth; + this.StringEncoding = stringEncoding; + this.ArrayPool = arrayPool; + } + } +} diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs new file mode 100644 index 000000000..54dab8ca0 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs @@ -0,0 +1,58 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Text; +using MsgPack.Internal; + +namespace MsgPack.Serialization +{ +#warning TODO: tuning default as backward compatible + public sealed class DeserializationOptionsBuilder + { + internal const int DefaultMaxArrayLength = 1024 * 1024; + internal const int DefaultMaxMapCount = 1024 * 1024; + internal const int DefaultMaxPropertyKeyLength = 256; + internal const int DefaultMaxDepth = 100; + + private int _maxArrayLength = DefaultMaxArrayLength; + + public int MaxArrayLength + { + get => this._maxArrayLength; + set => this._maxArrayLength = Ensure.IsNotLessThan(value, 0); + } + + private int _maxMapCount = DefaultMaxMapCount; + + public int MaxMapCount + { + get => this._maxMapCount; + set => this._maxMapCount = Ensure.IsNotLessThan(value, 0); + } + + private int _maxPropertyKeyLength = DefaultMaxPropertyKeyLength; + + public int MaxPropertyKeyLength + { + get => this._maxPropertyKeyLength; + set => this._maxPropertyKeyLength = Ensure.IsNotLessThan(value, 1); + } + + private int _maxDepth = DefaultMaxDepth; + + public int MaxDepth + { + get => this._maxDepth; + set => this._maxDepth = Ensure.IsNotLessThan(value, 1); + } + + public Encoding? StringEncoding { get; set; } + + public ArrayPool? ArrayPool { get; set; } + + public DeserializationOptions Create() + => new DeserializationOptions(this.MaxArrayLength, this.MaxMapCount, this.MaxPropertyKeyLength, this.MaxDepth, this.StringEncoding, this.ArrayPool ?? ArrayPool.Shared); + } +} diff --git a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext.cs b/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext.cs new file mode 100644 index 000000000..2c267e72f --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext.cs @@ -0,0 +1,65 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + public struct DeserializationOperationContext + { + public Decoder Decoder { get; } + public DeserializationOptions Options { get; } + public int CurrentDepth { get; private set; } + public CancellationToken CancellationToken { get; } + public ArrayPool ArrayPool => this.Options.ArrayPool; + public System.Text.Encoding? StringEncoding => this.Options.StringEncoding; + + public DeserializationOperationContext(Decoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) + { + this.Decoder = Ensure.NotNull(decoder); + this.Options = options ?? DeserializationOptions.Default; + this.CurrentDepth = 0; + this.CancellationToken = cancellationToken; + } + + public CollectionContext CollectionContext => + new CollectionContext( + this.Options.MaxArrayLength, + this.Options.MaxMapCount, + this.Options.MaxDepth, + this.CurrentDepth + ); + + public int IncrementDepth() + { + if (this.CurrentDepth == this.Options.MaxDepth) + { + Throw.DepthExeeded(this.CurrentDepth, this.Options.MaxDepth); + } + + return this.CurrentDepth++; + } + + public int DecrementDepth() + { + if (this.CurrentDepth == 0) + { + Throw.DepthUnderflow(); + } + + return this.CurrentDepth--; + } + + public readonly void ValidatePropertyKeyLength(long position, int length) + { + if(length > this.Options.MaxPropertyKeyLength) + { + Throw.TooLargePropertyKey(position, length, this.Options.MaxPropertyKeyLength); + } + } + } +} diff --git a/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`1.cs b/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`1.cs new file mode 100644 index 000000000..7f267b7db --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`1.cs @@ -0,0 +1,20 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.IO; +using System.Threading.Tasks; + +namespace MsgPack.Serialization.Internal +{ + public interface IObjectSerializer + { + void Serialize(in SerializationOperationContext context, T obj, IBufferWriter sink); + ValueTask SerializeAsync(SerializationOperationContext context, T obj, Stream streamSink); + T Deserialize(in DeserializationOperationContext context, in SequenceReader source); + ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource); + void DeserializeTo(in DeserializationOperationContext context, in SequenceReader source, in T obj); + ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, T obj); + } +} diff --git a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext.cs b/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext.cs new file mode 100644 index 000000000..689a70b57 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext.cs @@ -0,0 +1,50 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + public struct SerializationOperationContext + { + public Encoder Encoder { get; } + public SerializationOptions Options { get; } + private int _currentDepth; + public int CurrentDepth => this._currentDepth; + public CancellationToken CancellationToken { get; } + public System.Text.Encoding? StringEncoding => this.Options.StringEncoding; + + public SerializationOperationContext(Encoder encoder, SerializationOptions? options, CancellationToken cancellationToken) + { + this.Encoder = Ensure.NotNull(encoder); + this.Options = options ?? SerializationOptions.Default; + this._currentDepth = 0; + this.CancellationToken = cancellationToken; + } + + public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this._currentDepth); + + public int IncrementDepth() + { + if (this._currentDepth == this.Options.MaxDepth) + { + Throw.DepthExeeded(this._currentDepth, this.Options.MaxDepth); + } + + return this._currentDepth++; + } + + public int DecrementDepth() + { + if (this._currentDepth == 0) + { + Throw.DepthUnderflow(); + } + + return this._currentDepth--; + } + } +} diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOptions.cs b/src/MsgPack.Abstraction/Serialization/SerializationOptions.cs new file mode 100644 index 000000000..0b9d8d13d --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/SerializationOptions.cs @@ -0,0 +1,26 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Text; + +namespace MsgPack.Serialization +{ + public sealed class SerializationOptions + { + public static SerializationOptions Default { get; } = new SerializationOptionsBuilder().Create(); + + public int MaxDepth { get; } + + public Encoding? StringEncoding { get; } + + internal SerializationOptions( + int maxDepth, + Encoding? stringEncoding + ) + { + this.MaxDepth = maxDepth; + this.StringEncoding = stringEncoding; + } + } +} diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs b/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs new file mode 100644 index 000000000..8016eeece --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs @@ -0,0 +1,24 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using MsgPack.Internal; + +namespace MsgPack.Serialization +{ + public sealed class SerializationOptionsBuilder + { + private int _maxDepth; + + public int MaxDepth + { + get => this._maxDepth; + set => this._maxDepth = Ensure.IsNotLessThan(value, 1); + } + + public System.Text.Encoding? StringEncoding { get; set; } + + public SerializationOptions Create() + => new SerializationOptions(this.MaxDepth, this.StringEncoding); + } +} diff --git a/src/MsgPack.Abstraction/Serialization/Serializer`1.cs b/src/MsgPack.Abstraction/Serialization/Serializer`1.cs new file mode 100644 index 000000000..bb959aee2 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Serializer`1.cs @@ -0,0 +1,112 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MsgPack.Internal; +using MsgPack.Serialization.Internal; + +namespace MsgPack.Serialization +{ + public abstract class Serializer + { + private readonly Func _encoderFactory; + private readonly Func _decoderFactory; + private readonly IObjectSerializer _underlying; + private readonly SerializationOptions _serializationOptions; + private readonly DeserializationOptions _deserializationOptions; + + protected Serializer( + Func encoderFactory, + Func decoderFactory, + IObjectSerializer underlying, + SerializationOptions serializationOptions, + DeserializationOptions deserializationOptions + ) + { + this._encoderFactory = Ensure.NotNull(encoderFactory); + this._decoderFactory = Ensure.NotNull(decoderFactory); + this._underlying = Ensure.NotNull(underlying); + this._serializationOptions = Ensure.NotNull(serializationOptions); + this._deserializationOptions = Ensure.NotNull(deserializationOptions); + } + + private void InitializeSerializationOperationContext(CancellationToken cancellationToken, out SerializationOperationContext context) + => context = new SerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); + + private void InitializeDeserializationOperationContext(CancellationToken cancellationToken, out DeserializationOperationContext context) + => context = new DeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); + + public void Serialize(T obj, IBufferWriter sink, CancellationToken cancellationToken = default) + { + this.InitializeSerializationOperationContext(cancellationToken, out var context); + this._underlying.Serialize(context, obj, sink); + } + + public ReadOnlyMemory Serialize(T obj, CancellationToken cancellationToken = default) + { + this.InitializeSerializationOperationContext(cancellationToken, out var context); + var writer = new ArrayBufferWriter(); + this._underlying.Serialize(context, obj, writer); + return writer.WrittenMemory; + } + + public ValueTask SerializeAsync(T obj, Stream streamSink, CancellationToken cancellationToken = default) + { + this.InitializeSerializationOperationContext(cancellationToken, out var context); + return this._underlying.SerializeAsync(context, obj, streamSink); + } + + public T Deserialize(in SequenceReader reader, CancellationToken cancellationToken = default) + { + this.InitializeDeserializationOperationContext(cancellationToken, out var context); + return this._underlying.Deserialize(context, reader); + } + + public T Deserialize(in ReadOnlySequence source, CancellationToken cancellationToken = default) + { + this.InitializeDeserializationOperationContext(cancellationToken, out var context); + return this._underlying.Deserialize(context, new SequenceReader(source)); + } + + public T Deserialize(ReadOnlyMemory memorySource, CancellationToken cancellationToken = default) + { + this.InitializeDeserializationOperationContext(cancellationToken, out var context); + return this._underlying.Deserialize(context, new SequenceReader(new ReadOnlySequence(memorySource))); + } + + public ValueTask DeserializeAsync(Stream streamSource, CancellationToken cancellationToken = default) + { + this.InitializeDeserializationOperationContext(cancellationToken, out var context); + return this._underlying.DeserializeAsync(context, streamSource); + } + + public void DeserializeTo(in SequenceReader reader, T obj, CancellationToken cancellationToken = default) + { + this.InitializeDeserializationOperationContext(cancellationToken, out var context); + this._underlying.DeserializeTo(context, reader, obj); + } + + public void DeserializeTo(in ReadOnlySequence source, T obj, CancellationToken cancellationToken = default) + { + this.InitializeDeserializationOperationContext(cancellationToken, out var context); + this._underlying.DeserializeTo(context, new SequenceReader(source), obj); + } + + public void DeserializeTo(ReadOnlyMemory memorySource, T obj, CancellationToken cancellationToken = default) + { + this.InitializeDeserializationOperationContext(cancellationToken, out var context); + this._underlying.DeserializeTo(context, new SequenceReader(new ReadOnlySequence(memorySource)), obj); + } + + public ValueTask DeserializeToAsync(Stream streamSource, T obj, CancellationToken cancellationToken = default) + { + this.InitializeDeserializationOperationContext(cancellationToken, out var context); + return this._underlying.DeserializeToAsync(context, streamSource, obj); + } + } +} diff --git a/src/MsgPack.Compatibility/MsgPack.Compatibility.csproj b/src/MsgPack.Compatibility/MsgPack.Compatibility.csproj new file mode 100644 index 000000000..cb6319069 --- /dev/null +++ b/src/MsgPack.Compatibility/MsgPack.Compatibility.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs b/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs new file mode 100644 index 000000000..46a753c2a --- /dev/null +++ b/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs @@ -0,0 +1,163 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + /// + /// MessagePack encoder. + /// + internal sealed class CurrentMessagePackEncoder : MessagePackEncoder + { + public CurrentMessagePackEncoder(MessagePackEncoderOptions options) + : base(options) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + protected sealed override int EncodeStringHeader(uint length, Span buffer) + { + if (length < 32) + { + buffer[0] = unchecked((byte)(MessagePackCode.MinimumFixedRaw | length)); + return 1; + } + else if (length <= Byte.MaxValue) + { + buffer[1] = unchecked((byte)length); + buffer[0] = MessagePackCode.Str8; + return 2; + } + else if (length <= UInt16.MaxValue) + { + buffer[0] = MessagePackCode.Str16; + BinaryPrimitives.WriteUInt16BigEndian(buffer, unchecked((ushort)length)); + return sizeof(ushort) + 1; + } + else + { + buffer[0] = MessagePackCode.Str32; + BinaryPrimitives.WriteUInt32BigEndian(buffer, length); + return sizeof(uint) + 1; + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + protected sealed override int EncodeBinaryHeader(uint length, Span buffer) + { + if (length <= Byte.MaxValue) + { + buffer[1] = unchecked((byte)length); + buffer[0] = MessagePackCode.Bin8; + return 2; + } + else if (length <= UInt16.MaxValue) + { + buffer[0] = MessagePackCode.Bin16; + BinaryPrimitives.WriteUInt16BigEndian(buffer, unchecked((ushort)length)); + return sizeof(ushort) + 1; + } + else + { + buffer[0] = MessagePackCode.Bin32; + BinaryPrimitives.WriteUInt32BigEndian(buffer, length); + return sizeof(uint) + 1; + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeExtension(sbyte typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) + { + EncodeExtensionHeader(typeCode, unchecked((uint)serializedValue.Length), buffer); + buffer.Write(serializedValue); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeExtension(sbyte typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) + { + EncodeExtensionHeader(typeCode, unchecked((uint)serializedValue.Length), buffer); + this.WriteRaw(serializedValue, buffer); + } + + private static void EncodeExtensionHeader(sbyte typeCode, uint serializedValueLength, IBufferWriter buffer) + { + switch (serializedValueLength) + { + case 1: + { + var span = buffer.GetSpan(1); + span[0] = MessagePackCode.FixExt1; + buffer.Advance(1); + break; + } + case 2: + { + var span = buffer.GetSpan(1); + span[0] = MessagePackCode.FixExt2; + buffer.Advance(1); + break; + } + case 4: + { + var span = buffer.GetSpan(1); + span[0] = MessagePackCode.FixExt4; + buffer.Advance(1); + break; + } + case 8: + { + var span = buffer.GetSpan(1); + span[0] = MessagePackCode.FixExt8; + buffer.Advance(1); + break; + } + case 16: + { + var span = buffer.GetSpan(1); + span[0] = MessagePackCode.FixExt16; + buffer.Advance(1); + break; + } + default: + { + if (serializedValueLength <= Byte.MaxValue) + { + var span = buffer.GetSpan(2); + span[0] = MessagePackCode.Ext8; + span[1] = unchecked((byte)serializedValueLength); + buffer.Advance(2); + } + else if (serializedValueLength <= UInt16.MaxValue) + { + var span = buffer.GetSpan(sizeof(ushort) + 1); + span[0] = MessagePackCode.Ext16; + span = span.Slice(1); + BinaryPrimitives.WriteUInt16BigEndian(span, unchecked((ushort)serializedValueLength)); + buffer.Advance(sizeof(ushort) + 1); + } + else + { + var span = buffer.GetSpan(sizeof(uint) + 1); + span[0] = MessagePackCode.Ext32; + span = span.Slice(1); + BinaryPrimitives.WriteUInt32BigEndian(span, serializedValueLength); + buffer.Advance(sizeof(uint) + 1); + } + + break; + } + } // switch + + // type code + { + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)typeCode); + buffer.Advance(1); + } + } + + } +} diff --git a/src/MsgPack.Core/Internal/LegacyMessagePackEncoder.cs b/src/MsgPack.Core/Internal/LegacyMessagePackEncoder.cs new file mode 100644 index 000000000..f0d39367e --- /dev/null +++ b/src/MsgPack.Core/Internal/LegacyMessagePackEncoder.cs @@ -0,0 +1,45 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + /// + /// MessagePack encoder for legacy format which uses raw type instead of str type and does not support ext type. + /// + internal sealed class LegacyMessagePackEncoder : MessagePackEncoder + { + public LegacyMessagePackEncoder(MessagePackEncoderOptions options) + : base(options) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + protected sealed override int EncodeStringHeader(uint length, Span buffer) + { + if (length < 32) + { + buffer[0] = unchecked((byte)(MessagePackCode.MinimumFixedRaw | length)); + return 1; + } + else if (length <= UInt16.MaxValue) + { + buffer[0] = MessagePackCode.Raw16; + BinaryPrimitives.WriteUInt16BigEndian(buffer, unchecked((ushort)length)); + return sizeof(ushort) + 1; + } + else + { + buffer[0] = MessagePackCode.Raw32; + BinaryPrimitives.WriteUInt32BigEndian(buffer, length); + return sizeof(uint) + 1; + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + protected sealed override int EncodeBinaryHeader(uint length, Span buffer) + => this.EncodeStringHeader(length, buffer); + } +} diff --git a/src/MsgPack.Core/Internal/MemoryExtensions.cs b/src/MsgPack.Core/Internal/MemoryExtensions.cs new file mode 100644 index 000000000..02c2e72dc --- /dev/null +++ b/src/MsgPack.Core/Internal/MemoryExtensions.cs @@ -0,0 +1,34 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + internal static class MemoryExtensions + { + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static void Convert(this System.Text.Encoder encoder, ReadOnlyMemory source, Memory sink, bool flush, out int charsUsed, out int bytesUsed, out bool completed) + => encoder.Convert(source.Span, sink.Span, flush, out charsUsed, out bytesUsed, out completed); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static unsafe Memory Compact(this Memory memory, T[] array, int used) + where T : unmanaged + { + var span = memory.Span.Slice(used); + if (span.IsEmpty) + { + return array; + } + + fixed (T* pSrc = span) + fixed (T* pDest = array) + { + Buffer.MemoryCopy(pSrc, pDest, array.Length, span.Length); + return array.AsMemory(0, span.Length); + } + } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs new file mode 100644 index 000000000..5f8d6ca3f --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs @@ -0,0 +1,171 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; + +namespace MsgPack.Internal +{ + public partial class MessagePackDecoder + { + public sealed override CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out long itemsCount, out int requestHint) + { + var consumed = 0L; + var result = this.PrivateDecodeArrayOrMapHeader(source, ref consumed, out var header, out itemsCount, out requestHint); + + if (itemsCount > Int32.MaxValue) + { + MessagePackThrow.TooLargeArrayOrMapLength(header, source.Consumed - consumed, itemsCount); + } + + return result; + } + + private CollectionType PrivateDecodeArrayOrMapHeader(in SequenceReader source, ref long consumed, out byte header, out long itemsCount, out int requestHint) + { + var result = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out header, out itemsCount, out requestHint); + if (result == CollectionType.None) + { + MessagePackThrow.TypeIsNotArrayNorMap(header, source.Consumed - consumed); + } + + return result; + } + + private CollectionType DecodeArrayOrMapHeaderCore(in SequenceReader source, ref long consumed, out byte header, out long itemsCount, out int requestHint) + { + if (!this.TryPeek(source, out header)) + { + requestHint = 1; + itemsCount = 0; + return default; + } + + if ((header & MessagePackCode.MinimumFixedArray) == MessagePackCode.MinimumFixedArray) + { + itemsCount = (uint)header - MessagePackCode.MinimumFixedArray; + requestHint = 0; + source.Advance(1); + consumed++; + return CollectionType.Array; + } + + if ((header & MessagePackCode.MaximumFixedMap) == MessagePackCode.MaximumFixedMap) + { + itemsCount = (uint)header - MessagePackCode.MaximumFixedMap; + requestHint = 0; + source.Advance(1); + consumed++; + return CollectionType.Map; + } + + int length; + CollectionType type; + switch (header) + { + case MessagePackCode.Array16: + { + length = 3; + type = CollectionType.Array; + break; + } + case MessagePackCode.Array32: + { + length = 5; + type = CollectionType.Array; + break; + } + case MessagePackCode.Map16: + { + length = 3; + type = CollectionType.Map; + break; + } + case MessagePackCode.Map32: + { + length = 5; + type = CollectionType.Map; + break; + } + default: + { + requestHint = 0; + itemsCount = 0; + return default; + } + } + + var lengthOfLength = length - 1; + + switch (lengthOfLength) + { + case 2: + { + itemsCount = ReadValue(source, offset: 1, out requestHint); + break; + } + default: // 4 + { + itemsCount = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + if (requestHint != 0) + { + return CollectionType.None; + } + + source.Advance(length); + consumed += length; + return type; + } + + public sealed override long DecodeArrayHeader(in SequenceReader source, out int requestHint) + { + var consumed = 0L; + var type = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out var header, out var itemsCount, out requestHint); + if (requestHint != 0) + { + source.Rewind(consumed); + return 0; + } + + if (itemsCount > Int32.MaxValue) + { + MessagePackThrow.TooLargeArrayOrMapLength(header, source.Consumed - consumed, itemsCount); + } + + if (!type.IsArray) + { + MessagePackThrow.TypeIsNotArray(header, source.Consumed - consumed); + } + + return (int)itemsCount; + } + + public sealed override long DecodeMapHeader(in SequenceReader source, out int requestHint) + { + var consumed = 0L; + var type = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out var header, out var itemsCount, out requestHint); + if (requestHint != 0) + { + source.Rewind(consumed); + return 0; + } + + if (itemsCount > Int32.MaxValue) + { + MessagePackThrow.TooLargeArrayOrMapLength(header, source.Consumed - consumed, itemsCount); + } + + if (!type.IsMap) + { + MessagePackThrow.TypeIsNotMap(header, source.Consumed - consumed); + } + + return (int)itemsCount; + } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs new file mode 100644 index 000000000..d3e8190ca --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs @@ -0,0 +1,355 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace MsgPack.Internal +{ + public partial class MessagePackDecoder + { + public sealed override ElementType DecodeItem(in SequenceReader source, out ReadOnlySequence valueOrLength, out int requestHint, CancellationToken cancellationToken = default) + { + var elementType = this.ReadHeader(source, out var consumed, out var valueOrLengthScalar, out var requestHint32); + if (requestHint32 != 0) + { + requestHint = requestHint32; + valueOrLength = default; + return ElementType.Null; + } + + requestHint = 0; + + switch (elementType) + { + case ElementType.True: + case ElementType.False: + case ElementType.Null: + { + valueOrLength = default; + break; + } + case ElementType.Int32: + case ElementType.Single: + { + var buffer = new byte[sizeof(int)]; + var span = MemoryMarshal.Cast(buffer); + span[0] = (int)valueOrLengthScalar; + valueOrLength = new ReadOnlySequence(buffer); + break; + } + case ElementType.Array: + case ElementType.Map: + case ElementType.Int64: + case ElementType.UInt64: + case ElementType.Double: + { + var buffer = new byte[sizeof(long)]; + var span = MemoryMarshal.Cast(buffer); + span[0] = valueOrLengthScalar; + valueOrLength = new ReadOnlySequence(buffer); + break; + } + case ElementType.String: + case ElementType.Binary: + { + if (source.Remaining < valueOrLengthScalar + consumed) + { + requestHint = (int)((valueOrLengthScalar + consumed - source.Remaining) & Int32.MaxValue); + valueOrLength = default; + return default; + } + + valueOrLength = source.Sequence.Slice(source.Consumed + consumed, valueOrLengthScalar); + consumed += valueOrLengthScalar; + break; + } + default: + { + Debug.Assert(elementType == ElementType.Extension, $"elementType({elementType}, 0x{elementType:X8}) == ElementType.Extension"); + + if(source.Remaining < valueOrLengthScalar + consumed) + { + requestHint = (int)((valueOrLengthScalar + consumed - source.Remaining) & Int32.MaxValue); + valueOrLength = default; + return default; + } + + valueOrLength = source.Sequence.Slice(source.Consumed + consumed, valueOrLengthScalar + 1 /* typeCode */); + consumed += valueOrLengthScalar; + break; + } + } + + source.Advance(consumed); + return elementType; + } + + private ElementType ReadHeader(in SequenceReader source, out long consumed, out long valueOrLength, out int requestHint) + { + if (!this.TryPeek(source, out var header)) + { + requestHint = 1; + valueOrLength = default; + consumed = 0; + return default; + } + + consumed = 1; + ElementType result; + requestHint = 0; + + if (header < 128) + { + valueOrLength = header; + result = ElementType.Int32; + } + else if (header >= 0xE0) + { + valueOrLength = unchecked((sbyte)header); + result = ElementType.Int32; + } + else if (header >= MessagePackCode.MinimumFixedRaw && header <= MessagePackCode.MaximumFixedRaw) + { + valueOrLength = header - MessagePackCode.MinimumFixedRaw; + result = ElementType.String; + } + else if (header >= MessagePackCode.MinimumFixedArray && header <= MessagePackCode.MaximumFixedArray) + { + valueOrLength = header - MessagePackCode.MinimumFixedArray; + result = ElementType.Array; + } + else if (header >= MessagePackCode.MinimumFixedMap && header <= MessagePackCode.MaximumFixedMap) + { + valueOrLength = header - MessagePackCode.MinimumFixedMap; + result = ElementType.Map; + } + else + { + switch (header) + { + case MessagePackCode.SignedInt8: + { + valueOrLength = ReadSByte(source, offset: 1, out requestHint); + consumed += sizeof(sbyte); + result = ElementType.Int32; + break; + } + case MessagePackCode.SignedInt16: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(short); + result = ElementType.Int32; + break; + } + case MessagePackCode.SignedInt32: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(int); + result = ElementType.Int32; + break; + } + case MessagePackCode.SignedInt64: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(long); + result = ElementType.Int64; + break; + } + case MessagePackCode.UnsignedInt8: + { + valueOrLength = ReadByte(source, offset: 1, out requestHint); + consumed += sizeof(byte); + result = ElementType.Int32; + break; + } + case MessagePackCode.UnsignedInt16: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(ushort); + result = ElementType.Int32; + break; + } + case MessagePackCode.UnsignedInt32: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(uint); + result = ElementType.Int64; + break; + } + case MessagePackCode.UnsignedInt64: + { + valueOrLength = unchecked((long)ReadValue(source, offset: 1, out requestHint)); + consumed += sizeof(ulong); + result = ElementType.UInt64; + break; + } + case MessagePackCode.Real32: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(int); + result = ElementType.Single; + break; + } + case MessagePackCode.Real64: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(long); + result = ElementType.Double; + break; + } + case MessagePackCode.NilValue: + { + valueOrLength = 0; + result = ElementType.Null; + break; + } + case MessagePackCode.TrueValue: + { + valueOrLength = 1; + result = ElementType.True; + break; + } + case MessagePackCode.FalseValue: + { + valueOrLength = 0; + result = ElementType.False; + break; + } + case MessagePackCode.Array16: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(ushort); + result = ElementType.Array; + break; + } + case MessagePackCode.Array32: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(uint); + result = ElementType.Array; + break; + } + case MessagePackCode.Map16: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(ushort); + result = ElementType.Map; + break; + } + case MessagePackCode.Map32: + { + valueOrLength = ReadValue(source, offset: 1, out requestHint); + consumed += sizeof(uint); + result = ElementType.Map; + break; + } + case MessagePackCode.FixExt1: + case MessagePackCode.FixExt2: + case MessagePackCode.FixExt4: + case MessagePackCode.FixExt8: + case MessagePackCode.FixExt16: + case MessagePackCode.Ext8: + case MessagePackCode.Ext16: + case MessagePackCode.Ext32: + { + result = ReadExtentionItem(header, source, offset: 1, ref consumed, out valueOrLength, out requestHint); + break; + } + default: + { + Debug.Assert(header == 0xC1, $"header(0x{header:X2}) == 0xC1"); + valueOrLength = 0; + result = ElementType.None; + break; + } + } // switch + } // else + + return result; + } + + private static ElementType ReadExtentionItem(byte header, SequenceReader source, int offset, ref long consumed, out long valueOrLength, out int requestHint) + { + consumed++; + requestHint = 0; + + switch (header) + { + case MessagePackCode.FixExt1: + { + valueOrLength = 1; + break; + } + case MessagePackCode.FixExt2: + { + valueOrLength = 2; + break; + } + case MessagePackCode.FixExt4: + { + valueOrLength = 4; + break; + } + case MessagePackCode.FixExt8: + { + valueOrLength = 8; + break; + } + case MessagePackCode.FixExt16: + { + valueOrLength = 16; + break; + } + default: + { + _ = ReadByte(source, offset, out requestHint); + if (requestHint != 0) + { + valueOrLength = default; + return default; + } + + consumed++; + offset++; + + switch (header) + { + case MessagePackCode.Ext8: + { + valueOrLength = ReadByte(source, offset, out requestHint); + consumed++; + break; + } + case MessagePackCode.Ext16: + { + valueOrLength = ReadValue(source, offset, out requestHint); + consumed += sizeof(ushort); + break; + } + default: + { + Debug.Assert(header == MessagePackCode.Ext32, $"header(0x{header:X2}) == MessagePackCode.Ext32(0x{MessagePackCode.Ext32:X2})"); + valueOrLength = ReadValue(source, offset + 1, out requestHint); + consumed += sizeof(uint); + break; + } + } + + break; + } + } + + if (requestHint != 0) + { + return default; + } + + return ElementType.Extension; + } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs new file mode 100644 index 000000000..6523729bd --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs @@ -0,0 +1,138 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Threading; + +namespace MsgPack.Internal +{ + public partial class MessagePackDecoder + { + private uint ReadExtensionHeader(in SequenceReader source, out int consumed, out int requestHint) + { + if(!this.TryPeek(source, out var header)) + { + consumed = 0; + requestHint = 1; + return default; + } + + consumed = 1; + + int lengthOflength; + switch (header) + { + case MessagePackCode.FixExt1: + { + requestHint = 0; + return 1; + } + case MessagePackCode.FixExt2: + { + requestHint = 0; + return 2; + } + case MessagePackCode.FixExt4: + { + requestHint = 0; + return 4; + } + case MessagePackCode.FixExt8: + { + requestHint = 0; + return 8; + } + case MessagePackCode.FixExt16: + { + requestHint = 0; + return 16; + } + case MessagePackCode.Ext8: + { + lengthOflength = 1; + break; + } + case MessagePackCode.Ext16: + { + lengthOflength = 2; + break; + } + case MessagePackCode.Ext32: + { + lengthOflength = 4; + break; + } + default: + { + MessagePackThrow.IsNotExtension(header, source.Consumed); + // never + requestHint = default; + return default; + } + } // switch + + uint length; + switch (lengthOflength) + { + case 1: + { + length = ReadByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + length = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(lengthOflength == 4, $"length({lengthOflength}) == 4"); + length = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + if (requestHint != 0) + { + return default; + } + + consumed += lengthOflength; + return length; + } + + public override void DecodeExtension(in SequenceReader source, out byte typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) + { + var bodyLength = this.ReadExtensionHeader(source, out var consumed, out requestHint); + if (requestHint != 0) + { + typeCode = default; + body = default; + return; + } + + if (!this.TryPeek(source, out typeCode)) + { + requestHint = 1; + body = default; + return; + } + + consumed++; + + if (source.Remaining < consumed + bodyLength) + { + requestHint = (int)((consumed + bodyLength - (int)source.Remaining) & Int32.MaxValue); + body = default; + return; + } + + requestHint = 0; + body = source.Sequence.Slice(consumed, bodyLength); + source.Advance(consumed + bodyLength); + } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.cs new file mode 100644 index 000000000..1708f0b05 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.cs @@ -0,0 +1,449 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class MessagePackDecoder + { + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override SByte DecodeSByte(in SequenceReader source, out int requestHint) + { + if (!this.TryDecodeSignedInteger(source, typeof(SByte), out var header, out var result, out requestHint)) + { + return default; + } + if (result < SByte.MinValue || result > SByte.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, typeof(SByte)); + } + + return unchecked((SByte)result); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int16 DecodeInt16(in SequenceReader source, out int requestHint) + { + if (!this.TryDecodeSignedInteger(source, typeof(Int16), out var header, out var result, out requestHint)) + { + return default; + } + if (result < Int16.MinValue || result > Int16.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, typeof(Int16)); + } + + return unchecked((Int16)result); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int32 DecodeInt32(in SequenceReader source, out int requestHint) + { + if (!this.TryDecodeSignedInteger(source, typeof(Int32), out var header, out var result, out requestHint)) + { + return default; + } + if (result < Int32.MinValue || result > Int32.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, typeof(Int32)); + } + + return unchecked((Int32)result); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int64 DecodeInt64(in SequenceReader source, out int requestHint) + { + if (!this.TryDecodeSignedInteger(source, typeof(Int64), out var header, out var result, out requestHint)) + { + return default; + } + return unchecked((Int64)result); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Byte DecodeByte(in SequenceReader source, out int requestHint) + { + if (!this.TryDecodeUnsignedInteger(source, typeof(Byte), out var header, out var result, out requestHint)) + { + return default; + } + if (result < Byte.MinValue || result > Byte.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, typeof(Byte)); + } + + return unchecked((Byte)result); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt16 DecodeUInt16(in SequenceReader source, out int requestHint) + { + if (!this.TryDecodeUnsignedInteger(source, typeof(UInt16), out var header, out var result, out requestHint)) + { + return default; + } + if (result < UInt16.MinValue || result > UInt16.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, typeof(UInt16)); + } + + return unchecked((UInt16)result); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt32 DecodeUInt32(in SequenceReader source, out int requestHint) + { + if (!this.TryDecodeUnsignedInteger(source, typeof(UInt32), out var header, out var result, out requestHint)) + { + return default; + } + if (result < UInt32.MinValue || result > UInt32.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, typeof(UInt32)); + } + + return unchecked((UInt32)result); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt64 DecodeUInt64(in SequenceReader source, out int requestHint) + { + if (!this.TryDecodeUnsignedInteger(source, typeof(UInt64), out var header, out var result, out requestHint)) + { + return default; + } + return unchecked((UInt64)result); + } + + + private bool TryDecodeSignedInteger(in SequenceReader source, Type type, out byte header, out Int64 value, out int requestHint) + { + if (!this.TryPeek(source, out header)) + { + requestHint = 1; + value = default; + return false; + } + + requestHint = 0; + + if (header < 128) + { + value = header; + return true; + } + + if (header >= 0xE0) + { + value = unchecked((sbyte)header); + return true; + } + + ParseNumberHeader(header, source, type, out var length, out var kind); + + if ((kind & NumberKind.RealBitMask) != 0 && !this.Options.CanTreatRealAsInteger) + { + MessagePackThrow.RealCannotBeInteger(header, source.Consumed, type); + } + + switch (kind) + { + case NumberKind.Signed: + { + switch (length) + { + case 1: + { + value = ReadSByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + case 4: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + value = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + if (requestHint != 0) + { + value = default; + return false; + } + break; + } + case NumberKind.Unsigned: + { + ulong unsigned; + switch (length) + { + case 1: + { + unsigned = ReadByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + unsigned = ReadValue(source, offset: 1, out requestHint); + break; + } + case 4: + { + unsigned = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + unsigned = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + if (requestHint != 0) + { + value = default; + return false; + } + + if (unsigned > Int64.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((Int64)unsigned); + break; + } + case NumberKind.Single: + { + var real = ReadValue(source, offset: 1, out requestHint); + if (requestHint != 0) + { + value = default; + return false; + } + + if (real > Int64.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + if (real < Int64.MinValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((Int64)real); + break; + } + default: + { + Debug.Assert(kind == NumberKind.Double, $"kind({kind}) == NumberType.Double"); + var real = ReadValue(source, offset: 1, out requestHint); + if (requestHint != 0) + { + value = default; + return false; + } + + if (real > Int64.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + if (real < Int64.MinValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((Int64)real); + break; + } + } + + return true; + } + + + private bool TryDecodeUnsignedInteger(in SequenceReader source, Type type, out byte header, out UInt64 value, out int requestHint) + { + if (!this.TryPeek(source, out header)) + { + requestHint = 1; + value = default; + return false; + } + + requestHint = 0; + + if (header < 128) + { + value = header; + return true; + } + + ParseNumberHeader(header, source, type, out var length, out var kind); + + if ((kind & NumberKind.RealBitMask) != 0 && !this.Options.CanTreatRealAsInteger) + { + MessagePackThrow.RealCannotBeInteger(header, source.Consumed, type); + } + + switch (kind) + { + case NumberKind.Signed: + { + long signed; + switch (length) + { + case 1: + { + signed = ReadSByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + signed = ReadValue(source, offset: 1, out requestHint); + break; + } + case 4: + { + signed = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + signed = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + if (requestHint != 0) + { + value = default; + return false; + } + + if (signed < 0) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((ulong)signed); + break; + } + case NumberKind.Unsigned: + { + switch (length) + { + case 1: + { + value = ReadByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + case 4: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + value = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + if (requestHint != 0) + { + value = default; + return false; + } + break; + } + case NumberKind.Single: + { + var real = ReadValue(source, offset: 1, out requestHint); + if (requestHint != 0) + { + value = default; + return false; + } + + if (real > UInt64.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + if (real < UInt64.MinValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((UInt64)real); + break; + } + default: + { + Debug.Assert(kind == NumberKind.Double, $"kind({kind}) == NumberType.Double"); + var real = ReadValue(source, offset: 1, out requestHint); + if (requestHint != 0) + { + value = default; + return false; + } + + if (real > UInt64.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + if (real < UInt64.MinValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((UInt64)real); + break; + } + } + + return true; + } + + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.tt b/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.tt new file mode 100644 index 000000000..898d74a57 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.tt @@ -0,0 +1,262 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ include file="./MessagePackDecoder.Number.ttinclude" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class MessagePackDecoder + { +<# +// -------------- Decode() ------------------- + +// Signed +foreach (var type in + new [] + { + "SByte", + "Int16", + "Int32" + } +) +{ + this.WriteMethod(type, isSigned: true, is64Bit: false); +} + +this.WriteMethod("Int64", isSigned: true, is64Bit: true); + +// Unsigned +foreach (var type in + new [] + { + "Byte", + "UInt16", + "UInt32" + } +) +{ + this.WriteMethod(type, isSigned: false, is64Bit: false); +} + +this.WriteMethod("UInt64", isSigned: false, is64Bit: true); + +// ------------- Decode[Uns|S]ignedInteger ------------ +foreach (var isSigned in new [] { true, false }) +{ + var methodName = $"TryDecode{(isSigned ? "Signed" : "Unsigned")}Integer"; + var outType = isSigned ? "Int64": "UInt64"; +#> + + private bool <#= methodName #>(in SequenceReader source, Type type, out byte header, out <#= outType #> value, out int requestHint) + { + if (!this.TryPeek(source, out header)) + { + requestHint = 1; + value = default; + return false; + } + + requestHint = 0; + + if (header < 128) + { + value = header; + return true; + } + +<# + if (isSigned) + { +#> + if (header >= 0xE0) + { + value = unchecked((sbyte)header); + return true; + } + +<# + } // if (isSigned) +#> + ParseNumberHeader(header, source, type, out var length, out var kind); + + if ((kind & NumberKind.RealBitMask) != 0 && !this.Options.CanTreatRealAsInteger) + { + MessagePackThrow.RealCannotBeInteger(header, source.Consumed, type); + } + + switch (kind) + { + case NumberKind.Signed: + { +<# + if (isSigned) + { + WriteDecodeInteger("value", isSigned: true); +#> + if (requestHint != 0) + { + value = default; + return false; + } +<# + } + else + { +#> + long signed; +<# + WriteDecodeInteger("signed", isSigned: true); +#> + if (requestHint != 0) + { + value = default; + return false; + } + + if (signed < 0) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((ulong)signed); +<# + } // if (isSigned) +#> + break; + } + case NumberKind.Unsigned: + { +<# + if (isSigned) + { +#> + ulong unsigned; +<# + WriteDecodeInteger("unsigned", isSigned: false); +#> + if (requestHint != 0) + { + value = default; + return false; + } + + if (unsigned > Int64.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((<#= outType #>)unsigned); +<# + } + else + { + WriteDecodeInteger("value", isSigned: false); +#> + if (requestHint != 0) + { + value = default; + return false; + } +<# + } // if (isSigned) +#> + break; + } + case NumberKind.Single: + { + var real = ReadValue(source, offset: 1, out requestHint); + if (requestHint != 0) + { + value = default; + return false; + } + + if (real > <#= outType #>.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + if (real < <#= outType #>.MinValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((<#= outType #>)real); + break; + } + default: + { + Debug.Assert(kind == NumberKind.Double, $"kind({kind}) == NumberType.Double"); + var real = ReadValue(source, offset: 1, out requestHint); + if (requestHint != 0) + { + value = default; + return false; + } + + if (real > <#= outType #>.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + if (real < <#= outType #>.MinValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, type); + } + + value = unchecked((<#= outType #>)real); + break; + } + } + + return true; + } + +<# +} // foreach (var isSigned) +#> + } +} +<#+ +void WriteMethod(string type, bool isSigned, bool is64Bit) +{ + var coreMethod = $"TryDecode{(isSigned ? "Signed" : "Unsigned")}Integer"; + var resultType = isSigned ? "long" : "ulong"; +#> + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override <#= type #> Decode<#= type #>(in SequenceReader source, out int requestHint) + { + if (!this.<#= coreMethod #>(source, typeof(<#= type #>), out var header, out var result, out requestHint)) + { + return default; + } +<#+ + if (!is64Bit) + { +#> + if (result < <#= type #>.MinValue || result > <#= type #>.MaxValue) + { + MessagePackThrow.IsNotType(header, source.Consumed, typeof(<#= type #>)); + } + +<#+ + } // if (!is64Bit) +#> + return unchecked((<#= type #>)result); + } + +<#+ +} +#> diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Iteration.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Iteration.cs new file mode 100644 index 000000000..3dbb8f0b1 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Iteration.cs @@ -0,0 +1,67 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Internal +{ + public partial class MessagePackDecoder + { + private readonly CollectionItemIterator.CollectionEndDetection _detectCollectionEnds; + + // This is instance method now because CLR/CoreCLR delegates are optmized for instance method invocation. + // This method might be changed static if C# supports function pointer. + private bool DetectCollectionEnds(in SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) + { + requestHint = 0; + + if (nextItemIndex >= itemsCount) + { + return false; + } + + nextItemIndex++; + return true; + } + + public override CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator, out int requestHint) + { + var consumed = 0L; + var type = this.PrivateDecodeArrayOrMapHeader(source, ref consumed, out _, out var itemsCount, out requestHint); + if (requestHint != 0) + { + iterator = default; + return default; + } + + iterator = this.CreateIterator(itemsCount); + return type; + } + + private CollectionItemIterator CreateIterator(long itemsCount) + => new CollectionItemIterator(this._detectCollectionEnds, itemsCount); + + public override CollectionItemIterator DecodeArray(in SequenceReader source, out int requestHint) + { + var itemsCount = this.DecodeArrayHeader(source, out requestHint); + if (requestHint != 0) + { + return default; + } + + return new CollectionItemIterator(this._detectCollectionEnds, itemsCount); + } + + public override CollectionItemIterator DecodeMap(in SequenceReader source, out int requestHint) + { + var itemsCount = this.DecodeMapHeader(source, out requestHint); + if (requestHint != 0) + { + return default; + } + + return new CollectionItemIterator(this._detectCollectionEnds, itemsCount); + } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.cs new file mode 100644 index 000000000..0ca842cdc --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.cs @@ -0,0 +1,161 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class MessagePackDecoder + { + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Byte? DecodeNullableByte(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeByte(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override SByte? DecodeNullableSByte(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeSByte(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int16? DecodeNullableInt16(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeInt16(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt16? DecodeNullableUInt16(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeUInt16(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int32? DecodeNullableInt32(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeInt32(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt32? DecodeNullableUInt32(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeUInt32(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int64? DecodeNullableInt64(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeInt64(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt64? DecodeNullableUInt64(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeUInt64(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Single? DecodeNullableSingle(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeSingle(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Double? DecodeNullableDouble(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeDouble(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Boolean? DecodeNullableBoolean(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeBoolean(source, out requestHint); + } + + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.tt b/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.tt new file mode 100644 index 000000000..b73b9c7fc --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.tt @@ -0,0 +1,55 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class MessagePackDecoder + { +<# +foreach (var type in + new [] + { + "Byte", + "SByte", + "Int16", + "UInt16", + "Int32", + "UInt32", + "Int64", + "UInt64", + "Single", + "Double", + "Boolean" + } +) +{ +#> + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override <#= type #>? DecodeNullable<#= type #>(in SequenceReader source, out int requestHint) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.Decode<#= type #>(source, out requestHint); + } + +<# +} // foreach (var type) +#> + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Number.ttinclude b/src/MsgPack.Core/Internal/MessagePackDecoder.Number.ttinclude new file mode 100644 index 000000000..33d1a85d5 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Number.ttinclude @@ -0,0 +1,32 @@ +<#+ +void WriteDecodeInteger(string variable, bool isSigned) +{ +#> + switch (length) + { + case 1: + { + <#= variable #> = Read<#= isSigned ? "SByte" : "Byte" #>(source, offset: 1, out requestHint); + break; + } + case 2: + { + <#= variable #> = ReadValue<<#= isSigned ? "short" : "ushort" #>>(source, offset: 1, out requestHint); + break; + } + case 4: + { + <#= variable #> = ReadValue<<#= isSigned ? "int" : "uint" #>>(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + <#= variable #> = ReadValue<<#= isSigned ? "long" : "ulong" #>>(source, offset: 1, out requestHint); + break; + } + } + +<#+ +} +#> diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.cs new file mode 100644 index 000000000..0d5d871e7 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.cs @@ -0,0 +1,223 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class MessagePackDecoder + { + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Single DecodeSingle(in SequenceReader source, out int requestHint) + { + if (!this.TryPeek(source, out var header)) + { + requestHint = 1; + return default; + } + + requestHint = 0; + + if (header < 128 || header >= 0xE0) + { + source.Advance(1); + return header; + } + + ParseNumberHeader(header, source, typeof(Single), out var length, out var kind); + + Single value; + switch (kind) + { + case NumberKind.Signed: + { + switch (length) + { + case 1: + { + value = ReadSByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + case 4: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + value = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + break; + } + case NumberKind.Unsigned: + { + switch (length) + { + case 1: + { + value = ReadByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + case 4: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + value = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + break; + } + case NumberKind.Single: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + // Double + value = (Single)ReadValue(source, offset: 1, out requestHint); + break; + } + } + + if (requestHint != 0) + { + return default; + } + + return value; + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Double DecodeDouble(in SequenceReader source, out int requestHint) + { + if (!this.TryPeek(source, out var header)) + { + requestHint = 1; + return default; + } + + requestHint = 0; + + if (header < 128 || header >= 0xE0) + { + source.Advance(1); + return header; + } + + ParseNumberHeader(header, source, typeof(Double), out var length, out var kind); + + Double value; + switch (kind) + { + case NumberKind.Signed: + { + switch (length) + { + case 1: + { + value = ReadSByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + case 4: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + value = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + break; + } + case NumberKind.Unsigned: + { + switch (length) + { + case 1: + { + value = ReadByte(source, offset: 1, out requestHint); + break; + } + case 2: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + case 4: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + Debug.Assert(length == 8, $"length({length}) != 8"); + value = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + break; + } + case NumberKind.Single: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + // Double + value = ReadValue(source, offset: 1, out requestHint); + break; + } + } + + if (requestHint != 0) + { + return default; + } + + return value; + } + + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.tt b/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.tt new file mode 100644 index 000000000..cd5e02e09 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.tt @@ -0,0 +1,100 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ include file="./MessagePackDecoder.Number.ttinclude" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class MessagePackDecoder + { +<# +foreach (var type in new [] { "Single", "Double" }) +{ +#> + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override <#= type #> Decode<#= type #>(in SequenceReader source, out int requestHint) + { + if (!this.TryPeek(source, out var header)) + { + requestHint = 1; + return default; + } + + requestHint = 0; + + if (header < 128 || header >= 0xE0) + { + source.Advance(1); + return header; + } + + ParseNumberHeader(header, source, typeof(<#= type #>), out var length, out var kind); + + <#= type #> value; + switch (kind) + { + case NumberKind.Signed: + { +<# + WriteDecodeInteger("value", isSigned: true); +#> + break; + } + case NumberKind.Unsigned: + { +<# + WriteDecodeInteger("value", isSigned: false); +#> + break; + } + case NumberKind.Single: + { + value = ReadValue(source, offset: 1, out requestHint); + break; + } + default: + { + // Double +<# + if (type != "Double") + { +#> + value = (<#= type #>)ReadValue(source, offset: 1, out requestHint); +<# + } + else + { +#> + value = ReadValue(source, offset: 1, out requestHint); +<# + } +#> + break; + } + } + + if (requestHint != 0) + { + return default; + } + + return value; + } + +<# +} // foreach (var isSigned) +#> + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.SkipDrain.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.SkipDrain.cs new file mode 100644 index 000000000..2f343f721 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.SkipDrain.cs @@ -0,0 +1,281 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace MsgPack.Internal +{ + public partial class MessagePackDecoder + { + public sealed override void Skip(in SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default) + => this.Drain(source, collectionContext, itemsCount: 1, out requestHint, cancellationToken); + + public override void Drain(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default) + { + var consumed = 0L; + if (!this.SkipItems(source, collectionContext, itemsCount, ref consumed, out requestHint, cancellationToken)) + { + source.Rewind(consumed); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool SkipLength(in SequenceReader source, long length, ref long consumed, out int requestHint) + { + if (source.Remaining < length) + { + requestHint = (int)((length - source.Remaining) & Int32.MaxValue); + return false; + } + + requestHint = 0; + source.Advance(length); + consumed += length; + return true; + } + + private bool SkipArray(in SequenceReader source, in CollectionContext collectionContext, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) + { + collectionContext.IncrementDepth(); + + var initialConsumed = consumed; + var type = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out var header, out var arrayLength, out requestHint); + if (requestHint != 0) + { + return false; + } + + if (!type.IsArray) + { + MessagePackThrow.TypeIsNotArray(header, source.Consumed - consumed + initialConsumed); + } + + if (!this.SkipItems(source, collectionContext, arrayLength, ref consumed, out requestHint, cancellationToken)) + { + return false; + } + + collectionContext.DecrementDepth(); + return true; + } + + private bool SkipMap(in SequenceReader source, in CollectionContext collectionContext, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) + { + collectionContext.IncrementDepth(); + + var initialConsumed = consumed; + var type = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out var header, out var mapCount, out requestHint); + if (requestHint != 0) + { + return false; + } + + if (!type.IsMap) + { + MessagePackThrow.TypeIsNotMap(header, source.Consumed - consumed + initialConsumed); + } + + if (!this.SkipItems(source, collectionContext, mapCount * 2, ref consumed, out requestHint, cancellationToken)) + { + return false; + } + + collectionContext.DecrementDepth(); + return true; + } + + private bool SkipItems(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) + { + while (itemsCount > 0) + { + if (!this.TryPeek(source, out var header)) + { + requestHint = 1; + return false; + } + + consumed++; + + var length = 0L; + + if (header >= MessagePackCode.MinimumFixedArray && header <= MessagePackCode.MaximumFixedArray) + { + if (!this.SkipArray(source, collectionContext, ref consumed, out requestHint, cancellationToken)) + { + return false; + } + } + else if (header >= MessagePackCode.MinimumFixedMap && header <= MessagePackCode.MaximumFixedMap) + { + if (!this.SkipMap(source, collectionContext, ref consumed, out requestHint, cancellationToken)) + { + return false; + } + } + else if (header >= MessagePackCode.MinimumFixedRaw && header <= MessagePackCode.MaximumFixedRaw) + { + length = header - MessagePackCode.MinimumFixedRaw; + } + else + { + switch (header) + { + case MessagePackCode.SignedInt8: + case MessagePackCode.UnsignedInt8: + { + length = 1; + break; + } + case MessagePackCode.SignedInt16: + case MessagePackCode.UnsignedInt16: + case MessagePackCode.Real32: + case MessagePackCode.SignedInt32: + case MessagePackCode.UnsignedInt32: + { + length = 4; + break; + } + case MessagePackCode.Real64: + case MessagePackCode.SignedInt64: + case MessagePackCode.UnsignedInt64: + { + length = 8; + break; + } + case MessagePackCode.Bin8: + { + if (!source.TryRead(out var b)) + { + requestHint = 1; + return false; + } + + length = b; + consumed++; + break; + } + case MessagePackCode.Bin16: + case MessagePackCode.Raw16: + { + length = ReadValue(source, offset: 0, out requestHint); + if (requestHint != 0) + { + source.Rewind(consumed); + return false; + } + + consumed += sizeof(ushort); + break; + } + case MessagePackCode.Bin32: + case MessagePackCode.Raw32: + { + length = ReadValue(source, offset: 0, out requestHint); + if (requestHint != 0) + { + source.Rewind(consumed); + return false; + } + + consumed += sizeof(int); + break; + } + case MessagePackCode.FixExt1: + { + length = 2; + break; + } + case MessagePackCode.FixExt2: + { + length = 3; + break; + } + case MessagePackCode.FixExt4: + { + length = 5; + break; + } + case MessagePackCode.FixExt8: + { + length = 9; + break; + } + case MessagePackCode.FixExt16: + { + length = 17; + break; + } + case MessagePackCode.Ext8: + { + if (!source.TryRead(out var b)) + { + requestHint = 1; + return false; + } + + length = b + 1; + consumed++; + break; + } + case MessagePackCode.Ext16: + { + length = ReadValue(source, offset: 0, out requestHint); + if (requestHint != 0) + { + source.Rewind(consumed); + return false; + } + + consumed += sizeof(ushort) + 1; + break; + } + case MessagePackCode.Ext32: + { + length = ReadValue(source, offset: 0, out requestHint); + if (requestHint != 0) + { + source.Rewind(consumed); + return false; + } + + consumed += sizeof(int) + 1; + break; + } + case MessagePackCode.Array16: + case MessagePackCode.Array32: + { + if (!this.SkipArray(source, collectionContext, ref consumed, out requestHint, cancellationToken)) + { + return false; + } + break; + } + case MessagePackCode.Map16: + case MessagePackCode.Map32: + { + if (!this.SkipMap(source, collectionContext, ref consumed, out requestHint, cancellationToken)) + { + return false; + } + break; + } + } + } + + if (length > 0 && !SkipLength(source, length, ref consumed, out requestHint)) + { + return false; + } + + itemsCount--; + } // while (itemsCount > 0) + + requestHint = 0; + return true; + } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Strings.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Strings.cs new file mode 100644 index 000000000..04f043e73 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Strings.cs @@ -0,0 +1,283 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; + +namespace MsgPack.Internal +{ + public partial class MessagePackDecoder + { + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override bool GetRawString(in SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default) + { + var length = this.PeekRawStringLength(source, out requestHint, cancellationToken); + if (length < 0) + { + rawString = default; + return false; + } + + if (source.UnreadSpan.Length < length) + { + rawString = source.UnreadSpan.Slice(0, length); + source.Advance(length); + requestHint = 0; + return true; + } + + return this.GetRawStringMultiSegment(source, out rawString, out requestHint, length, cancellationToken); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private bool GetRawStringMultiSegment(in SequenceReader source, out ReadOnlySpan rawString, out int requestHint, int length, CancellationToken cancellationToken) + { + if (source.Remaining < length) + { + requestHint = length - (int)source.Remaining; + rawString = default; + return false; + } + + var result = new byte[length]; + var bufferSpan = result.AsSpan(); + + var copyLength = Math.Min(this.Options.CancellationSupportThreshold, length); + while (bufferSpan.Length > 0) + { + var destination = bufferSpan.Slice(0, copyLength); + var shouldTrue = source.TryCopyTo(destination); + Debug.Assert(shouldTrue, "SequenceReader.Remaining lied."); + + bufferSpan = bufferSpan.Slice(copyLength); + source.Advance(copyLength); + + cancellationToken.ThrowIfCancellationRequested(); + } + + rawString = result; + requestHint = 0; + return true; + } + + private int PeekRawStringLength(in SequenceReader source, out int requestHint, CancellationToken cancellationToken) + { + var length = this.DecodeStringHeader(source, out var header, out requestHint, out _); + if (requestHint != 0) + { + return default; + } + + if (length > Int32.MaxValue) + { + MessagePackThrow.TooLargeByteLength(header, source.Consumed, length); + } + + return (int)length; + } + + private long DecodeStringHeader(in SequenceReader source, out byte header, out int requestHint, out int consumed) + { + if (!this.TryPeek(source, out header)) + { + requestHint = 1; + consumed = 0; + return 0; + } + + if (header >= MessagePackCode.MinimumFixedRaw && header <= MessagePackCode.MaximumFixedRaw) + { + requestHint = 0; + consumed = 1; + return header - MessagePackCode.MinimumFixedRaw; + } + + long length; + switch (header) + { + case MessagePackCode.Str8: + { + length = ReadByte(source, offset: 1, out requestHint); + consumed = 2; + break; + } + case MessagePackCode.Str16: + { + length = ReadValue(source, offset: 1, out requestHint); + consumed = 3; + break; + } + case MessagePackCode.Str32: + { + length = ReadValue(source, offset: 1, out requestHint); + consumed = 5; + break; + } + default: + { + MessagePackThrow.IsNotUtf8String(header, source.Consumed); + // never + requestHint = default; + consumed = default; + return 0; + } + } + + return requestHint == 0 ? length : 0; + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override string? DecodeNullableString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeString(source, out requestHint, encoding, cancellationToken); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override string? DecodeString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + var length = this.DecodeStringHeader(source, out _, out requestHint, out var consumed); + if (requestHint != 0) + { + return default; + } + + if (source.Remaining < length + consumed) + { + requestHint = (int)((length + consumed - source.Remaining) & Int32.MaxValue); + return default; + } + + if (length > this.Options.MaxBinaryLengthInBytes) + { + Throw.StringLengthExceeded(source.Consumed - consumed, length, this.Options.MaxBinaryLengthInBytes); + } + + if (encoding == null && length <= this.Options.CancellationSupportThreshold) + { + // fast-path + var value = Utf8EncodingNonBom.Instance.GetString(source.UnreadSpan.Slice(consumed, (int)length)); + source.Advance(length + consumed); + requestHint = 0; + return value; + } + + var result = (encoding ?? Utf8EncodingNonBom.Instance).GetStringMultiSegment(source.Sequence.Slice(consumed), this.Options.CharBufferPool, cancellationToken); + source.Advance(length + consumed); + requestHint = 0; + return result; + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override byte[]? DecodeNullableBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + { + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeBinary(source, out requestHint, cancellationToken); + } + + private int DecodeBinaryHeader(in SequenceReader source, out int requestHint, out int consumed) + { + if (!this.TryPeek(source, out var header)) + { + requestHint = 1; + consumed = 0; + return 0; + } + + if (header >= MessagePackCode.MinimumFixedRaw && header <= MessagePackCode.MaximumFixedRaw) + { + requestHint = 0; + consumed = 1; + return header - MessagePackCode.MinimumFixedRaw; + } + + int length; + switch (header) + { + case MessagePackCode.Str8: + case MessagePackCode.Bin8: + { + length = ReadByte(source, offset: 1, out requestHint); + consumed = 2; + break; + } + case MessagePackCode.Str16: + case MessagePackCode.Bin16: + { + length = ReadValue(source, offset: 1, out requestHint); + consumed = 3; + break; + } + case MessagePackCode.Str32: + case MessagePackCode.Bin32: + { + length = ReadValue(source, offset: 1, out requestHint); + if (length < 0) + { + MessagePackThrow.TooLargeByteLength(header, source.Consumed - 5, unchecked((uint)length)); + } + + consumed = 5; + break; + } + default: + { + MessagePackThrow.IsNotUtf8String(header, source.Consumed); + // never + requestHint = default; + consumed = default; + return 0; + } + } + + return requestHint == 0 ? length : 0; + } + + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override byte[]? DecodeBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + { + var length = this.DecodeBinaryHeader(source, out requestHint, out var consumed); + if(requestHint != 0) + { + return default; + } + + if (length > this.Options.MaxBinaryLengthInBytes) + { + Throw.BinaryLengthExceeded(source.Consumed - consumed, length, this.Options.MaxBinaryLengthInBytes); + } + + if(source.Remaining < length + consumed) + { + requestHint = length + consumed - (int)source.Remaining; + return default; + } + + // This line may throw OutOfMemoryException, but we cannot determine the OOM is caused by heap exhausion or excess of the implementation specific max length of arrays. + // So, we just throws OOM for such conditions. + var result = new byte[length]; + var shouldBeTrue = source.TryCopyTo(result); + Debug.Assert(shouldBeTrue, "SequenceReader.Remaining lied."); + source.Advance(length); + return result; + } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.cs new file mode 100644 index 000000000..177a22159 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.cs @@ -0,0 +1,184 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MsgPack.Internal +{ + public sealed partial class MessagePackDecoder : Decoder + { + private static readonly FormatFeatures MessagePackFormatFeatures = + new FormatFeaturesBuilder + { + IsContextful = false, + CanCountCollectionItems = true, + CanSpecifyStringEncoding = true + }.Build(); + + public MessagePackDecoder(MessagePackDecoderOptions options) + : base(options, MessagePackFormatFeatures) + { + this._detectCollectionEnds = this.DetectCollectionEnds; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override bool DecodeBoolean(in SequenceReader source, out int requestHint) + { + if (!this.TryPeek(source, out var header)) + { + requestHint = 1; + return false; + } + + requestHint = 0; + switch (header) + { + case MessagePackCode.TrueValue: + { + source.Advance(1); + return true; + } + case MessagePackCode.FalseValue: + { + source.Advance(1); + return false; + } + default: + { + MessagePackThrow.IsNotType(header, source.Consumed, typeof(bool)); + // Never reached. + return false; + } + } + } + + private bool TryReadNull(in SequenceReader source) + { + if (this.TryPeek(source, out var b) && b == MessagePackCode.NilValue) + { + source.Advance(1); + return true; + } + + return false; + } + + private static void ParseNumberHeader(byte header, in SequenceReader source, Type type, out int length, out NumberKind kind) + { + // 0xD0-D3 -- SignedIntN + // 1101-0000 -> 1101-0011 + // 0xCC-CF -- UnsignedIntN + // 1100-1100 -> 1100-1111 + // 0XCA-CB -- RealN + // 1100-1010 -> 1100-1011 + + if ((header & 0xD3) == header) + { + // SignedIntN + length = (int)Math.Pow(2, (header & 0x3)) + 1; + kind = NumberKind.Signed; + } + else if ((header & 0xCC) == header) + { + // UnsignedIntN + length = (int)Math.Pow(2, (header & 0x3)) + 1; + kind = NumberKind.Unsigned; + } + if (header == MessagePackCode.Real64) + { + length = 9; + kind = NumberKind.Double; + } + else if (header == MessagePackCode.Real32) + { + length = 5; + kind = NumberKind.Single; + } + else + { + MessagePackThrow.IsNotNumber(header, source.Consumed, type); + length = default; + kind = NumberKind.Unknown; + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static byte ReadByte(in SequenceReader source, int offset, out int requestHint) + { + if (source.UnreadSpan.Length > offset + 1) + { + var result = source.UnreadSpan[offset]; + source.Advance(offset + 1); + requestHint = 0; + return result; + } + + return ReadByteMultiSegment(source, offset, out requestHint); + } + + private static byte ReadByteMultiSegment(in SequenceReader source, int offset, out int requestHint) + { + if (source.Remaining < offset + 1) + { + requestHint = 1; + return default; + } + + Span buffer = stackalloc byte[offset + 1]; + source.TryCopyTo(buffer); + source.Advance(offset + 1); + requestHint = 0; + return buffer[offset]; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static sbyte ReadSByte(in SequenceReader source, int offset, out int requestHint) + => unchecked((sbyte)ReadByte(source, offset, out requestHint)); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static unsafe T ReadValue(in SequenceReader source, int offset, out int requestHint) + where T : unmanaged + { + if (source.UnreadSpan.Length > offset + sizeof(T)) + { + var result = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(source.UnreadSpan.Slice(offset))); + source.Advance(offset + sizeof(T)); + requestHint = 0; + return result; + } + + return ReadMultiSegment(source, offset, out requestHint); + } + + private static unsafe T ReadMultiSegment(in SequenceReader source, int offset, out int requestHint) + where T : unmanaged + { + if (source.Remaining < offset + sizeof(T)) + { + requestHint = offset + sizeof(T) - (int)source.Remaining; + return default; + } + + Span buffer = stackalloc byte[offset + sizeof(T)]; + source.TryCopyTo(buffer); + source.Advance(offset + sizeof(T)); + requestHint = 0; + return Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(buffer.Slice(offset))); + } + + private enum NumberKind + { + Unknown = 0, + Unsigned = 0x1, + Signed = 0x2, + Half = 0x11, + Single = 0x12, + Double = 0x13, + RealBitMask = 0x10 + } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs b/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs new file mode 100644 index 000000000..b4415c2a4 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs @@ -0,0 +1,12 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Internal +{ + public sealed class MessagePackDecoderOptions : DecoderOptions + { + internal MessagePackDecoderOptions(MessagePackDecoderOptionsBuilder builder) + : base(builder) { } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackDecoderOptionsBuilder.cs b/src/MsgPack.Core/Internal/MessagePackDecoderOptionsBuilder.cs new file mode 100644 index 000000000..291e429b8 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackDecoderOptionsBuilder.cs @@ -0,0 +1,13 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Internal +{ + public sealed class MessagePackDecoderOptionsBuilder : DecoderOptionsBuilder + { + public MessagePackDecoderOptionsBuilder() { } + + public MessagePackDecoderOptions Build() => new MessagePackDecoderOptions(this); + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.cs new file mode 100644 index 000000000..d38e4fd18 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.cs @@ -0,0 +1,190 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class MessagePackEncoder + { + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeInt32(Int32 value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var leastByte = value & 0x000000FF; + if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) + { + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)(leastByte)); + buffer.Advance(1); + } + else + { + this.EncodeSlow(value, buffer); + } + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeUInt32(UInt32 value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var leastByte = value & 0x000000FF; + if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) + { + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)(leastByte)); + buffer.Advance(1); + } + else + { + this.EncodeSlow(value, buffer); + } + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeInt64(Int64 value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var leastByte = value & 0x000000FF; + if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) + { + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)(leastByte)); + buffer.Advance(1); + } + else + { + this.EncodeSlow(value, buffer); + } + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeUInt64(UInt64 value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var leastByte = value & 0x000000FF; + if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) + { + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)(leastByte)); + buffer.Advance(1); + } + else + { + this.EncodeSlow(value, buffer); + } + } + + private void EncodeSlow(int value, IBufferWriter buffer) + { + if (value >= SByte.MinValue && value <= SByte.MaxValue) + { + // SingedInt8 (SByte) + var span = buffer.GetSpan(2); + span[1] = unchecked((byte)(sbyte)value); + span[0] = MessagePackCode.SignedInt8; + buffer.Advance(2); + } + else if (value >= Int16.MinValue && value <= Int16.MaxValue) + { + // SignedInt16 (Int16) + var span = buffer.GetSpan(3); + span[0] = MessagePackCode.SignedInt16; + span = span.Slice(1); + BinaryPrimitives.WriteInt16BigEndian(span, unchecked((Int16)value)); + buffer.Advance(3); + } + else + { + // SignedInt32 (Int32) + var span = buffer.GetSpan(5); + span[0] = MessagePackCode.SignedInt32; + span = span.Slice(1); + BinaryPrimitives.WriteInt32BigEndian(span, unchecked((Int32)value)); + buffer.Advance(5); + } + } // EncodeSlow(int) + + private void EncodeSlow(uint value, IBufferWriter buffer) + { + if (value <= Byte.MaxValue) + { + // UnsingedInt8 (Byte) + var span = buffer.GetSpan(2); + span[1] = unchecked((byte)value); + span[0] = MessagePackCode.UnsignedInt8; + buffer.Advance(2); + } + else if (value <= UInt16.MaxValue) + { + // UnsignedInt16 (UInt16) + var span = buffer.GetSpan(3); + span[0] = MessagePackCode.UnsignedInt16; + span = span.Slice(1); + BinaryPrimitives.WriteUInt16BigEndian(span, unchecked((UInt16)value)); + buffer.Advance(3); + } + else + { + // UnsignedInt32 (UInt32) + var span = buffer.GetSpan(5); + span[0] = MessagePackCode.UnsignedInt32; + span = span.Slice(1); + BinaryPrimitives.WriteUInt32BigEndian(span, unchecked((UInt32)value)); + buffer.Advance(5); + } + } // EncodeSlow(uint) + + private void EncodeSlow(long value, IBufferWriter buffer) + { + if (value <= Int32.MaxValue && value >= Int32.MinValue) + { + this.EncodeSlow(unchecked((int)value), buffer); + } + else + { + // SignedInt64 (Int64) + var span = buffer.GetSpan(9); + span[0] = MessagePackCode.SignedInt64; + span = span.Slice(1); + BinaryPrimitives.WriteInt64BigEndian(span, unchecked((Int64)value)); + buffer.Advance(9); + } + } // EncodeSlow(long) + + private void EncodeSlow(ulong value, IBufferWriter buffer) + { + if (value <= UInt32.MaxValue) + { + this.EncodeSlow(unchecked((uint)value), buffer); + } + else + { + // UnsignedInt64 (UInt64) + var span = buffer.GetSpan(9); + span[0] = MessagePackCode.UnsignedInt64; + span = span.Slice(1); + BinaryPrimitives.WriteUInt64BigEndian(span, unchecked((UInt64)value)); + buffer.Advance(9); + } + } // EncodeSlow(ulong) + + } +} + diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.tt b/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.tt new file mode 100644 index 000000000..d82abb48f --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.tt @@ -0,0 +1,137 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + partial class MessagePackEncoder + { +<# +foreach (var inputType in new []{ "Int32", "UInt32", "Int64", "UInt64" }) +{ +#> + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void Encode<#= inputType #>(<#= inputType #> value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var leastByte = value & 0x000000FF; + if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) + { + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)(leastByte)); + buffer.Advance(1); + } + else + { + this.EncodeSlow(value, buffer); + } + } + +<# +} // public int/long +#> + private void EncodeSlow(int value, IBufferWriter buffer) + { + if (value >= SByte.MinValue && value <= SByte.MaxValue) + { + // SingedInt8 (SByte) + var span = buffer.GetSpan(2); + span[1] = unchecked((byte)(sbyte)value); + span[0] = MessagePackCode.SignedInt8; + buffer.Advance(2); + } + else if (value >= Int16.MinValue && value <= Int16.MaxValue) + { +<# + WriteEncodeSlowBody("Int16", sizeof(short) + 1, "SignedInt16"); +#> + } + else + { +<# + WriteEncodeSlowBody("Int32", sizeof(int) + 1, "SignedInt32"); +#> + } + } // EncodeSlow(int) + + private void EncodeSlow(uint value, IBufferWriter buffer) + { + if (value <= Byte.MaxValue) + { + // UnsingedInt8 (Byte) + var span = buffer.GetSpan(2); + span[1] = unchecked((byte)value); + span[0] = MessagePackCode.UnsignedInt8; + buffer.Advance(2); + } + else if (value <= UInt16.MaxValue) + { +<# + WriteEncodeSlowBody("UInt16", sizeof(ushort) + 1, "UnsignedInt16"); +#> + } + else + { +<# + WriteEncodeSlowBody("UInt32", sizeof(uint) + 1, "UnsignedInt32"); +#> + } + } // EncodeSlow(uint) + + private void EncodeSlow(long value, IBufferWriter buffer) + { + if (value <= Int32.MaxValue && value >= Int32.MinValue) + { + this.EncodeSlow(unchecked((int)value), buffer); + } + else + { +<# + WriteEncodeSlowBody("Int64", sizeof(long) + 1, "SignedInt64"); +#> + } + } // EncodeSlow(long) + + private void EncodeSlow(ulong value, IBufferWriter buffer) + { + if (value <= UInt32.MaxValue) + { + this.EncodeSlow(unchecked((uint)value), buffer); + } + else + { +<# + WriteEncodeSlowBody("UInt64", sizeof(ulong) + 1, "UnsignedInt64"); +#> + } + } // EncodeSlow(ulong) + + } +} + +<#+ +private void WriteEncodeSlowBody(string typeName, int size, string code) +{ +#> + // <#= code #> (<#= typeName #>) + var span = buffer.GetSpan(<#= size #>); + span[0] = MessagePackCode.<#= code #>; + span = span.Slice(1); + BinaryPrimitives.Write<#= typeName #>BigEndian(span, unchecked((<#= typeName #>)value)); + buffer.Advance(<#= size #>); +<#+ +} +#> diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs new file mode 100644 index 000000000..cd5b82f23 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs @@ -0,0 +1,272 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace MsgPack.Internal +{ + partial class MessagePackEncoder + { + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + buffer = EnsureNotNull(buffer); + encoding = encoding ?? Utf8EncodingNonBom.Instance; + if (value.Length == 0) + { + buffer.GetSpan(1)[0] = MessagePackCode.MinimumFixedRaw; + buffer.Advance(1); + return; + } + + if (value.Length < 256) + { + var valueLength = unchecked((int)value.Length); + var maxByteLength = encoding.GetMaxByteCount(valueLength); + + if (maxByteLength < 32) + { + // stack alloc is fastest + Span encodingBuffer = stackalloc byte[maxByteLength]; + var actualLength = encoding.GetBytes(value, encodingBuffer); + var sink = buffer.GetSpan(actualLength + 1); + sink[0] = unchecked((byte)(MessagePackCode.MinimumFixedRaw | actualLength)); + encodingBuffer.Slice(actualLength).CopyTo(sink.Slice(1)); + buffer.Advance(sink.Length); + return; + } + else if (maxByteLength < 256) + { + // stack alloc is fastest + Span encodingBuffer = stackalloc byte[maxByteLength + 3]; + var actualLength = encoding.GetBytes(value, encodingBuffer); + var sink = buffer.GetSpan(3); + var headerLength = this.EncodeStringHeader(unchecked((uint)actualLength), sink); + buffer.Advance(headerLength); + encodingBuffer.Slice(0, actualLength).CopyTo(buffer.GetSpan(actualLength)); + buffer.Advance(actualLength); + return; + } + } + + this.EncodeStringSlow(value, buffer, encoding); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void EncodeStringSlow(ReadOnlySpan value, IBufferWriter buffer, Encoding encoding) + { + // Use 1-path logic borrowed from MessagePack C# + + // Get max char bytes. Use 2 for 0 (maybe bug) or 1 (ASCII) to simplify following logic. + var maxBytesPerChar = Math.Max(encoding.GetMaxByteCount(1), 2); + + // In most case, msgpack uses UTF-8 and clob contains 1 to 3 byte chars, + // and may charactors are ASCII. + // So, estimate with 1char is 2bytes is good enough here. + var estimatedLength = unchecked((uint)value.Length * 2); + var bufferLength = encoding.GetMaxByteCount(unchecked((int)value.Length)) + 5; + + if (bufferLength <= 0x20_0000) // 2MB + { + Memory sinkMemory = buffer.GetMemory(bufferLength); + + if (MemoryMarshal.TryGetArray(sinkMemory, out var sinkBufferSegment)) + { + // We can get single array + buffer.Advance(this.EncodeLargeString1Path(value, buffer, encoding, estimatedLength, sinkBufferSegment)); + } + else + { + // Memory is not array backend, so use local buffer. + var encodingBuffer = base.Options.ByteBufferPool.Rent(bufferLength); + try + { + var totalLength = this.EncodeLargeString1Path(value, buffer, encoding, estimatedLength, encodingBuffer); + buffer.Write(encodingBuffer.AsMemory(0, totalLength).Span); + } + finally + { + base.Options.ByteBufferPool.Return(encodingBuffer, base.Options.ClearsBuffer); + } + } + } + + this.EncodeHugeString2Path(value, buffer, encoding); + } + + + private unsafe int EncodeLargeString1Path(ReadOnlySpan value, IBufferWriter buffer, Encoding encoding, uint estimatedLength, ArraySegment sinkBufferSegment) + { + ref var head = ref sinkBufferSegment.Array![sinkBufferSegment.Offset]; + + Span sinkBuffer = sinkBufferSegment; + var estimatedHeaderLength = this.EncodeStringHeader(estimatedLength, sinkBuffer); + + int actualLength; + + try + { + actualLength = encoding.GetBytes(value, sinkBuffer.Slice(estimatedHeaderLength)); + } + catch (OverflowException ex) + { + Throw.TooLargeByteLength(ex, encoding.EncodingName); + return 0; + } + + var realHeaderLength = this.EncodeStringHeader(unchecked((uint)actualLength), sinkBuffer); + + if (estimatedHeaderLength != realHeaderLength) + { + fixed (byte* pSinkBuffer = &head) + { + Buffer.MemoryCopy((void*)(pSinkBuffer + estimatedHeaderLength), (void*)(pSinkBuffer + realHeaderLength), actualLength, actualLength); + } + } + + return actualLength + realHeaderLength; + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + buffer = EnsureNotNull(buffer); + encoding = encoding ?? Utf8EncodingNonBom.Instance; + if (value.Length > UInt32.MaxValue) + { + Throw.TooLargeCharLength(value.Length); + return; + } + + if (value.Length == 0) + { + buffer.GetSpan(1)[0] = MessagePackCode.MinimumFixedRaw; + buffer.Advance(1); + return; + } + + if (value.Length < 256) + { + var valueLength = unchecked((int)value.Length); + var maxByteLength = encoding.GetMaxByteCount(valueLength); + + if (maxByteLength < 32) + { + // stack alloc is fastest + Span encodingBuffer = stackalloc byte[maxByteLength]; + var actualLength = encoding.GetBytes(value, encodingBuffer); + var sink = buffer.GetSpan(actualLength + 1); + sink[0] = unchecked((byte)(MessagePackCode.MinimumFixedRaw | actualLength)); + encodingBuffer.Slice(actualLength).CopyTo(sink.Slice(1)); + buffer.Advance(sink.Length); + return; + } + else if (maxByteLength < 256) + { + // stack alloc is fastest + Span encodingBuffer = stackalloc byte[maxByteLength + 3]; + var actualLength = encoding.GetBytes(value, encodingBuffer); + var sink = buffer.GetSpan(3); + var headerLength = this.EncodeStringHeader(unchecked((uint)actualLength), sink); + buffer.Advance(headerLength); + encodingBuffer.Slice(0, actualLength).CopyTo(buffer.GetSpan(actualLength)); + buffer.Advance(actualLength); + return; + } + } + + this.EncodeStringSlow(value, buffer, encoding); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void EncodeStringSlow(in ReadOnlySequence value, IBufferWriter buffer, Encoding encoding) + { + // Use 1-path logic borrowed from MessagePack C# + + // Get max char bytes. Use 2 for 0 (maybe bug) or 1 (ASCII) to simplify following logic. + var maxBytesPerChar = Math.Max(encoding.GetMaxByteCount(1), 2); + + // In most case, msgpack uses UTF-8 and clob contains 1 to 3 byte chars, + // and may charactors are ASCII. + // So, estimate with 1char is 2bytes is good enough here. + var estimatedLength = unchecked((uint)value.Length * 2); + var bufferLength = encoding.GetMaxByteCount(unchecked((int)value.Length)) + 5; + + if (bufferLength <= 0x20_0000) // 2MB + { + Memory sinkMemory = buffer.GetMemory(bufferLength); + + if (MemoryMarshal.TryGetArray(sinkMemory, out var sinkBufferSegment)) + { + // We can get single array + buffer.Advance(this.EncodeLargeString1Path(value, buffer, encoding, estimatedLength, sinkBufferSegment)); + } + else + { + // Memory is not array backend, so use local buffer. + var encodingBuffer = base.Options.ByteBufferPool.Rent(bufferLength); + try + { + var totalLength = this.EncodeLargeString1Path(value, buffer, encoding, estimatedLength, encodingBuffer); + buffer.Write(encodingBuffer.AsMemory(0, totalLength).Span); + } + finally + { + base.Options.ByteBufferPool.Return(encodingBuffer, base.Options.ClearsBuffer); + } + } + } + + this.EncodeHugeString2Path(value, buffer, encoding); + } + + + private unsafe int EncodeLargeString1Path(in ReadOnlySequence value, IBufferWriter buffer, Encoding encoding, uint estimatedLength, ArraySegment sinkBufferSegment) + { + ref var head = ref sinkBufferSegment.Array![sinkBufferSegment.Offset]; + + Span sinkBuffer = sinkBufferSegment; + var estimatedHeaderLength = this.EncodeStringHeader(estimatedLength, sinkBuffer); + + int actualLength; + + try + { + actualLength = encoding.GetBytes(value, sinkBuffer.Slice(estimatedHeaderLength)); + } + catch (OverflowException ex) + { + Throw.TooLargeByteLength(ex, encoding.EncodingName); + return 0; + } + + var realHeaderLength = this.EncodeStringHeader(unchecked((uint)actualLength), sinkBuffer); + + if (estimatedHeaderLength != realHeaderLength) + { + fixed (byte* pSinkBuffer = &head) + { + Buffer.MemoryCopy((void*)(pSinkBuffer + estimatedHeaderLength), (void*)(pSinkBuffer + realHeaderLength), actualLength, actualLength); + } + } + + return actualLength + realHeaderLength; + } + + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.tt b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.tt new file mode 100644 index 000000000..7f8eb2206 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.tt @@ -0,0 +1,173 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +namespace MsgPack.Internal +{ + partial class MessagePackEncoder + { +<# +foreach (var inputType in + new [] + { + new { Signature = "ReadOnlySpan", MayBe64BitLength = false }, + new { Signature = "in ReadOnlySequence" , MayBe64BitLength = true }, + } +) +{ +#> + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeString(<#= inputType.Signature #> value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + buffer = EnsureNotNull(buffer); + encoding = encoding ?? Utf8EncodingNonBom.Instance; +<# + if (inputType.MayBe64BitLength) + { +#> + if (value.Length > UInt32.MaxValue) + { + Throw.TooLargeCharLength(value.Length); + return; + } + +<# + } +#> + if (value.Length == 0) + { + buffer.GetSpan(1)[0] = MessagePackCode.MinimumFixedRaw; + buffer.Advance(1); + return; + } + + if (value.Length < 256) + { + var valueLength = unchecked((int)value.Length); + var maxByteLength = encoding.GetMaxByteCount(valueLength); + + if (maxByteLength < 32) + { + // stack alloc is fastest + Span encodingBuffer = stackalloc byte[maxByteLength]; + var actualLength = encoding.GetBytes(value, encodingBuffer); + var sink = buffer.GetSpan(actualLength + 1); + sink[0] = unchecked((byte)(MessagePackCode.MinimumFixedRaw | actualLength)); + encodingBuffer.Slice(actualLength).CopyTo(sink.Slice(1)); + buffer.Advance(sink.Length); + return; + } + else if (maxByteLength < 256) + { + // stack alloc is fastest + Span encodingBuffer = stackalloc byte[maxByteLength + 3]; + var actualLength = encoding.GetBytes(value, encodingBuffer); + var sink = buffer.GetSpan(3); + var headerLength = this.EncodeStringHeader(unchecked((uint)actualLength), sink); + buffer.Advance(headerLength); + encodingBuffer.Slice(0, actualLength).CopyTo(buffer.GetSpan(actualLength)); + buffer.Advance(actualLength); + return; + } + } + + this.EncodeStringSlow(value, buffer, encoding); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void EncodeStringSlow(<#= inputType.Signature #> value, IBufferWriter buffer, Encoding encoding) + { + // Use 1-path logic borrowed from MessagePack C# + + // Get max char bytes. Use 2 for 0 (maybe bug) or 1 (ASCII) to simplify following logic. + var maxBytesPerChar = Math.Max(encoding.GetMaxByteCount(1), 2); + + // In most case, msgpack uses UTF-8 and clob contains 1 to 3 byte chars, + // and may charactors are ASCII. + // So, estimate with 1char is 2bytes is good enough here. + var estimatedLength = unchecked((uint)value.Length * 2); + var bufferLength = encoding.GetMaxByteCount(unchecked((int)value.Length)) + 5; + + if (bufferLength <= 0x20_0000) // 2MB + { + Memory sinkMemory = buffer.GetMemory(bufferLength); + + if (MemoryMarshal.TryGetArray(sinkMemory, out var sinkBufferSegment)) + { + // We can get single array + buffer.Advance(this.EncodeLargeString1Path(value, buffer, encoding, estimatedLength, sinkBufferSegment)); + } + else + { + // Memory is not array backend, so use local buffer. + var encodingBuffer = base.Options.ByteBufferPool.Rent(bufferLength); + try + { + var totalLength = this.EncodeLargeString1Path(value, buffer, encoding, estimatedLength, encodingBuffer); + buffer.Write(encodingBuffer.AsMemory(0, totalLength).Span); + } + finally + { + base.Options.ByteBufferPool.Return(encodingBuffer, base.Options.ClearsBuffer); + } + } + } + + this.EncodeHugeString2Path(value, buffer, encoding); + } + + + private unsafe int EncodeLargeString1Path(<#= inputType.Signature #> value, IBufferWriter buffer, Encoding encoding, uint estimatedLength, ArraySegment sinkBufferSegment) + { + ref var head = ref sinkBufferSegment.Array![sinkBufferSegment.Offset]; + + Span sinkBuffer = sinkBufferSegment; + var estimatedHeaderLength = this.EncodeStringHeader(estimatedLength, sinkBuffer); + + int actualLength; + + try + { + actualLength = encoding.GetBytes(value, sinkBuffer.Slice(estimatedHeaderLength)); + } + catch (OverflowException ex) + { + Throw.TooLargeByteLength(ex, encoding.EncodingName); + return 0; + } + + var realHeaderLength = this.EncodeStringHeader(unchecked((uint)actualLength), sinkBuffer); + + if (estimatedHeaderLength != realHeaderLength) + { + fixed (byte* pSinkBuffer = &head) + { + Buffer.MemoryCopy((void*)(pSinkBuffer + estimatedHeaderLength), (void*)(pSinkBuffer + realHeaderLength), actualLength, actualLength); + } + } + + return actualLength + realHeaderLength; + } + +<# +} +#> + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.cs new file mode 100644 index 000000000..9e9023ac9 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.cs @@ -0,0 +1,249 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; + +namespace MsgPack.Internal +{ + /// + /// Common encoder implementation between legacy and current MessagePack format enconders. + /// + public abstract partial class MessagePackEncoder : Encoder + { +#warning TODO: Can devirt? + public static MessagePackEncoder CreateLegacy(MessagePackEncoderOptions options) => new LegacyMessagePackEncoder(options); + public static MessagePackEncoder CreateCurrent(MessagePackEncoderOptions options) => new CurrentMessagePackEncoder(options); + + public new MessagePackEncoderOptions Options => (base.Options as MessagePackEncoderOptions)!; + + protected MessagePackEncoder(MessagePackEncoderOptions options) + : base(options) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeSingle(float value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var span = buffer.GetSpan(sizeof(float) + 1); + + span[0] = MessagePackCode.Real32; + span = span.Slice(1); + BinaryPrimitives.WriteInt32BigEndian(span, BitConverter.SingleToInt32Bits(value)); + buffer.Advance(sizeof(float) + 1); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeDouble(double value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var span = buffer.GetSpan(sizeof(double) + 1); + + span[0] = MessagePackCode.Real64; + span = span.Slice(1); + BinaryPrimitives.WriteInt64BigEndian(span, BitConverter.DoubleToInt64Bits(value)); + buffer.Advance(sizeof(double) + 1); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeBoolean(bool value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)(value ? MessagePackCode.TrueValue : MessagePackCode.FalseValue)); + buffer.Advance(1); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeArrayStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) + { + buffer = EnsureNotNull(buffer); + collectionContext.IncrementDepth(); + + if (length < 16) + { + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)(MessagePackCode.MinimumFixedArray | length)); + buffer.Advance(1); + } + else if (length < 16) + { + var span = buffer.GetSpan(sizeof(ushort) + 1); + span[0] = MessagePackCode.Array16; + span = span.Slice(1); + BinaryPrimitives.WriteUInt16BigEndian(span, unchecked((ushort)length)); + buffer.Advance(sizeof(ushort) + 1); + } + else + { + var span = buffer.GetSpan(sizeof(uint) + 1); + span[0] = MessagePackCode.Array32; + span = span.Slice(1); + BinaryPrimitives.WriteInt32BigEndian(span, length); + buffer.Advance(sizeof(uint) + 1); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeArrayEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeArrayItemStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeArrayItemEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeMapStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) + { + buffer = EnsureNotNull(buffer); + + if (length < 16) + { + var span = buffer.GetSpan(1); + span[0] = unchecked((byte)(MessagePackCode.MinimumFixedMap | length)); + buffer.Advance(1); + } + else if (length < 16) + { + var span = buffer.GetSpan(sizeof(ushort) + 1); + span[0] = MessagePackCode.Map16; + span = span.Slice(1); + BinaryPrimitives.WriteUInt16BigEndian(span, unchecked((ushort)length)); + buffer.Advance(sizeof(ushort) + 1); + } + else + { + var span = buffer.GetSpan(sizeof(uint) + 1); + span[0] = MessagePackCode.Map32; + span = span.Slice(1); + BinaryPrimitives.WriteInt32BigEndian(span, length); + buffer.Advance(sizeof(uint) + 1); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeMapEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeMapKeyStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeMapKeyEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeMapValueStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeMapValueEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeNull(IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var span = buffer.GetSpan(1); + span[0] = MessagePackCode.NilValue; + buffer.Advance(1); + } + + private void EncodeHugeString2Path(ReadOnlySpan value, IBufferWriter buffer, Encoding encoding) + { + // Huge path, we cannot use rollback because it requires multiple span from buffer. + int actualLength; + try + { + actualLength = encoding.GetByteCount(value); + } + catch(OverflowException ex) + { + Throw.TooLargeByteLength(ex, encoding.EncodingName); + return; + } + + var span = buffer.GetSpan(5); + var headerLength = this.EncodeStringHeader(unchecked((uint)actualLength), span); + buffer.Advance(headerLength); + encoding.GetBytes(value, buffer); + } + + private void EncodeHugeString2Path(in ReadOnlySequence value, IBufferWriter buffer, Encoding encoding) + { + // Huge path, we cannot use rollback because it requires multiple span from buffer. + var actualLength = 0L; + var reader = new SequenceReader(value); + while (!reader.End) + { + actualLength += encoding.GetByteCount(reader.UnreadSpan); + reader.Advance(reader.UnreadSpan.Length); + } + + if (actualLength > UInt32.MaxValue) + { + Throw.TooLargeByteLength(actualLength, encoding.EncodingName); + } + + var span = buffer.GetSpan(5); + var headerLength = this.EncodeStringHeader(unchecked((uint)actualLength), span); + buffer.Advance(headerLength); + encoding.GetBytes(value, buffer); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeString(ReadOnlySpan encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) + { + buffer = EnsureNotNull(buffer); + + var span = buffer.GetSpan(5); + var used = this.EncodeStringHeader(unchecked((uint)encodedValue.Length), span); + buffer.Advance(used); + this.WriteRaw(encodedValue, buffer, cancellationToken); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private void EncodeStringHeader(uint length, Memory memory) + => this.EncodeStringHeader(length, memory.Span); + + protected abstract int EncodeStringHeader(uint length, Span buffer); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeString(in ReadOnlySequence encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) + { + var span = buffer.GetSpan(5); + var used = this.EncodeStringHeader(unchecked((uint)encodedValue.Length), span); + buffer.Advance(used); + this.WriteRaw(encodedValue, buffer, cancellationToken); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeBinary(ReadOnlySpan value, IBufferWriter buffer, CancellationToken cancellationToken = default) + { + var span = buffer.GetSpan(5); + var used = this.EncodeBinaryHeader(unchecked((uint)value.Length), span); + buffer.Advance(used); + this.WriteRaw(value, buffer, cancellationToken); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void EncodeBinary(in ReadOnlySequence value, IBufferWriter buffer, CancellationToken cancellationToken) + { + var span = buffer.GetSpan(5); + var used = this.EncodeBinaryHeader(unchecked((uint)value.Length), span); + buffer.Advance(used); + this.WriteRaw(value, buffer, cancellationToken); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private int EncodeBinaryHeader(uint length, Memory memory) + => this.EncodeBinaryHeader(length, memory.Span); + + protected abstract int EncodeBinaryHeader(uint length, Span buffer); + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs b/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs new file mode 100644 index 000000000..ad3efa2cf --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs @@ -0,0 +1,11 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Internal +{ + public sealed class MessagePackEncoderOptions : EncoderOptions + { + public MessagePackEncoderOptions(MessagePackEncoderOptionsBuilder builder) : base(builder) { } + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackEncoderOptionsBuilder.cs b/src/MsgPack.Core/Internal/MessagePackEncoderOptionsBuilder.cs new file mode 100644 index 000000000..0f343c3c2 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackEncoderOptionsBuilder.cs @@ -0,0 +1,13 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Internal +{ + public sealed class MessagePackEncoderOptionsBuilder : EncoderOptionsBuilder + { + public MessagePackEncoderOptionsBuilder() { } + + public MessagePackEncoderOptions Build() => new MessagePackEncoderOptions(this); + } +} diff --git a/src/MsgPack.Core/Internal/MessagePackThrow.cs b/src/MsgPack.Core/Internal/MessagePackThrow.cs new file mode 100644 index 000000000..c9c18f9b4 --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackThrow.cs @@ -0,0 +1,45 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache 2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + internal static class MessagePackThrow + { + public static void IsNotType(byte header, long position, Type requestType) + => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) but {requestType} is required at {position:#,0}."); + + public static void RealCannotBeInteger(byte header, long position, Type requestType) + => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) is not compatible for {requestType} in current configuration at {position:#,0}."); + + public static void TypeIsNotArray(byte header, long position) + => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) but array is required at {position:#,0}."); + + public static void TypeIsNotMap(byte header, long position) + => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) but map is required at {position:#,0}."); + + public static void TypeIsNotArrayNorMap(byte header, long position) + => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) but array or map is required at {position:#,0}."); + + public static void IsNotNumber(byte header, long position, Type requestType) + => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) but it is not compatible for {requestType} at {position:#,0}."); + + public static void TooLargeByteLength(byte header, long position, long byteLength) + => throw new MessageTypeException($"The length of string {MessagePackCode.ToString(header)}(0x{header:X2}) {byteLength:#,0}(0x{byteLength:X8}) exceeds Int32.MaxValue (0x7FFFFFFF) at {position:#,0}."); + + public static void TooLargeArrayOrMapLength(byte header, long position, long byteLength) + => throw new MessageTypeException($"The length of array or map {MessagePackCode.ToString(header)}(0x{header:X2}) {byteLength:#,0}(0x{byteLength:X8}) exceeds Int32.MaxValue (0x7FFFFFFF) at {position:#,0}."); + + public static void OutOfRangeExtensionTypeCode(int typeCode, [CallerArgumentExpression("typeCode")] string? paramName = default) + => throw new ArgumentOutOfRangeException(paramName, $"A type code of MessagePack must be non negative 1byte integer (between 0 to 127 inclusive). '{typeCode}' is too large."); + + public static void IsNotUtf8String(byte header, long position) + => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) but it is not compatible for UTF8String at {position:#,0}."); + + public static void IsNotExtension(byte header, long position) + => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) but it is not extension at {position:#,0}."); + } +} diff --git a/src/MsgPack.Core/Internal/MsgPackStringTrie`1.cs b/src/MsgPack.Core/Internal/MsgPackStringTrie`1.cs new file mode 100644 index 000000000..93ef16a78 --- /dev/null +++ b/src/MsgPack.Core/Internal/MsgPackStringTrie`1.cs @@ -0,0 +1,299 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers.Binary; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace MsgPack.Internal +{ + // Basic idea is borrwed from AutomataDictionary of Message Pack C# + // https://github.com/neuecc/MessagePack-CSharp/blob/51649e0d7b8641ad5d3cdd6dfdc130c7671066fc/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataDictionary.cs#L1 + + public sealed class MsgPackStringTrie + { + private readonly T _default; + private readonly Node _root; + + public MsgPackStringTrie(T defaultValue) + { + this._default = defaultValue; + this._root = new Node(defaultValue, Array.Empty(), Array.Empty()); + } + + public bool TryAdd(ReadOnlySpan utf8Key, T value) + => this.TryAdd(this._root, GenerateKeyHead(ref utf8Key), utf8Key, value); + + private static int BinarySearch(ulong[] nodes, ulong key) + { + // Span.BinarySearch is slower maybe because of ComapreTo method overhead, so we implement binary search manually. + var low = 0; + var high = nodes.Length - 1; + while (low <= high) + { + // Peformance trick borrowed from https://github.com/dotnet/runtime/blob/f2786223508c0c70040fbf48ec3a39a607dd7f75/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.BinarySearch.cs#L42 + var index = unchecked((int)(((uint)high + (uint)low) >> 1)); + var found = nodes[index]; + if (found == key) + { + return index; + } + else if (found < key) + { + low = index + 1; + } + else + { + high = index - 1; + } + } + + return ~low; + } + + public T GetOrDefault(ReadOnlySpan msgPackStringKey) + { + var result = this.Find(this._root, GenerateKeyRest(ref msgPackStringKey), msgPackStringKey); + return result != null ? result.Value : this._default; + } + + private bool TryAdd(Node parent, ulong key, ReadOnlySpan trailingKey, T value) + { + var nodes = parent.ChildNodes; + var keys = parent.ChildKeys; + + while (true) + { + var index = BinarySearch(keys, key); + + if (index < 0) + { + // No matching leaf. + this.AddNode(parent, key, trailingKey, ~index, value, out _); + return true; + } + + var found = nodes[index]; + if (trailingKey.IsEmpty) + { + // The leaf matches. + return false; + } + + if (found.ChildNodes.Length == 0) + { + // Search key is longer than trie path. + this.AddNode(found, key, trailingKey, 0, value, out _); + return true; + } + + nodes = found.ChildNodes; + keys = found.ChildKeys; + + key = GenerateKeyRest(ref trailingKey); + } + } + + private Node? Find(Node parent, ulong key, ReadOnlySpan trailingKey) + { + var nodes = parent.ChildNodes; + var keys = parent.ChildKeys; + + while (true) + { + var index = BinarySearch(keys, key); + + if (index < 0) + { + // No matching leaf. + return null; + } + + var found = nodes[index]; + if (trailingKey.IsEmpty) + { + // The leaf matches. + return found; + } + + if (found.ChildNodes.Length == 0) + { + // Search key is longer than trie path. + return null; + } + + nodes = found.ChildNodes; + keys = found.ChildKeys; + key = GenerateKeyRest(ref trailingKey); + } + } + + private void AddNode(Node node, ulong key, ReadOnlySpan trailingKey, int targetIndex, T value, out Node result) + { + Array.Resize(ref node.ChildKeys, node.ChildKeys.Length + 1); + Array.Resize(ref node.ChildNodes, node.ChildNodes.Length + 1); + var nodes = node.ChildNodes; + var keys = node.ChildKeys; + + while (true) + { + var child = + trailingKey.IsEmpty ? + new Node(value, Array.Empty(), Array.Empty()) : + new Node(this._default, new Node[1], new ulong[1]); + + if (nodes.Length > 1 && targetIndex < nodes.Length - 1) + { + // Shift existing. + Array.Copy(nodes, targetIndex, nodes, targetIndex + 1, nodes.Length - targetIndex - 1); + Array.Copy(keys, targetIndex, keys, targetIndex + 1, keys.Length - targetIndex - 1); + } + + nodes[targetIndex] = child; + keys[targetIndex] = key; + + if (trailingKey.IsEmpty) + { + // This is leaf. + result = child; + return; + } + + nodes = child.ChildNodes; + keys = child.ChildKeys; + + Debug.Assert(nodes.Length == 1); + Debug.Assert(keys.Length == 1); + targetIndex = 0; + + key = GenerateKeyRest(ref trailingKey); + } + } + + private static ulong GenerateKeyHead(ref ReadOnlySpan source) + { + Span bytes = stackalloc byte[sizeof(ulong)]; + var buffer = bytes; + int consumes; + if (source.Length < 16) + { + buffer[0] = (byte)(0xA0 | source.Length); + buffer = buffer.Slice(1); + consumes = Math.Min(source.Length, sizeof(long) - 1); + } + else if (source.Length <= Byte.MaxValue) + { + buffer[0] = 0xD9; + buffer[1] = (byte)source.Length; + buffer = buffer.Slice(2); + consumes = Math.Min(source.Length, sizeof(long) - 1 - 1); + } + else if (source.Length <= UInt16.MaxValue) + { + buffer[0] = 0xDA; + buffer = buffer.Slice(1); + BinaryPrimitives.WriteUInt16BigEndian(buffer, (ushort)source.Length); + buffer = buffer.Slice(sizeof(ushort)); + consumes = Math.Min(source.Length, sizeof(long) - 1 - sizeof(ushort)); + } + else + { + buffer[0] = 0xDB; + buffer = buffer.Slice(1); + BinaryPrimitives.WriteInt32BigEndian(buffer, source.Length); + buffer = buffer.Slice(sizeof(int)); + consumes = Math.Min(source.Length, sizeof(long) - 1 - sizeof(int)); + } + + source.Slice(0, consumes).CopyTo(buffer); + source = source.Slice(consumes); + return MemoryMarshal.Cast(bytes)[0]; + } + + private static ulong GenerateKeyRest(ref ReadOnlySpan source) + { + Debug.Assert(!source.IsEmpty); + if (source.Length >= sizeof(ulong)) + { + var result = MemoryMarshal.Cast(source)[0]; + source = source.Slice(sizeof(ulong)); + return result; + } + else + { + ulong result; + unchecked + { + switch (source.Length) + { + case 1: + { + result = source[0]; + break; + } + case 2: + { + result = BinaryPrimitives.ReadUInt16LittleEndian(source); + break; + } + case 3: + { + result = BinaryPrimitives.ReadUInt16LittleEndian(source); + source = source.Slice(2); + result |= (uint)(source[0] << 16); + break; + } + case 4: + { + result = BinaryPrimitives.ReadUInt32LittleEndian(source); + break; + } + case 5: + { + result = BinaryPrimitives.ReadUInt32LittleEndian(source); + source = source.Slice(4); + result |= ((ulong)source[0] << 32); + break; + } + case 6: + { + result = BinaryPrimitives.ReadUInt32LittleEndian(source); + source = source.Slice(4); + result |= ((ulong)BinaryPrimitives.ReadUInt16LittleEndian(source) << 32); + break; + } + default: // 7 + { + result = BinaryPrimitives.ReadUInt32LittleEndian(source); + source = source.Slice(4); + result |= ((ulong)BinaryPrimitives.ReadUInt16LittleEndian(source) << 32); + source = source.Slice(2); + result |= ((ulong)source[0] << 48); + break; + } + } + } + + source = ReadOnlySpan.Empty; + return result; + } + } + + private sealed class Node + { + // There 2 separate array to improve search performance due to CPU cache line and prediction. + public ulong[] ChildKeys; + public Node[] ChildNodes; + public readonly T Value; + + public Node(T value, Node[] childNodes, ulong[] childKeys) + { + this.Value = value; + this.ChildNodes = childNodes; + this.ChildKeys = childKeys; + } + } + } +} diff --git a/src/MsgPack.Core/MsgPack.Core.csproj b/src/MsgPack.Core/MsgPack.Core.csproj new file mode 100644 index 000000000..e04106cd6 --- /dev/null +++ b/src/MsgPack.Core/MsgPack.Core.csproj @@ -0,0 +1,71 @@ + + + + netcoreapp3.1 + true + + + + + + + + + + + + + True + True + MessagePackDecoder.Integers.tt + + + True + True + MessagePackDecoder.Nullables.tt + + + True + True + MessagePackDecoder.Reals.tt + + + True + True + MessagePackEncoder.Integers.tt + + + True + True + MessagePackEncoder.Strings.tt + + + + + + TextTemplatingFileGenerator + MessagePackDecoder.Nullables.cs + + + TextTemplatingFileGenerator + MessagePackDecoder.Reals.cs + + + MessagePackEncoder.Integers.cs + TextTemplatingFileGenerator + + + TextTemplatingFileGenerator + MessagePackDecoder.Integers.cs + + + MessagePackEncoder.Strings.cs + TextTemplatingFileGenerator + + + + + + + + diff --git a/src/MsgPack.Core/System.Text/EncodingExtensions.cs b/src/MsgPack.Core/System.Text/EncodingExtensions.cs index 9cc34cbd3..09f42c63d 100644 --- a/src/MsgPack.Core/System.Text/EncodingExtensions.cs +++ b/src/MsgPack.Core/System.Text/EncodingExtensions.cs @@ -13,6 +13,7 @@ using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using MsgPack.Internal; namespace System.Text @@ -141,7 +142,7 @@ public static int GetBytes(this Encoding encoding, in ReadOnlySequence cha ReadOnlySequence remainingChars = chars; int originalBytesLength = bytes.Length; - Encoder encoder = encoding.GetEncoder(); + var encoder = encoding.GetEncoder(); bool isFinalSegment; do @@ -188,7 +189,7 @@ public static byte[] GetBytes(this Encoding encoding, in ReadOnlySequence // If the incoming sequence is multi-segment, create a stateful Encoder // and use it as the workhorse. On the final iteration we'll pass flush=true. - Encoder encoder = encoding.GetEncoder(); + var encoder = encoding.GetEncoder(); // Maintain a list of all the segments we'll need to concat together. // These will be released back to the pool at the end of the method. @@ -352,7 +353,7 @@ public static int GetChars(this Encoding encoding, in ReadOnlySequence byt ReadOnlySequence remainingBytes = bytes; int originalCharsLength = chars.Length; - Decoder decoder = encoding.GetDecoder(); + var decoder = encoding.GetDecoder(); bool isFinalSegment; do @@ -392,59 +393,68 @@ public static string GetString(this Encoding encoding, in ReadOnlySequence } else { - // If the incoming sequence is multi-segment, create a stateful Decoder - // and use it as the workhorse. On the final iteration we'll pass flush=true. + return encoding.GetStringMultiSegment(bytes, ArrayPool.Shared); + } + } - Decoder decoder = encoding.GetDecoder(); + internal static string GetStringMultiSegment(this Encoding encoding, in ReadOnlySequence bytes, ArrayPool arrayPool, CancellationToken cancellationToken = default) + { + // If the incoming sequence is multi-segment, create a stateful Decoder + // and use it as the workhorse. On the final iteration we'll pass flush=true. - // Maintain a list of all the segments we'll need to concat together. - // These will be released back to the pool at the end of the method. + var decoder = encoding.GetDecoder(); - List<(char[], int)> listOfSegments = new List<(char[], int)>(); - int totalCharCount = 0; + // Maintain a list of all the segments we'll need to concat together. + // These will be released back to the pool at the end of the method. - ReadOnlySequence remainingBytes = bytes; - bool isFinalSegment; + List<(char[], int)> listOfSegments = new List<(char[], int)>(); + int totalCharCount = 0; - do - { - GetFirstSpan(remainingBytes, out ReadOnlySpan firstSpan, out SequencePosition next); - isFinalSegment = remainingBytes.IsSingleSegment; + ReadOnlySequence remainingBytes = bytes; + bool isFinalSegment; + + do + { + GetFirstSpan(remainingBytes, out ReadOnlySpan firstSpan, out SequencePosition next); + isFinalSegment = remainingBytes.IsSingleSegment; - int charCountThisIteration = decoder.GetCharCount(firstSpan, flush: isFinalSegment); // could throw ArgumentException if overflow would occur - char[] rentedArray = ArrayPool.Shared.Rent(charCountThisIteration); - int actualCharsWrittenThisIteration = decoder.GetChars(firstSpan, rentedArray, flush: isFinalSegment); - listOfSegments.Add((rentedArray, actualCharsWrittenThisIteration)); + int charCountThisIteration = decoder.GetCharCount(firstSpan, flush: isFinalSegment); // could throw ArgumentException if overflow would occur + char[] rentedArray = arrayPool.Rent(charCountThisIteration); + int actualCharsWrittenThisIteration = decoder.GetChars(firstSpan, rentedArray, flush: isFinalSegment); + listOfSegments.Add((rentedArray, actualCharsWrittenThisIteration)); - totalCharCount += actualCharsWrittenThisIteration; - if (totalCharCount < 0) + totalCharCount += actualCharsWrittenThisIteration; + if (totalCharCount < 0) + { + foreach(var segment in listOfSegments) { - // If we overflowed, call string.Create, passing int.MaxValue. - // This will end up throwing the expected OutOfMemoryException - // since strings are limited to under int.MaxValue elements in length. - - totalCharCount = Int32.MaxValue; - break; + arrayPool.Return(segment.Item1); } - remainingBytes = remainingBytes.Slice(next); - } while (!isFinalSegment); + // overflow + Throw.TooLargeByteLengthForString(encoding.EncodingName); + // never reaches. + return null!; + } - // Now build up the string to return, then release all of our scratch buffers - // back to the shared pool. + remainingBytes = remainingBytes.Slice(next); + cancellationToken.ThrowIfCancellationRequested(); + } while (!isFinalSegment); - return String.Create(totalCharCount, listOfSegments, (span, listOfSegments) => + // Now build up the string to return, then release all of our scratch buffers + // back to the shared pool. + + return String.Create(totalCharCount, listOfSegments, (span, listOfSegments) => + { + foreach ((char[] array, int length) in listOfSegments) { - foreach ((char[] array, int length) in listOfSegments) - { - array.AsSpan(0, length).CopyTo(span); - ArrayPool.Shared.Return(array); - span = span.Slice(length); - } + array.AsSpan(0, length).CopyTo(span); + arrayPool.Return(array); + span = span.Slice(length); + } - Debug.Assert(span.IsEmpty, "Over-allocated the string instance?"); - }); - } + Debug.Assert(span.IsEmpty, "Over-allocated the string instance?"); + }); } /// diff --git a/src/MsgPack.Core/_SampleObject.cs b/src/MsgPack.Core/_SampleObject.cs new file mode 100644 index 000000000..9c6e63c35 --- /dev/null +++ b/src/MsgPack.Core/_SampleObject.cs @@ -0,0 +1,1085 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading.Tasks; +using MsgPack.Serialization.Internal; + +namespace MsgPack.Internal +{ + internal sealed class SampleObject + { + public int Age { get; set; } + public string Name { get; set; } + public bool IsActive { get; set; } + public IDictionary Attributes { get; } = new Dictionary(); + public IList Roles { get; } = new List(); + } + + // MP.Core.Abstraction + // MP.Core / MP.Json.Core / MP.Yaml.Core / MP.Coff.Core + // MP.Serialization.Core -> Interfaces, .... + // MP.Serialization -> Basic serializers, context, utilities, Stream adapter, byte[] adapter, etc. + // MP.Serialization.Serializers-> Extended serializers implementation. + // MP.Serialization.Reflection -> Reflection + // MP.Serialization.ILGeneration -> Reflection + // MP.Serialization.SourceGeneration -> Reflection + // MP.Extensions / MP.Json.Extensions / MP.Yaml.Extensions / MP.Coff.Extensions -> MPO, DOM, etc + // MP.Compatibility1 -> v1 compatibility shims + // MP.Cli -> v1 compatibility shims + + // Packer/Unpacker compatibility note: + // Packer -> Packer.Create(new MemoryStream()) + // Unpacker -> Unpacker.Create(new MemoryStream())??? + + // For PoC of MVP and reference for emit. + /// + /// Sample hand made serializer. + /// + internal sealed class SampleSerializer : IObjectSerializer + { + private bool UseArray { get; set; } + + public void Serialize(in SerializationOperationContext context, SampleObject obj, IBufferWriter writer) + { + var encoder = context.Encoder; + if (obj == null) + { + encoder.EncodeNull(writer); + return; + } + + if (this.UseArray) + { + encoder.EncodeArrayStart(5, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeString(obj.Name, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + encoder.EncodeInt32(obj.Age, writer); + encoder.EncodeBoolean(obj.IsActive, writer); + if (obj.Attributes == null) + { + encoder.EncodeNull(writer); + } + else + { + encoder.EncodeMapStart(obj.Attributes.Count, writer, context.CollectionContext); // OMITTABLE + var i = 0; // OMITTABLE + foreach (var entry in obj.Attributes) + { + encoder.EncodeMapKeyStart(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeString(entry.Key, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + encoder.EncodeMapKeyEnd(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeString(entry.Value, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + encoder.EncodeMapValueEnd(i, writer, context.CollectionContext); // OMITTABLE + } + encoder.EncodeMapEnd(obj.Attributes.Count, writer, context.CollectionContext); + } + if (obj.Roles == null) + { + encoder.EncodeNull(writer); + } + else + { + encoder.EncodeArrayStart(obj.Roles.Count, writer, context.CollectionContext); // OMITTABLE + var i = 0; // OMITTABLE + foreach (var item in obj.Roles) + { + encoder.EncodeArrayItemStart(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeString(item, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + encoder.EncodeArrayItemEnd(i, writer, context.CollectionContext); // OMITTABLE + } + encoder.EncodeArrayEnd(obj.Roles.Count, writer, context.CollectionContext); + } + encoder.EncodeArrayEnd(5, writer, context.CollectionContext); // OMITTABLE + } + else + { + encoder.EncodeMapStart(5, writer, context.CollectionContext); // OMITTABLE + ReadOnlySpan key0 = new[] { (byte)'N', (byte)'a', (byte)'m', (byte)'e' }; // static readonly + encoder.EncodeString(key0, 4, writer, context.CancellationToken); + encoder.EncodeString(obj.Name, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + ReadOnlySpan key1 = new[] { (byte)'A', (byte)'g', (byte)'e' }; // static readonly + encoder.EncodeString(key1, 3, writer, context.CancellationToken); + encoder.EncodeInt32(obj.Age, writer); + ReadOnlySpan key2 = new[] { (byte)'I', (byte)'s', (byte)'a', (byte)'c', (byte)'t', (byte)'i', (byte)'v', (byte)'e' }; // static readonly + encoder.EncodeString(key2, 8, writer, context.CancellationToken); + encoder.EncodeBoolean(obj.IsActive, writer); + ReadOnlySpan key3 = new[] { (byte)'A', (byte)'t', (byte)'t', (byte)'r', (byte)'i', (byte)'b', (byte)'u', (byte)'t', (byte)'e', (byte)'s' }; // static readonly + encoder.EncodeString(key3, 10, writer, context.CancellationToken); + if (obj.Attributes == null) + { + encoder.EncodeNull(writer); + } + else + { + encoder.EncodeMapStart(obj.Attributes.Count, writer, context.CollectionContext); // OMITTABLE + var i = 0; // OMITTABLE + foreach (var entry in obj.Attributes) + { + encoder.EncodeMapKeyStart(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeString(entry.Key, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + encoder.EncodeMapKeyEnd(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeString(entry.Value, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + encoder.EncodeMapValueEnd(i, writer, context.CollectionContext); // OMITTABLE + } + encoder.EncodeMapEnd(obj.Attributes.Count, writer, context.CollectionContext); + } + ReadOnlySpan key4 = new[] { (byte)'R', (byte)'o', (byte)'l', (byte)'e' }; // static readonly + encoder.EncodeString(key4, 4, writer, context.CancellationToken); + if (obj.Roles == null) + { + encoder.EncodeNull(writer); + } + else + { + encoder.EncodeArrayStart(obj.Roles.Count, writer, context.CollectionContext); // OMITTABLE + var i = 0; // OMITTABLE + foreach (var item in obj.Roles) + { + encoder.EncodeArrayItemStart(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeString(item, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + encoder.EncodeArrayItemEnd(i, writer, context.CollectionContext); // OMITTABLE + } + encoder.EncodeArrayEnd(obj.Roles.Count, writer, context.CollectionContext); + } + encoder.EncodeMapEnd(5, writer, context.CollectionContext); // OMITTABLE + } + } + + public async ValueTask SerializeAsync(SerializationOperationContext context, SampleObject obj, Stream streamSink) + { + await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) + { + this.Serialize(context, obj, writer); + } + } + + private static readonly MsgPackStringTrie DeserializationTrie = InitializeDeserializationTrie(); + private static MsgPackStringTrie InitializeDeserializationTrie() + { + ReadOnlySpan __name = new byte[] { 0xA4, (byte)'N', (byte)'a', (byte)'m', (byte)'e' }; + ReadOnlySpan __age = new byte[] { 0xA3, (byte)'A', (byte)'g', (byte)'e' }; + ReadOnlySpan __isActive = new byte[] { 0xAA, (byte)'i', (byte)'s', (byte)'A', (byte)'c', (byte)'t', (byte)'i', (byte)'v', (byte)'e' }; + ReadOnlySpan __roles = new byte[] { 0xA4, (byte)'N', (byte)'a', (byte)'m', (byte)'e', }; + ReadOnlySpan __attributes = new byte[] { 0xAC, (byte)'A', (byte)'t', (byte)'t', (byte)'r', (byte)'i', (byte)'b', (byte)'u', (byte)'t', (byte)'e', (byte)'s' }; + var trie = new MsgPackStringTrie(5); + trie.TryAdd(__name, 0); + trie.TryAdd(__age, 1); + trie.TryAdd(__isActive, 2); + trie.TryAdd(__roles, 3); + trie.TryAdd(__attributes, 4); + return trie; + } + + public SampleObject Deserialize(in DeserializationOperationContext context, in SequenceReader reader) + { + // T が参照型でデフォルトコンストラクターがある -> DeserializeTo + var obj = new SampleObject(); + this.DeserializeTo(context, reader, obj); + return obj; + + // T にデフォルトコンストラクターがない -> from DeserializeTo と同じ処理をインライン実装 + // .... + // return new SampleObject(name, age, isActive, roles, attributes); + + // T がミュータブルな値型 + // ... + // var obj = default; + // obj.Name = name; + // obj.Age = age; + // obj.IsActive = isActive; + // obj.Roles = roles; // Roles は絶対に null のはず。 + // obj.Attributes = attributes; // Attributes は絶対に null のはず。 + // return obj; + } + + public async ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource) + { + // T が参照型でデフォルトコンストラクターがある -> DeserializeToAsync + var obj = new SampleObject(); + await this.DeserializeToAsync(context, streamSource, obj).ConfigureAwait(false); + return obj; + + // T にデフォルトコンストラクターがない -> DeserializeToAsync と同じ処理をインライン実装 + // .... + // return new SampleObject(name, age, isActive, roles, attributes); + + // T がミュータブルな値型 + // ... + // var obj = default; + // obj.Name = name; + // obj.Age = age; + // obj.IsActive = isActive; + // obj.Roles = roles; // Roles は絶対に null のはず。 + // obj.Attributes = attributes; // Attributes は絶対に null のはず。 + // return obj; + } + + public void DeserializeTo(in DeserializationOperationContext context, in SequenceReader reader, in SampleObject obj) + { + string name = default!; + int age = default; + bool isActive = default; + var roles = obj.Roles; + var attributes = obj.Attributes; + + var decoder = context.Decoder; + CollectionType arrayOrMap; + long itemsCount; + CollectionItemIterator propertyIterator; + + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + arrayOrMap = decoder.DecodeArrayOrMapHeader(reader, out itemsCount); + if (itemsCount < 5) + { + throw new MessageTypeException(); // Use Throws + } + + propertyIterator = default; + } + else + { + arrayOrMap = decoder.DecodeArrayOrMap(reader, out propertyIterator); + itemsCount = -1; + } + + context.IncrementDepth(); + + // If !SerializerGenerationOptions.InfersObjectSerialization && SerializerGenerationOptions.UseArray || typeof(T).IsDefined([SerializeAs(Array)]) + if (arrayOrMap.IsArray) + { + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(reader, ref propertyIterator); + } + + name = decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(reader, ref propertyIterator); + } + + age = decoder.DecodeInt32(reader); + + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(reader, ref propertyIterator); + } + + isActive = decoder.DecodeBoolean(reader); + + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(reader, ref propertyIterator); + } + + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + var arrayLength = decoder.DecodeArrayHeader(reader); + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + + for (var i = 0; i < arrayLength; i++) + { + roles.Add(decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + } + else + { + var iterator = decoder.DecodeArray(reader); + while (!iterator.CollectionEnds(reader)) + { + roles.Add(decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + iterator.Drain(reader); + } + context.DecrementDepth(); + + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(reader, ref propertyIterator); + } + + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + var mapCount = decoder.DecodeMapHeader(reader); + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var i = 0; i < mapCount; i++) + { + attributes.Add( + decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + } + else + { + var iterator = decoder.DecodeMap(reader); + while (!iterator.CollectionEnds(reader)) + { + attributes.Add( + decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + iterator.Drain(reader); + } + context.DecrementDepth(); + } + else + { + // Map + + // if !decoder.FormatFeatures.CanCountCollectionItems // OPTIMIZABLE + // while(!propertyIterator.CollectionEnds(reader)) + for (var i = 0; i < itemsCount; i++) + { + decoder.GetRawString(reader, out ReadOnlySpan key, context.CancellationToken); + + context.ValidatePropertyKeyLength(reader.Consumed, key.Length); + + // Use inlined trie, prefixed by the count as MP uint32. + // char is UTF-8, big endian. + // 1st node: [length(1-5)][chars(7-3)] + switch (DeserializationTrie.GetOrDefault(key)) + { + case 0: + { + name = decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + break; + } + case 1: + { + age = decoder.DecodeInt32(reader); + break; + } + case 2: + { + isActive = decoder.DecodeBoolean(reader); + break; + } + case 3: + { + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + var arrayLength = decoder.DecodeArrayHeader(reader); + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + for (var j = 0; j < arrayLength; j++) + { + roles.Add(decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + } + else + { + var iterator = decoder.DecodeArray(reader); + while (!iterator.CollectionEnds(reader)) + { + roles.Add(decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + iterator.Drain(reader); + } + context.DecrementDepth(); + break; + } + case 4: + { + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + var mapCount = decoder.DecodeMapHeader(reader); + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var j = 0; j < mapCount; j++) + { + attributes.Add( + decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + } + else + { + var iterator = decoder.DecodeMap(reader); + while (!iterator.CollectionEnds(reader)) + { + attributes.Add( + decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + iterator.Drain(reader); + } + context.DecrementDepth(); + break; + } + } + } + } + + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + decoder.Drain(reader, context.CollectionContext, itemsCount - 5, context.CancellationToken); + } + else + { + propertyIterator.Drain(reader); + } + + context.DecrementDepth(); + + obj.Name = name; + obj.Age = age; + obj.IsActive = isActive; + // If settable + // obj.Roles = roles; + // If settable + // obj.Attributes = attributes; + } + + private static void CheckNextItemExists(SequenceReader reader, ref CollectionItemIterator propertyIterator) + { + if (!propertyIterator.CollectionEnds(reader)) + { + throw new MessageTypeException(); // Use Throws + } + } + + private static bool TryDecodeArrayOrMapHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out long itemsCount, out CollectionType arrayOrMap, out CollectionItemIterator propertyIterator, out int requestHint) + { + context.IncrementDepth(); + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(reader, out itemsCount, out requestHint); + if (arrayOrMap.IsNone) + { + return false; + } + + if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + if (itemsCount < 5) + { + throw new MessageTypeException(); // Use Throws + } + } + + memory = memory.Slice(unchecked((int)reader.Consumed)); + + return true; + } + + private static bool TryDecodeArrayHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out int arrayLength, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + arrayLength = context.Decoder.DecodeArrayHeader(reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeMapHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out int mapCount, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + mapCount = context.Decoder.DecodeMapHeader(reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeValueOfName(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string name, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + name = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeValueOfAge(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out int age, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + age = context.Decoder.DecodeInt32(reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeValueOfIsActive(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out bool isActive, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + isActive = context.Decoder.DecodeBoolean(reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeItemOfRoles(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string item, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + item = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeKeyOfAttributes(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string key, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + key = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeValueOfAttributes(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string value, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + value = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeArray(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + iterator = context.Decoder.DecodeArray(reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeMap(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + iterator = context.Decoder.DecodeMap(reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryGetRawString(in DeserializationOperationContext context, ref ReadOnlyMemory memory, [NotNullWhen(true)]out byte[] key, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + ReadOnlySpan span; + if (!context.Decoder.GetRawString(reader, out span, out requestHint, context.CancellationToken)) + { + key = default!; + return false; + } + + key = context.ArrayPool.Rent(span.Length); + span.CopyTo(key); + return true; + } + + private static bool TryDrain(in DeserializationOperationContext context, ref ReadOnlyMemory memory, int remaining, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + context.Decoder.Drain(reader, context.CollectionContext, remaining, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private bool TryCheckNextItemExists(ref CollectionItemIterator propertyIterator, ReadOnlyMemory memory, out int requestHint) + { + if (propertyIterator.CollectionEnds(memory, out requestHint)) + { + throw new MessageTypeException(); // use throws + } + + return requestHint == 0; + } + + public async ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, SampleObject obj) + { + // T が値型 + // throw new NotSupportedException(); + + var buffer = context.ArrayPool.Rent(2 * 1024 * 1024); + try + { + var provider = new StreamReadOnlyMemoryProvider(streamSource, buffer); + var memory = await provider.GetNextAsync(default, 0, context.CancellationToken).ConfigureAwait(false); + int requestHint; + + string name = default!; + int age = default; + bool isActive = default; + var roles = obj.Roles; + var attributes = obj.Attributes; + + var decoder = context.Decoder; + context.IncrementDepth(); + + long itemsCount; + CollectionType arrayOrMap; + CollectionItemIterator propertyIterator; + while (!TryDecodeArrayOrMapHeader(context, ref memory, out itemsCount, out arrayOrMap, out propertyIterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If !SerializerGenerationOptions.InfersObjectSerialization && SerializerGenerationOptions.UseArray || typeof(T).IsDefined([SerializeAs(Array)]) + if (arrayOrMap.IsArray) + { + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + while (!TryDecodeValueOfName(context, ref memory, out name, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + while (!TryDecodeValueOfAge(context, ref memory, out age, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + while (!TryDecodeValueOfIsActive(context, ref memory, out isActive, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + context.IncrementDepth(); + + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long arrayLength; + while (!TryDecodeArrayHeader(context, ref memory, out arrayLength, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + + for (var i = 0; i < arrayLength; i++) + { + string item; + while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + roles.Add(item); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeArray(context, ref memory, out iterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(memory, out requestHint)) + { + if (requestHint != 0) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + string item; + while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + roles.Add(item); + } + + while(!iterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + context.DecrementDepth(); + + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long mapCount; + while (!TryDecodeMapHeader(context, ref memory, out mapCount, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var i = 0; i < mapCount; i++) + { + string key; + while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + string value; + while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + attributes.Add(key, value); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeMap(context, ref memory, out iterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(memory, out requestHint)) + { + if (requestHint != 0) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + string key; + while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + string value; + while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + attributes.Add(key, value); + } + + while (!iterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + context.DecrementDepth(); + } + else + { + // Map + // #if !context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + // while (!propertyIterator.CollectionEnds(memory, out requestHint)) + // { + // if (requestHint != 0) + // { + // memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + // continue; + // } + for (var i = 0; i < itemsCount; i++) + { + byte[] propertyKey = null!; + try + { + while (!TryGetRawString(context, ref memory, out propertyKey, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // Use inlined trie, prefixed by the count as MP uint32. + // char is UTF-8, big endian. + // 1st node: [length(1-5)][chars(7-3)] + switch (DeserializationTrie.GetOrDefault(propertyKey)) + { + case 0: + { + while (!TryDecodeValueOfName(context, ref memory, out name, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + break; + } + case 1: + { + while (!TryDecodeValueOfAge(context, ref memory, out age, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + break; + } + case 2: + { + while (!TryDecodeValueOfIsActive(context, ref memory, out isActive, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + break; + } + case 3: + { + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long arrayLength; + while (!TryDecodeArrayHeader(context, ref memory, out arrayLength, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + for (var j = 0; j < arrayLength; j++) + { + string item; + while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + roles.Add(item); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeArray(context, ref memory, out iterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(memory, out requestHint)) + { + if (requestHint != 0) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + string item; + while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + roles.Add(item); + } + + while (!iterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + context.DecrementDepth(); + break; + } + case 4: + { + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long mapCount; + while (!TryDecodeMapHeader(context, ref memory, out mapCount, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var j = 0; j < mapCount; j++) + { + string key; + while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + string value; + while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + attributes.Add(key, value); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeMap(context, ref memory, out iterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(memory, out requestHint)) + { + if (requestHint != 0) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + string key; + while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + string value; + while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + attributes.Add(key, value); + } + + while (!iterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + context.DecrementDepth(); + break; + } + } + } + finally + { + if (propertyKey != null) + { + context.ArrayPool.Return(propertyKey); + } + } + } + } + + if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + itemsCount -= 5; + while (!TryDrain(context, ref memory, itemsCount, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + else + { + while(!propertyIterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + context.DecrementDepth(); + + obj.Name = name; + obj.Age = age; + obj.IsActive = isActive; + // If settable + // obj.Roles = roles; + // If settable + // obj.Attributes = attributes; + } + finally + { + context.ArrayPool.Return(buffer, clearArray: true); + } + } + } +} diff --git a/src/MsgPack.Extensions/MsgPack.Extensions.csproj b/src/MsgPack.Extensions/MsgPack.Extensions.csproj new file mode 100644 index 000000000..cb6319069 --- /dev/null +++ b/src/MsgPack.Extensions/MsgPack.Extensions.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/src/MsgPack.Json/Json/JsonEncoder.cs b/src/MsgPack.Json/Json/JsonEncoder.cs new file mode 100644 index 000000000..721247c94 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonEncoder.cs @@ -0,0 +1,784 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.IO; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + internal static class JsonTokens + { + public static readonly ReadOnlyMemory Null = new[] { (byte)'n', (byte)'u', (byte)'l', (byte)'l' }; + public static readonly ReadOnlyMemory True = new[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; + public static readonly ReadOnlyMemory False = new[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; + public static readonly ReadOnlyMemory ArrayStart = new[] { (byte)'[' }; + public static readonly ReadOnlyMemory ArrayEnd = new[] { (byte)']' }; + public static readonly ReadOnlyMemory MapStart = new[] { (byte)'{' }; + public static readonly ReadOnlyMemory MapEnd = new[] { (byte)'}' }; + public static readonly ReadOnlyMemory Whitespace = new[] { (byte)' ' }; + public static readonly ReadOnlyMemory Comma = new[] { (byte)',' }; + public static readonly ReadOnlyMemory Colon = new[] { (byte)':' }; + public static readonly ReadOnlyMemory Quatation = new[] { (byte)'"' }; + } + + internal static class JsonCharactor + { + public static readonly ReadOnlyMemory MustBeEscaped = new int[] { '\\', '"' }.Concat(Enumerable.Range(0, 0x1F)).Select(i => unchecked((byte)i)).ToArray(); + public static readonly ReadOnlyMemory ShouldBeEscaped = new[] { (byte)'/', (byte)'\'', (byte)'<', (byte)'>', (byte)'&' }; + public static readonly ReadOnlyMemory CarriageReturn = new[] { (byte)'\r' }; + public static readonly ReadOnlyMemory LineFeed = new[] { (byte)'\n' }; + } + + internal static class JsonEscapeSequence + { + public static readonly ReadOnlyMemory Unicode = new[] { (byte)'\\', (byte)'u' }; + + public static readonly ReadOnlyMemory ReverseSolidous = new[] { (byte)'\\', (byte)'\\' }; + public static readonly ReadOnlyMemory Quatation = new[] { (byte)'\\', (byte)'"' }; + public static readonly ReadOnlyMemory Tab = new[] { (byte)'\\', (byte)'t' }; + public static readonly ReadOnlyMemory CarriageReturn = new[] { (byte)'\\', (byte)'r' }; + public static readonly ReadOnlyMemory LineFeed = new[] { (byte)'\\', (byte)'n' }; + public static readonly ReadOnlyMemory BackSpace = new[] { (byte)'\\', (byte)'b' }; + public static readonly ReadOnlyMemory FormFeed = new[] { (byte)'\\', (byte)'f' }; + + public static readonly StandardFormat UnicodeFormat = new StandardFormat('X', 4); + } + + public enum NaNHandling + { + Default = 0, + Null = 1, + Error = 2, + Custom = 3 + } + + public enum InfinityHandling + { + Default = 0, + MinMax = 1, + Error = 2, + Custom = 3 + } + + [Flags] + public enum JsonParseOptions + { + None = 0, + AllowHashSingleLineComment = 0x1, + AllowDoubleSolidousSingleLineComment = 0x2, + AllowUnicodeWhitespace = 0x10, + AllowAllTrivias = 0xFF, + AllowNaN = 0x100, + AllowInfinity = 0x200, + AllowUndefined = 0x1000, + AllowIrregalValues = 0xFF00, + AllowEqualSignSeparator = 0x10000, + AllowSemicolonDelimiter = 0x20000, + AllowExtraComma = 0x40000, + AllowSingleQuatationString = 0x100000, + AllowUnescapedNewLineInString = 0x200000, + AllowUnescapedTabInString = 0x400000, + AllowWellknownSyntaxErrors = 0xFF0000, + AllowAllErrors = unchecked((int)0xFFFFFFFF) + } + + public class JsonEncoderOptionsBase + { +#warning TODO: Options + } + + public class JsonEncoderBase : Internal.Encoder + { +#warning TODO: Abstract +#warning TODO: tuning + private static readonly Func, int> s_singleFormatter = + (v, m) => Utf8Formatter.TryFormat(v, m.Span, out var used) ? used : -1; + private static readonly Func, int> s_defaultSingleNanFormatter = + (_, m) => WriteNull(m.Span); + private static readonly Func, int> s_defaultSingleInfinityFormatter = + (v, m) => Utf8Formatter.TryFormat(v < 0 ? Single.MinValue : Single.MaxValue, m.Span, out var used) ? used : -1; + + private static readonly Func, int> s_doubleFormatter = + (v, m) => Utf8Formatter.TryFormat(v, m.Span, out var used) ? used : -1; + private static readonly Func, int> s_defaultDoubleNanFormatter = + (_, m) => WriteNull(m.Span); + private static readonly Func, int> s_defaultDoubleInfinityFormatter = + (v, m) => Utf8Formatter.TryFormat(v < 0 ? Single.MinValue : Single.MaxValue, m.Span, out var used) ? used : -1; + + private readonly Func, int> _singleInfinityFormatter; + private readonly Func, int> _singleNanFormatter; + private readonly Func, int> _doubleInfinityFormatter; + private readonly Func, int> _doubleNanFormatter; + private readonly bool _doIndent; + private readonly byte[] _indentString; + private readonly byte[] _newLine; + private readonly ReadOnlyMemory _toBeEscaped; + + private int _indentLevel; + +#warning TODO: Options + protected JsonEncoderBase() + { + this._singleInfinityFormatter = s_defaultSingleInfinityFormatter; + this._singleNanFormatter = s_defaultSingleNanFormatter; + this._doubleInfinityFormatter = s_defaultDoubleInfinityFormatter; + this._doubleNanFormatter = s_defaultDoubleNanFormatter; + this._doIndent = false; + this._indentString = new[] { (byte)' ', (byte)' ' }; + this._newLine = new[] { (byte)'\n' }; + this._toBeEscaped = JsonCharactor.MustBeEscaped; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static int WriteNull(Span span) + { + if (span.Length < 4) + { + return -1; + } + + JsonTokens.Null.Span.CopyTo(span); + return 4; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void Encode(int value, IBufferWriter buffer) + { + var span = buffer.GetSpan(); + while (true) + { + if (!Utf8Formatter.TryFormat(value, span, out var used)) + { + span = buffer.GetSpan(span.Length * 2); + } + else + { + buffer.Advance(used); + break; + } + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void Encode(long value, IBufferWriter buffer) + { + var span = buffer.GetSpan(); + while (true) + { + if (!Utf8Formatter.TryFormat(value, span, out var used)) + { + span = buffer.GetSpan(span.Length * 2); + } + else + { + buffer.Advance(used); + break; + } + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void Encode(float value, IBufferWriter buffer) + { + var formatter = + Single.IsNaN(value) ? + this._singleNanFormatter : + Single.IsInfinity(value) ? + this._singleInfinityFormatter : + s_singleFormatter; + + var memory = buffer.GetMemory(); + while (true) + { + var used = formatter(value, memory); + if (used < 0) + { + memory = buffer.GetMemory(memory.Length * 2); + } + else + { + buffer.Advance(used); + break; + } + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void Encode(double value, IBufferWriter buffer) + { + var formatter = + Double.IsNaN(value) ? + this._doubleNanFormatter : + Double.IsInfinity(value) ? + this._doubleInfinityFormatter : + s_doubleFormatter; + + var memory = buffer.GetMemory(); + while (true) + { + var used = formatter(value, memory); + if (used < 0) + { + memory = buffer.GetMemory(memory.Length * 2); + } + else + { + buffer.Advance(used); + break; + } + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void Encode(bool value, IBufferWriter buffer) + { + if (value) + { + buffer.Write(JsonTokens.True.Span); + } + else + { + buffer.Write(JsonTokens.False.Span); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeNull(IBufferWriter buffer) + => buffer.Write(JsonTokens.Null.Span); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + protected void WriteIndent(IBufferWriter buffer) + { + if (this._doIndent) + { + buffer.Write(this._newLine); + for (var i = 0; i < this._indentLevel; i++) + { + buffer.Write(this._indentString); + } + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeArrayStart(uint length, IBufferWriter buffer) + { + buffer.Write(JsonTokens.ArrayStart.Span); + this._indentLevel++; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeArrayEnd(uint length, IBufferWriter buffer) + { + this._indentLevel--; + this.WriteIndent(buffer); + buffer.Write(JsonTokens.ArrayEnd.Span); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeArrayItemStart(uint index, IBufferWriter buffer) + { + this.WriteIndent(buffer); + if (index > 0) + { + buffer.Write(JsonTokens.Comma.Span); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeArrayItemEnd(uint index, IBufferWriter buffer) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeMapStart(uint length, IBufferWriter buffer) + { + buffer.Write(JsonTokens.MapStart.Span); + this._indentLevel++; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeMapEnd(uint length, IBufferWriter buffer) + { + this._indentLevel--; + this.WriteIndent(buffer); + buffer.Write(JsonTokens.MapEnd.Span); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeMapKeyStart(uint index, IBufferWriter buffer) + { + this.WriteIndent(buffer); + if (index > 0) + { + buffer.Write(JsonTokens.Comma.Span); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeMapKeyEnd(uint index, IBufferWriter buffer) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeMapValueStart(uint index, IBufferWriter buffer) + { + buffer.Write(JsonTokens.Colon.Span); + if (this._doIndent) + { + buffer.Write(JsonTokens.Whitespace.Span); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeMapValueEnd(uint index, IBufferWriter buffer) { } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static ReadOnlyMemory GetEscapeSequence(byte c, bool newLineAllowed) + { + switch (c) + { + case (byte)'\b': + { + return JsonEscapeSequence.BackSpace; + } + case (byte)'\r': + { + if (newLineAllowed) + { + // Does not escape. + return JsonCharactor.CarriageReturn; + } + + return JsonEscapeSequence.CarriageReturn; + } + case (byte)'\f': + { + return JsonEscapeSequence.FormFeed; + } + case (byte)'\n': + { + if (newLineAllowed) + { + // Does not escape. + return JsonCharactor.LineFeed; + } + + return JsonEscapeSequence.LineFeed; + } + case (byte)'"': + { + return JsonEscapeSequence.Quatation; + } + case (byte)'\\': + { + return JsonEscapeSequence.ReverseSolidous; + } + case (byte)'\t': + { + return JsonEscapeSequence.Tab; + } + } + + // Use \uXXXX + return ReadOnlyMemory.Empty; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeString(ReadOnlySpan encodedValue, int charLength, IBufferWriter buffer) + { + buffer.Write(JsonTokens.Quatation.Span); + + this.EncodeStringBody(encodedValue, buffer); + + // End quot + buffer.Write(JsonTokens.Quatation.Span); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private void EncodeStringBody(ReadOnlySpan encodedValue, IBufferWriter buffer) + { + var source = encodedValue; + var sink = buffer.GetSpan(encodedValue.Length); + while (!source.IsEmpty) + { + var moreBytes = this.EscapeStringContent(source, sink, out var sourceUsed, out var sinkUsed); + buffer.Advance(sinkUsed); + source = source.Slice(sourceUsed); + sink = buffer.GetSpan(moreBytes); + } + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private int EscapeStringContent(ReadOnlyMemory source, Memory sink, out int sourceUsed, out int sinkUsed) + => this.EscapeStringContent(source.Span, sink.Span, out sourceUsed, out sinkUsed); + + private int EscapeStringContent(ReadOnlySpan source, Span sink, out int sourceUsed, out int sinkUsed) + { + sourceUsed = 0; + sinkUsed = 0; + + while (!sink.IsEmpty) + { + var toBeEscaped = source.IndexOfAny(this._toBeEscaped.Span); + if (toBeEscaped < 0) + { + // Copy entire source to sink, and then advance buffer. + source.CopyTo(sink); + sourceUsed += source.Length; + sinkUsed += source.Length; + continue; + } + + if (toBeEscaped > 0) + { + // Copy source data which have not to be escaped to sink, and then advance buffer. + source.Slice(0, toBeEscaped).CopyTo(sink); + sink = sink.Slice(toBeEscaped); + sourceUsed += toBeEscaped; + sinkUsed += toBeEscaped; + source = source.Slice(toBeEscaped); + } + +#warning TODO: OPTIONS + var escapeSequence = GetEscapeSequence(source[0], newLineAllowed: false); + switch (escapeSequence.Length) + { + case 1: + { + // Does not escape because of the option. + sink[0] = source[0]; + sink = sink.Slice(1); + sinkUsed++; + break; + } + case 0: + { + // \uXXXX escape + if (sink.Length < 6) + { + // Return to expecte realloc. + return source.Length + 6; + } + + JsonEscapeSequence.Unicode.Span.CopyTo(sink); + sink = sink.Slice(2); // \u + Utf8Formatter.TryFormat(source[0], sink, out _, JsonEscapeSequence.UnicodeFormat); + sink = sink.Slice(4); // xxxx + sinkUsed += 6; + break; + } + default: + { + // Use returned escape sequence + + if (sink.Length < escapeSequence.Length) + { + // Return to expecte realloc. + return source.Length + escapeSequence.Length; + } + + escapeSequence.Span.CopyTo(sink); + sink = sink.Slice(escapeSequence.Length); + sinkUsed += escapeSequence.Length; + break; + } + } // switch + + sourceUsed++; + source = source.Slice(1); + } // while (!source.IsEmpty) + + return 0; + } + + public override void EncodeString(in ReadOnlySequence encodedValue, int charLength, IBufferWriter buffer) + { + // Start quot + buffer.Write(JsonTokens.Quatation.Span); + + var source = new SequenceReader(encodedValue); + + while (!source.UnreadSpan.IsEmpty) + { + var length = source.UnreadSpan.Length; + this.EncodeStringBody(source.UnreadSpan, buffer); + source.Advance(length); + } + + // End quot + buffer.Write(JsonTokens.Quatation.Span); + } + + public override void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding encoding) + { + if (value.Length < 256) + { + // Fast-path with stackalloc + var charLength = unchecked((int)value.Length); + Span encodingBuffer = stackalloc byte[encoding.GetMaxByteCount(charLength)]; + var actualLength = encoding.GetBytes(value, encodingBuffer); + this.EncodeString(encodingBuffer.Slice(0, actualLength), charLength, buffer); + } + else + { + // Slow-path + + // Start quot + buffer.Write(JsonTokens.Quatation.Span); + + var encoder = encoding.GetEncoder(); + var source = value; + + var encodingBuffer = this.ByteArrayPool.Rent(Math.Min(2 * 1024 * 1024, encoding.GetMaxByteCount(unchecked((int)(value.Length & 0x1FFFFFFF))))); + try + { + Span encodingSpan = encodingBuffer; + while (!source.IsEmpty) + { + var length = source.Length; + encoder.Convert(source, encodingSpan, flush: source.Length <= encodingSpan.Length, out var charsUsed, out var bytesUsed, out _); + + this.EncodeStringBody(encodingSpan.Slice(0, bytesUsed), buffer); + source = source.Slice(charsUsed); + } + } + finally + { + this.ByteArrayPool.Return(encodingBuffer, this.ClearsBufferPool); + } + + // End quot + buffer.Write(JsonTokens.Quatation.Span); + } + } + + public override void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding encoding) + { + if (value.IsSingleSegment) + { + this.EncodeString(value.FirstSpan, buffer, encoding); + return; + } + + if (value.Length < 256) + { + // Fast-path with stackalloc + var charLength = unchecked((int)value.Length); + Span encodingBuffer = stackalloc byte[encoding.GetMaxByteCount(charLength)]; + var actualLength = encoding.GetBytes(value, encodingBuffer); + this.EncodeString(encodingBuffer.Slice(0, actualLength), charLength, buffer); + } + else + { + // Slow-path + + // Start quot + buffer.Write(JsonTokens.Quatation.Span); + + var encoder = encoding.GetEncoder(); + var source = new SequenceReader(value); + + var encodingBuffer = this.ByteArrayPool.Rent(Math.Min(2 * 1024 * 1024, encoding.GetMaxByteCount(unchecked((int)(value.Length & 0x1FFFFFFF))))); + try + { + Span encodingSpan = encodingBuffer; + while (!source.UnreadSpan.IsEmpty) + { + var length = source.UnreadSpan.Length; + encoder.Convert(source.UnreadSpan, encodingSpan, flush: source.Remaining <= encodingSpan.Length, out var charsUsed, out var bytesUsed, out _); + + this.EncodeStringBody(encodingSpan.Slice(0, bytesUsed), buffer); + source.Advance(charsUsed); + } + } + finally + { + this.ByteArrayPool.Return(encodingBuffer, this.ClearsBufferPool); + } + + // End quot + buffer.Write(JsonTokens.Quatation.Span); + } + } + + protected override async ValueTask EncodeLargeStringCoreAsync(TextReader source, Stream sink, Encoding encoding, Func bufferStreamProvider, CancellationToken cancellationToken) + { + // Start quot + await sink.WriteAsync(JsonTokens.Quatation, cancellationToken).ConfigureAwait(false); + long written = JsonTokens.Quatation.Length; + + var maxBytesPerChar = encoding.GetMaxByteCount(1); + var encoder = encoding.GetEncoder(); + var charBufferLength = ByteBufferLength / maxBytesPerChar; + + var encodingBufferArray = this.ByteArrayPool.Rent(ByteBufferLength); + try + { + var sourceBufferArray = this.CharArrayPool.Rent(charBufferLength); + try + { + var escapingBufferArray = this.ByteArrayPool.Rent(ByteBufferLength); + try + { + Memory sourceBuffer = sourceBufferArray; + Memory encodingBuffer = encodingBufferArray; + Memory escapingBuffer = escapingBufferArray; + + bool completed = false; + + do + { + var readLength = await source.ReadAsync(sourceBuffer, cancellationToken).ConfigureAwait(false); + var shouldFlush = readLength <= sourceBuffer.Length && source.Peek() < 0; + var chars = sourceBuffer.Slice(0, readLength); + while (!chars.IsEmpty) + { + encoder.Convert(chars, encodingBuffer, shouldFlush, out var charsUsed, out var bytesUsed, out completed); + encodingBuffer = encodingBuffer.Slice(0, bytesUsed); + + while (!encodingBuffer.IsEmpty) + { + var moreEscapingBufferSize = this.EscapeStringContent(encodingBuffer, escapingBuffer, out var encodingBufferUsed, out var escapingBufferUsed); + + await sink.WriteAsync(escapingBuffer.Slice(0, escapingBufferUsed), cancellationToken).ConfigureAwait(false); + written += escapingBufferUsed; + + encodingBuffer = encodingBuffer.Slice(encodingBufferUsed); + escapingBuffer = escapingBuffer.Compact(escapingBufferArray, escapingBufferUsed); + } + + chars = chars.Slice(charsUsed); + } + } while (!completed); + } + finally + { + this.ByteArrayPool.Return(escapingBufferArray, this.ClearsBufferPool); + } + } + finally + { + this.CharArrayPool.Return(sourceBufferArray, this.ClearsBufferPool); + } + } + finally + { + this.ByteArrayPool.Return(encodingBufferArray, this.ClearsBufferPool); + } + + // End quot + await sink.WriteAsync(JsonTokens.Quatation, cancellationToken).ConfigureAwait(false); + written += JsonTokens.Quatation.Length; + + return written; + } + + public override void EncodeBinary(ReadOnlySpan value, IBufferWriter buffer) + { + // Start quot + buffer.Write(JsonTokens.Quatation.Span); + + // Enough space -- over 138% + var span = buffer.GetSpan((int)(value.Length * 1.38)); + Base64.EncodeToUtf8(value, span, out var bytesConsumed, out var bytesWritten, isFinalBlock: true); + buffer.Advance(bytesWritten); + + // End quot + buffer.Write(JsonTokens.Quatation.Span); + } + + public override void EncodeBinary(in ReadOnlySequence value, IBufferWriter buffer) + { + // Start quot + buffer.Write(JsonTokens.Quatation.Span); + + var array = this.ByteArrayPool.Rent(ByteBufferLength); + try + { + var reader = new SequenceReader(value); + Span sourceBuffer = array; + + while (true) + { + if (!reader.TryCopyTo(sourceBuffer)) + { + while (!reader.End) + { + reader.UnreadSpan.CopyTo(sourceBuffer); + sourceBuffer = sourceBuffer.Slice(0, reader.UnreadSpan.Length); + reader.Advance(reader.UnreadSpan.Length); + } + } + + var span = buffer.GetSpan(sourceBuffer.Length * 2); + var status = Base64.EncodeToUtf8(sourceBuffer, span, out var bytesConsumed, out var bytesWritten, reader.End); + buffer.Advance(bytesWritten); + reader.Advance(bytesConsumed); + sourceBuffer = array; + + if (status == OperationStatus.Done && reader.End) + { + break; + } + } + + // End quot + buffer.Write(JsonTokens.Quatation.Span); + } + finally + { + this.ByteArrayPool.Return(array, this.ClearsBufferPool); + } + + // End quot + buffer.Write(JsonTokens.Quatation.Span); + } + + protected override async ValueTask EncodeLargeBinaryCoreAsync(Stream source, Stream sink, Func bufferStreamProvider, CancellationToken cancellationToken) + { + static OperationStatus EncodeBase64ToUtf8(ReadOnlyMemory sourceMemory, Memory sinkMemory, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) + => Base64.EncodeToUtf8(sourceMemory.Span, sinkMemory.Span, out bytesConsumed, out bytesWritten, isFinalBlock); + + // End quot + await sink.WriteAsync(JsonTokens.Quatation, cancellationToken).ConfigureAwait(false); + long written = JsonTokens.Quatation.Length; + + var sourceBufferArray = this.ByteArrayPool.Rent(ByteBufferLength / 2); + try + { + var sinkBufferArray = this.ByteArrayPool.Rent(ByteBufferLength); + try + { + Memory sourceBuffer = sourceBufferArray; + Memory sinkBuffer = sinkBufferArray; + + while (true) + { + await source.ReadAsync(sourceBuffer, cancellationToken).ConfigureAwait(false); + + var status = EncodeBase64ToUtf8(sourceBuffer, sinkBuffer, out var sourceConsumed, out var sinkWritten, source.Position == source.Length); + + if (status == OperationStatus.Done && source.Position == source.Length) + { + break; + } + } + } + finally + { + this.ByteArrayPool.Return(sinkBufferArray, this.ClearsBufferPool); + } + } + finally + { + this.ByteArrayPool.Return(sourceBufferArray, this.ClearsBufferPool); + } + + // End quot + await sink.WriteAsync(JsonTokens.Quatation, cancellationToken).ConfigureAwait(false); + written += JsonTokens.Quatation.Length; + return written; + } + } +} diff --git a/src/MsgPack.Json/MsgPack.Json.csproj b/src/MsgPack.Json/MsgPack.Json.csproj new file mode 100644 index 000000000..214660b44 --- /dev/null +++ b/src/MsgPack.Json/MsgPack.Json.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp3.1 + true + + + + + + + + + + + + + + + + diff --git a/src/MsgPack.Serialization.Extensions/MsgPack.Serialization.Extensions.csproj b/src/MsgPack.Serialization.Extensions/MsgPack.Serialization.Extensions.csproj new file mode 100644 index 000000000..cb6319069 --- /dev/null +++ b/src/MsgPack.Serialization.Extensions/MsgPack.Serialization.Extensions.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj b/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj new file mode 100644 index 000000000..cb6319069 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/src/MsgPack.Serialization.SourceGeneration/MsgPack.Serialization.SourceGeneration.csproj b/src/MsgPack.Serialization.SourceGeneration/MsgPack.Serialization.SourceGeneration.csproj new file mode 100644 index 000000000..cb6319069 --- /dev/null +++ b/src/MsgPack.Serialization.SourceGeneration/MsgPack.Serialization.SourceGeneration.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.1 + + + diff --git a/src/MsgPack.Serialization/MsgPack.Serialization.csproj b/src/MsgPack.Serialization/MsgPack.Serialization.csproj new file mode 100644 index 000000000..d7a9b6293 --- /dev/null +++ b/src/MsgPack.Serialization/MsgPack.Serialization.csproj @@ -0,0 +1,15 @@ + + + + netcoreapp3.1 + + + + + + + + + + + From 6bed26a1816812e91631dc271325bba50fd0636f Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 14 Jun 2020 22:52:45 +0900 Subject: [PATCH 14/82] Code cleanup --- src/MsgPack/MessageNotSupportedException.cs | 12 ++++++------ src/MsgPack/MessagePackCode.cs | 2 +- src/MsgPack/MessageTypeException.cs | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/MsgPack/MessageNotSupportedException.cs b/src/MsgPack/MessageNotSupportedException.cs index 7bb9e077b..1ef21ad74 100644 --- a/src/MsgPack/MessageNotSupportedException.cs +++ b/src/MsgPack/MessageNotSupportedException.cs @@ -1,4 +1,4 @@ -#region -- License Terms -- +#region -- License Terms -- // // MessagePack for CLI // @@ -36,13 +36,13 @@ public sealed class MessageNotSupportedException : Exception /// /// Initializes a new instance of the class with the default error message. /// - public MessageNotSupportedException() : this( null ) { } + public MessageNotSupportedException() : this(null) { } /// /// Initializes a new instance of the class with a specified error message. /// /// The message that describes the error. - public MessageNotSupportedException( string message ) : this( message, null ) { } + public MessageNotSupportedException(string? message) : this(message, null) { } /// /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. @@ -51,7 +51,7 @@ public MessageNotSupportedException( string message ) : this( message, null ) { /// /// The exception that is the cause of the current exception, or a null if no inner exception is specified. /// - public MessageNotSupportedException( string message, Exception inner ) : base( message ?? "Specified object is not supported.", inner ) { } + public MessageNotSupportedException(string? message, Exception? inner) : base(message ?? "Specified object is not supported.", inner) { } #if !SILVERLIGHT && !NETSTANDARD1_1 && !NETSTANDARD1_3 /// @@ -68,8 +68,8 @@ public MessageNotSupportedException( string message, Exception inner ) : base( m /// /// The class name is null or is zero (0). /// - private MessageNotSupportedException( SerializationInfo info, StreamingContext context ) - : base( info, context ) { } + private MessageNotSupportedException(SerializationInfo info, StreamingContext context) + : base(info, context) { } #endif // !SILVERLIGHT && !NETSTANDARD1_1 && !NETSTANDARD1_3 } } diff --git a/src/MsgPack/MessagePackCode.cs b/src/MsgPack/MessagePackCode.cs index 0ca7a0ab9..44a3838c1 100644 --- a/src/MsgPack/MessagePackCode.cs +++ b/src/MsgPack/MessagePackCode.cs @@ -27,7 +27,7 @@ namespace MsgPack { #if UNITY && DEBUG - public + public #else internal #endif diff --git a/src/MsgPack/MessageTypeException.cs b/src/MsgPack/MessageTypeException.cs index e93d18933..7f00d2ba5 100644 --- a/src/MsgPack/MessageTypeException.cs +++ b/src/MsgPack/MessageTypeException.cs @@ -1,4 +1,4 @@ -#region -- License Terms -- +#region -- License Terms -- // // MessagePack for CLI // @@ -36,13 +36,13 @@ public class MessageTypeException : Exception /// /// Initializes a new instance of the class with the default error message. /// - public MessageTypeException() : this( null ) { } + public MessageTypeException() : this(null) { } /// /// Initializes a new instance of the class with a specified error message. /// /// The message that describes the error. - public MessageTypeException( string message ) : this( message, null ) { } + public MessageTypeException(string? message) : this(message, null) { } /// /// Initializes a new instance of the class with a specified error message and a reference to the inner exception that is the cause of this exception. @@ -51,7 +51,7 @@ public MessageTypeException( string message ) : this( message, null ) { } /// /// The exception that is the cause of the current exception, or a null if no inner exception is specified. /// - public MessageTypeException( string message, Exception inner ) : base( message ?? "Invalid message type.", inner ) { } + public MessageTypeException(string? message, Exception? inner) : base(message ?? "Invalid message type.", inner) { } #if !SILVERLIGHT && !NETSTANDARD1_1 && !NETSTANDARD1_3 /// @@ -68,7 +68,7 @@ public MessageTypeException( string message, Exception inner ) : base( message ? /// /// The class name is null or is zero (0). /// - protected MessageTypeException( SerializationInfo info, StreamingContext context ) : base( info, context ) { } + protected MessageTypeException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif // !SILVERLIGHT && !NETSTANDARD1_1 && !NETSTANDARD1_3 } } From 937c33b925db32209f3d306ae3ea4a413256080b Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 14 Jun 2020 22:25:39 +0900 Subject: [PATCH 15/82] Introduce format neutral ExtensionType This enables msgpack incompatible formats' extension type handling. --- ....Primitives.cs => Decoder`1.Primitives.cs} | 2 +- ....Primitives.tt => Decoder`1.Primitives.tt} | 2 +- ...ecoder.Strings.cs => Decoder`1.Strings.cs} | 2 +- ...ecoder.Strings.tt => Decoder`1.Strings.tt} | 2 +- .../Internal/{Decoder.cs => Decoder`1.cs} | 13 +++-- ....Primitives.cs => Encoder`1.Primitives.cs} | 2 +- ....Primitives.tt => Encoder`1.Primitives.tt} | 2 +- .../Internal/{Encoder.cs => Encoder`1.cs} | 7 +-- src/MsgPack.Abstraction/Internal/Ensure.cs | 11 +++++ .../Internal/NullExtensionType.cs | 11 +++++ .../MsgPack.Abstraction.csproj | 24 ++++----- ...s => DeserializationOperationContext`1.cs} | 10 ++-- .../Internal/IObjectSerializer`1.cs | 20 -------- .../Internal/IObjectSerializer`2.cs | 20 ++++++++ ....cs => SerializationOperationContext`1.cs} | 9 ++-- .../{Serializer`1.cs => Serializer`2.cs} | 22 ++++----- .../Internal/CurrentMessagePackEncoder.cs | 8 +-- .../Internal/MessagePackDecoder.Extension.cs | 6 ++- .../Internal/MessagePackDecoder.cs | 2 +- .../Internal/MessagePackEncoder.cs | 2 +- .../Internal/MessagePackExtensionType.cs | 43 ++++++++++++++++ src/MsgPack.Core/_SampleObject.cs | 49 +++++++++++-------- 22 files changed, 177 insertions(+), 92 deletions(-) rename src/MsgPack.Abstraction/Internal/{Decoder.Primitives.cs => Decoder`1.Primitives.cs} (99%) rename src/MsgPack.Abstraction/Internal/{Decoder.Primitives.tt => Decoder`1.Primitives.tt} (99%) rename src/MsgPack.Abstraction/Internal/{Decoder.Strings.cs => Decoder`1.Strings.cs} (99%) rename src/MsgPack.Abstraction/Internal/{Decoder.Strings.tt => Decoder`1.Strings.tt} (99%) rename src/MsgPack.Abstraction/Internal/{Decoder.cs => Decoder`1.cs} (94%) rename src/MsgPack.Abstraction/Internal/{Encoder.Primitives.cs => Encoder`1.Primitives.cs} (99%) rename src/MsgPack.Abstraction/Internal/{Encoder.Primitives.tt => Encoder`1.Primitives.tt} (98%) rename src/MsgPack.Abstraction/Internal/{Encoder.cs => Encoder`1.cs} (94%) create mode 100644 src/MsgPack.Abstraction/Internal/NullExtensionType.cs rename src/MsgPack.Abstraction/Serialization/Internal/{DeserializationOperationContext.cs => DeserializationOperationContext`1.cs} (80%) delete mode 100644 src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`1.cs create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs rename src/MsgPack.Abstraction/Serialization/Internal/{SerializationOperationContext.cs => SerializationOperationContext`1.cs} (77%) rename src/MsgPack.Abstraction/Serialization/{Serializer`1.cs => Serializer`2.cs} (84%) create mode 100644 src/MsgPack.Core/Internal/MessagePackExtensionType.cs diff --git a/src/MsgPack.Abstraction/Internal/Decoder.Primitives.cs b/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs similarity index 99% rename from src/MsgPack.Abstraction/Internal/Decoder.Primitives.cs rename to src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs index 9d4066b1a..8c4d48b33 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder.Primitives.cs +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs @@ -12,7 +12,7 @@ namespace MsgPack.Internal { - partial class Decoder + partial class Decoder { /// /// Decodes value from specified sequence. diff --git a/src/MsgPack.Abstraction/Internal/Decoder.Primitives.tt b/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt similarity index 99% rename from src/MsgPack.Abstraction/Internal/Decoder.Primitives.tt rename to src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt index 4a4be9e47..c814ebe99 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder.Primitives.tt +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt @@ -14,7 +14,7 @@ using System.Runtime.CompilerServices; namespace MsgPack.Internal { - partial class Decoder + partial class Decoder { <# foreach (var outputType in new [] { diff --git a/src/MsgPack.Abstraction/Internal/Decoder.Strings.cs b/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs similarity index 99% rename from src/MsgPack.Abstraction/Internal/Decoder.Strings.cs rename to src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs index 78a9715d2..17c91200f 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder.Strings.cs +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs @@ -16,7 +16,7 @@ namespace MsgPack.Internal { - partial class Decoder + partial class Decoder { /// /// Decodes value from specified sequence. diff --git a/src/MsgPack.Abstraction/Internal/Decoder.Strings.tt b/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt similarity index 99% rename from src/MsgPack.Abstraction/Internal/Decoder.Strings.tt rename to src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt index 4b7a3da9e..ad9ec6506 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder.Strings.tt +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt @@ -18,7 +18,7 @@ using System.Threading; namespace MsgPack.Internal { - partial class Decoder + partial class Decoder { <# foreach (var spec in new [] { diff --git a/src/MsgPack.Abstraction/Internal/Decoder.cs b/src/MsgPack.Abstraction/Internal/Decoder`1.cs similarity index 94% rename from src/MsgPack.Abstraction/Internal/Decoder.cs rename to src/MsgPack.Abstraction/Internal/Decoder`1.cs index 1b5c814da..9e9b30293 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder.cs +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.cs @@ -9,9 +9,16 @@ namespace MsgPack.Internal { - public abstract partial class Decoder #warning TODO: Use 'Try' prefix for published APIs which can return 'requestHint'. + /// + /// Defines an interface and basic functionarity of stateless . + /// + /// A type of extension type. + /// + /// The is stateless, so caller (serializer, writer, etc.) can cache the instance for performance. + /// + public abstract partial class Decoder { public FormatFeatures FormatFeatures { get; } @@ -164,13 +171,13 @@ public long DecodeMapHeader(in SequenceReader source) /// The decoded value is not a map. public abstract long DecodeMapHeader(in SequenceReader source, out int requestHint); - public virtual void DecodeExtension(in SequenceReader source, out byte typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) + public virtual void DecodeExtension(in SequenceReader source, out TExtensionType typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) { Throw.ExtensionsIsNotSupported(); // never body = default; requestHint = -1; - typeCode = default; + typeCode = default!; } public CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator) diff --git a/src/MsgPack.Abstraction/Internal/Encoder.Primitives.cs b/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs similarity index 99% rename from src/MsgPack.Abstraction/Internal/Encoder.Primitives.cs rename to src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs index 3b66f1243..32f4ce55d 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder.Primitives.cs +++ b/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs @@ -12,7 +12,7 @@ namespace MsgPack.Internal { - partial class Encoder + partial class Encoder { /// /// Encodes value to specified buffer. diff --git a/src/MsgPack.Abstraction/Internal/Encoder.Primitives.tt b/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt similarity index 98% rename from src/MsgPack.Abstraction/Internal/Encoder.Primitives.tt rename to src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt index c94109f3e..4d393c801 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder.Primitives.tt +++ b/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt @@ -14,7 +14,7 @@ using System.Runtime.CompilerServices; namespace MsgPack.Internal { - partial class Encoder + partial class Encoder { <# foreach (var inputType in new [] { diff --git a/src/MsgPack.Abstraction/Internal/Encoder.cs b/src/MsgPack.Abstraction/Internal/Encoder`1.cs similarity index 94% rename from src/MsgPack.Abstraction/Internal/Encoder.cs rename to src/MsgPack.Abstraction/Internal/Encoder`1.cs index 6ef3a7360..55c783099 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder.cs +++ b/src/MsgPack.Abstraction/Internal/Encoder`1.cs @@ -13,10 +13,11 @@ namespace MsgPack.Internal /// /// Defines an interface and basic functionarity of stateless . /// + /// A type of extension type. /// /// The is stateless, so caller (serializer, reader, etc.) can cache the instance for performance. /// - public abstract partial class Encoder + public abstract partial class Encoder { public EncoderOptions Options { get; } @@ -81,10 +82,10 @@ public void EncodeString(string? value, IBufferWriter buffer, Encoding? en public abstract void EncodeMapValueEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext); - public virtual void EncodeExtension(sbyte typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) + public virtual void EncodeExtension(TExtensionType typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) => Throw.ExtensionsIsNotSupported(); - public virtual void EncodeExtension(sbyte typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) + public virtual void EncodeExtension(TExtensionType typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) => Throw.ExtensionsIsNotSupported(); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] diff --git a/src/MsgPack.Abstraction/Internal/Ensure.cs b/src/MsgPack.Abstraction/Internal/Ensure.cs index 3927e712f..e2a24c72c 100644 --- a/src/MsgPack.Abstraction/Internal/Ensure.cs +++ b/src/MsgPack.Abstraction/Internal/Ensure.cs @@ -41,5 +41,16 @@ public static T IsNotLessThan(T value, T minInclusive, [CallerArgumentExpress return value; } + + public static T IsNotGreaterThan(T value, T maxInclusive, [CallerArgumentExpression("value")] string paramName = null!) + where T : struct, IComparable + { + if (value.CompareTo(maxInclusive) > 0) + { + throw new ArgumentOutOfRangeException(paramName, $"Value cannot be greater than {maxInclusive}."); + } + + return value; + } } } diff --git a/src/MsgPack.Abstraction/Internal/NullExtensionType.cs b/src/MsgPack.Abstraction/Internal/NullExtensionType.cs new file mode 100644 index 000000000..0de495693 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/NullExtensionType.cs @@ -0,0 +1,11 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Internal +{ + /// + /// Represents null extension type for underlying formats which do not support extension type. + /// + public struct NullExtensionType { } +} diff --git a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj index 1d0ea90cf..efa4c70c2 100644 --- a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj +++ b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj @@ -10,17 +10,17 @@ - + TextTemplatingFileGenerator - Decoder.Strings.cs + Decoder`1.Strings.cs - - Encoder.Primitives.cs + + Encoder`1.Primitives.cs TextTemplatingFileGenerator - + TextTemplatingFileGenerator - Decoder.Primitives.cs + Decoder`1.Primitives.cs @@ -29,20 +29,20 @@ - + True True - Decoder.Primitives.tt + Decoder`1.Primitives.tt - + True True - Decoder.Strings.tt + Decoder`1.Strings.tt - + True True - Encoder.Primitives.tt + Encoder`1.Primitives.tt diff --git a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext.cs b/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs similarity index 80% rename from src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext.cs rename to src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs index 2c267e72f..bac67deb3 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext.cs +++ b/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs @@ -2,23 +2,23 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. -using System; using System.Buffers; +using System.Text; using System.Threading; using MsgPack.Internal; namespace MsgPack.Serialization.Internal { - public struct DeserializationOperationContext + public struct DeserializationOperationContext { - public Decoder Decoder { get; } + public Decoder Decoder { get; } public DeserializationOptions Options { get; } public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } public ArrayPool ArrayPool => this.Options.ArrayPool; - public System.Text.Encoding? StringEncoding => this.Options.StringEncoding; + public Encoding? StringEncoding => this.Options.StringEncoding; - public DeserializationOperationContext(Decoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) + public DeserializationOperationContext(Decoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) { this.Decoder = Ensure.NotNull(decoder); this.Options = options ?? DeserializationOptions.Default; diff --git a/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`1.cs b/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`1.cs deleted file mode 100644 index 7f267b7db..000000000 --- a/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`1.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. -// This file is licensed under Apache2 license. -// See the LICENSE in the project root for more information. - -using System.Buffers; -using System.IO; -using System.Threading.Tasks; - -namespace MsgPack.Serialization.Internal -{ - public interface IObjectSerializer - { - void Serialize(in SerializationOperationContext context, T obj, IBufferWriter sink); - ValueTask SerializeAsync(SerializationOperationContext context, T obj, Stream streamSink); - T Deserialize(in DeserializationOperationContext context, in SequenceReader source); - ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource); - void DeserializeTo(in DeserializationOperationContext context, in SequenceReader source, in T obj); - ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, T obj); - } -} diff --git a/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs b/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs new file mode 100644 index 000000000..00976498a --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs @@ -0,0 +1,20 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.IO; +using System.Threading.Tasks; + +namespace MsgPack.Serialization.Internal +{ + public interface IObjectSerializer + { + void Serialize(in SerializationOperationContext context, T obj, IBufferWriter sink); + ValueTask SerializeAsync(SerializationOperationContext context, T obj, Stream streamSink); + T Deserialize(in DeserializationOperationContext context, in SequenceReader source); + ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource); + void DeserializeTo(in DeserializationOperationContext context, in SequenceReader source, in T obj); + ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, T obj); + } +} diff --git a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext.cs b/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs similarity index 77% rename from src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext.cs rename to src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs index 689a70b57..ef6167fc4 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext.cs +++ b/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs @@ -3,21 +3,22 @@ // See the LICENSE in the project root for more information. using System; +using System.Text; using System.Threading; using MsgPack.Internal; namespace MsgPack.Serialization.Internal { - public struct SerializationOperationContext + public struct SerializationOperationContext { - public Encoder Encoder { get; } + public Encoder Encoder { get; } public SerializationOptions Options { get; } private int _currentDepth; public int CurrentDepth => this._currentDepth; public CancellationToken CancellationToken { get; } - public System.Text.Encoding? StringEncoding => this.Options.StringEncoding; + public Encoding? StringEncoding => this.Options.StringEncoding; - public SerializationOperationContext(Encoder encoder, SerializationOptions? options, CancellationToken cancellationToken) + public SerializationOperationContext(Encoder encoder, SerializationOptions? options, CancellationToken cancellationToken) { this.Encoder = Ensure.NotNull(encoder); this.Options = options ?? SerializationOptions.Default; diff --git a/src/MsgPack.Abstraction/Serialization/Serializer`1.cs b/src/MsgPack.Abstraction/Serialization/Serializer`2.cs similarity index 84% rename from src/MsgPack.Abstraction/Serialization/Serializer`1.cs rename to src/MsgPack.Abstraction/Serialization/Serializer`2.cs index bb959aee2..0a5628fab 100644 --- a/src/MsgPack.Abstraction/Serialization/Serializer`1.cs +++ b/src/MsgPack.Abstraction/Serialization/Serializer`2.cs @@ -12,18 +12,18 @@ namespace MsgPack.Serialization { - public abstract class Serializer + public abstract class Serializer { - private readonly Func _encoderFactory; - private readonly Func _decoderFactory; - private readonly IObjectSerializer _underlying; + private readonly Func> _encoderFactory; + private readonly Func> _decoderFactory; + private readonly IObjectSerializer _underlying; private readonly SerializationOptions _serializationOptions; private readonly DeserializationOptions _deserializationOptions; protected Serializer( - Func encoderFactory, - Func decoderFactory, - IObjectSerializer underlying, + Func> encoderFactory, + Func> decoderFactory, + IObjectSerializer underlying, SerializationOptions serializationOptions, DeserializationOptions deserializationOptions ) @@ -35,11 +35,11 @@ DeserializationOptions deserializationOptions this._deserializationOptions = Ensure.NotNull(deserializationOptions); } - private void InitializeSerializationOperationContext(CancellationToken cancellationToken, out SerializationOperationContext context) - => context = new SerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); + private void InitializeSerializationOperationContext(CancellationToken cancellationToken, out SerializationOperationContext context) + => context = new SerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); - private void InitializeDeserializationOperationContext(CancellationToken cancellationToken, out DeserializationOperationContext context) - => context = new DeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); + private void InitializeDeserializationOperationContext(CancellationToken cancellationToken, out DeserializationOperationContext context) + => context = new DeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); public void Serialize(T obj, IBufferWriter sink, CancellationToken cancellationToken = default) { diff --git a/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs b/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs index 46a753c2a..b8183564b 100644 --- a/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs +++ b/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs @@ -69,20 +69,20 @@ protected sealed override int EncodeBinaryHeader(uint length, Span buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override void EncodeExtension(sbyte typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) + public sealed override void EncodeExtension(MessagePackExtensionType typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) { EncodeExtensionHeader(typeCode, unchecked((uint)serializedValue.Length), buffer); buffer.Write(serializedValue); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override void EncodeExtension(sbyte typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) + public sealed override void EncodeExtension(MessagePackExtensionType typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) { EncodeExtensionHeader(typeCode, unchecked((uint)serializedValue.Length), buffer); this.WriteRaw(serializedValue, buffer); } - private static void EncodeExtensionHeader(sbyte typeCode, uint serializedValueLength, IBufferWriter buffer) + private static void EncodeExtensionHeader(MessagePackExtensionType typeCode, uint serializedValueLength, IBufferWriter buffer) { switch (serializedValueLength) { @@ -154,7 +154,7 @@ private static void EncodeExtensionHeader(sbyte typeCode, uint serializedValueLe // type code { var span = buffer.GetSpan(1); - span[0] = unchecked((byte)typeCode); + span[0] = unchecked(typeCode.TypeCode); buffer.Advance(1); } } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs index 6523729bd..de25b0528 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs @@ -104,7 +104,7 @@ private uint ReadExtensionHeader(in SequenceReader source, out int consume return length; } - public override void DecodeExtension(in SequenceReader source, out byte typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) + public override void DecodeExtension(in SequenceReader source, out MessagePackExtensionType typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) { var bodyLength = this.ReadExtensionHeader(source, out var consumed, out requestHint); if (requestHint != 0) @@ -114,14 +114,16 @@ public override void DecodeExtension(in SequenceReader source, out byte ty return; } - if (!this.TryPeek(source, out typeCode)) + if (!this.TryPeek(source, out byte typeCodeByte)) { requestHint = 1; + typeCode = default; body = default; return; } consumed++; + typeCode = new MessagePackExtensionType(typeCodeByte); if (source.Remaining < consumed + bodyLength) { diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.cs index 177a22159..d244bce91 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.cs @@ -9,7 +9,7 @@ namespace MsgPack.Internal { - public sealed partial class MessagePackDecoder : Decoder + public sealed partial class MessagePackDecoder : Decoder { private static readonly FormatFeatures MessagePackFormatFeatures = new FormatFeaturesBuilder diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.cs index 9e9023ac9..98fc04b86 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoder.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.cs @@ -14,7 +14,7 @@ namespace MsgPack.Internal /// /// Common encoder implementation between legacy and current MessagePack format enconders. /// - public abstract partial class MessagePackEncoder : Encoder + public abstract partial class MessagePackEncoder : Encoder { #warning TODO: Can devirt? public static MessagePackEncoder CreateLegacy(MessagePackEncoderOptions options) => new LegacyMessagePackEncoder(options); diff --git a/src/MsgPack.Core/Internal/MessagePackExtensionType.cs b/src/MsgPack.Core/Internal/MessagePackExtensionType.cs new file mode 100644 index 000000000..0287bf17b --- /dev/null +++ b/src/MsgPack.Core/Internal/MessagePackExtensionType.cs @@ -0,0 +1,43 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack.Internal +{ + /// + /// Represents type code of extention types. + /// + public readonly struct MessagePackExtensionType : IEquatable + { + public byte TypeCode { get; } + public bool IsSystemType => this.TypeCode < 0; + + internal MessagePackExtensionType(byte typeCode) + { + this.TypeCode = typeCode; + } + + public static MessagePackExtensionType Define(byte typeCode) + => new MessagePackExtensionType(Ensure.IsNotGreaterThan(typeCode, (byte)SByte.MaxValue)); + + /// + public bool Equals(MessagePackExtensionType other) + => this.TypeCode == other.TypeCode; + + /// + public override bool Equals(object? obj) + => (obj is MessagePackExtensionType other) ? this.Equals(other) : false; + + /// + public override int GetHashCode() + => this.TypeCode.GetHashCode(); + + public static bool operator ==(MessagePackExtensionType left, MessagePackExtensionType right) + => left.Equals(right); + + public static bool operator !=(MessagePackExtensionType left, MessagePackExtensionType right) + => !left.Equals(right); + } +} diff --git a/src/MsgPack.Core/_SampleObject.cs b/src/MsgPack.Core/_SampleObject.cs index 9c6e63c35..9641a7143 100644 --- a/src/MsgPack.Core/_SampleObject.cs +++ b/src/MsgPack.Core/_SampleObject.cs @@ -15,7 +15,7 @@ namespace MsgPack.Internal internal sealed class SampleObject { public int Age { get; set; } - public string Name { get; set; } + public string Name { get; set; } = null!; public bool IsActive { get; set; } public IDictionary Attributes { get; } = new Dictionary(); public IList Roles { get; } = new List(); @@ -41,11 +41,11 @@ internal sealed class SampleObject /// /// Sample hand made serializer. /// - internal sealed class SampleSerializer : IObjectSerializer + internal sealed class SampleSerializer : IObjectSerializer { private bool UseArray { get; set; } - public void Serialize(in SerializationOperationContext context, SampleObject obj, IBufferWriter writer) + public void Serialize(in SerializationOperationContext context, SampleObject obj, IBufferWriter writer) { var encoder = context.Encoder; if (obj == null) @@ -152,7 +152,7 @@ public void Serialize(in SerializationOperationContext context, SampleObject obj } } - public async ValueTask SerializeAsync(SerializationOperationContext context, SampleObject obj, Stream streamSink) + public async ValueTask SerializeAsync(SerializationOperationContext context, SampleObject obj, Stream streamSink) { await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) { @@ -177,7 +177,7 @@ private static MsgPackStringTrie InitializeDeserializationTrie() return trie; } - public SampleObject Deserialize(in DeserializationOperationContext context, in SequenceReader reader) + public SampleObject Deserialize(in DeserializationOperationContext context, in SequenceReader reader) { // T が参照型でデフォルトコンストラクターがある -> DeserializeTo var obj = new SampleObject(); @@ -199,7 +199,7 @@ public SampleObject Deserialize(in DeserializationOperationContext context, in S // return obj; } - public async ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource) + public async ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource) { // T が参照型でデフォルトコンストラクターがある -> DeserializeToAsync var obj = new SampleObject(); @@ -221,7 +221,7 @@ public async ValueTask DeserializeAsync(DeserializationOperationCo // return obj; } - public void DeserializeTo(in DeserializationOperationContext context, in SequenceReader reader, in SampleObject obj) + public void DeserializeTo(in DeserializationOperationContext context, in SequenceReader reader, in SampleObject obj) { string name = default!; int age = default; @@ -477,15 +477,24 @@ private static bool TryDecodeArrayOrMapHeader(in DeserializationOperationContext arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(reader, out itemsCount, out requestHint); if (arrayOrMap.IsNone) { + propertyIterator = default; return false; } if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE { + arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(reader, out itemsCount); if (itemsCount < 5) { throw new MessageTypeException(); // Use Throws } + + propertyIterator = default; + } + else + { + arrayOrMap = context.Decoder.DecodeArrayOrMap(reader, out propertyIterator); + itemsCount = -1; } memory = memory.Slice(unchecked((int)reader.Consumed)); @@ -493,7 +502,7 @@ private static bool TryDecodeArrayOrMapHeader(in DeserializationOperationContext return true; } - private static bool TryDecodeArrayHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out int arrayLength, out int requestHint) + private static bool TryDecodeArrayHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out long arrayLength, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -505,7 +514,7 @@ private static bool TryDecodeArrayHeader(in DeserializationOperationContext cont return requestHint == 0; } - private static bool TryDecodeMapHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out int mapCount, out int requestHint) + private static bool TryDecodeMapHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out long mapCount, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -517,7 +526,7 @@ private static bool TryDecodeMapHeader(in DeserializationOperationContext contex return requestHint == 0; } - private static bool TryDecodeValueOfName(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string name, out int requestHint) + private static bool TryDecodeValueOfName(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string name, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -529,7 +538,7 @@ private static bool TryDecodeValueOfName(in DeserializationOperationContext cont return requestHint == 0; } - private static bool TryDecodeValueOfAge(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out int age, out int requestHint) + private static bool TryDecodeValueOfAge(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out int age, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -541,7 +550,7 @@ private static bool TryDecodeValueOfAge(in DeserializationOperationContext conte return requestHint == 0; } - private static bool TryDecodeValueOfIsActive(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out bool isActive, out int requestHint) + private static bool TryDecodeValueOfIsActive(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out bool isActive, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -553,7 +562,7 @@ private static bool TryDecodeValueOfIsActive(in DeserializationOperationContext return requestHint == 0; } - private static bool TryDecodeItemOfRoles(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string item, out int requestHint) + private static bool TryDecodeItemOfRoles(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string item, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -565,7 +574,7 @@ private static bool TryDecodeItemOfRoles(in DeserializationOperationContext cont return requestHint == 0; } - private static bool TryDecodeKeyOfAttributes(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string key, out int requestHint) + private static bool TryDecodeKeyOfAttributes(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string key, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -577,11 +586,11 @@ private static bool TryDecodeKeyOfAttributes(in DeserializationOperationContext return requestHint == 0; } - private static bool TryDecodeValueOfAttributes(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string value, out int requestHint) + private static bool TryDecodeValueOfAttributes(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string value, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); - value = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + value = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding if (requestHint == 0) { memory = memory.Slice(unchecked((int)reader.Consumed)); @@ -589,7 +598,7 @@ private static bool TryDecodeValueOfAttributes(in DeserializationOperationContex return requestHint == 0; } - private static bool TryDecodeArray(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) + private static bool TryDecodeArray(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -601,7 +610,7 @@ private static bool TryDecodeArray(in DeserializationOperationContext context, r return requestHint == 0; } - private static bool TryDecodeMap(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) + private static bool TryDecodeMap(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -613,7 +622,7 @@ private static bool TryDecodeMap(in DeserializationOperationContext context, ref return requestHint == 0; } - private static bool TryGetRawString(in DeserializationOperationContext context, ref ReadOnlyMemory memory, [NotNullWhen(true)]out byte[] key, out int requestHint) + private static bool TryGetRawString(in DeserializationOperationContext context, ref ReadOnlyMemory memory, [NotNullWhen(true)]out byte[] key, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); @@ -629,7 +638,7 @@ private static bool TryGetRawString(in DeserializationOperationContext context, return true; } - private static bool TryDrain(in DeserializationOperationContext context, ref ReadOnlyMemory memory, int remaining, out int requestHint) + private static bool TryDrain(in DeserializationOperationContext context, ref ReadOnlyMemory memory, long remaining, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(memory)); context.Decoder.Drain(reader, context.CollectionContext, remaining, out requestHint); From 5024dcd5370f7d019b1ef52642ecfc31945dede3 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 14 Jun 2020 22:26:21 +0900 Subject: [PATCH 16/82] Refine DecodeItem API --- .../Internal/DecodeItemResult`1.cs | 74 +++++++++++++++++++ src/MsgPack.Abstraction/Internal/Decoder`1.cs | 2 +- .../Internal/MessagePackDecoder.DecodeItem.cs | 68 +++++++++++------ 3 files changed, 119 insertions(+), 25 deletions(-) create mode 100644 src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs diff --git a/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs b/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs new file mode 100644 index 000000000..96e2d8db7 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs @@ -0,0 +1,74 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace MsgPack.Internal +{ + /// + /// Represetns a result of . + /// + public readonly struct DecodeItemResult + { + public bool HasValue => this.ElementType != ElementType.None; + public ElementType ElementType { get; } + public ReadOnlySequence Value { get; } + public CollectionItemIterator CollectionIterator { get; } + public long CollectionLength { get; } + public TExtensionType ExtensionType { get; } + public ReadOnlySequence ExtensionBody => this.Value; + public long RequestHint { get; } + + private DecodeItemResult( + ElementType elementType, + in ReadOnlySequence value = default, + in CollectionItemIterator collectionIterator = default, + long collectionLength = default, + TExtensionType extensionType = default, + long requestHint = default + ) + { + this.ElementType = elementType; + this.Value = value; + this.CollectionIterator = collectionIterator; + this.CollectionLength = collectionLength; + this.ExtensionType = extensionType; + this.RequestHint = requestHint; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static DecodeItemResult CollectionHeader(ElementType elementType, in CollectionItemIterator iterator, long length = -1) + => new DecodeItemResult(elementType, collectionIterator: iterator, collectionLength: length); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static DecodeItemResult ScalarOrSequence(ElementType elementType, ReadOnlyMemory value) + => ScalarOrSequence(elementType, new ReadOnlySequence(value)); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static DecodeItemResult ScalarOrSequence(ElementType elementType, in ReadOnlySequence value) + => new DecodeItemResult(elementType, value: value); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static DecodeItemResult Null() + => new DecodeItemResult(ElementType.Null); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static DecodeItemResult True() + => new DecodeItemResult(ElementType.True); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static DecodeItemResult False() + => new DecodeItemResult(ElementType.False); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static DecodeItemResult ExtensionTypeObject(TExtensionType extensionType, in ReadOnlySequence body) + => new DecodeItemResult(ElementType.Extension, extensionType : extensionType, value : body); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static DecodeItemResult InsufficientInput(long requestHint) + => new DecodeItemResult(ElementType.None, requestHint: requestHint); + } +} diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.cs b/src/MsgPack.Abstraction/Internal/Decoder`1.cs index 9e9b30293..74d52cfc0 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.cs +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.cs @@ -44,7 +44,7 @@ public void Skip(in SequenceReader source, in CollectionContext collection [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public bool TryPeek(in SequenceReader source, out byte value) => source.TryRead(out value); - public abstract ElementType DecodeItem(in SequenceReader source, out ReadOnlySequence valueOrLength, out int requestHint, CancellationToken cancellationToken = default); + public abstract bool DecodeItem(in SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void GetRawString(in SequenceReader source, out ReadOnlySpan rawString, CancellationToken cancellationToken = default) diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs index d3e8190ca..b5950552a 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs @@ -12,14 +12,13 @@ namespace MsgPack.Internal { public partial class MessagePackDecoder { - public sealed override ElementType DecodeItem(in SequenceReader source, out ReadOnlySequence valueOrLength, out int requestHint, CancellationToken cancellationToken = default) + public sealed override bool DecodeItem(in SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default) { - var elementType = this.ReadHeader(source, out var consumed, out var valueOrLengthScalar, out var requestHint32); - if (requestHint32 != 0) + var elementType = this.ReadHeader(source, out var consumed, out var valueOrLength, out var requestHint); + if (requestHint != 0) { - requestHint = requestHint32; - valueOrLength = default; - return ElementType.Null; + result = DecodeItemResult.InsufficientInput(requestHint); + return false; } requestHint = 0; @@ -27,10 +26,18 @@ public sealed override ElementType DecodeItem(in SequenceReader source, ou switch (elementType) { case ElementType.True: + { + result = DecodeItemResult.True(); + break; + } case ElementType.False: + { + result = DecodeItemResult.False(); + break; + } case ElementType.Null: { - valueOrLength = default; + result = DecodeItemResult.Null(); break; } case ElementType.Int32: @@ -38,55 +45,68 @@ public sealed override ElementType DecodeItem(in SequenceReader source, ou { var buffer = new byte[sizeof(int)]; var span = MemoryMarshal.Cast(buffer); - span[0] = (int)valueOrLengthScalar; - valueOrLength = new ReadOnlySequence(buffer); + span[0] = (int)valueOrLength; + result = DecodeItemResult.ScalarOrSequence(elementType, buffer); break; } case ElementType.Array: case ElementType.Map: + { + this.DecodeArrayOrMap(source, out var iterator); + result = DecodeItemResult.CollectionHeader(elementType, this.CreateIterator((uint)valueOrLength), valueOrLength); + break; + } case ElementType.Int64: case ElementType.UInt64: case ElementType.Double: { var buffer = new byte[sizeof(long)]; var span = MemoryMarshal.Cast(buffer); - span[0] = valueOrLengthScalar; - valueOrLength = new ReadOnlySequence(buffer); + span[0] = valueOrLength; + result = DecodeItemResult.ScalarOrSequence(elementType, buffer); break; } case ElementType.String: case ElementType.Binary: { - if (source.Remaining < valueOrLengthScalar + consumed) + if (source.Remaining < valueOrLength + consumed) { - requestHint = (int)((valueOrLengthScalar + consumed - source.Remaining) & Int32.MaxValue); - valueOrLength = default; - return default; + result = + DecodeItemResult < MessagePackExtensionType >.InsufficientInput( + (int)((valueOrLength + consumed - source.Remaining) & Int32.MaxValue) + ); + return false; } - valueOrLength = source.Sequence.Slice(source.Consumed + consumed, valueOrLengthScalar); - consumed += valueOrLengthScalar; + result = DecodeItemResult.ScalarOrSequence(elementType, source.Sequence.Slice(source.Consumed + consumed, valueOrLength)); + consumed += valueOrLength; break; } default: { Debug.Assert(elementType == ElementType.Extension, $"elementType({elementType}, 0x{elementType:X8}) == ElementType.Extension"); - if(source.Remaining < valueOrLengthScalar + consumed) + if(source.Remaining < valueOrLength + consumed) { - requestHint = (int)((valueOrLengthScalar + consumed - source.Remaining) & Int32.MaxValue); - valueOrLength = default; - return default; + result = + DecodeItemResult.InsufficientInput( + (int)((valueOrLength + consumed - source.Remaining) & Int32.MaxValue) + ); + return false; } - valueOrLength = source.Sequence.Slice(source.Consumed + consumed, valueOrLengthScalar + 1 /* typeCode */); - consumed += valueOrLengthScalar; + var extensionSlice = source.Sequence.Slice(source.Consumed + consumed - 1); + var typeCode = new MessagePackExtensionType(extensionSlice.FirstSpan[0]); + var body = extensionSlice.Slice(1, valueOrLength); + + result = DecodeItemResult.ExtensionTypeObject(typeCode, body); + consumed += valueOrLength; break; } } source.Advance(consumed); - return elementType; + return true; } private ElementType ReadHeader(in SequenceReader source, out long consumed, out long valueOrLength, out int requestHint) From 1888701407d85f0fd97b3395027f13ba612e8030 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 14 Jun 2020 22:48:02 +0900 Subject: [PATCH 17/82] Add document --- .../Internal/FormatFeatures.cs | 51 ++++++++++++++++++- .../Internal/FormatFeaturesBuilder.cs | 3 +- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/MsgPack.Abstraction/Internal/FormatFeatures.cs b/src/MsgPack.Abstraction/Internal/FormatFeatures.cs index f20f4a578..57a1c8645 100644 --- a/src/MsgPack.Abstraction/Internal/FormatFeatures.cs +++ b/src/MsgPack.Abstraction/Internal/FormatFeatures.cs @@ -1,4 +1,4 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. +// Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. @@ -6,15 +6,64 @@ namespace MsgPack.Internal { public sealed class FormatFeatures { +#warning TODO: REMOVE public bool IsContextful { get; } + + /// + /// Gets a value which indicates the underlying format supports collection length. + /// + /// + /// true, if the underlyng format supports collection length; false, otherwise. + /// + /// + /// When this property returns false, following methods should throw . + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// public bool CanCountCollectionItems { get; } + + /// + /// Gets a value which indicates the underlying format supports custom string encoding. + /// + /// + /// true, if the underlyng format supports custom string encoding; false, otherwise. + /// + /// + /// When this property returns false, typed parameters in and methods will be ignored. + /// public bool CanSpecifyStringEncoding { get; } + /// + /// Gets a value which indicates the underlying format supports extension types. + /// + /// + /// true, if the underlyng format supports extension types; false, otherwise. + /// + /// + /// When this property returns false, following methods should throw . + /// + /// + /// + /// + /// + /// In additon, TExtentionType type parameter should be . + /// + public bool SupportsExtensionTypes { get; } + internal FormatFeatures(FormatFeaturesBuilder builder) { this.IsContextful = builder.IsContextful; this.CanCountCollectionItems = builder.CanCountCollectionItems; this.CanSpecifyStringEncoding = builder.CanSpecifyStringEncoding; + this.SupportsExtensionTypes = builder.SupportsExtensionTypes; } } } diff --git a/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs b/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs index 0a5d3ab2b..66aa16c24 100644 --- a/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs +++ b/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs @@ -1,4 +1,4 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. +// Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. @@ -9,6 +9,7 @@ public sealed class FormatFeaturesBuilder public bool IsContextful { get; set; } public bool CanCountCollectionItems { get; set; } public bool CanSpecifyStringEncoding { get; set; } + public bool SupportsExtensionTypes { get; set; } public FormatFeatures Build() => new FormatFeatures(this); } From e4dc5f37e8de270b9822d17eeaacf3ada0ded95b Mon Sep 17 00:00:00 2001 From: yfakariya Date: Thu, 11 Jun 2020 23:01:56 +0900 Subject: [PATCH 18/82] WIP: Implement JsonEncoder/JsonDecoder --- .../Json/FlexibleJsonDecoder.ReadTrivia.cs | 103 +++ src/MsgPack.Json/Json/FlexibleJsonDecoder.cs | 33 + src/MsgPack.Json/Json/InfinityHandling.cs | 32 + src/MsgPack.Json/Json/JsonCharactor.cs | 22 + .../Json/JsonDecoder.CollectionHeaders.cs | 22 + .../Json/JsonDecoder.DecodeItem.cs | 128 +++ .../Json/JsonDecoder.Iteration.cs | 362 ++++++++ .../Json/JsonDecoder.Nullables.cs | 173 ++++ .../Json/JsonDecoder.Nullables.tt | 57 ++ .../Json/JsonDecoder.NumberCore.cs | 254 ++++++ src/MsgPack.Json/Json/JsonDecoder.Numbers.cs | 139 ++++ src/MsgPack.Json/Json/JsonDecoder.Numbers.tt | 54 ++ .../Json/JsonDecoder.SkipDrain.cs | 46 + src/MsgPack.Json/Json/JsonDecoder.Strings.cs | 465 +++++++++++ src/MsgPack.Json/Json/JsonDecoder.cs | 166 ++++ src/MsgPack.Json/Json/JsonDecoderOptions.cs | 22 + .../Json/JsonDecoderOptionsBuilder.cs | 20 + src/MsgPack.Json/Json/JsonEncoder.cs | 787 ++++++++---------- src/MsgPack.Json/Json/JsonEncoderOptions.cs | 116 +++ .../Json/JsonEncoderOptionsBuilder.cs | 157 ++++ src/MsgPack.Json/Json/JsonEscapeSequence.cs | 26 + src/MsgPack.Json/Json/JsonFormatter.cs | 36 + src/MsgPack.Json/Json/JsonNumberTokens.cs | 95 +++ .../Json/JsonOptionsValidation.cs | 61 ++ src/MsgPack.Json/Json/JsonParseOptions.cs | 95 +++ src/MsgPack.Json/Json/JsonStringTokens.cs | 43 + src/MsgPack.Json/Json/JsonThrow.cs | 117 +++ src/MsgPack.Json/Json/JsonTokens.cs | 29 + src/MsgPack.Json/Json/JsonTriviaTokens.cs | 48 ++ src/MsgPack.Json/Json/NaNHandling.cs | 32 + src/MsgPack.Json/Json/SimpleJsonDecoder.cs | 27 + .../Json/StringBuilderBufferWriter.cs | 141 ++++ src/MsgPack.Json/Json/Unicode.cs | 18 + src/MsgPack.Json/MsgPack.Json.csproj | 28 + 34 files changed, 3526 insertions(+), 428 deletions(-) create mode 100644 src/MsgPack.Json/Json/FlexibleJsonDecoder.ReadTrivia.cs create mode 100644 src/MsgPack.Json/Json/FlexibleJsonDecoder.cs create mode 100644 src/MsgPack.Json/Json/InfinityHandling.cs create mode 100644 src/MsgPack.Json/Json/JsonCharactor.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.Iteration.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.Nullables.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.Nullables.tt create mode 100644 src/MsgPack.Json/Json/JsonDecoder.NumberCore.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.Numbers.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.Numbers.tt create mode 100644 src/MsgPack.Json/Json/JsonDecoder.SkipDrain.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.Strings.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoder.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoderOptions.cs create mode 100644 src/MsgPack.Json/Json/JsonDecoderOptionsBuilder.cs create mode 100644 src/MsgPack.Json/Json/JsonEncoderOptions.cs create mode 100644 src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs create mode 100644 src/MsgPack.Json/Json/JsonEscapeSequence.cs create mode 100644 src/MsgPack.Json/Json/JsonFormatter.cs create mode 100644 src/MsgPack.Json/Json/JsonNumberTokens.cs create mode 100644 src/MsgPack.Json/Json/JsonOptionsValidation.cs create mode 100644 src/MsgPack.Json/Json/JsonParseOptions.cs create mode 100644 src/MsgPack.Json/Json/JsonStringTokens.cs create mode 100644 src/MsgPack.Json/Json/JsonThrow.cs create mode 100644 src/MsgPack.Json/Json/JsonTokens.cs create mode 100644 src/MsgPack.Json/Json/JsonTriviaTokens.cs create mode 100644 src/MsgPack.Json/Json/NaNHandling.cs create mode 100644 src/MsgPack.Json/Json/SimpleJsonDecoder.cs create mode 100644 src/MsgPack.Json/Json/StringBuilderBufferWriter.cs create mode 100644 src/MsgPack.Json/Json/Unicode.cs diff --git a/src/MsgPack.Json/Json/FlexibleJsonDecoder.ReadTrivia.cs b/src/MsgPack.Json/Json/FlexibleJsonDecoder.ReadTrivia.cs new file mode 100644 index 000000000..8d09c4f33 --- /dev/null +++ b/src/MsgPack.Json/Json/FlexibleJsonDecoder.ReadTrivia.cs @@ -0,0 +1,103 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Json +{ + internal partial class FlexibleJsonDecoder + { + protected override void ReadTrivia(in SequenceReader source, out ReadOnlySequence trivia) + { + var offset = source.Consumed; + var singleByteWhitespaces = (this.Options.ParseOptions & JsonParseOptions.AllowUnicodeWhitespace) == 0 ? JsonTriviaTokens.StandardWhitespaces : JsonTriviaTokens.SingleByteUnicodeWhitespaces; + + var consumed = 0L; + while (!source.End) + { + consumed += source.AdvancePastAny(singleByteWhitespaces); + + if (this.TryReadMultiByteUnicodeWhitespaces(source, ref consumed)) + { + continue; + } + + // Single line comment + if (this.TryReadSingleLineComment(source, ref consumed)) + { + continue; + } + + // Multi line comment + if (this.TryReadMultiLineComment(source, ref consumed)) + { + continue; + } + } + + trivia = source.Sequence.Slice(offset, consumed); + } + + private bool TryReadMultiByteUnicodeWhitespaces(in SequenceReader source, ref long consumed) + { + if ((this.Options.ParseOptions & JsonParseOptions.AllowUnicodeWhitespace) == 0) + { + return false; + } + + foreach (var whitespace in JsonTriviaTokens.MultiByteUnicodeWhitespaces) + { + var length = source.AdvancePastAny(whitespace); + if (length > 0) + { + consumed += length; + return true; + } + } + + return false; + } + + private bool TryReadSingleLineComment(in SequenceReader source, ref long consumed) + { + if (((this.Options.ParseOptions & JsonParseOptions.AllowHashSingleLineComment) != 0 && source.IsNext((byte)'#', advancePast: true)) + || ((this.Options.ParseOptions & JsonParseOptions.AllowDoubleSolidousSingleLineComment) != 0 && source.IsNext(JsonTriviaTokens.SingleLineCommentStart2, advancePast: true))) + { + if (!source.TryReadToAny(out ReadOnlySequence line, JsonTriviaTokens.NewLine, advancePastDelimiter: true)) + { + // to EoF + consumed += source.Remaining; + source.Advance(source.Remaining); + return true; + } + + consumed += line.Length; + consumed += source.AdvancePastAny(JsonTriviaTokens.NewLine); + return true; + } + + return false; + } + + private bool TryReadMultiLineComment(in SequenceReader source, ref long consumed) + { + var offset = source.Consumed; + + if ((this.Options.ParseOptions & JsonParseOptions.AllowMultilineComment) == 0 || !source.IsNext(JsonTriviaTokens.MultiLineCommentStart, advancePast: true)) + { + return false; + } + + if (!source.TryReadToAny(out ReadOnlySequence comment, JsonTriviaTokens.MultiLineCommentEnd, advancePastDelimiter: true)) + { + // */ is not found. + source.Rewind(2); + return false; + } + + consumed += comment.Length; + return true; + } + } +} diff --git a/src/MsgPack.Json/Json/FlexibleJsonDecoder.cs b/src/MsgPack.Json/Json/FlexibleJsonDecoder.cs new file mode 100644 index 000000000..23bc34bcc --- /dev/null +++ b/src/MsgPack.Json/Json/FlexibleJsonDecoder.cs @@ -0,0 +1,33 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Json +{ + /// + /// Simple implementation which can handle various resource consuming relaxations. + /// + internal sealed partial class FlexibleJsonDecoder : JsonDecoder + { + public FlexibleJsonDecoder(JsonDecoderOptions options) + : base(options) { } + + protected override bool TryReadNull(in SequenceReader source) + { + if (source.IsNext(JsonTokens.Null, advancePast: true)) + { + return true; + } + + if ((this.Options.ParseOptions & JsonParseOptions.AllowUndefined) != 0 + && source.IsNext(JsonTokens.Undefined, advancePast: true)) + { + return true; + } + + return false; + } + } +} diff --git a/src/MsgPack.Json/Json/InfinityHandling.cs b/src/MsgPack.Json/Json/InfinityHandling.cs new file mode 100644 index 000000000..abe9026f4 --- /dev/null +++ b/src/MsgPack.Json/Json/InfinityHandling.cs @@ -0,0 +1,32 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Json +{ + /// + /// Defines positive and/or negative infinity handling in JSON serialization to be compliant with RFC 8259. + /// + public enum InfinityHandling + { + /// + /// Use system default setting. See for current default. + /// + Default = 0, + + /// + /// Use MinValue for negative infinity and MaxValue for positive infinity. + /// + MinMax = 1, + + /// + /// Throws exception conservatively. + /// + Error = 2, + + /// + /// Use own custom formatting logic. + /// + Custom = 3 + } +} diff --git a/src/MsgPack.Json/Json/JsonCharactor.cs b/src/MsgPack.Json/Json/JsonCharactor.cs new file mode 100644 index 000000000..3c06d41f1 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonCharactor.cs @@ -0,0 +1,22 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Linq; +using System.Text; + +namespace MsgPack.Json +{ + /// + /// Defines JSON constant chars in UTF-8 or related structs. + /// + internal static class JsonCharactor + { + public static readonly byte[] MustBeEscaped1Byte = new[] { (byte)'\\', (byte)'"' }; + public static readonly ReadOnlyMemory ShouldBeEscaped = new[] { (byte)'/', (byte)'\'', (byte)'<', (byte)'>', (byte)'&' }.Select(b => new Rune(b)).ToArray(); // For HTML embedding + public static ReadOnlySpan CarriageReturn => new[] { (byte)'\r' }; + public static ReadOnlySpan LineFeed => new[] { (byte)'\n' }; + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs b/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs new file mode 100644 index 000000000..cd72d8a69 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs @@ -0,0 +1,22 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + public partial class JsonDecoder + { + public override CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out long itemsCount, out int requestHint) + => JsonThrow.CollectionHeaderDecodingIsNotSupported(out itemsCount, out requestHint); + + public override long DecodeArrayHeader(in SequenceReader source, out int requestHint) + => JsonThrow.CollectionHeaderDecodingIsNotSupported(out requestHint); + + public override long DecodeMapHeader(in SequenceReader source, out int requestHint) + => JsonThrow.CollectionHeaderDecodingIsNotSupported(out requestHint); + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs b/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs new file mode 100644 index 000000000..aca36b59e --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs @@ -0,0 +1,128 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using MsgPack.Internal; + +using DecodeItemResult = MsgPack.Internal.DecodeItemResult; + +namespace MsgPack.Json +{ + public partial class JsonDecoder + { + public override bool DecodeItem(in SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default) + { + this.ReadTrivia(source, out var trivia); + if (!trivia.IsEmpty) + { + result = DecodeItemResult.ScalarOrSequence(ElementType.OtherTrivia, trivia); + return true; + } + + if (!this.TryPeek(source, out var token)) + { + result = DecodeItemResult.InsufficientInput(1); + return false; + } + + switch (token) + { + case (byte)'t': + { + if (source.IsNext(JsonTokens.True, advancePast: true)) + { + result = DecodeItemResult.True(); + return true; + } + + break; + } + case (byte)'f': + { + if (source.IsNext(JsonTokens.False, advancePast: true)) + { + result = DecodeItemResult.False(); + return true; + } + + break; + } + case (byte)'n': + { + if (source.IsNext(JsonTokens.Null, advancePast: true)) + { + result = DecodeItemResult.Null(); + return true; + } + + break; + } + case (byte)'0': + case (byte)'1': + case (byte)'2': + case (byte)'3': + case (byte)'4': + case (byte)'5': + case (byte)'6': + case (byte)'7': + case (byte)'8': + case (byte)'9': + case (byte)'-': + case (byte)'+': + { + var number = this.DecodeNumber(source, out var requestHint); + if(requestHint != 0) + { + result = DecodeItemResult.InsufficientInput(requestHint); + return false; + } + + var value = new byte[sizeof(double)]; + Unsafe.WriteUnaligned(ref MemoryMarshal.GetReference(value.AsSpan()), number); + result = DecodeItemResult.ScalarOrSequence(ElementType.Double, value); + return true; + } + case (byte)'[': + { + result = DecodeItemResult.CollectionHeader(ElementType.Array, this.CreateArrayIterator()); + return true; + } + case (byte)'{': + { + result = DecodeItemResult.CollectionHeader(ElementType.OtherTrivia, this.CreateObjectPropertyIterator()); + return true; + } + case (byte)'\'': + { + if ((this.Options.ParseOptions & JsonParseOptions.AllowSingleQuotationString) != 0) + { + goto case (byte)'"'; + } + + break; + } + case (byte)'"': + { + if (this.GetRawStringCore(source, out var rawString, out var requestHint)) + { + result = DecodeItemResult.InsufficientInput(requestHint); + return false; + } + + result = DecodeItemResult.ScalarOrSequence(ElementType.String, rawString); + return true; + } + } + + JsonThrow.UnexpectedToken(source.Consumed, token); + // never + result = default; + return default; + } + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.Iteration.cs b/src/MsgPack.Json/Json/JsonDecoder.Iteration.cs new file mode 100644 index 000000000..bd8458d2b --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.Iteration.cs @@ -0,0 +1,362 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Diagnostics; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + public partial class JsonDecoder + { + private sealed class ArrayIterator + { + private const byte ArrayStartToken = (byte)'['; + private const byte ArrayEndToken = (byte)']'; + private const byte ItemDelimiterToken = (byte)','; + + private readonly JsonDecoder _decoder; + private State _state; + + public ArrayIterator(JsonDecoder decoder) + { + this._decoder = decoder; + this._state = State.Head; + } + + public bool DetectArrayEnds(in SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) + { + if (this._state == State.Tail) + { + requestHint = 0; + return true; // End + } + + this._decoder.ReadTrivia(source, out _); + + switch (this._state) + { + case State.Head: + { + if (!source.TryPeek(out var shouldBeArrayStart)) + { + requestHint = 2; + return false; + } + + if (shouldBeArrayStart != ArrayStartToken) + { + // Invalid + JsonThrow.UnexpectedToken(source.Consumed, shouldBeArrayStart); + } + + source.Advance(1); + break; + } // case State.Head + default: + { + Debug.Assert(this._state == State.Item, $"this._state {(this._state)} == State.Item"); + + if (!source.TryPeek(out var mayBeDelimiterOrArrayEnd)) + { + requestHint = 1; + return false; + } + + switch (mayBeDelimiterOrArrayEnd) + { + case ArrayEndToken: + { + requestHint = 0; + source.Advance(1); + this._state = State.Tail; + return true; + } // case ArrayEndToken: + case ItemDelimiterToken: + { + source.Advance(1); + // goto maybe-item + break; + } // ItemDelimiterToken + default: + { + JsonThrow.UnexpectedToken(source.Consumed, mayBeDelimiterOrArrayEnd); + // never + break; + } + } // switch (mayBeDelimiterOrArrayEnd) + + break; + } // default: + } // swtich (this._state) + + // Handle 'maybe item' state + this._decoder.ReadTrivia(source, out _); + if (!source.TryPeek(out var mayBeArrayEnd)) + { + requestHint = 1; + return false; + } + + requestHint = 0; + + if (mayBeArrayEnd == ArrayEndToken) + { + source.Advance(1); + this._state = State.Tail; + return true; + } + else + { + this._state = State.Item; + return false; + } + } + + private enum State + { + Head = 0, + Item, + Tail + } + } + + private sealed class ObjectPropertyIterator + { + private const byte ObjectStartToken = (byte)'{'; + private const byte ObjectEndToken = (byte)'}'; + private const byte KeyValueSepratorToken = (byte)':'; + private const byte AltKeyValueSepratorToken = (byte)'='; + private const byte ItemDelimiterToken = (byte)','; + + private readonly JsonDecoder _decoder; + private readonly bool _isEqualSignAllowed; + private State _state; + + public ObjectPropertyIterator(JsonDecoder decoder) + { + this._decoder = decoder; + this._state = State.Head; + this._isEqualSignAllowed = (decoder.Options.ParseOptions & JsonParseOptions.AllowEqualSignSeparator) != 0; + } + + public bool DetectObjectEnds(in SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) + { + if (this._state == State.Tail) + { + requestHint = 0; + return true; // End + } + + this._decoder.ReadTrivia(source, out _); + + switch (this._state) + { + case State.Head: + { + if (!source.TryPeek(out var shouldBeObjectStart)) + { + requestHint = 2; + return false; + } + + if (shouldBeObjectStart != ObjectStartToken) + { + // Invalid + JsonThrow.UnexpectedToken(source.Consumed, shouldBeObjectStart); + } + + source.Advance(1); + break; + } // case State.Head + case State.Key: + { + if (!source.TryPeek(out var mayBeSeparator)) + { + requestHint = 3; + return false; + } + + switch (mayBeSeparator) + { + case AltKeyValueSepratorToken: + { + if (this._isEqualSignAllowed) + { + goto case KeyValueSepratorToken; + } + else + { + goto default; + } + } // case AltKeyValueSepratorToken + case KeyValueSepratorToken: + { + source.Advance(1); + this._state = State.Value; + requestHint = 0; + return false; + } // case KeyValueSepratorToken + default: + { + JsonThrow.UnexpectedToken(source.Consumed, mayBeSeparator); + // never + requestHint = default; + return false; + } + } // switch (mayBeSeparator) + + } // case State.Key + default: + { + Debug.Assert(this._state == State.Value, $"this._state {(this._state)} == State.Value"); + + if (!source.TryPeek(out var mayBeDelimiterOrObjectEnd)) + { + requestHint = 1; + return false; + } + + switch (mayBeDelimiterOrObjectEnd) + { + case ObjectEndToken: + { + source.Advance(1); + this._state = State.Tail; + requestHint = 0; + return true; + } // case ObjectEndToken + case ItemDelimiterToken: + { + source.Advance(1); + // goto maybe-key + break; + } // case ItemDelimiterToken + default: + { + JsonThrow.UnexpectedToken(source.Consumed, mayBeDelimiterOrObjectEnd); + // never + break; + } + } // switch (mayBeDelimiterOrArrayEnd) + + break; + } // default: + } // swtich (this._state) + + // Handle 'maybe key' state + this._decoder.ReadTrivia(source, out _); + if (!source.TryPeek(out var mayBeArrayEnd)) + { + requestHint = 1; + return false; + } + + requestHint = 0; + + if (mayBeArrayEnd == ObjectEndToken) + { + source.Advance(1); + this._state = State.Tail; + return true; + } + else + { + this._state = State.Key; + return false; + } + } + + private enum State + { + Head = 0, + Key, + Value, + Tail + } + } + + public override CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator, out int requestHint) + { + this.ReadTrivia(source, out _); + + if (!this.TryPeek(source, out var token)) + { + requestHint = 1; + iterator = default; + return CollectionType.None; + } + + requestHint = 0; + switch (token) + { + case (byte)'[': + { + iterator = this.CreateArrayIterator(); + return CollectionType.Array; + } + case (byte)'{': + { + iterator = this.CreateObjectPropertyIterator(); + return CollectionType.Map; + } + default: + { + var offset = source.Consumed; + var kind = TryGetUtf8Unit(source, out var sequence); + if (kind == Utf8UnitStatus.Valid) + { + JsonThrow.IsNotArrayNorObject(sequence, offset); + } + else + { + JsonThrow.MalformedUtf8(sequence, offset); + } + + // never + iterator = default; + return default; + } + } + } + + private CollectionItemIterator CreateArrayIterator() + => new CollectionItemIterator(new ArrayIterator(this).DetectArrayEnds, -1); + + private CollectionItemIterator CreateObjectPropertyIterator() + => new CollectionItemIterator(new ObjectPropertyIterator(this).DetectObjectEnds, -1); + + public override CollectionItemIterator DecodeArray(in SequenceReader source, out int requestHint) + { + var type = this.DecodeArrayOrMap(source, out var iterator, out requestHint); + if (requestHint != 0) + { + return default; + } + + if (!type.IsArray) + { + JsonThrow.IsNotArray(source.Consumed); + } + + return iterator; + } + + public override CollectionItemIterator DecodeMap(in SequenceReader source, out int requestHint) + { + var type = this.DecodeArrayOrMap(source, out var iterator, out requestHint); + if (requestHint != 0) + { + return default; + } + + if (!type.IsMap) + { + JsonThrow.IsNotObject(source.Consumed); + } + + return iterator; + } + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.Nullables.cs b/src/MsgPack.Json/Json/JsonDecoder.Nullables.cs new file mode 100644 index 000000000..bfe6936b5 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.Nullables.cs @@ -0,0 +1,173 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + partial class JsonDecoder + { + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Byte? DecodeNullableByte(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeByteCore(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override SByte? DecodeNullableSByte(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeSByteCore(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int16? DecodeNullableInt16(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeInt16Core(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt16? DecodeNullableUInt16(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeUInt16Core(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int32? DecodeNullableInt32(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeInt32Core(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt32? DecodeNullableUInt32(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeUInt32Core(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int64? DecodeNullableInt64(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeInt64Core(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt64? DecodeNullableUInt64(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeUInt64Core(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Single? DecodeNullableSingle(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeSingleCore(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Double? DecodeNullableDouble(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeDoubleCore(source, out requestHint); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Boolean? DecodeNullableBoolean(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeBooleanCore(source, out requestHint); + } + + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.Nullables.tt b/src/MsgPack.Json/Json/JsonDecoder.Nullables.tt new file mode 100644 index 000000000..c630c16c1 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.Nullables.tt @@ -0,0 +1,57 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + partial class JsonDecoder + { +<# +foreach (var type in + new [] + { + "Byte", + "SByte", + "Int16", + "UInt16", + "Int32", + "UInt32", + "Int64", + "UInt64", + "Single", + "Double", + "Boolean" + } +) +{ +#> + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override <#= type #>? DecodeNullable<#= type #>(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.Decode<#= type #>Core(source, out requestHint); + } + +<# +} // foreach (var type) +#> + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.NumberCore.cs b/src/MsgPack.Json/Json/JsonDecoder.NumberCore.cs new file mode 100644 index 000000000..02c905620 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.NumberCore.cs @@ -0,0 +1,254 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Diagnostics; + +namespace MsgPack.Json +{ + public partial class JsonDecoder + { + public override bool DecodeBoolean(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeBooleanCore(source, out requestHint); + } + + private bool DecodeBooleanCore(in SequenceReader source, out int requestHint) + { + requestHint = 0; + if (source.IsNext(JsonTokens.True, advancePast: true)) + { + return true; + } + else if (source.IsNext(JsonTokens.False, advancePast: true)) + { + return false; + } + else + { + requestHint = GetRequestHintForBoolean(source); + return default; + } + } + + private static int GetRequestHintForBoolean(in SequenceReader source) + { + if (!source.TryPeek(out var b)) + { + return 4; + } + + switch (b) + { + case (byte)'t': + { + return GetRequestHintForBoolean(source, offset: 1, JsonTokens.True); + } + case (byte)'f': + { + return GetRequestHintForBoolean(source, offset: 1, JsonTokens.False); + } + } + + ThrowNonBooleanUtfSequence(source, source.Consumed); + // never + return default; + } + + private static int GetRequestHintForBoolean(in SequenceReader source, int offset, ReadOnlySpan expected) + { + var subReeader = new SequenceReader(source.Sequence.Slice(source.Consumed + 1)); + + for (int i = 0; i < expected.Length; i++) + { + if (!subReeader.TryRead(out var b)) + { + return expected.Length - i; + } + + if (b != expected[i]) + { + ThrowNonBooleanUtfSequence(subReeader, subReeader.Consumed + source.Consumed); + } + } + + Debug.Fail("should be true/false..."); + return default; + } + + private static void ThrowNonBooleanUtfSequence(in SequenceReader source, long position) + { + if (TryGetUtf8Unit(source, out var unit) == Utf8UnitStatus.Invalid) + { + JsonThrow.MalformedUtf8(unit, position); + } + else + { + JsonThrow.IsNotType(typeof(bool), unit, position); + } + } + + private double DecodeNumber(in SequenceReader source, out int requestHint) + { + var start = source.Position; + var startOffset = source.Consumed; + var length = 0L; + // -? (0 | [1-9] \d*) (\. \d+ ([eE] [+-] \d+)? )? + // sign + if (source.IsNext(JsonNumberTokens.PlusMinusSigns, advancePast: true)) + { + length++; + } + + // 0 + if (source.IsNext((byte)'0', advancePast: true)) + { + length++; + } + // [1-9] + else if (source.IsNext(JsonNumberTokens.NonZeroDigits, advancePast: true)) + { + length++; + // \d* + length += source.AdvancePastAny(JsonNumberTokens.Digits); + } + else + { + ThrowInvalidNumber(source); + } + + // Check early for integer portion. + if (length > this.Options.MaxNumberLengthInBytes) + { + JsonThrow.TooLongNumber(length, this.Options.MaxNumberLengthInBytes, startOffset); + } + + // (\. ...)? + if (source.IsNext((byte)'.', advancePast: true)) + { + length++; + + if (source.End) + { + requestHint = 1; + source.Rewind(length); + return default; + } + + // \d+ + length += ReadAtLeastOneDigits(source, out requestHint); + if (requestHint != 0) + { + source.Rewind(length); + return default; + } + + // ([eE] ..)? + if (source.IsNext(JsonNumberTokens.ExponentialIndicators, advancePast: true)) + { + // [+-] + if (source.IsNext(JsonNumberTokens.PlusMinusSigns, advancePast: true)) + { + // \d+ + length += ReadAtLeastOneDigits(source, out requestHint); + if (requestHint != 0) + { + source.Rewind(length); + return default; + } + } + else + { + ThrowInvalidNumber(source); + } + } + } + + if (length > this.Options.MaxNumberLengthInBytes) + { + JsonThrow.TooLongNumber(length, this.Options.MaxNumberLengthInBytes, startOffset); + } + + var readDirect = source.UnreadSpan.Length <= length; + + var length32 = (int)length; + byte[]? arrayBuffer = default; + if (!readDirect && length > 32) + { + // length should be Int32 because MaxNumberLength is Int32 type. + arrayBuffer = this.Options.ByteBufferPool.Rent(length32); + } + + try + { + Span buffer = + readDirect ? + default : + arrayBuffer ?? (stackalloc byte[length32]); + var number = GetSpan(source.Sequence.Slice(start, length32), buffer); + + var shouldBeTrue = Utf8Parser.TryParse(number, out double result, out var shouldBeLength); + Debug.Assert(shouldBeTrue, "Utf8Parser.TryParse returns false!"); + Debug.Assert(shouldBeLength == length, $"Utf8Parser.TryParse outputs ({shouldBeLength}:#,0) is not {length:#,0}!"); + requestHint = 0; + source.Advance(length); + return result; + } + finally + { + if (arrayBuffer != null) + { + this.Options.ByteBufferPool.Return(arrayBuffer, this.Options.ClearsBuffer); + } + } + } + + private static ReadOnlySpan GetSpan(in ReadOnlySequence source, Span buffer) + { + if (source.IsSingleSegment) + { + Debug.Assert(source.FirstSpan.Length == source.Length, "source.FirstSpan.Length == source.Length"); + return source.FirstSpan; + } + + Debug.Assert(source.Length == buffer.Length, "source.Length == buffer.Length"); + source.CopyTo(buffer); + return buffer; + } + + private static long ReadAtLeastOneDigits(in SequenceReader source, out int requestHint) + { + if (source.End) + { + requestHint = 1; + return default; + } + + var decimalLength = source.AdvancePastAny(JsonNumberTokens.Digits); + if (decimalLength == 0) + { + ThrowInvalidNumber(source); + } + + requestHint = 0; + return decimalLength; + } + + private static void ThrowInvalidNumber(in SequenceReader source) + { + var position = source.Consumed; + if (TryGetUtf8Unit(source, out var unit) == Utf8UnitStatus.Valid) + { + JsonThrow.IsNotType(typeof(double), unit, position); + } + else + { + JsonThrow.MalformedUtf8(unit, position); + } + } + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.Numbers.cs b/src/MsgPack.Json/Json/JsonDecoder.Numbers.cs new file mode 100644 index 000000000..1eff27f97 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.Numbers.cs @@ -0,0 +1,139 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + partial class JsonDecoder + { + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Byte DecodeByte(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeByteCore(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private Byte DecodeByteCore(in SequenceReader source, out int requestHint) + => (Byte)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override SByte DecodeSByte(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeSByteCore(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private SByte DecodeSByteCore(in SequenceReader source, out int requestHint) + => (SByte)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int16 DecodeInt16(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeInt16Core(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private Int16 DecodeInt16Core(in SequenceReader source, out int requestHint) + => (Int16)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt16 DecodeUInt16(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeUInt16Core(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private UInt16 DecodeUInt16Core(in SequenceReader source, out int requestHint) + => (UInt16)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int32 DecodeInt32(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeInt32Core(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private Int32 DecodeInt32Core(in SequenceReader source, out int requestHint) + => (Int32)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt32 DecodeUInt32(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeUInt32Core(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private UInt32 DecodeUInt32Core(in SequenceReader source, out int requestHint) + => (UInt32)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Int64 DecodeInt64(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeInt64Core(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private Int64 DecodeInt64Core(in SequenceReader source, out int requestHint) + => (Int64)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override UInt64 DecodeUInt64(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeUInt64Core(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private UInt64 DecodeUInt64Core(in SequenceReader source, out int requestHint) + => (UInt64)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Single DecodeSingle(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeSingleCore(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private Single DecodeSingleCore(in SequenceReader source, out int requestHint) + => (Single)this.DecodeNumber(source, out requestHint); + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override Double DecodeDouble(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.DecodeDoubleCore(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private Double DecodeDoubleCore(in SequenceReader source, out int requestHint) + => (Double)this.DecodeNumber(source, out requestHint); + + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.Numbers.tt b/src/MsgPack.Json/Json/JsonDecoder.Numbers.tt new file mode 100644 index 000000000..70674fd15 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.Numbers.tt @@ -0,0 +1,54 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + partial class JsonDecoder + { +<# +foreach (var type in + new [] + { + "Byte", + "SByte", + "Int16", + "UInt16", + "Int32", + "UInt32", + "Int64", + "UInt64", + "Single", + "Double" + } +) +{ +#> + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override <#= type #> Decode<#= type #>(in SequenceReader source, out int requestHint) + { + this.ReadTrivia(source, out _); + return this.Decode<#= type #>Core(source, out requestHint); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private <#= type #> Decode<#= type #>Core(in SequenceReader source, out int requestHint) + => (<#= type #>)this.DecodeNumber(source, out requestHint); + +<# +} // foreach (var type) +#> + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.SkipDrain.cs b/src/MsgPack.Json/Json/JsonDecoder.SkipDrain.cs new file mode 100644 index 000000000..c7c6fb4a5 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.SkipDrain.cs @@ -0,0 +1,46 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + public partial class JsonDecoder + { + public override void Drain(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default) + => JsonThrow.DrainIsNotSupported(out requestHint); + + public override void Skip(in SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default) + { + var originalPosition = source.Consumed; + + if(!this.DecodeItem(source, out var decodeItemResult, cancellationToken)) + { + requestHint = (int)(decodeItemResult.RequestHint & Int32.MaxValue); + return; + } + + switch (decodeItemResult.ElementType) + { + case ElementType.Array: + case ElementType.Map: + { + // Skip current collection with CollectionIterator.Drain() + if(!decodeItemResult.CollectionIterator.Drain(source, out requestHint)) + { + source.Rewind(source.Consumed - originalPosition); + return; + } + + break; + } + } + + requestHint = 0; + } + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.Strings.cs b/src/MsgPack.Json/Json/JsonDecoder.Strings.cs new file mode 100644 index 000000000..616a6f0aa --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.Strings.cs @@ -0,0 +1,465 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + public partial class JsonDecoder + { + /// + public override bool GetRawString(in SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default) + { + if (!this.GetRawStringCore(source, out var rawStringSequence, out requestHint, cancellationToken)) + { + rawString = default; + return false; + } + + if (rawStringSequence.IsSingleSegment) + { + rawString = rawStringSequence.FirstSpan; + } + else + { + // length should be Int32 because max length option is Int32 type. + Span result = new byte[(int)rawStringSequence.Length]; + rawStringSequence.CopyTo(result); + rawString = result; + } + + return true; + } + + private bool GetRawStringCore(in SequenceReader source, out ReadOnlySequence rawString, out int requestHint, CancellationToken cancellationToken = default) + { + this.ReadTrivia(source, out _); + var startOffset = source.Consumed; + + var quotation = this.ReadStringStart(source, out var delimiters, out requestHint); + if (requestHint != 0) + { + rawString = default; + return false; + } + + var length = 0L; + var rawStringStart = source.Position; + // We accept unescaped charactors except quotation even if the value is '\0' because we can handle them correctly. + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!source.TryReadToAny(out ReadOnlySequence sequence, delimiters, advancePastDelimiter: false)) + { + // EoF + requestHint = 1; + break; + } + + if (sequence.Length + length > this.Options.MaxStringLengthInBytes) + { + Throw.StringLengthExceeded(source.Consumed - sequence.Length, sequence.Length + length, this.Options.MaxBinaryLengthInBytes); + } + + length += (int)sequence.Length; + + // Handle delimiter + source.TryRead(out var delimiter); + if (delimiter == quotation) + { + // End + requestHint = 0; + rawString = source.Sequence.Slice(rawStringStart, length); + return true; + } + + // Decode escape sequence + if (source.End) + { + // EoF + requestHint = 2; + break; + } + } + + // No closing quotation. + rawString = default; + source.Rewind(source.Consumed - startOffset); + return false; + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override string? DecodeNullableString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeStringCore(source, out requestHint, cancellationToken); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override string? DecodeString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + this.ReadTrivia(source, out _); + return this.DecodeStringCore(source, out requestHint, cancellationToken); + } + + private string? DecodeStringCore(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + { + var startOffset = source.Consumed; + + var quotation = this.ReadStringStart(source, out var delimiters, out requestHint); + if (requestHint != 0) + { + return default; + } + + var length = 0L; + using (var result = new StringBuilderBufferWriter(new StringBuilder(), this.Options)) + { + // We accept unescaped charactors except quotation even if the value is '\0' because we can handle them correctly. + while (true) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (!source.TryReadToAny(out ReadOnlySequence sequence, delimiters, advancePastDelimiter: false)) + { + // EoF + requestHint = 1; + source.Rewind(source.Consumed - startOffset); + break; + } + + if (sequence.Length + length > this.Options.MaxStringLengthInBytes) + { + Throw.StringLengthExceeded(source.Consumed - sequence.Length, sequence.Length + length, this.Options.MaxBinaryLengthInBytes); + } + + // Copy & UTF8 decoding existing. + Encoding.UTF8.GetChars(sequence, result); + length += (int)sequence.Length; + + // Handle delimiter + source.TryRead(out var delimiter); + if (delimiter == quotation) + { + // End + requestHint = 0; + return result.ToString(); + } + + // Decode escape sequence + if (source.End) + { + // EoF + requestHint = 2; + break; + } + + if (!source.IsNext((byte)'u', advancePast: true)) + { + DecodeSpetialEscapeSequence(source, result); + length += 2; + } + else + { + DecodeUnicodeEscapceSequence(source, result, out requestHint); + if (requestHint != 0) + { + source.Rewind(source.Consumed - startOffset); + return default; + } + + length += 6; + } + } + } + + // No closing quotation. + return default; + } + + private byte ReadStringStart(in SequenceReader source, out ReadOnlySpan delimiters, out int requestHint) + { + if (source.End) + { + requestHint = 2; + delimiters = default; + return default; + } + + if (source.IsNext((byte)'"', advancePast: false)) + { + delimiters = JsonStringTokens.DoubleQuotationDelimiters; + } + else + { + if ((this.Options.ParseOptions & JsonParseOptions.AllowSingleQuotationString) != 0) + { + // ['"] + if (source.IsNext((byte)'\'', advancePast: false)) + { + delimiters = JsonStringTokens.SingleQuotationDelimiters; + } + else + { + JsonThrow.IsNotStringStart(source.Consumed, JsonStringTokens.AnyQuotations); + // never + delimiters = default; + } + } + else + { + JsonThrow.IsNotStringStart(source.Consumed, JsonStringTokens.DoubleQuotation); + // never + delimiters = default; + } + } + + source.TryRead(out var quotation); + requestHint = 0; + return quotation; + } + + private static void DecodeSpetialEscapeSequence(in SequenceReader source, StringBuilderBufferWriter result) + { + source.TryPeek(out var escaped); + // Handle known escape sequence + var index = escaped - JsonStringTokens.UnescapedCharactorsOffset; + var unescaped = Byte.MaxValue; + if (index >= 0 && index < JsonStringTokens.UnescapedCharactors.Length) + { + unescaped = JsonStringTokens.UnescapedCharactors[index]; + } + + if (unescaped >= 0x80) + { + JsonThrow.InvalidEscapeSequence(source.Consumed, escaped); + } + + result.AppendUtf16CodePoint(unescaped); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static void DecodeUnicodeEscapceSequence(SequenceReader source, StringBuilderBufferWriter result, out int requestHint) + { + Span buffer = stackalloc byte[4]; + if (!source.TryCopyTo(buffer)) + { + requestHint = buffer.Length - (int)source.Remaining; + return; + } + + if (!Utf8Parser.TryParse(buffer, out int codePointOrHighSurrogate, out var consumed, standardFormat: 'X')) + { + JsonThrow.InvalidUnicodeEscapeSequence(source.Consumed - 2, buffer); + } + + Debug.Assert(consumed == 4, $"consumed ({consumed}) == 4 for '{BitConverter.ToString(buffer.ToArray())}'"); + + var positionOf1stSurrogate = source.Consumed - 2; + if (Unicode.IsLowSurrogate(codePointOrHighSurrogate)) + { + // 1st surrogte must be high surrogate. + JsonThrow.OrphanSurrogate(positionOf1stSurrogate, codePointOrHighSurrogate); + } + + source.Advance(consumed); + result.AppendUtf16CodePoint(codePointOrHighSurrogate); + + if (Unicode.IsHighSurrogate(codePointOrHighSurrogate)) + { + if (source.Remaining < 7) + { + requestHint = 7 - (int)source.Remaining; + return; + } + + // Search lower surrogate + if (!source.IsNext((byte)'\\', advancePast: true)) + { + // No paired low surrogate. + JsonThrow.OrphanSurrogate(positionOf1stSurrogate, codePointOrHighSurrogate); + } + + if (!source.IsNext((byte)'u', advancePast: true)) + { + // No paired low surrogate. + JsonThrow.OrphanSurrogate(positionOf1stSurrogate, codePointOrHighSurrogate); + } + + source.Sequence.Slice(source.Consumed, 4).CopyTo(buffer); + if (!Utf8Parser.TryParse(buffer, out int shouldBeLowSurrogate, out consumed, standardFormat: 'X')) + { + JsonThrow.InvalidUnicodeEscapeSequence(source.Consumed, buffer); + } + + if (!Unicode.IsLowSurrogate(shouldBeLowSurrogate)) + { + // No paired low surrogate. + JsonThrow.OrphanSurrogate(positionOf1stSurrogate, codePointOrHighSurrogate); + } + + Debug.Assert(consumed == 4, $"consumed ({consumed}) == 4 for '{BitConverter.ToString(buffer.ToArray())}'"); + + source.Advance(consumed); + result.AppendUtf16CodePoint(shouldBeLowSurrogate); + } + + requestHint = 0; + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override byte[]? DecodeNullableBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + { + this.ReadTrivia(source, out _); + if (this.TryReadNull(source)) + { + requestHint = 0; + return null; + } + + return this.DecodeBinaryCore(source, out requestHint, cancellationToken); + } + + /// + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override byte[]? DecodeBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + { + this.ReadTrivia(source, out _); + return this.DecodeBinaryCore(source, out requestHint, cancellationToken); + } + + private byte[]? DecodeBinaryCore(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + { + var startOffset = source.Consumed; + + var quotation = this.ReadStringStart(source, out _, out requestHint); + if (requestHint != 0) + { + return default; + } + + if (!source.TryReadTo(out ReadOnlySequence sequence, quotation, advancePastDelimiter: true)) + { + // EoF + requestHint = 1; + source.Rewind(source.Consumed - startOffset); + return default; + } + + if (sequence.Length > this.Options.MaxBinaryLengthInBytes) + { + Throw.BinaryLengthExceeded(startOffset + 1, sequence.Length, this.Options.MaxBinaryLengthInBytes); + } + + var resultLength = 0; + var inputBuffer = sequence.IsSingleSegment ? null : this.Options.ByteBufferPool.Rent(this.Options.MaxByteBufferLength); + try + { + var outputBuffer = this.Options.ByteBufferPool.Rent(this.Options.MaxByteBufferLength); + try + { + ReadOnlySpan inputSpan = sequence.FirstSpan; + Span outputSpan = outputBuffer; + var status = OperationStatus.DestinationTooSmall; + while (true) + { + status = Base64.DecodeFromUtf8(inputSpan, outputSpan, out var bytesConsumed, out var bytesWritten, isFinalBlock: sequence.IsSingleSegment); + inputSpan = inputSpan.Slice(bytesConsumed); + outputSpan = outputSpan.Slice(bytesWritten); + + resultLength += bytesWritten; + switch (status) + { + case OperationStatus.Done: + { + // OK + var result = new byte[resultLength]; + unsafe + { + fixed (byte* pBuffer = outputBuffer) + fixed (byte* pResult = result) + { + Buffer.MemoryCopy(pBuffer, pResult, result.Length, resultLength); + } + } + + return result; + } + case OperationStatus.DestinationTooSmall: + { + // Realloc buffer and set span head. + + var newBuffer = this.Options.ByteBufferPool.Rent(outputBuffer.Length * 2); + unsafe + { + fixed (byte* pBuffer = outputBuffer) + fixed (byte* pNewBuffer = newBuffer) + { + Buffer.MemoryCopy(pBuffer, pNewBuffer, outputBuffer.Length, outputBuffer.Length); + } + } + + outputSpan = newBuffer.AsSpan(outputBuffer.Length - outputSpan.Length); + this.Options.ByteBufferPool.Return(outputBuffer, this.Options.ClearsBuffer); + outputBuffer = newBuffer; + break; + } + case OperationStatus.NeedMoreData: + { + // In this case, ReadOnlySequence is multi segment. + Debug.Assert(!sequence.IsSingleSegment, "!sequence.IsSingleSegment"); + Debug.Assert(inputBuffer != null, "inputBuffer != null"); + + // Realloc input span. + + var consumed = inputBuffer.Length - inputSpan.Length; + + sequence = sequence.Slice(consumed); + var newInputSpanLength = (int)Math.Min(inputBuffer.Length, sequence.Length); + sequence.Slice(0, newInputSpanLength).CopyTo(inputBuffer); + inputSpan = inputBuffer.AsSpan(0, newInputSpanLength); + break; + } + default: + { + Debug.Assert(status == OperationStatus.InvalidData, $"status ({status}) == OperationStatus.InvalidData"); + JsonThrow.InvalidBase64(startOffset); + // never + return default; + } + } + } + } + finally + { + this.Options.ByteBufferPool.Return(outputBuffer, this.Options.ClearsBuffer); + } + } + finally + { + if (!(inputBuffer is null)) + { + this.Options.ByteBufferPool.Return(inputBuffer, this.Options.ClearsBuffer); + } + } + } + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoder.cs b/src/MsgPack.Json/Json/JsonDecoder.cs new file mode 100644 index 000000000..87fc25bf5 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoder.cs @@ -0,0 +1,166 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Text; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + /// + /// A decoder for JSON format. + /// + public abstract partial class JsonDecoder : Decoder + { + private static readonly FormatFeatures JsonFormatFeatures = + new FormatFeaturesBuilder + { + CanCountCollectionItems = false, + CanSpecifyStringEncoding = false, + SupportsExtensionTypes = false, + IsContextful = true + }.Build(); + + public new JsonDecoderOptions Options { get; } + + protected JsonDecoder(JsonDecoderOptions options) + : base(options, JsonFormatFeatures) + { + this.Options = options; + } + + public static JsonDecoder Create(JsonDecoderOptions options) + { + if ((options?.ParseOptions).GetValueOrDefault() == JsonParseOptions.None) + { + return new SimpleJsonDecoder(options!); + } + else + { + return new FlexibleJsonDecoder(options!); + } + } + + private protected static Utf8UnitStatus TryGetUtf8Unit(in SequenceReader source, out ReadOnlySequence unit) + { + if (source.UnreadSpan.IsEmpty) + { + unit = default; + return Utf8UnitStatus.TooShort; + } + + var utf80 = source.UnreadSpan[0]; + if ((utf80 & 0b_1000_0000) == 0) + { + // 1byte + unit = source.Sequence.Slice(source.Consumed, 1); + return Utf8UnitStatus.Valid; + } + else if ((utf80 & 0b_1110_0000) == 0b_1100_0000) + { + Span utf8 = stackalloc byte[2]; + if (!source.TryCopyTo(utf8)) + { + unit = source.Sequence.Slice(source.Consumed, source.Remaining); + return Utf8UnitStatus.TooShort; + } + + unit = source.Sequence.Slice(source.Consumed, 2); + + // 2 bytes, 5 + 6 bits + var bits1 = utf8[0] & 0b_0001_1111; + var bits2 = utf8[1] & 0b_0011_1111; + + if ((bits1 & 0b_1_1110) == 0 + || (utf8[1] & 0b_1100_0000) != 0b_1000_0000) + { + return Utf8UnitStatus.Invalid; + } + + return Utf8UnitStatus.Valid; + } + else if ((utf80 & 0b_1111_0000) == 0b_1110_0000) + { + Span utf8 = stackalloc byte[3]; + if (!source.TryCopyTo(utf8)) + { + unit = source.Sequence.Slice(source.Consumed, source.Remaining); + return Utf8UnitStatus.TooShort; + } + + unit = source.Sequence.Slice(source.Consumed, 3); + + if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[2] & 0b_1100_0000) != 0b_1000_0000) + { + return Utf8UnitStatus.Invalid; + } + + // 3 bytes, 4 + 6 + 6 bits + var bits1 = utf8[0] & 0b_0000_1111; + var bits2 = utf8[1] & 0b_0011_1111; + var bits3 = utf8[2] & 0b_0011_1111; + + ushort codePoint = (ushort)((bits1 << 12) | (bits2 << 6) | bits3); + + if ((codePoint & 0b_1111_100000_000000) == 0 || !Rune.IsValid(codePoint)) + { + return Utf8UnitStatus.Invalid; + } + + return Utf8UnitStatus.Valid; + } + else if ((utf80 & 0b_1111_1000) == 0b_1111_0000) + { + Span utf8 = stackalloc byte[4]; + if (!source.TryCopyTo(utf8)) + { + unit = source.Sequence.Slice(source.Consumed, source.Remaining); + return Utf8UnitStatus.TooShort; + } + + unit = source.Sequence.Slice(source.Consumed, 4); + + if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[2] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[3] & 0b_1100_0000) != 0b_1000_0000) + { + return Utf8UnitStatus.Invalid; + } + + // 4 bytes, 3 + 6 + 6 + 6 bits + var bits1 = (utf8[0] & 0b_0000_0111) << 18; + var bits2 = (utf8[1] & 0b_0011_1111) << 12; + var bits3 = (utf8[2] & 0b_0011_1111) << 6; + var bits4 = (utf8[3] & 0b_0011_1111); + + int codePoint = ((bits1 << 18) | (bits2 << 12) | (bits1 << 6) | bits4); + + if ((codePoint & 0b_111_110000_000000_000000) == 0) + { + return Utf8UnitStatus.Invalid; + } + + return Utf8UnitStatus.Valid; + } + else + { + unit = source.Sequence.Slice(source.Consumed, 1); + return Utf8UnitStatus.Invalid; + } + } + + protected abstract void ReadTrivia(in SequenceReader source, out ReadOnlySequence trivia); + + protected abstract bool TryReadNull(in SequenceReader source); + + protected enum Utf8UnitStatus + { + TooShort = 0, + Valid = 1, + Invalid = 2 + } + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoderOptions.cs b/src/MsgPack.Json/Json/JsonDecoderOptions.cs new file mode 100644 index 000000000..eb4e36dbc --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoderOptions.cs @@ -0,0 +1,22 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using MsgPack.Internal; + +namespace MsgPack.Json +{ + /// + /// Defines decoder options for . + /// + public class JsonDecoderOptions : DecoderOptions + { + public JsonParseOptions ParseOptions { get; } + + public JsonDecoderOptions(JsonDecoderOptionsBuilder builder) + : base(builder) + { + this.ParseOptions = builder.ParseOptions; + } + } +} diff --git a/src/MsgPack.Json/Json/JsonDecoderOptionsBuilder.cs b/src/MsgPack.Json/Json/JsonDecoderOptionsBuilder.cs new file mode 100644 index 000000000..2ac9b8d4a --- /dev/null +++ b/src/MsgPack.Json/Json/JsonDecoderOptionsBuilder.cs @@ -0,0 +1,20 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using MsgPack.Internal; + +namespace MsgPack.Json +{ + /// + /// A builder object for immutable . + /// + public class JsonDecoderOptionsBuilder : DecoderOptionsBuilder + { + public JsonParseOptions ParseOptions { get; set; } + + public JsonDecoderOptionsBuilder() { } + + public JsonDecoderOptions Build() => new JsonDecoderOptions(this); + } +} diff --git a/src/MsgPack.Json/Json/JsonEncoder.cs b/src/MsgPack.Json/Json/JsonEncoder.cs index 721247c94..ab21260ad 100644 --- a/src/MsgPack.Json/Json/JsonEncoder.cs +++ b/src/MsgPack.Json/Json/JsonEncoder.cs @@ -1,137 +1,49 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + using System; using System.Buffers; using System.Buffers.Text; -using System.IO; using System.Linq; using System.Runtime.CompilerServices; using System.Text; using System.Threading; -using System.Threading.Tasks; using MsgPack.Internal; namespace MsgPack.Json { - internal static class JsonTokens - { - public static readonly ReadOnlyMemory Null = new[] { (byte)'n', (byte)'u', (byte)'l', (byte)'l' }; - public static readonly ReadOnlyMemory True = new[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; - public static readonly ReadOnlyMemory False = new[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; - public static readonly ReadOnlyMemory ArrayStart = new[] { (byte)'[' }; - public static readonly ReadOnlyMemory ArrayEnd = new[] { (byte)']' }; - public static readonly ReadOnlyMemory MapStart = new[] { (byte)'{' }; - public static readonly ReadOnlyMemory MapEnd = new[] { (byte)'}' }; - public static readonly ReadOnlyMemory Whitespace = new[] { (byte)' ' }; - public static readonly ReadOnlyMemory Comma = new[] { (byte)',' }; - public static readonly ReadOnlyMemory Colon = new[] { (byte)':' }; - public static readonly ReadOnlyMemory Quatation = new[] { (byte)'"' }; - } - - internal static class JsonCharactor - { - public static readonly ReadOnlyMemory MustBeEscaped = new int[] { '\\', '"' }.Concat(Enumerable.Range(0, 0x1F)).Select(i => unchecked((byte)i)).ToArray(); - public static readonly ReadOnlyMemory ShouldBeEscaped = new[] { (byte)'/', (byte)'\'', (byte)'<', (byte)'>', (byte)'&' }; - public static readonly ReadOnlyMemory CarriageReturn = new[] { (byte)'\r' }; - public static readonly ReadOnlyMemory LineFeed = new[] { (byte)'\n' }; - } - - internal static class JsonEscapeSequence - { - public static readonly ReadOnlyMemory Unicode = new[] { (byte)'\\', (byte)'u' }; - - public static readonly ReadOnlyMemory ReverseSolidous = new[] { (byte)'\\', (byte)'\\' }; - public static readonly ReadOnlyMemory Quatation = new[] { (byte)'\\', (byte)'"' }; - public static readonly ReadOnlyMemory Tab = new[] { (byte)'\\', (byte)'t' }; - public static readonly ReadOnlyMemory CarriageReturn = new[] { (byte)'\\', (byte)'r' }; - public static readonly ReadOnlyMemory LineFeed = new[] { (byte)'\\', (byte)'n' }; - public static readonly ReadOnlyMemory BackSpace = new[] { (byte)'\\', (byte)'b' }; - public static readonly ReadOnlyMemory FormFeed = new[] { (byte)'\\', (byte)'f' }; - - public static readonly StandardFormat UnicodeFormat = new StandardFormat('X', 4); - } - - public enum NaNHandling - { - Default = 0, - Null = 1, - Error = 2, - Custom = 3 - } - - public enum InfinityHandling + /// + /// An encoder for JSON format. + /// + public sealed class JsonEncoder : Encoder { - Default = 0, - MinMax = 1, - Error = 2, - Custom = 3 - } - - [Flags] - public enum JsonParseOptions - { - None = 0, - AllowHashSingleLineComment = 0x1, - AllowDoubleSolidousSingleLineComment = 0x2, - AllowUnicodeWhitespace = 0x10, - AllowAllTrivias = 0xFF, - AllowNaN = 0x100, - AllowInfinity = 0x200, - AllowUndefined = 0x1000, - AllowIrregalValues = 0xFF00, - AllowEqualSignSeparator = 0x10000, - AllowSemicolonDelimiter = 0x20000, - AllowExtraComma = 0x40000, - AllowSingleQuatationString = 0x100000, - AllowUnescapedNewLineInString = 0x200000, - AllowUnescapedTabInString = 0x400000, - AllowWellknownSyntaxErrors = 0xFF0000, - AllowAllErrors = unchecked((int)0xFFFFFFFF) - } - - public class JsonEncoderOptionsBase - { -#warning TODO: Options - } - - public class JsonEncoderBase : Internal.Encoder - { -#warning TODO: Abstract -#warning TODO: tuning - private static readonly Func, int> s_singleFormatter = - (v, m) => Utf8Formatter.TryFormat(v, m.Span, out var used) ? used : -1; - private static readonly Func, int> s_defaultSingleNanFormatter = - (_, m) => WriteNull(m.Span); - private static readonly Func, int> s_defaultSingleInfinityFormatter = - (v, m) => Utf8Formatter.TryFormat(v < 0 ? Single.MinValue : Single.MaxValue, m.Span, out var used) ? used : -1; - - private static readonly Func, int> s_doubleFormatter = - (v, m) => Utf8Formatter.TryFormat(v, m.Span, out var used) ? used : -1; - private static readonly Func, int> s_defaultDoubleNanFormatter = - (_, m) => WriteNull(m.Span); - private static readonly Func, int> s_defaultDoubleInfinityFormatter = - (v, m) => Utf8Formatter.TryFormat(v < 0 ? Single.MinValue : Single.MaxValue, m.Span, out var used) ? used : -1; - - private readonly Func, int> _singleInfinityFormatter; - private readonly Func, int> _singleNanFormatter; - private readonly Func, int> _doubleInfinityFormatter; - private readonly Func, int> _doubleNanFormatter; - private readonly bool _doIndent; - private readonly byte[] _indentString; - private readonly byte[] _newLine; - private readonly ReadOnlyMemory _toBeEscaped; - - private int _indentLevel; - -#warning TODO: Options - protected JsonEncoderBase() + private readonly Action, JsonEncoderOptions> _singleInfinityFormatter; + private readonly Action, JsonEncoderOptions> _singleNanFormatter; + private readonly Action, JsonEncoderOptions> _doubleInfinityFormatter; + private readonly Action, JsonEncoderOptions> _doubleNanFormatter; + private readonly ReadOnlyMemory _indentChars; + private readonly ReadOnlyMemory _newLineChars; + private readonly ReadOnlyMemory _escapeTargetChars1Byte; + private readonly ReadOnlyMemory _escapeTargetChars2Byte; + private readonly ReadOnlyMemory _escapeTargetChars4Byte; + private readonly bool _isPrettyPrint; + private readonly JsonEncoderOptions _options; + + public JsonEncoder(JsonEncoderOptions options) + : base(options) { - this._singleInfinityFormatter = s_defaultSingleInfinityFormatter; - this._singleNanFormatter = s_defaultSingleNanFormatter; - this._doubleInfinityFormatter = s_defaultDoubleInfinityFormatter; - this._doubleNanFormatter = s_defaultDoubleNanFormatter; - this._doIndent = false; - this._indentString = new[] { (byte)' ', (byte)' ' }; - this._newLine = new[] { (byte)'\n' }; - this._toBeEscaped = JsonCharactor.MustBeEscaped; + this._options = options; + this._singleInfinityFormatter = options.SingleInfinityFormatter; + this._singleNanFormatter = options.SingleNaNFormatter; + this._doubleInfinityFormatter = options.DoubleInfinityFormatter; + this._doubleNanFormatter = options.DoubleNaNFormatter; + this._indentChars = options.IndentChars; + this._newLineChars = options.NewLineChars; + this._isPrettyPrint = options.IsPrettyPrint; + this._escapeTargetChars1Byte = options.EscapeTargetChars1Byte; + this._escapeTargetChars2Byte = options.EscapeTargetChars2Byte; + this._escapeTargetChars4Byte = options.EscapeTargetChars4Byte; } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] @@ -142,13 +54,15 @@ private static int WriteNull(Span span) return -1; } - JsonTokens.Null.Span.CopyTo(span); + JsonTokens.Null.CopyTo(span); return 4; } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void Encode(int value, IBufferWriter buffer) + public override void EncodeInt32(int value, IBufferWriter buffer) { + buffer = EnsureNotNull(buffer); + var span = buffer.GetSpan(); while (true) { @@ -165,8 +79,10 @@ public override void Encode(int value, IBufferWriter buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void Encode(long value, IBufferWriter buffer) + public override void EncodeUInt32(uint value, IBufferWriter buffer) { + buffer = EnsureNotNull(buffer); + var span = buffer.GetSpan(); while (true) { @@ -183,22 +99,16 @@ public override void Encode(long value, IBufferWriter buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void Encode(float value, IBufferWriter buffer) + public override void EncodeInt64(long value, IBufferWriter buffer) { - var formatter = - Single.IsNaN(value) ? - this._singleNanFormatter : - Single.IsInfinity(value) ? - this._singleInfinityFormatter : - s_singleFormatter; + buffer = EnsureNotNull(buffer); - var memory = buffer.GetMemory(); + var span = buffer.GetSpan(); while (true) { - var used = formatter(value, memory); - if (used < 0) + if (!Utf8Formatter.TryFormat(value, span, out var used)) { - memory = buffer.GetMemory(memory.Length * 2); + span = buffer.GetSpan(span.Length * 2); } else { @@ -209,22 +119,16 @@ public override void Encode(float value, IBufferWriter buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void Encode(double value, IBufferWriter buffer) + public override void EncodeUInt64(ulong value, IBufferWriter buffer) { - var formatter = - Double.IsNaN(value) ? - this._doubleNanFormatter : - Double.IsInfinity(value) ? - this._doubleInfinityFormatter : - s_doubleFormatter; + buffer = EnsureNotNull(buffer); - var memory = buffer.GetMemory(); + var span = buffer.GetSpan(); while (true) { - var used = formatter(value, memory); - if (used < 0) + if (!Utf8Formatter.TryFormat(value, span, out var used)) { - memory = buffer.GetMemory(memory.Length * 2); + span = buffer.GetSpan(span.Length * 2); } else { @@ -235,106 +139,152 @@ public override void Encode(double value, IBufferWriter buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void Encode(bool value, IBufferWriter buffer) + public override void EncodeSingle(float value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + + var formatter = + Single.IsNaN(value) ? + this._singleNanFormatter : + Single.IsInfinity(value) ? + this._singleInfinityFormatter : + JsonFormatter.Format; + formatter(value, buffer, this._options); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeDouble(double value, IBufferWriter buffer) { + buffer = EnsureNotNull(buffer); + + var formatter = + Double.IsNaN(value) ? + this._doubleNanFormatter : + Double.IsInfinity(value) ? + this._doubleInfinityFormatter : + JsonFormatter.Format; + formatter(value, buffer, this._options); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public override void EncodeBoolean(bool value, IBufferWriter buffer) + { + buffer = EnsureNotNull(buffer); + if (value) { - buffer.Write(JsonTokens.True.Span); + buffer.Write(JsonTokens.True); } else { - buffer.Write(JsonTokens.False.Span); + buffer.Write(JsonTokens.False); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public override void EncodeNull(IBufferWriter buffer) - => buffer.Write(JsonTokens.Null.Span); + => buffer.Write(JsonTokens.Null); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - protected void WriteIndent(IBufferWriter buffer) + private void WriteIndent(IBufferWriter buffer, in CollectionContext collectionContext) { - if (this._doIndent) + buffer = EnsureNotNull(buffer); + + if (this._isPrettyPrint) { - buffer.Write(this._newLine); - for (var i = 0; i < this._indentLevel; i++) + buffer.Write(this._newLineChars.Span); + for (var i = 0; i < collectionContext.CurrentDepth; i++) { - buffer.Write(this._indentString); + buffer.Write(this._indentChars.Span); } } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeArrayStart(uint length, IBufferWriter buffer) + public override void EncodeArrayStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer.Write(JsonTokens.ArrayStart.Span); - this._indentLevel++; + buffer = EnsureNotNull(buffer); + + buffer.Write(JsonTokens.ArrayStart); + collectionContext.IncrementDepth(); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeArrayEnd(uint length, IBufferWriter buffer) + public override void EncodeArrayEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - this._indentLevel--; - this.WriteIndent(buffer); - buffer.Write(JsonTokens.ArrayEnd.Span); + buffer = EnsureNotNull(buffer); + + collectionContext.DecrementDepth(); + this.WriteIndent(buffer, collectionContext); + buffer.Write(JsonTokens.ArrayEnd); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeArrayItemStart(uint index, IBufferWriter buffer) + public override void EncodeArrayItemStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { - this.WriteIndent(buffer); + buffer = EnsureNotNull(buffer); + + this.WriteIndent(buffer, collectionContext); if (index > 0) { - buffer.Write(JsonTokens.Comma.Span); + buffer.Write(JsonTokens.Comma); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeArrayItemEnd(uint index, IBufferWriter buffer) { } + public override void EncodeArrayItemEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapStart(uint length, IBufferWriter buffer) + public override void EncodeMapStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer.Write(JsonTokens.MapStart.Span); - this._indentLevel++; + buffer = EnsureNotNull(buffer); + + buffer.Write(JsonTokens.MapStart); + collectionContext.IncrementDepth(); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapEnd(uint length, IBufferWriter buffer) + public override void EncodeMapEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - this._indentLevel--; - this.WriteIndent(buffer); - buffer.Write(JsonTokens.MapEnd.Span); + buffer = EnsureNotNull(buffer); + + collectionContext.DecrementDepth(); + this.WriteIndent(buffer, collectionContext); + buffer.Write(JsonTokens.MapEnd); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapKeyStart(uint index, IBufferWriter buffer) + public override void EncodeMapKeyStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { - this.WriteIndent(buffer); + buffer = EnsureNotNull(buffer); + + this.WriteIndent(buffer, collectionContext); if (index > 0) { - buffer.Write(JsonTokens.Comma.Span); + buffer.Write(JsonTokens.Comma); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapKeyEnd(uint index, IBufferWriter buffer) { } + public override void EncodeMapKeyEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapValueStart(uint index, IBufferWriter buffer) + public override void EncodeMapValueStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer.Write(JsonTokens.Colon.Span); - if (this._doIndent) + buffer = EnsureNotNull(buffer); + + buffer.Write(JsonTokens.Colon); + if (this._isPrettyPrint) { - buffer.Write(JsonTokens.Whitespace.Span); + buffer.Write(JsonTokens.Whitespace); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapValueEnd(uint index, IBufferWriter buffer) { } + public override void EncodeMapValueEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private static ReadOnlyMemory GetEscapeSequence(byte c, bool newLineAllowed) + private static ReadOnlySpan GetEscapeSequence(byte c, bool newLineAllowed) { switch (c) { @@ -381,137 +331,224 @@ private static ReadOnlyMemory GetEscapeSequence(byte c, bool newLineAllowe } // Use \uXXXX - return ReadOnlyMemory.Empty; + return ReadOnlySpan.Empty; } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeString(ReadOnlySpan encodedValue, int charLength, IBufferWriter buffer) + public override void EncodeString(ReadOnlySpan encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) { - buffer.Write(JsonTokens.Quatation.Span); + buffer = EnsureNotNull(buffer); + + buffer.Write(JsonTokens.Quatation); - this.EncodeStringBody(encodedValue, buffer); + this.EncodeStringBody(encodedValue, 0, buffer); // End quot - buffer.Write(JsonTokens.Quatation.Span); + buffer.Write(JsonTokens.Quatation); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private void EncodeStringBody(ReadOnlySpan encodedValue, IBufferWriter buffer) + private void EncodeStringBody(ReadOnlySpan encodedValue, long position, IBufferWriter buffer) { var source = encodedValue; - var sink = buffer.GetSpan(encodedValue.Length); while (!source.IsEmpty) { - var moreBytes = this.EscapeStringContent(source, sink, out var sourceUsed, out var sinkUsed); - buffer.Advance(sinkUsed); - source = source.Slice(sourceUsed); - sink = buffer.GetSpan(moreBytes); + this.EscapeUtf8(ref source, ref position, buffer); } } - [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private int EscapeStringContent(ReadOnlyMemory source, Memory sink, out int sourceUsed, out int sinkUsed) - => this.EscapeStringContent(source.Span, sink.Span, out sourceUsed, out sinkUsed); - - private int EscapeStringContent(ReadOnlySpan source, Span sink, out int sourceUsed, out int sinkUsed) + private void EscapeUtf8(ref ReadOnlySpan utf8, ref long position, IBufferWriter buffer) { - sourceUsed = 0; - sinkUsed = 0; + if (utf8.IsEmpty) + { + return; + } + + if ((utf8[0] & 0b_1000_0000) == 0) + { + // 1 byte, 7bits + byte codePoint = (byte)(utf8[0] & 0b_0111_1111); + + if (this._escapeTargetChars1Byte.Span.Contains(codePoint) + || codePoint < 0x09 // Control chars must be escaped except horizontal tab + || (this._options.EscapesHorizontalTab && codePoint == 0x09) + || (codePoint > 0x09 && codePoint < 0x20) // Control chars must be escaped except horizontal tab + || codePoint >= 0x7F // Control chars must be escaped (DEL) + ) + { + var escapeSequence = GetEscapeSequence(codePoint, newLineAllowed: false); + if (escapeSequence.IsEmpty) + { + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(escapeSequence); + } + } + else + { + buffer.Write(utf8.Slice(0, 1)); + } - while (!sink.IsEmpty) + utf8 = utf8.Slice(1); + position += 1; + } + else if ((utf8[0] & 0b_1110_0000) == 0b_1100_0000) { - var toBeEscaped = source.IndexOfAny(this._toBeEscaped.Span); - if (toBeEscaped < 0) + if (utf8.Length < 2) { - // Copy entire source to sink, and then advance buffer. - source.CopyTo(sink); - sourceUsed += source.Length; - sinkUsed += source.Length; - continue; + JsonThrow.TooShortUtf8(); } - if (toBeEscaped > 0) + // 2 bytes, 5 + 6 bits + var bits1 = utf8[0] & 0b_0001_1111; + var bits2 = utf8[1] & 0b_0011_1111; + + if ((bits1 & 0b_1_1110) == 0 + || (utf8[1] & 0b_1100_0000) != 0b_1000_0000) { - // Copy source data which have not to be escaped to sink, and then advance buffer. - source.Slice(0, toBeEscaped).CopyTo(sink); - sink = sink.Slice(toBeEscaped); - sourceUsed += toBeEscaped; - sinkUsed += toBeEscaped; - source = source.Slice(toBeEscaped); + JsonThrow.MalformedUtf8(utf8, position); } -#warning TODO: OPTIONS - var escapeSequence = GetEscapeSequence(source[0], newLineAllowed: false); - switch (escapeSequence.Length) + ushort codePoint = (ushort)((bits1 << 6) | (bits2 << 6)); + + if (codePoint < 0xA0 // Control chars must be escaped + || this._escapeTargetChars2Byte.Span.Contains(codePoint)) { - case 1: - { - // Does not escape because of the option. - sink[0] = source[0]; - sink = sink.Slice(1); - sinkUsed++; - break; - } - case 0: - { - // \uXXXX escape - if (sink.Length < 6) - { - // Return to expecte realloc. - return source.Length + 6; - } - - JsonEscapeSequence.Unicode.Span.CopyTo(sink); - sink = sink.Slice(2); // \u - Utf8Formatter.TryFormat(source[0], sink, out _, JsonEscapeSequence.UnicodeFormat); - sink = sink.Slice(4); // xxxx - sinkUsed += 6; - break; - } - default: - { - // Use returned escape sequence - - if (sink.Length < escapeSequence.Length) - { - // Return to expecte realloc. - return source.Length + escapeSequence.Length; - } - - escapeSequence.Span.CopyTo(sink); - sink = sink.Slice(escapeSequence.Length); - sinkUsed += escapeSequence.Length; - break; - } - } // switch + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(utf8.Slice(0, 1)); + } + + utf8 = utf8.Slice(2); + position += 2; + } + else if ((utf8[0] & 0b_1111_0000) == 0b_1110_0000) + { + if (utf8.Length < 3) + { + JsonThrow.TooShortUtf8(); + } + + if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[2] & 0b_1100_0000) != 0b_1000_0000) + { + JsonThrow.MalformedUtf8(utf8, position); + } + + // 3 bytes, 4 + 6 + 6 bits + var bits1 = utf8[0] & 0b_0000_1111; + var bits2 = utf8[1] & 0b_0011_1111; + var bits3 = utf8[2] & 0b_0011_1111; + + ushort codePoint = (ushort)((bits1 << 12) | (bits2 << 6) | bits3); + + if ((codePoint & 0b_1111_100000_000000) == 0) + { + JsonThrow.MalformedUtf8(utf8, position); + } + + if (!Rune.IsValid(codePoint)) + { + JsonThrow.SurrogateCharInUtf8(position, codePoint); + } + + if (codePoint >= 0xFFFE // U+FFFE and U+FFFF are Reserved + || (this._options.EscapesPrivateUseCharactors && codePoint >= 0xE000 && codePoint <= 0xF8FF) // Privte Use + || this._escapeTargetChars2Byte.Span.Contains(codePoint)) + { + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(utf8.Slice(0, 1)); + } + + utf8 = utf8.Slice(3); + position += 3; + } + else if ((utf8[0] & 0b_1111_1000) == 0b_1111_0000) + { + if (utf8.Length < 4) + { + JsonThrow.TooShortUtf8(); + } + + if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[2] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[3] & 0b_1100_0000) != 0b_1000_0000) + { + JsonThrow.MalformedUtf8(utf8, position); + } + + // 4 bytes, 3 + 6 + 6 + 6 bits + var bits1 = (utf8[0] & 0b_0000_0111) << 18; + var bits2 = (utf8[1] & 0b_0011_1111) << 12; + var bits3 = (utf8[2] & 0b_0011_1111) << 6; + var bits4 = (utf8[3] & 0b_0011_1111); - sourceUsed++; - source = source.Slice(1); - } // while (!source.IsEmpty) + int codePoint = ((bits1 << 18) | (bits2 << 12) | (bits1 << 6) | bits4); + + if ((codePoint & 0b_111_110000_000000_000000) == 0) + { + JsonThrow.MalformedUtf8(utf8, position); + } + + if ((this._options.EscapesPrivateUseCharactors && codePoint >= 0xF0000) // Private use + || this._escapeTargetChars4Byte.Span.Contains(codePoint)) + { + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(utf8.Slice(0, 1)); + } + + utf8 = utf8.Slice(4); + position += 4; + } + else + { + JsonThrow.MalformedUtf8(utf8, position); + } + } - return 0; + private static void EscapeCodePoint(int codePoint, IBufferWriter buffer) + { + buffer.Write(JsonEscapeSequence.Unicode); + Span numbers = stackalloc byte[4]; + Utf8Formatter.TryFormat(codePoint, numbers, out _, JsonEscapeSequence.UnicodeFormat); + buffer.Write(numbers); } - public override void EncodeString(in ReadOnlySequence encodedValue, int charLength, IBufferWriter buffer) + public override void EncodeString(in ReadOnlySequence encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) { + buffer = EnsureNotNull(buffer); + // Start quot - buffer.Write(JsonTokens.Quatation.Span); + buffer.Write(JsonTokens.Quatation); var source = new SequenceReader(encodedValue); while (!source.UnreadSpan.IsEmpty) { var length = source.UnreadSpan.Length; - this.EncodeStringBody(source.UnreadSpan, buffer); + this.EncodeStringBody(source.UnreadSpan, source.Consumed, buffer); source.Advance(length); } // End quot - buffer.Write(JsonTokens.Quatation.Span); + buffer.Write(JsonTokens.Quatation); } - public override void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding encoding) + public override void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) { + buffer = EnsureNotNull(buffer); + encoding = encoding ?? Utf8EncodingNonBom.Instance; + if (value.Length < 256) { // Fast-path with stackalloc @@ -525,12 +562,12 @@ public override void EncodeString(ReadOnlySpan value, IBufferWriter // Slow-path // Start quot - buffer.Write(JsonTokens.Quatation.Span); + buffer.Write(JsonTokens.Quatation); var encoder = encoding.GetEncoder(); var source = value; - var encodingBuffer = this.ByteArrayPool.Rent(Math.Min(2 * 1024 * 1024, encoding.GetMaxByteCount(unchecked((int)(value.Length & 0x1FFFFFFF))))); + var encodingBuffer = base.Options.ByteBufferPool.Rent(Math.Min(2 * 1024 * 1024, encoding.GetMaxByteCount(unchecked((int)(value.Length & 0x1FFFFFFF))))); try { Span encodingSpan = encodingBuffer; @@ -539,22 +576,25 @@ public override void EncodeString(ReadOnlySpan value, IBufferWriter var length = source.Length; encoder.Convert(source, encodingSpan, flush: source.Length <= encodingSpan.Length, out var charsUsed, out var bytesUsed, out _); - this.EncodeStringBody(encodingSpan.Slice(0, bytesUsed), buffer); + this.EncodeStringBody(encodingSpan.Slice(0, bytesUsed), position: 0 /* should not be used because ALWAYS valid UTF-8 */, buffer); source = source.Slice(charsUsed); } } finally { - this.ByteArrayPool.Return(encodingBuffer, this.ClearsBufferPool); + base.Options.ByteBufferPool.Return(encodingBuffer, base.Options.ClearsBuffer); } // End quot - buffer.Write(JsonTokens.Quatation.Span); + buffer.Write(JsonTokens.Quatation); } } - public override void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding encoding) + public override void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) { + buffer = EnsureNotNull(buffer); + encoding = encoding ?? Utf8EncodingNonBom.Instance; + if (value.IsSingleSegment) { this.EncodeString(value.FirstSpan, buffer, encoding); @@ -574,211 +614,102 @@ public override void EncodeString(in ReadOnlySequence value, IBufferWriter // Slow-path // Start quot - buffer.Write(JsonTokens.Quatation.Span); + buffer.Write(JsonTokens.Quatation); var encoder = encoding.GetEncoder(); var source = new SequenceReader(value); - var encodingBuffer = this.ByteArrayPool.Rent(Math.Min(2 * 1024 * 1024, encoding.GetMaxByteCount(unchecked((int)(value.Length & 0x1FFFFFFF))))); + var encodingBuffer = base.Options.ByteBufferPool.Rent(Math.Min(2 * 1024 * 1024, encoding.GetMaxByteCount(unchecked((int)(value.Length & 0x1FFFFFFF))))); try { Span encodingSpan = encodingBuffer; while (!source.UnreadSpan.IsEmpty) { + cancellationToken.ThrowIfCancellationRequested(); + var length = source.UnreadSpan.Length; encoder.Convert(source.UnreadSpan, encodingSpan, flush: source.Remaining <= encodingSpan.Length, out var charsUsed, out var bytesUsed, out _); - this.EncodeStringBody(encodingSpan.Slice(0, bytesUsed), buffer); + this.EncodeStringBody(encodingSpan.Slice(0, bytesUsed), position: 0 /* should not be used because ALWAYS valid UTF-8 */, buffer); source.Advance(charsUsed); } } finally { - this.ByteArrayPool.Return(encodingBuffer, this.ClearsBufferPool); + base.Options.ByteBufferPool.Return(encodingBuffer, base.Options.ClearsBuffer); } // End quot - buffer.Write(JsonTokens.Quatation.Span); + buffer.Write(JsonTokens.Quatation); } } - protected override async ValueTask EncodeLargeStringCoreAsync(TextReader source, Stream sink, Encoding encoding, Func bufferStreamProvider, CancellationToken cancellationToken) + public override void EncodeBinary(ReadOnlySpan value, IBufferWriter buffer, CancellationToken cancellationToken = default) { // Start quot - await sink.WriteAsync(JsonTokens.Quatation, cancellationToken).ConfigureAwait(false); - long written = JsonTokens.Quatation.Length; - - var maxBytesPerChar = encoding.GetMaxByteCount(1); - var encoder = encoding.GetEncoder(); - var charBufferLength = ByteBufferLength / maxBytesPerChar; + buffer.Write(JsonTokens.Quatation); - var encodingBufferArray = this.ByteArrayPool.Rent(ByteBufferLength); - try + if (value.Length < this.Options.CancellationSupportThreshold) { - var sourceBufferArray = this.CharArrayPool.Rent(charBufferLength); - try - { - var escapingBufferArray = this.ByteArrayPool.Rent(ByteBufferLength); - try - { - Memory sourceBuffer = sourceBufferArray; - Memory encodingBuffer = encodingBufferArray; - Memory escapingBuffer = escapingBufferArray; - - bool completed = false; - - do - { - var readLength = await source.ReadAsync(sourceBuffer, cancellationToken).ConfigureAwait(false); - var shouldFlush = readLength <= sourceBuffer.Length && source.Peek() < 0; - var chars = sourceBuffer.Slice(0, readLength); - while (!chars.IsEmpty) - { - encoder.Convert(chars, encodingBuffer, shouldFlush, out var charsUsed, out var bytesUsed, out completed); - encodingBuffer = encodingBuffer.Slice(0, bytesUsed); - - while (!encodingBuffer.IsEmpty) - { - var moreEscapingBufferSize = this.EscapeStringContent(encodingBuffer, escapingBuffer, out var encodingBufferUsed, out var escapingBufferUsed); - - await sink.WriteAsync(escapingBuffer.Slice(0, escapingBufferUsed), cancellationToken).ConfigureAwait(false); - written += escapingBufferUsed; - - encodingBuffer = encodingBuffer.Slice(encodingBufferUsed); - escapingBuffer = escapingBuffer.Compact(escapingBufferArray, escapingBufferUsed); - } - - chars = chars.Slice(charsUsed); - } - } while (!completed); - } - finally - { - this.ByteArrayPool.Return(escapingBufferArray, this.ClearsBufferPool); - } - } - finally - { - this.CharArrayPool.Return(sourceBufferArray, this.ClearsBufferPool); - } + // Enough space -- over 138% + var span = buffer.GetSpan((int)(value.Length * 1.38)); + Base64.EncodeToUtf8(value, span, out var bytesConsumed, out var bytesWritten, isFinalBlock: true); + buffer.Advance(bytesWritten); } - finally + else { - this.ByteArrayPool.Return(encodingBufferArray, this.ClearsBufferPool); + EncodeBinarySlow(value, buffer, this.Options.CancellationSupportThreshold, cancellationToken); } // End quot - await sink.WriteAsync(JsonTokens.Quatation, cancellationToken).ConfigureAwait(false); - written += JsonTokens.Quatation.Length; - - return written; - } - - public override void EncodeBinary(ReadOnlySpan value, IBufferWriter buffer) - { - // Start quot - buffer.Write(JsonTokens.Quatation.Span); - - // Enough space -- over 138% - var span = buffer.GetSpan((int)(value.Length * 1.38)); - Base64.EncodeToUtf8(value, span, out var bytesConsumed, out var bytesWritten, isFinalBlock: true); - buffer.Advance(bytesWritten); - - // End quot - buffer.Write(JsonTokens.Quatation.Span); + buffer.Write(JsonTokens.Quatation); } - public override void EncodeBinary(in ReadOnlySequence value, IBufferWriter buffer) + [MethodImpl(MethodImplOptions.NoInlining)] + private static void EncodeBinarySlow(ReadOnlySpan value, IBufferWriter buffer, int bufferSize, CancellationToken cancellationToken) { - // Start quot - buffer.Write(JsonTokens.Quatation.Span); - - var array = this.ByteArrayPool.Rent(ByteBufferLength); - try + while (true) { - var reader = new SequenceReader(value); - Span sourceBuffer = array; - - while (true) + var span = buffer.GetSpan(bufferSize); + if (Base64.EncodeToUtf8(value, span, out var bytesConsumed, out var bytesWritten, isFinalBlock: value.IsEmpty) == OperationStatus.Done) { - if (!reader.TryCopyTo(sourceBuffer)) - { - while (!reader.End) - { - reader.UnreadSpan.CopyTo(sourceBuffer); - sourceBuffer = sourceBuffer.Slice(0, reader.UnreadSpan.Length); - reader.Advance(reader.UnreadSpan.Length); - } - } - - var span = buffer.GetSpan(sourceBuffer.Length * 2); - var status = Base64.EncodeToUtf8(sourceBuffer, span, out var bytesConsumed, out var bytesWritten, reader.End); - buffer.Advance(bytesWritten); - reader.Advance(bytesConsumed); - sourceBuffer = array; - - if (status == OperationStatus.Done && reader.End) - { - break; - } + break; } - // End quot - buffer.Write(JsonTokens.Quatation.Span); - } - finally - { - this.ByteArrayPool.Return(array, this.ClearsBufferPool); + buffer.Advance(bytesWritten); + value = value.Slice(bytesConsumed); + cancellationToken.ThrowIfCancellationRequested(); } - - // End quot - buffer.Write(JsonTokens.Quatation.Span); } - protected override async ValueTask EncodeLargeBinaryCoreAsync(Stream source, Stream sink, Func bufferStreamProvider, CancellationToken cancellationToken) + public override void EncodeBinary(in ReadOnlySequence value, IBufferWriter buffer, CancellationToken cancellationToken = default) { - static OperationStatus EncodeBase64ToUtf8(ReadOnlyMemory sourceMemory, Memory sinkMemory, out int bytesConsumed, out int bytesWritten, bool isFinalBlock) - => Base64.EncodeToUtf8(sourceMemory.Span, sinkMemory.Span, out bytesConsumed, out bytesWritten, isFinalBlock); - - // End quot - await sink.WriteAsync(JsonTokens.Quatation, cancellationToken).ConfigureAwait(false); - long written = JsonTokens.Quatation.Length; - - var sourceBufferArray = this.ByteArrayPool.Rent(ByteBufferLength / 2); - try + if (value.IsSingleSegment) { - var sinkBufferArray = this.ByteArrayPool.Rent(ByteBufferLength); - try - { - Memory sourceBuffer = sourceBufferArray; - Memory sinkBuffer = sinkBufferArray; - - while (true) - { - await source.ReadAsync(sourceBuffer, cancellationToken).ConfigureAwait(false); + // Fast-path + this.EncodeBinary(value.FirstSpan, buffer, cancellationToken); + return; + } - var status = EncodeBase64ToUtf8(sourceBuffer, sinkBuffer, out var sourceConsumed, out var sinkWritten, source.Position == source.Length); + // Start quot + buffer.Write(JsonTokens.Quatation); - if (status == OperationStatus.Done && source.Position == source.Length) - { - break; - } - } - } - finally + var reader = new SequenceReader(value); + while (true) + { + var span = buffer.GetSpan(this.Options.CancellationSupportThreshold); + if (Base64.EncodeToUtf8(reader.UnreadSpan, span, out var bytesConsumed, out var bytesWritten, isFinalBlock: reader.End) == OperationStatus.Done) { - this.ByteArrayPool.Return(sinkBufferArray, this.ClearsBufferPool); + break; } - } - finally - { - this.ByteArrayPool.Return(sourceBufferArray, this.ClearsBufferPool); + + buffer.Advance(bytesWritten); + reader.Advance(bytesConsumed); + cancellationToken.ThrowIfCancellationRequested(); } // End quot - await sink.WriteAsync(JsonTokens.Quatation, cancellationToken).ConfigureAwait(false); - written += JsonTokens.Quatation.Length; - return written; + buffer.Write(JsonTokens.Quatation); } } } diff --git a/src/MsgPack.Json/Json/JsonEncoderOptions.cs b/src/MsgPack.Json/Json/JsonEncoderOptions.cs new file mode 100644 index 000000000..66014e1cf --- /dev/null +++ b/src/MsgPack.Json/Json/JsonEncoderOptions.cs @@ -0,0 +1,116 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Linq; +using System.Text; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + /// + /// Defines encoder options for . + /// + public class JsonEncoderOptions : EncoderOptions + { + private static readonly Action, JsonEncoderOptions> ErrorSingleInfinityFormatter = + (value, buffer, options) => throw new ArgumentException($"Cannot serialize infinity ({value}) to JSON.", "value"); + + private static readonly Action, JsonEncoderOptions> MinMaxSingleInfinityFormatter = + (value, buffer, options) => JsonFormatter.Format(Single.IsPositiveInfinity(value) ? Single.MaxValue : Single.MinValue, buffer, options); + + private static readonly Action, JsonEncoderOptions> ErrorDoubleInfinityFormatter = + (value, buffer, options) => throw new ArgumentException($"Cannot serialize infinity ({value}) to JSON.", "value"); + + private static readonly Action, JsonEncoderOptions> MinMaxDoubleInfinityFormatter = + (value, buffer, options) => JsonFormatter.Format(Double.IsPositiveInfinity(value) ? Double.MaxValue : Double.MinValue, buffer, options); + + private static readonly Action, JsonEncoderOptions> ErrorSingleNaNFormatter = + (value, buffer, options) => throw new ArgumentException($"Cannot serialize NaN to JSON.", "value"); + private static readonly Action, JsonEncoderOptions> NullSingleNaNFormatter = + (value, buffer, options) => JsonFormatter.WriteNull(buffer); + + private static readonly Action, JsonEncoderOptions> ErrorDoubleNaNFormatter = + (value, buffer, options) => throw new ArgumentException($"Cannot serialize NaN to JSON.", "value"); + + private static readonly Action, JsonEncoderOptions> NullDoubleNaNFormatter = + (value, buffer, options) => JsonFormatter.WriteNull(buffer); + + private readonly Action, JsonEncoderOptions>? _singleInfinityFormatter; + private readonly Action, JsonEncoderOptions>? _doubleInfinityFormatter; + private readonly Action, JsonEncoderOptions>? _singleNaNFormatter; + private readonly Action, JsonEncoderOptions>? _doubleNaNFormatter; + + public ReadOnlyMemory IndentChars { get; } + public ReadOnlyMemory NewLineChars { get; } + public bool IsPrettyPrint { get; } + public bool EscapesHorizontalTab { get; } + public bool EscapesPrivateUseCharactors { get; } + public ReadOnlyMemory EscapeTargetChars { get; } + internal ReadOnlyMemory EscapeTargetChars1Byte { get; } + internal ReadOnlyMemory EscapeTargetChars2Byte { get; } + internal ReadOnlyMemory EscapeTargetChars4Byte { get; } + + public JsonEncoderOptions(JsonEncoderOptionsBuilder builder) + : base(builder) + { + this._singleInfinityFormatter = + builder.InfinityHandling switch + { + InfinityHandling.Custom => builder.SingleInfinityFormatter, + InfinityHandling.Error => ErrorSingleInfinityFormatter, + InfinityHandling.MinMax => MinMaxSingleInfinityFormatter, + _ => default, + }; + this._doubleInfinityFormatter = + builder.InfinityHandling switch + { + InfinityHandling.Custom => builder.DoubleInfinityFormatter, + InfinityHandling.Error => ErrorDoubleInfinityFormatter, + InfinityHandling.MinMax => MinMaxDoubleInfinityFormatter, + _ => default, + }; + this._singleNaNFormatter = + builder.NaNHandling switch + { + NaNHandling.Custom => builder.SingleNaNFormatter, + NaNHandling.Error => ErrorSingleNaNFormatter, + NaNHandling.Null => NullSingleNaNFormatter, + _ => default, + }; + this._doubleNaNFormatter = + builder.NaNHandling switch + { + NaNHandling.Custom => builder.DoubleNaNFormatter, + NaNHandling.Error => ErrorDoubleNaNFormatter, + NaNHandling.Null => NullDoubleNaNFormatter, + _ => default, + }; + + this.IndentChars = builder.IndentChars; + this.NewLineChars = builder.NewLineChars; + this.IsPrettyPrint = builder.IsPrettyPrint; + var escapeTargetChars = builder.AdditionalEscapeTargetChars.ToArray().Concat(JsonCharactor.MustBeEscaped1Byte.Select(b => new Rune(b))).Distinct().OrderBy(r => r).ToArray(); + this.EscapeTargetChars = escapeTargetChars; + this.EscapeTargetChars1Byte = escapeTargetChars.Where(r => r.Value <= Byte.MaxValue).Select(r => (byte)r.Value).ToArray(); + this.EscapeTargetChars2Byte = escapeTargetChars.Where(r => r.Value > Byte.MaxValue && r.Value <= UInt16.MaxValue).Select(r => (ushort)r.Value).ToArray(); + this.EscapeTargetChars4Byte = escapeTargetChars.Where(r => r.Value > UInt16.MaxValue).Select(r => r.Value).ToArray(); + this.EscapesHorizontalTab = builder.EscapesHorizontalTab; + this.EscapesPrivateUseCharactors = builder.EscapesPrivateUseCharactors; + } + + internal Action, JsonEncoderOptions> SingleInfinityFormatter => + this._singleInfinityFormatter ?? MinMaxSingleInfinityFormatter; + + internal Action, JsonEncoderOptions> SingleNaNFormatter => + this._singleNaNFormatter ?? NullSingleNaNFormatter; + + internal Action, JsonEncoderOptions> DoubleInfinityFormatter => + this._doubleInfinityFormatter ?? MinMaxDoubleInfinityFormatter; + + internal Action, JsonEncoderOptions> DoubleNaNFormatter => + this._doubleNaNFormatter ?? NullDoubleNaNFormatter; + } +} diff --git a/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs b/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs new file mode 100644 index 000000000..c68260e1d --- /dev/null +++ b/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs @@ -0,0 +1,157 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Text; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + /// + /// A builder object to construct immutable object. + /// + public class JsonEncoderOptionsBuilder : EncoderOptionsBuilder + { + private static readonly ReadOnlyMemory DefaultIndentChars = + new byte[] { (byte)' ', (byte)' ' }; + + private static readonly ReadOnlyMemory DefaultNewLineChars = + new byte[] { (byte)'\n' }; + + private InfinityHandling _infinityHandling = InfinityHandling.Default; + + public InfinityHandling InfinityHandling + { + get => this._infinityHandling; + set => this._infinityHandling = JsonOptionsValidation.EnsureKnownNonCustom(value); + } + + private NaNHandling _nanHandling; + + public NaNHandling NaNHandling + { + get => this._nanHandling; + set => this._nanHandling = JsonOptionsValidation.EnsureKnownNonCustom(value); + } + + private ReadOnlyMemory _indentChars = DefaultIndentChars; + public ReadOnlyMemory IndentChars + { + get => this._indentChars; + set + { + for (var i = 0; i < value.Length; i++) + { + switch (value.Span[i]) + { + case (byte)' ': + case (byte)'\t': + case (byte)'\r': + case (byte)'\n': + { + // OK + break; + } + default: + { + throw new ArgumentException( + $"IndentChars can only contains whitespace (U+0020), horizontal tab (U+0009), carriage return (U+000A), and line feed (U+000D) according to RFC8259, but char at {i} is U+00{value.Span[i]:X2}.", + "value" + ); + } + } + } + + this._indentChars = value; + } + } + + private ReadOnlyMemory _newLineChars = DefaultNewLineChars; + public ReadOnlyMemory NewLineChars + { + get => this._newLineChars; + set + { + for (var i = 0; i < value.Length; i++) + { + switch (value.Span[i]) + { + case (byte)'\r': + case (byte)'\n': + { + // OK + break; + } + default: + { + throw new ArgumentException( + $"NewLineChars can only contains carriage return (U+000A), and line feed (U+000D) according to RFC8259, but char at {i} is U+00{value.Span[i]:X2}.", + "value" + ); + } + } + } + + this._newLineChars = value; + } + } + + public bool IsPrettyPrint { get; set; } + + public ReadOnlyMemory AdditionalEscapeTargetChars { get; set; } + + public bool EscapesHorizontalTab { get; set; } = true; + public bool EscapesPrivateUseCharactors { get; set; } = true; + + internal Action, JsonEncoderOptions>? SingleInfinityFormatter; + internal Action, JsonEncoderOptions>? DoubleInfinityFormatter; + internal Action, JsonEncoderOptions>? SingleNaNFormatter; + internal Action, JsonEncoderOptions>? DoubleNaNFormatter; + + public JsonEncoderOptionsBuilder() { } + + public JsonEncoderOptionsBuilder SetCustomInfinityFormatter( + Action, JsonEncoderOptions> singleInfinityFormatter, + Action, JsonEncoderOptions> doubleInfinityFormatter + ) + { + this.SingleInfinityFormatter = Ensure.NotNull(singleInfinityFormatter); + this.DoubleInfinityFormatter = Ensure.NotNull(doubleInfinityFormatter); + this._infinityHandling = InfinityHandling.Custom; + return this; + } + + public JsonEncoderOptionsBuilder SetCustomNaNFormatter( + Action, JsonEncoderOptions> singleNaNFormatter, + Action, JsonEncoderOptions> doubleNaNFormatter + ) + { + this.SingleNaNFormatter = Ensure.NotNull(singleNaNFormatter); + this.DoubleNaNFormatter = Ensure.NotNull(doubleNaNFormatter); + this._nanHandling = NaNHandling.Custom; + return this; + } + + public JsonEncoderOptionsBuilder WithHtmlCharactorEscaping() + { + this.AdditionalEscapeTargetChars = JsonCharactor.ShouldBeEscaped; + return this; + } + + public JsonEncoderOptionsBuilder WithoutHorizontalTabEscaping() + { + this.EscapesHorizontalTab = false; + return this; + } + + public JsonEncoderOptionsBuilder WithoutPrivateUseCharsEscaping() + { + this.EscapesPrivateUseCharactors = false; + return this; + } + + public JsonEncoderOptions Build() => new JsonEncoderOptions(this); + } +} diff --git a/src/MsgPack.Json/Json/JsonEscapeSequence.cs b/src/MsgPack.Json/Json/JsonEscapeSequence.cs new file mode 100644 index 000000000..ef8af6318 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonEscapeSequence.cs @@ -0,0 +1,26 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; + +namespace MsgPack.Json +{ + /// + /// Defines known escape sequence int UTF-8 and related structures. + /// + internal static class JsonEscapeSequence + { + public static ReadOnlySpan Unicode => new[] { (byte)'\\', (byte)'u' }; + public static ReadOnlySpan ReverseSolidous => new[] { (byte)'\\', (byte)'\\' }; + public static ReadOnlySpan Quatation => new[] { (byte)'\\', (byte)'"' }; + public static ReadOnlySpan Tab => new[] { (byte)'\\', (byte)'t' }; + public static ReadOnlySpan CarriageReturn => new[] { (byte)'\\', (byte)'r' }; + public static ReadOnlySpan LineFeed => new[] { (byte)'\\', (byte)'n' }; + public static ReadOnlySpan BackSpace => new[] { (byte)'\\', (byte)'b' }; + public static ReadOnlySpan FormFeed => new[] { (byte)'\\', (byte)'f' }; + + public static readonly StandardFormat UnicodeFormat = new StandardFormat('X', 4); + } +} diff --git a/src/MsgPack.Json/Json/JsonFormatter.cs b/src/MsgPack.Json/Json/JsonFormatter.cs new file mode 100644 index 000000000..084622065 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonFormatter.cs @@ -0,0 +1,36 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Buffers.Text; + +namespace MsgPack.Json +{ + /// + /// Defines common JSON formatting logic. + /// + internal static class JsonFormatter + { + public static void WriteNull(IBufferWriter buffer) + => buffer.Write(JsonTokens.Null); + + public static void Format(float value, IBufferWriter buffer, JsonEncoderOptions options) + { + var span = buffer.GetSpan(0); + while (!Utf8Formatter.TryFormat(value, span, out _)) + { + span = buffer.GetSpan((span.Length + 1) * 2); + } + } + + public static void Format(double value, IBufferWriter buffer, JsonEncoderOptions options) + { + var span = buffer.GetSpan(0); + while (!Utf8Formatter.TryFormat(value, span, out _)) + { + span = buffer.GetSpan((span.Length + 1) * 2); + } + } + } +} diff --git a/src/MsgPack.Json/Json/JsonNumberTokens.cs b/src/MsgPack.Json/Json/JsonNumberTokens.cs new file mode 100644 index 000000000..e8d9b1c00 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonNumberTokens.cs @@ -0,0 +1,95 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack.Json +{ + /// + /// Defines JSON number decoding related binary constants which are stored as native BLOB. + /// + internal static class JsonNumberTokens + { + public static readonly byte MinusSign = (byte)'-'; + public static readonly byte PlusSign = (byte)'+'; + public static readonly byte SingleByteUnicodeMinusSign = MinusSign; + public static readonly byte[][] MultiByteUnicodeMinusSigns = + new[] + { + new byte[] { 0xE2, 0x81, 0xBB }, // U+207B SUPERSCRIPT MINUS SIGN + new byte[] { 0xE2, 0x82, 0x8B }, // U+208B SUBSCRIPT MINUS SIGN + new byte[] { 0xE2, 0x88, 0x92 }, // U+2212 MINUS SIGN + new byte[] { 0xEF, 0xB9, 0xA3 }, // U+FE63 SMALL HYPHEN-MINUS + new byte[] { 0xEF, 0xBC, 0x8D }, // U+FF0D FULLWIDTH HYPHEN-MINUS + }; + public static readonly byte SingleByteUnicodePlus = PlusSign; + public static readonly byte[][] MultiByteUnicodePlusSigns = + new[] + { + new byte[] { 0xE2, 0x81, 0xBA }, // U+207A SUPERSCRIPT PLUS SIGN + new byte[] { 0xE2, 0x82, 0x8A }, // U+208A SUBSCRIPT PLUS SIGN + new byte[] { 0xEF, 0xAC, 0xA9 }, // U+FB29 HEBREW LETTER ALTERNATIVE PLUS SIGN + new byte[] { 0xEF, 0xB9, 0xA2 }, // U+FE62 SMALL PLUS SIGN + new byte[] { 0xEF, 0xBC, 0x8B }, // U+FE0B FULLWIDTH PLUS SIGN + }; + public static ReadOnlySpan Digits => new byte[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; + public static ReadOnlySpan NonZeroDigits => new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; + public static ReadOnlySpan ExponentialIndicators => new[] { (byte)'E', (byte)'e' }; + public static ReadOnlySpan PlusMinusSigns => new[] { (byte)'-', (byte)'+' }; + public static readonly byte[][] MultiByteUnicodePlusMinusSigns = + new[] + { + new byte[] { 0xE2, 0x81, 0xBB }, // U+207B SUPERSCRIPT MINUS SIGN + new byte[] { 0xE2, 0x82, 0x8B }, // U+208B SUBSCRIPT MINUS SIGN + new byte[] { 0xE2, 0x88, 0x92 }, // U+2212 MINUS SIGN + new byte[] { 0xEF, 0xB9, 0xA3 }, // U+FE63 SMALL HYPHEN-MINUS + new byte[] { 0xEF, 0xBC, 0x8D }, // U+FF0D FULLWIDTH HYPHEN-MINUS + new byte[] { 0xE2, 0x81, 0xBA }, // U+207A SUPERSCRIPT PLUS SIGN + new byte[] { 0xE2, 0x82, 0x8A }, // U+208A SUBSCRIPT PLUS SIGN + new byte[] { 0xEF, 0xAC, 0xA9 }, // U+FB29 HEBREW LETTER ALTERNATIVE PLUS SIGN + new byte[] { 0xEF, 0xB9, 0xA2 }, // U+FE62 SMALL PLUS SIGN + new byte[] { 0xEF, 0xBC, 0x8B }, // U+FE0B FULLWIDTH PLUS SIGN + }; + public static ReadOnlySpan SingleByteUnicodeExponentialIndicators => ExponentialIndicators; + public static readonly byte[][] MultiByteUnicodeExponentialIndicators = + new[] + { + new byte[] { 0xE1, 0xB4, 0xB1 }, // U+1D31 MODIFIER LETTER CAPITAL E + new byte[] { 0xE1, 0xB5, 0x89 }, // U+1D49 MODIFIER LETTER SMALL E + new byte[] { 0xE2, 0x82, 0x91 }, // U+2091 LATIN SUBSCRIPT SMALL LETTER E + new byte[] { 0xE2, 0x84, 0xAF }, // U+212F SCRIPT SMALL E + new byte[] { 0xE2, 0x84, 0xB0 }, // U+2130 SCRIPT CAPITAL E + new byte[] { 0xE2, 0x85, 0x87 }, // U+2147 DOUBLE-STRUCK ITALIC SMALL E + new byte[] { 0xE2, 0x92, 0xBA }, // U+24BA CIRCLED LATIN CAPITAL LETTER E + new byte[] { 0xE2, 0x93, 0x94 }, // U+24D4 CIRCLED LATIN SMALL LETTER E + new byte[] { 0xEF, 0xBC, 0xA5 }, // U+FF25 FULLWIDTH LATIN CAPITAL LETTER E + new byte[] { 0xEF, 0xBD, 0x85 }, // U+FF45 FULLWIDTH LATIN SMALL LETTER E + new byte[] { 0xF0, 0x9D, 0x90, 0x84 }, // U+1D404 MATHEMATICAL BOLD CAPITAL E + new byte[] { 0xF0, 0x9D, 0x90, 0x9E }, // U+1D41E ATHEMATICAL BOLD SMALL E + new byte[] { 0xF0, 0x9D, 0x90, 0xB8 }, // U+1D438 MATHEMATICAL ITALIC CAPITAL E + new byte[] { 0xF0, 0x9D, 0x91, 0x92 }, // U+1D452 MATHEMATICAL ITALIC SMALL E + new byte[] { 0xF0, 0x9D, 0x91, 0xAC }, // U+1D46C MATHEMATICAL BOLD ITALIC CAPITAL E + new byte[] { 0xF0, 0x9D, 0x92, 0x86 }, // U+1D486 MATHEMATICAL BOLD ITALIC SMALL E + new byte[] { 0xF0, 0x9D, 0x93, 0x94 }, // U+1D4D4 MATHEMATICAL BOLD SCRIPT CAPITAL E + new byte[] { 0xF0, 0x9D, 0x93, 0xAE }, // U+1D4EE MATHEMATICAL BOLD SCRIPT SMALL E + new byte[] { 0xF0, 0x9D, 0x94, 0x88 }, // U+1D508 MATHEMATICAL FRAKTUR CAPITAL E + new byte[] { 0xF0, 0x9D, 0x94, 0xA2 }, // U+1D522 MATHEMATICAL FRAKTUR SMALL E + new byte[] { 0xF0, 0x9D, 0x94, 0xBC }, // U+1D53C MATHEMATICAL DOUBLE-STRUCK CAPITAL E + new byte[] { 0xF0, 0x9D, 0x95, 0x96 }, // U+1D556 MATHEMATICAL DOUBLE-STRUCK SMALL E + new byte[] { 0xF0, 0x9D, 0x95, 0xB0 }, // U+1D570 MATHEMATICAL BOLD FRAKTUR CAPITAL E + new byte[] { 0xF0, 0x9D, 0x96, 0x8A }, // U+1D58A MATHEMATICAL BOLD FRAKTUR SMALL E + new byte[] { 0xF0, 0x9D, 0x96, 0xA4 }, // U+1D5A4 MATHEMATICAL SANS-SERIF CAPITAL E + new byte[] { 0xF0, 0x9D, 0x96, 0xBE }, // U+1D5BE MATHEMATICAL SANS-SERIF SMALL E + new byte[] { 0xF0, 0x9D, 0x97, 0x98 }, // U+1D5D8 MATHEMATICAL SANS-SERIF BOLD CAPITAL E + new byte[] { 0xF0, 0x9D, 0x97, 0xB2 }, // U+1D5F2 MATHEMATICAL SANS-SERIF BOLD SMALL E + new byte[] { 0xF0, 0x9D, 0x98, 0x8C }, // U+1D60C MATHEMATICAL SANS-SERIF ITALIC CAPITAL E + new byte[] { 0xF0, 0x9D, 0x98, 0xA6 }, // U+1D626 MATHEMATICAL SANS-SERIF ITALIC SMALL E + new byte[] { 0xF0, 0x9D, 0x99, 0x80 }, // U+1D640 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E + new byte[] { 0xF0, 0x9D, 0x99, 0x9A }, // U+1D65A MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E + new byte[] { 0xF0, 0x9D, 0x99, 0xB4 }, // U+1D674 MATHEMATICAL MONOSPACE CAPITAL E + new byte[] { 0xF0, 0x9D, 0x9A, 0x8E }, // U+1D68E MATHEMATICAL MONOSPACE SMALL E + new byte[] { 0xF0, 0x9F, 0x84, 0xB4 }, // U+1F134 SQUARED LATIN CAPITAL LETTER E + }; + } +} diff --git a/src/MsgPack.Json/Json/JsonOptionsValidation.cs b/src/MsgPack.Json/Json/JsonOptionsValidation.cs new file mode 100644 index 000000000..a74f29922 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonOptionsValidation.cs @@ -0,0 +1,61 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Runtime.CompilerServices; + +namespace MsgPack.Json +{ + /// + /// Defines common validation logic for option builders. + /// + internal static class JsonOptionsValidation + { + public static InfinityHandling EnsureKnownNonCustom(InfinityHandling value, [CallerArgumentExpression("value")]string paramName = null!) + { + switch (value) + { + case InfinityHandling.Default: + case InfinityHandling.Error: + case InfinityHandling.MinMax: + { + break; + } + case InfinityHandling.Custom: + { + throw new ArgumentException(paramName, $"Value 'Custom' cannot be set via property setter."); + } + default: + { + throw new ArgumentOutOfRangeException(paramName, $"Value '{value:d}' is not known enum value of enum type '{typeof(InfinityHandling)}'"); + } + } + + return value; + } + + public static NaNHandling EnsureKnownNonCustom(NaNHandling value, [CallerArgumentExpression("value")]string paramName = null!) + { + switch (value) + { + case NaNHandling.Default: + case NaNHandling.Error: + case NaNHandling.Null: + { + break; + } + case NaNHandling.Custom: + { + throw new ArgumentException(paramName, $"Value 'Custom' cannot be set via property setter."); + } + default: + { + throw new ArgumentOutOfRangeException(paramName, $"Value '{value:d}' is not known enum value of enum type '{typeof(NaNHandling)}'"); + } + } + + return value; + } + } +} diff --git a/src/MsgPack.Json/Json/JsonParseOptions.cs b/src/MsgPack.Json/Json/JsonParseOptions.cs new file mode 100644 index 000000000..fc7f5f6c1 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonParseOptions.cs @@ -0,0 +1,95 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack.Json +{ + /// + /// Defines JSON parser options in deserialization. + /// + [Flags] + public enum JsonParseOptions + { + /// + /// No options specified. This should be fastest. + /// + None = 0, + + /// + /// Allows single line comment which starts with # outside of string value. + /// + AllowHashSingleLineComment = 0x1, + + /// + /// Allows single line comment which starts with // outside of string value. + /// + AllowDoubleSolidousSingleLineComment = 0x2, + + /// + /// Allows multi line comment which starts with /* and ends with */ outside of string value. + /// + AllowMultilineComment = 0x4, + + /// + /// Allows Unicode whitespace chars such as 1/4 spacing and full width space, and treats them as ASCII whitespace. + /// + AllowUnicodeWhitespace = 0x10, + + /// + /// Allows all trivial tokens. + /// + AllowAllTrivias = 0xFF, + + /// + /// Allows +NaN and -NaN as valid number. + /// + AllowNaN = 0x100, + + /// + /// Allows +Infinity and -Infinity as valid number. + /// + AllowInfinity = 0x200, + + /// + /// Allows undefined as valid value, and treats it as null. + /// + AllowUndefined = 0x1000, + + /// + /// Allows all irregal values which is not valid in RFC 8259 but is valid as ECMA Script literal. + /// + AllowIrregalValues = 0xFF00, + + /// + /// Allows = as object key-valid separator as well as :. + /// + AllowEqualSignSeparator = 0x10000, + + /// + /// Allows ; as collection item delimiter as well as ,. + /// + AllowSemicolonDelimiter = 0x20000, + + /// + /// Allows extra trailing comma after last collection item. + /// + AllowExtraComma = 0x40000, + + /// + /// Allows ' for string value quotation instead of ". + /// + AllowSingleQuotationString = 0x100000, + + /// + /// Allows all known syntax errors. + /// + AllowWellknownSyntaxErrors = 0xFF0000, + + /// + /// Allows all grammer errors. + /// + AllowAllErrors = unchecked((int)0xFFFFFFFF) + } +} diff --git a/src/MsgPack.Json/Json/JsonStringTokens.cs b/src/MsgPack.Json/Json/JsonStringTokens.cs new file mode 100644 index 000000000..89334c75b --- /dev/null +++ b/src/MsgPack.Json/Json/JsonStringTokens.cs @@ -0,0 +1,43 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack.Json +{ + /// + /// Defines JSON string decoding related binary constants which are stored as native BLOB. + /// + internal static class JsonStringTokens + { + public static ReadOnlySpan AnyQuotations => new[] { (byte)'"', (byte)'\'' }; + public static ReadOnlySpan DoubleQuotation => AnyQuotations.Slice(0, 1); + public static ReadOnlySpan SingleQuotationDelimiters => new[] { (byte)'\'', (byte)'\\' }; + public static ReadOnlySpan DoubleQuotationDelimiters => new[] { (byte)'"', (byte)'\\' }; + public static readonly byte UnescapedCharactorsOffset = UnescapedCharactors[0]; + public static ReadOnlySpan UnescapedCharactors => + new byte[] + { + (byte)'"', // U+0022 " + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, // U+0023-U+002E + (byte)'/', // U+002F / + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, // U+0030-U+003F + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // U+0040-U+004F + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, // U+0050-U+005B + (byte)'\\', // U+005C \ + 0xBD, 0xBE, 0xBF, // U+005D-U+005F + 0xC0, 0xC1, // U+0060-U+0061 + (byte)'\b', // U+0062 -> U+0008 Back Space + 0xC3, 0xC4, 0xC5, // U+0063-U+0065 + (byte)'\f', // U+0066 -> U+000C Form Feed + 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, // UL0067-U+006D + (byte)'\n', // U+006E -> U+000A Line Feed + 0xCF, // U+006F + 0xD0, 0xD1, // U+0070-U+0071 + (byte)'\r', // U+0072 -> U+000D Carriage Return + 0xD3, // U+0073 + (byte)'\t', // U+0074 -> U+0009 Tab + }; + } +} diff --git a/src/MsgPack.Json/Json/JsonThrow.cs b/src/MsgPack.Json/Json/JsonThrow.cs new file mode 100644 index 000000000..aa6f09536 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonThrow.cs @@ -0,0 +1,117 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Globalization; +using System.Linq; +using System.Text; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + internal static class JsonThrow + { + public static void TooShortUtf8() + => throw new FormatException($"Input UTF-8 sequence is invalid. The sequence unexpectedly ends."); + + public static CollectionType CollectionHeaderDecodingIsNotSupported(out long itemsCount, out int requestHint) + => throw new NotSupportedException("JSON does not support collection length."); + + public static long CollectionHeaderDecodingIsNotSupported(out int requestHint) + => throw new NotSupportedException("JSON does not support collection length."); + + public static void DrainIsNotSupported(out int requestHint) + => throw new NotSupportedException("JSON does not support collection length."); + + public static void MalformedUtf8(in ReadOnlySpan sequence, long position) + => throw new FormatException($"Input UTF-8 has invalid sequence {BitConverter.ToString(sequence.ToArray())} at position {position:#,0}."); + + public static void MalformedUtf8(in ReadOnlySequence sequence, long position) + => throw new FormatException($"Input UTF-8 has invalid sequence {BitConverter.ToString(sequence.ToArray())} at position {position:#,0}."); + + public static void SurrogateCharInUtf8(long position, int codePoint) + => throw new FormatException($"Input UTF-8 has surrogate charactor U+{codePoint:X4} at position {position:#,0}."); + + public static void IsNotArrayNorObject(in ReadOnlySequence sequence, long position) + => throw new FormatException($"Char '{Stringify(sequence)}' at position {position:#,0} is not start of array nor object."); + + public static void IsNotArray(long position) + => throw new FormatException($"Char '{{' at position {position:#,0} is not start of array."); + + public static void IsNotObject(long position) + => throw new FormatException($"Char '[' at position {position:#,0} is not start of object."); + + public static void IsNotType(Type type, in ReadOnlySequence unit, long position) + => throw new FormatException($"Char '{Stringify(unit)}' at position {position:#,0} is not valid for {type} value."); + + public static void TooLongNumber(long numberLength, long maxLength, long position) + => throw new FormatException($"The number at position {position:#,0} has {numberLength:#,0} charactors, but maximum allowed length is {maxLength:#,0}."); + + public static void IsNotStringStart(long position, ReadOnlySpan validQuotations) + { + if (validQuotations.Length == 1) + { + throw new FormatException($"String must starts with '{(char)validQuotations[0]}' (U+00{validQuotations[0]:X2}) at {position:#,0}."); + } + else + { + throw new FormatException($"String must starts with one of [{String.Join(", ", validQuotations.ToArray().Select(b => $"'{(char)b}'(U + 00{b:X2})"))}] at {position:#,0}."); + } + } + + public static void InvalidEscapeSequence(long position, byte escaped) + => throw new FormatException($"Escape sequence '\\{(char)escaped}' at {position:#,0} is not valid."); + + public static void InvalidUnicodeEscapeSequence(long position, Span chars) + => throw new FormatException($"Escape sequence '\\u{Encoding.UTF8.GetString(chars)}' at {position:#,0} is not valid."); + + public static void OrphanSurrogate(long position, int codePoint) + => throw new FormatException($"Surrogate char U+{codePoint:X4} at {position:#,0} is not valid."); + + public static void InvalidBase64(long position) + => throw new FormatException($"String at {position:#,0} is not valid BASE64 sequence."); + + public static void UnexpectedToken(long position, byte token) + => throw new FormatException($"A token {(token >= 0x80 ? $"0x{token:X2}" : (token < 0x7F && token >= 0x20 ? $"'{(char)token}'" : $"U+00{token:X2}"))} is not expected at {position:#,0}."); + + private static string Stringify(in ReadOnlySequence unit) + { + var buffer = new StringBuilder(); + foreach (var rune in Encoding.UTF8.GetString(unit).EnumerateRunes()) + { + var category = Rune.GetUnicodeCategory(rune); + switch (category) + { + case UnicodeCategory.Control: + case UnicodeCategory.Format: + case UnicodeCategory.LineSeparator: + case UnicodeCategory.ModifierLetter: + case UnicodeCategory.ModifierSymbol: + case UnicodeCategory.NonSpacingMark: + case UnicodeCategory.OtherNotAssigned: + case UnicodeCategory.ParagraphSeparator: + case UnicodeCategory.SpacingCombiningMark: + case UnicodeCategory.Surrogate: + { + buffer.Append($"(U+{rune.Value:X4}, {category})"); + break; + } + case UnicodeCategory.SpaceSeparator: + { + buffer.Append($"'{rune}'(U+{rune.Value:X4}, {category})"); + break; + } + default: + { + buffer.Append('\'').Append(rune).Append('\''); + break; + } + } + } + + return buffer.ToString(); + } + } +} diff --git a/src/MsgPack.Json/Json/JsonTokens.cs b/src/MsgPack.Json/Json/JsonTokens.cs new file mode 100644 index 000000000..0ac1260dd --- /dev/null +++ b/src/MsgPack.Json/Json/JsonTokens.cs @@ -0,0 +1,29 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack.Json +{ + /// + /// Defines JSON tokens as UTF-8 byte blob data. + /// + internal static class JsonTokens + { + // Utilizes C# optimization to read data directly from blob metadata without array allocation. + public static ReadOnlySpan Null => new[] { (byte)'n', (byte)'u', (byte)'l', (byte)'l' }; + public static ReadOnlySpan True => new[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; + public static ReadOnlySpan False => new[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; + public static ReadOnlySpan ArrayStart => new[] { (byte)'[' }; + public static ReadOnlySpan ArrayEnd => new[] { (byte)']' }; + public static ReadOnlySpan MapStart => new[] { (byte)'{' }; + public static ReadOnlySpan MapEnd => new[] { (byte)'}' }; + public static ReadOnlySpan Whitespace => new[] { (byte)' ' }; + public static ReadOnlySpan Comma => new[] { (byte)',' }; + public static ReadOnlySpan Colon => new[] { (byte)':' }; + public static ReadOnlySpan Quatation => new[] { (byte)'"' }; + + public static ReadOnlySpan Undefined => new[] { (byte)'u', (byte)'n', (byte)'d', (byte)'e', (byte)'f', (byte)'i', (byte)'n', (byte)'e', (byte)'d' }; + } +} diff --git a/src/MsgPack.Json/Json/JsonTriviaTokens.cs b/src/MsgPack.Json/Json/JsonTriviaTokens.cs new file mode 100644 index 000000000..89a3552f1 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonTriviaTokens.cs @@ -0,0 +1,48 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack.Json +{ + /// + /// Defines JSON trivials decoding related binary constants which are stored as native BLOB. + /// + internal static class JsonTriviaTokens + { + public static ReadOnlySpan StandardWhitespaces => new[] { (byte)' ', (byte)'\t', (byte)'\r', (byte)'\n' }; + public static ReadOnlySpan NewLine => new[] { (byte)'\r', (byte)'\n' }; + public static ReadOnlySpan SingleByteUnicodeWhitespaces => + new[] { + (byte)' ', (byte)'\t', (byte)'\r', (byte)'\n', + (byte)'\u000B',(byte)'\u000C' + }; + public static readonly byte[][] MultiByteUnicodeWhitespaces = + new[] + { + new byte[] { 0xC2, 0x85 }, // U+0085 NEXT LINE (NEL) + new byte[] { 0xC2, 0xA0 }, // U+00A0 NO-BREAK SPACE + new byte[] { 0xE1, 0x9A, 0x80 }, // U+1680 OGHAM SPACE MARK + new byte[] { 0xE1, 0x9A, 0x80 }, // U+2000 EN QUAD + new byte[] { 0xE2, 0x80, 0x81 }, // U+2001 EM QUAD + new byte[] { 0xE2, 0x80, 0x82 }, // U+2002 EN SPACE + new byte[] { 0xE2, 0x80, 0x83 }, // U+2003 EM SPACE + new byte[] { 0xE2, 0x80, 0x84 }, // U+2004 THREE-PER-EM SPACE + new byte[] { 0xE2, 0x80, 0x85 }, // U+2005 FOUR-PER-EM SPACE + new byte[] { 0xE2, 0x80, 0x86 }, // U+2006 SIX-PER-EM SPACE + new byte[] { 0xE2, 0x80, 0x87 }, // U+2007 FIGURE SPACE + new byte[] { 0xE2, 0x80, 0x88 }, // U+2008 PUNCTUATION SPACE + new byte[] { 0xE2, 0x80, 0x89 }, // U+2009 THIN SPACE + new byte[] { 0xE2, 0x80, 0x8A }, // U+200A HAIR SPACE + new byte[] { 0xE2, 0x80, 0xA8 }, // U+2028 LINE SEPARATOR + new byte[] { 0xE2, 0x80, 0xA9 }, // U+2029 PARAGRAPH SEPARATOR + new byte[] { 0xE2, 0x80, 0xAF }, // U+202F NARROW NO-BREAK SPACE + new byte[] { 0xE2, 0x81, 0x9F }, // U+205F MEDIUM MATHEMATICAL SPACE + new byte[] { 0xEe, 0x80, 0x80 }, // U+3000 IDEOGRAPHIC SPACE + }; + public static ReadOnlySpan SingleLineCommentStart2 => new byte[] { (byte)'/', (byte)'/' }; + public static ReadOnlySpan MultiLineCommentStart => new byte[] { (byte)'/', (byte)'*' }; + public static ReadOnlySpan MultiLineCommentEnd => new byte[] { (byte)'*', (byte)'/' }; + } +} diff --git a/src/MsgPack.Json/Json/NaNHandling.cs b/src/MsgPack.Json/Json/NaNHandling.cs new file mode 100644 index 000000000..fe7e95b66 --- /dev/null +++ b/src/MsgPack.Json/Json/NaNHandling.cs @@ -0,0 +1,32 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Json +{ + /// + /// Defines NaN handling in JSON serialization to be compliant with RFC 8259. + /// + public enum NaNHandling + { + /// + /// Use system default setting. See for current default. + /// + Default = 0, + + /// + /// Use null for NaN. + /// + Null = 1, + + /// + /// Throws exception conservatively. + /// + Error = 2, + + /// + /// Use own custom formatting logic. + /// + Custom = 3 + } +} diff --git a/src/MsgPack.Json/Json/SimpleJsonDecoder.cs b/src/MsgPack.Json/Json/SimpleJsonDecoder.cs new file mode 100644 index 000000000..9fd56f598 --- /dev/null +++ b/src/MsgPack.Json/Json/SimpleJsonDecoder.cs @@ -0,0 +1,27 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack.Json +{ + /// + /// Simple implementation which is optimized for RFC compliant JSON parsing. + /// + internal sealed class SimpleJsonDecoder : JsonDecoder + { + public SimpleJsonDecoder(JsonDecoderOptions options) + : base(options) { } + + protected override void ReadTrivia(in SequenceReader source, out ReadOnlySequence trivia) + { + var offset = source.Consumed; + var consumed = source.AdvancePastAny(JsonTriviaTokens.StandardWhitespaces); + trivia = source.Sequence.Slice(offset, consumed); + } + + protected override bool TryReadNull(in SequenceReader source) + => source.IsNext(JsonTokens.Null, advancePast: true); + } +} diff --git a/src/MsgPack.Json/Json/StringBuilderBufferWriter.cs b/src/MsgPack.Json/Json/StringBuilderBufferWriter.cs new file mode 100644 index 000000000..649d095ae --- /dev/null +++ b/src/MsgPack.Json/Json/StringBuilderBufferWriter.cs @@ -0,0 +1,141 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Json +{ + /// + /// wrapping . + /// + /// + /// method returns internal char buffer to the pool, so it is important to ensure calling it. + /// + internal sealed class StringBuilderBufferWriter : IBufferWriter, IDisposable + { + private readonly ArrayPool _bufferPool; + private readonly bool _clearsArray; + private readonly StringBuilder _stringBuilder; + private char[] _buffer; + private int _currentOffset; + + private int CurrentBufferSize => this._buffer.Length - this._currentOffset; + + public StringBuilderBufferWriter(StringBuilder stringBuilder, JsonDecoderOptions options) + { + this._stringBuilder = stringBuilder; + this._bufferPool = options.CharBufferPool; + this._clearsArray = options.ClearsBuffer; + this._buffer = this._bufferPool.Rent(options.MaxCharBufferLength); + } + + public void Dispose() + { + var array = Interlocked.Exchange(ref this._buffer, null!); + if (array != null) + { + this._bufferPool.Return(array, this._clearsArray); + } + } + + public override string ToString() + { + this.Flush(this._currentOffset); + return this._stringBuilder.ToString(); + } + + private unsafe void Flush(int length) + { + Debug.Assert(length <= this._buffer.Length, $"length ({length}) <= this._buffer.Length ({this._buffer.Length})"); + Debug.Assert(length <= this._currentOffset, $"length ({length}) <= this._currentOffset ({this._currentOffset})"); + + if (length == 0) + { + return; + } + + this._stringBuilder.Append(this._buffer, 0, length); + if (this._currentOffset > length) + { + // Compact + fixed (char* source = this._buffer) + fixed (char* destination = this._buffer) + { + Buffer.MemoryCopy(source + length, destination, this._buffer.Length, this._currentOffset - length); + } + } + + this._currentOffset = 0; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void Advance(int count) + { + if (this.CurrentBufferSize > count) + { + // Just advance offset. + this._currentOffset += count; + return; + } + + this.AdvanceToNewBuffer(count); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void AdvanceToNewBuffer(int count) + { + // We must prepare new buffer. + var remainingCount = count - this._currentOffset; + this.Flush(this._currentOffset); + if (remainingCount <= 0) + { + return; + } + + // Fill StringBuilder with zero. + this._stringBuilder.Append('\0', remainingCount); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Memory GetMemory(int sizeHint = 0) + { + if (sizeHint <= this.CurrentBufferSize) + { + return this._buffer.AsMemory(this._currentOffset); + } + + return this.GetMemorySlow(sizeHint); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private Memory GetMemorySlow(int sizeHint) + { + this.Flush(this._currentOffset); + Debug.Assert(this._currentOffset == 0, "this._currentOffset == 0"); + if (sizeHint <= this._buffer.Length) + { + return this._buffer.AsMemory(); + } + + // sizeHint is too large. So realloc buffer. + this._bufferPool.Return(this._buffer, this._clearsArray); + this._buffer = this._bufferPool.Rent(sizeHint); + return this._buffer.AsMemory(); + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public Span GetSpan(int sizeHint = 0) + => this.GetMemory(sizeHint).Span; + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void AppendUtf16CodePoint(int utf16CodePoint) + => this.GetSpan(1)[0] = (char)utf16CodePoint; + } +} diff --git a/src/MsgPack.Json/Json/Unicode.cs b/src/MsgPack.Json/Json/Unicode.cs new file mode 100644 index 000000000..281448db7 --- /dev/null +++ b/src/MsgPack.Json/Json/Unicode.cs @@ -0,0 +1,18 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Json +{ + /// + /// Defines Unicode related utilities. + /// + internal static class Unicode + { + public static bool IsLowSurrogate(int cp) + => cp >= 0xDC00u && cp <= 0xDFFFu; + + public static bool IsHighSurrogate(int cp) + => cp >= 0xD800u && cp <= 0xDBFFu; + } +} diff --git a/src/MsgPack.Json/MsgPack.Json.csproj b/src/MsgPack.Json/MsgPack.Json.csproj index 214660b44..d3a1baa67 100644 --- a/src/MsgPack.Json/MsgPack.Json.csproj +++ b/src/MsgPack.Json/MsgPack.Json.csproj @@ -18,4 +18,32 @@ + + + TextTemplatingFileGenerator + JsonDecoder.Nullables.cs + + + TextTemplatingFileGenerator + JsonDecoder.Numbers.cs + + + + + + + + + + True + True + JsonDecoder.Nullables.tt + + + True + True + JsonDecoder.Numbers.tt + + + From cb2b4c3482e30b87e936666b31fa6443079b4502 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 14 Jun 2020 23:33:13 +0900 Subject: [PATCH 19/82] Add devdoc for JSON deserialization --- ...onCollectionStatemachine.drawio.svg.drawio | 414 ++++++++++++++++++ 1 file changed, 414 insertions(+) create mode 100644 doc/dev/JsonCollectionStatemachine.drawio.svg.drawio diff --git a/doc/dev/JsonCollectionStatemachine.drawio.svg.drawio b/doc/dev/JsonCollectionStatemachine.drawio.svg.drawio new file mode 100644 index 000000000..484b7d2a2 --- /dev/null +++ b/doc/dev/JsonCollectionStatemachine.drawio.svg.drawio @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + +
+
+
match /\s*\[/
+
+
+
+ match /\s*\[/ +
+
+ + + + +
+
+
Invalid
+
+
+
+ Invalid +
+
+ + + + + + +
+
+
else
+
+
+
+ else +
+
+ + + + +
+
+
+ MaybeItem +
+
+
+
+ MaybeItem +
+
+ + + + + + +
+
+
Item
+
+
+
+ Item +
+
+ + + + + + + + +
+
+
(external)
+
+
+
+ (external) +
+
+ + + + + +
+
+
Array
+
+
+
+ Array +
+
+ + + + + +
+
+
match /\s*,/
+
+
+
+ match /\s*,/ +
+
+ + + +
+
+
else
+
+
+
+ else +
+
+ + + + + +
+
+
else
+
+
+
+ else +
+
+ + + + + + + +
+
+
match /\s*]/
+
+
+
+ match /\s*]/ +
+
+ + + +
+
+
match /\s*]/
+
+
+
+ match /\s*]/ +
+
+ + + + + + + +
+
+
+ Object +
+
+
+
+ Object +
+
+ + + + + + + + +
+
+
match /\s*\[/
+
+
+
+ match /\s*\[/ +
+
+ + + + +
+
+
Invalid
+
+
+
+ Invalid +
+
+ + + + + +
+
+
else
+
+
+
+ else +
+
+ + + + +
+
+
+ MaybeKey +
+
+
+
+ MaybeKey +
+
+ + + + + + +
+
+
Key
+
+
+
+ Key +
+
+ + + + + + + + +
+
+
(external)
+
+
+
+ (external) +
+
+ + + + + +
+
+
match /\s*,/
+
+
+
+ match /\s*,/ +
+
+ + + +
+
+
else
+
+
+
+ else +
+
+ + + + + +
+
+
else
+
+
+
+ else +
+
+ + + + + + + +
+
+
match /\s*}/
+
+
+
+ match /\s*}/ +
+
+ + + +
+
+
match /\s*]/
+
+
+
+ match /\s*]/ +
+
+ + + + + + + + +
+
+
Value
+
+
+
+ Value +
+
+ + + + + + + + +
+
+
(external)
+
+
+
+ (external) +
+
+ + + +
+
+
match /[:=]/
+
+
+
+ match /[:=]/ +
+
+ + +
+ + + + Viewer does not support full SVG 1.1 + + +
\ No newline at end of file From c70399e1dd3b83d265a45658727e05e37d61932f Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 15 Jun 2020 21:32:31 +0900 Subject: [PATCH 20/82] Add MessagePackEncoderOptions/MessagePackDecoderOptions Default property --- src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs | 2 ++ src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs b/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs index b4415c2a4..9472d0358 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs @@ -6,6 +6,8 @@ namespace MsgPack.Internal { public sealed class MessagePackDecoderOptions : DecoderOptions { + public static MessagePackDecoderOptions Default { get; } = new MessagePackDecoderOptionsBuilder().Build(); + internal MessagePackDecoderOptions(MessagePackDecoderOptionsBuilder builder) : base(builder) { } } diff --git a/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs b/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs index ad3efa2cf..dfc160c3b 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs @@ -1,4 +1,4 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. +// Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. @@ -6,6 +6,8 @@ namespace MsgPack.Internal { public sealed class MessagePackEncoderOptions : EncoderOptions { + public static MessagePackEncoderOptions Default { get; } = new MessagePackEncoderOptionsBuilder().Build(); + public MessagePackEncoderOptions(MessagePackEncoderOptionsBuilder builder) : base(builder) { } } } From 0e9a7f45187a501b14b4b450b3ae1302915b75a3 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 15 Jun 2020 21:32:46 +0900 Subject: [PATCH 21/82] WIP: initial benchmark --- .../Internal/StreamBufferWriter.cs | 1 + .../Internal/StreamReadOnlyMemoryProvider.cs | 1 + src/MsgPack.Core/_SampleObject.cs | 107 +++++++++++++++++- 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs b/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs index e320035ad..975db06c4 100644 --- a/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs +++ b/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs @@ -9,6 +9,7 @@ namespace MsgPack.Internal { +#warning TODO: Pubternal or CodeGen internal sealed class StreamBufferWriter : IBufferWriter, IAsyncDisposable { private readonly Stream _underlying; diff --git a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs index c09f44bc8..c67c81e08 100644 --- a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs +++ b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs @@ -9,6 +9,7 @@ namespace MsgPack.Internal { +#warning TODO: PubTernal or CodeGen internal sealed class StreamReadOnlyMemoryProvider { private readonly Stream _stream; diff --git a/src/MsgPack.Core/_SampleObject.cs b/src/MsgPack.Core/_SampleObject.cs index 9641a7143..ec77520f9 100644 --- a/src/MsgPack.Core/_SampleObject.cs +++ b/src/MsgPack.Core/_SampleObject.cs @@ -8,11 +8,12 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading.Tasks; +using MsgPack.Internal; using MsgPack.Serialization.Internal; -namespace MsgPack.Internal +namespace MsgPack.Samples { - internal sealed class SampleObject + public class SampleObject { public int Age { get; set; } public string Name { get; set; } = null!; @@ -41,7 +42,7 @@ internal sealed class SampleObject /// /// Sample hand made serializer. /// - internal sealed class SampleSerializer : IObjectSerializer + public sealed class SampleSerializer : IObjectSerializer { private bool UseArray { get; set; } @@ -1091,4 +1092,104 @@ public async ValueTask DeserializeToAsync(DeserializationOperationContext : IObjectSerializer + { + public void Serialize(in SerializationOperationContext context, int[] obj, IBufferWriter sink) + { + if (obj is null) + { + context.Encoder.EncodeNull(sink); + return; + } + + context.Encoder.EncodeArrayStart(obj.Length, sink, context.CollectionContext); + for(var i=0; i context, int[] obj, Stream streamSink) + { + throw new NotImplementedException(); + } + + public int[] Deserialize(in DeserializationOperationContext context, in SequenceReader source) + { + if (context.Decoder.FormatFeatures.CanCountCollectionItems) + { + var length = context.Decoder.DecodeArrayHeader(source); + var result = new int[length]; + for (var i = 0; i < result.Length; i++) + { + result[i] = context.Decoder.DecodeInt32(source); + } + context.Decoder.Drain(source, context.CollectionContext, 0); + return result; + } + else + { + var result = new List(); + var iterator = context.Decoder.DecodeArray(source); + while (!iterator.CollectionEnds(source)) + { + result.Add(context.Decoder.DecodeInt32(source)); + } + iterator.Drain(source); + return result.ToArray(); + } + } + + public ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource) + { + throw new NotImplementedException(); + } + + public void DeserializeTo(in DeserializationOperationContext context, in SequenceReader source, in int[] obj) + { + throw new NotImplementedException(); + } + + public ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, int[] obj) + { + throw new NotImplementedException(); + } + } + + public sealed class SampleInt32Serializer : IObjectSerializer + { + public void Serialize(in SerializationOperationContext context, int obj, IBufferWriter sink) + { + context.Encoder.EncodeInt32(obj, sink); + } + + public ValueTask SerializeAsync(SerializationOperationContext context, int obj, Stream streamSink) + { + throw new NotImplementedException(); + } + + public int Deserialize(in DeserializationOperationContext context, in SequenceReader source) + { + return context.Decoder.DecodeInt32(source); + } + + public ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource) + { + throw new NotImplementedException(); + } + + public void DeserializeTo(in DeserializationOperationContext context, in SequenceReader source, in int obj) + { + throw new NotImplementedException(); + } + + public ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, int obj) + { + throw new NotImplementedException(); + } + } } From 7ae74e547cbb784808dd6584d4cba3baea06940f Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 21 Jun 2020 01:09:35 +0900 Subject: [PATCH 22/82] Port benchmark script from MessagePack for C# --- test/Benchmark/Benchmark.csproj | 40 + test/Benchmark/LICENSE | 39 + .../Benchmark/BenchmarkConfig.cs | 190 +++ .../Benchmark/ExtensionMethods.cs | 153 ++ .../Benchmark/Fixture/BooleanValueFixture.cs | 19 + .../Benchmark/Fixture/ByteValueFixture.cs | 19 + .../Fixture/DateTimeOffsetValueFixture.cs | 20 + .../Benchmark/Fixture/DateTimeValueFixture.cs | 22 + .../Benchmark/Fixture/DecimalValueFixture.cs | 19 + .../Benchmark/Fixture/DoubleValueFixture.cs | 19 + .../Benchmark/Fixture/EnumValueFixture.cs | 26 + .../Fixture/ExpressionTreeFixture.cs | 255 ++++ .../Benchmark/Fixture/FloatValueFixture.cs | 19 + .../Benchmark/Fixture/GuidValueFixture.cs | 17 + .../Benchmark/Fixture/IValueFixture.cs | 14 + .../Benchmark/Fixture/IntValueFixture.cs | 19 + .../Benchmark/Fixture/LongValueFixture.cs | 19 + .../Fixture/OtherPrimitiveFixtures.cs | 71 + .../Benchmark/Fixture/ShortValueFixture.cs | 19 + .../Benchmark/Fixture/StringValueFixture.cs | 31 + .../Benchmark/IGenericEquality`1.cs | 12 + .../Benchmark/Models/AccessToken.cs | 44 + .../Benchmark/Models/AccountMerge.cs | 38 + .../Benchmark/Models/Answer.cs | 234 ++++ .../Benchmark/Models/Badge.cs | 75 + .../Benchmark/Models/Comment.cs | 145 ++ .../Benchmark/Models/Error.cs | 37 + .../Benchmark/Models/Event.cs | 57 + .../Benchmark/Models/Feed.cs | 693 +++++++++ .../Benchmark/Models/FlagOption.cs | 68 + .../Benchmark/Models/InboxItem.cs | 85 ++ .../Benchmark/Models/Info.cs | 274 ++++ .../Benchmark/Models/NetworkUser.cs | 78 ++ .../Benchmark/Models/Notification.cs | 70 + .../Benchmark/Models/Post.cs | 114 ++ .../Benchmark/Models/Privilege.cs | 37 + .../Benchmark/Models/Question.cs | 361 +++++ .../Benchmark/Models/QuestionTimeline.cs | 85 ++ .../Benchmark/Models/Reputation.cs | 74 + .../Benchmark/Models/ReputationHistory.cs | 85 ++ .../Benchmark/Models/Revision.cs | 110 ++ .../Benchmark/Models/SearchExcerpt.cs | 123 ++ .../Benchmark/Models/ShallowUser.cs | 112 ++ .../Benchmark/Models/SuggestedEdit.cs | 80 ++ .../MessagePackCsharp/Benchmark/Models/Tag.cs | 64 + .../Benchmark/Models/TagScore.cs | 37 + .../Benchmark/Models/TagSynonym.cs | 48 + .../Benchmark/Models/TagWiki.cs | 60 + .../Benchmark/Models/TopTag.cs | 52 + .../Benchmark/Models/User.cs | 219 +++ .../Benchmark/Models/UserTimeline.cs | 90 ++ .../Benchmark/Models/WritePermission.cs | 57 + .../Benchmark/SerializerBenchmark.cs | 1234 +++++++++++++++++ .../Benchmark/Serializers/BinaryFormatter.cs | 29 + .../Benchmark/Serializers/CerasSerializer.cs | 21 + .../Serializers/DataContractSerializer.cs | 29 + .../Serializers/FsPicklerSerializer.cs | 31 + .../Benchmark/Serializers/HagarSerializer.cs | 59 + .../Serializers/HyperionSerializer.cs | 31 + .../Benchmark/Serializers/JilSerializer.cs | 21 + .../Serializers/JsonNetSerializer.cs | 39 + .../Serializers/MessagePackSerializer.cs | 123 ++ .../Serializers/MsgPackCliSerializer.cs | 20 + .../Serializers/ProtobufSerializer.cs | 29 + .../Benchmark/Serializers/SerializerBase.cs | 14 + .../Serializers/SpanJsonSerializer.cs | 19 + .../Serializers/SystemTextJsonSerializer.cs | 20 + .../Serializers/Utf8JsonSerializer.cs | 19 + test/Benchmark/Readme.md | 14 + 69 files changed, 6500 insertions(+) create mode 100644 test/Benchmark/Benchmark.csproj create mode 100644 test/Benchmark/LICENSE create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/BenchmarkConfig.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/ExtensionMethods.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/BooleanValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ByteValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DateTimeOffsetValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DateTimeValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DecimalValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DoubleValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/EnumValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ExpressionTreeFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/FloatValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/GuidValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/IValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/IntValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/LongValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/OtherPrimitiveFixtures.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ShortValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Fixture/StringValueFixture.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/IGenericEquality`1.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/AccessToken.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/AccountMerge.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Answer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Badge.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Comment.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Error.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Event.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Feed.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/FlagOption.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/InboxItem.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Info.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/NetworkUser.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Notification.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Post.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Privilege.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Question.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/QuestionTimeline.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Reputation.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/ReputationHistory.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Revision.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/SearchExcerpt.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/ShallowUser.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/SuggestedEdit.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/Tag.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/TagScore.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/TagSynonym.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/TagWiki.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/TopTag.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/User.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/UserTimeline.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Models/WritePermission.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/SerializerBenchmark.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/BinaryFormatter.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/CerasSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/DataContractSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/FsPicklerSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/HagarSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/HyperionSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/JilSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/JsonNetSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MessagePackSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/ProtobufSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SerializerBase.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SpanJsonSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SystemTextJsonSerializer.cs create mode 100644 test/Benchmark/MessagePackCsharp/Benchmark/Serializers/Utf8JsonSerializer.cs create mode 100644 test/Benchmark/Readme.md diff --git a/test/Benchmark/Benchmark.csproj b/test/Benchmark/Benchmark.csproj new file mode 100644 index 000000000..c6d057c6d --- /dev/null +++ b/test/Benchmark/Benchmark.csproj @@ -0,0 +1,40 @@ + + + + Exe + netcoreapp3.1 + true + disable + + + + + + + + + + + + + + + + + + + + newmpcli + + + newmpcli + + + newmpcli + + + + + + + diff --git a/test/Benchmark/LICENSE b/test/Benchmark/LICENSE new file mode 100644 index 000000000..8519f42f4 --- /dev/null +++ b/test/Benchmark/LICENSE @@ -0,0 +1,39 @@ +MessagePack for C# + +MIT License + +Copyright (c) 2017 Yoshifumi Kawai and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--- + +lz4net + +Copyright (c) 2013-2017, Milosz Krajewski + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/BenchmarkConfig.cs b/test/Benchmark/MessagePackCsharp/Benchmark/BenchmarkConfig.cs new file mode 100644 index 000000000..1462ad452 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/BenchmarkConfig.cs @@ -0,0 +1,190 @@ +// 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 System.Collections.Immutable; +using System.Linq; +using Benchmark.Serializers; +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Exporters.Csv; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Order; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; + +namespace Benchmark +{ + public class BenchmarkConfig : ManualConfig + { + public BenchmarkConfig() + { + // Add(baseConfig.With(Runtime.Clr).With(Jit.RyuJit).With(Platform.X64)); + this.AddJob( + // run quickly:) + Job.ShortRun + .WithIterationCount(1) + .WithWarmupCount(1) + .WithRuntime(CoreRuntime.Core31) + .WithJit(Jit.RyuJit) + .WithPlatform(Platform.X64) + ).AddExporter( + MarkdownExporter.GitHub, + CsvExporter.Default + ).AddDiagnoser( + MemoryDiagnoser.Default + ).AddColumn( + new DataSizeColumn() + ); + + this.Orderer = new CustomOrderer(); + } + + public class CustomOrderer : IOrderer + { + public bool SeparateLogicalGroups => false; + + public IEnumerable GetExecutionOrder(ImmutableArray benchmarksCase) + { + return benchmarksCase; + } + + public string GetHighlightGroupKey(BenchmarkCase benchmarkCase) + { + return benchmarkCase.Descriptor.MethodIndex.ToString(); + } + + public string GetLogicalGroupKey(ImmutableArray allBenchmarksCases, BenchmarkCase benchmarkCase) + { + return null; + } + + public IEnumerable> GetLogicalGroupOrder(IEnumerable> logicalGroups) + { + return logicalGroups; + } + + public IEnumerable GetSummaryOrder(ImmutableArray benchmarksCases, Summary summary) + { + return benchmarksCases + .OrderBy(x => x.Descriptor.WorkloadMethod.Name) + .ThenBy(x => x.Parameters.Items.Select(y => y.Value).OfType().First().GetType().Name); + } + } + + public class DataSizeColumn : IColumn + { + public string Id => "DataSize"; + + public string ColumnName => "DataSize"; + + public bool AlwaysShow => true; + + public ColumnCategory Category => ColumnCategory.Custom; + + public int PriorityInCategory => int.MaxValue; + + public bool IsNumeric => true; + + public UnitType UnitType => UnitType.Size; + + public string Legend => null; + + public string GetValue(Summary summary, BenchmarkCase benchmarkCase) + { + return this.GetValue(summary, benchmarkCase, null); + } + + public string GetValue(Summary summary, BenchmarkCase benchmarkCase, SummaryStyle style) + { + System.Reflection.MethodInfo mi = benchmarkCase.Descriptor.WorkloadMethod; + if (mi.Name.Contains("Serialize")) + { + var instance = Activator.CreateInstance(mi.DeclaringType); + mi.DeclaringType.GetField("Serializer").SetValue(instance, benchmarkCase.Parameters[0].Value); + mi.DeclaringType.GetMethod("Setup").Invoke(instance, null); + + var bytes = (byte[])mi.Invoke(instance, null); + return ToHumanReadableSize(bytes.Length); + } + else + { + return "-"; + } + } + + public bool IsAvailable(Summary summary) + { + return true; + } + + public bool IsDefault(Summary summary, BenchmarkCase benchmarkCase) + { + return false; + } + + private static string ToHumanReadableSize(long size) + { + return ToHumanReadableSize(new long?(size)); + } + + private static string ToHumanReadableSize(long? size) + { + if (size == null) + { + return "NULL"; + } + + double bytes = size.Value; + + if (bytes <= 1024) + { + return bytes.ToString("f2") + " B"; + } + + bytes = bytes / 1024; + if (bytes <= 1024) + { + return bytes.ToString("f2") + " KB"; + } + + bytes = bytes / 1024; + if (bytes <= 1024) + { + return bytes.ToString("f2") + " MB"; + } + + bytes = bytes / 1024; + if (bytes <= 1024) + { + return bytes.ToString("f2") + " GB"; + } + + bytes = bytes / 1024; + if (bytes <= 1024) + { + return bytes.ToString("f2") + " TB"; + } + + bytes = bytes / 1024; + if (bytes <= 1024) + { + return bytes.ToString("f2") + " PB"; + } + + bytes = bytes / 1024; + if (bytes <= 1024) + { + return bytes.ToString("f2") + " EB"; + } + + bytes = bytes / 1024; + return bytes + " ZB"; + } + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/ExtensionMethods.cs b/test/Benchmark/MessagePackCsharp/Benchmark/ExtensionMethods.cs new file mode 100644 index 000000000..086187dcf --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/ExtensionMethods.cs @@ -0,0 +1,153 @@ +// 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 System.Linq; +using System.Reflection; + +namespace Benchmark +{ + public static class ExtensionMethods + { + public static bool TrueEqualsString(this IEnumerable a, IEnumerable b) + { + return a.SequenceEqual(b); + } + + public static bool TrueEqualsString(this string a, string b) + { + return a == b; + } + + public static bool TrueEquals(this T? a, T? b) + where T : struct + { + if (!a.HasValue && !b.HasValue) + { + return true; + } + + if (!a.HasValue) + { + return false; + } + + if (!b.HasValue) + { + return false; + } + + return a.Value.Equals(b.Value); + } + + public static bool TrueEquals(this T a, T b) + where T : class, IGenericEquality + { + if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) + { + return true; + } + + if (ReferenceEquals(a, null)) + { + return false; + } + + if (ReferenceEquals(b, null)) + { + return false; + } + + return a.Equals(b); + } + + public static bool TrueEqualsList(this IEnumerable a, IEnumerable b) + where T : class, IGenericEquality + { + return a.SequenceEqual(b, GenericEqualityComparer.Default); + } + + public static bool TrueEqualsListDynamic(this IEnumerable a, IEnumerable b) + where T : class, IGenericEquality + { + if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) + { + return true; + } + + if (ReferenceEquals(a, null)) + { + return false; + } + + if (ReferenceEquals(b, null)) + { + return false; + } + + using (IEnumerator e1 = a.GetEnumerator()) + using (IEnumerator e2 = b.GetEnumerator()) + { + while (true) + { + var e1Next = e1.MoveNext(); + var e2Next = e2.MoveNext(); + if (e1Next != e2Next) + { + return false; + } + + if (!e1Next && !e2Next) + { + break; + } + + T c1 = e1.Current; + dynamic c2 = e2.Current; + + if (c1 == null && c2 != null) + { + return false; + } + + if (c2 == null && c1 != null) + { + return false; + } + + if (!c1.EqualsDynamic(c2)) + { + return false; + } + } + } + + return true; + } + + public static bool IsTypedList(this Type type) + { + return + (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(IList<>)) || + type.GetTypeInfo().GetInterfaces().Any(i => + i.GetTypeInfo().IsGenericType && i.GetGenericTypeDefinition() == typeof(IList<>)); + } + + private class GenericEqualityComparer : IEqualityComparer + where T : class, IGenericEquality + { + public static readonly GenericEqualityComparer Default = new GenericEqualityComparer(); + + public bool Equals(T x, T y) + { + return x.TrueEquals(y); + } + + public int GetHashCode(T obj) + { + return 0; // not fast, but not really important here + } + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/BooleanValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/BooleanValueFixture.cs new file mode 100644 index 000000000..dfd0ce6ee --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/BooleanValueFixture.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class BooleanValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(bool); + + public object Generate() + { + return this.prng.Next() % 2 == 1; + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ByteValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ByteValueFixture.cs new file mode 100644 index 000000000..9b32dd953 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ByteValueFixture.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class ByteValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(byte); + + public object Generate() + { + return (byte)(this.prng.Next() & 0xFF); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DateTimeOffsetValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DateTimeOffsetValueFixture.cs new file mode 100644 index 000000000..e12fd57bc --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DateTimeOffsetValueFixture.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class DateTimeOffsetValueFixture : IValueFixture + { + private long lastValue; + + public Type Type { get; } = typeof(DateTimeOffset); + + public object Generate() + { + this.lastValue += 1000; + return DateTimeOffset.FromUnixTimeMilliseconds(this.lastValue); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DateTimeValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DateTimeValueFixture.cs new file mode 100644 index 000000000..0b484c1cc --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DateTimeValueFixture.cs @@ -0,0 +1,22 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class DateTimeValueFixture : IValueFixture + { + private long lastValue; + private static readonly long Offset = new DateTime(1970, 1, 1, 0, 0, 0).ToFileTime(); + + public Type Type { get; } = typeof(DateTime); + + public object Generate() + { + this.lastValue += 1000; + var dt = DateTime.FromFileTime(this.lastValue + Offset); + return dt; + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DecimalValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DecimalValueFixture.cs new file mode 100644 index 000000000..89d9dd047 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DecimalValueFixture.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class DecimalValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(decimal); + + public object Generate() + { + return this.prng.Next() + 0.66m; + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DoubleValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DoubleValueFixture.cs new file mode 100644 index 000000000..848cbd7e3 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/DoubleValueFixture.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class DoubleValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(double); + + public object Generate() + { + return this.prng.Next() + 0.5d; + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/EnumValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/EnumValueFixture.cs new file mode 100644 index 000000000..18e7dd0c2 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/EnumValueFixture.cs @@ -0,0 +1,26 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class EnumValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + private readonly string[] values; + + public EnumValueFixture(Type type) + { + this.Type = type; + this.values = Enum.GetNames(this.Type); + } + + public Type Type { get; } + + public object Generate() + { + return Enum.Parse(this.Type, this.values[this.prng.Next(this.values.Length)]); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ExpressionTreeFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ExpressionTreeFixture.cs new file mode 100644 index 000000000..86c9a6788 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ExpressionTreeFixture.cs @@ -0,0 +1,255 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Benchmark.Fixture +{ + public class ExpressionTreeFixture + { + private readonly ConcurrentDictionary> functorCache = + new ConcurrentDictionary>(); + + private readonly Dictionary valueFixtures = new Dictionary(); + + public ExpressionTreeFixture() + { + IValueFixture[] fixtures = new IValueFixture[] + { + new StringValueFixture(), + new IntValueFixture(), + new GuidValueFixture(), + new DateTimeOffsetValueFixture(), + new DateTimeValueFixture(), + new BooleanValueFixture(), + new DecimalValueFixture(), + new LongValueFixture(), + new FloatValueFixture(), + new DoubleValueFixture(), + new ByteValueFixture(), + new ShortValueFixture(), + new ByteArrayFixture(), + new SByteValueFixture(), + new UShortValueFixture(), + new UInt32ValueFixture(), + new UInt64ValueFixture(), + new CharValueFixture(), + }; + + foreach (IValueFixture item in fixtures) + { + this.valueFixtures.Add(item.Type, item); + } + } + + public object Create(Type type, int repeatCount = 1, int recursiveCount = 1) + { + Func functor = this.functorCache.GetOrAdd(type, this.AddFunctor); + return functor(repeatCount, recursiveCount); + } + + private Func AddFunctor(Type type) + { + ParameterExpression repeatCount = Expression.Parameter(typeof(int), "repeatCount"); + ParameterExpression recursiveCount = Expression.Parameter(typeof(int), "recursiveCount"); + var subExpressions = new List(); + ParameterExpression typedOutput = Expression.Variable(type, "typedOutput"); + if (this.valueFixtures.ContainsKey(type) || type.IsArray || type.IsTypedList()) + { + // they can be generated directly + Expression expression = this.GenerateValue(typedOutput, repeatCount, recursiveCount, type); + subExpressions.Add(expression); + } + else + { + subExpressions.Add(Expression.Assign(typedOutput, Expression.New(type))); + PropertyInfo[] typeProps = type.GetProperties(); + foreach (PropertyInfo propertyInfo in typeProps) + { + if (!propertyInfo.CanWrite) + { + continue; + } + + Type propertyType = propertyInfo.PropertyType; + var isRecursion = this.IsRecursion(type, propertyType) || this.IsRecursion(propertyType, type); + MemberExpression memberAccess = Expression.MakeMemberAccess(typedOutput, propertyInfo); + Expression expression = this.GenerateValue( + memberAccess, + repeatCount, + isRecursion ? Expression.Decrement(recursiveCount) : (Expression)recursiveCount, + propertyType); + subExpressions.Add(expression); + } + } + + LabelTarget returnTarget = Expression.Label(typeof(object)); + LabelExpression returnLabel = Expression.Label(returnTarget, Expression.Convert(typedOutput, typeof(object))); + subExpressions.Add(returnLabel); + BlockExpression block = Expression.Block(new[] { typedOutput }, subExpressions); + var lambda = Expression.Lambda>(block, repeatCount, recursiveCount); + return lambda.Compile(); + } + + private bool IsRecursion(Type parentType, Type type) + { + if (type == parentType) + { + return true; + } + + if (parentType.IsTypedList()) + { + Type childType = parentType.GetGenericArguments()[0]; + return this.IsRecursion(type, childType); + } + + if (parentType.IsArray) + { + Type elementType = parentType.GetElementType(); + return this.IsRecursion(type, elementType); + } + + if (Nullable.GetUnderlyingType(parentType) != null) + { + Type nullableType = Nullable.GetUnderlyingType(parentType); + return this.IsRecursion(type, nullableType); + } + + return false; + } + + private Expression GenerateValue(Expression generatedValue, Expression repeatCount, Expression recursiveCount, Type type) + { + var result = new List(); + if (this.valueFixtures.TryGetValue(type, out IValueFixture valueFixture)) + { + MethodInfo generateMethodInfo = valueFixture.GetType().GetMethod(nameof(IValueFixture.Generate)); + result.Add(Expression.Assign( + generatedValue, + Expression.Convert(Expression.Call(Expression.Constant(valueFixture), generateMethodInfo), generatedValue.Type))); + } + else if (type.IsTypedList()) + { + var expressionList = new List(); + Type elementType = type.GetGenericArguments()[0]; + expressionList.Add(Expression.Assign(generatedValue, Expression.New(type))); + ParameterExpression index = Expression.Parameter(typeof(int), "i"); + MethodInfo addMi = type.GetMethod("Add", new[] { elementType }); + ParameterExpression childValue = Expression.Parameter(elementType); + var loopBlock = new List + { + this.GenerateValue(childValue, repeatCount, recursiveCount, elementType), + Expression.Call(generatedValue, addMi, childValue), + }; + if (loopBlock.Count > 0) + { + BlockExpression loopContent = Expression.Block(new[] { childValue, index }, loopBlock); + expressionList.Add(ForLoop(index, repeatCount, loopContent)); + } + + result.Add(this.MakeIfExpression(recursiveCount, expressionList)); + } + else if (type.IsArray) + { + Type elementType = type.GetElementType(); + ParameterExpression index = Expression.Parameter(typeof(int), "i"); + var arrayList = new List + { + Expression.Assign(generatedValue, Expression.NewArrayBounds(elementType, repeatCount)), + ForLoop( + index, + repeatCount, + this.GenerateValue(Expression.ArrayAccess(generatedValue, index), repeatCount, recursiveCount, elementType)), + }; + result.Add(this.MakeIfExpression(recursiveCount, arrayList)); + } + else if (Nullable.GetUnderlyingType(type) != null) + { + Type elementType = Nullable.GetUnderlyingType(type); + result.Add(this.GenerateValue(generatedValue, repeatCount, recursiveCount, elementType)); + } + else if (type.GetTypeInfo().IsEnum) + { + if (!this.valueFixtures.TryGetValue(type, out valueFixture)) + { + valueFixture = new EnumValueFixture(type); + this.valueFixtures.Add(valueFixture.Type, valueFixture); + } + + result.Add(this.GenerateValue(generatedValue, repeatCount, recursiveCount, type)); // call again for main method + } + else + { + result.Add(this.MakeIfExpression(recursiveCount, this.InvokeCreate(type, generatedValue, repeatCount, recursiveCount))); + } + + return result.Count > 1 ? Expression.Block(result) : result.Single(); + } + + private Expression InvokeCreate(Type type, Expression generatedValue, Expression repeatCount, Expression recursiveCount) + { + MethodInfo mi = typeof(ExpressionTreeFixture).GetMethod(nameof(this.Create), new[] { typeof(Type), typeof(int), typeof(int) }); + return Expression.Assign( + generatedValue, + Expression.Convert( + Expression.Call(Expression.Constant(this), mi, new[] { Expression.Constant(type), repeatCount, recursiveCount }), + generatedValue.Type)); + } + + private Expression MakeIfExpression(Expression recursiveCount, params Expression[] input) + { + return Expression.IfThen( + Expression.GreaterThanOrEqual(recursiveCount, Expression.Constant(0)), + input.Length > 1 ? Expression.Block(input) : input.Single()); + } + + private Expression MakeIfExpression(Expression recursiveCount, IList input) + { + return Expression.IfThen( + Expression.GreaterThanOrEqual(recursiveCount, Expression.Constant(0)), + input.Count > 1 ? Expression.Block(input) : input.Single()); + } + + public T Create(int repeatCount = 1, int recursiveCount = 1) + { + return (T)this.Create(typeof(T), repeatCount, recursiveCount); + } + + public IEnumerable CreateMany(int count, int repeatCount = 1, int recursiveCount = 1) + { + return this.CreateMany(typeof(T), count, repeatCount, recursiveCount).Cast(); + } + + public IEnumerable CreateMany(Type type, int count, int repeatCount = 1, int recursiveCount = 1) + { + Func functor = this.functorCache.GetOrAdd(type, this.AddFunctor); + for (var i = 0; i < count; i++) + { + yield return functor(repeatCount, recursiveCount); + } + } + + public static Expression ForLoop(ParameterExpression index, Expression lengthExpression, Expression loopContent) + { + LabelTarget breakLabel = Expression.Label("LoopBreak"); + ParameterExpression length = Expression.Variable(typeof(int), "length"); + BlockExpression block = Expression.Block( + new[] { index, length }, + Expression.Assign(index, Expression.Constant(0)), + Expression.Assign(length, lengthExpression), + Expression.Loop( + Expression.IfThenElse( + Expression.LessThan(index, length), + Expression.Block(loopContent, Expression.PostIncrementAssign(index)), + Expression.Break(breakLabel)), + breakLabel)); + return block; + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/FloatValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/FloatValueFixture.cs new file mode 100644 index 000000000..2a88dcc33 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/FloatValueFixture.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class FloatValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(float); + + public object Generate() + { + return ((float)this.prng.Next() % 10000) + 0.5f; + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/GuidValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/GuidValueFixture.cs new file mode 100644 index 000000000..7fcdf6558 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/GuidValueFixture.cs @@ -0,0 +1,17 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class GuidValueFixture : IValueFixture + { + public Type Type { get; } = typeof(Guid); + + public object Generate() + { + return Guid.NewGuid(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/IValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/IValueFixture.cs new file mode 100644 index 000000000..73d23d46f --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/IValueFixture.cs @@ -0,0 +1,14 @@ +// 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; + +namespace Benchmark.Fixture +{ + public interface IValueFixture + { + Type Type { get; } + + object Generate(); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/IntValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/IntValueFixture.cs new file mode 100644 index 000000000..1766d881e --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/IntValueFixture.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class IntValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(int); + + public object Generate() + { + return this.prng.Next(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/LongValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/LongValueFixture.cs new file mode 100644 index 000000000..ecc9df160 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/LongValueFixture.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class LongValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(long); + + public object Generate() + { + return ((long)this.prng.Next() << 32) | (uint)this.prng.Next(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/OtherPrimitiveFixtures.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/OtherPrimitiveFixtures.cs new file mode 100644 index 000000000..b73b10e83 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/OtherPrimitiveFixtures.cs @@ -0,0 +1,71 @@ +// 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.Runtime.CompilerServices; + +#pragma warning disable SA1649 // File name should match first type name + +namespace Benchmark.Fixture +{ + public delegate T Converter(ReadOnlySpan buffer); + + public abstract class PrimitiveFixtureBase : IValueFixture + where T : unmanaged + { + private readonly Random rand = new Random(); + + public Type Type => typeof(T); + + public abstract object Generate(); + + protected unsafe object GenerateBytes(Converter converter) + { + Span buffer = stackalloc byte[sizeof(T)]; + this.rand.NextBytes(buffer); + return converter(buffer); + } + } + + public class SByteValueFixture : PrimitiveFixtureBase + { + public override object Generate() + { + return this.GenerateBytes(x => unchecked((sbyte)x[0])); + } + } + + public class UShortValueFixture : PrimitiveFixtureBase + { + public override object Generate() => this.GenerateBytes(BitConverter.ToUInt16); + } + + public class UInt32ValueFixture : PrimitiveFixtureBase + { + public override object Generate() => this.GenerateBytes(BitConverter.ToUInt32); + } + + public class UInt64ValueFixture : PrimitiveFixtureBase + { + public override object Generate() => this.GenerateBytes(BitConverter.ToUInt64); + } + + public class CharValueFixture : PrimitiveFixtureBase + { + public override object Generate() => this.GenerateBytes(BitConverter.ToChar); + } + + public class ByteArrayFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(byte[]); + + public object Generate() + { + var bytes = new byte[100]; + this.prng.NextBytes(bytes); + return bytes; + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ShortValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ShortValueFixture.cs new file mode 100644 index 000000000..2b621e1f7 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/ShortValueFixture.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class ShortValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(short); + + public object Generate() + { + return (short)(this.prng.Next() & 0xFFFF); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/StringValueFixture.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/StringValueFixture.cs new file mode 100644 index 000000000..a16becbec --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Fixture/StringValueFixture.cs @@ -0,0 +1,31 @@ +// 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; + +namespace Benchmark.Fixture +{ + public class StringValueFixture : IValueFixture + { + private readonly Random prng = new Random(); + + public Type Type { get; } = typeof(string); + + public object Generate() + { + return this.Generate(8); + } + + private string Generate(int length) + { + const string chars = "abcdefghijklmnopqrstuvwxyz0123456789"; + var cArray = new char[length]; + for (var i = 0; i < length; i++) + { + cArray[i] = chars[this.prng.Next(chars.Length)]; + } + + return new string(cArray); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/IGenericEquality`1.cs b/test/Benchmark/MessagePackCsharp/Benchmark/IGenericEquality`1.cs new file mode 100644 index 000000000..81500e3dc --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/IGenericEquality`1.cs @@ -0,0 +1,12 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Benchmark +{ + public interface IGenericEquality + { + bool Equals(T obj); + + bool EqualsDynamic(dynamic obj); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/AccessToken.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/AccessToken.cs new file mode 100644 index 000000000..f7b8f022b --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/AccessToken.cs @@ -0,0 +1,44 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class AccessToken : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string access_token { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public DateTime? expires_on_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? account_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public List scope { get; set; } + + public bool Equals(AccessToken obj) + { + return + this.access_token.TrueEqualsString(obj.access_token) || + this.expires_on_date.TrueEquals(obj.expires_on_date) || + this.account_id.TrueEquals(obj.account_id) || + this.scope.TrueEqualsString(obj.scope); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.access_token.TrueEqualsString((string)obj.access_token) || + this.expires_on_date.TrueEquals((DateTime?)obj.expires_on_date) || + this.account_id.TrueEquals((int?)obj.account_id) || + this.scope.TrueEqualsString((IEnumerable)obj.scope); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/AccountMerge.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/AccountMerge.cs new file mode 100644 index 000000000..31f777bf7 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/AccountMerge.cs @@ -0,0 +1,38 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class AccountMerge : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? old_account_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? new_account_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? merge_date { get; set; } + + public bool Equals(AccountMerge obj) + { + return + this.old_account_id.TrueEquals(obj.old_account_id) && + this.new_account_id.TrueEquals(obj.new_account_id) && + this.merge_date.TrueEquals(obj.merge_date); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.old_account_id.TrueEquals((int?)obj.old_account_id) && + this.new_account_id.TrueEquals((int?)obj.new_account_id) && + this.merge_date.TrueEquals((DateTime?)obj.merge_date); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Answer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Answer.cs new file mode 100644 index 000000000..0b8d997a0 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Answer.cs @@ -0,0 +1,234 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable IDE1006 +#pragma warning disable SA1516 + + +using System; +using System.Collections.Generic; +using ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Answer : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? answer_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? locked_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public DateTime? last_edit_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public DateTime? last_activity_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? score { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public DateTime? community_owned_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public bool? is_accepted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public ShallowUser owner { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public int? up_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public int? down_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(15), MessagePack.Key(15 - 1)] + public List comments { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(16), MessagePack.Key(16 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(17), MessagePack.Key(17 - 1)] + public List tags { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(18), MessagePack.Key(18 - 1)] + public bool? upvoted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(19), MessagePack.Key(19 - 1)] + public bool? downvoted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(20), MessagePack.Key(20 - 1)] + public bool? accepted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(21), MessagePack.Key(21 - 1)] + public ShallowUser last_editor { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(22), MessagePack.Key(22 - 1)] + public int? comment_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(23), MessagePack.Key(23 - 1)] + public string body_markdown { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(24), MessagePack.Key(24 - 1)] + public string share_link { get; set; } + + public bool Equals(Answer obj) + { + return + this.accepted.TrueEquals(obj.accepted) && + this.answer_id.TrueEquals(obj.answer_id) && + this.body.TrueEqualsString(obj.body) && + this.body_markdown.TrueEqualsString(obj.body_markdown) && + this.comment_count.TrueEquals(obj.comment_count) && + this.comments.TrueEqualsList(obj.comments) && + this.community_owned_date.TrueEquals(obj.community_owned_date) && + this.creation_date.TrueEquals(obj.creation_date) && + this.down_vote_count.TrueEquals(obj.down_vote_count) && + this.downvoted.TrueEquals(obj.downvoted) && + this.is_accepted.TrueEquals(obj.is_accepted) && + this.last_activity_date.TrueEquals(obj.last_activity_date) && + this.last_edit_date.TrueEquals(obj.last_edit_date) && + this.last_editor.TrueEquals(obj.last_editor) && + this.link.TrueEqualsString(obj.link) && + this.locked_date.TrueEquals(obj.locked_date) && + this.owner.TrueEquals(obj.owner) && + this.question_id.TrueEquals(obj.question_id) && + this.score.TrueEquals(obj.score) && + this.share_link.TrueEqualsString(obj.share_link) && + this.tags.TrueEqualsString(obj.tags) && + this.title.TrueEqualsString(obj.title) && + this.up_vote_count.TrueEquals(obj.up_vote_count) && + this.upvoted.TrueEquals(obj.upvoted); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.accepted.TrueEquals((bool?)obj.accepted) && + this.answer_id.TrueEquals((int?)obj.answer_id) && + this.body.TrueEqualsString((string)obj.body) && + this.body_markdown.TrueEqualsString((string)obj.body_markdown) && + this.comment_count.TrueEquals((int?)obj.comment_count) && + this.comments.TrueEqualsListDynamic((IEnumerable)obj.comments) && + this.community_owned_date.TrueEquals((DateTime?)obj.community_owned_date) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.down_vote_count.TrueEquals((int?)obj.down_vote_count) && + this.downvoted.TrueEquals((bool?)obj.downvoted) && + this.is_accepted.TrueEquals((bool?)obj.is_accepted) && + this.last_activity_date.TrueEquals((DateTime?)obj.last_activity_date) && + this.last_edit_date.TrueEquals((DateTime?)obj.last_edit_date) && + ((this.last_editor == null && obj.last_editor == null) || this.last_editor.EqualsDynamic(obj.last_editor)) && + this.link.TrueEqualsString((string)obj.link) && + this.locked_date.TrueEquals((DateTime?)obj.locked_date) && + ((this.owner == null && obj.owner == null) || this.owner.EqualsDynamic(obj.owner)) && + this.question_id.TrueEquals((int?)obj.question_id) && + this.score.TrueEquals((int?)obj.score) && + this.share_link.TrueEqualsString((string)obj.share_link) && + this.tags.TrueEqualsString((IEnumerable)obj.tags) && + this.title.TrueEqualsString((string)obj.title) && + this.up_vote_count.TrueEquals((int?)obj.up_vote_count) && + this.upvoted.TrueEquals((bool?)obj.upvoted); + } + } + + [MessagePack.MessagePackObject(true)] + public class Answer2 : IGenericEquality + { + public int? question_id { get; set; } + public int? answer_id { get; set; } + public DateTime? locked_date { get; set; } + public DateTime? creation_date { get; set; } + public DateTime? last_edit_date { get; set; } + public DateTime? last_activity_date { get; set; } + public int? score { get; set; } + public DateTime? community_owned_date { get; set; } + public bool? is_accepted { get; set; } + public string body { get; set; } + public ShallowUser2 owner { get; set; } + public string title { get; set; } + public int? up_vote_count { get; set; } + public int? down_vote_count { get; set; } + public List comments { get; set; } + public string link { get; set; } + public List tags { get; set; } + public bool? upvoted { get; set; } + public bool? downvoted { get; set; } + public bool? accepted { get; set; } + public ShallowUser2 last_editor { get; set; } + public int? comment_count { get; set; } + public string body_markdown { get; set; } + public string share_link { get; set; } + + public bool Equals(Answer2 obj) + { + return + this.accepted.TrueEquals(obj.accepted) && + this.answer_id.TrueEquals(obj.answer_id) && + this.body.TrueEqualsString(obj.body) && + this.body_markdown.TrueEqualsString(obj.body_markdown) && + this.comment_count.TrueEquals(obj.comment_count) && + this.comments.TrueEqualsList(obj.comments) && + this.community_owned_date.TrueEquals(obj.community_owned_date) && + this.creation_date.TrueEquals(obj.creation_date) && + this.down_vote_count.TrueEquals(obj.down_vote_count) && + this.downvoted.TrueEquals(obj.downvoted) && + this.is_accepted.TrueEquals(obj.is_accepted) && + this.last_activity_date.TrueEquals(obj.last_activity_date) && + this.last_edit_date.TrueEquals(obj.last_edit_date) && + this.last_editor.TrueEquals(obj.last_editor) && + this.link.TrueEqualsString(obj.link) && + this.locked_date.TrueEquals(obj.locked_date) && + this.owner.TrueEquals(obj.owner) && + this.question_id.TrueEquals(obj.question_id) && + this.score.TrueEquals(obj.score) && + this.share_link.TrueEqualsString(obj.share_link) && + this.tags.TrueEqualsString(obj.tags) && + this.title.TrueEqualsString(obj.title) && + this.up_vote_count.TrueEquals(obj.up_vote_count) && + this.upvoted.TrueEquals(obj.upvoted); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.accepted.TrueEquals((bool?)obj.accepted) && + this.answer_id.TrueEquals((int?)obj.answer_id) && + this.body.TrueEqualsString((string)obj.body) && + this.body_markdown.TrueEqualsString((string)obj.body_markdown) && + this.comment_count.TrueEquals((int?)obj.comment_count) && + this.comments.TrueEqualsListDynamic((IEnumerable)obj.comments) && + this.community_owned_date.TrueEquals((DateTime?)obj.community_owned_date) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.down_vote_count.TrueEquals((int?)obj.down_vote_count) && + this.downvoted.TrueEquals((bool?)obj.downvoted) && + this.is_accepted.TrueEquals((bool?)obj.is_accepted) && + this.last_activity_date.TrueEquals((DateTime?)obj.last_activity_date) && + this.last_edit_date.TrueEquals((DateTime?)obj.last_edit_date) && + ((this.last_editor == null && obj.last_editor == null) || this.last_editor.EqualsDynamic(obj.last_editor)) && + this.link.TrueEqualsString((string)obj.link) && + this.locked_date.TrueEquals((DateTime?)obj.locked_date) && + ((this.owner == null && obj.owner == null) || this.owner.EqualsDynamic(obj.owner)) && + this.question_id.TrueEquals((int?)obj.question_id) && + this.score.TrueEquals((int?)obj.score) && + this.share_link.TrueEqualsString((string)obj.share_link) && + this.tags.TrueEqualsString((IEnumerable)obj.tags) && + this.title.TrueEqualsString((string)obj.title) && + this.up_vote_count.TrueEquals((int?)obj.up_vote_count) && + this.upvoted.TrueEquals((bool?)obj.upvoted); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Badge.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Badge.cs new file mode 100644 index 000000000..01818df5a --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Badge.cs @@ -0,0 +1,75 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using ProtoBuf; + +namespace Benchmark.Models +{ + public enum BadgeRank : byte + { + bronze = 3, + silver = 2, + gold = 1, + } + + public enum BadgeType + { + named = 1, + tag_based = 2, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Badge : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? badge_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public BadgeRank? rank { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string description { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? award_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public BadgeType? badge_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public ShallowUser user { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public string link { get; set; } + + public bool Equals(Badge obj) + { + return + this.award_count.TrueEquals(obj.award_count) && + this.badge_id.TrueEquals(obj.badge_id) && + this.badge_type.TrueEquals(obj.badge_type) && + this.description.TrueEqualsString(obj.description) && + this.link.TrueEqualsString(obj.link) && + this.name.TrueEqualsString(obj.name) && + this.rank.TrueEquals(obj.rank) && + this.user.TrueEquals(obj.user); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.award_count.TrueEquals((int?)obj.award_count) && + this.badge_id.TrueEquals((int?)obj.badge_id) && + this.badge_type.TrueEquals((BadgeType?)obj.badge_type) && + this.description.TrueEqualsString((string)obj.description) && + this.link.TrueEqualsString((string)obj.link) && + this.name.TrueEqualsString((string)obj.name) && + this.rank.TrueEquals((BadgeRank?)obj.rank) && + ((this.user == null && obj.user == null) || this.user.EqualsDynamic(obj.user)); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Comment.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Comment.cs new file mode 100644 index 000000000..b36aee46a --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Comment.cs @@ -0,0 +1,145 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable IDE1006 +#pragma warning disable SA1516 + + +using System; +using ProtoBuf; + +namespace Benchmark.Models +{ + public enum PostType : byte + { + question = 1, + answer = 2, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Comment : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? comment_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public PostType? post_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? score { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public bool? edited { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public ShallowUser owner { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public ShallowUser reply_to_user { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public string body_markdown { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public bool? upvoted { get; set; } + + public bool Equals(Comment obj) + { + return + this.body.TrueEqualsString(obj.body) && + this.body_markdown.TrueEqualsString(obj.body_markdown) && + this.comment_id.TrueEquals(obj.comment_id) && + this.creation_date.TrueEquals(obj.creation_date) && + this.edited.TrueEquals(obj.edited) && + this.link.TrueEqualsString(obj.link) && + this.owner.TrueEquals(obj.owner) && + this.post_id.TrueEquals(obj.post_id) && + this.post_type.TrueEquals(obj.post_type) && + this.reply_to_user.TrueEquals(obj.reply_to_user) && + this.score.TrueEquals(obj.score) && + this.upvoted.TrueEquals(obj.upvoted); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.body.TrueEqualsString((string)obj.body) && + this.body_markdown.TrueEqualsString((string)obj.body_markdown) && + this.comment_id.TrueEquals((int?)obj.comment_id) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.edited.TrueEquals((bool?)obj.edited) && + this.link.TrueEqualsString((string)obj.link) && + ((this.owner == null && obj.owner == null) || this.owner.EqualsDynamic(obj.owner)) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.post_type.TrueEquals((PostType?)obj.post_type) && + ((this.reply_to_user == null && obj.reply_to_user == null) || + this.reply_to_user.EqualsDynamic(obj.reply_to_user)) && + this.score.TrueEquals((int?)obj.score) && + this.upvoted.TrueEquals((bool?)obj.upvoted); + } + } + + [MessagePack.MessagePackObject(true)] + public class Comment2 : IGenericEquality + { + public int? comment_id { get; set; } + public int? post_id { get; set; } + public DateTime? creation_date { get; set; } + public PostType? post_type { get; set; } + public int? score { get; set; } + public bool? edited { get; set; } + public string body { get; set; } + public ShallowUser2 owner { get; set; } + public ShallowUser2 reply_to_user { get; set; } + public string link { get; set; } + public string body_markdown { get; set; } + public bool? upvoted { get; set; } + + public bool Equals(Comment2 obj) + { + return + this.body.TrueEqualsString(obj.body) && + this.body_markdown.TrueEqualsString(obj.body_markdown) && + this.comment_id.TrueEquals(obj.comment_id) && + this.creation_date.TrueEquals(obj.creation_date) && + this.edited.TrueEquals(obj.edited) && + this.link.TrueEqualsString(obj.link) && + this.owner.TrueEquals(obj.owner) && + this.post_id.TrueEquals(obj.post_id) && + this.post_type.TrueEquals(obj.post_type) && + this.reply_to_user.TrueEquals(obj.reply_to_user) && + this.score.TrueEquals(obj.score) && + this.upvoted.TrueEquals(obj.upvoted); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.body.TrueEqualsString((string)obj.body) && + this.body_markdown.TrueEqualsString((string)obj.body_markdown) && + this.comment_id.TrueEquals((int?)obj.comment_id) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.edited.TrueEquals((bool?)obj.edited) && + this.link.TrueEqualsString((string)obj.link) && + ((this.owner == null && obj.owner == null) || this.owner.EqualsDynamic(obj.owner)) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.post_type.TrueEquals((PostType?)obj.post_type) && + ((this.reply_to_user == null && obj.reply_to_user == null) || + this.reply_to_user.EqualsDynamic(obj.reply_to_user)) && + this.score.TrueEquals((int?)obj.score) && + this.upvoted.TrueEquals((bool?)obj.upvoted); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Error.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Error.cs new file mode 100644 index 000000000..4b19fec54 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Error.cs @@ -0,0 +1,37 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Error : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? error_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string error_name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string description { get; set; } + + public bool Equals(Error obj) + { + return + this.error_id.TrueEquals(obj.error_id) && + this.error_name.TrueEqualsString(obj.error_name) && + this.description.TrueEqualsString(obj.description); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.error_id.TrueEquals((int?)obj.error_id) && + this.error_name.TrueEqualsString((string)obj.error_name) && + this.description.TrueEqualsString((string)obj.description); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Event.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Event.cs new file mode 100644 index 000000000..79599aa42 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Event.cs @@ -0,0 +1,57 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + public enum EventType : byte + { + question_posted = 1, + answer_posted = 2, + comment_posted = 3, + post_edited = 4, + user_created = 5, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Event : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public EventType? event_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? event_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string excerpt { get; set; } + + public bool Equals(Event obj) + { + return + this.creation_date.TrueEquals(obj.creation_date) && + this.event_id.TrueEquals(obj.event_id) && + this.event_type.TrueEquals(obj.event_type) && + this.excerpt.TrueEqualsString(obj.excerpt) && + this.link.TrueEqualsString(obj.link); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.event_id.TrueEquals((int?)obj.event_id) && + this.event_type.TrueEquals((EventType?)obj.event_type) && + this.excerpt.TrueEqualsString((string)obj.excerpt) && + this.link.TrueEqualsString((string)obj.link); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Feed.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Feed.cs new file mode 100644 index 000000000..1ceb29798 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Feed.cs @@ -0,0 +1,693 @@ +// 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.Collections.Generic; +using ProtoBuf; + +#pragma warning disable SA1649 // File name should match first type name + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class MobileFeed : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public List hot_questions { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public List inbox_items { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public List likely_to_answer_questions { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public List reputation_events { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public List cross_site_interesting_questions { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public List badges { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public List earned_privileges { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public List upcoming_privileges { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public List community_bulletins { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public List association_bonuses { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public List careers_job_ads { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public List banner_ads { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public long? before { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public long? since { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(15), MessagePack.Key(15 - 1)] + public int? account_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(16), MessagePack.Key(16 - 1)] + public MobileUpdateNotice update_notice { get; set; } + + public bool Equals(MobileFeed obj) + { + return + this.account_id == obj.account_id && + this.association_bonuses.TrueEqualsList(obj.association_bonuses) && + this.badges.TrueEqualsList(obj.badges) && + this.banner_ads.TrueEqualsList(obj.banner_ads) && + this.before == obj.before && + this.careers_job_ads.TrueEqualsList(obj.careers_job_ads) && + this.community_bulletins.TrueEqualsList(obj.community_bulletins) && + this.cross_site_interesting_questions.TrueEqualsList(obj.cross_site_interesting_questions) && + this.earned_privileges.TrueEqualsList(obj.earned_privileges) && + this.hot_questions.TrueEqualsList(obj.hot_questions) && + this.inbox_items.TrueEqualsList(obj.inbox_items) && + this.likely_to_answer_questions.TrueEqualsList(obj.likely_to_answer_questions) && + this.reputation_events.TrueEqualsList(obj.reputation_events) && + this.since == obj.since && + this.upcoming_privileges.TrueEqualsList(obj.upcoming_privileges) && + this.update_notice.TrueEquals(obj.update_notice); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.account_id == (int?)obj.account_id && + this.association_bonuses.TrueEqualsListDynamic((IEnumerable)obj.association_bonuses) && + this.badges.TrueEqualsListDynamic((IEnumerable)obj.badges) && + this.banner_ads.TrueEqualsListDynamic((IEnumerable)obj.banner_ads) && + this.before == (long?)obj.before && + this.careers_job_ads.TrueEqualsListDynamic((IEnumerable)obj.careers_job_ads) && + this.community_bulletins.TrueEqualsListDynamic((IEnumerable)obj.community_bulletins) && + this.cross_site_interesting_questions.TrueEqualsListDynamic( + (IEnumerable)obj.cross_site_interesting_questions) && + this.earned_privileges.TrueEqualsListDynamic((IEnumerable)obj.earned_privileges) && + this.hot_questions.TrueEqualsListDynamic((IEnumerable)obj.hot_questions) && + this.inbox_items.TrueEqualsListDynamic((IEnumerable)obj.inbox_items) && + this.likely_to_answer_questions.TrueEqualsListDynamic( + (IEnumerable)obj.likely_to_answer_questions) && + this.reputation_events.TrueEqualsListDynamic((IEnumerable)obj.reputation_events) && + this.since == (long?)obj.since && + this.upcoming_privileges.TrueEqualsListDynamic((IEnumerable)obj.upcoming_privileges) && + ((this.update_notice == null && obj.update_notice == null) || this.update_notice.EqualsDynamic(obj.update_notice)); + } + } + + public interface IMobileFeedBase : IGenericEquality + { + int? group_id { get; set; } + + long? added_date { get; set; } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileQuestion : IMobileFeedBase + { + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public long? question_creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public long? last_activity_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public List tags { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public string site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public bool? is_deleted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public bool? has_accepted_answer { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public int? answer_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobileQuestion obj) + { + return + this.added_date == obj.added_date && + this.answer_count == obj.answer_count && + this.group_id == obj.group_id && + this.has_accepted_answer == obj.has_accepted_answer && + this.is_deleted == obj.is_deleted && + this.last_activity_date == obj.last_activity_date && + this.question_creation_date == obj.question_creation_date && + this.question_id == obj.question_id && + this.site == obj.site && + this.tags.TrueEqualsString(obj.tags) && + this.title == obj.title; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.answer_count == (int?)obj.answer_count && + this.group_id == (int?)obj.group_id && + this.has_accepted_answer == (bool?)obj.has_accepted_answer && + this.is_deleted == (bool?)obj.is_deleted && + this.last_activity_date == (long?)obj.last_activity_date && + this.question_creation_date == (long?)obj.question_creation_date && + this.question_id == (int?)obj.question_id && + this.site == (string)obj.site && + this.tags.TrueEqualsString((IEnumerable)obj.tags) && + this.title == (string)obj.title; + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileRepChange : IMobileFeedBase + { + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? rep_change { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobileRepChange obj) + { + return + this.added_date == obj.added_date && + this.group_id == obj.group_id && + this.link == obj.link && + this.rep_change == obj.rep_change && + this.site == obj.site && + this.title == obj.title; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.group_id == (int?)obj.group_id && + this.link == (string)obj.link && + this.rep_change == (int?)obj.rep_change && + this.site == (string)obj.site && + this.title == (string)obj.title; + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileInboxItem : IMobileFeedBase + { + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? answer_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? comment_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public long? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public string item_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public string site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobileInboxItem obj) + { + return + this.added_date == obj.added_date && + this.answer_id == obj.answer_id && + this.body == obj.body && + this.comment_id == obj.comment_id && + this.creation_date == obj.creation_date && + this.group_id == obj.group_id && + this.item_type == obj.item_type && + this.link == obj.link && + this.question_id == obj.question_id && + this.site == obj.site && + this.title == obj.title; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.answer_id == (int?)obj.answer_id && + this.body == (string)obj.body && + this.comment_id == (int?)obj.comment_id && + this.creation_date == (long?)obj.creation_date && + this.group_id == (int?)obj.group_id && + this.item_type == (string)obj.item_type && + this.link == (string)obj.link && + this.question_id == (int?)obj.question_id && + this.site == (string)obj.site && + this.title == (string)obj.title; + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileBadgeAward : IMobileFeedBase + { + public enum BadgeRank : byte + { + bronze = 1, + silver = 2, + gold = 3, + } + + public enum BadgeType + { + named = 1, + tag_based = 2, + } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string badge_name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string badge_description { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? badge_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public BadgeRank? rank { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public BadgeType? badge_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobileBadgeAward obj) + { + return + this.added_date == obj.added_date && + this.badge_description == obj.badge_description && + this.badge_id == obj.badge_id && + this.badge_name == obj.badge_name && + this.badge_type == obj.badge_type && + this.group_id == obj.group_id && + this.link == obj.link && + this.post_id == obj.post_id && + this.rank == obj.rank && + this.site == obj.site; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.badge_description == (string)obj.badge_description && + this.badge_id == (int?)obj.badge_id && + this.badge_name == (string)obj.badge_name && + this.badge_type == (BadgeType?)obj.badge_type && + this.group_id == (int?)obj.group_id && + this.link == (string)obj.link && + this.post_id == (int?)obj.post_id && + this.rank == (BadgeRank?)obj.rank && + this.site == (string)obj.site; + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobilePrivilege : IMobileFeedBase + { + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string privilege_short_description { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string privilege_long_description { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? privilege_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? reputation_required { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobilePrivilege obj) + { + return + this.added_date == obj.added_date && + this.group_id == obj.group_id && + this.link == obj.link && + this.privilege_id == obj.privilege_id && + this.privilege_long_description == obj.privilege_long_description && + this.privilege_short_description == obj.privilege_short_description && + this.reputation_required == obj.reputation_required && + this.site == obj.site; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.group_id == (int?)obj.group_id && + this.link == (string)obj.link && + this.privilege_id == (int?)obj.privilege_id && + this.privilege_long_description == (string)obj.privilege_long_description && + this.privilege_short_description == (string)obj.privilege_short_description && + this.reputation_required == (int?)obj.reputation_required && + this.site == (string)obj.site; + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileCommunityBulletin : IMobileFeedBase + { + public enum CommunityBulletinType : byte + { + blog_post = 1, + featured_meta_question = 2, + upcoming_event = 3, + } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public CommunityBulletinType? bulletin_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public long? begin_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public long? end_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public string custom_date_string { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public List tags { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public bool? is_deleted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public bool? has_accepted_answer { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public int? answer_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public bool? is_promoted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobileCommunityBulletin obj) + { + return + this.added_date == obj.added_date && + this.answer_count == obj.answer_count && + this.begin_date == obj.begin_date && + this.bulletin_type == obj.bulletin_type && + this.custom_date_string == obj.custom_date_string && + this.end_date == obj.end_date && + this.group_id == obj.group_id && + this.has_accepted_answer == obj.has_accepted_answer && + this.is_deleted == obj.is_deleted && + this.is_promoted == obj.is_promoted && + this.link == obj.link && + this.site == obj.site && + this.tags.TrueEqualsString(obj.tags) && + this.title == obj.title; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.answer_count == (int?)obj.answer_count && + this.begin_date == (long?)obj.begin_date && + this.bulletin_type == (CommunityBulletinType?)obj.bulletin_type && + this.custom_date_string == (string)obj.custom_date_string && + this.end_date == (long?)obj.end_date && + this.group_id == (int?)obj.group_id && + this.has_accepted_answer == (bool?)obj.has_accepted_answer && + this.is_deleted == (bool?)obj.is_deleted && + this.is_promoted == (bool?)obj.is_promoted && + this.link == (string)obj.link && + this.site == (string)obj.site && + this.tags.TrueEqualsString((IEnumerable)obj.tags) && + this.title == (string)obj.title; + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileAssociationBonus : IMobileFeedBase + { + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? amount { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobileAssociationBonus obj) + { + return + this.added_date == obj.added_date && + this.amount == obj.amount && + this.group_id == obj.group_id && + this.site == obj.site; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.amount == (int?)obj.amount && + this.group_id == (int?)obj.group_id && + this.site == (string)obj.site; + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileCareersJobAd : IMobileFeedBase + { + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? job_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string company_name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public string location { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobileCareersJobAd obj) + { + return + this.added_date == obj.added_date && + this.company_name == obj.company_name && + this.group_id == obj.group_id && + this.job_id == obj.job_id && + this.link == obj.link && + this.location == obj.location && + this.title == obj.title; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.company_name == (string)obj.company_name && + this.group_id == (int?)obj.group_id && + this.job_id == (int?)obj.job_id && + this.link == (string)obj.link && + this.location == (string)obj.location && + this.title == (string)obj.title; + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileBannerAd : IMobileFeedBase + { + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public List images { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? group_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public long? added_date { get; set; } + + public bool Equals(MobileBannerAd obj) + { + return + this.added_date == obj.added_date && + this.group_id == obj.group_id && + this.images.TrueEqualsList(obj.images) && + this.link == obj.link; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.added_date == (long?)obj.added_date && + this.group_id == (int?)obj.group_id && + this.images.TrueEqualsListDynamic((IEnumerable)obj.images) && + this.link == (string)obj.link; + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileBannerAdImage : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string image_url { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? width { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? height { get; set; } + + public bool Equals(MobileBannerAdImage obj) + { + return + this.height == obj.height && + this.image_url == obj.image_url && + this.width == obj.width; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.height == (int?)obj.height && + this.image_url == (string)obj.image_url && + this.width == (int?)obj.width; + } + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public sealed class MobileUpdateNotice : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public bool? should_update { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string message { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string minimum_supported_version { get; set; } + + public bool Equals(MobileUpdateNotice obj) + { + return + this.message == obj.message && + this.minimum_supported_version == obj.minimum_supported_version && + this.should_update == obj.should_update; + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.message == (string)obj.message && + this.minimum_supported_version == (string)obj.minimum_supported_version && + this.should_update == (bool?)obj.should_update; + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/FlagOption.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/FlagOption.cs new file mode 100644 index 000000000..c2e25f78b --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/FlagOption.cs @@ -0,0 +1,68 @@ +// 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.Collections.Generic; +using ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class FlagOption : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? option_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public bool? requires_comment { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public bool? requires_site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public bool? requires_question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public string description { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public List sub_options { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public bool? has_flagged { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public int? count { get; set; } + + public bool Equals(FlagOption obj) + { + return + this.count.TrueEquals(obj.count) && + this.description.TrueEqualsString(obj.description) && + this.has_flagged.TrueEquals(obj.has_flagged) && + this.option_id.TrueEquals(obj.option_id) && + this.requires_comment.TrueEquals(obj.requires_comment) && + this.requires_question_id.TrueEquals(obj.requires_question_id) && + this.requires_site.TrueEquals(obj.requires_site) && + this.sub_options.TrueEqualsList(obj.sub_options) && + this.title.TrueEqualsString(obj.title); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.count.TrueEquals((int?)obj.count) && + this.description.TrueEqualsString((string)obj.description) && + this.has_flagged.TrueEquals((bool?)obj.has_flagged) && + this.option_id.TrueEquals((int?)obj.option_id) && + this.requires_comment.TrueEquals((bool?)obj.requires_comment) && + this.requires_question_id.TrueEquals((bool?)obj.requires_question_id) && + this.requires_site.TrueEquals((bool?)obj.requires_site) && + this.sub_options.TrueEqualsListDynamic((IEnumerable)obj.sub_options) && + this.title.TrueEqualsString((string)obj.title); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/InboxItem.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/InboxItem.cs new file mode 100644 index 000000000..8d51d5fcf --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/InboxItem.cs @@ -0,0 +1,85 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + public enum InboxItemType + { + comment = 1, + chat_message = 2, + new_answer = 3, + careers_message = 4, + careers_invitations = 5, + meta_question = 6, + post_notice = 7, + moderator_message = 8, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class InboxItem : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public InboxItemType? item_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? answer_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? comment_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public bool? is_unread { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public Info.Site site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public string link { get; set; } + + public bool Equals(InboxItem obj) + { + return + this.answer_id.TrueEquals(obj.answer_id) && + this.body.TrueEqualsString(obj.body) && + this.comment_id.TrueEquals(obj.comment_id) && + this.creation_date.TrueEquals(obj.creation_date) && + this.is_unread.TrueEquals(obj.is_unread) && + this.item_type.TrueEquals(obj.item_type) && + this.link.TrueEqualsString(obj.link) && + this.question_id.TrueEquals(obj.question_id) && + this.site.TrueEquals(obj.site) && + this.title.TrueEqualsString(obj.title); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.answer_id.TrueEquals((int?)obj.answer_id) && + this.body.TrueEqualsString((string)obj.body) && + this.comment_id.TrueEquals((int?)obj.comment_id) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.is_unread.TrueEquals((bool?)obj.is_unread) && + this.item_type.TrueEquals((InboxItemType?)obj.item_type) && + this.link.TrueEqualsString((string)obj.link) && + this.question_id.TrueEquals((int?)obj.question_id) && + ((this.site == null && obj.site == null) || this.site.EqualsDynamic(obj.site)) && + this.title.TrueEqualsString((string)obj.title); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Info.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Info.cs new file mode 100644 index 000000000..409595d7e --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Info.cs @@ -0,0 +1,274 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Info : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? total_questions { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? total_unanswered { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? total_accepted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? total_answers { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public decimal? questions_per_minute { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public decimal? answers_per_minute { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? total_comments { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public int? total_votes { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public int? total_badges { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public decimal? badges_per_minute { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public int? total_users { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public int? new_active_users { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public string api_revision { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public Site site { get; set; } + + public bool Equals(Info obj) + { + return + this.answers_per_minute.TrueEquals(obj.answers_per_minute) && + this.api_revision.TrueEqualsString(obj.api_revision) && + this.badges_per_minute.TrueEquals(obj.badges_per_minute) && + this.new_active_users.TrueEquals(obj.new_active_users) && + this.questions_per_minute.TrueEquals(obj.questions_per_minute) && + this.site.TrueEquals(obj.site) && + this.total_accepted.TrueEquals(obj.total_accepted) && + this.total_answers.TrueEquals(obj.total_answers) && + this.total_badges.TrueEquals(obj.total_badges) && + this.total_comments.TrueEquals(obj.total_comments) && + this.total_questions.TrueEquals(obj.total_questions) && + this.total_unanswered.TrueEquals(obj.total_unanswered) && + this.total_users.TrueEquals(obj.total_users) && + this.total_votes.TrueEquals(obj.total_votes); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.answers_per_minute.TrueEquals((decimal?)obj.answers_per_minute) && + this.api_revision.TrueEqualsString((string)obj.api_revision) && + this.badges_per_minute.TrueEquals((decimal?)obj.badges_per_minute) && + this.new_active_users.TrueEquals((int?)obj.new_active_users) && + this.questions_per_minute.TrueEquals((decimal?)obj.questions_per_minute) && + ((this.site == null && obj.site == null) || this.site.EqualsDynamic(obj.site)) && + this.total_accepted.TrueEquals((int?)obj.total_accepted) && + this.total_answers.TrueEquals((int?)obj.total_answers) && + this.total_badges.TrueEquals((int?)obj.total_badges) && + this.total_comments.TrueEquals((int?)obj.total_comments) && + this.total_questions.TrueEquals((int?)obj.total_questions) && + this.total_unanswered.TrueEquals((int?)obj.total_unanswered) && + this.total_users.TrueEquals((int?)obj.total_users) && + this.total_votes.TrueEquals((int?)obj.total_votes); + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Site : IGenericEquality + { + public enum SiteState + { + normal, + closed_beta, + open_beta, + linked_meta, + } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string site_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string logo_url { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string api_site_parameter { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string site_url { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public string audience { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public string icon_url { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public List aliases { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public SiteState? site_state { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public Styling styling { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public DateTime? closed_beta_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public DateTime? open_beta_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public DateTime? launch_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public string favicon_url { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(15), MessagePack.Key(15 - 1)] + public List related_sites { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(16), MessagePack.Key(16 - 1)] + public string twitter_account { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(17), MessagePack.Key(17 - 1)] + public List markdown_extensions { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(18), MessagePack.Key(18 - 1)] + public string high_resolution_icon_url { get; set; } + + public bool Equals(Site obj) + { + return + this.aliases.TrueEqualsString(obj.aliases) && + this.api_site_parameter.TrueEqualsString(obj.api_site_parameter) && + this.audience.TrueEqualsString(obj.audience) && + this.closed_beta_date.TrueEquals(obj.closed_beta_date) && + this.favicon_url.TrueEqualsString(obj.favicon_url) && + this.high_resolution_icon_url.TrueEqualsString(obj.high_resolution_icon_url) && + this.icon_url.TrueEqualsString(obj.icon_url) && + this.launch_date.TrueEquals(obj.launch_date) && + this.logo_url.TrueEqualsString(obj.logo_url) && + this.markdown_extensions.TrueEqualsString(obj.markdown_extensions) && + this.name.TrueEqualsString(obj.name) && + this.open_beta_date.TrueEquals(obj.open_beta_date) && + this.related_sites.TrueEqualsList(obj.related_sites) && + this.site_state.TrueEquals(obj.site_state) && + this.site_type.TrueEqualsString(obj.site_type) && + this.site_url.TrueEqualsString(obj.site_url) && + this.styling.TrueEquals(obj.styling) && + this.twitter_account.TrueEqualsString(obj.twitter_account); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.aliases.TrueEqualsString((IEnumerable)obj.aliases) && + this.api_site_parameter.TrueEqualsString((string)obj.api_site_parameter) && + this.audience.TrueEqualsString((string)obj.audience) && + this.closed_beta_date.TrueEquals((DateTime?)obj.closed_beta_date) && + this.favicon_url.TrueEqualsString((string)obj.favicon_url) && + this.high_resolution_icon_url.TrueEqualsString((string)obj.high_resolution_icon_url) && + this.icon_url.TrueEqualsString((string)obj.icon_url) && + this.launch_date.TrueEquals((DateTime?)obj.launch_date) && + this.logo_url.TrueEqualsString((string)obj.logo_url) && + this.markdown_extensions.TrueEqualsString((IEnumerable)obj.markdown_extensions) && + this.name.TrueEqualsString((string)obj.name) && + this.open_beta_date.TrueEquals((DateTime?)obj.open_beta_date) && + this.related_sites.TrueEqualsListDynamic((IEnumerable)obj.related_sites) && + this.site_state.TrueEquals((SiteState?)obj.site_state) && + this.site_type.TrueEqualsString((string)obj.site_type) && + this.site_url.TrueEqualsString((string)obj.site_url) && + ((this.styling == null && obj.styling == null) || this.styling.EqualsDynamic(obj.styling)) && + this.twitter_account.TrueEqualsString((string)obj.twitter_account); + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Styling : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string link_color { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string tag_foreground_color { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string tag_background_color { get; set; } + + public bool Equals(Styling obj) + { + return + this.link_color.TrueEqualsString(obj.link_color) && + this.tag_background_color.TrueEqualsString(obj.tag_background_color) && + this.tag_foreground_color.TrueEqualsString(obj.tag_foreground_color); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.link_color.TrueEqualsString((string)obj.link_color) && + this.tag_background_color.TrueEqualsString((string)obj.tag_background_color) && + this.tag_foreground_color.TrueEqualsString((string)obj.tag_foreground_color); + } + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class RelatedSite : IGenericEquality + { + public enum SiteRelation + { + parent, + meta, + chat, + } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string site_url { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public SiteRelation? relation { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string api_site_parameter { get; set; } + + public bool Equals(RelatedSite obj) + { + return + this.name.TrueEqualsString(obj.name) && + this.relation.TrueEquals(obj.relation) && + this.api_site_parameter.TrueEqualsString(obj.api_site_parameter); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.name.TrueEqualsString((string)obj.name) && + this.relation.TrueEquals((SiteRelation?)obj.relation) && + this.api_site_parameter.TrueEqualsString((string)obj.api_site_parameter); + } + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/NetworkUser.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/NetworkUser.cs new file mode 100644 index 000000000..ec4b2eb4a --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/NetworkUser.cs @@ -0,0 +1,78 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class NetworkUser : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string site_name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string site_url { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? user_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? reputation { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? account_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public UserType? user_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public User.BadgeCount badge_counts { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public DateTime? last_access_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public int? answer_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public int? question_count { get; set; } + + public bool Equals(NetworkUser obj) + { + return + this.account_id.TrueEquals(obj.account_id) && + this.answer_count.TrueEquals(obj.answer_count) && + this.badge_counts.TrueEquals(obj.badge_counts) && + this.creation_date.TrueEquals(obj.creation_date) && + this.last_access_date.TrueEquals(obj.last_access_date) && + this.question_count.TrueEquals(obj.question_count) && + this.reputation.TrueEquals(obj.reputation) && + this.site_name.TrueEqualsString(obj.site_name) && + this.site_url.TrueEqualsString(obj.site_url) && + this.user_id.TrueEquals(obj.user_id) && + this.user_type.TrueEquals(obj.user_type); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.account_id.TrueEquals((int?)obj.account_id) && + this.answer_count.TrueEquals((int?)obj.answer_count) && + ((this.badge_counts == null && obj.badge_counts == null) || this.badge_counts.EqualsDynamic(obj.badge_counts)) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.last_access_date.TrueEquals((DateTime?)obj.last_access_date) && + this.question_count.TrueEquals((int?)obj.question_count) && + this.reputation.TrueEquals((int?)obj.reputation) && + this.site_name.TrueEqualsString((string)obj.site_name) && + this.site_url.TrueEqualsString((string)obj.site_url) && + this.user_id.TrueEquals((int?)obj.user_id) && + this.user_type.TrueEquals((UserType?)obj.user_type); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Notification.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Notification.cs new file mode 100644 index 000000000..09c21f133 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Notification.cs @@ -0,0 +1,70 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + public enum NotificationType : byte + { + generic = 1, + accounts_associated = 8, + badge_earned = 5, + profile_activity = 2, + bounty_expired = 3, + bounty_expires_in_one_day = 4, + bounty_expires_in_three_days = 6, + edit_suggested = 22, + new_privilege = 9, + post_migrated = 10, + moderator_message = 11, + registration_reminder = 12, + substantive_edit = 23, + reputation_bonus = 7, + bounty_grace_period_started = 24, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Notification : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public NotificationType? notification_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public Info.Site site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public bool? is_unread { get; set; } + + public bool Equals(Notification obj) + { + return + this.body.TrueEqualsString(obj.body) && + this.site.TrueEquals(obj.site) && + this.creation_date.TrueEquals(obj.creation_date) && + this.post_id.TrueEquals(obj.post_id) && + this.is_unread.TrueEquals(obj.is_unread); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.body.TrueEqualsString((string)obj.body) && + ((this.site == null && obj.site == null) || this.site.EqualsDynamic(obj.site)) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.is_unread.TrueEquals((bool?)obj.is_unread); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Post.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Post.cs new file mode 100644 index 000000000..2f81cbd05 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Post.cs @@ -0,0 +1,114 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Post : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public PostType? post_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public ShallowUser owner { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public DateTime? last_activity_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public DateTime? last_edit_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public int? score { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public int? up_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public int? down_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public List comments { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public bool? upvoted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public bool? downvoted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(15), MessagePack.Key(15 - 1)] + public ShallowUser last_editor { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(16), MessagePack.Key(16 - 1)] + public int? comment_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(17), MessagePack.Key(17 - 1)] + public string body_markdown { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(18), MessagePack.Key(18 - 1)] + public string share_link { get; set; } + + public bool Equals(Post obj) + { + return + this.body.TrueEqualsString(obj.body) && + this.body_markdown.TrueEqualsString(obj.body_markdown) && + this.comment_count.TrueEquals(obj.comment_count) && + this.comments.TrueEqualsList(obj.comments) && + this.creation_date.TrueEquals(obj.creation_date) && + this.down_vote_count.TrueEquals(obj.down_vote_count) && + this.downvoted.TrueEquals(obj.downvoted) && + this.last_activity_date.TrueEquals(obj.last_activity_date) && + this.last_edit_date.TrueEquals(obj.last_edit_date) && + this.last_editor.TrueEquals(obj.last_editor) && + this.link.TrueEqualsString(obj.link) && + this.owner.TrueEquals(obj.owner) && + this.post_id.TrueEquals(obj.post_id) && + this.post_type.TrueEquals(obj.post_type) && + this.score.TrueEquals(obj.score) && + this.share_link.TrueEqualsString(obj.share_link) && + this.up_vote_count.TrueEquals(obj.up_vote_count) && + this.upvoted.TrueEquals(obj.upvoted); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.body.TrueEqualsString((string)obj.body) && + this.body_markdown.TrueEqualsString((string)obj.body_markdown) && + this.comment_count.TrueEquals((int?)obj.comment_count) && + this.comments.TrueEqualsListDynamic((IEnumerable)obj.comments) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.down_vote_count.TrueEquals((int?)obj.down_vote_count) && + this.downvoted.TrueEquals((bool?)obj.downvoted) && + this.last_activity_date.TrueEquals((DateTime?)obj.last_activity_date) && + this.last_edit_date.TrueEquals((DateTime?)obj.last_edit_date) && + ((this.last_editor == null && obj.last_editor == null) || this.last_editor.EqualsDynamic(obj.last_editor)) && + this.link.TrueEqualsString((string)obj.link) && + ((this.owner == null && obj.owner == null) || this.owner.EqualsDynamic(obj.owner)) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.post_type.TrueEquals((PostType?)obj.post_type) && + this.score.TrueEquals((int?)obj.score) && + this.share_link.TrueEqualsString((string)obj.share_link) && + this.up_vote_count.TrueEquals((int?)obj.up_vote_count) && + this.upvoted.TrueEquals((bool?)obj.upvoted); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Privilege.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Privilege.cs new file mode 100644 index 000000000..25dd1725b --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Privilege.cs @@ -0,0 +1,37 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Privilege : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string short_description { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string description { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? reputation { get; set; } + + public bool Equals(Privilege obj) + { + return + this.description.TrueEqualsString(obj.description) && + this.reputation.TrueEquals(obj.reputation) && + this.short_description.TrueEqualsString(obj.short_description); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.description.TrueEqualsString((string)obj.description) && + this.reputation.TrueEquals((int?)obj.reputation) && + this.short_description.TrueEqualsString((string)obj.short_description); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Question.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Question.cs new file mode 100644 index 000000000..37fce2ddc --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Question.cs @@ -0,0 +1,361 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Question : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public DateTime? last_edit_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public DateTime? last_activity_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public DateTime? locked_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? score { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public DateTime? community_owned_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public int? answer_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public int? accepted_answer_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public MigrationInfo migrated_to { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public MigrationInfo migrated_from { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public DateTime? bounty_closes_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public int? bounty_amount { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public DateTime? closed_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(15), MessagePack.Key(15 - 1)] + public DateTime? protected_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(16), MessagePack.Key(16 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(17), MessagePack.Key(17 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(18), MessagePack.Key(18 - 1)] + public List tags { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(19), MessagePack.Key(19 - 1)] + public string closed_reason { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(20), MessagePack.Key(20 - 1)] + public int? up_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(21), MessagePack.Key(21 - 1)] + public int? down_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(22), MessagePack.Key(22 - 1)] + public int? favorite_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(23), MessagePack.Key(23 - 1)] + public int? view_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(24), MessagePack.Key(24 - 1)] + public ShallowUser owner { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(25), MessagePack.Key(25 - 1)] + public List comments { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(26), MessagePack.Key(26 - 1)] + public List answers { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(27), MessagePack.Key(27 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(28), MessagePack.Key(28 - 1)] + public bool? is_answered { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(29), MessagePack.Key(29 - 1)] + public int? close_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(30), MessagePack.Key(30 - 1)] + public int? reopen_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(31), MessagePack.Key(31 - 1)] + public int? delete_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(32), MessagePack.Key(32 - 1)] + public Notice notice { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(33), MessagePack.Key(33 - 1)] + public bool? upvoted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(34), MessagePack.Key(34 - 1)] + public bool? downvoted { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(35), MessagePack.Key(35 - 1)] + public bool? favorited { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(36), MessagePack.Key(36 - 1)] + public ShallowUser last_editor { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(37), MessagePack.Key(37 - 1)] + public int? comment_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(38), MessagePack.Key(38 - 1)] + public string body_markdown { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(39), MessagePack.Key(39 - 1)] + public ClosedDetails closed_details { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(40), MessagePack.Key(40 - 1)] + public string share_link { get; set; } + + public bool Equals(Question obj) + { + return + this.accepted_answer_id.TrueEquals(obj.accepted_answer_id) && + this.answer_count.TrueEquals(obj.answer_count) && + this.answers.TrueEqualsList(obj.answers) && + this.body.TrueEqualsString(obj.body) && + this.body_markdown.TrueEqualsString(obj.body_markdown) && + this.bounty_amount.TrueEquals(obj.bounty_amount) && + this.bounty_closes_date.TrueEquals(obj.bounty_closes_date) && + this.close_vote_count.TrueEquals(obj.close_vote_count) && + this.closed_date.TrueEquals(obj.closed_date) && + this.closed_details.TrueEquals(obj.closed_details) && + this.closed_reason.TrueEqualsString(obj.closed_reason) && + this.comment_count.TrueEquals(obj.comment_count) && + this.comments.TrueEqualsList(obj.comments) && + this.community_owned_date.TrueEquals(obj.community_owned_date) && + this.creation_date.TrueEquals(obj.creation_date) && + this.delete_vote_count.TrueEquals(obj.delete_vote_count) && + this.down_vote_count.TrueEquals(obj.down_vote_count) && + this.downvoted.TrueEquals(obj.downvoted) && + this.favorite_count.TrueEquals(obj.favorite_count) && + this.favorited.TrueEquals(obj.favorited) && + this.is_answered.TrueEquals(obj.is_answered) && + this.last_activity_date.TrueEquals(obj.last_activity_date) && + this.last_edit_date.TrueEquals(obj.last_edit_date) && + this.last_editor.TrueEquals(obj.last_editor) && + this.link.TrueEqualsString(obj.link) && + this.locked_date.TrueEquals(obj.locked_date) && + this.migrated_from.TrueEquals(obj.migrated_from) && + this.migrated_to.TrueEquals(obj.migrated_to) && + this.notice.TrueEquals(obj.notice) && + this.owner.TrueEquals(obj.owner) && + this.protected_date.TrueEquals(obj.protected_date) && + this.question_id.TrueEquals(obj.question_id) && + this.reopen_vote_count.TrueEquals(obj.reopen_vote_count) && + this.score.TrueEquals(obj.score) && + this.share_link.TrueEqualsString(obj.share_link) && + this.tags.TrueEqualsString(obj.tags) && + this.title.TrueEqualsString(obj.title) && + this.up_vote_count.TrueEquals(obj.up_vote_count) && + this.upvoted.TrueEquals(obj.upvoted) && + this.view_count.TrueEquals(obj.view_count); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.accepted_answer_id.TrueEquals((int?)obj.accepted_answer_id) && + this.answer_count.TrueEquals((int?)obj.answer_count) && + this.answers.TrueEqualsListDynamic((IEnumerable)obj.answers) && + this.body.TrueEqualsString((string)obj.body) && + this.body_markdown.TrueEqualsString((string)obj.body_markdown) && + this.bounty_amount.TrueEquals((int?)obj.bounty_amount) && + this.bounty_closes_date.TrueEquals((DateTime?)obj.bounty_closes_date) && + this.close_vote_count.TrueEquals((int?)obj.close_vote_count) && + this.closed_date.TrueEquals((DateTime?)obj.closed_date) && + ((this.closed_details == null && obj.closed_details == null) || + this.closed_details.EqualsDynamic(obj.closed_details)) && + this.closed_reason.TrueEqualsString((string)obj.closed_reason) && + this.comment_count.TrueEquals((int?)obj.comment_count) && + this.comments.TrueEqualsListDynamic((IEnumerable)obj.comments) && + this.community_owned_date.TrueEquals((DateTime?)obj.community_owned_date) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.delete_vote_count.TrueEquals((int?)obj.delete_vote_count) && + this.down_vote_count.TrueEquals((int?)obj.down_vote_count) && + this.downvoted.TrueEquals((bool?)obj.downvoted) && + this.favorite_count.TrueEquals((int?)obj.favorite_count) && + this.favorited.TrueEquals((bool?)obj.favorited) && + this.is_answered.TrueEquals((bool?)obj.is_answered) && + this.last_activity_date.TrueEquals((DateTime?)obj.last_activity_date) && + this.last_edit_date.TrueEquals((DateTime?)obj.last_edit_date) && + ((this.last_editor == null && obj.last_editor == null) || this.last_editor.EqualsDynamic(obj.last_editor)) && + this.link.TrueEqualsString((string)obj.link) && + this.locked_date.TrueEquals((DateTime?)obj.locked_date) && + ((this.migrated_from == null && obj.migrated_from == null) || + this.migrated_from.EqualsDynamic(obj.migrated_from)) && + ((this.migrated_to == null && obj.migrated_to == null) || this.migrated_to.EqualsDynamic(obj.migrated_to)) && + ((this.notice == null && obj.notice == null) || this.notice.EqualsDynamic(obj.notice)) && + ((this.owner == null && obj.owner == null) || this.owner.EqualsDynamic(obj.owner)) && + this.protected_date.TrueEquals((DateTime?)obj.protected_date) && + this.question_id.TrueEquals((int?)obj.question_id) && + this.reopen_vote_count.TrueEquals((int?)obj.reopen_vote_count) && + this.score.TrueEquals((int?)obj.score) && + this.share_link.TrueEqualsString((string)obj.share_link) && + this.tags.TrueEqualsString((IEnumerable)obj.tags) && + this.title.TrueEqualsString((string)obj.title) && + this.up_vote_count.TrueEquals((int?)obj.up_vote_count) && + this.upvoted.TrueEquals((bool?)obj.upvoted) && + this.view_count.TrueEquals((int?)obj.view_count); + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class ClosedDetails : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public bool? on_hold { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string reason { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string description { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public List by_users { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public List original_questions { get; set; } + + public bool Equals(ClosedDetails obj) + { + return + this.by_users.TrueEqualsList(obj.by_users) && + this.description.TrueEqualsString(obj.description) && + this.on_hold.TrueEquals(obj.on_hold) && + this.original_questions.TrueEqualsList(obj.original_questions) && + this.reason.TrueEqualsString(obj.reason); + } + + public bool EqualsDynamic(dynamic obj) + { + dynamic oq = obj.original_questions; + var oqI = (IEnumerable)oq; + + return + this.by_users.TrueEqualsListDynamic((IEnumerable)obj.by_users) && + this.description.TrueEqualsString((string)obj.description) && + this.on_hold.TrueEquals((bool?)obj.on_hold) && + ////this.original_questions.TrueEqualsListDynamic((IEnumerable)obj.original_questions) && + this.original_questions.TrueEqualsListDynamic(oqI) && + this.reason.TrueEqualsString((string)obj.reason); + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class OriginalQuestion : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? answer_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? accepted_answer_id { get; set; } + + public bool Equals(OriginalQuestion obj) + { + return + this.accepted_answer_id.TrueEquals(obj.accepted_answer_id) && + this.answer_count.TrueEquals(obj.answer_count) && + this.question_id.TrueEquals(obj.question_id) && + this.title.TrueEqualsString(obj.title); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.accepted_answer_id.TrueEquals((int?)obj.accepted_answer_id) && + this.answer_count.TrueEquals((int?)obj.answer_count) && + this.question_id.TrueEquals((int?)obj.question_id) && + this.title.TrueEqualsString((string)obj.title); + } + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Notice : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? owner_user_id { get; set; } + + public bool Equals(Notice obj) + { + return + this.body.TrueEqualsString(obj.body) && + this.creation_date.TrueEquals(obj.creation_date) && + this.owner_user_id.TrueEquals(obj.owner_user_id); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.body.TrueEqualsString((string)obj.body) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.owner_user_id.TrueEquals((int?)obj.owner_user_id); + } + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class MigrationInfo : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public Info.Site other_site { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? on_date { get; set; } + + public bool Equals(MigrationInfo obj) + { + return + this.on_date.TrueEquals(obj.on_date) && + this.other_site.TrueEquals(obj.other_site) && + this.question_id.TrueEquals(obj.question_id); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.on_date.TrueEquals((DateTime?)obj.on_date) && + ((this.other_site == null && obj.other_site == null) || this.other_site.EqualsDynamic(obj.other_site)) && + this.question_id.TrueEquals((int?)obj.question_id); + } + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/QuestionTimeline.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/QuestionTimeline.cs new file mode 100644 index 000000000..76f1f2b94 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/QuestionTimeline.cs @@ -0,0 +1,85 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + public enum QuestionTimelineAction : byte + { + question = 1, + answer = 2, + comment = 3, + unaccepted_answer = 4, + accepted_answer = 5, + vote_aggregate = 6, + revision = 7, + post_state_changed = 8, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class QuestionTimeline : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public QuestionTimelineAction? timeline_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? comment_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string revision_guid { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? up_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? down_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public ShallowUser user { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public ShallowUser owner { get; set; } + + public bool Equals(QuestionTimeline obj) + { + return + this.comment_id.TrueEquals(obj.comment_id) && + this.creation_date.TrueEquals(obj.creation_date) && + this.down_vote_count.TrueEquals(obj.down_vote_count) && + this.owner.TrueEquals(obj.owner) && + this.post_id.TrueEquals(obj.post_id) && + this.question_id.TrueEquals(obj.question_id) && + this.revision_guid.TrueEqualsString(obj.revision_guid) && + this.timeline_type.TrueEquals(obj.timeline_type) && + this.up_vote_count.TrueEquals(obj.up_vote_count) && + this.user.TrueEquals(obj.user); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.comment_id.TrueEquals((int?)obj.comment_id) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.down_vote_count.TrueEquals((int?)obj.down_vote_count) && + ((this.owner == null && obj.owner == null) || this.owner.EqualsDynamic(obj.owner)) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.question_id.TrueEquals((int?)obj.question_id) && + this.revision_guid.TrueEqualsString((string)obj.revision_guid) && + this.timeline_type.TrueEquals((QuestionTimelineAction?)obj.timeline_type) && + this.up_vote_count.TrueEquals((int?)obj.up_vote_count) && + ((this.user == null && obj.user == null) || this.user.EqualsDynamic(obj.user)); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Reputation.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Reputation.cs new file mode 100644 index 000000000..7f032c73f --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Reputation.cs @@ -0,0 +1,74 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + public enum VoteType : byte + { + up_votes = 2, + down_votes = 3, + spam = 12, + accepts = 1, + bounties_won = 9, + bounties_offered = 8, + suggested_edits = 16, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Reputation : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? user_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public PostType? post_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public VoteType? vote_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? reputation_change { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public DateTime? on_date { get; set; } + + public bool Equals(Reputation obj) + { + return + this.link.TrueEqualsString(obj.link) && + this.on_date.TrueEquals(obj.on_date) && + this.post_id.TrueEquals(obj.post_id) && + this.post_type.TrueEquals(obj.post_type) && + this.reputation_change.TrueEquals(obj.reputation_change) && + this.title.TrueEqualsString(obj.title) && + this.user_id.TrueEquals(obj.user_id) && + this.vote_type.TrueEquals(obj.vote_type); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.link.TrueEqualsString((string)obj.link) && + this.on_date.TrueEquals((DateTime?)obj.on_date) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.post_type.TrueEquals((PostType?)obj.post_type) && + this.reputation_change.TrueEquals((int?)obj.reputation_change) && + this.title.TrueEqualsString((string)obj.title) && + this.user_id.TrueEquals((int?)obj.user_id) && + this.vote_type.TrueEquals((VoteType?)obj.vote_type); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/ReputationHistory.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/ReputationHistory.cs new file mode 100644 index 000000000..3e43eb422 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/ReputationHistory.cs @@ -0,0 +1,85 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class ReputationHistory : IGenericEquality + { + public enum ReputationHistoryType : byte + { + asker_accepts_answer = 1, + asker_unaccept_answer = 2, + answer_accepted = 3, + answer_unaccepted = 4, + + voter_downvotes = 5, + voter_undownvotes = 6, + post_downvoted = 7, + post_undownvoted = 8, + + post_upvoted = 9, + post_unupvoted = 10, + + suggested_edit_approval_received = 11, + + post_flagged_as_spam = 12, + post_flagged_as_offensive = 13, + + bounty_given = 14, + bounty_earned = 15, + bounty_cancelled = 16, + + post_deleted = 17, + post_undeleted = 18, + + association_bonus = 19, + arbitrary_reputation_change = 20, + + vote_fraud_reversal = 21, + + post_migrated = 22, + + user_deleted = 23, + } + + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? user_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? reputation_change { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public ReputationHistoryType? reputation_history_type { get; set; } + + public bool Equals(ReputationHistory obj) + { + return + this.creation_date.TrueEquals(obj.creation_date) && + this.post_id.TrueEquals(obj.post_id) && + this.reputation_change.TrueEquals(obj.reputation_change) && + this.reputation_history_type.TrueEquals(obj.reputation_history_type) && + this.user_id.TrueEquals(obj.user_id); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.reputation_change.TrueEquals((int?)obj.reputation_change) && + this.reputation_history_type.TrueEquals((ReputationHistoryType?)obj.reputation_history_type) && + this.user_id.TrueEquals((int?)obj.user_id); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Revision.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Revision.cs new file mode 100644 index 000000000..1a4588773 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Revision.cs @@ -0,0 +1,110 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + public enum RevisionType : byte + { + single_user = 1, + vote_based = 2, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Revision : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string revision_guid { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? revision_number { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public RevisionType? revision_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public PostType? post_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public string comment { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public bool? is_rollback { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public string last_body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public string last_title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public List last_tags { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public List tags { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(15), MessagePack.Key(15 - 1)] + public bool? set_community_wiki { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(16), MessagePack.Key(16 - 1)] + public ShallowUser user { get; set; } + + public bool Equals(Revision obj) + { + return + this.body.TrueEqualsString(obj.body) && + this.comment.TrueEqualsString(obj.comment) && + this.creation_date.TrueEquals(obj.creation_date) && + this.is_rollback.TrueEquals(obj.is_rollback) && + this.last_body.TrueEqualsString(obj.last_body) && + this.last_tags.TrueEqualsString(obj.last_tags) && + this.last_title.TrueEqualsString(obj.last_title) && + this.post_id.TrueEquals(obj.post_id) && + this.post_type.TrueEquals(obj.post_type) && + this.revision_guid.TrueEqualsString(obj.revision_guid) && + this.revision_number.TrueEquals(obj.revision_number) && + this.revision_type.TrueEquals(obj.revision_type) && + this.set_community_wiki.TrueEquals(obj.set_community_wiki) && + this.tags.TrueEqualsString(obj.tags) && + this.title.TrueEqualsString(obj.title) && + this.user.TrueEquals(obj.user); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.body.TrueEqualsString((string)obj.body) && + this.comment.TrueEqualsString((string)obj.comment) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.is_rollback.TrueEquals((bool?)obj.is_rollback) && + this.last_body.TrueEqualsString((string)obj.last_body) && + this.last_tags.TrueEqualsString((IEnumerable)obj.last_tags) && + this.last_title.TrueEqualsString((string)obj.last_title) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.post_type.TrueEquals((PostType?)obj.post_type) && + this.revision_guid.TrueEqualsString((string)obj.revision_guid) && + this.revision_number.TrueEquals((int?)obj.revision_number) && + this.revision_type.TrueEquals((RevisionType?)obj.revision_type) && + this.set_community_wiki.TrueEquals((bool?)obj.set_community_wiki) && + this.tags.TrueEqualsString((IEnumerable)obj.tags) && + this.title.TrueEqualsString((string)obj.title) && + ((this.user == null && obj.user == null) || this.user.EqualsDynamic(obj.user)); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/SearchExcerpt.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/SearchExcerpt.cs new file mode 100644 index 000000000..b08ecfcb0 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/SearchExcerpt.cs @@ -0,0 +1,123 @@ +// 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 ProtoBuf; + +#pragma warning disable SA1300 // Element should begin with upper-case letter + +namespace Benchmark.Models +{ + public enum SearchExcerptItemType : byte + { + question = 1, + answer = 2, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class SearchExcerpt : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string excerpt { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? community_owned_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public DateTime? locked_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public DateTime? last_activity_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public ShallowUser owner { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public ShallowUser last_activity_user { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public int? score { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public SearchExcerptItemType? item_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public int? question_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public bool? is_answered { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public int? answer_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(15), MessagePack.Key(15 - 1)] + public List tags { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(16), MessagePack.Key(16 - 1)] + public DateTime? closed_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(17), MessagePack.Key(17 - 1)] + public int? answer_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(18), MessagePack.Key(18 - 1)] + public bool? is_accepted { get; set; } + + public bool Equals(SearchExcerpt obj) + { + return + this.answer_count.TrueEquals(obj.answer_count) && + this.answer_id.TrueEquals(obj.answer_id) && + this.body.TrueEqualsString(obj.body) && + this.closed_date.TrueEquals(obj.closed_date) && + this.community_owned_date.TrueEquals(obj.community_owned_date) && + this.creation_date.TrueEquals(obj.creation_date) && + this.excerpt.TrueEqualsString(obj.excerpt) && + this.is_accepted.TrueEquals(obj.is_accepted) && + this.is_answered.TrueEquals(obj.is_answered) && + this.item_type.TrueEquals(obj.item_type) && + this.last_activity_date.TrueEquals(obj.last_activity_date) && + this.last_activity_user.TrueEquals(obj.last_activity_user) && + this.locked_date.TrueEquals(obj.locked_date) && + this.owner.TrueEquals(obj.owner) && + this.question_id.TrueEquals(obj.question_id) && + this.score.TrueEquals(obj.score) && + this.tags.TrueEqualsString(obj.tags) && + this.title.TrueEqualsString(obj.title); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.answer_count.TrueEquals((int?)obj.answer_count) && + this.answer_id.TrueEquals((int?)obj.answer_id) && + this.body.TrueEqualsString((string)obj.body) && + this.closed_date.TrueEquals((DateTime?)obj.closed_date) && + this.community_owned_date.TrueEquals((DateTime?)obj.community_owned_date) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.excerpt.TrueEqualsString((string)obj.excerpt) && + this.is_accepted.TrueEquals((bool?)obj.is_accepted) && + this.is_answered.TrueEquals((bool?)obj.is_answered) && + this.item_type.TrueEquals((SearchExcerptItemType?)obj.item_type) && + this.last_activity_date.TrueEquals((DateTime?)obj.last_activity_date) && + ((this.last_activity_user == null && obj.last_activity_user == null) || + this.last_activity_user.EqualsDynamic(obj.last_activity_user)) && + this.locked_date.TrueEquals((DateTime?)obj.locked_date) && + ((this.owner == null && obj.owner == null) || this.owner.EqualsDynamic(obj.owner)) && + this.question_id.TrueEquals((int?)obj.question_id) && + this.score.TrueEquals((int?)obj.score) && + this.tags.TrueEqualsString((IEnumerable)obj.tags) && + this.title.TrueEqualsString((string)obj.title); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/ShallowUser.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/ShallowUser.cs new file mode 100644 index 000000000..c0371d7bb --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/ShallowUser.cs @@ -0,0 +1,112 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable IDE1006 +#pragma warning disable SA1516 + + +using ProtoBuf; + +namespace Benchmark.Models +{ + public enum UserType : byte + { + unregistered = 2, + registered = 3, + moderator = 4, + does_not_exist = 255, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class ShallowUser : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? user_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string display_name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? reputation { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public UserType? user_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string profile_image { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? accept_rate { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public User.BadgeCount badge_counts { get; set; } + + public bool Equals(ShallowUser obj) + { + return + this.accept_rate.TrueEquals(obj.accept_rate) && + this.badge_counts.TrueEquals(obj.badge_counts) && + this.display_name.TrueEqualsString(obj.display_name) && + this.link.TrueEqualsString(obj.link) && + this.profile_image.TrueEqualsString(obj.profile_image) && + this.reputation.TrueEquals(obj.reputation) && + this.user_id.TrueEquals(obj.user_id) && + this.user_type.TrueEquals(obj.user_type); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.accept_rate.TrueEquals((int?)obj.accept_rate) && + ((this.badge_counts == null && obj.badge_counts == null) || this.badge_counts.EqualsDynamic(obj.badge_counts)) && + this.display_name.TrueEqualsString((string)obj.display_name) && + this.link.TrueEqualsString((string)obj.link) && + this.profile_image.TrueEqualsString((string)obj.profile_image) && + this.reputation.TrueEquals((int?)obj.reputation) && + this.user_id.TrueEquals((int?)obj.user_id) && + this.user_type.TrueEquals((UserType?)obj.user_type); + } + } + + [MessagePack.MessagePackObject(true)] + public class ShallowUser2 : IGenericEquality + { + public int? user_id { get; set; } + public string display_name { get; set; } + public int? reputation { get; set; } + public UserType? user_type { get; set; } + public string profile_image { get; set; } + public string link { get; set; } + public int? accept_rate { get; set; } + public User.BadgeCount2 badge_counts { get; set; } + + public bool Equals(ShallowUser2 obj) + { + return + this.accept_rate.TrueEquals(obj.accept_rate) && + this.badge_counts.TrueEquals(obj.badge_counts) && + this.display_name.TrueEqualsString(obj.display_name) && + this.link.TrueEqualsString(obj.link) && + this.profile_image.TrueEqualsString(obj.profile_image) && + this.reputation.TrueEquals(obj.reputation) && + this.user_id.TrueEquals(obj.user_id) && + this.user_type.TrueEquals(obj.user_type); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.accept_rate.TrueEquals((int?)obj.accept_rate) && + ((this.badge_counts == null && obj.badge_counts == null) || this.badge_counts.EqualsDynamic(obj.badge_counts)) && + this.display_name.TrueEqualsString((string)obj.display_name) && + this.link.TrueEqualsString((string)obj.link) && + this.profile_image.TrueEqualsString((string)obj.profile_image) && + this.reputation.TrueEquals((int?)obj.reputation) && + this.user_id.TrueEquals((int?)obj.user_id) && + this.user_type.TrueEquals((UserType?)obj.user_type); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/SuggestedEdit.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/SuggestedEdit.cs new file mode 100644 index 000000000..caaafcef6 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/SuggestedEdit.cs @@ -0,0 +1,80 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class SuggestedEdit : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? suggested_edit_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public PostType? post_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public List tags { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public string comment { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public DateTime? approval_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public DateTime? rejection_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public ShallowUser proposing_user { get; set; } + + public bool Equals(SuggestedEdit obj) + { + return + this.approval_date.TrueEquals(obj.approval_date) && + this.body.TrueEqualsString(obj.body) && + this.comment.TrueEqualsString(obj.comment) && + this.creation_date.TrueEquals(obj.creation_date) && + this.post_id.TrueEquals(obj.post_id) && + this.post_type.TrueEquals(obj.post_type) && + this.proposing_user.TrueEquals(obj.proposing_user) && + this.rejection_date.TrueEquals(obj.rejection_date) && + this.suggested_edit_id.TrueEquals(obj.suggested_edit_id) && + this.tags.TrueEqualsString(obj.tags) && + this.title.TrueEqualsString(obj.title); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.approval_date.TrueEquals((DateTime?)obj.approval_date) && + this.body.TrueEqualsString((string)obj.body) && + this.comment.TrueEqualsString((string)obj.comment) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.post_type.TrueEquals((PostType?)obj.post_type) && + ((this.proposing_user == null && obj.proposing_user == null) || + this.proposing_user.EqualsDynamic(obj.proposing_user)) && + this.rejection_date.TrueEquals((DateTime?)obj.rejection_date) && + this.suggested_edit_id.TrueEquals((int?)obj.suggested_edit_id) && + this.tags.TrueEqualsString((IEnumerable)obj.tags) && + this.title.TrueEqualsString((string)obj.title); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/Tag.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Tag.cs new file mode 100644 index 000000000..60164f344 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/Tag.cs @@ -0,0 +1,64 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class Tag : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public bool? is_required { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public bool? is_moderator_only { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? user_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public bool? has_synonyms { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public DateTime? last_activity_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public List synonyms { get; set; } + + public bool Equals(Tag obj) + { + return + this.count.TrueEquals(obj.count) && + this.has_synonyms.TrueEquals(obj.has_synonyms) && + this.is_moderator_only.TrueEquals(obj.is_moderator_only) && + this.is_required.TrueEquals(obj.is_required) && + this.last_activity_date.TrueEquals(obj.last_activity_date) && + this.name.TrueEqualsString(obj.name) && + this.synonyms.TrueEqualsString(obj.synonyms) && + this.user_id.TrueEquals(obj.user_id); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.count.TrueEquals((int?)obj.count) && + this.has_synonyms.TrueEquals((bool?)obj.has_synonyms) && + this.is_moderator_only.TrueEquals((bool?)obj.is_moderator_only) && + this.is_required.TrueEquals((bool?)obj.is_required) && + this.last_activity_date.TrueEquals((DateTime?)obj.last_activity_date) && + this.name.TrueEqualsString((string)obj.name) && + this.synonyms.TrueEqualsString((IEnumerable)obj.synonyms) && + this.user_id.TrueEquals((int?)obj.user_id); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagScore.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagScore.cs new file mode 100644 index 000000000..a7e7b225d --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagScore.cs @@ -0,0 +1,37 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class TagScore : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public ShallowUser user { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? score { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? post_count { get; set; } + + public bool Equals(TagScore obj) + { + return + this.post_count.TrueEquals(obj.post_count) && + this.score.TrueEquals(obj.score) && + this.user.TrueEquals(obj.user); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.post_count.TrueEquals((int?)obj.post_count) && + this.score.TrueEquals((int?)obj.score) && + ((this.user == null && obj.user == null) || this.user.EqualsDynamic(obj.user)); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagSynonym.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagSynonym.cs new file mode 100644 index 000000000..8006725e2 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagSynonym.cs @@ -0,0 +1,48 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class TagSynonym : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string from_tag { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string to_tag { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? applied_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public DateTime? last_applied_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public DateTime? creation_date { get; set; } + + public bool Equals(TagSynonym obj) + { + return + this.applied_count.TrueEquals(obj.applied_count) && + this.creation_date.TrueEquals(obj.creation_date) && + this.from_tag.TrueEqualsString(obj.from_tag) && + this.last_applied_date.TrueEquals(obj.last_applied_date) && + this.to_tag.TrueEqualsString(obj.to_tag); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.applied_count.TrueEquals((int?)obj.applied_count) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.from_tag.TrueEqualsString((string)obj.from_tag) && + this.last_applied_date.TrueEquals((DateTime?)obj.last_applied_date) && + this.to_tag.TrueEqualsString((string)obj.to_tag); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagWiki.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagWiki.cs new file mode 100644 index 000000000..3fb7d2bcc --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/TagWiki.cs @@ -0,0 +1,60 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class TagWiki : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string tag_name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string body { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public string excerpt { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public DateTime? body_last_edit_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public DateTime? excerpt_last_edit_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public ShallowUser last_body_editor { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public ShallowUser last_excerpt_editor { get; set; } + + public bool Equals(TagWiki obj) + { + return + this.body.TrueEqualsString(obj.body) && + this.body_last_edit_date.TrueEquals(obj.body_last_edit_date) && + this.excerpt.TrueEqualsString(obj.excerpt) && + this.excerpt_last_edit_date.TrueEquals(obj.excerpt_last_edit_date) && + this.last_body_editor.TrueEquals(obj.last_body_editor) && + this.last_excerpt_editor.TrueEquals(obj.last_excerpt_editor) && + this.tag_name.TrueEqualsString(obj.tag_name); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.body.TrueEqualsString((string)obj.body) && + this.body_last_edit_date.TrueEquals((DateTime?)obj.body_last_edit_date) && + this.excerpt.TrueEqualsString((string)obj.excerpt) && + this.excerpt_last_edit_date.TrueEquals((DateTime?)obj.excerpt_last_edit_date) && + ((this.last_body_editor == null && obj.last_body_editor == null) || + this.last_body_editor.EqualsDynamic(obj.last_body_editor)) && + ((this.last_excerpt_editor == null && obj.last_excerpt_editor == null) || + this.last_excerpt_editor.EqualsDynamic(obj.last_excerpt_editor)) && + this.tag_name.TrueEqualsString((string)obj.tag_name); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/TopTag.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/TopTag.cs new file mode 100644 index 000000000..5776f4978 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/TopTag.cs @@ -0,0 +1,52 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class TopTag : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public string tag_name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? question_score { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? question_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? answer_score { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? answer_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? user_id { get; set; } + + public bool Equals(TopTag obj) + { + return + this.answer_count.TrueEquals(obj.answer_count) && + this.answer_score.TrueEquals(obj.answer_score) && + this.question_count.TrueEquals(obj.question_count) && + this.question_score.TrueEquals(obj.question_score) && + this.tag_name.TrueEqualsString(obj.tag_name) && + this.user_id.TrueEquals(obj.user_id); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.answer_count.TrueEquals((int?)obj.answer_count) && + this.answer_score.TrueEquals((int?)obj.answer_score) && + this.question_count.TrueEquals((int?)obj.question_count) && + this.question_score.TrueEquals((int?)obj.question_score) && + this.tag_name.TrueEqualsString((string)obj.tag_name) && + this.user_id.TrueEquals((int?)obj.user_id); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/User.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/User.cs new file mode 100644 index 000000000..e55106802 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/User.cs @@ -0,0 +1,219 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma warning disable IDE1006 +#pragma warning disable SA1516 + + +using System; +using ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class User : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? user_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public UserType? user_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public string display_name { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public string profile_image { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? reputation { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? reputation_change_day { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public int? reputation_change_week { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public int? reputation_change_month { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public int? reputation_change_quarter { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public int? reputation_change_year { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(12), MessagePack.Key(12 - 1)] + public int? age { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(13), MessagePack.Key(13 - 1)] + public DateTime? last_access_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(14), MessagePack.Key(14 - 1)] + public DateTime? last_modified_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(15), MessagePack.Key(15 - 1)] + public bool? is_employee { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(16), MessagePack.Key(16 - 1)] + public string link { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(17), MessagePack.Key(17 - 1)] + public string website_url { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(18), MessagePack.Key(18 - 1)] + public string location { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(19), MessagePack.Key(19 - 1)] + public int? account_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(20), MessagePack.Key(20 - 1)] + public DateTime? timed_penalty_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(21), MessagePack.Key(21 - 1)] + public BadgeCount badge_counts { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(22), MessagePack.Key(22 - 1)] + public int? question_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(23), MessagePack.Key(23 - 1)] + public int? answer_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(24), MessagePack.Key(24 - 1)] + public int? up_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(25), MessagePack.Key(25 - 1)] + public int? down_vote_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(26), MessagePack.Key(26 - 1)] + public string about_me { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(27), MessagePack.Key(27 - 1)] + public int? view_count { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(28), MessagePack.Key(28 - 1)] + public int? accept_rate { get; set; } + + public bool Equals(User obj) + { + return + this.about_me.TrueEqualsString(obj.about_me) && + this.accept_rate.TrueEquals(obj.accept_rate) && + this.account_id.TrueEquals(obj.account_id) && + this.age.TrueEquals(obj.age) && + this.answer_count.TrueEquals(obj.answer_count) && + this.badge_counts.TrueEquals(obj.badge_counts) && + this.creation_date.TrueEquals(obj.creation_date) && + this.display_name.TrueEqualsString(obj.display_name) && + this.down_vote_count.TrueEquals(obj.down_vote_count) && + this.is_employee.TrueEquals(obj.is_employee) && + this.last_access_date.TrueEquals(obj.last_access_date) && + this.last_modified_date.TrueEquals(obj.last_modified_date) && + this.link.TrueEqualsString(obj.link) && + this.location.TrueEqualsString(obj.location) && + this.profile_image.TrueEqualsString(obj.profile_image) && + this.question_count.TrueEquals(obj.question_count) && + this.reputation.TrueEquals(obj.reputation) && + this.reputation_change_day.TrueEquals(obj.reputation_change_day) && + this.reputation_change_month.TrueEquals(obj.reputation_change_month) && + this.reputation_change_quarter.TrueEquals(obj.reputation_change_quarter) && + this.reputation_change_week.TrueEquals(obj.reputation_change_week) && + this.reputation_change_year.TrueEquals(obj.reputation_change_year) && + this.timed_penalty_date.TrueEquals(obj.timed_penalty_date) && + this.up_vote_count.TrueEquals(obj.up_vote_count) && + this.user_id.TrueEquals(obj.user_id) && + this.user_type.TrueEquals(obj.user_type) && + this.view_count.TrueEquals(obj.view_count) && + this.website_url.TrueEqualsString(obj.website_url); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.about_me.TrueEqualsString((string)obj.about_me) && + this.accept_rate.TrueEquals((int?)obj.accept_rate) && + this.account_id.TrueEquals((int?)obj.account_id) && + this.age.TrueEquals((int?)obj.age) && + this.answer_count.TrueEquals((int?)obj.answer_count) && + ((this.badge_counts == null && obj.badge_counts == null) || this.badge_counts.EqualsDynamic(obj.badge_counts)) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.display_name.TrueEqualsString((string)obj.display_name) && + this.down_vote_count.TrueEquals((int?)obj.down_vote_count) && + this.is_employee.TrueEquals((bool?)obj.is_employee) && + this.last_access_date.TrueEquals((DateTime?)obj.last_access_date) && + this.last_modified_date.TrueEquals((DateTime?)obj.last_modified_date) && + this.link.TrueEqualsString((string)obj.link) && + this.location.TrueEqualsString((string)obj.location) && + this.profile_image.TrueEqualsString((string)obj.profile_image) && + this.question_count.TrueEquals((int?)obj.question_count) && + this.reputation.TrueEquals((int?)obj.reputation) && + this.reputation_change_day.TrueEquals((int?)obj.reputation_change_day) && + this.reputation_change_month.TrueEquals((int?)obj.reputation_change_month) && + this.reputation_change_quarter.TrueEquals((int?)obj.reputation_change_quarter) && + this.reputation_change_week.TrueEquals((int?)obj.reputation_change_week) && + this.reputation_change_year.TrueEquals((int?)obj.reputation_change_year) && + this.timed_penalty_date.TrueEquals((DateTime?)obj.timed_penalty_date) && + this.up_vote_count.TrueEquals((int?)obj.up_vote_count) && + this.user_id.TrueEquals((int?)obj.user_id) && + this.user_type.TrueEquals((UserType?)obj.user_type) && + this.view_count.TrueEquals((int?)obj.view_count) && + this.website_url.TrueEqualsString((string)obj.website_url); + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class BadgeCount : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? gold { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public int? silver { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public int? bronze { get; set; } + + public bool Equals(BadgeCount obj) + { + return + this.bronze.TrueEquals(obj.bronze) && + this.silver.TrueEquals(obj.silver) && + this.gold.TrueEquals(obj.gold); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.bronze.TrueEquals((int?)obj.bronze) && + this.silver.TrueEquals((int?)obj.silver) && + this.gold.TrueEquals((int?)obj.gold); + } + } + + [MessagePack.MessagePackObject(true)] + public class BadgeCount2 : IGenericEquality + { + public int? gold { get; set; } + public int? silver { get; set; } + public int? bronze { get; set; } + + public bool Equals(BadgeCount2 obj) + { + return + this.bronze.TrueEquals(obj.bronze) && + this.silver.TrueEquals(obj.silver) && + this.gold.TrueEquals(obj.gold); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.bronze.TrueEquals((int?)obj.bronze) && + this.silver.TrueEquals((int?)obj.silver) && + this.gold.TrueEquals((int?)obj.gold); + } + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/UserTimeline.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/UserTimeline.cs new file mode 100644 index 000000000..435f125c7 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/UserTimeline.cs @@ -0,0 +1,90 @@ +// 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 ProtoBuf; + +namespace Benchmark.Models +{ + public enum UserTimelineType : byte + { + commented = 1, + asked = 2, + answered = 3, + badge = 4, + revision = 5, + accepted = 6, + reviewed = 7, + suggested = 8, + } + + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class UserTimeline : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public DateTime? creation_date { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public PostType? post_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public UserTimelineType? timeline_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public int? user_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public int? post_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? comment_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? suggested_edit_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(8), MessagePack.Key(8 - 1)] + public int? badge_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(9), MessagePack.Key(9 - 1)] + public string title { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(10), MessagePack.Key(10 - 1)] + public string detail { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(11), MessagePack.Key(11 - 1)] + public string link { get; set; } + + public bool Equals(UserTimeline obj) + { + return + this.badge_id.TrueEquals(obj.badge_id) && + this.comment_id.TrueEquals(obj.comment_id) && + this.creation_date.TrueEquals(obj.creation_date) && + this.detail.TrueEqualsString(obj.detail) && + this.link.TrueEqualsString(obj.link) && + this.post_id.TrueEquals(obj.post_id) && + this.post_type.TrueEquals(obj.post_type) && + this.suggested_edit_id.TrueEquals(obj.suggested_edit_id) && + this.timeline_type.TrueEquals(obj.timeline_type) && + this.title.TrueEqualsString(obj.title) && + this.user_id.TrueEquals(obj.user_id); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.badge_id.TrueEquals((int?)obj.badge_id) && + this.comment_id.TrueEquals((int?)obj.comment_id) && + this.creation_date.TrueEquals((DateTime?)obj.creation_date) && + this.detail.TrueEqualsString((string)obj.detail) && + this.link.TrueEqualsString((string)obj.link) && + this.post_id.TrueEquals((int?)obj.post_id) && + this.post_type.TrueEquals((PostType?)obj.post_type) && + this.suggested_edit_id.TrueEquals((int?)obj.suggested_edit_id) && + this.timeline_type.TrueEquals((UserTimelineType?)obj.timeline_type) && + this.title.TrueEqualsString((string)obj.title) && + this.user_id.TrueEquals((int?)obj.user_id); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Models/WritePermission.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Models/WritePermission.cs new file mode 100644 index 000000000..61e1af74c --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Models/WritePermission.cs @@ -0,0 +1,57 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using ProtoBuf; + +namespace Benchmark.Models +{ + [ProtoContract, System.Serializable, System.Runtime.Serialization.DataContract, MessagePack.MessagePackObject] + public class WritePermission : IGenericEquality + { + [System.Runtime.Serialization.DataMember, ProtoMember(1), MessagePack.Key(1 - 1)] + public int? user_id { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(2), MessagePack.Key(2 - 1)] + public string object_type { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(3), MessagePack.Key(3 - 1)] + public bool? can_add { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(4), MessagePack.Key(4 - 1)] + public bool? can_edit { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(5), MessagePack.Key(5 - 1)] + public bool? can_delete { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(6), MessagePack.Key(6 - 1)] + public int? max_daily_actions { get; set; } + + [System.Runtime.Serialization.DataMember, ProtoMember(7), MessagePack.Key(7 - 1)] + public int? min_seconds_between_actions { get; set; } + + public bool Equals(WritePermission obj) + { + return + this.can_add.TrueEquals(obj.can_add) && + this.can_delete.TrueEquals(obj.can_delete) && + this.can_edit.TrueEquals(obj.can_edit) && + this.max_daily_actions.TrueEquals(obj.max_daily_actions) && + this.min_seconds_between_actions.TrueEquals(obj.min_seconds_between_actions) && + this.object_type.TrueEqualsString(obj.object_type) && + this.user_id.TrueEquals(obj.user_id); + } + + public bool EqualsDynamic(dynamic obj) + { + return + this.can_add.TrueEquals((bool?)obj.can_add) && + this.can_delete.TrueEquals((bool?)obj.can_delete) && + this.can_edit.TrueEquals((bool?)obj.can_edit) && + this.max_daily_actions.TrueEquals((int?)obj.max_daily_actions) && + this.min_seconds_between_actions.TrueEquals((int?)obj.min_seconds_between_actions) && + this.object_type.TrueEqualsString((string)obj.object_type) && + this.user_id.TrueEquals((int?)obj.user_id); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/SerializerBenchmark.cs b/test/Benchmark/MessagePackCsharp/Benchmark/SerializerBenchmark.cs new file mode 100644 index 000000000..3070e6268 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/SerializerBenchmark.cs @@ -0,0 +1,1234 @@ +// 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 Benchmark.Fixture; +using Benchmark.Models; +using Benchmark.Serializers; +using BenchmarkDotNet.Attributes; + +#pragma warning disable SA1300 // Element should begin with upper-case letter +#pragma warning disable SA1306 // Field names should begin with lower-case letter +#pragma warning disable SA1401 // Fields should be private +#pragma warning disable SA1649 // File name should match first type name +#pragma warning disable IDE004 +#pragma warning disable IDE0049 + +namespace Benchmark +{ + [Config(typeof(BenchmarkConfig))] + public class AllSerializerBenchmark_BytesInOut + { + [ParamsSource(nameof(Serializers))] + public SerializerBase Serializer; + + // Currently BenchmarkdDotNet does not detect inherited ParamsSource so use copy and paste:) + public IEnumerable Serializers => new SerializerBase[] + { + new MessagePack_v2(), + new MessagePackLz4_v2(), + new MsgPack_v2_opt(), + //new MsgPack_v2_string(), + //new MsgPack_v2_str_lz4(), + new ProtobufNet(), + new JsonNet(), + new BinaryFormatter_(), + new DataContract_(), + new Hyperion_(), + new Jil_(), + new SpanJson_(), + new Utf8Json_(), + new SystemTextJson(), + new MsgPackCli(), + new MsgPackCli_v2(), + new FsPickler_(), + new Ceras_(), + }; + + protected static readonly ExpressionTreeFixture ExpressionTreeFixture = new ExpressionTreeFixture(); + + // primitives + protected static readonly sbyte SByteInput = ExpressionTreeFixture.Create(); + protected static readonly short ShortInput = ExpressionTreeFixture.Create(); + protected static readonly int IntInput = ExpressionTreeFixture.Create(); + protected static readonly long LongInput = ExpressionTreeFixture.Create(); + protected static readonly byte ByteInput = ExpressionTreeFixture.Create(); + protected static readonly ushort UShortInput = ExpressionTreeFixture.Create(); + protected static readonly uint UIntInput = ExpressionTreeFixture.Create(); + protected static readonly ulong ULongInput = ExpressionTreeFixture.Create(); + protected static readonly bool BoolInput = ExpressionTreeFixture.Create(); + protected static readonly string StringInput = ExpressionTreeFixture.Create(); + protected static readonly char CharInput = ExpressionTreeFixture.Create(); + protected static readonly DateTime DateTimeInput = ExpressionTreeFixture.Create(); + protected static readonly Guid GuidInput = ExpressionTreeFixture.Create(); + protected static readonly byte[] BytesInput = ExpressionTreeFixture.Create(); + + // models + protected static readonly Benchmark.Models.AccessToken AccessTokenInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.AccountMerge AccountMergeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Answer AnswerInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Badge BadgeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Comment CommentInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Error ErrorInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Event EventInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileFeed MobileFeedInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileQuestion MobileQuestionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileRepChange MobileRepChangeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileInboxItem MobileInboxItemInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileBadgeAward MobileBadgeAwardInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobilePrivilege MobilePrivilegeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileCommunityBulletin MobileCommunityBulletinInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileAssociationBonus MobileAssociationBonusInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileCareersJobAd MobileCareersJobAdInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileBannerAd MobileBannerAdInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileUpdateNotice MobileUpdateNoticeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.FlagOption FlagOptionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.InboxItem InboxItemInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Info InfoInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.NetworkUser NetworkUserInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Notification NotificationInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Post PostInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Privilege PrivilegeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question QuestionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.QuestionTimeline QuestionTimelineInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Reputation ReputationInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.ReputationHistory ReputationHistoryInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Revision RevisionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.SearchExcerpt SearchExcerptInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.ShallowUser ShallowUserInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.SuggestedEdit SuggestedEditInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Tag TagInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.TagScore TagScoreInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.TagSynonym TagSynonymInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.TagWiki TagWikiInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.TopTag TopTagInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.User UserInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.UserTimeline UserTimelineInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.WritePermission WritePermissionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileBannerAd.MobileBannerAdImage MobileBannerAdImageInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Info.Site SiteInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Info.RelatedSite RelatedSiteInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question.ClosedDetails ClosedDetailsInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question.Notice NoticeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question.MigrationInfo MigrationInfoInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.User.BadgeCount BadgeCountInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Info.Site.Styling StylingInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question.ClosedDetails.OriginalQuestion OriginalQuestionInput = ExpressionTreeFixture.Create(); + + private object SByteOutput; + private object ShortOutput; + private object IntOutput; + private object LongOutput; + private object ByteOutput; + private object UShortOutput; + private object UIntOutput; + private object ULongOutput; + private object BoolOutput; + private object StringOutput; + private object CharOutput; + private object DateTimeOutput; + private object GuidOutput; + private object BytesOutput; + + private object AccessTokenOutput; + private object AccountMergeOutput; + private object AnswerOutput; + private object BadgeOutput; + private object CommentOutput; + private object ErrorOutput; + private object EventOutput; + private object MobileFeedOutput; + private object MobileQuestionOutput; + private object MobileRepChangeOutput; + private object MobileInboxItemOutput; + private object MobileBadgeAwardOutput; + private object MobilePrivilegeOutput; + private object MobileCommunityBulletinOutput; + private object MobileAssociationBonusOutput; + private object MobileCareersJobAdOutput; + private object MobileBannerAdOutput; + private object MobileUpdateNoticeOutput; + private object FlagOptionOutput; + private object InboxItemOutput; + private object InfoOutput; + private object NetworkUserOutput; + private object NotificationOutput; + private object PostOutput; + private object PrivilegeOutput; + private object QuestionOutput; + private object QuestionTimelineOutput; + private object ReputationOutput; + private object ReputationHistoryOutput; + private object RevisionOutput; + private object SearchExcerptOutput; + private object ShallowUserOutput; + private object SuggestedEditOutput; + private object TagOutput; + private object TagScoreOutput; + private object TagSynonymOutput; + private object TagWikiOutput; + private object TopTagOutput; + private object UserOutput; + private object UserTimelineOutput; + private object WritePermissionOutput; + private object MobileBannerAdImageOutput; + private object SiteOutput; + private object RelatedSiteOutput; + private object ClosedDetailsOutput; + private object NoticeOutput; + private object MigrationInfoOutput; + private object BadgeCountOutput; + private object StylingOutput; + private object OriginalQuestionOutput; + + [GlobalSetup] + public void Setup() + { + // primitives + this.SByteOutput = this.Serializer.Serialize(SByteInput); + this.ShortOutput = this.Serializer.Serialize(ShortInput); + this.IntOutput = this.Serializer.Serialize(IntInput); + this.LongOutput = this.Serializer.Serialize(LongInput); + this.ByteOutput = this.Serializer.Serialize(ByteInput); + this.UShortOutput = this.Serializer.Serialize(UShortInput); + this.UIntOutput = this.Serializer.Serialize(UIntInput); + this.ULongOutput = this.Serializer.Serialize(ULongInput); + this.BoolOutput = this.Serializer.Serialize(BoolInput); + this.StringOutput = this.Serializer.Serialize(StringInput); + this.CharOutput = this.Serializer.Serialize(CharInput); + this.DateTimeOutput = this.Serializer.Serialize(DateTimeInput); + this.GuidOutput = this.Serializer.Serialize(GuidInput); + this.BytesOutput = this.Serializer.Serialize(BytesInput); + + // models + this.AccessTokenOutput = this.Serializer.Serialize(AccessTokenInput); + this.AccountMergeOutput = this.Serializer.Serialize(AccountMergeInput); + this.AnswerOutput = this.Serializer.Serialize(AnswerInput); + this.BadgeOutput = this.Serializer.Serialize(BadgeInput); + this.CommentOutput = this.Serializer.Serialize(CommentInput); + this.ErrorOutput = this.Serializer.Serialize(ErrorInput); + this.EventOutput = this.Serializer.Serialize(EventInput); + this.MobileFeedOutput = this.Serializer.Serialize(MobileFeedInput); + this.MobileQuestionOutput = this.Serializer.Serialize(MobileQuestionInput); + this.MobileRepChangeOutput = this.Serializer.Serialize(MobileRepChangeInput); + this.MobileInboxItemOutput = this.Serializer.Serialize(MobileInboxItemInput); + this.MobileBadgeAwardOutput = this.Serializer.Serialize(MobileBadgeAwardInput); + this.MobilePrivilegeOutput = this.Serializer.Serialize(MobilePrivilegeInput); + this.MobileCommunityBulletinOutput = this.Serializer.Serialize(MobileCommunityBulletinInput); + this.MobileAssociationBonusOutput = this.Serializer.Serialize(MobileAssociationBonusInput); + this.MobileCareersJobAdOutput = this.Serializer.Serialize(MobileCareersJobAdInput); + this.MobileBannerAdOutput = this.Serializer.Serialize(MobileBannerAdInput); + this.MobileUpdateNoticeOutput = this.Serializer.Serialize(MobileUpdateNoticeInput); + this.FlagOptionOutput = this.Serializer.Serialize(FlagOptionInput); + this.InboxItemOutput = this.Serializer.Serialize(InboxItemInput); + this.InfoOutput = this.Serializer.Serialize(InfoInput); + this.NetworkUserOutput = this.Serializer.Serialize(NetworkUserInput); + this.NotificationOutput = this.Serializer.Serialize(NotificationInput); + this.PostOutput = this.Serializer.Serialize(PostInput); + this.PrivilegeOutput = this.Serializer.Serialize(PrivilegeInput); + this.QuestionOutput = this.Serializer.Serialize(QuestionInput); + this.QuestionTimelineOutput = this.Serializer.Serialize(QuestionTimelineInput); + this.ReputationOutput = this.Serializer.Serialize(ReputationInput); + this.ReputationHistoryOutput = this.Serializer.Serialize(ReputationHistoryInput); + this.RevisionOutput = this.Serializer.Serialize(RevisionInput); + this.SearchExcerptOutput = this.Serializer.Serialize(SearchExcerptInput); + this.ShallowUserOutput = this.Serializer.Serialize(ShallowUserInput); + this.SuggestedEditOutput = this.Serializer.Serialize(SuggestedEditInput); + this.TagOutput = this.Serializer.Serialize(TagInput); + this.TagScoreOutput = this.Serializer.Serialize(TagScoreInput); + this.TagSynonymOutput = this.Serializer.Serialize(TagSynonymInput); + this.TagWikiOutput = this.Serializer.Serialize(TagWikiInput); + this.TopTagOutput = this.Serializer.Serialize(TopTagInput); + this.UserOutput = this.Serializer.Serialize(UserInput); + this.UserTimelineOutput = this.Serializer.Serialize(UserTimelineInput); + this.WritePermissionOutput = this.Serializer.Serialize(WritePermissionInput); + this.MobileBannerAdImageOutput = this.Serializer.Serialize(MobileBannerAdImageInput); + this.SiteOutput = this.Serializer.Serialize(SiteInput); + this.RelatedSiteOutput = this.Serializer.Serialize(RelatedSiteInput); + this.ClosedDetailsOutput = this.Serializer.Serialize(ClosedDetailsInput); + this.NoticeOutput = this.Serializer.Serialize(NoticeInput); + this.MigrationInfoOutput = this.Serializer.Serialize(MigrationInfoInput); + this.BadgeCountOutput = this.Serializer.Serialize(BadgeCountInput); + this.StylingOutput = this.Serializer.Serialize(StylingInput); + this.OriginalQuestionOutput = this.Serializer.Serialize(OriginalQuestionInput); + } + + // Serialize + [Benchmark] public object _PrimitiveSByteSerialize() => this.Serializer.Serialize(SByteInput); + + [Benchmark] public object _PrimitiveShortSerialize() => this.Serializer.Serialize(ShortInput); + + [Benchmark] public object _PrimitiveIntSerialize() => this.Serializer.Serialize(IntInput); + + [Benchmark] public object _PrimitiveLongSerialize() => this.Serializer.Serialize(LongInput); + + [Benchmark] public object _PrimitiveByteSerialize() => this.Serializer.Serialize(ByteInput); + + [Benchmark] public object _PrimitiveUShortSerialize() => this.Serializer.Serialize(UShortInput); + + [Benchmark] public object _PrimitiveUIntSerialize() => this.Serializer.Serialize(UIntInput); + + [Benchmark] public object _PrimitiveULongSerialize() => this.Serializer.Serialize(ULongInput); + + [Benchmark] public object _PrimitiveBoolSerialize() => this.Serializer.Serialize(BoolInput); + + [Benchmark] public object _PrimitiveStringSerialize() => this.Serializer.Serialize(StringInput); + + [Benchmark] public object _PrimitiveCharSerialize() => this.Serializer.Serialize(CharInput); + + [Benchmark] public object _PrimitiveDateTimeSerialize() => this.Serializer.Serialize(DateTimeInput); + + [Benchmark] public object _PrimitiveGuidSerialize() => this.Serializer.Serialize(GuidInput); + + [Benchmark] public object _PrimitiveBytesSerialize() => this.Serializer.Serialize(BytesInput); + + [Benchmark] public object AccessTokenSerialize() => this.Serializer.Serialize(AccessTokenInput); + + [Benchmark] public object AccountMergeSerialize() => this.Serializer.Serialize(AccountMergeInput); + + [Benchmark] public object AnswerSerialize() => this.Serializer.Serialize(AnswerInput); + + [Benchmark] public object BadgeSerialize() => this.Serializer.Serialize(BadgeInput); + + [Benchmark] public object CommentSerialize() => this.Serializer.Serialize(CommentInput); + + [Benchmark] public object ErrorSerialize() => this.Serializer.Serialize(ErrorInput); + + [Benchmark] public object EventSerialize() => this.Serializer.Serialize(EventInput); + + [Benchmark] public object MobileFeedSerialize() => this.Serializer.Serialize(MobileFeedInput); + + [Benchmark] public object MobileQuestionSerialize() => this.Serializer.Serialize(MobileQuestionInput); + + [Benchmark] public object MobileRepChangeSerialize() => this.Serializer.Serialize(MobileRepChangeInput); + + [Benchmark] public object MobileInboxItemSerialize() => this.Serializer.Serialize(MobileInboxItemInput); + + [Benchmark] public object MobileBadgeAwardSerialize() => this.Serializer.Serialize(MobileBadgeAwardInput); + + [Benchmark] public object MobilePrivilegeSerialize() => this.Serializer.Serialize(MobilePrivilegeInput); + + [Benchmark] public object MobileCommunityBulletinSerialize() => this.Serializer.Serialize(MobileCommunityBulletinInput); + + [Benchmark] public object MobileAssociationBonusSerialize() => this.Serializer.Serialize(MobileAssociationBonusInput); + + [Benchmark] public object MobileCareersJobAdSerialize() => this.Serializer.Serialize(MobileCareersJobAdInput); + + [Benchmark] public object MobileBannerAdSerialize() => this.Serializer.Serialize(MobileBannerAdInput); + + [Benchmark] public object MobileUpdateNoticeSerialize() => this.Serializer.Serialize(MobileUpdateNoticeInput); + + [Benchmark] public object FlagOptionSerialize() => this.Serializer.Serialize(FlagOptionInput); + + [Benchmark] public object InboxItemSerialize() => this.Serializer.Serialize(InboxItemInput); + + [Benchmark] public object InfoSerialize() => this.Serializer.Serialize(InfoInput); + + [Benchmark] public object NetworkUserSerialize() => this.Serializer.Serialize(NetworkUserInput); + + [Benchmark] public object NotificationSerialize() => this.Serializer.Serialize(NotificationInput); + + [Benchmark] public object PostSerialize() => this.Serializer.Serialize(PostInput); + + [Benchmark] public object PrivilegeSerialize() => this.Serializer.Serialize(PrivilegeInput); + + [Benchmark] public object QuestionSerialize() => this.Serializer.Serialize(QuestionInput); + + [Benchmark] public object QuestionTimelineSerialize() => this.Serializer.Serialize(QuestionTimelineInput); + + [Benchmark] public object ReputationSerialize() => this.Serializer.Serialize(ReputationInput); + + [Benchmark] public object ReputationHistorySerialize() => this.Serializer.Serialize(ReputationHistoryInput); + + [Benchmark] public object RevisionSerialize() => this.Serializer.Serialize(RevisionInput); + + [Benchmark] public object SearchExcerptSerialize() => this.Serializer.Serialize(SearchExcerptInput); + + [Benchmark] public object ShallowUserSerialize() => this.Serializer.Serialize(ShallowUserInput); + + [Benchmark] public object SuggestedEditSerialize() => this.Serializer.Serialize(SuggestedEditInput); + + [Benchmark] public object TagSerialize() => this.Serializer.Serialize(TagInput); + + [Benchmark] public object TagScoreSerialize() => this.Serializer.Serialize(TagScoreInput); + + [Benchmark] public object TagSynonymSerialize() => this.Serializer.Serialize(TagSynonymInput); + + [Benchmark] public object TagWikiSerialize() => this.Serializer.Serialize(TagWikiInput); + + [Benchmark] public object TopTagSerialize() => this.Serializer.Serialize(TopTagInput); + + [Benchmark] public object UserSerialize() => this.Serializer.Serialize(UserInput); + + [Benchmark] public object UserTimelineSerialize() => this.Serializer.Serialize(UserTimelineInput); + + [Benchmark] public object WritePermissionSerialize() => this.Serializer.Serialize(WritePermissionInput); + + [Benchmark] public object MobileBannerAdImageSerialize() => this.Serializer.Serialize(MobileBannerAdImageInput); + + [Benchmark] public object SiteSerialize() => this.Serializer.Serialize(SiteInput); + + [Benchmark] public object RelatedSiteSerialize() => this.Serializer.Serialize(RelatedSiteInput); + + [Benchmark] public object ClosedDetailsSerialize() => this.Serializer.Serialize(ClosedDetailsInput); + + [Benchmark] public object NoticeSerialize() => this.Serializer.Serialize(NoticeInput); + + [Benchmark] public object MigrationInfoSerialize() => this.Serializer.Serialize(MigrationInfoInput); + + [Benchmark] public object BadgeCountSerialize() => this.Serializer.Serialize(BadgeCountInput); + + [Benchmark] public object StylingSerialize() => this.Serializer.Serialize(StylingInput); + + [Benchmark] public object OriginalQuestionSerialize() => this.Serializer.Serialize(OriginalQuestionInput); + + // Deserialize + [Benchmark] public SByte _PrimitiveSByteDeserialize() => this.Serializer.Deserialize(this.SByteOutput); + + [Benchmark] public short _PrimitiveShortDeserialize() => this.Serializer.Deserialize(this.ShortOutput); + + [Benchmark] public Int32 _PrimitiveIntDeserialize() => this.Serializer.Deserialize(this.IntOutput); + + [Benchmark] public Int64 _PrimitiveLongDeserialize() => this.Serializer.Deserialize(this.LongOutput); + + [Benchmark] public Byte _PrimitiveByteDeserialize() => this.Serializer.Deserialize(this.ByteOutput); + + [Benchmark] public ushort _PrimitiveUShortDeserialize() => this.Serializer.Deserialize(this.UShortOutput); + + [Benchmark] public uint _PrimitiveUIntDeserialize() => this.Serializer.Deserialize(this.UIntOutput); + + [Benchmark] public ulong _PrimitiveULongDeserialize() => this.Serializer.Deserialize(this.ULongOutput); + + [Benchmark] public bool _PrimitiveBoolDeserialize() => this.Serializer.Deserialize(this.BoolOutput); + + [Benchmark] public String _PrimitiveStringDeserialize() => this.Serializer.Deserialize(this.StringOutput); + + [Benchmark] public Char _PrimitiveCharDeserialize() => this.Serializer.Deserialize(this.CharOutput); + + [Benchmark] public DateTime _PrimitiveDateTimeDeserialize() => this.Serializer.Deserialize(this.DateTimeOutput); + + [Benchmark] public Guid _PrimitiveGuidDeserialize() => this.Serializer.Deserialize(this.GuidOutput); + + [Benchmark] public byte[] _PrimitiveBytesDeserialize() => this.Serializer.Deserialize(this.BytesOutput); + + [Benchmark] public AccessToken AccessTokenDeserialize() => this.Serializer.Deserialize(this.AccessTokenOutput); + + [Benchmark] public AccountMerge AccountMergeDeserialize() => this.Serializer.Deserialize(this.AccountMergeOutput); + + [Benchmark] public Answer AnswerDeserialize() => this.Serializer.Deserialize(this.AnswerOutput); + + [Benchmark] public Badge BadgeDeserialize() => this.Serializer.Deserialize(this.BadgeOutput); + + [Benchmark] public Comment CommentDeserialize() => this.Serializer.Deserialize(this.CommentOutput); + + [Benchmark] public Error ErrorDeserialize() => this.Serializer.Deserialize(this.ErrorOutput); + + [Benchmark] public Event EventDeserialize() => this.Serializer.Deserialize(this.EventOutput); + + [Benchmark] public MobileFeed MobileFeedDeserialize() => this.Serializer.Deserialize(this.MobileFeedOutput); + + [Benchmark] public MobileQuestion MobileQuestionDeserialize() => this.Serializer.Deserialize(this.MobileQuestionOutput); + + [Benchmark] public MobileRepChange MobileRepChangeDeserialize() => this.Serializer.Deserialize(this.MobileRepChangeOutput); + + [Benchmark] public MobileInboxItem MobileInboxItemDeserialize() => this.Serializer.Deserialize(this.MobileInboxItemOutput); + + [Benchmark] public MobileBadgeAward MobileBadgeAwardDeserialize() => this.Serializer.Deserialize(this.MobileBadgeAwardOutput); + + [Benchmark] public MobilePrivilege MobilePrivilegeDeserialize() => this.Serializer.Deserialize(this.MobilePrivilegeOutput); + + [Benchmark] public MobileCommunityBulletin MobileCommunityBulletinDeserialize() => this.Serializer.Deserialize(this.MobileCommunityBulletinOutput); + + [Benchmark] public MobileAssociationBonus MobileAssociationBonusDeserialize() => this.Serializer.Deserialize(this.MobileAssociationBonusOutput); + + [Benchmark] public MobileCareersJobAd MobileCareersJobAdDeserialize() => this.Serializer.Deserialize(this.MobileCareersJobAdOutput); + + [Benchmark] public MobileBannerAd MobileBannerAdDeserialize() => this.Serializer.Deserialize(this.MobileBannerAdOutput); + + [Benchmark] public MobileUpdateNotice MobileUpdateNoticeDeserialize() => this.Serializer.Deserialize(this.MobileUpdateNoticeOutput); + + [Benchmark] public FlagOption FlagOptionDeserialize() => this.Serializer.Deserialize(this.FlagOptionOutput); + + [Benchmark] public InboxItem InboxItemDeserialize() => this.Serializer.Deserialize(this.InboxItemOutput); + + [Benchmark] public Info InfoDeserialize() => this.Serializer.Deserialize(this.InfoOutput); + + [Benchmark] public NetworkUser NetworkUserDeserialize() => this.Serializer.Deserialize(this.NetworkUserOutput); + + [Benchmark] public Notification NotificationDeserialize() => this.Serializer.Deserialize(this.NotificationOutput); + + [Benchmark] public Post PostDeserialize() => this.Serializer.Deserialize(this.PostOutput); + + [Benchmark] public Privilege PrivilegeDeserialize() => this.Serializer.Deserialize(this.PrivilegeOutput); + + [Benchmark] public Question QuestionDeserialize() => this.Serializer.Deserialize(this.QuestionOutput); + + [Benchmark] public QuestionTimeline QuestionTimelineDeserialize() => this.Serializer.Deserialize(this.QuestionTimelineOutput); + + [Benchmark] public Reputation ReputationDeserialize() => this.Serializer.Deserialize(this.ReputationOutput); + + [Benchmark] public ReputationHistory ReputationHistoryDeserialize() => this.Serializer.Deserialize(this.ReputationHistoryOutput); + + [Benchmark] public Revision RevisionDeserialize() => this.Serializer.Deserialize(this.RevisionOutput); + + [Benchmark] public SearchExcerpt SearchExcerptDeserialize() => this.Serializer.Deserialize(this.SearchExcerptOutput); + + [Benchmark] public ShallowUser ShallowUserDeserialize() => this.Serializer.Deserialize(this.ShallowUserOutput); + + [Benchmark] public SuggestedEdit SuggestedEditDeserialize() => this.Serializer.Deserialize(this.SuggestedEditOutput); + + [Benchmark] public Tag TagDeserialize() => this.Serializer.Deserialize(this.TagOutput); + + [Benchmark] public TagScore TagScoreDeserialize() => this.Serializer.Deserialize(this.TagScoreOutput); + + [Benchmark] public TagSynonym TagSynonymDeserialize() => this.Serializer.Deserialize(this.TagSynonymOutput); + + [Benchmark] public TagWiki TagWikiDeserialize() => this.Serializer.Deserialize(this.TagWikiOutput); + + [Benchmark] public TopTag TopTagDeserialize() => this.Serializer.Deserialize(this.TopTagOutput); + + [Benchmark] public User UserDeserialize() => this.Serializer.Deserialize(this.UserOutput); + + [Benchmark] public UserTimeline UserTimelineDeserialize() => this.Serializer.Deserialize(this.UserTimelineOutput); + + [Benchmark] public WritePermission WritePermissionDeserialize() => this.Serializer.Deserialize(this.WritePermissionOutput); + + [Benchmark] public MobileBannerAd.MobileBannerAdImage MobileBannerAdImageDeserialize() => this.Serializer.Deserialize(this.MobileBannerAdImageOutput); + + [Benchmark] public Info.Site SiteDeserialize() => this.Serializer.Deserialize(this.SiteOutput); + + [Benchmark] public Info.RelatedSite RelatedSiteDeserialize() => this.Serializer.Deserialize(this.RelatedSiteOutput); + + [Benchmark] public Question.ClosedDetails ClosedDetailsDeserialize() => this.Serializer.Deserialize(this.ClosedDetailsOutput); + + [Benchmark] public Question.Notice NoticeDeserialize() => this.Serializer.Deserialize(this.NoticeOutput); + + [Benchmark] public Question.MigrationInfo MigrationInfoDeserialize() => this.Serializer.Deserialize(this.MigrationInfoOutput); + + [Benchmark] public User.BadgeCount BadgeCountDeserialize() => this.Serializer.Deserialize(this.BadgeCountOutput); + + [Benchmark] public Info.Site.Styling StylingDeserialize() => this.Serializer.Deserialize(this.StylingOutput); + + [Benchmark] public Question.ClosedDetails.OriginalQuestion OriginalQuestionDeserialize() => this.Serializer.Deserialize(this.OriginalQuestionOutput); + } + + [Config(typeof(BenchmarkConfig))] + public class MsgPackV1_Vs_MsgPackV2_BytesInOut // : AllSerializerBenchmark + { + [ParamsSource(nameof(Serializers))] + public SerializerBase Serializer; + + // Currently BenchmarkdDotNet does not detect inherited ParamsSource so use copy and paste:) + public IEnumerable Serializers => new SerializerBase[] + { + new MsgPackCli(), + new MsgPackCli_v2(), + }; + + protected static readonly ExpressionTreeFixture ExpressionTreeFixture = new ExpressionTreeFixture(); + + // primitives + protected static readonly sbyte SByteInput = ExpressionTreeFixture.Create(); + protected static readonly short ShortInput = ExpressionTreeFixture.Create(); + protected static readonly int IntInput = ExpressionTreeFixture.Create(); + protected static readonly long LongInput = ExpressionTreeFixture.Create(); + protected static readonly byte ByteInput = ExpressionTreeFixture.Create(); + protected static readonly ushort UShortInput = ExpressionTreeFixture.Create(); + protected static readonly uint UIntInput = ExpressionTreeFixture.Create(); + protected static readonly ulong ULongInput = ExpressionTreeFixture.Create(); + protected static readonly bool BoolInput = ExpressionTreeFixture.Create(); + protected static readonly string StringInput = ExpressionTreeFixture.Create(); + protected static readonly char CharInput = ExpressionTreeFixture.Create(); + protected static readonly DateTime DateTimeInput = ExpressionTreeFixture.Create(); + protected static readonly Guid GuidInput = ExpressionTreeFixture.Create(); + protected static readonly byte[] BytesInput = ExpressionTreeFixture.Create(); + + // models + protected static readonly Benchmark.Models.AccessToken AccessTokenInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.AccountMerge AccountMergeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Answer AnswerInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Badge BadgeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Comment CommentInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Error ErrorInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Event EventInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileFeed MobileFeedInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileQuestion MobileQuestionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileRepChange MobileRepChangeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileInboxItem MobileInboxItemInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileBadgeAward MobileBadgeAwardInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobilePrivilege MobilePrivilegeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileCommunityBulletin MobileCommunityBulletinInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileAssociationBonus MobileAssociationBonusInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileCareersJobAd MobileCareersJobAdInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileBannerAd MobileBannerAdInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileUpdateNotice MobileUpdateNoticeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.FlagOption FlagOptionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.InboxItem InboxItemInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Info InfoInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.NetworkUser NetworkUserInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Notification NotificationInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Post PostInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Privilege PrivilegeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question QuestionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.QuestionTimeline QuestionTimelineInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Reputation ReputationInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.ReputationHistory ReputationHistoryInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Revision RevisionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.SearchExcerpt SearchExcerptInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.ShallowUser ShallowUserInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.SuggestedEdit SuggestedEditInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Tag TagInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.TagScore TagScoreInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.TagSynonym TagSynonymInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.TagWiki TagWikiInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.TopTag TopTagInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.User UserInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.UserTimeline UserTimelineInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.WritePermission WritePermissionInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.MobileBannerAd.MobileBannerAdImage MobileBannerAdImageInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Info.Site SiteInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Info.RelatedSite RelatedSiteInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question.ClosedDetails ClosedDetailsInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question.Notice NoticeInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question.MigrationInfo MigrationInfoInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.User.BadgeCount BadgeCountInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Info.Site.Styling StylingInput = ExpressionTreeFixture.Create(); + + protected static readonly Benchmark.Models.Question.ClosedDetails.OriginalQuestion OriginalQuestionInput = ExpressionTreeFixture.Create(); + + private object SByteOutput; + private object ShortOutput; + private object IntOutput; + private object LongOutput; + private object ByteOutput; + private object UShortOutput; + private object UIntOutput; + private object ULongOutput; + private object BoolOutput; + private object StringOutput; + private object CharOutput; + private object DateTimeOutput; + private object GuidOutput; + private object BytesOutput; + + private object AccessTokenOutput; + private object AccountMergeOutput; + private object AnswerOutput; + private object BadgeOutput; + private object CommentOutput; + private object ErrorOutput; + private object EventOutput; + private object MobileFeedOutput; + private object MobileQuestionOutput; + private object MobileRepChangeOutput; + private object MobileInboxItemOutput; + private object MobileBadgeAwardOutput; + private object MobilePrivilegeOutput; + private object MobileCommunityBulletinOutput; + private object MobileAssociationBonusOutput; + private object MobileCareersJobAdOutput; + private object MobileBannerAdOutput; + private object MobileUpdateNoticeOutput; + private object FlagOptionOutput; + private object InboxItemOutput; + private object InfoOutput; + private object NetworkUserOutput; + private object NotificationOutput; + private object PostOutput; + private object PrivilegeOutput; + private object QuestionOutput; + private object QuestionTimelineOutput; + private object ReputationOutput; + private object ReputationHistoryOutput; + private object RevisionOutput; + private object SearchExcerptOutput; + private object ShallowUserOutput; + private object SuggestedEditOutput; + private object TagOutput; + private object TagScoreOutput; + private object TagSynonymOutput; + private object TagWikiOutput; + private object TopTagOutput; + private object UserOutput; + private object UserTimelineOutput; + private object WritePermissionOutput; + private object MobileBannerAdImageOutput; + private object SiteOutput; + private object RelatedSiteOutput; + private object ClosedDetailsOutput; + private object NoticeOutput; + private object MigrationInfoOutput; + private object BadgeCountOutput; + private object StylingOutput; + private object OriginalQuestionOutput; + + [GlobalSetup] + public void Setup() + { + // primitives + this.SByteOutput = this.Serializer.Serialize(SByteInput); + this.ShortOutput = this.Serializer.Serialize(ShortInput); + this.IntOutput = this.Serializer.Serialize(IntInput); + this.LongOutput = this.Serializer.Serialize(LongInput); + this.ByteOutput = this.Serializer.Serialize(ByteInput); + this.UShortOutput = this.Serializer.Serialize(UShortInput); + this.UIntOutput = this.Serializer.Serialize(UIntInput); + this.ULongOutput = this.Serializer.Serialize(ULongInput); + this.BoolOutput = this.Serializer.Serialize(BoolInput); + this.StringOutput = this.Serializer.Serialize(StringInput); + this.CharOutput = this.Serializer.Serialize(CharInput); + this.DateTimeOutput = this.Serializer.Serialize(DateTimeInput); + this.GuidOutput = this.Serializer.Serialize(GuidInput); + this.BytesOutput = this.Serializer.Serialize(BytesInput); + + // models + this.AccessTokenOutput = this.Serializer.Serialize(AccessTokenInput); + this.AccountMergeOutput = this.Serializer.Serialize(AccountMergeInput); + this.AnswerOutput = this.Serializer.Serialize(AnswerInput); + this.BadgeOutput = this.Serializer.Serialize(BadgeInput); + this.CommentOutput = this.Serializer.Serialize(CommentInput); + this.ErrorOutput = this.Serializer.Serialize(ErrorInput); + this.EventOutput = this.Serializer.Serialize(EventInput); + this.MobileFeedOutput = this.Serializer.Serialize(MobileFeedInput); + this.MobileQuestionOutput = this.Serializer.Serialize(MobileQuestionInput); + this.MobileRepChangeOutput = this.Serializer.Serialize(MobileRepChangeInput); + this.MobileInboxItemOutput = this.Serializer.Serialize(MobileInboxItemInput); + this.MobileBadgeAwardOutput = this.Serializer.Serialize(MobileBadgeAwardInput); + this.MobilePrivilegeOutput = this.Serializer.Serialize(MobilePrivilegeInput); + this.MobileCommunityBulletinOutput = this.Serializer.Serialize(MobileCommunityBulletinInput); + this.MobileAssociationBonusOutput = this.Serializer.Serialize(MobileAssociationBonusInput); + this.MobileCareersJobAdOutput = this.Serializer.Serialize(MobileCareersJobAdInput); + this.MobileBannerAdOutput = this.Serializer.Serialize(MobileBannerAdInput); + this.MobileUpdateNoticeOutput = this.Serializer.Serialize(MobileUpdateNoticeInput); + this.FlagOptionOutput = this.Serializer.Serialize(FlagOptionInput); + this.InboxItemOutput = this.Serializer.Serialize(InboxItemInput); + this.InfoOutput = this.Serializer.Serialize(InfoInput); + this.NetworkUserOutput = this.Serializer.Serialize(NetworkUserInput); + this.NotificationOutput = this.Serializer.Serialize(NotificationInput); + this.PostOutput = this.Serializer.Serialize(PostInput); + this.PrivilegeOutput = this.Serializer.Serialize(PrivilegeInput); + this.QuestionOutput = this.Serializer.Serialize(QuestionInput); + this.QuestionTimelineOutput = this.Serializer.Serialize(QuestionTimelineInput); + this.ReputationOutput = this.Serializer.Serialize(ReputationInput); + this.ReputationHistoryOutput = this.Serializer.Serialize(ReputationHistoryInput); + this.RevisionOutput = this.Serializer.Serialize(RevisionInput); + this.SearchExcerptOutput = this.Serializer.Serialize(SearchExcerptInput); + this.ShallowUserOutput = this.Serializer.Serialize(ShallowUserInput); + this.SuggestedEditOutput = this.Serializer.Serialize(SuggestedEditInput); + this.TagOutput = this.Serializer.Serialize(TagInput); + this.TagScoreOutput = this.Serializer.Serialize(TagScoreInput); + this.TagSynonymOutput = this.Serializer.Serialize(TagSynonymInput); + this.TagWikiOutput = this.Serializer.Serialize(TagWikiInput); + this.TopTagOutput = this.Serializer.Serialize(TopTagInput); + this.UserOutput = this.Serializer.Serialize(UserInput); + this.UserTimelineOutput = this.Serializer.Serialize(UserTimelineInput); + this.WritePermissionOutput = this.Serializer.Serialize(WritePermissionInput); + this.MobileBannerAdImageOutput = this.Serializer.Serialize(MobileBannerAdImageInput); + this.SiteOutput = this.Serializer.Serialize(SiteInput); + this.RelatedSiteOutput = this.Serializer.Serialize(RelatedSiteInput); + this.ClosedDetailsOutput = this.Serializer.Serialize(ClosedDetailsInput); + this.NoticeOutput = this.Serializer.Serialize(NoticeInput); + this.MigrationInfoOutput = this.Serializer.Serialize(MigrationInfoInput); + this.BadgeCountOutput = this.Serializer.Serialize(BadgeCountInput); + this.StylingOutput = this.Serializer.Serialize(StylingInput); + this.OriginalQuestionOutput = this.Serializer.Serialize(OriginalQuestionInput); + } + + // Serialize + [Benchmark] public object _PrimitiveSByteSerialize() => this.Serializer.Serialize(SByteInput); + + [Benchmark] public object _PrimitiveShortSerialize() => this.Serializer.Serialize(ShortInput); + + [Benchmark] public object _PrimitiveIntSerialize() => this.Serializer.Serialize(IntInput); + + [Benchmark] public object _PrimitiveLongSerialize() => this.Serializer.Serialize(LongInput); + + [Benchmark] public object _PrimitiveByteSerialize() => this.Serializer.Serialize(ByteInput); + + [Benchmark] public object _PrimitiveUShortSerialize() => this.Serializer.Serialize(UShortInput); + + [Benchmark] public object _PrimitiveUIntSerialize() => this.Serializer.Serialize(UIntInput); + + [Benchmark] public object _PrimitiveULongSerialize() => this.Serializer.Serialize(ULongInput); + + [Benchmark] public object _PrimitiveBoolSerialize() => this.Serializer.Serialize(BoolInput); + + [Benchmark] public object _PrimitiveStringSerialize() => this.Serializer.Serialize(StringInput); + + [Benchmark] public object _PrimitiveCharSerialize() => this.Serializer.Serialize(CharInput); + + [Benchmark] public object _PrimitiveDateTimeSerialize() => this.Serializer.Serialize(DateTimeInput); + + [Benchmark] public object _PrimitiveGuidSerialize() => this.Serializer.Serialize(GuidInput); + + [Benchmark] public object _PrimitiveBytesSerialize() => this.Serializer.Serialize(BytesInput); + + [Benchmark] public object AccessTokenSerialize() => this.Serializer.Serialize(AccessTokenInput); + + [Benchmark] public object AccountMergeSerialize() => this.Serializer.Serialize(AccountMergeInput); + + [Benchmark] public object AnswerSerialize() => this.Serializer.Serialize(AnswerInput); + + [Benchmark] public object BadgeSerialize() => this.Serializer.Serialize(BadgeInput); + + [Benchmark] public object CommentSerialize() => this.Serializer.Serialize(CommentInput); + + [Benchmark] public object ErrorSerialize() => this.Serializer.Serialize(ErrorInput); + + [Benchmark] public object EventSerialize() => this.Serializer.Serialize(EventInput); + + [Benchmark] public object MobileFeedSerialize() => this.Serializer.Serialize(MobileFeedInput); + + [Benchmark] public object MobileQuestionSerialize() => this.Serializer.Serialize(MobileQuestionInput); + + [Benchmark] public object MobileRepChangeSerialize() => this.Serializer.Serialize(MobileRepChangeInput); + + [Benchmark] public object MobileInboxItemSerialize() => this.Serializer.Serialize(MobileInboxItemInput); + + [Benchmark] public object MobileBadgeAwardSerialize() => this.Serializer.Serialize(MobileBadgeAwardInput); + + [Benchmark] public object MobilePrivilegeSerialize() => this.Serializer.Serialize(MobilePrivilegeInput); + + [Benchmark] public object MobileCommunityBulletinSerialize() => this.Serializer.Serialize(MobileCommunityBulletinInput); + + [Benchmark] public object MobileAssociationBonusSerialize() => this.Serializer.Serialize(MobileAssociationBonusInput); + + [Benchmark] public object MobileCareersJobAdSerialize() => this.Serializer.Serialize(MobileCareersJobAdInput); + + [Benchmark] public object MobileBannerAdSerialize() => this.Serializer.Serialize(MobileBannerAdInput); + + [Benchmark] public object MobileUpdateNoticeSerialize() => this.Serializer.Serialize(MobileUpdateNoticeInput); + + [Benchmark] public object FlagOptionSerialize() => this.Serializer.Serialize(FlagOptionInput); + + [Benchmark] public object InboxItemSerialize() => this.Serializer.Serialize(InboxItemInput); + + [Benchmark] public object InfoSerialize() => this.Serializer.Serialize(InfoInput); + + [Benchmark] public object NetworkUserSerialize() => this.Serializer.Serialize(NetworkUserInput); + + [Benchmark] public object NotificationSerialize() => this.Serializer.Serialize(NotificationInput); + + [Benchmark] public object PostSerialize() => this.Serializer.Serialize(PostInput); + + [Benchmark] public object PrivilegeSerialize() => this.Serializer.Serialize(PrivilegeInput); + + [Benchmark] public object QuestionSerialize() => this.Serializer.Serialize(QuestionInput); + + [Benchmark] public object QuestionTimelineSerialize() => this.Serializer.Serialize(QuestionTimelineInput); + + [Benchmark] public object ReputationSerialize() => this.Serializer.Serialize(ReputationInput); + + [Benchmark] public object ReputationHistorySerialize() => this.Serializer.Serialize(ReputationHistoryInput); + + [Benchmark] public object RevisionSerialize() => this.Serializer.Serialize(RevisionInput); + + [Benchmark] public object SearchExcerptSerialize() => this.Serializer.Serialize(SearchExcerptInput); + + [Benchmark] public object ShallowUserSerialize() => this.Serializer.Serialize(ShallowUserInput); + + [Benchmark] public object SuggestedEditSerialize() => this.Serializer.Serialize(SuggestedEditInput); + + [Benchmark] public object TagSerialize() => this.Serializer.Serialize(TagInput); + + [Benchmark] public object TagScoreSerialize() => this.Serializer.Serialize(TagScoreInput); + + [Benchmark] public object TagSynonymSerialize() => this.Serializer.Serialize(TagSynonymInput); + + [Benchmark] public object TagWikiSerialize() => this.Serializer.Serialize(TagWikiInput); + + [Benchmark] public object TopTagSerialize() => this.Serializer.Serialize(TopTagInput); + + [Benchmark] public object UserSerialize() => this.Serializer.Serialize(UserInput); + + [Benchmark] public object UserTimelineSerialize() => this.Serializer.Serialize(UserTimelineInput); + + [Benchmark] public object WritePermissionSerialize() => this.Serializer.Serialize(WritePermissionInput); + + [Benchmark] public object MobileBannerAdImageSerialize() => this.Serializer.Serialize(MobileBannerAdImageInput); + + [Benchmark] public object SiteSerialize() => this.Serializer.Serialize(SiteInput); + + [Benchmark] public object RelatedSiteSerialize() => this.Serializer.Serialize(RelatedSiteInput); + + [Benchmark] public object ClosedDetailsSerialize() => this.Serializer.Serialize(ClosedDetailsInput); + + [Benchmark] public object NoticeSerialize() => this.Serializer.Serialize(NoticeInput); + + [Benchmark] public object MigrationInfoSerialize() => this.Serializer.Serialize(MigrationInfoInput); + + [Benchmark] public object BadgeCountSerialize() => this.Serializer.Serialize(BadgeCountInput); + + [Benchmark] public object StylingSerialize() => this.Serializer.Serialize(StylingInput); + + [Benchmark] public object OriginalQuestionSerialize() => this.Serializer.Serialize(OriginalQuestionInput); + + // Deserialize + [Benchmark] public SByte _PrimitiveSByteDeserialize() => this.Serializer.Deserialize(this.SByteOutput); + + [Benchmark] public short _PrimitiveShortDeserialize() => this.Serializer.Deserialize(this.ShortOutput); + + [Benchmark] public Int32 _PrimitiveIntDeserialize() => this.Serializer.Deserialize(this.IntOutput); + + [Benchmark] public Int64 _PrimitiveLongDeserialize() => this.Serializer.Deserialize(this.LongOutput); + + [Benchmark] public Byte _PrimitiveByteDeserialize() => this.Serializer.Deserialize(this.ByteOutput); + + [Benchmark] public ushort _PrimitiveUShortDeserialize() => this.Serializer.Deserialize(this.UShortOutput); + + [Benchmark] public uint _PrimitiveUIntDeserialize() => this.Serializer.Deserialize(this.UIntOutput); + + [Benchmark] public ulong _PrimitiveULongDeserialize() => this.Serializer.Deserialize(this.ULongOutput); + + [Benchmark] public bool _PrimitiveBoolDeserialize() => this.Serializer.Deserialize(this.BoolOutput); + + [Benchmark] public String _PrimitiveStringDeserialize() => this.Serializer.Deserialize(this.StringOutput); + + [Benchmark] public Char _PrimitiveCharDeserialize() => this.Serializer.Deserialize(this.CharOutput); + + [Benchmark] public DateTime _PrimitiveDateTimeDeserialize() => this.Serializer.Deserialize(this.DateTimeOutput); + + [Benchmark] public Guid _PrimitiveGuidDeserialize() => this.Serializer.Deserialize(this.GuidOutput); + + [Benchmark] public byte[] _PrimitiveBytesDeserialize() => this.Serializer.Deserialize(this.BytesOutput); + + [Benchmark] public AccessToken AccessTokenDeserialize() => this.Serializer.Deserialize(this.AccessTokenOutput); + + [Benchmark] public AccountMerge AccountMergeDeserialize() => this.Serializer.Deserialize(this.AccountMergeOutput); + + [Benchmark] public Answer AnswerDeserialize() => this.Serializer.Deserialize(this.AnswerOutput); + + [Benchmark] public Badge BadgeDeserialize() => this.Serializer.Deserialize(this.BadgeOutput); + + [Benchmark] public Comment CommentDeserialize() => this.Serializer.Deserialize(this.CommentOutput); + + [Benchmark] public Error ErrorDeserialize() => this.Serializer.Deserialize(this.ErrorOutput); + + [Benchmark] public Event EventDeserialize() => this.Serializer.Deserialize(this.EventOutput); + + [Benchmark] public MobileFeed MobileFeedDeserialize() => this.Serializer.Deserialize(this.MobileFeedOutput); + + [Benchmark] public MobileQuestion MobileQuestionDeserialize() => this.Serializer.Deserialize(this.MobileQuestionOutput); + + [Benchmark] public MobileRepChange MobileRepChangeDeserialize() => this.Serializer.Deserialize(this.MobileRepChangeOutput); + + [Benchmark] public MobileInboxItem MobileInboxItemDeserialize() => this.Serializer.Deserialize(this.MobileInboxItemOutput); + + [Benchmark] public MobileBadgeAward MobileBadgeAwardDeserialize() => this.Serializer.Deserialize(this.MobileBadgeAwardOutput); + + [Benchmark] public MobilePrivilege MobilePrivilegeDeserialize() => this.Serializer.Deserialize(this.MobilePrivilegeOutput); + + [Benchmark] public MobileCommunityBulletin MobileCommunityBulletinDeserialize() => this.Serializer.Deserialize(this.MobileCommunityBulletinOutput); + + [Benchmark] public MobileAssociationBonus MobileAssociationBonusDeserialize() => this.Serializer.Deserialize(this.MobileAssociationBonusOutput); + + [Benchmark] public MobileCareersJobAd MobileCareersJobAdDeserialize() => this.Serializer.Deserialize(this.MobileCareersJobAdOutput); + + [Benchmark] public MobileBannerAd MobileBannerAdDeserialize() => this.Serializer.Deserialize(this.MobileBannerAdOutput); + + [Benchmark] public MobileUpdateNotice MobileUpdateNoticeDeserialize() => this.Serializer.Deserialize(this.MobileUpdateNoticeOutput); + + [Benchmark] public FlagOption FlagOptionDeserialize() => this.Serializer.Deserialize(this.FlagOptionOutput); + + [Benchmark] public InboxItem InboxItemDeserialize() => this.Serializer.Deserialize(this.InboxItemOutput); + + [Benchmark] public Info InfoDeserialize() => this.Serializer.Deserialize(this.InfoOutput); + + [Benchmark] public NetworkUser NetworkUserDeserialize() => this.Serializer.Deserialize(this.NetworkUserOutput); + + [Benchmark] public Notification NotificationDeserialize() => this.Serializer.Deserialize(this.NotificationOutput); + + [Benchmark] public Post PostDeserialize() => this.Serializer.Deserialize(this.PostOutput); + + [Benchmark] public Privilege PrivilegeDeserialize() => this.Serializer.Deserialize(this.PrivilegeOutput); + + [Benchmark] public Question QuestionDeserialize() => this.Serializer.Deserialize(this.QuestionOutput); + + [Benchmark] public QuestionTimeline QuestionTimelineDeserialize() => this.Serializer.Deserialize(this.QuestionTimelineOutput); + + [Benchmark] public Reputation ReputationDeserialize() => this.Serializer.Deserialize(this.ReputationOutput); + + [Benchmark] public ReputationHistory ReputationHistoryDeserialize() => this.Serializer.Deserialize(this.ReputationHistoryOutput); + + [Benchmark] public Revision RevisionDeserialize() => this.Serializer.Deserialize(this.RevisionOutput); + + [Benchmark] public SearchExcerpt SearchExcerptDeserialize() => this.Serializer.Deserialize(this.SearchExcerptOutput); + + [Benchmark] public ShallowUser ShallowUserDeserialize() => this.Serializer.Deserialize(this.ShallowUserOutput); + + [Benchmark] public SuggestedEdit SuggestedEditDeserialize() => this.Serializer.Deserialize(this.SuggestedEditOutput); + + [Benchmark] public Tag TagDeserialize() => this.Serializer.Deserialize(this.TagOutput); + + [Benchmark] public TagScore TagScoreDeserialize() => this.Serializer.Deserialize(this.TagScoreOutput); + + [Benchmark] public TagSynonym TagSynonymDeserialize() => this.Serializer.Deserialize(this.TagSynonymOutput); + + [Benchmark] public TagWiki TagWikiDeserialize() => this.Serializer.Deserialize(this.TagWikiOutput); + + [Benchmark] public TopTag TopTagDeserialize() => this.Serializer.Deserialize(this.TopTagOutput); + + [Benchmark] public User UserDeserialize() => this.Serializer.Deserialize(this.UserOutput); + + [Benchmark] public UserTimeline UserTimelineDeserialize() => this.Serializer.Deserialize(this.UserTimelineOutput); + + [Benchmark] public WritePermission WritePermissionDeserialize() => this.Serializer.Deserialize(this.WritePermissionOutput); + + [Benchmark] public MobileBannerAd.MobileBannerAdImage MobileBannerAdImageDeserialize() => this.Serializer.Deserialize(this.MobileBannerAdImageOutput); + + [Benchmark] public Info.Site SiteDeserialize() => this.Serializer.Deserialize(this.SiteOutput); + + [Benchmark] public Info.RelatedSite RelatedSiteDeserialize() => this.Serializer.Deserialize(this.RelatedSiteOutput); + + [Benchmark] public Question.ClosedDetails ClosedDetailsDeserialize() => this.Serializer.Deserialize(this.ClosedDetailsOutput); + + [Benchmark] public Question.Notice NoticeDeserialize() => this.Serializer.Deserialize(this.NoticeOutput); + + [Benchmark] public Question.MigrationInfo MigrationInfoDeserialize() => this.Serializer.Deserialize(this.MigrationInfoOutput); + + [Benchmark] public User.BadgeCount BadgeCountDeserialize() => this.Serializer.Deserialize(this.BadgeCountOutput); + + [Benchmark] public Info.Site.Styling StylingDeserialize() => this.Serializer.Deserialize(this.StylingOutput); + + [Benchmark] public Question.ClosedDetails.OriginalQuestion OriginalQuestionDeserialize() => this.Serializer.Deserialize(this.OriginalQuestionOutput); + } + + [Config(typeof(BenchmarkConfig))] + public class ShortRun_AllSerializerBenchmark_BytesInOut + { + [ParamsSource(nameof(Serializers))] + public SerializerBase Serializer; + + private bool isContractless; + + // Currently BenchmarkdDotNet does not detect inherited ParamsSource so use copy and paste:) + public IEnumerable Serializers => new SerializerBase[] + { + new MessagePack_v2(), + new MsgPack_v2_opt(), + new MessagePackLz4_v2(), + new MsgPack_v2_string(), + new MsgPack_v2_str_lz4(), + new ProtobufNet(), + new JsonNet(), + new BinaryFormatter_(), + new DataContract_(), + new Hyperion_(), + new Jil_(), + new SpanJson_(), + new Utf8Json_(), + new SystemTextJson(), + new MsgPackCli(), + new MsgPackCli_v2(), + new FsPickler_(), + new Ceras_(), + }; + + protected static readonly ExpressionTreeFixture ExpressionTreeFixture = new ExpressionTreeFixture(); + + // primitives + protected static readonly int IntInput = ExpressionTreeFixture.Create(); + + // models + protected static readonly Benchmark.Models.Answer AnswerInput = ExpressionTreeFixture.Create(); + // not same data so does not gurantee correctly. + protected static readonly Benchmark.Models.Answer2 Answer2Input = ExpressionTreeFixture.Create(); + + private object IntOutput; + private object AnswerOutput; + + [GlobalSetup] + public void Setup() + { + this.isContractless = (Serializer is MsgPack_v2_string) || (Serializer is MsgPack_v2_str_lz4); + + // primitives + this.IntOutput = this.Serializer.Serialize(IntInput); + + // models + if (isContractless) + { + this.AnswerOutput = this.Serializer.Serialize(Answer2Input); + } + else + { + this.AnswerOutput = this.Serializer.Serialize(AnswerInput); + } + } + + // Serialize + /* [Benchmark] public object _PrimitiveIntSerialize() => this.Serializer.Serialize(IntInput); */ + + [Benchmark] + public object AnswerSerialize() + { + if (isContractless) + { + return this.Serializer.Serialize(Answer2Input); + } + else + { + return this.Serializer.Serialize(AnswerInput); + } + } + + // Deserialize + /* [Benchmark] public Int32 _PrimitiveIntDeserialize() => this.Serializer.Deserialize(this.IntOutput); */ + + [Benchmark] + public object AnswerDeserialize() + { + if (isContractless) + { + return this.Serializer.Deserialize(this.AnswerOutput); + } + else + { + return this.Serializer.Deserialize(this.AnswerOutput); + } + } + } + + [Config(typeof(BenchmarkConfig))] + public class ShortRun_MsgPackV1_Vs_MsgPackV2_BytesInOut + { + [ParamsSource(nameof(Serializers))] + public SerializerBase Serializer; + + // Currently BenchmarkdDotNet does not detect inherited ParamsSource so use copy and paste:) + public IEnumerable Serializers => new SerializerBase[] + { + new MsgPackCli(), + new MsgPackCli_v2(), + }; + + protected static readonly ExpressionTreeFixture ExpressionTreeFixture = new ExpressionTreeFixture(); + + // primitives + protected static readonly int IntInput = ExpressionTreeFixture.Create(); + + // models + protected static readonly Benchmark.Models.Answer AnswerInput = ExpressionTreeFixture.Create(); + + private object IntOutput; + private object AnswerOutput; + + [GlobalSetup] + public void Setup() + { + // primitives + this.IntOutput = this.Serializer.Serialize(IntInput); + + // models + this.AnswerOutput = this.Serializer.Serialize(AnswerInput); + } + + // Serialize + [Benchmark] public object _PrimitiveIntSerialize() => this.Serializer.Serialize(IntInput); + + [Benchmark] public object AnswerSerialize() => this.Serializer.Serialize(AnswerInput); + + // Deserialize + [Benchmark] public Int32 _PrimitiveIntDeserialize() => this.Serializer.Deserialize(this.IntOutput); + + [Benchmark] public Answer AnswerDeserialize() => this.Serializer.Deserialize(this.AnswerOutput); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/BinaryFormatter.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/BinaryFormatter.cs new file mode 100644 index 000000000..b3981a890 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/BinaryFormatter.cs @@ -0,0 +1,29 @@ +// 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.IO; +using System.Runtime.Serialization.Formatters.Binary; +using Benchmark.Serializers; + +#pragma warning disable SA1649 // File name should match first type name + +public class BinaryFormatter_ : SerializerBase +{ + public override T Deserialize(object input) + { + using (var ms = new MemoryStream((byte[])input)) + { + return (T)new BinaryFormatter().Deserialize(ms); + } + } + + public override object Serialize(T input) + { + using (var ms = new MemoryStream()) + { + new BinaryFormatter().Serialize(ms, input); + ms.Flush(); + return ms.ToArray(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/CerasSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/CerasSerializer.cs new file mode 100644 index 000000000..cfdd9be09 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/CerasSerializer.cs @@ -0,0 +1,21 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Benchmark.Serializers; + +#pragma warning disable SA1649 // File name should match first type name + +public class Ceras_ : SerializerBase +{ + private Ceras.CerasSerializer ceras = new Ceras.CerasSerializer(); + + public override T Deserialize(object input) + { + return this.ceras.Deserialize((byte[])input); + } + + public override object Serialize(T input) + { + return this.ceras.Serialize(input); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/DataContractSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/DataContractSerializer.cs new file mode 100644 index 000000000..eae1431ff --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/DataContractSerializer.cs @@ -0,0 +1,29 @@ +// 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.IO; +using System.Runtime.Serialization; +using Benchmark.Serializers; + +#pragma warning disable SA1649 // File name should match first type name + +public class DataContract_ : SerializerBase +{ + public override T Deserialize(object input) + { + using (var ms = new MemoryStream((byte[])input)) + { + return (T)new DataContractSerializer(typeof(T)).ReadObject(ms); + } + } + + public override object Serialize(T input) + { + using (var ms = new MemoryStream()) + { + new DataContractSerializer(typeof(T)).WriteObject(ms, input); + ms.Flush(); + return ms.ToArray(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/FsPicklerSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/FsPicklerSerializer.cs new file mode 100644 index 000000000..18b1c88e8 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/FsPicklerSerializer.cs @@ -0,0 +1,31 @@ +// 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.IO; +using Benchmark.Serializers; +using MBrace.FsPickler; + +#pragma warning disable SA1649 // File name should match first type name + +public class FsPickler_ : SerializerBase +{ + private static readonly BinarySerializer Serializer = MBrace.FsPickler.FsPickler.CreateBinarySerializer(); + + public override T Deserialize(object input) + { + using (var ms = new MemoryStream((byte[])input)) + { + return Serializer.Deserialize(ms); + } + } + + public override object Serialize(T input) + { + using (var ms = new MemoryStream()) + { + Serializer.Serialize(ms, input); + ms.Flush(); + return ms.ToArray(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/HagarSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/HagarSerializer.cs new file mode 100644 index 000000000..9a33c7d05 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/HagarSerializer.cs @@ -0,0 +1,59 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +////using Benchmark.Serializers; +////using System.Buffers; +////using Hagar; +////using Hagar.Buffers; +////using Hagar.Session; +////using Microsoft.Extensions.DependencyInjection; +////using System.IO.Pipelines; + +////public class Hagar_ : SerializerBase +////{ +//// readonly ServiceProvider serviceProvider; + +//// public Hagar_() +//// { +//// this.serviceProvider = new ServiceCollection() +//// .AddHagar() +//// .AddISerializableSupport() +//// .AddSerializers(typeof(Hagar_).Assembly) // this assembly +//// .BuildServiceProvider(); +//// } + +//// public override T Deserialize(object input) +//// { +//// var serializer = serviceProvider.GetRequiredService>(); +//// var sessionPool = serviceProvider.GetRequiredService(); + +//// var pipe = new Pipe(); +//// pipe.Writer.WriteAsync((byte[])input).GetAwaiter().GetResult(); +//// pipe.Writer.Complete(); + +//// using (var session = sessionPool.GetSession()) +//// { +//// pipe.Reader.TryRead(out var readResult); +//// var reader = new Reader(readResult.Buffer, session); +//// var result = serializer.Deserialize(ref reader); +//// return result; +//// } +//// } + +//// public override object Serialize(T input) +//// { +//// var serializer = serviceProvider.GetRequiredService>(); +//// var sessionPool = serviceProvider.GetRequiredService(); + +//// var pipe = new Pipe(); + +//// using (var session = sessionPool.GetSession()) +//// { +//// var writer = pipe.Writer.CreateWriter(session); +//// serializer.Serialize(ref writer, input); +//// pipe.Writer.Complete(); +//// pipe.Reader.TryRead(out var result); +//// return result.Buffer.ToArray(); +//// } +//// } +////} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/HyperionSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/HyperionSerializer.cs new file mode 100644 index 000000000..b38ef9494 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/HyperionSerializer.cs @@ -0,0 +1,31 @@ +// 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.IO; +using Benchmark.Serializers; +using Hyperion; + +#pragma warning disable SA1649 // File name should match first type name + +public class Hyperion_ : SerializerBase +{ + private static readonly Serializer Serializer = new Hyperion.Serializer(); + + public override T Deserialize(object input) + { + using (var ms = new MemoryStream((byte[])input)) + { + return Serializer.Deserialize(ms); + } + } + + public override object Serialize(T input) + { + using (var ms = new MemoryStream()) + { + Serializer.Serialize(input, ms); + ms.Flush(); + return ms.ToArray(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/JilSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/JilSerializer.cs new file mode 100644 index 000000000..b68a0585f --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/JilSerializer.cs @@ -0,0 +1,21 @@ +// 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.Text; +using Benchmark.Serializers; +using Jil; + +#pragma warning disable SA1649 // File name should match first type name + +public class Jil_ : SerializerBase +{ + public override object Serialize(T input) + { + return Encoding.UTF8.GetBytes(Jil.JSON.Serialize(input, Options.ISO8601)); + } + + public override T Deserialize(object input) + { + return Jil.JSON.Deserialize(Encoding.UTF8.GetString((byte[])input), Options.ISO8601); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/JsonNetSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/JsonNetSerializer.cs new file mode 100644 index 000000000..80761c6a4 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/JsonNetSerializer.cs @@ -0,0 +1,39 @@ +// 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.IO; +using System.Text; +using Benchmark.Serializers; +using Newtonsoft.Json; + +#pragma warning disable SA1649 // File name should match first type name + +public class JsonNet : SerializerBase +{ + private static readonly JsonSerializer Serializer = new JsonSerializer(); + + public override T Deserialize(object input) + { + using (var ms = new MemoryStream((byte[])input)) + using (var sr = new StreamReader(ms, Encoding.UTF8)) + using (var jr = new JsonTextReader(sr)) + { + return Serializer.Deserialize(jr); + } + } + + public override object Serialize(T input) + { + using (var ms = new MemoryStream()) + { + using (var sw = new StreamWriter(ms, Encoding.UTF8)) + using (var jw = new JsonTextWriter(sw)) + { + Serializer.Serialize(jw, input); + } + + ms.Flush(); + return ms.ToArray(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MessagePackSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MessagePackSerializer.cs new file mode 100644 index 000000000..b3d64acfe --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MessagePackSerializer.cs @@ -0,0 +1,123 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + + +using Benchmark.Serializers; + +#pragma warning disable SA1649 // File name should match first type name + +public class MessagePack_v2 : SerializerBase +{ + public override T Deserialize(object input) + { + return MessagePack.MessagePackSerializer.Deserialize((byte[])input); + } + + public override object Serialize(T input) + { + return MessagePack.MessagePackSerializer.Serialize(input); + } +} + +public class MsgPack_v2_string : SerializerBase +{ + private static readonly MessagePack.MessagePackSerializerOptions Options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.ContractlessStandardResolver.Instance); + + public override T Deserialize(object input) + { + return MessagePack.MessagePackSerializer.Deserialize((byte[])input, options: Options); + } + + public override object Serialize(T input) + { + return MessagePack.MessagePackSerializer.Serialize(input, options: Options); + } +} + +public class MessagePackLz4_v2 : SerializerBase +{ + private static readonly MessagePack.MessagePackSerializerOptions LZ4BlockArray = MessagePack.MessagePackSerializerOptions.Standard.WithCompression(MessagePack.MessagePackCompression.Lz4BlockArray); + + public override T Deserialize(object input) + { + return MessagePack.MessagePackSerializer.Deserialize((byte[])input, LZ4BlockArray); + } + + public override object Serialize(T input) + { + return MessagePack.MessagePackSerializer.Serialize(input, LZ4BlockArray); + } +} + +public class MsgPack_v2_str_lz4 : SerializerBase +{ + private static readonly MessagePack.MessagePackSerializerOptions Options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(MessagePack.Resolvers.ContractlessStandardResolver.Instance).WithCompression(MessagePack.MessagePackCompression.Lz4BlockArray); + + public override T Deserialize(object input) + { + return MessagePack.MessagePackSerializer.Deserialize((byte[])input, Options); + } + + public override object Serialize(T input) + { + return MessagePack.MessagePackSerializer.Serialize(input, Options); + } +} + +public class MsgPack_v2_opt : SerializerBase +{ + private static readonly MessagePack.MessagePackSerializerOptions Options = MessagePack.MessagePackSerializerOptions.Standard.WithResolver(OptimizedResolver.Instance); + + public override T Deserialize(object input) + { + return MessagePack.MessagePackSerializer.Deserialize((byte[])input, Options); + } + + public override object Serialize(T input) + { + return MessagePack.MessagePackSerializer.Serialize(input, Options); + } +} + +public class OptimizedResolver : MessagePack.IFormatterResolver +{ + public static readonly MessagePack.IFormatterResolver Instance = new OptimizedResolver(); + + // configure your custom resolvers. + private static readonly MessagePack.IFormatterResolver[] Resolvers = new MessagePack.IFormatterResolver[] + { + MessagePack.Resolvers.NativeGuidResolver.Instance, + MessagePack.Resolvers.NativeDecimalResolver.Instance, + MessagePack.Resolvers.NativeDateTimeResolver.Instance, + MessagePack.Resolvers.StandardResolver.Instance, + }; + + private OptimizedResolver() + { + } + + public MessagePack.Formatters.IMessagePackFormatter GetFormatter() + { + return Cache.Formatter; + } + + private static class Cache + { +#pragma warning disable SA1401 // Fields should be private + public static MessagePack.Formatters.IMessagePackFormatter Formatter; +#pragma warning restore SA1401 // Fields should be private + + static Cache() + { + foreach (var resolver in Resolvers) + { + var f = resolver.GetFormatter(); + if (f != null) + { + Formatter = f; + return; + } + } + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs new file mode 100644 index 000000000..342bb2af9 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs @@ -0,0 +1,20 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +extern alias newmpcli; +using Benchmark.Serializers; + +#pragma warning disable SA1649 // File name should match first type name + +public class MsgPackCli : SerializerBase +{ + public override T Deserialize(object input) + { + return MsgPack.Serialization.MessagePackSerializer.Get().UnpackSingleObject((byte[])input); + } + + public override object Serialize(T input) + { + return MsgPack.Serialization.MessagePackSerializer.Get().PackSingleObject(input); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/ProtobufSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/ProtobufSerializer.cs new file mode 100644 index 000000000..d0394065d --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/ProtobufSerializer.cs @@ -0,0 +1,29 @@ +// 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.IO; +using Benchmark.Serializers; +using ProtoBuf; + +public class +#pragma warning disable SA1649 // File name should match first type name + +ProtobufNet : SerializerBase +{ + public override T Deserialize(object input) + { + using (var ms = new MemoryStream((byte[])input)) + { + return Serializer.Deserialize(ms); + } + } + + public override object Serialize(T input) + { + using (var ms = new MemoryStream()) + { + Serializer.Serialize(ms, input); + return ms.ToArray(); + } + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SerializerBase.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SerializerBase.cs new file mode 100644 index 000000000..cd092a77d --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SerializerBase.cs @@ -0,0 +1,14 @@ +// 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; + +namespace Benchmark.Serializers +{ + public abstract class SerializerBase + { + public abstract object Serialize(T input); + + public abstract T Deserialize(object input); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SpanJsonSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SpanJsonSerializer.cs new file mode 100644 index 000000000..a3a2f44d7 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SpanJsonSerializer.cs @@ -0,0 +1,19 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Benchmark.Serializers; + +#pragma warning disable SA1649 // File name should match first type name + +public class SpanJson_ : SerializerBase +{ + public override object Serialize(T input) + { + return SpanJson.JsonSerializer.Generic.Utf8.Serialize(input); + } + + public override T Deserialize(object input) + { + return SpanJson.JsonSerializer.Generic.Utf8.Deserialize((byte[])input); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SystemTextJsonSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SystemTextJsonSerializer.cs new file mode 100644 index 000000000..f6358d154 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/SystemTextJsonSerializer.cs @@ -0,0 +1,20 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Benchmark.Serializers; + +#pragma warning disable SA1649 // File name should match first type name + +public class SystemTextJson : SerializerBase +{ + public override object Serialize(T input) + { + return System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(input); + } + + public override T Deserialize(object input) + { + var span = (byte[])input; + return System.Text.Json.JsonSerializer.Deserialize(span); + } +} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/Utf8JsonSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/Utf8JsonSerializer.cs new file mode 100644 index 000000000..f52b0d9b7 --- /dev/null +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/Utf8JsonSerializer.cs @@ -0,0 +1,19 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Benchmark.Serializers; + +#pragma warning disable SA1649 // File name should match first type name + +public class Utf8Json_ : SerializerBase +{ + public override object Serialize(T input) + { + return Utf8Json.JsonSerializer.Serialize(input); + } + + public override T Deserialize(object input) + { + return Utf8Json.JsonSerializer.Deserialize((byte[])input); + } +} diff --git a/test/Benchmark/Readme.md b/test/Benchmark/Readme.md new file mode 100644 index 000000000..c46cdcca7 --- /dev/null +++ b/test/Benchmark/Readme.md @@ -0,0 +1,14 @@ +Benchmark +=== + +For fair comparison and utilize their great job, this directory tree is ported from MessagePack for C# (https://github.com/neuecc/MessagePack-CSharp/). + +License and Copyright Notice +--- + +Files under this directory are licensed under MIT license, not Apache2 license, and note that copyright holders are Yoshifumi Kawai and all MessagePack for C# contributors who include all contributors to this directory. + +Acknowledgement +--- + +Thank you for their excellent jobs and we truly respect them! From a8439b4d989c71d57db52e2bd51cab2cc7254c56 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 21 Jun 2020 01:18:32 +0900 Subject: [PATCH 23/82] Tune-up to exclude central repository overhead --- .../Serializers/MsgPackCliSerializer.cs | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs index 342bb2af9..408bd801c 100644 --- a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs @@ -8,13 +8,32 @@ public class MsgPackCli : SerializerBase { - public override T Deserialize(object input) - { - return MsgPack.Serialization.MessagePackSerializer.Get().UnpackSingleObject((byte[])input); - } + public override T Deserialize(object input) + { + return MsgPackCliSerializerRepository.V1.UnpackSingleObject((byte[])input); + } - public override object Serialize(T input) - { - return MsgPack.Serialization.MessagePackSerializer.Get().PackSingleObject(input); - } + public override object Serialize(T input) + { + return MsgPackCliSerializerRepository.V1.PackSingleObject(input); + } +} +public class MsgPackCli_with_Get : SerializerBase +{ + public override T Deserialize(object input) + { + return MsgPack.Serialization.MessagePackSerializer.Get().UnpackSingleObject((byte[])input); + } + + public override object Serialize(T input) + { + return MsgPack.Serialization.MessagePackSerializer.Get().PackSingleObject(input); + } +} + +internal static class MsgPackCliSerializerRepository +{ + public static readonly MsgPack.Serialization.MessagePackSerializer V1 = SampleSerializer.SerializationContext.GetSerializer(); + +} } From b016661a76718747203493016a9d88e06c609c87 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 21 Jun 2020 01:07:23 +0900 Subject: [PATCH 24/82] Fix prototype v2 serializer primitives * Redesign API for correct behavior * Bug fixes found in initial benchmarking * Tune up to prove whether the architecture is good or not. --- .../Internal/CollectionContext.cs | 8 +- .../Internal/CollectionItemIterator.cs | 22 +- .../Internal/CollectionType.cs | 15 +- .../Internal/DecodeItemResult`1.cs | 2 +- .../Internal/Decoder`1.Primitives.cs | 132 +- .../Internal/Decoder`1.Primitives.tt | 12 +- .../Internal/Decoder`1.Strings.cs | 36 +- .../Internal/Decoder`1.Strings.tt | 12 +- src/MsgPack.Abstraction/Internal/Decoder`1.cs | 87 +- .../Internal/Encoder`1.Primitives.cs | 14 +- .../Internal/Encoder`1.Primitives.tt | 2 +- src/MsgPack.Abstraction/Internal/Encoder`1.cs | 7 +- src/MsgPack.Abstraction/Internal/Ensure.cs | 10 +- .../Internal/OptionsDefaults.cs | 5 + .../Internal/StreamBufferWriter.cs | 2 +- .../Internal/StreamReadOnlyMemoryProvider.cs | 2 +- src/MsgPack.Abstraction/Internal/Throw.cs | 19 + .../MsgPack.Abstraction.csproj | 36 + .../Serialization/DeserializationOptions.cs | 4 +- .../DeserializationOptionsBuilder.cs | 14 +- .../AsyncDeserializationOperationContext`1.cs | 69 + .../AsyncDeserializationOperationContext`1.tt | 6 + .../AsyncSerializationOperationContext`1.cs | 59 + .../AsyncSerializationOperationContext`1.tt | 6 + .../DeserializationOperationContext`1.cs | 19 +- .../DeserializationOperationContext`1.tt | 6 + .../Internal/IObjectSerializer`2.cs | 12 +- .../Internal/OperationContext.ttinclude | 111 + .../SerializationOperationContext`1.cs | 25 +- .../SerializationOperationContext`1.tt | 6 + .../SerializationOptionsBuilder.cs | 2 +- .../Serialization/Serializer`2.cs | 54 +- .../Internal/CurrentMessagePackEncoder.cs | 21 +- .../Internal/LegacyMessagePackEncoder.cs | 10 + .../MessagePackDecoder.CollectionHeaders.cs | 82 +- .../Internal/MessagePackDecoder.DecodeItem.cs | 54 +- .../Internal/MessagePackDecoder.Extension.cs | 16 +- .../Internal/MessagePackDecoder.Integers.cs | 103 +- .../Internal/MessagePackDecoder.Integers.tt | 24 +- .../Internal/MessagePackDecoder.Iteration.cs | 15 +- .../Internal/MessagePackDecoder.Nullables.cs | 66 +- .../Internal/MessagePackDecoder.Nullables.tt | 6 +- .../MessagePackDecoder.Number.ttinclude | 8 +- .../Internal/MessagePackDecoder.Reals.cs | 52 +- .../Internal/MessagePackDecoder.Reals.tt | 12 +- .../Internal/MessagePackDecoder.SkipDrain.cs | 55 +- .../Internal/MessagePackDecoder.Strings.cs | 78 +- .../Internal/MessagePackDecoder.cs | 41 +- .../Internal/MessagePackEncoder.Integers.cs | 8 +- .../Internal/MessagePackEncoder.Integers.tt | 2 +- .../Internal/MessagePackEncoder.Strings.cs | 31 +- .../Internal/MessagePackEncoder.Strings.tt | 13 +- .../Internal/MessagePackEncoder.cs | 18 +- .../Internal/MsgPackStringTrie`1.cs | 354 ++- src/MsgPack.Core/System.Text/Throw.cs | 14 + .../Json/FlexibleJsonDecoder.ReadTrivia.cs | 64 +- src/MsgPack.Json/Json/FlexibleJsonDecoder.cs | 26 +- src/MsgPack.Json/Json/JsonCharactor.cs | 1 + .../Json/JsonDecoder.CollectionHeaders.cs | 7 +- .../Json/JsonDecoder.DecodeItem.cs | 43 +- .../Json/JsonDecoder.Iteration.cs | 48 +- .../Json/JsonDecoder.Nullables.cs | 88 +- .../Json/JsonDecoder.Nullables.tt | 8 +- .../Json/JsonDecoder.NumberCore.cs | 195 +- src/MsgPack.Json/Json/JsonDecoder.Numbers.cs | 100 +- src/MsgPack.Json/Json/JsonDecoder.Numbers.tt | 10 +- .../Json/JsonDecoder.SkipDrain.cs | 11 +- src/MsgPack.Json/Json/JsonDecoder.Strings.cs | 271 +- src/MsgPack.Json/Json/JsonDecoder.cs | 22 +- src/MsgPack.Json/Json/JsonDecoderOptions.cs | 2 + src/MsgPack.Json/Json/JsonEncoder.cs | 715 +++--- src/MsgPack.Json/Json/JsonEncoderOptions.cs | 12 +- .../Json/JsonEncoderOptionsBuilder.cs | 5 +- .../Json/JsonEscapeSequence.EscapeTables.cs | 2186 +++++++++++++++++ .../Json/JsonEscapeSequence.EscapeTables.tt | 218 ++ src/MsgPack.Json/Json/JsonEscapeSequence.cs | 2 +- src/MsgPack.Json/Json/JsonFormatter.cs | 18 +- src/MsgPack.Json/Json/JsonNumberTokens.cs | 95 - src/MsgPack.Json/Json/JsonStringTokens.cs | 26 - src/MsgPack.Json/Json/JsonThrow.cs | 4 +- src/MsgPack.Json/Json/JsonTokens.cs | 29 - src/MsgPack.Json/Json/JsonTriviaTokens.cs | 12 - src/MsgPack.Json/Json/SimpleJsonDecoder.cs | 28 +- .../Json/StringBuilderBufferWriter.cs | 3 +- src/MsgPack.Json/MsgPack.Json.csproj | 20 +- 85 files changed, 4767 insertions(+), 1401 deletions(-) create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.cs create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.tt create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.cs create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.tt create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.tt create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/OperationContext.ttinclude create mode 100644 src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.tt create mode 100644 src/MsgPack.Core/System.Text/Throw.cs create mode 100644 src/MsgPack.Json/Json/JsonEscapeSequence.EscapeTables.cs create mode 100644 src/MsgPack.Json/Json/JsonEscapeSequence.EscapeTables.tt delete mode 100644 src/MsgPack.Json/Json/JsonNumberTokens.cs delete mode 100644 src/MsgPack.Json/Json/JsonTokens.cs diff --git a/src/MsgPack.Abstraction/Internal/CollectionContext.cs b/src/MsgPack.Abstraction/Internal/CollectionContext.cs index 8b2cdb5d5..75b82355e 100644 --- a/src/MsgPack.Abstraction/Internal/CollectionContext.cs +++ b/src/MsgPack.Abstraction/Internal/CollectionContext.cs @@ -2,17 +2,15 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. -using MsgPack.Serialization; - namespace MsgPack.Internal { public partial struct CollectionContext { public static CollectionContext Default => new CollectionContext( - DeserializationOptionsBuilder.DefaultMaxArrayLength, - DeserializationOptionsBuilder.DefaultMaxMapCount, - DeserializationOptionsBuilder.DefaultMaxDepth, + OptionsDefaults.MaxArrayLength, + OptionsDefaults.MaxMapCount, + OptionsDefaults.MaxDepth, currentDepth: 0 ); diff --git a/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs b/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs index ddf24be47..9fa613652 100644 --- a/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs +++ b/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs @@ -10,7 +10,7 @@ namespace MsgPack.Internal { public struct CollectionItemIterator { - public delegate bool CollectionEndDetection(in SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint); + public delegate bool CollectionEndDetection(ref SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint); private readonly long _itemsCount; private long _nextItemIndex; @@ -27,9 +27,9 @@ long itemsCount } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public bool CollectionEnds(in SequenceReader source) + public bool CollectionEnds(ref SequenceReader source) { - var result = this._collectionEnds(source, ref this._nextItemIndex, this._itemsCount, out var requestHint); + var result = this._collectionEnds(ref source, ref this._nextItemIndex, this._itemsCount, out var requestHint); if (requestHint != 0) { Throw.InsufficientInputForDetectCollectionEnds(source.Consumed, requestHint); @@ -39,29 +39,29 @@ public bool CollectionEnds(in SequenceReader source) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public bool CollectionEnds(in SequenceReader source, out int requestHint) - => this._collectionEnds(source, ref this._nextItemIndex, this._itemsCount, out requestHint); + public bool CollectionEnds(ref SequenceReader source, out int requestHint) + => this._collectionEnds(ref source, ref this._nextItemIndex, this._itemsCount, out requestHint); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public bool CollectionEnds(ReadOnlyMemory source, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(source)); - return this.CollectionEnds(reader, out requestHint); + return this.CollectionEnds(ref reader, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public void Drain(in SequenceReader source) + public void Drain(ref SequenceReader source) { - if (!this.Drain(source, out var requestHint)) + if (!this.Drain(ref source, out var requestHint)) { Throw.InsufficientInputForDrainCollectionItems(source.Consumed, requestHint); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public bool Drain(in SequenceReader source, out int requestHint) + public bool Drain(ref SequenceReader source, out int requestHint) { - while (!this.CollectionEnds(source, out requestHint)) + while (!this.CollectionEnds(ref source, out requestHint)) { if (requestHint != 0) { @@ -76,7 +76,7 @@ public bool Drain(in SequenceReader source, out int requestHint) public bool Drain(ref ReadOnlyMemory source, out int requestHint) { var reader = new SequenceReader(new ReadOnlySequence(source)); - var ends = this.Drain(reader, out requestHint); + var ends = this.Drain(ref reader, out requestHint); source = source.Slice((int)reader.Consumed); return ends; } diff --git a/src/MsgPack.Abstraction/Internal/CollectionType.cs b/src/MsgPack.Abstraction/Internal/CollectionType.cs index 1875b8472..ad9a9fadd 100644 --- a/src/MsgPack.Abstraction/Internal/CollectionType.cs +++ b/src/MsgPack.Abstraction/Internal/CollectionType.cs @@ -13,23 +13,30 @@ namespace MsgPack.Internal public readonly struct CollectionType : IEquatable { public static readonly CollectionType None = default; - public static readonly CollectionType Array = new CollectionType(1); - public static readonly CollectionType Map = new CollectionType(2); + public static readonly CollectionType Null = new CollectionType(1); + public static readonly CollectionType Array = new CollectionType(2); + public static readonly CollectionType Map = new CollectionType(3); private readonly int _type; - public bool IsArray + public bool IsNull { [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] get => this._type == 1; } - public bool IsMap + public bool IsArray { [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] get => this._type == 2; } + public bool IsMap + { + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + get => this._type == 3; + } + public bool IsNone { [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] diff --git a/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs b/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs index 96e2d8db7..42749053b 100644 --- a/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs +++ b/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs @@ -9,7 +9,7 @@ namespace MsgPack.Internal { /// - /// Represetns a result of . + /// Represents a result of . /// public readonly struct DecodeItemResult { diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs b/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs index 8c4d48b33..7765926f1 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs @@ -29,9 +29,9 @@ partial class Decoder /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Byte DecodeByte(in SequenceReader source) + public Byte DecodeByte(ref SequenceReader source) { - var result = this.DecodeByte(source, out var requestHint); + var result = this.DecodeByte(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Byte), requestHint); @@ -57,7 +57,7 @@ public Byte DecodeByte(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract Byte DecodeByte(in SequenceReader source, out int requestHint); + public abstract Byte DecodeByte(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -74,9 +74,9 @@ public Byte DecodeByte(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Byte? DecodeNullableByte(in SequenceReader source) + public Byte? DecodeNullableByte(ref SequenceReader source) { - var result = this.DecodeNullableByte(source, out var requestHint); + var result = this.DecodeNullableByte(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Byte), requestHint); @@ -103,7 +103,7 @@ public Byte DecodeByte(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract Byte? DecodeNullableByte(in SequenceReader source, out int requestHint); + public abstract Byte? DecodeNullableByte(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -120,9 +120,9 @@ public Byte DecodeByte(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Int16 DecodeInt16(in SequenceReader source) + public Int16 DecodeInt16(ref SequenceReader source) { - var result = this.DecodeInt16(source, out var requestHint); + var result = this.DecodeInt16(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Int16), requestHint); @@ -148,7 +148,7 @@ public Int16 DecodeInt16(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract Int16 DecodeInt16(in SequenceReader source, out int requestHint); + public abstract Int16 DecodeInt16(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -165,9 +165,9 @@ public Int16 DecodeInt16(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Int16? DecodeNullableInt16(in SequenceReader source) + public Int16? DecodeNullableInt16(ref SequenceReader source) { - var result = this.DecodeNullableInt16(source, out var requestHint); + var result = this.DecodeNullableInt16(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Int16), requestHint); @@ -194,7 +194,7 @@ public Int16 DecodeInt16(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract Int16? DecodeNullableInt16(in SequenceReader source, out int requestHint); + public abstract Int16? DecodeNullableInt16(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -211,9 +211,9 @@ public Int16 DecodeInt16(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Int32 DecodeInt32(in SequenceReader source) + public Int32 DecodeInt32(ref SequenceReader source) { - var result = this.DecodeInt32(source, out var requestHint); + var result = this.DecodeInt32(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Int32), requestHint); @@ -239,7 +239,7 @@ public Int32 DecodeInt32(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract Int32 DecodeInt32(in SequenceReader source, out int requestHint); + public abstract Int32 DecodeInt32(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -256,9 +256,9 @@ public Int32 DecodeInt32(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Int32? DecodeNullableInt32(in SequenceReader source) + public Int32? DecodeNullableInt32(ref SequenceReader source) { - var result = this.DecodeNullableInt32(source, out var requestHint); + var result = this.DecodeNullableInt32(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Int32), requestHint); @@ -285,7 +285,7 @@ public Int32 DecodeInt32(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract Int32? DecodeNullableInt32(in SequenceReader source, out int requestHint); + public abstract Int32? DecodeNullableInt32(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -302,9 +302,9 @@ public Int32 DecodeInt32(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Int64 DecodeInt64(in SequenceReader source) + public Int64 DecodeInt64(ref SequenceReader source) { - var result = this.DecodeInt64(source, out var requestHint); + var result = this.DecodeInt64(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Int64), requestHint); @@ -330,7 +330,7 @@ public Int64 DecodeInt64(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract Int64 DecodeInt64(in SequenceReader source, out int requestHint); + public abstract Int64 DecodeInt64(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -347,9 +347,9 @@ public Int64 DecodeInt64(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Int64? DecodeNullableInt64(in SequenceReader source) + public Int64? DecodeNullableInt64(ref SequenceReader source) { - var result = this.DecodeNullableInt64(source, out var requestHint); + var result = this.DecodeNullableInt64(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Int64), requestHint); @@ -376,7 +376,7 @@ public Int64 DecodeInt64(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract Int64? DecodeNullableInt64(in SequenceReader source, out int requestHint); + public abstract Int64? DecodeNullableInt64(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -393,9 +393,9 @@ public Int64 DecodeInt64(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public SByte DecodeSByte(in SequenceReader source) + public SByte DecodeSByte(ref SequenceReader source) { - var result = this.DecodeSByte(source, out var requestHint); + var result = this.DecodeSByte(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(SByte), requestHint); @@ -421,7 +421,7 @@ public SByte DecodeSByte(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract SByte DecodeSByte(in SequenceReader source, out int requestHint); + public abstract SByte DecodeSByte(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -438,9 +438,9 @@ public SByte DecodeSByte(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public SByte? DecodeNullableSByte(in SequenceReader source) + public SByte? DecodeNullableSByte(ref SequenceReader source) { - var result = this.DecodeNullableSByte(source, out var requestHint); + var result = this.DecodeNullableSByte(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(SByte), requestHint); @@ -467,7 +467,7 @@ public SByte DecodeSByte(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract SByte? DecodeNullableSByte(in SequenceReader source, out int requestHint); + public abstract SByte? DecodeNullableSByte(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -484,9 +484,9 @@ public SByte DecodeSByte(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public UInt16 DecodeUInt16(in SequenceReader source) + public UInt16 DecodeUInt16(ref SequenceReader source) { - var result = this.DecodeUInt16(source, out var requestHint); + var result = this.DecodeUInt16(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(UInt16), requestHint); @@ -512,7 +512,7 @@ public UInt16 DecodeUInt16(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract UInt16 DecodeUInt16(in SequenceReader source, out int requestHint); + public abstract UInt16 DecodeUInt16(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -529,9 +529,9 @@ public UInt16 DecodeUInt16(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public UInt16? DecodeNullableUInt16(in SequenceReader source) + public UInt16? DecodeNullableUInt16(ref SequenceReader source) { - var result = this.DecodeNullableUInt16(source, out var requestHint); + var result = this.DecodeNullableUInt16(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(UInt16), requestHint); @@ -558,7 +558,7 @@ public UInt16 DecodeUInt16(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract UInt16? DecodeNullableUInt16(in SequenceReader source, out int requestHint); + public abstract UInt16? DecodeNullableUInt16(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -575,9 +575,9 @@ public UInt16 DecodeUInt16(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public UInt32 DecodeUInt32(in SequenceReader source) + public UInt32 DecodeUInt32(ref SequenceReader source) { - var result = this.DecodeUInt32(source, out var requestHint); + var result = this.DecodeUInt32(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(UInt32), requestHint); @@ -603,7 +603,7 @@ public UInt32 DecodeUInt32(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract UInt32 DecodeUInt32(in SequenceReader source, out int requestHint); + public abstract UInt32 DecodeUInt32(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -620,9 +620,9 @@ public UInt32 DecodeUInt32(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public UInt32? DecodeNullableUInt32(in SequenceReader source) + public UInt32? DecodeNullableUInt32(ref SequenceReader source) { - var result = this.DecodeNullableUInt32(source, out var requestHint); + var result = this.DecodeNullableUInt32(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(UInt32), requestHint); @@ -649,7 +649,7 @@ public UInt32 DecodeUInt32(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract UInt32? DecodeNullableUInt32(in SequenceReader source, out int requestHint); + public abstract UInt32? DecodeNullableUInt32(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -666,9 +666,9 @@ public UInt32 DecodeUInt32(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public UInt64 DecodeUInt64(in SequenceReader source) + public UInt64 DecodeUInt64(ref SequenceReader source) { - var result = this.DecodeUInt64(source, out var requestHint); + var result = this.DecodeUInt64(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(UInt64), requestHint); @@ -694,7 +694,7 @@ public UInt64 DecodeUInt64(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract UInt64 DecodeUInt64(in SequenceReader source, out int requestHint); + public abstract UInt64 DecodeUInt64(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -711,9 +711,9 @@ public UInt64 DecodeUInt64(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public UInt64? DecodeNullableUInt64(in SequenceReader source) + public UInt64? DecodeNullableUInt64(ref SequenceReader source) { - var result = this.DecodeNullableUInt64(source, out var requestHint); + var result = this.DecodeNullableUInt64(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(UInt64), requestHint); @@ -740,7 +740,7 @@ public UInt64 DecodeUInt64(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract UInt64? DecodeNullableUInt64(in SequenceReader source, out int requestHint); + public abstract UInt64? DecodeNullableUInt64(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -757,9 +757,9 @@ public UInt64 DecodeUInt64(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Single DecodeSingle(in SequenceReader source) + public Single DecodeSingle(ref SequenceReader source) { - var result = this.DecodeSingle(source, out var requestHint); + var result = this.DecodeSingle(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Single), requestHint); @@ -785,7 +785,7 @@ public Single DecodeSingle(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract Single DecodeSingle(in SequenceReader source, out int requestHint); + public abstract Single DecodeSingle(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -802,9 +802,9 @@ public Single DecodeSingle(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Single? DecodeNullableSingle(in SequenceReader source) + public Single? DecodeNullableSingle(ref SequenceReader source) { - var result = this.DecodeNullableSingle(source, out var requestHint); + var result = this.DecodeNullableSingle(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Single), requestHint); @@ -831,7 +831,7 @@ public Single DecodeSingle(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract Single? DecodeNullableSingle(in SequenceReader source, out int requestHint); + public abstract Single? DecodeNullableSingle(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -848,9 +848,9 @@ public Single DecodeSingle(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Double DecodeDouble(in SequenceReader source) + public Double DecodeDouble(ref SequenceReader source) { - var result = this.DecodeDouble(source, out var requestHint); + var result = this.DecodeDouble(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Double), requestHint); @@ -876,7 +876,7 @@ public Double DecodeDouble(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract Double DecodeDouble(in SequenceReader source, out int requestHint); + public abstract Double DecodeDouble(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -893,9 +893,9 @@ public Double DecodeDouble(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Double? DecodeNullableDouble(in SequenceReader source) + public Double? DecodeNullableDouble(ref SequenceReader source) { - var result = this.DecodeNullableDouble(source, out var requestHint); + var result = this.DecodeNullableDouble(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Double), requestHint); @@ -922,7 +922,7 @@ public Double DecodeDouble(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract Double? DecodeNullableDouble(in SequenceReader source, out int requestHint); + public abstract Double? DecodeNullableDouble(ref SequenceReader source, out int requestHint); /// /// Decodes value from specified sequence. @@ -939,9 +939,9 @@ public Double DecodeDouble(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Boolean DecodeBoolean(in SequenceReader source) + public Boolean DecodeBoolean(ref SequenceReader source) { - var result = this.DecodeBoolean(source, out var requestHint); + var result = this.DecodeBoolean(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Boolean), requestHint); @@ -967,7 +967,7 @@ public Boolean DecodeBoolean(in SequenceReader source) /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract Boolean DecodeBoolean(in SequenceReader source, out int requestHint); + public abstract Boolean DecodeBoolean(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -984,9 +984,9 @@ public Boolean DecodeBoolean(in SequenceReader source) /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Boolean? DecodeNullableBoolean(in SequenceReader source) + public Boolean? DecodeNullableBoolean(ref SequenceReader source) { - var result = this.DecodeNullableBoolean(source, out var requestHint); + var result = this.DecodeNullableBoolean(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(Boolean), requestHint); @@ -1013,7 +1013,7 @@ public Boolean DecodeBoolean(in SequenceReader source) /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract Boolean? DecodeNullableBoolean(in SequenceReader source, out int requestHint); + public abstract Boolean? DecodeNullableBoolean(ref SequenceReader source, out int requestHint); } } diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt b/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt index c814ebe99..0544c21c5 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt @@ -47,9 +47,9 @@ foreach (var outputType in new [] { /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public <#= outputType #> Decode<#= outputType #>(in SequenceReader source) + public <#= outputType #> Decode<#= outputType #>(ref SequenceReader source) { - var result = this.Decode<#= outputType #>(source, out var requestHint); + var result = this.Decode<#= outputType #>(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(<#= outputType #>), requestHint); @@ -75,7 +75,7 @@ foreach (var outputType in new [] { /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract <#= outputType #> Decode<#= outputType #>(in SequenceReader source, out int requestHint); + public abstract <#= outputType #> Decode<#= outputType #>(ref SequenceReader source, out int requestHint); /// /// Encodes value or null from specified sequence. @@ -92,9 +92,9 @@ foreach (var outputType in new [] { /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public <#= outputType #>? DecodeNullable<#= outputType #>(in SequenceReader source) + public <#= outputType #>? DecodeNullable<#= outputType #>(ref SequenceReader source) { - var result = this.DecodeNullable<#= outputType #>(source, out var requestHint); + var result = this.DecodeNullable<#= outputType #>(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(<#= outputType #>), requestHint); @@ -121,7 +121,7 @@ foreach (var outputType in new [] { /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract <#= outputType #>? DecodeNullable<#= outputType #>(in SequenceReader source, out int requestHint); + public abstract <#= outputType #>? DecodeNullable<#= outputType #>(ref SequenceReader source, out int requestHint); <# } diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs b/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs index 17c91200f..f9aeb3571 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs @@ -35,9 +35,9 @@ partial class Decoder /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public String DecodeString(in SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) + public String DecodeString(ref SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) { - var result = this.DecodeString(source, out var requestHint, encoding, cancellationToken); + var result = this.DecodeString(ref source, out var requestHint, encoding, cancellationToken); if (requestHint != 0) { Throw.InsufficientInputForString(source.Consumed, typeof(String), encoding, requestHint); @@ -65,7 +65,7 @@ public String DecodeString(in SequenceReader source, Encoding? encoding = /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract String? DecodeString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); + public abstract String? DecodeString(ref SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); /// /// Decodes value or null from specified sequence. @@ -83,9 +83,9 @@ public String DecodeString(in SequenceReader source, Encoding? encoding = /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public String? DecodeNullableString(in SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) + public String? DecodeNullableString(ref SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) { - var result = this.DecodeNullableString(source, out var requestHint, encoding, cancellationToken); + var result = this.DecodeNullableString(ref source, out var requestHint, encoding, cancellationToken); if (requestHint != 0) { Throw.InsufficientInputForString(source.Consumed, typeof(String), encoding, requestHint); @@ -113,7 +113,7 @@ public String DecodeString(in SequenceReader source, Encoding? encoding = /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract String? DecodeNullableString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); + public abstract String? DecodeNullableString(ref SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); #if FEATURE_UTF8STRING @@ -135,9 +135,9 @@ public String DecodeString(in SequenceReader source, Encoding? encoding = /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Utf8String DecodeUtf8String(in SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) + public Utf8String DecodeUtf8String(ref SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) { - var result = this.DecodeUtf8String(source, out var requestHint, encoding, cancellationToken); + var result = this.DecodeUtf8String(ref source, out var requestHint, encoding, cancellationToken); if (requestHint != 0) { Throw.InsufficientInputForUtf8String(source.Consumed, typeof(Utf8String), encoding, requestHint); @@ -165,7 +165,7 @@ public Utf8String DecodeUtf8String(in SequenceReader source, Encoding? enc /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract Utf8String? DecodeUtf8String(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); + public abstract Utf8String? DecodeUtf8String(ref SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); /// /// Decodes value or null from specified sequence. @@ -183,9 +183,9 @@ public Utf8String DecodeUtf8String(in SequenceReader source, Encoding? enc /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public Utf8String? DecodeNullableUtf8String(in SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) + public Utf8String? DecodeNullableUtf8String(ref SequenceReader source, Encoding? encoding = null, CancellationToken cancellationToken = default) { - var result = this.DecodeNullableUtf8String(source, out var requestHint, encoding, cancellationToken); + var result = this.DecodeNullableUtf8String(ref source, out var requestHint, encoding, cancellationToken); if (requestHint != 0) { Throw.InsufficientInputForUtf8String(source.Consumed, typeof(Utf8String), encoding, requestHint); @@ -213,7 +213,7 @@ public Utf8String DecodeUtf8String(in SequenceReader source, Encoding? enc /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract Utf8String? DecodeNullableUtf8String(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); + public abstract Utf8String? DecodeNullableUtf8String(ref SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default); #endif // FEATURE_UTF8STRING @@ -234,9 +234,9 @@ public Utf8String DecodeUtf8String(in SequenceReader source, Encoding? enc /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public byte[] DecodeBinary(in SequenceReader source, CancellationToken cancellationToken = default) + public byte[] DecodeBinary(ref SequenceReader source, CancellationToken cancellationToken = default) { - var result = this.DecodeBinary(source, out var requestHint, cancellationToken); + var result = this.DecodeBinary(ref source, out var requestHint, cancellationToken); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(byte[]), requestHint); @@ -263,7 +263,7 @@ public byte[] DecodeBinary(in SequenceReader source, CancellationToken can /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract byte[]? DecodeBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default); + public abstract byte[]? DecodeBinary(ref SequenceReader source, out int requestHint, CancellationToken cancellationToken = default); /// /// Decodes value or null from specified sequence. @@ -280,9 +280,9 @@ public byte[] DecodeBinary(in SequenceReader source, CancellationToken can /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public byte[]? DecodeNullableBinary(in SequenceReader source, CancellationToken cancellationToken = default) + public byte[]? DecodeNullableBinary(ref SequenceReader source, CancellationToken cancellationToken = default) { - var result = this.DecodeNullableBinary(source, out var requestHint, cancellationToken); + var result = this.DecodeNullableBinary(ref source, out var requestHint, cancellationToken); if (requestHint != 0) { Throw.InsufficientInput(source.Consumed, typeof(byte[]), requestHint); @@ -309,7 +309,7 @@ public byte[] DecodeBinary(in SequenceReader source, CancellationToken can /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract byte[]? DecodeNullableBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default); + public abstract byte[]? DecodeNullableBinary(ref SequenceReader source, out int requestHint, CancellationToken cancellationToken = default); } } diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt b/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt index ad9ec6506..f731b08a9 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt @@ -78,9 +78,9 @@ foreach (var spec in new [] { /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public <#= spec.Type #> Decode<#= spec.Name #>(in SequenceReader source, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default) + public <#= spec.Type #> Decode<#= spec.Name #>(ref SequenceReader source, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default) { - var result = this.Decode<#= spec.Name #>(source, out var requestHint, <#= spec.ExtraArguments #>cancellationToken); + var result = this.Decode<#= spec.Name #>(ref source, out var requestHint, <#= spec.ExtraArguments #>cancellationToken); if (requestHint != 0) { Throw.InsufficientInput<#= spec.ThrowSuffix #>(source.Consumed, typeof(<#= spec.Type #>), <#= spec.ExtraArguments #>requestHint); @@ -115,7 +115,7 @@ foreach (var spec in new [] { /// /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. - public abstract <#= spec.Type #>? Decode<#= spec.Name #>(in SequenceReader source, out int requestHint, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default); + public abstract <#= spec.Type #>? Decode<#= spec.Name #>(ref SequenceReader source, out int requestHint, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default); /// /// Decodes value or null from specified sequence. @@ -140,9 +140,9 @@ foreach (var spec in new [] { /// The underlying format does not suppor this type. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public <#= spec.Type #>? DecodeNullable<#= spec.Name #>(in SequenceReader source, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default) + public <#= spec.Type #>? DecodeNullable<#= spec.Name #>(ref SequenceReader source, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default) { - var result = this.DecodeNullable<#= spec.Name #>(source, out var requestHint, <#= spec.ExtraArguments #>cancellationToken); + var result = this.DecodeNullable<#= spec.Name #>(ref source, out var requestHint, <#= spec.ExtraArguments #>cancellationToken); if (requestHint != 0) { Throw.InsufficientInput<#= spec.ThrowSuffix #>(source.Consumed, typeof(<#= spec.Type #>), <#= spec.ExtraArguments #>requestHint); @@ -177,7 +177,7 @@ foreach (var spec in new [] { /// The underlying format value is not compatible to type. /// The underlying format does not suppor this type. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract <#= spec.Type #>? DecodeNullable<#= spec.Name #>(in SequenceReader source, out int requestHint, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default); + public abstract <#= spec.Type #>? DecodeNullable<#= spec.Name #>(ref SequenceReader source, out int requestHint, <#= spec.ExtraParameters #>CancellationToken cancellationToken = default); <# if (!String.IsNullOrEmpty(spec.IfDef)) diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.cs b/src/MsgPack.Abstraction/Internal/Decoder`1.cs index 74d52cfc0..271fd0ee9 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.cs +++ b/src/MsgPack.Abstraction/Internal/Decoder`1.cs @@ -30,32 +30,49 @@ protected Decoder(DecoderOptions options, FormatFeatures formatFeatures) this.FormatFeatures = Ensure.NotNull(formatFeatures); } - public void Skip(in SequenceReader source, in CollectionContext collectionContext, CancellationToken cancellationToken = default) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void Skip(ref SequenceReader source, in CollectionContext collectionContext, CancellationToken cancellationToken = default) { - this.Skip(source, collectionContext, out var requestHint, cancellationToken); + this.Skip(ref source, collectionContext, out var requestHint, cancellationToken); if (requestHint != 0) { Throw.InsufficientInputForSkip(source.Consumed, requestHint); } } - public abstract void Skip(in SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default); - - [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public bool TryPeek(in SequenceReader source, out byte value) => source.TryRead(out value); + public abstract void Skip(ref SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default); - public abstract bool DecodeItem(in SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default); + public abstract bool DecodeItem(ref SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public void GetRawString(in SequenceReader source, out ReadOnlySpan rawString, CancellationToken cancellationToken = default) + public void GetRawString(ref SequenceReader source, out ReadOnlySpan rawString, CancellationToken cancellationToken = default) { - if (!this.GetRawString(source, out rawString, out var requestHint, cancellationToken)) + if (!this.GetRawString(ref source, out rawString, out var requestHint, cancellationToken)) { Throw.InsufficientInputForRawString(source.Consumed, requestHint); } } - public abstract bool GetRawString(in SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default); + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public bool TryDecodeNull(ref SequenceReader source) + { + if (this.TryDecodeNull(ref source, out var requestHint)) + { + return true; + } + + if (requestHint != 0) + { + Throw.InsufficientInputForNull(source.Consumed, requestHint); + } + + return false; + } + + public abstract bool TryDecodeNull(ref SequenceReader source, out int requestHint); + + + public abstract bool GetRawString(ref SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default); /// /// Decodes current data as array or map header, and returns the items count if known. @@ -69,9 +86,9 @@ public void GetRawString(in SequenceReader source, out ReadOnlySpan /// The decoded value is not an array nor a map. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out long itemsCount) + public CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out long itemsCount) { - var result = this.DecodeArrayOrMapHeader(source, out itemsCount, out var requestHint); + var result = this.DecodeArrayOrMapHeader(ref source, out itemsCount, out var requestHint); if (requestHint != 0) { Throw.InsufficientInputForDecodeArrayOrMapHeader(source.Consumed, requestHint); @@ -94,7 +111,7 @@ public CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out /// This method does not return anything else, but may throw an exception. /// /// The decoded value is not an array nor a map. - public abstract CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out long itemsCount, out int requestHint); + public abstract CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out long itemsCount, out int requestHint); /// /// Decodes current data as array header, and returns the items count if known. @@ -107,9 +124,9 @@ public CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out /// The decoded value is not an array. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public long DecodeArrayHeader(in SequenceReader source) + public long DecodeArrayHeader(ref SequenceReader source) { - var result = this.DecodeArrayHeader(source, out var requestHint); + var result = this.DecodeArrayHeader(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInputForDecodeArrayHeader(source.Consumed, requestHint); @@ -132,7 +149,7 @@ public long DecodeArrayHeader(in SequenceReader source) /// /// The decoded value is not an array. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract long DecodeArrayHeader(in SequenceReader source, out int requestHint); + public abstract long DecodeArrayHeader(ref SequenceReader source, out int requestHint); /// /// Decodes current data as map header, and returns the items count if known. @@ -145,9 +162,9 @@ public long DecodeArrayHeader(in SequenceReader source) /// The decoded value is not a map. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public long DecodeMapHeader(in SequenceReader source) + public long DecodeMapHeader(ref SequenceReader source) { - var result = this.DecodeMapHeader(source, out var requestHint); + var result = this.DecodeMapHeader(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInputForDecodeMapHeader(source.Consumed, requestHint); @@ -169,9 +186,9 @@ public long DecodeMapHeader(in SequenceReader source) /// Note that 0 is valid value when the map is empty. /// /// The decoded value is not a map. - public abstract long DecodeMapHeader(in SequenceReader source, out int requestHint); + public abstract long DecodeMapHeader(ref SequenceReader source, out int requestHint); - public virtual void DecodeExtension(in SequenceReader source, out TExtensionType typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) + public virtual void DecodeExtension(ref SequenceReader source, out TExtensionType typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) { Throw.ExtensionsIsNotSupported(); // never @@ -180,9 +197,9 @@ public virtual void DecodeExtension(in SequenceReader source, out TExtensi typeCode = default!; } - public CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator) + public CollectionType DecodeArrayOrMap(ref SequenceReader source, out CollectionItemIterator iterator) { - var result = this.DecodeArrayOrMap(source, out iterator, out var requestHint); + var result = this.DecodeArrayOrMap(ref source, out iterator, out var requestHint); if (requestHint != 0) { Throw.InsufficientInputForDecodeArrayOrMapHeader(source.Consumed, requestHint); @@ -191,11 +208,11 @@ public CollectionType DecodeArrayOrMap(in SequenceReader source, out Colle return result; } - public abstract CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator, out int requestHint); + public abstract CollectionType DecodeArrayOrMap(ref SequenceReader source, out CollectionItemIterator iterator, out int requestHint); - public CollectionItemIterator DecodeArray(in SequenceReader source) + public CollectionItemIterator DecodeArray(ref SequenceReader source) { - var result = this.DecodeArray(source, out var requestHint); + var result = this.DecodeArray(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInputForDecodeArrayHeader(source.Consumed, requestHint); @@ -204,11 +221,11 @@ public CollectionItemIterator DecodeArray(in SequenceReader source) return result; } - public abstract CollectionItemIterator DecodeArray(in SequenceReader source, out int requestHint); + public abstract CollectionItemIterator DecodeArray(ref SequenceReader source, out int requestHint); - public CollectionItemIterator DecodeMap(in SequenceReader source) + public CollectionItemIterator DecodeMap(ref SequenceReader source) { - var result = this.DecodeMap(source, out var requestHint); + var result = this.DecodeMap(ref source, out var requestHint); if (requestHint != 0) { Throw.InsufficientInputForDecodeMapHeader(source.Consumed, requestHint); @@ -217,17 +234,23 @@ public CollectionItemIterator DecodeMap(in SequenceReader source) return result; } - public abstract CollectionItemIterator DecodeMap(in SequenceReader source, out int requestHint); + public abstract CollectionItemIterator DecodeMap(ref SequenceReader source, out int requestHint); - public void Drain(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, CancellationToken cancellationToken = default) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void Drain(ref SequenceReader source, in CollectionContext collectionContext, long itemsCount, CancellationToken cancellationToken = default) { - this.Drain(source, collectionContext, itemsCount, out var requestHint, cancellationToken); + if (itemsCount <= 0) + { + return; + } + + this.Drain(ref source, collectionContext, itemsCount, out var requestHint, cancellationToken); if (requestHint != 0) { Throw.InsufficientInputForDecodeMapHeader(source.Consumed, requestHint); } } - public abstract void Drain(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default); + public abstract void Drain(ref SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default); } } diff --git a/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs b/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs index 32f4ce55d..bea877506 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs +++ b/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs @@ -33,7 +33,7 @@ partial class Encoder [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void EncodeInt32(Int32? value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value == null) { @@ -63,7 +63,7 @@ public void EncodeInt32(Int32? value, IBufferWriter buffer) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void EncodeInt64(Int64? value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value == null) { @@ -93,7 +93,7 @@ public void EncodeInt64(Int64? value, IBufferWriter buffer) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void EncodeUInt32(UInt32? value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value == null) { @@ -123,7 +123,7 @@ public void EncodeUInt32(UInt32? value, IBufferWriter buffer) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void EncodeUInt64(UInt64? value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value == null) { @@ -153,7 +153,7 @@ public void EncodeUInt64(UInt64? value, IBufferWriter buffer) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void EncodeSingle(Single? value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value == null) { @@ -183,7 +183,7 @@ public void EncodeSingle(Single? value, IBufferWriter buffer) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void EncodeDouble(Double? value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value == null) { @@ -213,7 +213,7 @@ public void EncodeDouble(Double? value, IBufferWriter buffer) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void EncodeBoolean(Boolean? value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value == null) { diff --git a/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt b/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt index 4d393c801..84556bc5c 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt +++ b/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt @@ -47,7 +47,7 @@ foreach (var inputType in new [] { [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void Encode<#= inputType #>(<#= inputType #>? value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value == null) { diff --git a/src/MsgPack.Abstraction/Internal/Encoder`1.cs b/src/MsgPack.Abstraction/Internal/Encoder`1.cs index 55c783099..709e4f1f9 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder`1.cs +++ b/src/MsgPack.Abstraction/Internal/Encoder`1.cs @@ -58,6 +58,8 @@ public void EncodeString(string? value, IBufferWriter buffer, Encoding? en public abstract void EncodeString(in ReadOnlySequence encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default); + public abstract void EncodeRawString(ReadOnlySpan rawString, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default); + public abstract void EncodeBinary(ReadOnlySpan value, IBufferWriter buffer, CancellationToken cancellationToken = default); public abstract void EncodeBinary(in ReadOnlySequence value, IBufferWriter buffer, CancellationToken cancellationToken = default); @@ -135,7 +137,7 @@ private static void ThrowBufferWriterReturnsEmptyBuffer(string paramName) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void WriteRaw(in ReadOnlySequence value, IBufferWriter buffer, CancellationToken cancellationToken = default) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value.Length <= this.Options.MaxByteBufferLength) { @@ -169,8 +171,5 @@ private static void WriteRawSlow(in ReadOnlySequence value, IBufferWriter< reader.Advance(source.Length); } } - - private protected static IBufferWriter EnsureNotNull(IBufferWriter buffer) - => buffer ?? throw new ArgumentNullException(nameof(buffer)); } } diff --git a/src/MsgPack.Abstraction/Internal/Ensure.cs b/src/MsgPack.Abstraction/Internal/Ensure.cs index e2a24c72c..379f8e90f 100644 --- a/src/MsgPack.Abstraction/Internal/Ensure.cs +++ b/src/MsgPack.Abstraction/Internal/Ensure.cs @@ -3,6 +3,7 @@ // See the LICENSE in the project root for more information. using System; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -15,7 +16,8 @@ public static T NotNull([NotNull]T value, [CallerArgumentExpression("value")] { if (value == null) { - throw new ArgumentNullException(paramName); + Throw.ArgumentNull(paramName); + Debug.Assert(value != null); } return value; @@ -25,7 +27,7 @@ public static int IsNotNegative(int value, [CallerArgumentExpression("value")]st { if (value < 0) { - throw new ArgumentOutOfRangeException(paramName, "Value cannot be negative number."); + Throw.ArgumentOutOfRange(paramName, "Value cannot be negative number."); } return value; @@ -36,7 +38,7 @@ public static T IsNotLessThan(T value, T minInclusive, [CallerArgumentExpress { if (value.CompareTo(minInclusive) < 0) { - throw new ArgumentOutOfRangeException(paramName, $"Value cannot be less than {minInclusive}."); + Throw.ArgumentOutOfRange(paramName, $"Value cannot be less than {minInclusive}."); } return value; @@ -47,7 +49,7 @@ public static T IsNotGreaterThan(T value, T maxInclusive, [CallerArgumentExpr { if (value.CompareTo(maxInclusive) > 0) { - throw new ArgumentOutOfRangeException(paramName, $"Value cannot be greater than {maxInclusive}."); + Throw.ArgumentOutOfRange(paramName, $"Value cannot be greater than {maxInclusive}."); } return value; diff --git a/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs b/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs index c8fa9fbed..7f470ff1f 100644 --- a/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs +++ b/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs @@ -6,6 +6,7 @@ namespace MsgPack.Internal { +#warning TODO: tuning default as backward compatible internal static class OptionsDefaults { public static readonly int CancellationSupportThreshold = 128 * 1024 * 1024; // About 0.1 sec in desktop, more for IoT @@ -14,6 +15,10 @@ internal static class OptionsDefaults public static readonly int MaxBinaryLengthInBytes = 256 * 1024 * 1024; public static readonly int MaxByteBufferLength = 2 * 1024 * 1024; public static readonly int MaxCharBufferLength = 2 * 1024 * 1024; + public static readonly int MaxArrayLength = 1024 * 1024; + public static readonly int MaxMapCount = 1024 * 1024; + public static readonly int MaxPropertyKeyLength = 256; + public static readonly int MaxDepth = 100; public static readonly ArrayPool ByteBufferPool = ArrayPool.Shared; public static readonly ArrayPool CharBufferPool = ArrayPool.Shared; public static readonly bool ClearsBuffer = true; diff --git a/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs b/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs index 975db06c4..ab347a216 100644 --- a/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs +++ b/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs @@ -10,7 +10,7 @@ namespace MsgPack.Internal { #warning TODO: Pubternal or CodeGen - internal sealed class StreamBufferWriter : IBufferWriter, IAsyncDisposable + public sealed class StreamBufferWriter : IBufferWriter, IAsyncDisposable { private readonly Stream _underlying; private readonly bool _ownsStream; diff --git a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs index c67c81e08..adfd64d77 100644 --- a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs +++ b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs @@ -10,7 +10,7 @@ namespace MsgPack.Internal { #warning TODO: PubTernal or CodeGen - internal sealed class StreamReadOnlyMemoryProvider + public sealed class StreamReadOnlyMemoryProvider { private readonly Stream _stream; private readonly byte[] _buffer; diff --git a/src/MsgPack.Abstraction/Internal/Throw.cs b/src/MsgPack.Abstraction/Internal/Throw.cs index 91f780428..929b3670d 100644 --- a/src/MsgPack.Abstraction/Internal/Throw.cs +++ b/src/MsgPack.Abstraction/Internal/Throw.cs @@ -9,6 +9,12 @@ namespace MsgPack.Internal { internal static class Throw { + public static void ArgumentNull(string paramName) + => throw new ArgumentNullException(paramName); + + public static void ArgumentOutOfRange(string paramName, string message) + => throw new ArgumentOutOfRangeException(paramName, message); + public static void ExtensionsIsNotSupported() => throw new NotSupportedException($"Extension type is not supported in this encoder."); @@ -51,6 +57,19 @@ public static void InsufficientInput(long position, Type targetType, int request } } + public static void InsufficientInputForNull(long position, int requestHint) + { + if (requestHint < 0) + { + throw new InsufficientInputException($"It is required more bytes in input ReadOnlySequence to decode null at {position:#,0}."); + } + else + { + throw new InsufficientInputException($"It is required more {requestHint:#,0} bytes in input ReadOnlySequence to decode null at {position:#,0}."); + } + } + + public static void InsufficientInputForDecodeArrayOrMapHeader(long position, int requestHint) { if (requestHint < 0) diff --git a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj index efa4c70c2..19123fb42 100644 --- a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj +++ b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj @@ -22,6 +22,22 @@ TextTemplatingFileGenerator Decoder`1.Primitives.cs + + AsyncSerializationOperationContext`1.cs + TextTemplatingFileGenerator + + + AsyncDeserializationOperationContext`1.cs + TextTemplatingFileGenerator + + + DeserializationOperationContext`1.cs + TextTemplatingFileGenerator + + + SerializationOperationContext`1.cs + TextTemplatingFileGenerator + @@ -44,6 +60,26 @@ True Encoder`1.Primitives.tt + + True + True + AsyncDeserializationOperationContext`1.tt + + + True + True + AsyncSerializationOperationContext`1.tt + + + DeserializationOperationContext`1.tt + True + True + + + SerializationOperationContext`1.tt + True + True + diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs index 7469d883d..7f7f81ccf 100644 --- a/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs @@ -16,7 +16,7 @@ public sealed class DeserializationOptions public int MaxPropertyKeyLength { get; } public int MaxDepth { get; } public Encoding? StringEncoding { get; } - public ArrayPool ArrayPool { get; } + public ArrayPool ByteBufferPool { get; } internal DeserializationOptions( int maxArrayLength, @@ -32,7 +32,7 @@ ArrayPool arrayPool this.MaxPropertyKeyLength = maxPropertyKeyLength; this.MaxDepth = maxDepth; this.StringEncoding = stringEncoding; - this.ArrayPool = arrayPool; + this.ByteBufferPool = arrayPool; } } } diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs index 54dab8ca0..2bba8d676 100644 --- a/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs @@ -8,15 +8,9 @@ namespace MsgPack.Serialization { -#warning TODO: tuning default as backward compatible public sealed class DeserializationOptionsBuilder { - internal const int DefaultMaxArrayLength = 1024 * 1024; - internal const int DefaultMaxMapCount = 1024 * 1024; - internal const int DefaultMaxPropertyKeyLength = 256; - internal const int DefaultMaxDepth = 100; - - private int _maxArrayLength = DefaultMaxArrayLength; + private int _maxArrayLength = OptionsDefaults.MaxArrayLength; public int MaxArrayLength { @@ -24,7 +18,7 @@ public int MaxArrayLength set => this._maxArrayLength = Ensure.IsNotLessThan(value, 0); } - private int _maxMapCount = DefaultMaxMapCount; + private int _maxMapCount = OptionsDefaults.MaxMapCount; public int MaxMapCount { @@ -32,7 +26,7 @@ public int MaxMapCount set => this._maxMapCount = Ensure.IsNotLessThan(value, 0); } - private int _maxPropertyKeyLength = DefaultMaxPropertyKeyLength; + private int _maxPropertyKeyLength = OptionsDefaults.MaxPropertyKeyLength; public int MaxPropertyKeyLength { @@ -40,7 +34,7 @@ public int MaxPropertyKeyLength set => this._maxPropertyKeyLength = Ensure.IsNotLessThan(value, 1); } - private int _maxDepth = DefaultMaxDepth; + private int _maxDepth = OptionsDefaults.MaxDepth; public int MaxDepth { diff --git a/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.cs new file mode 100644 index 000000000..58b091d48 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.cs @@ -0,0 +1,69 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Buffers; +using System.Text; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + public sealed class AsyncDeserializationOperationContext + { + public Decoder Decoder { get; } + public DeserializationOptions Options { get; } + public Encoding? StringEncoding => this.Options.StringEncoding; + public ArrayPool ByteBufferPool => this.Options.ByteBufferPool; + public int CurrentDepth { get; private set; } + public CancellationToken CancellationToken { get; } + + public AsyncDeserializationOperationContext(Decoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) + { + this.Decoder = Ensure.NotNull(decoder); + this.Options = options ?? DeserializationOptions.Default; + this.CurrentDepth = 0; + this.CancellationToken = cancellationToken; + } + + public DeserializationOperationContext AsDeserializationOperationContext() + => new DeserializationOperationContext(this.Decoder, this.Options, this.CancellationToken); + + public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this.CurrentDepth); + + public int IncrementDepth() + { + if (this.CurrentDepth == this.Options.MaxDepth) + { + Throw.DepthExeeded(this.CurrentDepth, this.Options.MaxDepth); + } + + return this.CurrentDepth++; + } + + public int DecrementDepth() + { + if (this.CurrentDepth == 0) + { + Throw.DepthUnderflow(); + } + + return this.CurrentDepth--; + } + + public void ValidatePropertyKeyLength(long position, int length) + { + if(length > this.Options.MaxPropertyKeyLength) + { + Throw.TooLargePropertyKey(position, length, this.Options.MaxPropertyKeyLength); + } + } + } +} diff --git a/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.tt new file mode 100644 index 000000000..5861989a8 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.tt @@ -0,0 +1,6 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ include file="./OperationContext.ttinclude" #> +<#@ output extension=".cs" #> +<# +WriteContext(isSerialization: false, isAsync: true); +#> diff --git a/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.cs new file mode 100644 index 000000000..2c358a124 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.cs @@ -0,0 +1,59 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Text; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + public sealed class AsyncSerializationOperationContext + { + public Encoder Encoder { get; } + public SerializationOptions Options { get; } + public Encoding? StringEncoding => this.Options.StringEncoding; + public int CurrentDepth { get; private set; } + public CancellationToken CancellationToken { get; } + + public AsyncSerializationOperationContext(Encoder encoder, SerializationOptions? options, CancellationToken cancellationToken) + { + this.Encoder = Ensure.NotNull(encoder); + this.Options = options ?? SerializationOptions.Default; + this.CurrentDepth = 0; + this.CancellationToken = cancellationToken; + } + + public SerializationOperationContext AsSerializationOperationContext() + => new SerializationOperationContext(this.Encoder, this.Options, this.CancellationToken); + + public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this.CurrentDepth); + + public int IncrementDepth() + { + if (this.CurrentDepth == this.Options.MaxDepth) + { + Throw.DepthExeeded(this.CurrentDepth, this.Options.MaxDepth); + } + + return this.CurrentDepth++; + } + + public int DecrementDepth() + { + if (this.CurrentDepth == 0) + { + Throw.DepthUnderflow(); + } + + return this.CurrentDepth--; + } + } +} diff --git a/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.tt new file mode 100644 index 000000000..5d6330403 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.tt @@ -0,0 +1,6 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ include file="./OperationContext.ttinclude" #> +<#@ output extension=".cs" #> +<# +WriteContext(isSerialization: true, isAsync: true); +#> diff --git a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs index bac67deb3..fe2014917 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs @@ -2,6 +2,13 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; using System.Buffers; using System.Text; using System.Threading; @@ -13,10 +20,10 @@ public struct DeserializationOperationContext { public Decoder Decoder { get; } public DeserializationOptions Options { get; } + public Encoding? StringEncoding => this.Options.StringEncoding; + public ArrayPool ByteBufferPool => this.Options.ByteBufferPool; public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } - public ArrayPool ArrayPool => this.Options.ArrayPool; - public Encoding? StringEncoding => this.Options.StringEncoding; public DeserializationOperationContext(Decoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) { @@ -26,13 +33,7 @@ public DeserializationOperationContext(Decoder decoder, Deserial this.CancellationToken = cancellationToken; } - public CollectionContext CollectionContext => - new CollectionContext( - this.Options.MaxArrayLength, - this.Options.MaxMapCount, - this.Options.MaxDepth, - this.CurrentDepth - ); + public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this.CurrentDepth); public int IncrementDepth() { diff --git a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.tt new file mode 100644 index 000000000..045ece58c --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.tt @@ -0,0 +1,6 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ include file="./OperationContext.ttinclude" #> +<#@ output extension=".cs" #> +<# +WriteContext(isSerialization: false, isAsync: false); +#> diff --git a/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs b/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs index 00976498a..762b962da 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs +++ b/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs @@ -10,11 +10,11 @@ namespace MsgPack.Serialization.Internal { public interface IObjectSerializer { - void Serialize(in SerializationOperationContext context, T obj, IBufferWriter sink); - ValueTask SerializeAsync(SerializationOperationContext context, T obj, Stream streamSink); - T Deserialize(in DeserializationOperationContext context, in SequenceReader source); - ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource); - void DeserializeTo(in DeserializationOperationContext context, in SequenceReader source, in T obj); - ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, T obj); + void Serialize(ref SerializationOperationContext context, T obj, IBufferWriter sink); + ValueTask SerializeAsync(AsyncSerializationOperationContext context, T obj, Stream streamSink); + T Deserialize(ref DeserializationOperationContext context, ref SequenceReader source); + ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource); + bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in T obj); + ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, T obj); } } diff --git a/src/MsgPack.Abstraction/Serialization/Internal/OperationContext.ttinclude b/src/MsgPack.Abstraction/Serialization/Internal/OperationContext.ttinclude new file mode 100644 index 000000000..5cdba01ee --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/OperationContext.ttinclude @@ -0,0 +1,111 @@ +<#+ +void WriteContext(bool isSerialization, bool isAsync) +{ + var typeClass = isAsync ? "sealed class" : "struct"; + var typePrefix = isSerialization ? "Serialization" : "Deserialization"; + var typeName = $"{(isAsync ? "Async" : String.Empty)}{typePrefix}OperationContext"; + var optionTypeName = $"{typePrefix}Options"; + var primitive = isSerialization ? "Encoder" : "Decoder"; +#> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +<#+ +if (!isSerialization) +{ +#> +using System.Buffers; +<#+ +} +#> +using System.Text; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + public <#= typeClass #> <#= typeName #> + { + public <#= primitive #> <#= primitive #> { get; } + public <#= optionTypeName #> Options { get; } + public Encoding? StringEncoding => this.Options.StringEncoding; +<#+ + if (!isSerialization) + { +#> + public ArrayPool ByteBufferPool => this.Options.ByteBufferPool; +<#+ + } +#> + public int CurrentDepth { get; private set; } + public CancellationToken CancellationToken { get; } + + public <#= typeName #>(<#= primitive #> <#= primitive.ToLowerInvariant() #>, <#= optionTypeName #>? options, CancellationToken cancellationToken) + { + this.<#= primitive #> = Ensure.NotNull(<#= primitive.ToLowerInvariant() #>); + this.Options = options ?? <#= optionTypeName #>.Default; + this.CurrentDepth = 0; + this.CancellationToken = cancellationToken; + } + +<#+ + if (isAsync) + { + var syncTypeName = typeName.Substring("Async".Length); +#> + public <#= syncTypeName #> As<#= syncTypeName #>() + => new <#= syncTypeName #>(this.<#= primitive #>, this.Options, this.CancellationToken); + +<#+ + } +#> + public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this.CurrentDepth); + + public int IncrementDepth() + { + if (this.CurrentDepth == this.Options.MaxDepth) + { + Throw.DepthExeeded(this.CurrentDepth, this.Options.MaxDepth); + } + + return this.CurrentDepth++; + } + + public int DecrementDepth() + { + if (this.CurrentDepth == 0) + { + Throw.DepthUnderflow(); + } + + return this.CurrentDepth--; + } +<#+ + if (!isSerialization) + { + var qualifier = isAsync ? String.Empty : "readonly "; +#> + + public <#= qualifier #>void ValidatePropertyKeyLength(long position, int length) + { + if(length > this.Options.MaxPropertyKeyLength) + { + Throw.TooLargePropertyKey(position, length, this.Options.MaxPropertyKeyLength); + } + } +<#+ + } +#> + } +} +<#+ +} +#> diff --git a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs index ef6167fc4..b1f829e15 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs @@ -2,6 +2,12 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + using System; using System.Text; using System.Threading; @@ -13,39 +19,38 @@ public struct SerializationOperationContext { public Encoder Encoder { get; } public SerializationOptions Options { get; } - private int _currentDepth; - public int CurrentDepth => this._currentDepth; - public CancellationToken CancellationToken { get; } public Encoding? StringEncoding => this.Options.StringEncoding; + public int CurrentDepth { get; private set; } + public CancellationToken CancellationToken { get; } public SerializationOperationContext(Encoder encoder, SerializationOptions? options, CancellationToken cancellationToken) { this.Encoder = Ensure.NotNull(encoder); this.Options = options ?? SerializationOptions.Default; - this._currentDepth = 0; + this.CurrentDepth = 0; this.CancellationToken = cancellationToken; } - public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this._currentDepth); + public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this.CurrentDepth); public int IncrementDepth() { - if (this._currentDepth == this.Options.MaxDepth) + if (this.CurrentDepth == this.Options.MaxDepth) { - Throw.DepthExeeded(this._currentDepth, this.Options.MaxDepth); + Throw.DepthExeeded(this.CurrentDepth, this.Options.MaxDepth); } - return this._currentDepth++; + return this.CurrentDepth++; } public int DecrementDepth() { - if (this._currentDepth == 0) + if (this.CurrentDepth == 0) { Throw.DepthUnderflow(); } - return this._currentDepth--; + return this.CurrentDepth--; } } } diff --git a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.tt new file mode 100644 index 000000000..f04ef1652 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.tt @@ -0,0 +1,6 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ include file="./OperationContext.ttinclude" #> +<#@ output extension=".cs" #> +<# +WriteContext(isSerialization: true, isAsync: false); +#> diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs b/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs index 8016eeece..90ef1bd8a 100644 --- a/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs @@ -8,7 +8,7 @@ namespace MsgPack.Serialization { public sealed class SerializationOptionsBuilder { - private int _maxDepth; + private int _maxDepth = OptionsDefaults.MaxDepth; public int MaxDepth { diff --git a/src/MsgPack.Abstraction/Serialization/Serializer`2.cs b/src/MsgPack.Abstraction/Serialization/Serializer`2.cs index 0a5628fab..07abafe57 100644 --- a/src/MsgPack.Abstraction/Serialization/Serializer`2.cs +++ b/src/MsgPack.Abstraction/Serialization/Serializer`2.cs @@ -38,74 +38,92 @@ DeserializationOptions deserializationOptions private void InitializeSerializationOperationContext(CancellationToken cancellationToken, out SerializationOperationContext context) => context = new SerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); + private void InitializeAsyncSerializationOperationContext(CancellationToken cancellationToken, out AsyncSerializationOperationContext context) + => context = new AsyncSerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); + private void InitializeDeserializationOperationContext(CancellationToken cancellationToken, out DeserializationOperationContext context) => context = new DeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); + private void InitializeAsyncDeserializationOperationContext(CancellationToken cancellationToken, out AsyncDeserializationOperationContext context) + => context = new AsyncDeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); + public void Serialize(T obj, IBufferWriter sink, CancellationToken cancellationToken = default) { this.InitializeSerializationOperationContext(cancellationToken, out var context); - this._underlying.Serialize(context, obj, sink); + this._underlying.Serialize(ref context, obj, sink); } public ReadOnlyMemory Serialize(T obj, CancellationToken cancellationToken = default) { this.InitializeSerializationOperationContext(cancellationToken, out var context); var writer = new ArrayBufferWriter(); - this._underlying.Serialize(context, obj, writer); + this._underlying.Serialize(ref context, obj, writer); return writer.WrittenMemory; } public ValueTask SerializeAsync(T obj, Stream streamSink, CancellationToken cancellationToken = default) { - this.InitializeSerializationOperationContext(cancellationToken, out var context); + this.InitializeAsyncSerializationOperationContext(cancellationToken, out var context); return this._underlying.SerializeAsync(context, obj, streamSink); } - public T Deserialize(in SequenceReader reader, CancellationToken cancellationToken = default) + public T Deserialize(ref SequenceReader reader, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); - return this._underlying.Deserialize(context, reader); + return this._underlying.Deserialize(ref context, ref reader); } - public T Deserialize(in ReadOnlySequence source, CancellationToken cancellationToken = default) + public T Deserialize(ref ReadOnlySequence source, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); - return this._underlying.Deserialize(context, new SequenceReader(source)); + var reader = new SequenceReader(source); + var result = this._underlying.Deserialize(ref context, ref reader); + source = source.Slice(source.Start, reader.Position); + return result; } - public T Deserialize(ReadOnlyMemory memorySource, CancellationToken cancellationToken = default) + public T Deserialize(ref ReadOnlyMemory memorySource, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); - return this._underlying.Deserialize(context, new SequenceReader(new ReadOnlySequence(memorySource))); + var reader = new SequenceReader(new ReadOnlySequence(memorySource)); + var result = this._underlying.Deserialize(ref context, ref reader); + memorySource = memorySource.Slice((int)reader.Consumed); + return result; } public ValueTask DeserializeAsync(Stream streamSource, CancellationToken cancellationToken = default) { - this.InitializeDeserializationOperationContext(cancellationToken, out var context); + this.InitializeAsyncDeserializationOperationContext(cancellationToken, out var context); return this._underlying.DeserializeAsync(context, streamSource); } - public void DeserializeTo(in SequenceReader reader, T obj, CancellationToken cancellationToken = default) + public bool DeserializeTo(ref SequenceReader reader, T obj, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); - this._underlying.DeserializeTo(context, reader, obj); + return this._underlying.DeserializeTo(ref context, ref reader, obj); } - public void DeserializeTo(in ReadOnlySequence source, T obj, CancellationToken cancellationToken = default) + public bool DeserializeTo(ref ReadOnlySequence source, T obj, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); - this._underlying.DeserializeTo(context, new SequenceReader(source), obj); + var reader = new SequenceReader(source); + var result = this._underlying.DeserializeTo(ref context, ref reader, obj); + source = source.Slice(source.Start, reader.Position); + return result; } - public void DeserializeTo(ReadOnlyMemory memorySource, T obj, CancellationToken cancellationToken = default) + public bool DeserializeTo(ref ReadOnlyMemory memorySource, T obj, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); - this._underlying.DeserializeTo(context, new SequenceReader(new ReadOnlySequence(memorySource)), obj); + var reader = new SequenceReader(new ReadOnlySequence(memorySource)); + var result = this._underlying.DeserializeTo(ref context, ref reader, obj); + memorySource = memorySource.Slice((int)reader.Consumed); + return result; } - public ValueTask DeserializeToAsync(Stream streamSource, T obj, CancellationToken cancellationToken = default) + public ValueTask DeserializeToAsync(Stream streamSource, T obj, CancellationToken cancellationToken = default) { - this.InitializeDeserializationOperationContext(cancellationToken, out var context); + this.InitializeAsyncDeserializationOperationContext(cancellationToken, out var context); return this._underlying.DeserializeToAsync(context, streamSource, obj); } } diff --git a/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs b/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs index b8183564b..a41c8c45e 100644 --- a/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs +++ b/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs @@ -14,9 +14,18 @@ namespace MsgPack.Internal /// internal sealed class CurrentMessagePackEncoder : MessagePackEncoder { + private static readonly CurrentMessagePackEncoder DefaultInstance = new CurrentMessagePackEncoder(MessagePackEncoderOptions.Default); + public CurrentMessagePackEncoder(MessagePackEncoderOptions options) : base(options) { } + internal static byte[] InternalEncodeString(string value) + { + var arrayBuffer = new ArrayBufferWriter(); + DefaultInstance.EncodeString(value, arrayBuffer); + return arrayBuffer.WrittenMemory.ToArray(); + } + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] protected sealed override int EncodeStringHeader(uint length, Span buffer) { @@ -27,20 +36,20 @@ protected sealed override int EncodeStringHeader(uint length, Span buffer) } else if (length <= Byte.MaxValue) { - buffer[1] = unchecked((byte)length); buffer[0] = MessagePackCode.Str8; + buffer[1] = unchecked((byte)length); return 2; } else if (length <= UInt16.MaxValue) { buffer[0] = MessagePackCode.Str16; - BinaryPrimitives.WriteUInt16BigEndian(buffer, unchecked((ushort)length)); + BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(1), unchecked((ushort)length)); return sizeof(ushort) + 1; } else { buffer[0] = MessagePackCode.Str32; - BinaryPrimitives.WriteUInt32BigEndian(buffer, length); + BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(1), length); return sizeof(uint) + 1; } } @@ -50,20 +59,20 @@ protected sealed override int EncodeBinaryHeader(uint length, Span buffer) { if (length <= Byte.MaxValue) { - buffer[1] = unchecked((byte)length); buffer[0] = MessagePackCode.Bin8; + buffer[1] = unchecked((byte)length); return 2; } else if (length <= UInt16.MaxValue) { buffer[0] = MessagePackCode.Bin16; - BinaryPrimitives.WriteUInt16BigEndian(buffer, unchecked((ushort)length)); + BinaryPrimitives.WriteUInt16BigEndian(buffer.Slice(1), unchecked((ushort)length)); return sizeof(ushort) + 1; } else { buffer[0] = MessagePackCode.Bin32; - BinaryPrimitives.WriteUInt32BigEndian(buffer, length); + BinaryPrimitives.WriteUInt32BigEndian(buffer.Slice(1), length); return sizeof(uint) + 1; } } diff --git a/src/MsgPack.Core/Internal/LegacyMessagePackEncoder.cs b/src/MsgPack.Core/Internal/LegacyMessagePackEncoder.cs index f0d39367e..eb5f16bc0 100644 --- a/src/MsgPack.Core/Internal/LegacyMessagePackEncoder.cs +++ b/src/MsgPack.Core/Internal/LegacyMessagePackEncoder.cs @@ -3,6 +3,7 @@ // See the LICENSE in the project root for more information. using System; +using System.Buffers; using System.Buffers.Binary; using System.Runtime.CompilerServices; @@ -13,9 +14,18 @@ namespace MsgPack.Internal /// internal sealed class LegacyMessagePackEncoder : MessagePackEncoder { + private static readonly LegacyMessagePackEncoder DefaultInstance = new LegacyMessagePackEncoder(MessagePackEncoderOptions.Default); + public LegacyMessagePackEncoder(MessagePackEncoderOptions options) : base(options) { } + internal static byte[] InternalEncodeString(string value) + { + var arrayBuffer = new ArrayBufferWriter(); + DefaultInstance.EncodeString(value, arrayBuffer); + return arrayBuffer.WrittenMemory.ToArray(); + } + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] protected sealed override int EncodeStringHeader(uint length, Span buffer) { diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs index 5f8d6ca3f..e2f6bf09a 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs @@ -9,82 +9,85 @@ namespace MsgPack.Internal { public partial class MessagePackDecoder { - public sealed override CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out long itemsCount, out int requestHint) + public sealed override CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out long itemsCount, out int requestHint) { - var consumed = 0L; - var result = this.PrivateDecodeArrayOrMapHeader(source, ref consumed, out var header, out itemsCount, out requestHint); + var startOffset = source.Consumed; + var result = this.PrivateDecodeArrayOrMapHeader(ref source, out var header, out itemsCount, out requestHint); if (itemsCount > Int32.MaxValue) { - MessagePackThrow.TooLargeArrayOrMapLength(header, source.Consumed - consumed, itemsCount); + MessagePackThrow.TooLargeArrayOrMapLength(header, startOffset, itemsCount); } return result; } - private CollectionType PrivateDecodeArrayOrMapHeader(in SequenceReader source, ref long consumed, out byte header, out long itemsCount, out int requestHint) + private CollectionType PrivateDecodeArrayOrMapHeader(ref SequenceReader source, out byte header, out long itemsCount, out int requestHint) { - var result = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out header, out itemsCount, out requestHint); + var startOffset = source.Consumed; + var result = this.DecodeArrayOrMapHeaderCore(ref source, out header, out itemsCount, out requestHint); if (result == CollectionType.None) { - MessagePackThrow.TypeIsNotArrayNorMap(header, source.Consumed - consumed); + MessagePackThrow.TypeIsNotArrayNorMap(header, startOffset); } return result; } - private CollectionType DecodeArrayOrMapHeaderCore(in SequenceReader source, ref long consumed, out byte header, out long itemsCount, out int requestHint) + private CollectionType DecodeArrayOrMapHeaderCore(ref SequenceReader source, out byte header, out long itemsCount, out int requestHint) { - if (!this.TryPeek(source, out header)) + if (!source.TryRead(out header)) { requestHint = 1; itemsCount = 0; return default; } + requestHint = 0; + + if (header == MessagePackCode.NilValue) + { + itemsCount = 0; + return CollectionType.Null; + } + if ((header & MessagePackCode.MinimumFixedArray) == MessagePackCode.MinimumFixedArray) { itemsCount = (uint)header - MessagePackCode.MinimumFixedArray; - requestHint = 0; - source.Advance(1); - consumed++; return CollectionType.Array; } - if ((header & MessagePackCode.MaximumFixedMap) == MessagePackCode.MaximumFixedMap) + if ((header & MessagePackCode.MinimumFixedMap) == MessagePackCode.MinimumFixedMap) { - itemsCount = (uint)header - MessagePackCode.MaximumFixedMap; - requestHint = 0; - source.Advance(1); - consumed++; + itemsCount = (uint)header - MessagePackCode.MinimumFixedMap; return CollectionType.Map; } - int length; + int lengthOfLength; CollectionType type; switch (header) { case MessagePackCode.Array16: { - length = 3; + lengthOfLength = 2; type = CollectionType.Array; break; } case MessagePackCode.Array32: { - length = 5; + lengthOfLength = 4; type = CollectionType.Array; break; } case MessagePackCode.Map16: { - length = 3; + lengthOfLength = 2; type = CollectionType.Map; break; } case MessagePackCode.Map32: { - length = 5; + lengthOfLength = 4; type = CollectionType.Map; break; } @@ -96,73 +99,68 @@ private CollectionType DecodeArrayOrMapHeaderCore(in SequenceReader source } } - var lengthOfLength = length - 1; - switch (lengthOfLength) { case 2: { - itemsCount = ReadValue(source, offset: 1, out requestHint); + itemsCount = ReadValue(ref source, offset: 1, out requestHint); break; } default: // 4 { - itemsCount = ReadValue(source, offset: 1, out requestHint); + itemsCount = ReadValue(ref source, offset: 1, out requestHint); break; } } if (requestHint != 0) { + source.Rewind(1); return CollectionType.None; } - source.Advance(length); - consumed += length; return type; } - public sealed override long DecodeArrayHeader(in SequenceReader source, out int requestHint) + public sealed override long DecodeArrayHeader(ref SequenceReader source, out int requestHint) { - var consumed = 0L; - var type = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out var header, out var itemsCount, out requestHint); + var startOffset = source.Consumed; + var type = this.DecodeArrayOrMapHeaderCore(ref source, out var header, out var itemsCount, out requestHint); if (requestHint != 0) { - source.Rewind(consumed); return 0; } if (itemsCount > Int32.MaxValue) { - MessagePackThrow.TooLargeArrayOrMapLength(header, source.Consumed - consumed, itemsCount); + MessagePackThrow.TooLargeArrayOrMapLength(header, startOffset, itemsCount); } - if (!type.IsArray) + if (type.IsMap) { - MessagePackThrow.TypeIsNotArray(header, source.Consumed - consumed); + MessagePackThrow.TypeIsNotArray(header, startOffset); } return (int)itemsCount; } - public sealed override long DecodeMapHeader(in SequenceReader source, out int requestHint) + public sealed override long DecodeMapHeader(ref SequenceReader source, out int requestHint) { - var consumed = 0L; - var type = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out var header, out var itemsCount, out requestHint); + var startOffset = source.Consumed; + var type = this.DecodeArrayOrMapHeaderCore(ref source, out var header, out var itemsCount, out requestHint); if (requestHint != 0) { - source.Rewind(consumed); return 0; } if (itemsCount > Int32.MaxValue) { - MessagePackThrow.TooLargeArrayOrMapLength(header, source.Consumed - consumed, itemsCount); + MessagePackThrow.TooLargeArrayOrMapLength(header, startOffset, itemsCount); } - if (!type.IsMap) + if (type.IsArray) { - MessagePackThrow.TypeIsNotMap(header, source.Consumed - consumed); + MessagePackThrow.TypeIsNotMap(header, startOffset); } return (int)itemsCount; diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs index b5950552a..71c6ac50d 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs @@ -12,9 +12,9 @@ namespace MsgPack.Internal { public partial class MessagePackDecoder { - public sealed override bool DecodeItem(in SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default) + public sealed override bool DecodeItem(ref SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default) { - var elementType = this.ReadHeader(source, out var consumed, out var valueOrLength, out var requestHint); + var elementType = this.ReadHeader(ref source, out var consumed, out var valueOrLength, out var requestHint); if (requestHint != 0) { result = DecodeItemResult.InsufficientInput(requestHint); @@ -52,7 +52,7 @@ public sealed override bool DecodeItem(in SequenceReader source, out Decod case ElementType.Array: case ElementType.Map: { - this.DecodeArrayOrMap(source, out var iterator); + this.DecodeArrayOrMap(ref source, out var iterator); result = DecodeItemResult.CollectionHeader(elementType, this.CreateIterator((uint)valueOrLength), valueOrLength); break; } @@ -78,7 +78,7 @@ public sealed override bool DecodeItem(in SequenceReader source, out Decod return false; } - result = DecodeItemResult.ScalarOrSequence(elementType, source.Sequence.Slice(source.Consumed + consumed, valueOrLength)); + result = DecodeItemResult.ScalarOrSequence(elementType, source.Sequence.Slice(source.Position).Slice(consumed, valueOrLength)); consumed += valueOrLength; break; } @@ -95,7 +95,7 @@ public sealed override bool DecodeItem(in SequenceReader source, out Decod return false; } - var extensionSlice = source.Sequence.Slice(source.Consumed + consumed - 1); + var extensionSlice = source.Sequence.Slice(source.Position).Slice(consumed - 1); var typeCode = new MessagePackExtensionType(extensionSlice.FirstSpan[0]); var body = extensionSlice.Slice(1, valueOrLength); @@ -109,9 +109,9 @@ public sealed override bool DecodeItem(in SequenceReader source, out Decod return true; } - private ElementType ReadHeader(in SequenceReader source, out long consumed, out long valueOrLength, out int requestHint) + private ElementType ReadHeader(ref SequenceReader source, out long consumed, out long valueOrLength, out int requestHint) { - if (!this.TryPeek(source, out var header)) + if (!source.TryPeek(out var header)) { requestHint = 1; valueOrLength = default; @@ -154,70 +154,70 @@ private ElementType ReadHeader(in SequenceReader source, out long consumed { case MessagePackCode.SignedInt8: { - valueOrLength = ReadSByte(source, offset: 1, out requestHint); + valueOrLength = ReadSByte(ref source, offset: 1, out requestHint); consumed += sizeof(sbyte); result = ElementType.Int32; break; } case MessagePackCode.SignedInt16: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(short); result = ElementType.Int32; break; } case MessagePackCode.SignedInt32: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(int); result = ElementType.Int32; break; } case MessagePackCode.SignedInt64: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(long); result = ElementType.Int64; break; } case MessagePackCode.UnsignedInt8: { - valueOrLength = ReadByte(source, offset: 1, out requestHint); + valueOrLength = ReadByte(ref source, offset: 1, out requestHint); consumed += sizeof(byte); result = ElementType.Int32; break; } case MessagePackCode.UnsignedInt16: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(ushort); result = ElementType.Int32; break; } case MessagePackCode.UnsignedInt32: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(uint); result = ElementType.Int64; break; } case MessagePackCode.UnsignedInt64: { - valueOrLength = unchecked((long)ReadValue(source, offset: 1, out requestHint)); + valueOrLength = unchecked((long)ReadValue(ref source, offset: 1, out requestHint)); consumed += sizeof(ulong); result = ElementType.UInt64; break; } case MessagePackCode.Real32: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(int); result = ElementType.Single; break; } case MessagePackCode.Real64: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(long); result = ElementType.Double; break; @@ -242,28 +242,28 @@ private ElementType ReadHeader(in SequenceReader source, out long consumed } case MessagePackCode.Array16: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(ushort); result = ElementType.Array; break; } case MessagePackCode.Array32: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(uint); result = ElementType.Array; break; } case MessagePackCode.Map16: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(ushort); result = ElementType.Map; break; } case MessagePackCode.Map32: { - valueOrLength = ReadValue(source, offset: 1, out requestHint); + valueOrLength = ReadValue(ref source, offset: 1, out requestHint); consumed += sizeof(uint); result = ElementType.Map; break; @@ -277,7 +277,7 @@ private ElementType ReadHeader(in SequenceReader source, out long consumed case MessagePackCode.Ext16: case MessagePackCode.Ext32: { - result = ReadExtentionItem(header, source, offset: 1, ref consumed, out valueOrLength, out requestHint); + result = ReadExtentionItem(header, ref source, offset: 1, ref consumed, out valueOrLength, out requestHint); break; } default: @@ -293,7 +293,7 @@ private ElementType ReadHeader(in SequenceReader source, out long consumed return result; } - private static ElementType ReadExtentionItem(byte header, SequenceReader source, int offset, ref long consumed, out long valueOrLength, out int requestHint) + private static ElementType ReadExtentionItem(byte header, ref SequenceReader source, int offset, ref long consumed, out long valueOrLength, out int requestHint) { consumed++; requestHint = 0; @@ -327,7 +327,7 @@ private static ElementType ReadExtentionItem(byte header, SequenceReader s } default: { - _ = ReadByte(source, offset, out requestHint); + _ = ReadByte(ref source, offset, out requestHint); if (requestHint != 0) { valueOrLength = default; @@ -341,20 +341,20 @@ private static ElementType ReadExtentionItem(byte header, SequenceReader s { case MessagePackCode.Ext8: { - valueOrLength = ReadByte(source, offset, out requestHint); + valueOrLength = ReadByte(ref source, offset, out requestHint); consumed++; break; } case MessagePackCode.Ext16: { - valueOrLength = ReadValue(source, offset, out requestHint); + valueOrLength = ReadValue(ref source, offset, out requestHint); consumed += sizeof(ushort); break; } default: { Debug.Assert(header == MessagePackCode.Ext32, $"header(0x{header:X2}) == MessagePackCode.Ext32(0x{MessagePackCode.Ext32:X2})"); - valueOrLength = ReadValue(source, offset + 1, out requestHint); + valueOrLength = ReadValue(ref source, offset + 1, out requestHint); consumed += sizeof(uint); break; } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs index de25b0528..603775c8c 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs @@ -11,9 +11,9 @@ namespace MsgPack.Internal { public partial class MessagePackDecoder { - private uint ReadExtensionHeader(in SequenceReader source, out int consumed, out int requestHint) + private uint ReadExtensionHeader(ref SequenceReader source, out int consumed, out int requestHint) { - if(!this.TryPeek(source, out var header)) + if(!source.TryPeek(out var header)) { consumed = 0; requestHint = 1; @@ -79,18 +79,18 @@ private uint ReadExtensionHeader(in SequenceReader source, out int consume { case 1: { - length = ReadByte(source, offset: 1, out requestHint); + length = ReadByte(ref source, offset: 1, out requestHint); break; } case 2: { - length = ReadValue(source, offset: 1, out requestHint); + length = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(lengthOflength == 4, $"length({lengthOflength}) == 4"); - length = ReadValue(source, offset: 1, out requestHint); + length = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -104,9 +104,9 @@ private uint ReadExtensionHeader(in SequenceReader source, out int consume return length; } - public override void DecodeExtension(in SequenceReader source, out MessagePackExtensionType typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) + public override void DecodeExtension(ref SequenceReader source, out MessagePackExtensionType typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) { - var bodyLength = this.ReadExtensionHeader(source, out var consumed, out requestHint); + var bodyLength = this.ReadExtensionHeader(ref source, out var consumed, out requestHint); if (requestHint != 0) { typeCode = default; @@ -114,7 +114,7 @@ public override void DecodeExtension(in SequenceReader source, out Message return; } - if (!this.TryPeek(source, out byte typeCodeByte)) + if (!source.TryPeek(out byte typeCodeByte)) { requestHint = 1; typeCode = default; diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.cs index 1708f0b05..ff2a60e40 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.cs @@ -17,9 +17,9 @@ partial class MessagePackDecoder { /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override SByte DecodeSByte(in SequenceReader source, out int requestHint) + public sealed override SByte DecodeSByte(ref SequenceReader source, out int requestHint) { - if (!this.TryDecodeSignedInteger(source, typeof(SByte), out var header, out var result, out requestHint)) + if (!this.TryDecodeSignedInteger(ref source, typeof(SByte), out var header, out var result, out requestHint)) { return default; } @@ -33,9 +33,9 @@ public sealed override SByte DecodeSByte(in SequenceReader source, out int /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int16 DecodeInt16(in SequenceReader source, out int requestHint) + public sealed override Int16 DecodeInt16(ref SequenceReader source, out int requestHint) { - if (!this.TryDecodeSignedInteger(source, typeof(Int16), out var header, out var result, out requestHint)) + if (!this.TryDecodeSignedInteger(ref source, typeof(Int16), out var header, out var result, out requestHint)) { return default; } @@ -49,9 +49,9 @@ public sealed override Int16 DecodeInt16(in SequenceReader source, out int /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int32 DecodeInt32(in SequenceReader source, out int requestHint) + public sealed override Int32 DecodeInt32(ref SequenceReader source, out int requestHint) { - if (!this.TryDecodeSignedInteger(source, typeof(Int32), out var header, out var result, out requestHint)) + if (!this.TryDecodeSignedInteger(ref source, typeof(Int32), out var header, out var result, out requestHint)) { return default; } @@ -65,9 +65,9 @@ public sealed override Int32 DecodeInt32(in SequenceReader source, out int /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int64 DecodeInt64(in SequenceReader source, out int requestHint) + public sealed override Int64 DecodeInt64(ref SequenceReader source, out int requestHint) { - if (!this.TryDecodeSignedInteger(source, typeof(Int64), out var header, out var result, out requestHint)) + if (!this.TryDecodeSignedInteger(ref source, typeof(Int64), out var header, out var result, out requestHint)) { return default; } @@ -76,9 +76,9 @@ public sealed override Int64 DecodeInt64(in SequenceReader source, out int /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Byte DecodeByte(in SequenceReader source, out int requestHint) + public sealed override Byte DecodeByte(ref SequenceReader source, out int requestHint) { - if (!this.TryDecodeUnsignedInteger(source, typeof(Byte), out var header, out var result, out requestHint)) + if (!this.TryDecodeUnsignedInteger(ref source, typeof(Byte), out var header, out var result, out requestHint)) { return default; } @@ -92,9 +92,9 @@ public sealed override Byte DecodeByte(in SequenceReader source, out int r /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt16 DecodeUInt16(in SequenceReader source, out int requestHint) + public sealed override UInt16 DecodeUInt16(ref SequenceReader source, out int requestHint) { - if (!this.TryDecodeUnsignedInteger(source, typeof(UInt16), out var header, out var result, out requestHint)) + if (!this.TryDecodeUnsignedInteger(ref source, typeof(UInt16), out var header, out var result, out requestHint)) { return default; } @@ -108,9 +108,9 @@ public sealed override UInt16 DecodeUInt16(in SequenceReader source, out i /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt32 DecodeUInt32(in SequenceReader source, out int requestHint) + public sealed override UInt32 DecodeUInt32(ref SequenceReader source, out int requestHint) { - if (!this.TryDecodeUnsignedInteger(source, typeof(UInt32), out var header, out var result, out requestHint)) + if (!this.TryDecodeUnsignedInteger(ref source, typeof(UInt32), out var header, out var result, out requestHint)) { return default; } @@ -124,19 +124,19 @@ public sealed override UInt32 DecodeUInt32(in SequenceReader source, out i /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt64 DecodeUInt64(in SequenceReader source, out int requestHint) + public sealed override UInt64 DecodeUInt64(ref SequenceReader source, out int requestHint) { - if (!this.TryDecodeUnsignedInteger(source, typeof(UInt64), out var header, out var result, out requestHint)) + if (!this.TryDecodeUnsignedInteger(ref source, typeof(UInt64), out var header, out var result, out requestHint)) { return default; } return unchecked((UInt64)result); } - - private bool TryDecodeSignedInteger(in SequenceReader source, Type type, out byte header, out Int64 value, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private bool TryDecodeSignedInteger(ref SequenceReader source, Type type, out byte header, out Int64 value, out int requestHint) { - if (!this.TryPeek(source, out header)) + if (!source.TryPeek(out header)) { requestHint = 1; value = default; @@ -148,16 +148,24 @@ private bool TryDecodeSignedInteger(in SequenceReader source, Type type, o if (header < 128) { value = header; + source.Advance(1); return true; } if (header >= 0xE0) { value = unchecked((sbyte)header); + source.Advance(1); return true; } - ParseNumberHeader(header, source, type, out var length, out var kind); + return this.TryDecodeSignedIntegerSlow(ref source, type, header, out value, out requestHint); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private bool TryDecodeSignedIntegerSlow(ref SequenceReader source, Type type, byte header, out Int64 value, out int requestHint) + { + ParseNumberHeader(header, ref source, type, out var length, out var kind); if ((kind & NumberKind.RealBitMask) != 0 && !this.Options.CanTreatRealAsInteger) { @@ -172,23 +180,23 @@ private bool TryDecodeSignedInteger(in SequenceReader source, Type type, o { case 1: { - value = ReadSByte(source, offset: 1, out requestHint); + value = ReadSByte(ref source, offset: 1, out requestHint); break; } case 2: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } case 4: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -207,23 +215,23 @@ private bool TryDecodeSignedInteger(in SequenceReader source, Type type, o { case 1: { - unsigned = ReadByte(source, offset: 1, out requestHint); + unsigned = ReadByte(ref source, offset: 1, out requestHint); break; } case 2: { - unsigned = ReadValue(source, offset: 1, out requestHint); + unsigned = ReadValue(ref source, offset: 1, out requestHint); break; } case 4: { - unsigned = ReadValue(source, offset: 1, out requestHint); + unsigned = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - unsigned = ReadValue(source, offset: 1, out requestHint); + unsigned = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -244,7 +252,7 @@ private bool TryDecodeSignedInteger(in SequenceReader source, Type type, o } case NumberKind.Single: { - var real = ReadValue(source, offset: 1, out requestHint); + var real = ReadValue(ref source, offset: 1, out requestHint); if (requestHint != 0) { value = default; @@ -267,7 +275,7 @@ private bool TryDecodeSignedInteger(in SequenceReader source, Type type, o default: { Debug.Assert(kind == NumberKind.Double, $"kind({kind}) == NumberType.Double"); - var real = ReadValue(source, offset: 1, out requestHint); + var real = ReadValue(ref source, offset: 1, out requestHint); if (requestHint != 0) { value = default; @@ -292,10 +300,10 @@ private bool TryDecodeSignedInteger(in SequenceReader source, Type type, o return true; } - - private bool TryDecodeUnsignedInteger(in SequenceReader source, Type type, out byte header, out UInt64 value, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private bool TryDecodeUnsignedInteger(ref SequenceReader source, Type type, out byte header, out UInt64 value, out int requestHint) { - if (!this.TryPeek(source, out header)) + if (!source.TryPeek(out header)) { requestHint = 1; value = default; @@ -307,10 +315,17 @@ private bool TryDecodeUnsignedInteger(in SequenceReader source, Type type, if (header < 128) { value = header; + source.Advance(1); return true; } - ParseNumberHeader(header, source, type, out var length, out var kind); + return this.TryDecodeUnsignedIntegerSlow(ref source, type, header, out value, out requestHint); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private bool TryDecodeUnsignedIntegerSlow(ref SequenceReader source, Type type, byte header, out UInt64 value, out int requestHint) + { + ParseNumberHeader(header, ref source, type, out var length, out var kind); if ((kind & NumberKind.RealBitMask) != 0 && !this.Options.CanTreatRealAsInteger) { @@ -326,23 +341,23 @@ private bool TryDecodeUnsignedInteger(in SequenceReader source, Type type, { case 1: { - signed = ReadSByte(source, offset: 1, out requestHint); + signed = ReadSByte(ref source, offset: 1, out requestHint); break; } case 2: { - signed = ReadValue(source, offset: 1, out requestHint); + signed = ReadValue(ref source, offset: 1, out requestHint); break; } case 4: { - signed = ReadValue(source, offset: 1, out requestHint); + signed = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - signed = ReadValue(source, offset: 1, out requestHint); + signed = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -367,23 +382,23 @@ private bool TryDecodeUnsignedInteger(in SequenceReader source, Type type, { case 1: { - value = ReadByte(source, offset: 1, out requestHint); + value = ReadByte(ref source, offset: 1, out requestHint); break; } case 2: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } case 4: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -397,7 +412,7 @@ private bool TryDecodeUnsignedInteger(in SequenceReader source, Type type, } case NumberKind.Single: { - var real = ReadValue(source, offset: 1, out requestHint); + var real = ReadValue(ref source, offset: 1, out requestHint); if (requestHint != 0) { value = default; @@ -420,7 +435,7 @@ private bool TryDecodeUnsignedInteger(in SequenceReader source, Type type, default: { Debug.Assert(kind == NumberKind.Double, $"kind({kind}) == NumberType.Double"); - var real = ReadValue(source, offset: 1, out requestHint); + var real = ReadValue(ref source, offset: 1, out requestHint); if (requestHint != 0) { value = default; diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.tt b/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.tt index 898d74a57..329a60b0c 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.tt +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Integers.tt @@ -57,10 +57,10 @@ foreach (var isSigned in new [] { true, false }) var methodName = $"TryDecode{(isSigned ? "Signed" : "Unsigned")}Integer"; var outType = isSigned ? "Int64": "UInt64"; #> - - private bool <#= methodName #>(in SequenceReader source, Type type, out byte header, out <#= outType #> value, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private bool <#= methodName #>(ref SequenceReader source, Type type, out byte header, out <#= outType #> value, out int requestHint) { - if (!this.TryPeek(source, out header)) + if (!source.TryPeek(out header)) { requestHint = 1; value = default; @@ -72,6 +72,7 @@ foreach (var isSigned in new [] { true, false }) if (header < 128) { value = header; + source.Advance(1); return true; } @@ -82,13 +83,20 @@ foreach (var isSigned in new [] { true, false }) if (header >= 0xE0) { value = unchecked((sbyte)header); + source.Advance(1); return true; } <# } // if (isSigned) #> - ParseNumberHeader(header, source, type, out var length, out var kind); + return this.<#= methodName #>Slow(ref source, type, header, out value, out requestHint); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private bool <#= methodName #>Slow(ref SequenceReader source, Type type, byte header, out <#= outType #> value, out int requestHint) + { + ParseNumberHeader(header, ref source, type, out var length, out var kind); if ((kind & NumberKind.RealBitMask) != 0 && !this.Options.CanTreatRealAsInteger) { @@ -175,7 +183,7 @@ foreach (var isSigned in new [] { true, false }) } case NumberKind.Single: { - var real = ReadValue(source, offset: 1, out requestHint); + var real = ReadValue(ref source, offset: 1, out requestHint); if (requestHint != 0) { value = default; @@ -198,7 +206,7 @@ foreach (var isSigned in new [] { true, false }) default: { Debug.Assert(kind == NumberKind.Double, $"kind({kind}) == NumberType.Double"); - var real = ReadValue(source, offset: 1, out requestHint); + var real = ReadValue(ref source, offset: 1, out requestHint); if (requestHint != 0) { value = default; @@ -236,9 +244,9 @@ void WriteMethod(string type, bool isSigned, bool is64Bit) #> /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override <#= type #> Decode<#= type #>(in SequenceReader source, out int requestHint) + public sealed override <#= type #> Decode<#= type #>(ref SequenceReader source, out int requestHint) { - if (!this.<#= coreMethod #>(source, typeof(<#= type #>), out var header, out var result, out requestHint)) + if (!this.<#= coreMethod #>(ref source, typeof(<#= type #>), out var header, out var result, out requestHint)) { return default; } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Iteration.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Iteration.cs index 3dbb8f0b1..cb5fc938d 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Iteration.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Iteration.cs @@ -12,7 +12,7 @@ public partial class MessagePackDecoder // This is instance method now because CLR/CoreCLR delegates are optmized for instance method invocation. // This method might be changed static if C# supports function pointer. - private bool DetectCollectionEnds(in SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) + private bool DetectCollectionEnds(ref SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) { requestHint = 0; @@ -25,10 +25,9 @@ private bool DetectCollectionEnds(in SequenceReader source, ref long nextI return true; } - public override CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator, out int requestHint) + public override CollectionType DecodeArrayOrMap(ref SequenceReader source, out CollectionItemIterator iterator, out int requestHint) { - var consumed = 0L; - var type = this.PrivateDecodeArrayOrMapHeader(source, ref consumed, out _, out var itemsCount, out requestHint); + var type = this.PrivateDecodeArrayOrMapHeader(ref source, out _, out var itemsCount, out requestHint); if (requestHint != 0) { iterator = default; @@ -42,9 +41,9 @@ public override CollectionType DecodeArrayOrMap(in SequenceReader source, private CollectionItemIterator CreateIterator(long itemsCount) => new CollectionItemIterator(this._detectCollectionEnds, itemsCount); - public override CollectionItemIterator DecodeArray(in SequenceReader source, out int requestHint) + public override CollectionItemIterator DecodeArray(ref SequenceReader source, out int requestHint) { - var itemsCount = this.DecodeArrayHeader(source, out requestHint); + var itemsCount = this.DecodeArrayHeader(ref source, out requestHint); if (requestHint != 0) { return default; @@ -53,9 +52,9 @@ public override CollectionItemIterator DecodeArray(in SequenceReader sourc return new CollectionItemIterator(this._detectCollectionEnds, itemsCount); } - public override CollectionItemIterator DecodeMap(in SequenceReader source, out int requestHint) + public override CollectionItemIterator DecodeMap(ref SequenceReader source, out int requestHint) { - var itemsCount = this.DecodeMapHeader(source, out requestHint); + var itemsCount = this.DecodeMapHeader(ref source, out requestHint); if (requestHint != 0) { return default; diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.cs index 0ca842cdc..272a67fc9 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.cs @@ -16,145 +16,145 @@ partial class MessagePackDecoder { /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Byte? DecodeNullableByte(in SequenceReader source, out int requestHint) + public sealed override Byte? DecodeNullableByte(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeByte(source, out requestHint); + return this.DecodeByte(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override SByte? DecodeNullableSByte(in SequenceReader source, out int requestHint) + public sealed override SByte? DecodeNullableSByte(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeSByte(source, out requestHint); + return this.DecodeSByte(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int16? DecodeNullableInt16(in SequenceReader source, out int requestHint) + public sealed override Int16? DecodeNullableInt16(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeInt16(source, out requestHint); + return this.DecodeInt16(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt16? DecodeNullableUInt16(in SequenceReader source, out int requestHint) + public sealed override UInt16? DecodeNullableUInt16(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeUInt16(source, out requestHint); + return this.DecodeUInt16(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int32? DecodeNullableInt32(in SequenceReader source, out int requestHint) + public sealed override Int32? DecodeNullableInt32(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeInt32(source, out requestHint); + return this.DecodeInt32(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt32? DecodeNullableUInt32(in SequenceReader source, out int requestHint) + public sealed override UInt32? DecodeNullableUInt32(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeUInt32(source, out requestHint); + return this.DecodeUInt32(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int64? DecodeNullableInt64(in SequenceReader source, out int requestHint) + public sealed override Int64? DecodeNullableInt64(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeInt64(source, out requestHint); + return this.DecodeInt64(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt64? DecodeNullableUInt64(in SequenceReader source, out int requestHint) + public sealed override UInt64? DecodeNullableUInt64(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeUInt64(source, out requestHint); + return this.DecodeUInt64(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Single? DecodeNullableSingle(in SequenceReader source, out int requestHint) + public sealed override Single? DecodeNullableSingle(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeSingle(source, out requestHint); + return this.DecodeSingle(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Double? DecodeNullableDouble(in SequenceReader source, out int requestHint) + public sealed override Double? DecodeNullableDouble(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeDouble(source, out requestHint); + return this.DecodeDouble(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Boolean? DecodeNullableBoolean(in SequenceReader source, out int requestHint) + public sealed override Boolean? DecodeNullableBoolean(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeBoolean(source, out requestHint); + return this.DecodeBoolean(ref source, out requestHint); } } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.tt b/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.tt index b73b9c7fc..5af782a13 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.tt +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Nullables.tt @@ -37,15 +37,15 @@ foreach (var type in #> /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override <#= type #>? DecodeNullable<#= type #>(in SequenceReader source, out int requestHint) + public sealed override <#= type #>? DecodeNullable<#= type #>(ref SequenceReader source, out int requestHint) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.Decode<#= type #>(source, out requestHint); + return this.Decode<#= type #>(ref source, out requestHint); } <# diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Number.ttinclude b/src/MsgPack.Core/Internal/MessagePackDecoder.Number.ttinclude index 33d1a85d5..f694f1406 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Number.ttinclude +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Number.ttinclude @@ -6,23 +6,23 @@ void WriteDecodeInteger(string variable, bool isSigned) { case 1: { - <#= variable #> = Read<#= isSigned ? "SByte" : "Byte" #>(source, offset: 1, out requestHint); + <#= variable #> = Read<#= isSigned ? "SByte" : "Byte" #>(ref source, offset: 1, out requestHint); break; } case 2: { - <#= variable #> = ReadValue<<#= isSigned ? "short" : "ushort" #>>(source, offset: 1, out requestHint); + <#= variable #> = ReadValue<<#= isSigned ? "short" : "ushort" #>>(ref source, offset: 1, out requestHint); break; } case 4: { - <#= variable #> = ReadValue<<#= isSigned ? "int" : "uint" #>>(source, offset: 1, out requestHint); + <#= variable #> = ReadValue<<#= isSigned ? "int" : "uint" #>>(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - <#= variable #> = ReadValue<<#= isSigned ? "long" : "ulong" #>>(source, offset: 1, out requestHint); + <#= variable #> = ReadValue<<#= isSigned ? "long" : "ulong" #>>(ref source, offset: 1, out requestHint); break; } } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.cs index 0d5d871e7..3023f9ff2 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.cs @@ -17,9 +17,9 @@ partial class MessagePackDecoder { /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Single DecodeSingle(in SequenceReader source, out int requestHint) + public sealed override Single DecodeSingle(ref SequenceReader source, out int requestHint) { - if (!this.TryPeek(source, out var header)) + if (!source.TryPeek(out var header)) { requestHint = 1; return default; @@ -33,7 +33,7 @@ public sealed override Single DecodeSingle(in SequenceReader source, out i return header; } - ParseNumberHeader(header, source, typeof(Single), out var length, out var kind); + ParseNumberHeader(header, ref source, typeof(Single), out var length, out var kind); Single value; switch (kind) @@ -44,23 +44,23 @@ public sealed override Single DecodeSingle(in SequenceReader source, out i { case 1: { - value = ReadSByte(source, offset: 1, out requestHint); + value = ReadSByte(ref source, offset: 1, out requestHint); break; } case 2: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } case 4: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -73,23 +73,23 @@ public sealed override Single DecodeSingle(in SequenceReader source, out i { case 1: { - value = ReadByte(source, offset: 1, out requestHint); + value = ReadByte(ref source, offset: 1, out requestHint); break; } case 2: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } case 4: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -98,13 +98,13 @@ public sealed override Single DecodeSingle(in SequenceReader source, out i } case NumberKind.Single: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: { // Double - value = (Single)ReadValue(source, offset: 1, out requestHint); + value = (Single)ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -119,9 +119,9 @@ public sealed override Single DecodeSingle(in SequenceReader source, out i /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Double DecodeDouble(in SequenceReader source, out int requestHint) + public sealed override Double DecodeDouble(ref SequenceReader source, out int requestHint) { - if (!this.TryPeek(source, out var header)) + if (!source.TryPeek(out var header)) { requestHint = 1; return default; @@ -135,7 +135,7 @@ public sealed override Double DecodeDouble(in SequenceReader source, out i return header; } - ParseNumberHeader(header, source, typeof(Double), out var length, out var kind); + ParseNumberHeader(header, ref source, typeof(Double), out var length, out var kind); Double value; switch (kind) @@ -146,23 +146,23 @@ public sealed override Double DecodeDouble(in SequenceReader source, out i { case 1: { - value = ReadSByte(source, offset: 1, out requestHint); + value = ReadSByte(ref source, offset: 1, out requestHint); break; } case 2: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } case 4: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -175,23 +175,23 @@ public sealed override Double DecodeDouble(in SequenceReader source, out i { case 1: { - value = ReadByte(source, offset: 1, out requestHint); + value = ReadByte(ref source, offset: 1, out requestHint); break; } case 2: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } case 4: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: { Debug.Assert(length == 8, $"length({length}) != 8"); - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } } @@ -200,13 +200,13 @@ public sealed override Double DecodeDouble(in SequenceReader source, out i } case NumberKind.Single: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: { // Double - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.tt b/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.tt index cd5e02e09..72d577988 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.tt +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Reals.tt @@ -24,9 +24,9 @@ foreach (var type in new [] { "Single", "Double" }) #> /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override <#= type #> Decode<#= type #>(in SequenceReader source, out int requestHint) + public sealed override <#= type #> Decode<#= type #>(ref SequenceReader source, out int requestHint) { - if (!this.TryPeek(source, out var header)) + if (!source.TryPeek(out var header)) { requestHint = 1; return default; @@ -40,7 +40,7 @@ foreach (var type in new [] { "Single", "Double" }) return header; } - ParseNumberHeader(header, source, typeof(<#= type #>), out var length, out var kind); + ParseNumberHeader(header, ref source, typeof(<#= type #>), out var length, out var kind); <#= type #> value; switch (kind) @@ -61,7 +61,7 @@ foreach (var type in new [] { "Single", "Double" }) } case NumberKind.Single: { - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); break; } default: @@ -71,13 +71,13 @@ foreach (var type in new [] { "Single", "Double" }) if (type != "Double") { #> - value = (<#= type #>)ReadValue(source, offset: 1, out requestHint); + value = (<#= type #>)ReadValue(ref source, offset: 1, out requestHint); <# } else { #> - value = ReadValue(source, offset: 1, out requestHint); + value = ReadValue(ref source, offset: 1, out requestHint); <# } #> diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.SkipDrain.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.SkipDrain.cs index 2f343f721..6337cec11 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.SkipDrain.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.SkipDrain.cs @@ -11,20 +11,23 @@ namespace MsgPack.Internal { public partial class MessagePackDecoder { - public sealed override void Skip(in SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default) - => this.Drain(source, collectionContext, itemsCount: 1, out requestHint, cancellationToken); + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void Skip(ref SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default) + => this.Drain(ref source, collectionContext, itemsCount: 1, out requestHint, cancellationToken); - public override void Drain(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default) +#warning TODO: Remove ref consumed + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override void Drain(ref SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default) { var consumed = 0L; - if (!this.SkipItems(source, collectionContext, itemsCount, ref consumed, out requestHint, cancellationToken)) + if (!this.SkipItems(ref source, collectionContext, itemsCount, ref consumed, out requestHint, cancellationToken)) { source.Rewind(consumed); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private static bool SkipLength(in SequenceReader source, long length, ref long consumed, out int requestHint) + private static bool SkipLength(ref SequenceReader source, long length, ref long consumed, out int requestHint) { if (source.Remaining < length) { @@ -38,12 +41,12 @@ private static bool SkipLength(in SequenceReader source, long length, ref return true; } - private bool SkipArray(in SequenceReader source, in CollectionContext collectionContext, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) + private bool SkipArray(ref SequenceReader source, in CollectionContext collectionContext, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) { collectionContext.IncrementDepth(); var initialConsumed = consumed; - var type = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out var header, out var arrayLength, out requestHint); + var type = this.DecodeArrayOrMapHeaderCore(ref source, out var header, out var arrayLength, out requestHint); if (requestHint != 0) { return false; @@ -51,10 +54,12 @@ private bool SkipArray(in SequenceReader source, in CollectionContext coll if (!type.IsArray) { - MessagePackThrow.TypeIsNotArray(header, source.Consumed - consumed + initialConsumed); + MessagePackThrow.TypeIsNotArray(header, initialConsumed); } - if (!this.SkipItems(source, collectionContext, arrayLength, ref consumed, out requestHint, cancellationToken)) + consumed = source.Consumed - initialConsumed; + + if (!this.SkipItems(ref source, collectionContext, arrayLength, ref consumed, out requestHint, cancellationToken)) { return false; } @@ -63,12 +68,12 @@ private bool SkipArray(in SequenceReader source, in CollectionContext coll return true; } - private bool SkipMap(in SequenceReader source, in CollectionContext collectionContext, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) + private bool SkipMap(ref SequenceReader source, in CollectionContext collectionContext, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) { collectionContext.IncrementDepth(); var initialConsumed = consumed; - var type = this.DecodeArrayOrMapHeaderCore(source, ref consumed, out var header, out var mapCount, out requestHint); + var type = this.DecodeArrayOrMapHeaderCore(ref source, out var header, out var mapCount, out requestHint); if (requestHint != 0) { return false; @@ -76,10 +81,12 @@ private bool SkipMap(in SequenceReader source, in CollectionContext collec if (!type.IsMap) { - MessagePackThrow.TypeIsNotMap(header, source.Consumed - consumed + initialConsumed); + MessagePackThrow.TypeIsNotMap(header, initialConsumed); } - if (!this.SkipItems(source, collectionContext, mapCount * 2, ref consumed, out requestHint, cancellationToken)) + consumed = source.Consumed - initialConsumed; + + if (!this.SkipItems(ref source, collectionContext, mapCount * 2, ref consumed, out requestHint, cancellationToken)) { return false; } @@ -88,11 +95,11 @@ private bool SkipMap(in SequenceReader source, in CollectionContext collec return true; } - private bool SkipItems(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) + private bool SkipItems(ref SequenceReader source, in CollectionContext collectionContext, long itemsCount, ref long consumed, out int requestHint, CancellationToken cancellationToken = default) { while (itemsCount > 0) { - if (!this.TryPeek(source, out var header)) + if (!source.TryPeek(out var header)) { requestHint = 1; return false; @@ -104,14 +111,14 @@ private bool SkipItems(in SequenceReader source, in CollectionContext coll if (header >= MessagePackCode.MinimumFixedArray && header <= MessagePackCode.MaximumFixedArray) { - if (!this.SkipArray(source, collectionContext, ref consumed, out requestHint, cancellationToken)) + if (!this.SkipArray(ref source, collectionContext, ref consumed, out requestHint, cancellationToken)) { return false; } } else if (header >= MessagePackCode.MinimumFixedMap && header <= MessagePackCode.MaximumFixedMap) { - if (!this.SkipMap(source, collectionContext, ref consumed, out requestHint, cancellationToken)) + if (!this.SkipMap(ref source, collectionContext, ref consumed, out requestHint, cancellationToken)) { return false; } @@ -161,7 +168,7 @@ private bool SkipItems(in SequenceReader source, in CollectionContext coll case MessagePackCode.Bin16: case MessagePackCode.Raw16: { - length = ReadValue(source, offset: 0, out requestHint); + length = ReadValue(ref source, offset: 0, out requestHint); if (requestHint != 0) { source.Rewind(consumed); @@ -174,7 +181,7 @@ private bool SkipItems(in SequenceReader source, in CollectionContext coll case MessagePackCode.Bin32: case MessagePackCode.Raw32: { - length = ReadValue(source, offset: 0, out requestHint); + length = ReadValue(ref source, offset: 0, out requestHint); if (requestHint != 0) { source.Rewind(consumed); @@ -223,7 +230,7 @@ private bool SkipItems(in SequenceReader source, in CollectionContext coll } case MessagePackCode.Ext16: { - length = ReadValue(source, offset: 0, out requestHint); + length = ReadValue(ref source, offset: 0, out requestHint); if (requestHint != 0) { source.Rewind(consumed); @@ -235,7 +242,7 @@ private bool SkipItems(in SequenceReader source, in CollectionContext coll } case MessagePackCode.Ext32: { - length = ReadValue(source, offset: 0, out requestHint); + length = ReadValue(ref source, offset: 0, out requestHint); if (requestHint != 0) { source.Rewind(consumed); @@ -248,7 +255,7 @@ private bool SkipItems(in SequenceReader source, in CollectionContext coll case MessagePackCode.Array16: case MessagePackCode.Array32: { - if (!this.SkipArray(source, collectionContext, ref consumed, out requestHint, cancellationToken)) + if (!this.SkipArray(ref source, collectionContext, ref consumed, out requestHint, cancellationToken)) { return false; } @@ -257,7 +264,7 @@ private bool SkipItems(in SequenceReader source, in CollectionContext coll case MessagePackCode.Map16: case MessagePackCode.Map32: { - if (!this.SkipMap(source, collectionContext, ref consumed, out requestHint, cancellationToken)) + if (!this.SkipMap(ref source, collectionContext, ref consumed, out requestHint, cancellationToken)) { return false; } @@ -266,7 +273,7 @@ private bool SkipItems(in SequenceReader source, in CollectionContext coll } } - if (length > 0 && !SkipLength(source, length, ref consumed, out requestHint)) + if (length > 0 && !SkipLength(ref source, length, ref consumed, out requestHint)) { return false; } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Strings.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Strings.cs index 04f043e73..af13ee46a 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Strings.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Strings.cs @@ -14,9 +14,11 @@ namespace MsgPack.Internal public partial class MessagePackDecoder { [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override bool GetRawString(in SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default) + public sealed override bool GetRawString(ref SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default) { - var length = this.PeekRawStringLength(source, out requestHint, cancellationToken); + var subReader = source; + + var length = this.GetRawStringLength(ref subReader, out var headerLength, out requestHint, cancellationToken); if (length < 0) { rawString = default; @@ -25,17 +27,17 @@ public override bool GetRawString(in SequenceReader source, out ReadOnlySp if (source.UnreadSpan.Length < length) { - rawString = source.UnreadSpan.Slice(0, length); + rawString = source.UnreadSpan.Slice(0, length + headerLength); source.Advance(length); requestHint = 0; return true; } - return this.GetRawStringMultiSegment(source, out rawString, out requestHint, length, cancellationToken); + return this.GetRawStringMultiSegment(ref source, out rawString, out requestHint, length + headerLength, cancellationToken); } [MethodImpl(MethodImplOptions.NoInlining)] - private bool GetRawStringMultiSegment(in SequenceReader source, out ReadOnlySpan rawString, out int requestHint, int length, CancellationToken cancellationToken) + private bool GetRawStringMultiSegment(ref SequenceReader source, out ReadOnlySpan rawString, out int requestHint, int length, CancellationToken cancellationToken) { if (source.Remaining < length) { @@ -45,6 +47,7 @@ private bool GetRawStringMultiSegment(in SequenceReader source, out ReadOn } var result = new byte[length]; + var bufferSpan = result.AsSpan(); var copyLength = Math.Min(this.Options.CancellationSupportThreshold, length); @@ -65,9 +68,10 @@ private bool GetRawStringMultiSegment(in SequenceReader source, out ReadOn return true; } - private int PeekRawStringLength(in SequenceReader source, out int requestHint, CancellationToken cancellationToken) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private int GetRawStringLength(ref SequenceReader source, out int consumed, out int requestHint, CancellationToken cancellationToken) { - var length = this.DecodeStringHeader(source, out var header, out requestHint, out _); + var length = this.DecodeStringHeader(ref source, out var header, out requestHint, out consumed); if (requestHint != 0) { return default; @@ -75,15 +79,15 @@ private int PeekRawStringLength(in SequenceReader source, out int requestH if (length > Int32.MaxValue) { - MessagePackThrow.TooLargeByteLength(header, source.Consumed, length); + MessagePackThrow.TooLargeByteLength(header, source.Consumed - consumed, length); } return (int)length; } - private long DecodeStringHeader(in SequenceReader source, out byte header, out int requestHint, out int consumed) + private long DecodeStringHeader(ref SequenceReader source, out byte header, out int requestHint, out int consumed) { - if (!this.TryPeek(source, out header)) + if (!source.TryPeek(out header)) { requestHint = 1; consumed = 0; @@ -94,6 +98,7 @@ private long DecodeStringHeader(in SequenceReader source, out byte header, { requestHint = 0; consumed = 1; + source.Advance(1); return header - MessagePackCode.MinimumFixedRaw; } @@ -102,19 +107,19 @@ private long DecodeStringHeader(in SequenceReader source, out byte header, { case MessagePackCode.Str8: { - length = ReadByte(source, offset: 1, out requestHint); + length = ReadByte(ref source, offset: 1, out requestHint); consumed = 2; break; } case MessagePackCode.Str16: { - length = ReadValue(source, offset: 1, out requestHint); + length = ReadValue(ref source, offset: 1, out requestHint); consumed = 3; break; } case MessagePackCode.Str32: { - length = ReadValue(source, offset: 1, out requestHint); + length = ReadValue(ref source, offset: 1, out requestHint); consumed = 5; break; } @@ -128,34 +133,35 @@ private long DecodeStringHeader(in SequenceReader source, out byte header, } } + source.Advance(consumed); return requestHint == 0 ? length : 0; } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override string? DecodeNullableString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) + public sealed override string? DecodeNullableString(ref SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeString(source, out requestHint, encoding, cancellationToken); + return this.DecodeString(ref source, out requestHint, encoding, cancellationToken); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override string? DecodeString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) + public sealed override string? DecodeString(ref SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) { - var length = this.DecodeStringHeader(source, out _, out requestHint, out var consumed); + var length = this.DecodeStringHeader(ref source, out _, out requestHint, out var consumed); if (requestHint != 0) { return default; } - if (source.Remaining < length + consumed) + if (source.Remaining < length) { - requestHint = (int)((length + consumed - source.Remaining) & Int32.MaxValue); + requestHint = (int)((length - source.Remaining) & Int32.MaxValue); return default; } @@ -167,34 +173,34 @@ private long DecodeStringHeader(in SequenceReader source, out byte header, if (encoding == null && length <= this.Options.CancellationSupportThreshold) { // fast-path - var value = Utf8EncodingNonBom.Instance.GetString(source.UnreadSpan.Slice(consumed, (int)length)); - source.Advance(length + consumed); + var value = Utf8EncodingNonBom.Instance.GetString(source.UnreadSpan.Slice(0, (int)length)); + source.Advance(length); requestHint = 0; return value; } - var result = (encoding ?? Utf8EncodingNonBom.Instance).GetStringMultiSegment(source.Sequence.Slice(consumed), this.Options.CharBufferPool, cancellationToken); - source.Advance(length + consumed); + var result = (encoding ?? Utf8EncodingNonBom.Instance).GetStringMultiSegment(source.Sequence.Slice(source.Position), this.Options.CharBufferPool, cancellationToken); + source.Advance(length); requestHint = 0; return result; } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override byte[]? DecodeNullableBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + public sealed override byte[]? DecodeNullableBinary(ref SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) { - if (this.TryReadNull(source)) + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeBinary(source, out requestHint, cancellationToken); + return this.DecodeBinary(ref source, out requestHint, cancellationToken); } - private int DecodeBinaryHeader(in SequenceReader source, out int requestHint, out int consumed) + private int DecodeBinaryHeader(ref SequenceReader source, out int requestHint, out int consumed) { - if (!this.TryPeek(source, out var header)) + if (!source.TryPeek(out var header)) { requestHint = 1; consumed = 0; @@ -214,21 +220,21 @@ private int DecodeBinaryHeader(in SequenceReader source, out int requestHi case MessagePackCode.Str8: case MessagePackCode.Bin8: { - length = ReadByte(source, offset: 1, out requestHint); + length = ReadByte(ref source, offset: 1, out requestHint); consumed = 2; break; } case MessagePackCode.Str16: case MessagePackCode.Bin16: { - length = ReadValue(source, offset: 1, out requestHint); + length = ReadValue(ref source, offset: 1, out requestHint); consumed = 3; break; } case MessagePackCode.Str32: case MessagePackCode.Bin32: { - length = ReadValue(source, offset: 1, out requestHint); + length = ReadValue(ref source, offset: 1, out requestHint); if (length < 0) { MessagePackThrow.TooLargeByteLength(header, source.Consumed - 5, unchecked((uint)length)); @@ -252,9 +258,9 @@ private int DecodeBinaryHeader(in SequenceReader source, out int requestHi [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override byte[]? DecodeBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + public sealed override byte[]? DecodeBinary(ref SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) { - var length = this.DecodeBinaryHeader(source, out requestHint, out var consumed); + var length = this.DecodeBinaryHeader(ref source, out requestHint, out var consumed); if(requestHint != 0) { return default; @@ -265,9 +271,9 @@ private int DecodeBinaryHeader(in SequenceReader source, out int requestHi Throw.BinaryLengthExceeded(source.Consumed - consumed, length, this.Options.MaxBinaryLengthInBytes); } - if(source.Remaining < length + consumed) + if(source.Remaining < length) { - requestHint = length + consumed - (int)source.Remaining; + requestHint = length - (int)source.Remaining; return default; } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.cs index d244bce91..f08ec168e 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.cs @@ -26,9 +26,9 @@ public MessagePackDecoder(MessagePackDecoderOptions options) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override bool DecodeBoolean(in SequenceReader source, out int requestHint) + public override bool DecodeBoolean(ref SequenceReader source, out int requestHint) { - if (!this.TryPeek(source, out var header)) + if (!source.TryPeek(out var header)) { requestHint = 1; return false; @@ -56,9 +56,18 @@ public override bool DecodeBoolean(in SequenceReader source, out int reque } } - private bool TryReadNull(in SequenceReader source) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override bool TryDecodeNull(ref SequenceReader source, out int requestHint) { - if (this.TryPeek(source, out var b) && b == MessagePackCode.NilValue) + if(!source.TryPeek(out var b)) + { + requestHint = 1; + return default; + } + + requestHint = 0; + + if (b == MessagePackCode.NilValue) { source.Advance(1); return true; @@ -67,7 +76,7 @@ private bool TryReadNull(in SequenceReader source) return false; } - private static void ParseNumberHeader(byte header, in SequenceReader source, Type type, out int length, out NumberKind kind) + private static void ParseNumberHeader(byte header, ref SequenceReader source, Type type, out int length, out NumberKind kind) { // 0xD0-D3 -- SignedIntN // 1101-0000 -> 1101-0011 @@ -79,16 +88,16 @@ private static void ParseNumberHeader(byte header, in SequenceReader sourc if ((header & 0xD3) == header) { // SignedIntN - length = (int)Math.Pow(2, (header & 0x3)) + 1; + length = (int)Math.Pow(2, (header & 0x3)); kind = NumberKind.Signed; } else if ((header & 0xCC) == header) { // UnsignedIntN - length = (int)Math.Pow(2, (header & 0x3)) + 1; + length = (int)Math.Pow(2, (header & 0x3)); kind = NumberKind.Unsigned; } - if (header == MessagePackCode.Real64) + else if (header == MessagePackCode.Real64) { length = 9; kind = NumberKind.Double; @@ -107,7 +116,7 @@ private static void ParseNumberHeader(byte header, in SequenceReader sourc } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private static byte ReadByte(in SequenceReader source, int offset, out int requestHint) + private static byte ReadByte(ref SequenceReader source, int offset, out int requestHint) { if (source.UnreadSpan.Length > offset + 1) { @@ -117,10 +126,10 @@ private static byte ReadByte(in SequenceReader source, int offset, out int return result; } - return ReadByteMultiSegment(source, offset, out requestHint); + return ReadByteMultiSegment(ref source, offset, out requestHint); } - private static byte ReadByteMultiSegment(in SequenceReader source, int offset, out int requestHint) + private static byte ReadByteMultiSegment(ref SequenceReader source, int offset, out int requestHint) { if (source.Remaining < offset + 1) { @@ -136,11 +145,11 @@ private static byte ReadByteMultiSegment(in SequenceReader source, int off } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private static sbyte ReadSByte(in SequenceReader source, int offset, out int requestHint) - => unchecked((sbyte)ReadByte(source, offset, out requestHint)); + private static sbyte ReadSByte(ref SequenceReader source, int offset, out int requestHint) + => unchecked((sbyte)ReadByte(ref source, offset, out requestHint)); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private static unsafe T ReadValue(in SequenceReader source, int offset, out int requestHint) + private static unsafe T ReadValue(ref SequenceReader source, int offset, out int requestHint) where T : unmanaged { if (source.UnreadSpan.Length > offset + sizeof(T)) @@ -151,10 +160,10 @@ private static unsafe T ReadValue(in SequenceReader source, int offset, return result; } - return ReadMultiSegment(source, offset, out requestHint); + return ReadMultiSegment(ref source, offset, out requestHint); } - private static unsafe T ReadMultiSegment(in SequenceReader source, int offset, out int requestHint) + private static unsafe T ReadMultiSegment(ref SequenceReader source, int offset, out int requestHint) where T : unmanaged { if (source.Remaining < offset + sizeof(T)) diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.cs index d38e4fd18..aa94028cc 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.cs @@ -19,7 +19,7 @@ partial class MessagePackEncoder [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeInt32(Int32 value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var leastByte = value & 0x000000FF; if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) @@ -38,7 +38,7 @@ public sealed override void EncodeInt32(Int32 value, IBufferWriter buffer) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeUInt32(UInt32 value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var leastByte = value & 0x000000FF; if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) @@ -57,7 +57,7 @@ public sealed override void EncodeUInt32(UInt32 value, IBufferWriter buffe [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeInt64(Int64 value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var leastByte = value & 0x000000FF; if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) @@ -76,7 +76,7 @@ public sealed override void EncodeInt64(Int64 value, IBufferWriter buffer) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeUInt64(UInt64 value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var leastByte = value & 0x000000FF; if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.tt b/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.tt index d82abb48f..4796d7ef4 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.tt +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Integers.tt @@ -25,7 +25,7 @@ foreach (var inputType in new []{ "Int32", "UInt32", "Int64", "UInt64" }) [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void Encode<#= inputType #>(<#= inputType #> value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var leastByte = value & 0x000000FF; if (leastByte == value || (value ^ 0xFFFFFF00) == leastByte) diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs index cd5b82f23..c5b737a4c 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs @@ -24,8 +24,7 @@ partial class MessagePackEncoder [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) { - buffer = EnsureNotNull(buffer); - encoding = encoding ?? Utf8EncodingNonBom.Instance; + buffer = Ensure.NotNull(buffer); if (value.Length == 0) { buffer.GetSpan(1)[0] = MessagePackCode.MinimumFixedRaw; @@ -33,6 +32,8 @@ public sealed override void EncodeString(ReadOnlySpan value, IBufferWriter return; } + encoding = encoding ?? Utf8EncodingNonBom.Instance; + if (value.Length < 256) { var valueLength = unchecked((int)value.Length); @@ -41,12 +42,10 @@ public sealed override void EncodeString(ReadOnlySpan value, IBufferWriter if (maxByteLength < 32) { // stack alloc is fastest - Span encodingBuffer = stackalloc byte[maxByteLength]; - var actualLength = encoding.GetBytes(value, encodingBuffer); - var sink = buffer.GetSpan(actualLength + 1); + var sink = buffer.GetSpan(maxByteLength + 1); + var actualLength = encoding.GetBytes(value, sink.Slice(1)); sink[0] = unchecked((byte)(MessagePackCode.MinimumFixedRaw | actualLength)); - encodingBuffer.Slice(actualLength).CopyTo(sink.Slice(1)); - buffer.Advance(sink.Length); + buffer.Advance(actualLength + 1); return; } else if (maxByteLength < 256) @@ -96,7 +95,7 @@ private void EncodeStringSlow(ReadOnlySpan value, IBufferWriter buff try { var totalLength = this.EncodeLargeString1Path(value, buffer, encoding, estimatedLength, encodingBuffer); - buffer.Write(encodingBuffer.AsMemory(0, totalLength).Span); + buffer.Write(encodingBuffer.AsSpan(0, totalLength)); } finally { @@ -145,8 +144,7 @@ private unsafe int EncodeLargeString1Path(ReadOnlySpan value, IBufferWrite [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) { - buffer = EnsureNotNull(buffer); - encoding = encoding ?? Utf8EncodingNonBom.Instance; + buffer = Ensure.NotNull(buffer); if (value.Length > UInt32.MaxValue) { Throw.TooLargeCharLength(value.Length); @@ -160,6 +158,8 @@ public sealed override void EncodeString(in ReadOnlySequence value, IBuffe return; } + encoding = encoding ?? Utf8EncodingNonBom.Instance; + if (value.Length < 256) { var valueLength = unchecked((int)value.Length); @@ -168,12 +168,10 @@ public sealed override void EncodeString(in ReadOnlySequence value, IBuffe if (maxByteLength < 32) { // stack alloc is fastest - Span encodingBuffer = stackalloc byte[maxByteLength]; - var actualLength = encoding.GetBytes(value, encodingBuffer); - var sink = buffer.GetSpan(actualLength + 1); + var sink = buffer.GetSpan(maxByteLength + 1); + var actualLength = encoding.GetBytes(value, sink.Slice(1)); sink[0] = unchecked((byte)(MessagePackCode.MinimumFixedRaw | actualLength)); - encodingBuffer.Slice(actualLength).CopyTo(sink.Slice(1)); - buffer.Advance(sink.Length); + buffer.Advance(actualLength + 1); return; } else if (maxByteLength < 256) @@ -223,7 +221,7 @@ private void EncodeStringSlow(in ReadOnlySequence value, IBufferWriter value, IBufferWriter value, IBufferWriter buffer, Encoding encoding, uint estimatedLength, ArraySegment sinkBufferSegment) { diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.tt b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.tt index 7f8eb2206..eb7638a19 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.tt +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.tt @@ -36,8 +36,7 @@ foreach (var inputType in [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeString(<#= inputType.Signature #> value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) { - buffer = EnsureNotNull(buffer); - encoding = encoding ?? Utf8EncodingNonBom.Instance; + buffer = Ensure.NotNull(buffer); <# if (inputType.MayBe64BitLength) { @@ -58,6 +57,8 @@ foreach (var inputType in return; } + encoding = encoding ?? Utf8EncodingNonBom.Instance; + if (value.Length < 256) { var valueLength = unchecked((int)value.Length); @@ -66,12 +67,10 @@ foreach (var inputType in if (maxByteLength < 32) { // stack alloc is fastest - Span encodingBuffer = stackalloc byte[maxByteLength]; - var actualLength = encoding.GetBytes(value, encodingBuffer); - var sink = buffer.GetSpan(actualLength + 1); + var sink = buffer.GetSpan(maxByteLength + 1); + var actualLength = encoding.GetBytes(value, sink.Slice(1)); sink[0] = unchecked((byte)(MessagePackCode.MinimumFixedRaw | actualLength)); - encodingBuffer.Slice(actualLength).CopyTo(sink.Slice(1)); - buffer.Advance(sink.Length); + buffer.Advance(actualLength + 1); return; } else if (maxByteLength < 256) diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.cs index 98fc04b86..153da2005 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoder.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.cs @@ -25,10 +25,14 @@ public abstract partial class MessagePackEncoder : Encoder rawString, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) + => Ensure.NotNull(buffer).Write(rawString); + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeSingle(float value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var span = buffer.GetSpan(sizeof(float) + 1); @@ -41,7 +45,7 @@ public sealed override void EncodeSingle(float value, IBufferWriter buffer [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeDouble(double value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var span = buffer.GetSpan(sizeof(double) + 1); @@ -54,7 +58,7 @@ public sealed override void EncodeDouble(double value, IBufferWriter buffe [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeBoolean(bool value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var span = buffer.GetSpan(1); span[0] = unchecked((byte)(value ? MessagePackCode.TrueValue : MessagePackCode.FalseValue)); @@ -64,7 +68,7 @@ public sealed override void EncodeBoolean(bool value, IBufferWriter buffer [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeArrayStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); collectionContext.IncrementDepth(); if (length < 16) @@ -103,7 +107,7 @@ public sealed override void EncodeArrayItemEnd(int index, IBufferWriter bu [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeMapStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (length < 16) { @@ -147,7 +151,7 @@ public sealed override void EncodeMapValueEnd(int index, IBufferWriter buf [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeNull(IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var span = buffer.GetSpan(1); span[0] = MessagePackCode.NilValue; @@ -199,7 +203,7 @@ private void EncodeHugeString2Path(in ReadOnlySequence value, IBufferWrite [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public sealed override void EncodeString(ReadOnlySpan encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); var span = buffer.GetSpan(5); var used = this.EncodeStringHeader(unchecked((uint)encodedValue.Length), span); diff --git a/src/MsgPack.Core/Internal/MsgPackStringTrie`1.cs b/src/MsgPack.Core/Internal/MsgPackStringTrie`1.cs index 93ef16a78..18bb5f3e1 100644 --- a/src/MsgPack.Core/Internal/MsgPackStringTrie`1.cs +++ b/src/MsgPack.Core/Internal/MsgPackStringTrie`1.cs @@ -4,6 +4,7 @@ using System; using System.Buffers.Binary; +using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; @@ -12,6 +13,7 @@ namespace MsgPack.Internal // Basic idea is borrwed from AutomataDictionary of Message Pack C# // https://github.com/neuecc/MessagePack-CSharp/blob/51649e0d7b8641ad5d3cdd6dfdc130c7671066fc/src/MessagePack.UnityClient/Assets/Scripts/MessagePack/Internal/AutomataDictionary.cs#L1 +#warning TODO: Pubternal public sealed class MsgPackStringTrie { private readonly T _default; @@ -24,7 +26,10 @@ public MsgPackStringTrie(T defaultValue) } public bool TryAdd(ReadOnlySpan utf8Key, T value) - => this.TryAdd(this._root, GenerateKeyHead(ref utf8Key), utf8Key, value); + => this.TryAdd(this._root, MsgPackStringTrieKey.GetAsMsgPackString(ref utf8Key), utf8Key, value); + + public bool TryAddRaw(ReadOnlySpan utf8Key, T value) + => this.TryAdd(this._root, MsgPackStringTrieKey.GetRaw64(ref utf8Key), utf8Key, value); private static int BinarySearch(ulong[] nodes, ulong key) { @@ -55,7 +60,7 @@ private static int BinarySearch(ulong[] nodes, ulong key) public T GetOrDefault(ReadOnlySpan msgPackStringKey) { - var result = this.Find(this._root, GenerateKeyRest(ref msgPackStringKey), msgPackStringKey); + var result = this.Find(this._root, MsgPackStringTrieKey.GetRaw64(ref msgPackStringKey), msgPackStringKey); return result != null ? result.Value : this._default; } @@ -92,7 +97,7 @@ private bool TryAdd(Node parent, ulong key, ReadOnlySpan trailingKey, T va nodes = found.ChildNodes; keys = found.ChildKeys; - key = GenerateKeyRest(ref trailingKey); + key = MsgPackStringTrieKey.GetRaw64(ref trailingKey); } } @@ -126,7 +131,7 @@ private bool TryAdd(Node parent, ulong key, ReadOnlySpan trailingKey, T va nodes = found.ChildNodes; keys = found.ChildKeys; - key = GenerateKeyRest(ref trailingKey); + key = MsgPackStringTrieKey.GetRaw64(ref trailingKey); } } @@ -168,11 +173,260 @@ private void AddNode(Node node, ulong key, ReadOnlySpan trailingKey, int t Debug.Assert(keys.Length == 1); targetIndex = 0; - key = GenerateKeyRest(ref trailingKey); + key = MsgPackStringTrieKey.GetRaw64(ref trailingKey); + } + } + + private sealed class Node + { + // There 2 separate array to improve search performance due to CPU cache line and prediction. + public ulong[] ChildKeys; + public Node[] ChildNodes; + public readonly T Value; + + public Node(T value, Node[] childNodes, ulong[] childKeys) + { + this.Value = value; + this.ChildNodes = childNodes; + this.ChildKeys = childKeys; + } + } + +#warning TODO: REMOVE + public IEnumerable<(ulong[] Keys, T Value)> GetDebugView() + => GetDebugView(this._root, new List()); + + private static IEnumerable<(ulong[] Keys, T Value)> GetDebugView(Node node, List keyChain) + { + if (node.ChildKeys.Length == 0) + { + yield return (keyChain.ToArray(), node.Value); + } + else + { + for (var i = 0; i < node.ChildKeys.Length; i++) + { + var childKey = node.ChildKeys[i]; + keyChain.Add(childKey); + var childNode = node.ChildNodes[i]; + foreach (var item in GetDebugView(childNode, keyChain)) + { + yield return item; + } + keyChain.RemoveAt(keyChain.Count - 1); + } + } + } + } + +#if FALSE + public sealed class MsgPackStringTrie32 + { + private readonly T _default; + private readonly Node _root; + + public MsgPackStringTrie32(T defaultValue) + { + this._default = defaultValue; + this._root = new Node(defaultValue, Array.Empty(), Array.Empty()); + } + + public bool TryAdd(ReadOnlySpan utf8Key, T value) + => this.TryAdd(this._root, MsgPackStringTrieKey.GetAsMsgPackString32(ref utf8Key), utf8Key, value); + + private static int BinarySearch(uint[] nodes, ulong key) + { + // Span.BinarySearch is slower maybe because of ComapreTo method overhead, so we implement binary search manually. + var low = 0; + var high = nodes.Length - 1; + while (low <= high) + { + // Peformance trick borrowed from https://github.com/dotnet/runtime/blob/f2786223508c0c70040fbf48ec3a39a607dd7f75/src/libraries/System.Private.CoreLib/src/System/SpanHelpers.BinarySearch.cs#L42 + var index = unchecked((int)(((uint)high + (uint)low) >> 1)); + var found = nodes[index]; + if (found == key) + { + return index; + } + else if (found < key) + { + low = index + 1; + } + else + { + high = index - 1; + } + } + + return ~low; + } + + public T GetOrDefault(ReadOnlySpan msgPackStringKey) + { + var result = this.Find(this._root, MsgPackStringTrieKey.GetRaw32(ref msgPackStringKey), msgPackStringKey); + return result != null ? result.Value : this._default; + } + + private bool TryAdd(Node parent, uint key, ReadOnlySpan trailingKey, T value) + { + var nodes = parent.ChildNodes; + var keys = parent.ChildKeys; + + while (true) + { + var index = BinarySearch(keys, key); + + if (index < 0) + { + // No matching leaf. + this.AddNode(parent, key, trailingKey, ~index, value, out _); + return true; + } + + var found = nodes[index]; + if (trailingKey.IsEmpty) + { + // The leaf matches. + return false; + } + + if (found.ChildNodes.Length == 0) + { + // Search key is longer than trie path. + this.AddNode(found, key, trailingKey, 0, value, out _); + return true; + } + + nodes = found.ChildNodes; + keys = found.ChildKeys; + + key = MsgPackStringTrieKey.GetRaw32(ref trailingKey); + } + } + + private Node? Find(Node parent, uint key, ReadOnlySpan trailingKey) + { + var nodes = parent.ChildNodes; + var keys = parent.ChildKeys; + + while (true) + { + var index = BinarySearch(keys, key); + + if (index < 0) + { + // No matching leaf. + return null; + } + + var found = nodes[index]; + if (trailingKey.IsEmpty) + { + // The leaf matches. + return found; + } + + if (found.ChildNodes.Length == 0) + { + // Search key is longer than trie path. + return null; + } + + nodes = found.ChildNodes; + keys = found.ChildKeys; + key = MsgPackStringTrieKey.GetRaw32(ref trailingKey); } } - private static ulong GenerateKeyHead(ref ReadOnlySpan source) + private void AddNode(Node node, uint key, ReadOnlySpan trailingKey, int targetIndex, T value, out Node result) + { + Array.Resize(ref node.ChildKeys, node.ChildKeys.Length + 1); + Array.Resize(ref node.ChildNodes, node.ChildNodes.Length + 1); + var nodes = node.ChildNodes; + var keys = node.ChildKeys; + + while (true) + { + var child = + trailingKey.IsEmpty ? + new Node(value, Array.Empty(), Array.Empty()) : + new Node(this._default, new Node[1], new uint[1]); + + if (nodes.Length > 1 && targetIndex < nodes.Length - 1) + { + // Shift existing. + Array.Copy(nodes, targetIndex, nodes, targetIndex + 1, nodes.Length - targetIndex - 1); + Array.Copy(keys, targetIndex, keys, targetIndex + 1, keys.Length - targetIndex - 1); + } + + nodes[targetIndex] = child; + keys[targetIndex] = key; + + if (trailingKey.IsEmpty) + { + // This is leaf. + result = child; + return; + } + + nodes = child.ChildNodes; + keys = child.ChildKeys; + + Debug.Assert(nodes.Length == 1); + Debug.Assert(keys.Length == 1); + targetIndex = 0; + + key = MsgPackStringTrieKey.GetRaw32(ref trailingKey); + } + } + + private sealed class Node + { + // There 2 separate array to improve search performance due to CPU cache line and prediction. + public uint[] ChildKeys; + public Node[] ChildNodes; + public readonly T Value; + + public Node(T value, Node[] childNodes, uint[] childKeys) + { + this.Value = value; + this.ChildNodes = childNodes; + this.ChildKeys = childKeys; + } + } + +#warning TODO: REMOVE + public IEnumerable<(uint[] Keys, T Value)> GetDebugView() + => GetDebugView(this._root, new List()); + + private static IEnumerable<(uint[] Keys, T Value)> GetDebugView(Node node, List keyChain) + { + if (node.ChildKeys.Length == 0) + { + yield return (keyChain.ToArray(), node.Value); + } + else + { + for (var i = 0; i < node.ChildKeys.Length; i++) + { + var childKey = node.ChildKeys[i]; + keyChain.Add(childKey); + var childNode = node.ChildNodes[i]; + foreach (var item in GetDebugView(childNode, keyChain)) + { + yield return item; + } + keyChain.RemoveAt(keyChain.Count - 1); + } + } + } + } +#endif + +#warning TODO: Pubternal + public static class MsgPackStringTrieKey + { + public static ulong GetAsMsgPackString(ref ReadOnlySpan source) { Span bytes = stackalloc byte[sizeof(ulong)]; var buffer = bytes; @@ -212,7 +466,7 @@ private static ulong GenerateKeyHead(ref ReadOnlySpan source) return MemoryMarshal.Cast(bytes)[0]; } - private static ulong GenerateKeyRest(ref ReadOnlySpan source) + public static ulong GetRaw64(ref ReadOnlySpan source) { Debug.Assert(!source.IsEmpty); if (source.Length >= sizeof(ulong)) @@ -281,19 +535,87 @@ private static ulong GenerateKeyRest(ref ReadOnlySpan source) } } - private sealed class Node +#if FALSE + public static uint GetAsMsgPackString32(ref ReadOnlySpan source) { - // There 2 separate array to improve search performance due to CPU cache line and prediction. - public ulong[] ChildKeys; - public Node[] ChildNodes; - public readonly T Value; + Span bytes = stackalloc byte[sizeof(uint)]; + var buffer = bytes; + int consumes; + if (source.Length < 16) + { + buffer[0] = (byte)(0xA0 | source.Length); + buffer = buffer.Slice(1); + consumes = Math.Min(source.Length, sizeof(uint) - 1); + } + else if (source.Length <= Byte.MaxValue) + { + buffer[0] = 0xD9; + buffer[1] = (byte)source.Length; + buffer = buffer.Slice(2); + consumes = Math.Min(source.Length, sizeof(uint) - 1 - 1); + } + else if (source.Length <= UInt16.MaxValue) + { + buffer[0] = 0xDA; + buffer = buffer.Slice(1); + BinaryPrimitives.WriteUInt16BigEndian(buffer, (ushort)source.Length); + buffer = buffer.Slice(sizeof(ushort)); + consumes = Math.Min(source.Length, sizeof(uint) - 1 - sizeof(ushort)); + } + else + { + buffer[0] = 0xDB; + buffer = buffer.Slice(1); + BinaryPrimitives.WriteInt32BigEndian(buffer, source.Length); + buffer = buffer.Slice(sizeof(int)); + consumes = Math.Min(source.Length, sizeof(uint) - 1 - sizeof(int)); + } - public Node(T value, Node[] childNodes, ulong[] childKeys) + source.Slice(0, consumes).CopyTo(buffer); + source = source.Slice(consumes); + return MemoryMarshal.Cast(bytes)[0]; + } + + public static uint GetRaw32(ref ReadOnlySpan source) + { + Debug.Assert(!source.IsEmpty); + if (source.Length >= sizeof(uint)) { - this.Value = value; - this.ChildNodes = childNodes; - this.ChildKeys = childKeys; + var result = MemoryMarshal.Cast(source)[0]; + source = source.Slice(sizeof(uint)); + return result; + } + else + { + uint result; + unchecked + { + switch (source.Length) + { + case 1: + { + result = source[0]; + break; + } + case 2: + { + result = BinaryPrimitives.ReadUInt16LittleEndian(source); + break; + } + default: // 3 + { + result = BinaryPrimitives.ReadUInt16LittleEndian(source); + source = source.Slice(2); + result |= (uint)(source[0] << 16); + break; + } + } + } + + source = ReadOnlySpan.Empty; + return result; } } +#endif } } diff --git a/src/MsgPack.Core/System.Text/Throw.cs b/src/MsgPack.Core/System.Text/Throw.cs new file mode 100644 index 000000000..07a723adf --- /dev/null +++ b/src/MsgPack.Core/System.Text/Throw.cs @@ -0,0 +1,14 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using MsgPack; + +namespace System.Text +{ + internal static class Throw + { + public static void TooLargeByteLengthForString(string encodingName) + => throw new MessageTypeException($"Input is too large for encoding '{encodingName}' to decode."); + } +} diff --git a/src/MsgPack.Json/Json/FlexibleJsonDecoder.ReadTrivia.cs b/src/MsgPack.Json/Json/FlexibleJsonDecoder.ReadTrivia.cs index 8d09c4f33..db6aa493e 100644 --- a/src/MsgPack.Json/Json/FlexibleJsonDecoder.ReadTrivia.cs +++ b/src/MsgPack.Json/Json/FlexibleJsonDecoder.ReadTrivia.cs @@ -2,16 +2,37 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +using System; using System.Buffers; +using System.Runtime.CompilerServices; +using MsgPack.Internal; namespace MsgPack.Json { internal partial class FlexibleJsonDecoder { - protected override void ReadTrivia(in SequenceReader source, out ReadOnlySequence trivia) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + protected sealed override long ReadTrivia(ref SequenceReader source) { - var offset = source.Consumed; - var singleByteWhitespaces = (this.Options.ParseOptions & JsonParseOptions.AllowUnicodeWhitespace) == 0 ? JsonTriviaTokens.StandardWhitespaces : JsonTriviaTokens.SingleByteUnicodeWhitespaces; + ReadOnlySpan singleByteWhitespaces; + unsafe + { + if ((this.Options.ParseOptions & JsonParseOptions.AllowUnicodeWhitespace) == 0) + { + byte* pStandardWhitespaces = stackalloc byte[] { (byte)' ', (byte)'\t', (byte)'\r', (byte)'\n' }; + singleByteWhitespaces = new ReadOnlySpan(pStandardWhitespaces, 4); + } + else + { + byte* pSingleByteUnicodeWhitespaces = + stackalloc byte[] + { + (byte)' ', (byte)'\t', (byte)'\r', (byte)'\n', + (byte)'\u000B', (byte)'\u000C' + }; + singleByteWhitespaces = new ReadOnlySpan(pSingleByteUnicodeWhitespaces, 6); + } + } var consumed = 0L; while (!source.End) @@ -36,7 +57,7 @@ protected override void ReadTrivia(in SequenceReader source, out ReadOnlyS } } - trivia = source.Sequence.Slice(offset, consumed); + return consumed; } private bool TryReadMultiByteUnicodeWhitespaces(in SequenceReader source, ref long consumed) @@ -61,10 +82,20 @@ private bool TryReadMultiByteUnicodeWhitespaces(in SequenceReader source, private bool TryReadSingleLineComment(in SequenceReader source, ref long consumed) { + ReadOnlySpan singleLineCommentStart; + ReadOnlySpan newLine; + unsafe + { + byte* pSingleLineCommentStart = stackalloc[] { (byte)'/', (byte)'/' }; + singleLineCommentStart = new ReadOnlySpan(pSingleLineCommentStart, 2); + byte* pNewLine = stackalloc[] { (byte)'\r', (byte)'\n' }; + newLine = new ReadOnlySpan(pNewLine, 2); + } + if (((this.Options.ParseOptions & JsonParseOptions.AllowHashSingleLineComment) != 0 && source.IsNext((byte)'#', advancePast: true)) - || ((this.Options.ParseOptions & JsonParseOptions.AllowDoubleSolidousSingleLineComment) != 0 && source.IsNext(JsonTriviaTokens.SingleLineCommentStart2, advancePast: true))) + || ((this.Options.ParseOptions & JsonParseOptions.AllowDoubleSolidousSingleLineComment) != 0 && source.IsNext(singleLineCommentStart, advancePast: true))) { - if (!source.TryReadToAny(out ReadOnlySequence line, JsonTriviaTokens.NewLine, advancePastDelimiter: true)) + if (!source.TryReadToAny(out ReadOnlySequence line, newLine, advancePastDelimiter: true)) { // to EoF consumed += source.Remaining; @@ -73,7 +104,7 @@ private bool TryReadSingleLineComment(in SequenceReader source, ref long c } consumed += line.Length; - consumed += source.AdvancePastAny(JsonTriviaTokens.NewLine); + consumed += source.AdvancePastAny((byte)'\r', (byte)'\n'); return true; } @@ -82,14 +113,29 @@ private bool TryReadSingleLineComment(in SequenceReader source, ref long c private bool TryReadMultiLineComment(in SequenceReader source, ref long consumed) { + if((this.Options.ParseOptions & JsonParseOptions.AllowMultilineComment) == 0) + { + return false; + } + + ReadOnlySpan multiLineCommentStart; + ReadOnlySpan multiLineCommentEnd; + unsafe + { + byte* pMultiLineCommentStart = stackalloc[] { (byte)'/', (byte)'*' }; + multiLineCommentStart = new ReadOnlySpan(pMultiLineCommentStart, 2); + byte* pMultiLineCommentEnd = stackalloc[] { (byte)'*', (byte)'/' }; + multiLineCommentEnd = new ReadOnlySpan(pMultiLineCommentEnd, 2); + } + var offset = source.Consumed; - if ((this.Options.ParseOptions & JsonParseOptions.AllowMultilineComment) == 0 || !source.IsNext(JsonTriviaTokens.MultiLineCommentStart, advancePast: true)) + if (!source.IsNext(multiLineCommentStart, advancePast: true)) { return false; } - if (!source.TryReadToAny(out ReadOnlySequence comment, JsonTriviaTokens.MultiLineCommentEnd, advancePastDelimiter: true)) + if (!source.TryReadToAny(out ReadOnlySequence comment, multiLineCommentEnd, advancePastDelimiter: true)) { // */ is not found. source.Rewind(2); diff --git a/src/MsgPack.Json/Json/FlexibleJsonDecoder.cs b/src/MsgPack.Json/Json/FlexibleJsonDecoder.cs index 23bc34bcc..b7f4c4b3b 100644 --- a/src/MsgPack.Json/Json/FlexibleJsonDecoder.cs +++ b/src/MsgPack.Json/Json/FlexibleJsonDecoder.cs @@ -2,7 +2,10 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +using System; using System.Buffers; +using System.Runtime.CompilerServices; +using MsgPack.Internal; namespace MsgPack.Json { @@ -14,19 +17,36 @@ internal sealed partial class FlexibleJsonDecoder : JsonDecoder public FlexibleJsonDecoder(JsonDecoderOptions options) : base(options) { } - protected override bool TryReadNull(in SequenceReader source) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override unsafe bool TryDecodeNull(ref SequenceReader source, out int requestHint) { - if (source.IsNext(JsonTokens.Null, advancePast: true)) + byte* pNull= stackalloc byte[] { (byte)'n', (byte)'u', (byte)'l', (byte)'l' }; + ReadOnlySpan @null = new ReadOnlySpan(pNull, 4); + + if (source.IsNext(@null, advancePast: true)) { + requestHint = 0; + source.Advance(4); return true; } + return this.TryDecodeNullSlow(ref source, out requestHint); + } + + private unsafe bool TryDecodeNullSlow(ref SequenceReader source, out int requestHint) + { + byte* pUndefined = stackalloc byte[] { (byte)'u', (byte)'n', (byte)'d', (byte)'e', (byte)'f', (byte)'i', (byte)'n', (byte)'e', (byte)'d', }; + ReadOnlySpan undefined = new ReadOnlySpan(pUndefined, 9); + if ((this.Options.ParseOptions & JsonParseOptions.AllowUndefined) != 0 - && source.IsNext(JsonTokens.Undefined, advancePast: true)) + && source.IsNext(undefined, advancePast: true)) { + requestHint = 0; + source.Advance(9); return true; } + requestHint = Math.Max(0, 4 - (int)source.Remaining); return false; } } diff --git a/src/MsgPack.Json/Json/JsonCharactor.cs b/src/MsgPack.Json/Json/JsonCharactor.cs index 3c06d41f1..777c447f7 100644 --- a/src/MsgPack.Json/Json/JsonCharactor.cs +++ b/src/MsgPack.Json/Json/JsonCharactor.cs @@ -18,5 +18,6 @@ internal static class JsonCharactor public static readonly ReadOnlyMemory ShouldBeEscaped = new[] { (byte)'/', (byte)'\'', (byte)'<', (byte)'>', (byte)'&' }.Select(b => new Rune(b)).ToArray(); // For HTML embedding public static ReadOnlySpan CarriageReturn => new[] { (byte)'\r' }; public static ReadOnlySpan LineFeed => new[] { (byte)'\n' }; + public static ReadOnlySpan Tab => new[] { (byte)'\t' }; } } diff --git a/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs b/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs index cd72d8a69..c5f94307c 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs @@ -2,7 +2,6 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. -using System; using System.Buffers; using MsgPack.Internal; @@ -10,13 +9,13 @@ namespace MsgPack.Json { public partial class JsonDecoder { - public override CollectionType DecodeArrayOrMapHeader(in SequenceReader source, out long itemsCount, out int requestHint) + public sealed override CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out long itemsCount, out int requestHint) => JsonThrow.CollectionHeaderDecodingIsNotSupported(out itemsCount, out requestHint); - public override long DecodeArrayHeader(in SequenceReader source, out int requestHint) + public sealed override long DecodeArrayHeader(ref SequenceReader source, out int requestHint) => JsonThrow.CollectionHeaderDecodingIsNotSupported(out requestHint); - public override long DecodeMapHeader(in SequenceReader source, out int requestHint) + public sealed override long DecodeMapHeader(ref SequenceReader source, out int requestHint) => JsonThrow.CollectionHeaderDecodingIsNotSupported(out requestHint); } } diff --git a/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs b/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs index aca36b59e..9fe873247 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs @@ -15,26 +15,35 @@ namespace MsgPack.Json { public partial class JsonDecoder { - public override bool DecodeItem(in SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default) + public sealed override bool DecodeItem(ref SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default) { - this.ReadTrivia(source, out var trivia); - if (!trivia.IsEmpty) + var startPosition = source.Position; + var triviaLength = this.ReadTrivia(ref source); + if (triviaLength != 0) { - result = DecodeItemResult.ScalarOrSequence(ElementType.OtherTrivia, trivia); + result = DecodeItemResult.ScalarOrSequence(ElementType.OtherTrivia, source.Sequence.Slice(startPosition, source.Position)); return true; } - if (!this.TryPeek(source, out var token)) + if (!source.TryPeek(out var token)) { result = DecodeItemResult.InsufficientInput(1); return false; } +#warning HANDLE :/= and , switch (token) { case (byte)'t': { - if (source.IsNext(JsonTokens.True, advancePast: true)) + ReadOnlySpan @true; + unsafe + { + byte* pTrue = stackalloc byte[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; + @true = new ReadOnlySpan(pTrue, 4); + } + + if (source.IsNext(@true, advancePast: true)) { result = DecodeItemResult.True(); return true; @@ -44,7 +53,14 @@ public override bool DecodeItem(in SequenceReader source, out DecodeItemRe } case (byte)'f': { - if (source.IsNext(JsonTokens.False, advancePast: true)) + ReadOnlySpan @false; + unsafe + { + byte* pFalse = stackalloc byte[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; + @false = new ReadOnlySpan(pFalse, 5); + } + + if (source.IsNext(@false, advancePast: true)) { result = DecodeItemResult.False(); return true; @@ -54,7 +70,14 @@ public override bool DecodeItem(in SequenceReader source, out DecodeItemRe } case (byte)'n': { - if (source.IsNext(JsonTokens.Null, advancePast: true)) + ReadOnlySpan @null; + unsafe + { + byte* pNull = stackalloc byte[] { (byte)'n', (byte)'u', (byte)'l', (byte)'l' }; + @null = new ReadOnlySpan(pNull, 4); + } + + if (source.IsNext(@null, advancePast: true)) { result = DecodeItemResult.Null(); return true; @@ -75,7 +98,7 @@ public override bool DecodeItem(in SequenceReader source, out DecodeItemRe case (byte)'-': case (byte)'+': { - var number = this.DecodeNumber(source, out var requestHint); + var number = this.DecodeNumber(ref source, out var requestHint); if(requestHint != 0) { result = DecodeItemResult.InsufficientInput(requestHint); @@ -108,7 +131,7 @@ public override bool DecodeItem(in SequenceReader source, out DecodeItemRe } case (byte)'"': { - if (this.GetRawStringCore(source, out var rawString, out var requestHint)) + if (this.GetRawStringCore(ref source, out var rawString, out var requestHint)) { result = DecodeItemResult.InsufficientInput(requestHint); return false; diff --git a/src/MsgPack.Json/Json/JsonDecoder.Iteration.cs b/src/MsgPack.Json/Json/JsonDecoder.Iteration.cs index bd8458d2b..82e84a498 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.Iteration.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.Iteration.cs @@ -4,6 +4,7 @@ using System.Buffers; using System.Diagnostics; +using System.Runtime.CompilerServices; using MsgPack.Internal; namespace MsgPack.Json @@ -25,7 +26,7 @@ public ArrayIterator(JsonDecoder decoder) this._state = State.Head; } - public bool DetectArrayEnds(in SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) + public bool DetectArrayEnds(ref SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) { if (this._state == State.Tail) { @@ -33,7 +34,7 @@ public bool DetectArrayEnds(in SequenceReader source, ref long nextItemInd return true; // End } - this._decoder.ReadTrivia(source, out _); + this._decoder.ReadTrivia(ref source); switch (this._state) { @@ -92,7 +93,7 @@ public bool DetectArrayEnds(in SequenceReader source, ref long nextItemInd } // swtich (this._state) // Handle 'maybe item' state - this._decoder.ReadTrivia(source, out _); + this._decoder.ReadTrivia(ref source); if (!source.TryPeek(out var mayBeArrayEnd)) { requestHint = 1; @@ -141,7 +142,7 @@ public ObjectPropertyIterator(JsonDecoder decoder) this._isEqualSignAllowed = (decoder.Options.ParseOptions & JsonParseOptions.AllowEqualSignSeparator) != 0; } - public bool DetectObjectEnds(in SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) + public bool DetectObjectEnds(ref SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint) { if (this._state == State.Tail) { @@ -149,7 +150,7 @@ public bool DetectObjectEnds(in SequenceReader source, ref long nextItemIn return true; // End } - this._decoder.ReadTrivia(source, out _); + this._decoder.ReadTrivia(ref source); switch (this._state) { @@ -246,7 +247,7 @@ public bool DetectObjectEnds(in SequenceReader source, ref long nextItemIn } // swtich (this._state) // Handle 'maybe key' state - this._decoder.ReadTrivia(source, out _); + this._decoder.ReadTrivia(ref source); if (!source.TryPeek(out var mayBeArrayEnd)) { requestHint = 1; @@ -277,11 +278,12 @@ private enum State } } - public override CollectionType DecodeArrayOrMap(in SequenceReader source, out CollectionItemIterator iterator, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override CollectionType DecodeArrayOrMap(ref SequenceReader source, out CollectionItemIterator iterator, out int requestHint) { - this.ReadTrivia(source, out _); + this.ReadTrivia(ref source); - if (!this.TryPeek(source, out var token)) + if (!source.TryPeek(out var token)) { requestHint = 1; iterator = default; @@ -301,10 +303,26 @@ public override CollectionType DecodeArrayOrMap(in SequenceReader source, iterator = this.CreateObjectPropertyIterator(); return CollectionType.Map; } + case (byte)'n': + { + iterator = default; + + if (this.TryDecodeNull(ref source, out requestHint)) + { + return CollectionType.Null; + } + + if (requestHint != 0) + { + return CollectionType.None; + } + + goto default; + } default: { var offset = source.Consumed; - var kind = TryGetUtf8Unit(source, out var sequence); + var kind = TryGetUtf8Unit(ref source, out var sequence); if (kind == Utf8UnitStatus.Valid) { JsonThrow.IsNotArrayNorObject(sequence, offset); @@ -327,9 +345,10 @@ private CollectionItemIterator CreateArrayIterator() private CollectionItemIterator CreateObjectPropertyIterator() => new CollectionItemIterator(new ObjectPropertyIterator(this).DetectObjectEnds, -1); - public override CollectionItemIterator DecodeArray(in SequenceReader source, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override CollectionItemIterator DecodeArray(ref SequenceReader source, out int requestHint) { - var type = this.DecodeArrayOrMap(source, out var iterator, out requestHint); + var type = this.DecodeArrayOrMap(ref source, out var iterator, out requestHint); if (requestHint != 0) { return default; @@ -343,9 +362,10 @@ public override CollectionItemIterator DecodeArray(in SequenceReader sourc return iterator; } - public override CollectionItemIterator DecodeMap(in SequenceReader source, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override CollectionItemIterator DecodeMap(ref SequenceReader source, out int requestHint) { - var type = this.DecodeArrayOrMap(source, out var iterator, out requestHint); + var type = this.DecodeArrayOrMap(ref source, out var iterator, out requestHint); if (requestHint != 0) { return default; diff --git a/src/MsgPack.Json/Json/JsonDecoder.Nullables.cs b/src/MsgPack.Json/Json/JsonDecoder.Nullables.cs index bfe6936b5..6351dfc13 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.Nullables.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.Nullables.cs @@ -17,156 +17,156 @@ partial class JsonDecoder { /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Byte? DecodeNullableByte(in SequenceReader source, out int requestHint) + public sealed override Byte? DecodeNullableByte(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeByteCore(source, out requestHint); + return this.DecodeByteCore(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override SByte? DecodeNullableSByte(in SequenceReader source, out int requestHint) + public sealed override SByte? DecodeNullableSByte(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeSByteCore(source, out requestHint); + return this.DecodeSByteCore(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int16? DecodeNullableInt16(in SequenceReader source, out int requestHint) + public sealed override Int16? DecodeNullableInt16(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeInt16Core(source, out requestHint); + return this.DecodeInt16Core(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt16? DecodeNullableUInt16(in SequenceReader source, out int requestHint) + public sealed override UInt16? DecodeNullableUInt16(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeUInt16Core(source, out requestHint); + return this.DecodeUInt16Core(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int32? DecodeNullableInt32(in SequenceReader source, out int requestHint) + public sealed override Int32? DecodeNullableInt32(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeInt32Core(source, out requestHint); + return this.DecodeInt32Core(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt32? DecodeNullableUInt32(in SequenceReader source, out int requestHint) + public sealed override UInt32? DecodeNullableUInt32(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeUInt32Core(source, out requestHint); + return this.DecodeUInt32Core(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int64? DecodeNullableInt64(in SequenceReader source, out int requestHint) + public sealed override Int64? DecodeNullableInt64(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeInt64Core(source, out requestHint); + return this.DecodeInt64Core(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt64? DecodeNullableUInt64(in SequenceReader source, out int requestHint) + public sealed override UInt64? DecodeNullableUInt64(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeUInt64Core(source, out requestHint); + return this.DecodeUInt64Core(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Single? DecodeNullableSingle(in SequenceReader source, out int requestHint) + public sealed override Single? DecodeNullableSingle(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeSingleCore(source, out requestHint); + return this.DecodeSingleCore(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Double? DecodeNullableDouble(in SequenceReader source, out int requestHint) + public sealed override Double? DecodeNullableDouble(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeDoubleCore(source, out requestHint); + return this.DecodeDoubleCore(ref source, out requestHint); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Boolean? DecodeNullableBoolean(in SequenceReader source, out int requestHint) + public sealed override Boolean? DecodeNullableBoolean(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeBooleanCore(source, out requestHint); + return this.DecodeBooleanCore(ref source, out requestHint); } } diff --git a/src/MsgPack.Json/Json/JsonDecoder.Nullables.tt b/src/MsgPack.Json/Json/JsonDecoder.Nullables.tt index c630c16c1..ce9e49b17 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.Nullables.tt +++ b/src/MsgPack.Json/Json/JsonDecoder.Nullables.tt @@ -38,16 +38,16 @@ foreach (var type in #> /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override <#= type #>? DecodeNullable<#= type #>(in SequenceReader source, out int requestHint) + public sealed override <#= type #>? DecodeNullable<#= type #>(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.Decode<#= type #>Core(source, out requestHint); + return this.Decode<#= type #>Core(ref source, out requestHint); } <# diff --git a/src/MsgPack.Json/Json/JsonDecoder.NumberCore.cs b/src/MsgPack.Json/Json/JsonDecoder.NumberCore.cs index 02c905620..d2d0b8f13 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.NumberCore.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.NumberCore.cs @@ -6,36 +6,51 @@ using System.Buffers; using System.Buffers.Text; using System.Diagnostics; +using System.Runtime.CompilerServices; +using MsgPack.Internal; namespace MsgPack.Json { public partial class JsonDecoder { - public override bool DecodeBoolean(in SequenceReader source, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override bool DecodeBoolean(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeBooleanCore(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeBooleanCore(ref source, out requestHint); } - private bool DecodeBooleanCore(in SequenceReader source, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private bool DecodeBooleanCore(ref SequenceReader source, out int requestHint) { + ReadOnlySpan @true; + ReadOnlySpan @false; + unsafe + { + byte* pTrue = stackalloc byte[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e', }; + @true = new ReadOnlySpan(pTrue, 4); + byte* pFalse = stackalloc byte[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e', }; + @false = new ReadOnlySpan(pFalse, 5); + } + requestHint = 0; - if (source.IsNext(JsonTokens.True, advancePast: true)) + if (source.IsNext(@true, advancePast: true)) { return true; } - else if (source.IsNext(JsonTokens.False, advancePast: true)) + else if (source.IsNext(@false, advancePast: true)) { return false; } else { - requestHint = GetRequestHintForBoolean(source); + requestHint = GetRequestHintForBoolean(ref source); return default; } } - private static int GetRequestHintForBoolean(in SequenceReader source) + [MethodImpl(MethodImplOptions.NoInlining)] + private static int GetRequestHintForBoolean(ref SequenceReader source) { if (!source.TryPeek(out var b)) { @@ -46,43 +61,52 @@ private static int GetRequestHintForBoolean(in SequenceReader source) { case (byte)'t': { - return GetRequestHintForBoolean(source, offset: 1, JsonTokens.True); + ReadOnlySpan @true; + unsafe + { + byte* pTrue = stackalloc byte[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; + @true = new ReadOnlySpan(pTrue, 4); + } + + return GetRequestHintForBoolean(ref source, offset: 1, @true); } case (byte)'f': { - return GetRequestHintForBoolean(source, offset: 1, JsonTokens.False); + ReadOnlySpan @false; + unsafe + { + byte* pFalse = stackalloc byte[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; + @false = new ReadOnlySpan(pFalse, 5); + } + + return GetRequestHintForBoolean(ref source, offset: 1, @false); } } - ThrowNonBooleanUtfSequence(source, source.Consumed); + ThrowNonBooleanUtfSequence(ref source, source.Consumed); // never return default; } - private static int GetRequestHintForBoolean(in SequenceReader source, int offset, ReadOnlySpan expected) + private static int GetRequestHintForBoolean(ref SequenceReader source, int offset, ReadOnlySpan expected) { - var subReeader = new SequenceReader(source.Sequence.Slice(source.Consumed + 1)); + Span buffer = stackalloc byte[(int)Math.Min(expected.Length, source.Remaining)]; + source.TryCopyTo(buffer); - for (int i = 0; i < expected.Length; i++) + for (var i = 0; i < buffer.Length; i++) { - if (!subReeader.TryRead(out var b)) - { - return expected.Length - i; - } - - if (b != expected[i]) + if (buffer[i] != expected[i]) { - ThrowNonBooleanUtfSequence(subReeader, subReeader.Consumed + source.Consumed); + ThrowNonBooleanUtfSequence(ref source, source.Consumed + i); } } - Debug.Fail("should be true/false..."); - return default; + return expected.Length - buffer.Length; } - private static void ThrowNonBooleanUtfSequence(in SequenceReader source, long position) + private static void ThrowNonBooleanUtfSequence(ref SequenceReader source, long position) { - if (TryGetUtf8Unit(source, out var unit) == Utf8UnitStatus.Invalid) + if (TryGetUtf8Unit(ref source, out var unit) == Utf8UnitStatus.Invalid) { JsonThrow.MalformedUtf8(unit, position); } @@ -92,92 +116,117 @@ private static void ThrowNonBooleanUtfSequence(in SequenceReader source, l } } - private double DecodeNumber(in SequenceReader source, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private double DecodeNumber(ref SequenceReader source, out int requestHint) { - var start = source.Position; - var startOffset = source.Consumed; + var lengthReader = source; + ReadOnlySpan plusMinusSigns; + ReadOnlySpan nonZeroDigits; + ReadOnlySpan digits; + unsafe + { + byte* pPlusMinusSigns = stackalloc[] { (byte)'+', (byte)'-' }; + plusMinusSigns = new ReadOnlySpan(pPlusMinusSigns, 2); + byte* pDigits = stackalloc[] { (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', (byte)'8', (byte)'9' }; + digits = new ReadOnlySpan(pDigits, 9); + nonZeroDigits = digits.Slice(1); + } + var length = 0L; // -? (0 | [1-9] \d*) (\. \d+ ([eE] [+-] \d+)? )? // sign - if (source.IsNext(JsonNumberTokens.PlusMinusSigns, advancePast: true)) - { - length++; - } + length += lengthReader.AdvancePastAny((byte)'+', (byte)'-'); // 0 - if (source.IsNext((byte)'0', advancePast: true)) + if (lengthReader.IsNext((byte)'0', advancePast: true)) { length++; } // [1-9] - else if (source.IsNext(JsonNumberTokens.NonZeroDigits, advancePast: true)) - { - length++; - // \d* - length += source.AdvancePastAny(JsonNumberTokens.Digits); - } else { - ThrowInvalidNumber(source); + var nonZeroLength = lengthReader.AdvancePastAny(nonZeroDigits); + if (nonZeroLength > 0) + { + length += nonZeroLength; + // \d* + length += lengthReader.AdvancePastAny(digits); + } + else + { + ThrowInvalidNumber(ref lengthReader); + } } // Check early for integer portion. if (length > this.Options.MaxNumberLengthInBytes) { - JsonThrow.TooLongNumber(length, this.Options.MaxNumberLengthInBytes, startOffset); + JsonThrow.TooLongNumber(length, this.Options.MaxNumberLengthInBytes, source.Consumed); } // (\. ...)? - if (source.IsNext((byte)'.', advancePast: true)) + if (lengthReader.IsNext((byte)'.', advancePast: true)) { length++; - if (source.End) + if (lengthReader.End) { requestHint = 1; - source.Rewind(length); return default; } // \d+ - length += ReadAtLeastOneDigits(source, out requestHint); + length += ReadAtLeastOneDigits(ref lengthReader, digits, out requestHint); if (requestHint != 0) { - source.Rewind(length); return default; } + ReadOnlySpan exponentialIndicators; + unsafe + { + byte* pExponentialIndicators = stackalloc byte[] { (byte)'E', (byte)'e' }; + exponentialIndicators = new ReadOnlySpan(pExponentialIndicators, 2); + } + // ([eE] ..)? - if (source.IsNext(JsonNumberTokens.ExponentialIndicators, advancePast: true)) + if (lengthReader.IsNext(exponentialIndicators, advancePast: true)) { // [+-] - if (source.IsNext(JsonNumberTokens.PlusMinusSigns, advancePast: true)) + if (lengthReader.IsNext(plusMinusSigns, advancePast: true)) { // \d+ - length += ReadAtLeastOneDigits(source, out requestHint); + length += ReadAtLeastOneDigits(ref lengthReader, digits, out requestHint); if (requestHint != 0) { - source.Rewind(length); return default; } } else { - ThrowInvalidNumber(source); + ThrowInvalidNumber(ref lengthReader); } } } if (length > this.Options.MaxNumberLengthInBytes) { - JsonThrow.TooLongNumber(length, this.Options.MaxNumberLengthInBytes, startOffset); + JsonThrow.TooLongNumber(length, this.Options.MaxNumberLengthInBytes, source.Consumed); } - var readDirect = source.UnreadSpan.Length <= length; + if (source.UnreadSpan.Length >= length) + { + var shouldBeTrue = Utf8Parser.TryParse(source.UnreadSpan, out double result, out var consumed); + Debug.Assert(shouldBeTrue, "Utf8Parser.TryParse returns false!"); + Debug.Assert(consumed == length, $"Utf8Parser.TryParse outputs ({consumed}:#,0) is not {length:#,0}!"); + source.Advance(consumed); + requestHint = 0; + return result; + } var length32 = (int)length; byte[]? arrayBuffer = default; - if (!readDirect && length > 32) + if (length > 32) { // length should be Int32 because MaxNumberLength is Int32 type. arrayBuffer = this.Options.ByteBufferPool.Rent(length32); @@ -185,17 +234,14 @@ private double DecodeNumber(in SequenceReader source, out int requestHint) try { - Span buffer = - readDirect ? - default : - arrayBuffer ?? (stackalloc byte[length32]); - var number = GetSpan(source.Sequence.Slice(start, length32), buffer); + Span buffer = arrayBuffer ?? (stackalloc byte[length32]); + source.TryCopyTo(buffer); - var shouldBeTrue = Utf8Parser.TryParse(number, out double result, out var shouldBeLength); + var shouldBeTrue = Utf8Parser.TryParse(buffer, out double result, out var consumed); Debug.Assert(shouldBeTrue, "Utf8Parser.TryParse returns false!"); - Debug.Assert(shouldBeLength == length, $"Utf8Parser.TryParse outputs ({shouldBeLength}:#,0) is not {length:#,0}!"); + Debug.Assert(consumed == length, $"Utf8Parser.TryParse outputs ({consumed}:#,0) is not {length:#,0}!"); + source.Advance(consumed); requestHint = 0; - source.Advance(length); return result; } finally @@ -207,20 +253,8 @@ private double DecodeNumber(in SequenceReader source, out int requestHint) } } - private static ReadOnlySpan GetSpan(in ReadOnlySequence source, Span buffer) - { - if (source.IsSingleSegment) - { - Debug.Assert(source.FirstSpan.Length == source.Length, "source.FirstSpan.Length == source.Length"); - return source.FirstSpan; - } - - Debug.Assert(source.Length == buffer.Length, "source.Length == buffer.Length"); - source.CopyTo(buffer); - return buffer; - } - - private static long ReadAtLeastOneDigits(in SequenceReader source, out int requestHint) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static long ReadAtLeastOneDigits(ref SequenceReader source, ReadOnlySpan digits, out int requestHint) { if (source.End) { @@ -228,20 +262,21 @@ private static long ReadAtLeastOneDigits(in SequenceReader source, out int return default; } - var decimalLength = source.AdvancePastAny(JsonNumberTokens.Digits); + var decimalLength = source.AdvancePastAny(digits); if (decimalLength == 0) { - ThrowInvalidNumber(source); + ThrowInvalidNumber(ref source); } requestHint = 0; return decimalLength; } - private static void ThrowInvalidNumber(in SequenceReader source) + [MethodImpl(MethodImplOptions.NoInlining)] + private static void ThrowInvalidNumber(ref SequenceReader source) { var position = source.Consumed; - if (TryGetUtf8Unit(source, out var unit) == Utf8UnitStatus.Valid) + if (TryGetUtf8Unit(ref source, out var unit) == Utf8UnitStatus.Valid) { JsonThrow.IsNotType(typeof(double), unit, position); } diff --git a/src/MsgPack.Json/Json/JsonDecoder.Numbers.cs b/src/MsgPack.Json/Json/JsonDecoder.Numbers.cs index 1eff27f97..0be7af301 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.Numbers.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.Numbers.cs @@ -17,123 +17,123 @@ partial class JsonDecoder { /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Byte DecodeByte(in SequenceReader source, out int requestHint) + public sealed override Byte DecodeByte(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeByteCore(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeByteCore(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private Byte DecodeByteCore(in SequenceReader source, out int requestHint) - => (Byte)this.DecodeNumber(source, out requestHint); + private Byte DecodeByteCore(ref SequenceReader source, out int requestHint) + => (Byte)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override SByte DecodeSByte(in SequenceReader source, out int requestHint) + public sealed override SByte DecodeSByte(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeSByteCore(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeSByteCore(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private SByte DecodeSByteCore(in SequenceReader source, out int requestHint) - => (SByte)this.DecodeNumber(source, out requestHint); + private SByte DecodeSByteCore(ref SequenceReader source, out int requestHint) + => (SByte)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int16 DecodeInt16(in SequenceReader source, out int requestHint) + public sealed override Int16 DecodeInt16(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeInt16Core(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeInt16Core(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private Int16 DecodeInt16Core(in SequenceReader source, out int requestHint) - => (Int16)this.DecodeNumber(source, out requestHint); + private Int16 DecodeInt16Core(ref SequenceReader source, out int requestHint) + => (Int16)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt16 DecodeUInt16(in SequenceReader source, out int requestHint) + public sealed override UInt16 DecodeUInt16(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeUInt16Core(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeUInt16Core(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private UInt16 DecodeUInt16Core(in SequenceReader source, out int requestHint) - => (UInt16)this.DecodeNumber(source, out requestHint); + private UInt16 DecodeUInt16Core(ref SequenceReader source, out int requestHint) + => (UInt16)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int32 DecodeInt32(in SequenceReader source, out int requestHint) + public sealed override Int32 DecodeInt32(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeInt32Core(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeInt32Core(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private Int32 DecodeInt32Core(in SequenceReader source, out int requestHint) - => (Int32)this.DecodeNumber(source, out requestHint); + private Int32 DecodeInt32Core(ref SequenceReader source, out int requestHint) + => (Int32)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt32 DecodeUInt32(in SequenceReader source, out int requestHint) + public sealed override UInt32 DecodeUInt32(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeUInt32Core(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeUInt32Core(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private UInt32 DecodeUInt32Core(in SequenceReader source, out int requestHint) - => (UInt32)this.DecodeNumber(source, out requestHint); + private UInt32 DecodeUInt32Core(ref SequenceReader source, out int requestHint) + => (UInt32)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Int64 DecodeInt64(in SequenceReader source, out int requestHint) + public sealed override Int64 DecodeInt64(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeInt64Core(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeInt64Core(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private Int64 DecodeInt64Core(in SequenceReader source, out int requestHint) - => (Int64)this.DecodeNumber(source, out requestHint); + private Int64 DecodeInt64Core(ref SequenceReader source, out int requestHint) + => (Int64)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override UInt64 DecodeUInt64(in SequenceReader source, out int requestHint) + public sealed override UInt64 DecodeUInt64(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeUInt64Core(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeUInt64Core(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private UInt64 DecodeUInt64Core(in SequenceReader source, out int requestHint) - => (UInt64)this.DecodeNumber(source, out requestHint); + private UInt64 DecodeUInt64Core(ref SequenceReader source, out int requestHint) + => (UInt64)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Single DecodeSingle(in SequenceReader source, out int requestHint) + public sealed override Single DecodeSingle(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeSingleCore(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeSingleCore(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private Single DecodeSingleCore(in SequenceReader source, out int requestHint) - => (Single)this.DecodeNumber(source, out requestHint); + private Single DecodeSingleCore(ref SequenceReader source, out int requestHint) + => (Single)this.DecodeNumber(ref source, out requestHint); /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override Double DecodeDouble(in SequenceReader source, out int requestHint) + public sealed override Double DecodeDouble(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.DecodeDoubleCore(source, out requestHint); + this.ReadTrivia(ref source); + return this.DecodeDoubleCore(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private Double DecodeDoubleCore(in SequenceReader source, out int requestHint) - => (Double)this.DecodeNumber(source, out requestHint); + private Double DecodeDoubleCore(ref SequenceReader source, out int requestHint) + => (Double)this.DecodeNumber(ref source, out requestHint); } } diff --git a/src/MsgPack.Json/Json/JsonDecoder.Numbers.tt b/src/MsgPack.Json/Json/JsonDecoder.Numbers.tt index 70674fd15..5372860bd 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.Numbers.tt +++ b/src/MsgPack.Json/Json/JsonDecoder.Numbers.tt @@ -37,15 +37,15 @@ foreach (var type in #> /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override <#= type #> Decode<#= type #>(in SequenceReader source, out int requestHint) + public sealed override <#= type #> Decode<#= type #>(ref SequenceReader source, out int requestHint) { - this.ReadTrivia(source, out _); - return this.Decode<#= type #>Core(source, out requestHint); + this.ReadTrivia(ref source); + return this.Decode<#= type #>Core(ref source, out requestHint); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private <#= type #> Decode<#= type #>Core(in SequenceReader source, out int requestHint) - => (<#= type #>)this.DecodeNumber(source, out requestHint); + private <#= type #> Decode<#= type #>Core(ref SequenceReader source, out int requestHint) + => (<#= type #>)this.DecodeNumber(ref source, out requestHint); <# } // foreach (var type) diff --git a/src/MsgPack.Json/Json/JsonDecoder.SkipDrain.cs b/src/MsgPack.Json/Json/JsonDecoder.SkipDrain.cs index c7c6fb4a5..d45e0c7d5 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.SkipDrain.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.SkipDrain.cs @@ -1,4 +1,4 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. +// Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. @@ -11,14 +11,14 @@ namespace MsgPack.Json { public partial class JsonDecoder { - public override void Drain(in SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default) + public override void Drain(ref SequenceReader source, in CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken = default) => JsonThrow.DrainIsNotSupported(out requestHint); - public override void Skip(in SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default) + public override void Skip(ref SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default) { var originalPosition = source.Consumed; - if(!this.DecodeItem(source, out var decodeItemResult, cancellationToken)) + if(!this.DecodeItem(ref source, out var decodeItemResult, cancellationToken)) { requestHint = (int)(decodeItemResult.RequestHint & Int32.MaxValue); return; @@ -30,7 +30,8 @@ public override void Skip(in SequenceReader source, in CollectionContext c case ElementType.Map: { // Skip current collection with CollectionIterator.Drain() - if(!decodeItemResult.CollectionIterator.Drain(source, out requestHint)) + var iterator = decodeItemResult.CollectionIterator; + if (!iterator.Drain(ref source, out requestHint)) { source.Rewind(source.Consumed - originalPosition); return; diff --git a/src/MsgPack.Json/Json/JsonDecoder.Strings.cs b/src/MsgPack.Json/Json/JsonDecoder.Strings.cs index 616a6f0aa..e728da428 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.Strings.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.Strings.cs @@ -16,9 +16,10 @@ namespace MsgPack.Json public partial class JsonDecoder { /// - public override bool GetRawString(in SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override bool GetRawString(ref SequenceReader source, out ReadOnlySpan rawString, out int requestHint, CancellationToken cancellationToken = default) { - if (!this.GetRawStringCore(source, out var rawStringSequence, out requestHint, cancellationToken)) + if (!this.GetRawStringCore(ref source, out var rawStringSequence, out requestHint, cancellationToken)) { rawString = default; return false; @@ -39,105 +40,127 @@ public override bool GetRawString(in SequenceReader source, out ReadOnlySp return true; } - private bool GetRawStringCore(in SequenceReader source, out ReadOnlySequence rawString, out int requestHint, CancellationToken cancellationToken = default) + private bool GetRawStringCore(ref SequenceReader source, out ReadOnlySequence rawString, out int requestHint, CancellationToken cancellationToken = default) { - this.ReadTrivia(source, out _); + this.ReadTrivia(ref source); var startOffset = source.Consumed; - var quotation = this.ReadStringStart(source, out var delimiters, out requestHint); + var quotation = this.ReadStringStart(ref source, out requestHint); if (requestHint != 0) { rawString = default; return false; } - var length = 0L; - var rawStringStart = source.Position; - // We accept unescaped charactors except quotation even if the value is '\0' because we can handle them correctly. - while (true) + if (!source.TryReadTo(out ReadOnlySequence sequence, quotation, (byte)'\\', advancePastDelimiter: false)) { - cancellationToken.ThrowIfCancellationRequested(); - - if (!source.TryReadToAny(out ReadOnlySequence sequence, delimiters, advancePastDelimiter: false)) - { - // EoF - requestHint = 1; - break; - } - - if (sequence.Length + length > this.Options.MaxStringLengthInBytes) - { - Throw.StringLengthExceeded(source.Consumed - sequence.Length, sequence.Length + length, this.Options.MaxBinaryLengthInBytes); - } - - length += (int)sequence.Length; - - // Handle delimiter - source.TryRead(out var delimiter); - if (delimiter == quotation) - { - // End - requestHint = 0; - rawString = source.Sequence.Slice(rawStringStart, length); - return true; - } - - // Decode escape sequence - if (source.End) - { - // EoF - requestHint = 2; - break; - } + // EoF + requestHint = 1; + rawString = default; + source.Rewind(source.Consumed - startOffset); + return false; } - // No closing quotation. - rawString = default; - source.Rewind(source.Consumed - startOffset); - return false; + rawString = sequence; + source.Advance(1); // skip end quote + return true; } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override string? DecodeNullableString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) + public sealed override string? DecodeNullableString(ref SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeStringCore(source, out requestHint, cancellationToken); + return this.DecodeStringCore(ref source, out requestHint, cancellationToken); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override string? DecodeString(in SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) + public sealed override string? DecodeString(ref SequenceReader source, out int requestHint, Encoding? encoding = null, CancellationToken cancellationToken = default) { - this.ReadTrivia(source, out _); - return this.DecodeStringCore(source, out requestHint, cancellationToken); + this.ReadTrivia(ref source); + return this.DecodeStringCore(ref source, out requestHint, cancellationToken); } - private string? DecodeStringCore(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private string? DecodeStringCore(ref SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) { var startOffset = source.Consumed; - var quotation = this.ReadStringStart(source, out var delimiters, out requestHint); + var quotation = this.ReadStringStart(ref source, out requestHint); if (requestHint != 0) { return default; } - var length = 0L; + // fast-path + if (!source.TryReadTo(out ReadOnlySequence sequence, quotation, (byte)'\\', advancePastDelimiter: false) || !source.TryRead(out var delimiter)) + { + // EoF + requestHint = 1; + source.Rewind(source.Consumed - startOffset); + return default; + } + + if (sequence.Length > this.Options.MaxStringLengthInBytes) + { + Throw.StringLengthExceeded(source.Consumed - sequence.Length, sequence.Length, this.Options.MaxBinaryLengthInBytes); + } + + if (delimiter == quotation) + { + return Encoding.UTF8.GetString(sequence); + } + + return this.DecodeStringCoreSlow(ref source, out requestHint, startOffset, quotation, ref sequence, delimiter, cancellationToken); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private string? DecodeStringCoreSlow(ref SequenceReader source, out int requestHint, long startOffset, byte quotation, ref ReadOnlySequence sequence, byte delimiter, CancellationToken cancellationToken) + { + var length = sequence.Length; using (var result = new StringBuilderBufferWriter(new StringBuilder(), this.Options)) { + Encoding.UTF8.GetChars(sequence, result); + length += (int)sequence.Length; + // We accept unescaped charactors except quotation even if the value is '\0' because we can handle them correctly. while (true) { + // Decode escape sequence + if (source.End) + { + // EoF + requestHint = 2; + break; + } + + if (!source.IsNext((byte)'u', advancePast: true)) + { + DecodeSpetialEscapeSequence(ref source, result); + length += 2; + } + else + { + DecodeUnicodeEscapceSequence(source, result, out requestHint); + if (requestHint != 0) + { + source.Rewind(source.Consumed - startOffset); + return default; + } + + length += 6; + } + cancellationToken.ThrowIfCancellationRequested(); - if (!source.TryReadToAny(out ReadOnlySequence sequence, delimiters, advancePastDelimiter: false)) + if (!source.TryReadTo(out sequence, quotation, (byte)'\\', advancePastDelimiter: false)) { // EoF requestHint = 1; @@ -155,38 +178,13 @@ private bool GetRawStringCore(in SequenceReader source, out ReadOnlySequen length += (int)sequence.Length; // Handle delimiter - source.TryRead(out var delimiter); + source.TryRead(out delimiter); if (delimiter == quotation) { // End requestHint = 0; return result.ToString(); } - - // Decode escape sequence - if (source.End) - { - // EoF - requestHint = 2; - break; - } - - if (!source.IsNext((byte)'u', advancePast: true)) - { - DecodeSpetialEscapeSequence(source, result); - length += 2; - } - else - { - DecodeUnicodeEscapceSequence(source, result, out requestHint); - if (requestHint != 0) - { - source.Rewind(source.Consumed - startOffset); - return default; - } - - length += 6; - } } } @@ -194,18 +192,20 @@ private bool GetRawStringCore(in SequenceReader source, out ReadOnlySequen return default; } - private byte ReadStringStart(in SequenceReader source, out ReadOnlySpan delimiters, out int requestHint) + private byte ReadStringStart(ref SequenceReader source, out int requestHint) { if (source.End) { requestHint = 2; - delimiters = default; return default; } + requestHint = 0; + if (source.IsNext((byte)'"', advancePast: false)) { - delimiters = JsonStringTokens.DoubleQuotationDelimiters; + source.Advance(1); + return (byte)'"'; } else { @@ -214,42 +214,78 @@ private byte ReadStringStart(in SequenceReader source, out ReadOnlySpan source, StringBuilderBufferWriter result) + private static void DecodeSpetialEscapeSequence(ref SequenceReader source, StringBuilderBufferWriter result) { source.TryPeek(out var escaped); - // Handle known escape sequence - var index = escaped - JsonStringTokens.UnescapedCharactorsOffset; - var unescaped = Byte.MaxValue; - if (index >= 0 && index < JsonStringTokens.UnescapedCharactors.Length) - { - unescaped = JsonStringTokens.UnescapedCharactors[index]; - } - - if (unescaped >= 0x80) + char unescaped; + switch(escaped) { - JsonThrow.InvalidEscapeSequence(source.Consumed, escaped); + case (byte)'b': + { + unescaped = '\b'; + break; + } + case (byte)'t': + { + unescaped = '\t'; + break; + } + case (byte)'f': + { + unescaped = '\f'; + break; + } + case (byte)'r': + { + unescaped = '\r'; + break; + } + case (byte)'n': + { + unescaped = '\n'; + break; + } + case (byte)'"': + { + unescaped = '"'; + break; + } + case (byte)'\\': + { + unescaped = '\\'; + break; + } + case (byte)'/': + { + unescaped = '/'; + break; + } + default: + { + JsonThrow.InvalidEscapeSequence(source.Consumed - 1, escaped); + // never + unescaped = default; + break; + } } result.AppendUtf16CodePoint(unescaped); @@ -303,7 +339,14 @@ private static void DecodeUnicodeEscapceSequence(SequenceReader source, St JsonThrow.OrphanSurrogate(positionOf1stSurrogate, codePointOrHighSurrogate); } - source.Sequence.Slice(source.Consumed, 4).CopyTo(buffer); + if(!source.TryCopyTo(buffer)) + { + requestHint = buffer.Length - (int)source.Remaining; + return; + } + + source.Advance(4); + if (!Utf8Parser.TryParse(buffer, out int shouldBeLowSurrogate, out consumed, standardFormat: 'X')) { JsonThrow.InvalidUnicodeEscapeSequence(source.Consumed, buffer); @@ -326,31 +369,31 @@ private static void DecodeUnicodeEscapceSequence(SequenceReader source, St /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override byte[]? DecodeNullableBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + public sealed override byte[]? DecodeNullableBinary(ref SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) { - this.ReadTrivia(source, out _); - if (this.TryReadNull(source)) + this.ReadTrivia(ref source); + if (this.TryDecodeNull(ref source)) { requestHint = 0; return null; } - return this.DecodeBinaryCore(source, out requestHint, cancellationToken); + return this.DecodeBinaryCore(ref source, out requestHint, cancellationToken); } /// [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override byte[]? DecodeBinary(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + public sealed override byte[]? DecodeBinary(ref SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) { - this.ReadTrivia(source, out _); - return this.DecodeBinaryCore(source, out requestHint, cancellationToken); + this.ReadTrivia(ref source); + return this.DecodeBinaryCore(ref source, out requestHint, cancellationToken); } - private byte[]? DecodeBinaryCore(in SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) + private byte[]? DecodeBinaryCore(ref SequenceReader source, out int requestHint, CancellationToken cancellationToken = default) { var startOffset = source.Consumed; - var quotation = this.ReadStringStart(source, out _, out requestHint); + var quotation = this.ReadStringStart(ref source, out requestHint); if (requestHint != 0) { return default; diff --git a/src/MsgPack.Json/Json/JsonDecoder.cs b/src/MsgPack.Json/Json/JsonDecoder.cs index 87fc25bf5..aa17d1b2d 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.cs @@ -43,7 +43,7 @@ public static JsonDecoder Create(JsonDecoderOptions options) } } - private protected static Utf8UnitStatus TryGetUtf8Unit(in SequenceReader source, out ReadOnlySequence unit) + private protected static Utf8UnitStatus TryGetUtf8Unit(ref SequenceReader source, out ReadOnlySequence unit) { if (source.UnreadSpan.IsEmpty) { @@ -55,7 +55,7 @@ private protected static Utf8UnitStatus TryGetUtf8Unit(in SequenceReader s if ((utf80 & 0b_1000_0000) == 0) { // 1byte - unit = source.Sequence.Slice(source.Consumed, 1); + unit = source.Sequence.Slice(source.Position, 1); return Utf8UnitStatus.Valid; } else if ((utf80 & 0b_1110_0000) == 0b_1100_0000) @@ -63,11 +63,11 @@ private protected static Utf8UnitStatus TryGetUtf8Unit(in SequenceReader s Span utf8 = stackalloc byte[2]; if (!source.TryCopyTo(utf8)) { - unit = source.Sequence.Slice(source.Consumed, source.Remaining); + unit = source.Sequence.Slice(source.Position); return Utf8UnitStatus.TooShort; } - unit = source.Sequence.Slice(source.Consumed, 2); + unit = source.Sequence.Slice(source.Position, 2); // 2 bytes, 5 + 6 bits var bits1 = utf8[0] & 0b_0001_1111; @@ -86,11 +86,11 @@ private protected static Utf8UnitStatus TryGetUtf8Unit(in SequenceReader s Span utf8 = stackalloc byte[3]; if (!source.TryCopyTo(utf8)) { - unit = source.Sequence.Slice(source.Consumed, source.Remaining); + unit = source.Sequence.Slice(source.Position); return Utf8UnitStatus.TooShort; } - unit = source.Sequence.Slice(source.Consumed, 3); + unit = source.Sequence.Slice(source.Position, 3); if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 || (utf8[2] & 0b_1100_0000) != 0b_1000_0000) @@ -117,11 +117,11 @@ private protected static Utf8UnitStatus TryGetUtf8Unit(in SequenceReader s Span utf8 = stackalloc byte[4]; if (!source.TryCopyTo(utf8)) { - unit = source.Sequence.Slice(source.Consumed, source.Remaining); + unit = source.Sequence.Slice(source.Position); return Utf8UnitStatus.TooShort; } - unit = source.Sequence.Slice(source.Consumed, 4); + unit = source.Sequence.Slice(source.Position, 4); if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 || (utf8[2] & 0b_1100_0000) != 0b_1000_0000 @@ -147,14 +147,12 @@ private protected static Utf8UnitStatus TryGetUtf8Unit(in SequenceReader s } else { - unit = source.Sequence.Slice(source.Consumed, 1); + unit = source.Sequence.Slice(source.Position, 1); return Utf8UnitStatus.Invalid; } } - protected abstract void ReadTrivia(in SequenceReader source, out ReadOnlySequence trivia); - - protected abstract bool TryReadNull(in SequenceReader source); + protected abstract long ReadTrivia(ref SequenceReader source); protected enum Utf8UnitStatus { diff --git a/src/MsgPack.Json/Json/JsonDecoderOptions.cs b/src/MsgPack.Json/Json/JsonDecoderOptions.cs index eb4e36dbc..6ff0dc68e 100644 --- a/src/MsgPack.Json/Json/JsonDecoderOptions.cs +++ b/src/MsgPack.Json/Json/JsonDecoderOptions.cs @@ -11,6 +11,8 @@ namespace MsgPack.Json /// public class JsonDecoderOptions : DecoderOptions { + public static JsonDecoderOptions Default { get; } = new JsonDecoderOptionsBuilder().Build(); + public JsonParseOptions ParseOptions { get; } public JsonDecoderOptions(JsonDecoderOptionsBuilder builder) diff --git a/src/MsgPack.Json/Json/JsonEncoder.cs b/src/MsgPack.Json/Json/JsonEncoder.cs index ab21260ad..d70a1bfa3 100644 --- a/src/MsgPack.Json/Json/JsonEncoder.cs +++ b/src/MsgPack.Json/Json/JsonEncoder.cs @@ -5,6 +5,7 @@ using System; using System.Buffers; using System.Buffers.Text; +using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Text; @@ -47,23 +48,15 @@ public JsonEncoder(JsonEncoderOptions options) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private static int WriteNull(Span span) - { - if (span.Length < 4) - { - return -1; - } - - JsonTokens.Null.CopyTo(span); - return 4; - } + public sealed override void EncodeRawString(ReadOnlySpan rawString, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) + => Ensure.NotNull(buffer).Write(rawString); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeInt32(int value, IBufferWriter buffer) + public sealed override void EncodeInt32(int value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - var span = buffer.GetSpan(); + var span = buffer.GetSpan(11); while (true) { if (!Utf8Formatter.TryFormat(value, span, out var used)) @@ -79,11 +72,11 @@ public override void EncodeInt32(int value, IBufferWriter buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeUInt32(uint value, IBufferWriter buffer) + public sealed override void EncodeUInt32(uint value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - var span = buffer.GetSpan(); + var span = buffer.GetSpan(10); while (true) { if (!Utf8Formatter.TryFormat(value, span, out var used)) @@ -99,11 +92,11 @@ public override void EncodeUInt32(uint value, IBufferWriter buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeInt64(long value, IBufferWriter buffer) + public sealed override void EncodeInt64(long value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - var span = buffer.GetSpan(); + var span = buffer.GetSpan(20); while (true) { if (!Utf8Formatter.TryFormat(value, span, out var used)) @@ -119,11 +112,11 @@ public override void EncodeInt64(long value, IBufferWriter buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeUInt64(ulong value, IBufferWriter buffer) + public sealed override void EncodeUInt64(ulong value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - var span = buffer.GetSpan(); + var span = buffer.GetSpan(19); while (true) { if (!Utf8Formatter.TryFormat(value, span, out var used)) @@ -139,152 +132,186 @@ public override void EncodeUInt64(ulong value, IBufferWriter buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeSingle(float value, IBufferWriter buffer) + public sealed override void EncodeSingle(float value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); - - var formatter = - Single.IsNaN(value) ? - this._singleNanFormatter : - Single.IsInfinity(value) ? - this._singleInfinityFormatter : - JsonFormatter.Format; - formatter(value, buffer, this._options); + buffer = Ensure.NotNull(buffer); + + if (Single.IsNaN(value)) + { + this._singleNanFormatter(value, buffer, this._options); + } + else if (Single.IsInfinity(value)) + { + this._singleInfinityFormatter(value, buffer, this._options); + } + else + { + JsonFormatter.Format(value, buffer); + } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeDouble(double value, IBufferWriter buffer) + public sealed override void EncodeDouble(double value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); - - var formatter = - Double.IsNaN(value) ? - this._doubleNanFormatter : - Double.IsInfinity(value) ? - this._doubleInfinityFormatter : - JsonFormatter.Format; - formatter(value, buffer, this._options); + buffer = Ensure.NotNull(buffer); + + if (Double.IsNaN(value)) + { + this._doubleNanFormatter(value, buffer, this._options); + } + else if (Double.IsInfinity(value)) + { + this._doubleInfinityFormatter(value, buffer, this._options); + } + else + { + JsonFormatter.Format(value, buffer); + } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeBoolean(bool value, IBufferWriter buffer) + public sealed override void EncodeBoolean(bool value, IBufferWriter buffer) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); if (value) { - buffer.Write(JsonTokens.True); + var span = buffer.GetSpan(4); + span[0] = (byte)'t'; + span[1] = (byte)'r'; + span[2] = (byte)'u'; + span[3] = (byte)'e'; + buffer.Advance(4); } else { - buffer.Write(JsonTokens.False); + var span = buffer.GetSpan(5); + span[0] = (byte)'f'; + span[1] = (byte)'a'; + span[2] = (byte)'l'; + span[3] = (byte)'s'; + span[4] = (byte)'e'; + buffer.Advance(5); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeNull(IBufferWriter buffer) - => buffer.Write(JsonTokens.Null); + public sealed override void EncodeNull(IBufferWriter buffer) + => JsonFormatter.WriteNull(Ensure.NotNull(buffer)); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] private void WriteIndent(IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); - if (this._isPrettyPrint) { - buffer.Write(this._newLineChars.Span); - for (var i = 0; i < collectionContext.CurrentDepth; i++) - { - buffer.Write(this._indentChars.Span); - } + this.WriteIndentCore(buffer, collectionContext); + } + } + + private void WriteIndentCore(IBufferWriter buffer, CollectionContext collectionContext) + { + Debug.Assert(buffer != null); + buffer.Write(this._newLineChars.Span); + for (var i = 0; i < collectionContext.CurrentDepth; i++) + { + buffer.Write(this._indentChars.Span); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeArrayStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) + public sealed override void EncodeArrayStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - buffer.Write(JsonTokens.ArrayStart); - collectionContext.IncrementDepth(); + buffer.GetSpan(1)[0] = (byte)'['; + buffer.Advance(1); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeArrayEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext) + public sealed override void EncodeArrayEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - collectionContext.DecrementDepth(); this.WriteIndent(buffer, collectionContext); - buffer.Write(JsonTokens.ArrayEnd); + buffer.GetSpan(1)[0] = (byte)']'; + buffer.Advance(1); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeArrayItemStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) + public sealed override void EncodeArrayItemStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); this.WriteIndent(buffer, collectionContext); if (index > 0) { - buffer.Write(JsonTokens.Comma); + buffer.GetSpan(1)[0] = (byte)','; + buffer.Advance(1); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeArrayItemEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + public sealed override void EncodeArrayItemEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) + public sealed override void EncodeMapStart(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - buffer.Write(JsonTokens.MapStart); - collectionContext.IncrementDepth(); + buffer.GetSpan(1)[0] = (byte)'{'; + buffer.Advance(1); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext) + public sealed override void EncodeMapEnd(int length, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - collectionContext.DecrementDepth(); this.WriteIndent(buffer, collectionContext); - buffer.Write(JsonTokens.MapEnd); + buffer.GetSpan(1)[0] = (byte)'}'; + buffer.Advance(1); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapKeyStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) + public sealed override void EncodeMapKeyStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); this.WriteIndent(buffer, collectionContext); if (index > 0) { - buffer.Write(JsonTokens.Comma); + buffer.GetSpan(1)[0] = (byte)','; + buffer.Advance(1); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapKeyEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + public sealed override void EncodeMapKeyEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapValueStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) + public sealed override void EncodeMapValueStart(int index, IBufferWriter buffer, in CollectionContext collectionContext) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - buffer.Write(JsonTokens.Colon); - if (this._isPrettyPrint) + if (!this._isPrettyPrint) { - buffer.Write(JsonTokens.Whitespace); + buffer.GetSpan(1)[0] = (byte)':'; + buffer.Advance(1); + } + else + { + var span = buffer.GetSpan(2); + span[0] = (byte)':'; + span[1] = (byte)' '; + buffer.Advance(2); } } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeMapValueEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } + public sealed override void EncodeMapValueEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext) { } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private static ReadOnlySpan GetEscapeSequence(byte c, bool newLineAllowed) + private static ReadOnlySpan GetEscapeSequence(byte c, bool newLineAllowed, bool tabAllowed, bool escapeHtmlChars) { switch (c) { @@ -318,6 +345,11 @@ private static ReadOnlySpan GetEscapeSequence(byte c, bool newLineAllowed) } case (byte)'"': { + if (escapeHtmlChars) + { + return ReadOnlySpan.Empty; + } + return JsonEscapeSequence.Quatation; } case (byte)'\\': @@ -326,6 +358,12 @@ private static ReadOnlySpan GetEscapeSequence(byte c, bool newLineAllowed) } case (byte)'\t': { + if (newLineAllowed) + { + // Does not escape. + return JsonCharactor.Tab; + } + return JsonEscapeSequence.Tab; } } @@ -335,185 +373,185 @@ private static ReadOnlySpan GetEscapeSequence(byte c, bool newLineAllowed) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public override void EncodeString(ReadOnlySpan encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) + public sealed override void EncodeString(ReadOnlySpan encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); - buffer.Write(JsonTokens.Quatation); - - this.EncodeStringBody(encodedValue, 0, buffer); - - // End quot - buffer.Write(JsonTokens.Quatation); - } + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); - [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - private void EncodeStringBody(ReadOnlySpan encodedValue, long position, IBufferWriter buffer) - { var source = encodedValue; - while (!source.IsEmpty) + this.EncodeStringBody(ref source, 0, buffer, out var requestHint); + if (requestHint > 0) { - this.EscapeUtf8(ref source, ref position, buffer); + JsonThrow.TooShortUtf8(); } + + // End quot + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); } - private void EscapeUtf8(ref ReadOnlySpan utf8, ref long position, IBufferWriter buffer) + private void EncodeStringBody(ref ReadOnlySpan encodedValue, long position, IBufferWriter buffer, out int requestHint) { - if (utf8.IsEmpty) + var utf8 = encodedValue; + while (utf8.Length > 0) { - return; - } - - if ((utf8[0] & 0b_1000_0000) == 0) - { - // 1 byte, 7bits - byte codePoint = (byte)(utf8[0] & 0b_0111_1111); - - if (this._escapeTargetChars1Byte.Span.Contains(codePoint) - || codePoint < 0x09 // Control chars must be escaped except horizontal tab - || (this._options.EscapesHorizontalTab && codePoint == 0x09) - || (codePoint > 0x09 && codePoint < 0x20) // Control chars must be escaped except horizontal tab - || codePoint >= 0x7F // Control chars must be escaped (DEL) - ) + if ((utf8[0] & 0b_1000_0000) == 0) { - var escapeSequence = GetEscapeSequence(codePoint, newLineAllowed: false); - if (escapeSequence.IsEmpty) + // 1 byte, 7bits + byte codePoint = utf8[0]; + + if (this._escapeTargetChars1Byte.Span.Contains(codePoint) + || codePoint < 0x20 // Control chars must be escaped + || codePoint == 0x7F // Control chars must be escaped (DEL) + ) { - EscapeCodePoint(codePoint, buffer); + var escapeSequence = GetEscapeSequence(codePoint, newLineAllowed: false, tabAllowed: !this._options.EscapesHorizontalTab, this._options.EscapesHtmlChars); + if (escapeSequence.IsEmpty) + { + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(escapeSequence); + } } else { - buffer.Write(escapeSequence); + buffer.Write(utf8.Slice(0, 1)); } - } - else - { - buffer.Write(utf8.Slice(0, 1)); + + utf8 = utf8.Slice(1); + position += 1; } - utf8 = utf8.Slice(1); - position += 1; - } - else if ((utf8[0] & 0b_1110_0000) == 0b_1100_0000) - { - if (utf8.Length < 2) + else if ((utf8[0] & 0b_1110_0000) == 0b_1100_0000) { - JsonThrow.TooShortUtf8(); - } + if (utf8.Length < 2) + { + requestHint = 1; + return; + } - // 2 bytes, 5 + 6 bits - var bits1 = utf8[0] & 0b_0001_1111; - var bits2 = utf8[1] & 0b_0011_1111; + // 2 bytes, 5 + 6 bits + var bits1 = utf8[0] & 0b_0001_1111; + var bits2 = utf8[1] & 0b_0011_1111; - if ((bits1 & 0b_1_1110) == 0 - || (utf8[1] & 0b_1100_0000) != 0b_1000_0000) - { - JsonThrow.MalformedUtf8(utf8, position); - } + if ((bits1 & 0b_1_1110) == 0 + || (utf8[1] & 0b_1100_0000) != 0b_1000_0000) + { + JsonThrow.MalformedUtf8(utf8, position); + } - ushort codePoint = (ushort)((bits1 << 6) | (bits2 << 6)); + ushort codePoint = (ushort)((bits1 << 6) | (bits2 << 6)); - if (codePoint < 0xA0 // Control chars must be escaped - || this._escapeTargetChars2Byte.Span.Contains(codePoint)) - { - EscapeCodePoint(codePoint, buffer); + if (codePoint < 0xA0 // Control chars must be escaped + || this._escapeTargetChars2Byte.Span.Contains(codePoint)) + { + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(utf8.Slice(0, 1)); + } + + utf8 = utf8.Slice(2); + position += 2; } - else + else if ((utf8[0] & 0b_1111_0000) == 0b_1110_0000) { - buffer.Write(utf8.Slice(0, 1)); - } + if (utf8.Length < 3) + { + requestHint = 3 - utf8.Length; + return; + } - utf8 = utf8.Slice(2); - position += 2; - } - else if ((utf8[0] & 0b_1111_0000) == 0b_1110_0000) - { - if (utf8.Length < 3) - { - JsonThrow.TooShortUtf8(); - } + if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[2] & 0b_1100_0000) != 0b_1000_0000) + { + JsonThrow.MalformedUtf8(utf8, position); + } - if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 - || (utf8[2] & 0b_1100_0000) != 0b_1000_0000) - { - JsonThrow.MalformedUtf8(utf8, position); - } + // 3 bytes, 4 + 6 + 6 bits + var bits1 = utf8[0] & 0b_0000_1111; + var bits2 = utf8[1] & 0b_0011_1111; + var bits3 = utf8[2] & 0b_0011_1111; - // 3 bytes, 4 + 6 + 6 bits - var bits1 = utf8[0] & 0b_0000_1111; - var bits2 = utf8[1] & 0b_0011_1111; - var bits3 = utf8[2] & 0b_0011_1111; + ushort codePoint = (ushort)((bits1 << 12) | (bits2 << 6) | bits3); - ushort codePoint = (ushort)((bits1 << 12) | (bits2 << 6) | bits3); + if ((codePoint & 0b_1111_100000_000000) == 0) + { + JsonThrow.MalformedUtf8(utf8, position); + } - if ((codePoint & 0b_1111_100000_000000) == 0) - { - JsonThrow.MalformedUtf8(utf8, position); - } + if (!Rune.IsValid(codePoint)) + { + JsonThrow.SurrogateCharInUtf8(position, codePoint); + } - if (!Rune.IsValid(codePoint)) - { - JsonThrow.SurrogateCharInUtf8(position, codePoint); - } + if (codePoint >= 0xFFFE // U+FFFE and U+FFFF are Reserved + || (this._options.EscapesPrivateUseCharactors && codePoint >= 0xE000 && codePoint <= 0xF8FF) // Privte Use + || this._escapeTargetChars2Byte.Span.Contains(codePoint)) + { + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(utf8.Slice(0, 1)); + } - if (codePoint >= 0xFFFE // U+FFFE and U+FFFF are Reserved - || (this._options.EscapesPrivateUseCharactors && codePoint >= 0xE000 && codePoint <= 0xF8FF) // Privte Use - || this._escapeTargetChars2Byte.Span.Contains(codePoint)) - { - EscapeCodePoint(codePoint, buffer); + utf8 = utf8.Slice(3); + position += 3; } - else + else if ((utf8[0] & 0b_1111_1000) == 0b_1111_0000) { - buffer.Write(utf8.Slice(0, 1)); - } + if (utf8.Length < 4) + { + requestHint = 4 - utf8.Length; + return; + } - utf8 = utf8.Slice(3); - position += 3; - } - else if ((utf8[0] & 0b_1111_1000) == 0b_1111_0000) - { - if (utf8.Length < 4) - { - JsonThrow.TooShortUtf8(); - } + if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[2] & 0b_1100_0000) != 0b_1000_0000 + || (utf8[3] & 0b_1100_0000) != 0b_1000_0000) + { + JsonThrow.MalformedUtf8(utf8, position); + } - if ((utf8[1] & 0b_1100_0000) != 0b_1000_0000 - || (utf8[2] & 0b_1100_0000) != 0b_1000_0000 - || (utf8[3] & 0b_1100_0000) != 0b_1000_0000) - { - JsonThrow.MalformedUtf8(utf8, position); - } + // 4 bytes, 3 + 6 + 6 + 6 bits + var bits1 = (utf8[0] & 0b_0000_0111) << 18; + var bits2 = (utf8[1] & 0b_0011_1111) << 12; + var bits3 = (utf8[2] & 0b_0011_1111) << 6; + var bits4 = (utf8[3] & 0b_0011_1111); - // 4 bytes, 3 + 6 + 6 + 6 bits - var bits1 = (utf8[0] & 0b_0000_0111) << 18; - var bits2 = (utf8[1] & 0b_0011_1111) << 12; - var bits3 = (utf8[2] & 0b_0011_1111) << 6; - var bits4 = (utf8[3] & 0b_0011_1111); + int codePoint = ((bits1 << 18) | (bits2 << 12) | (bits3 << 6) | bits4); - int codePoint = ((bits1 << 18) | (bits2 << 12) | (bits1 << 6) | bits4); + if ((codePoint & 0b_111_110000_000000_000000) == 0) + { + JsonThrow.MalformedUtf8(utf8, position); + } - if ((codePoint & 0b_111_110000_000000_000000) == 0) - { - JsonThrow.MalformedUtf8(utf8, position); - } + if ((this._options.EscapesPrivateUseCharactors && codePoint >= 0xF0000) // Private use + || this._escapeTargetChars4Byte.Span.Contains(codePoint)) + { + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(utf8.Slice(0, 1)); + } - if ((this._options.EscapesPrivateUseCharactors && codePoint >= 0xF0000) // Private use - || this._escapeTargetChars4Byte.Span.Contains(codePoint)) - { - EscapeCodePoint(codePoint, buffer); + utf8 = utf8.Slice(4); + position += 4; } else { - buffer.Write(utf8.Slice(0, 1)); + JsonThrow.MalformedUtf8(utf8, position); } - - utf8 = utf8.Slice(4); - position += 4; - } - else - { - JsonThrow.MalformedUtf8(utf8, position); } + + requestHint = 0; } private static void EscapeCodePoint(int codePoint, IBufferWriter buffer) @@ -524,130 +562,208 @@ private static void EscapeCodePoint(int codePoint, IBufferWriter buffer) buffer.Write(numbers); } - public override void EncodeString(in ReadOnlySequence encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) + public sealed override void EncodeString(in ReadOnlySequence encodedValue, int charLength, IBufferWriter buffer, CancellationToken cancellationToken = default) { - buffer = EnsureNotNull(buffer); + buffer = Ensure.NotNull(buffer); // Start quot - buffer.Write(JsonTokens.Quatation); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); var source = new SequenceReader(encodedValue); - while (!source.UnreadSpan.IsEmpty) + while (!source.End) { var length = source.UnreadSpan.Length; - this.EncodeStringBody(source.UnreadSpan, source.Consumed, buffer); - source.Advance(length); + var span = source.UnreadSpan; + this.EncodeStringBody(ref span, source.Consumed, buffer, out var requestHint); + source.Advance(length - span.Length); + + if (requestHint > 0) + { + Span codePoints = stackalloc byte[span.Length + requestHint]; + if (!source.TryCopyTo(codePoints)) + { + JsonThrow.TooShortUtf8(); + } + + source.Advance(codePoints.Length); + + ReadOnlySpan readOnlyCodePoints = codePoints; + this.EncodeStringBody(ref readOnlyCodePoints, source.Consumed, buffer, out requestHint); + } } // End quot - buffer.Write(JsonTokens.Quatation); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); } - public override void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) + public sealed override void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) { - buffer = EnsureNotNull(buffer); - encoding = encoding ?? Utf8EncodingNonBom.Instance; + buffer = Ensure.NotNull(buffer); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); + + var source = value; + this.EncodeStringBody(ref source, buffer, out var requestHint); + if (requestHint > 0) + { + JsonThrow.OrphanSurrogate(value.Length - source.Length, source[0]); + } + + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); + } - if (value.Length < 256) + public sealed override void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + if (value.IsSingleSegment) { - // Fast-path with stackalloc - var charLength = unchecked((int)value.Length); - Span encodingBuffer = stackalloc byte[encoding.GetMaxByteCount(charLength)]; - var actualLength = encoding.GetBytes(value, encodingBuffer); - this.EncodeString(encodingBuffer.Slice(0, actualLength), charLength, buffer); + this.EncodeString(value.FirstSpan, buffer, encoding); + return; } else { - // Slow-path + buffer = Ensure.NotNull(buffer); // Start quot - buffer.Write(JsonTokens.Quatation); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); - var encoder = encoding.GetEncoder(); - var source = value; + var source = new SequenceReader(value); - var encodingBuffer = base.Options.ByteBufferPool.Rent(Math.Min(2 * 1024 * 1024, encoding.GetMaxByteCount(unchecked((int)(value.Length & 0x1FFFFFFF))))); - try + while (!source.End) { - Span encodingSpan = encodingBuffer; - while (!source.IsEmpty) + var length = source.UnreadSpan.Length; + var span = source.UnreadSpan; + this.EncodeStringBody(ref span, buffer, out var requestHint); + source.Advance(length - span.Length); + + if (requestHint > 0) { - var length = source.Length; - encoder.Convert(source, encodingSpan, flush: source.Length <= encodingSpan.Length, out var charsUsed, out var bytesUsed, out _); + Span codePoints = stackalloc char[span.Length + requestHint]; + if (!source.TryCopyTo(codePoints)) + { + JsonThrow.TooShortUtf8(); + } - this.EncodeStringBody(encodingSpan.Slice(0, bytesUsed), position: 0 /* should not be used because ALWAYS valid UTF-8 */, buffer); - source = source.Slice(charsUsed); + source.Advance(codePoints.Length); + + ReadOnlySpan readOnlyCodePoints = codePoints; + this.EncodeStringBody(ref readOnlyCodePoints, buffer, out requestHint); } } - finally - { - base.Options.ByteBufferPool.Return(encodingBuffer, base.Options.ClearsBuffer); - } // End quot - buffer.Write(JsonTokens.Quatation); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); } } - public override void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) + private void EncodeStringBody(ref ReadOnlySpan chars, IBufferWriter buffer, out int requestHint) { - buffer = EnsureNotNull(buffer); - encoding = encoding ?? Utf8EncodingNonBom.Instance; - - if (value.IsSingleSegment) + var position = 0; + for(var i = 0; i < chars.Length; i++) { - this.EncodeString(value.FirstSpan, buffer, encoding); - return; - } - - if (value.Length < 256) - { - // Fast-path with stackalloc - var charLength = unchecked((int)value.Length); - Span encodingBuffer = stackalloc byte[encoding.GetMaxByteCount(charLength)]; - var actualLength = encoding.GetBytes(value, encodingBuffer); - this.EncodeString(encodingBuffer.Slice(0, actualLength), charLength, buffer); - } - else - { - // Slow-path + var c = chars[i]; + if (c < 0x80) + { + var codePoint = (byte)c; - // Start quot - buffer.Write(JsonTokens.Quatation); + if (this._escapeTargetChars1Byte.Span.Contains(codePoint) + || codePoint < 0x20 // Control chars must be escaped + || codePoint == 0x7F // Control chars must be escaped (DEL) + ) + { + Utf8EncodingNonBom.Instance.GetBytes(chars.Slice(0, i), buffer); + chars = chars.Slice(i + 1); + position += i + 1; + i = 0; + + var escapeSequence = GetEscapeSequence(codePoint, newLineAllowed: false, tabAllowed: !this._options.EscapesHorizontalTab, this._options.EscapesHtmlChars); + if (escapeSequence.IsEmpty) + { + EscapeCodePoint(codePoint, buffer); + } + else + { + buffer.Write(escapeSequence); + } + } + } + else if (c < 0x800) + { + ushort codePoint = c; - var encoder = encoding.GetEncoder(); - var source = new SequenceReader(value); + if (codePoint < 0xA0 // Control chars must be escaped + || this._escapeTargetChars2Byte.Span.Contains(codePoint)) + { + Utf8EncodingNonBom.Instance.GetBytes(chars.Slice(0, i), buffer); + chars = chars.Slice(i); + position += i + 1; + i = 0; - var encodingBuffer = base.Options.ByteBufferPool.Rent(Math.Min(2 * 1024 * 1024, encoding.GetMaxByteCount(unchecked((int)(value.Length & 0x1FFFFFFF))))); - try + EscapeCodePoint(codePoint, buffer); + } + } + else if (!Char.IsSurrogate(c)) { - Span encodingSpan = encodingBuffer; - while (!source.UnreadSpan.IsEmpty) - { - cancellationToken.ThrowIfCancellationRequested(); + ushort codePoint = c; - var length = source.UnreadSpan.Length; - encoder.Convert(source.UnreadSpan, encodingSpan, flush: source.Remaining <= encodingSpan.Length, out var charsUsed, out var bytesUsed, out _); + if (codePoint >= 0xFFFE // U+FFFE and U+FFFF are Reserved + || (this._options.EscapesPrivateUseCharactors && codePoint >= 0xE000 && codePoint <= 0xF8FF) // Privte Use + || this._escapeTargetChars2Byte.Span.Contains(codePoint)) + { + Utf8EncodingNonBom.Instance.GetBytes(chars.Slice(0, i), buffer); + chars = chars.Slice(i); + position += i + 1; + i = 0; - this.EncodeStringBody(encodingSpan.Slice(0, bytesUsed), position: 0 /* should not be used because ALWAYS valid UTF-8 */, buffer); - source.Advance(charsUsed); + EscapeCodePoint(codePoint, buffer); } } - finally + else { - base.Options.ByteBufferPool.Return(encodingBuffer, base.Options.ClearsBuffer); - } + // Surrogate + if(chars.Length < i + 2) + { + Utf8EncodingNonBom.Instance.GetBytes(chars.Slice(0, i), buffer); + chars = chars.Slice(i); + position += i; + i = 0; + requestHint = 1; + return; + } - // End quot - buffer.Write(JsonTokens.Quatation); + if(!Char.IsSurrogatePair(c, chars[1])) + { + JsonThrow.OrphanSurrogate(position, c); + } + + var codePoint = Char.ConvertToUtf32(c, chars[1]); + + if ((this._options.EscapesPrivateUseCharactors && codePoint >= 0xF0000) // Private use + || this._escapeTargetChars4Byte.Span.Contains(codePoint)) + { + EscapeCodePoint(codePoint, buffer); + } + + position += 2; + i++; + } } + + requestHint = 0; + Utf8EncodingNonBom.Instance.GetBytes(chars, buffer); + chars = ReadOnlySpan.Empty; } - public override void EncodeBinary(ReadOnlySpan value, IBufferWriter buffer, CancellationToken cancellationToken = default) + public sealed override void EncodeBinary(ReadOnlySpan value, IBufferWriter buffer, CancellationToken cancellationToken = default) { // Start quot - buffer.Write(JsonTokens.Quatation); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); if (value.Length < this.Options.CancellationSupportThreshold) { @@ -662,7 +778,8 @@ public override void EncodeBinary(ReadOnlySpan value, IBufferWriter } // End quot - buffer.Write(JsonTokens.Quatation); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); } [MethodImpl(MethodImplOptions.NoInlining)] @@ -682,7 +799,7 @@ private static void EncodeBinarySlow(ReadOnlySpan value, IBufferWriter value, IBufferWriter buffer, CancellationToken cancellationToken = default) + public sealed override void EncodeBinary(in ReadOnlySequence value, IBufferWriter buffer, CancellationToken cancellationToken = default) { if (value.IsSingleSegment) { @@ -692,7 +809,8 @@ public override void EncodeBinary(in ReadOnlySequence value, IBufferWriter } // Start quot - buffer.Write(JsonTokens.Quatation); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); var reader = new SequenceReader(value); while (true) @@ -709,7 +827,8 @@ public override void EncodeBinary(in ReadOnlySequence value, IBufferWriter } // End quot - buffer.Write(JsonTokens.Quatation); + buffer.GetSpan(1)[0] = (byte)'"'; + buffer.Advance(1); } } } diff --git a/src/MsgPack.Json/Json/JsonEncoderOptions.cs b/src/MsgPack.Json/Json/JsonEncoderOptions.cs index 66014e1cf..0fa00f966 100644 --- a/src/MsgPack.Json/Json/JsonEncoderOptions.cs +++ b/src/MsgPack.Json/Json/JsonEncoderOptions.cs @@ -13,19 +13,21 @@ namespace MsgPack.Json /// /// Defines encoder options for . /// - public class JsonEncoderOptions : EncoderOptions + public sealed class JsonEncoderOptions : EncoderOptions { + public static JsonEncoderOptions Default { get; } = new JsonEncoderOptionsBuilder().Build(); + private static readonly Action, JsonEncoderOptions> ErrorSingleInfinityFormatter = (value, buffer, options) => throw new ArgumentException($"Cannot serialize infinity ({value}) to JSON.", "value"); private static readonly Action, JsonEncoderOptions> MinMaxSingleInfinityFormatter = - (value, buffer, options) => JsonFormatter.Format(Single.IsPositiveInfinity(value) ? Single.MaxValue : Single.MinValue, buffer, options); + (value, buffer, _) => JsonFormatter.Format(Single.IsPositiveInfinity(value) ? Single.MaxValue : Single.MinValue, buffer); private static readonly Action, JsonEncoderOptions> ErrorDoubleInfinityFormatter = (value, buffer, options) => throw new ArgumentException($"Cannot serialize infinity ({value}) to JSON.", "value"); private static readonly Action, JsonEncoderOptions> MinMaxDoubleInfinityFormatter = - (value, buffer, options) => JsonFormatter.Format(Double.IsPositiveInfinity(value) ? Double.MaxValue : Double.MinValue, buffer, options); + (value, buffer, _) => JsonFormatter.Format(Double.IsPositiveInfinity(value) ? Double.MaxValue : Double.MinValue, buffer); private static readonly Action, JsonEncoderOptions> ErrorSingleNaNFormatter = (value, buffer, options) => throw new ArgumentException($"Cannot serialize NaN to JSON.", "value"); @@ -47,6 +49,7 @@ public class JsonEncoderOptions : EncoderOptions public ReadOnlyMemory NewLineChars { get; } public bool IsPrettyPrint { get; } public bool EscapesHorizontalTab { get; } + public bool EscapesHtmlChars { get; } public bool EscapesPrivateUseCharactors { get; } public ReadOnlyMemory EscapeTargetChars { get; } internal ReadOnlyMemory EscapeTargetChars1Byte { get; } @@ -92,11 +95,12 @@ public JsonEncoderOptions(JsonEncoderOptionsBuilder builder) this.IndentChars = builder.IndentChars; this.NewLineChars = builder.NewLineChars; this.IsPrettyPrint = builder.IsPrettyPrint; - var escapeTargetChars = builder.AdditionalEscapeTargetChars.ToArray().Concat(JsonCharactor.MustBeEscaped1Byte.Select(b => new Rune(b))).Distinct().OrderBy(r => r).ToArray(); + var escapeTargetChars = builder.AdditionalEscapeTargetChars.ToArray().Concat(JsonCharactor.MustBeEscaped1Byte.Select(b => new Rune(b))).Concat(builder.EscapesHtmlChars ? JsonCharactor.ShouldBeEscaped.ToArray() : Array.Empty()).Distinct().OrderBy(r => r).ToArray(); this.EscapeTargetChars = escapeTargetChars; this.EscapeTargetChars1Byte = escapeTargetChars.Where(r => r.Value <= Byte.MaxValue).Select(r => (byte)r.Value).ToArray(); this.EscapeTargetChars2Byte = escapeTargetChars.Where(r => r.Value > Byte.MaxValue && r.Value <= UInt16.MaxValue).Select(r => (ushort)r.Value).ToArray(); this.EscapeTargetChars4Byte = escapeTargetChars.Where(r => r.Value > UInt16.MaxValue).Select(r => r.Value).ToArray(); + this.EscapesHtmlChars = builder.EscapesHtmlChars; this.EscapesHorizontalTab = builder.EscapesHorizontalTab; this.EscapesPrivateUseCharactors = builder.EscapesPrivateUseCharactors; } diff --git a/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs b/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs index c68260e1d..812dc7680 100644 --- a/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs +++ b/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs @@ -103,7 +103,8 @@ public ReadOnlyMemory NewLineChars public ReadOnlyMemory AdditionalEscapeTargetChars { get; set; } public bool EscapesHorizontalTab { get; set; } = true; - public bool EscapesPrivateUseCharactors { get; set; } = true; + public bool EscapesPrivateUseCharactors { get; set; } = false; + public bool EscapesHtmlChars { get; set; } = false; internal Action, JsonEncoderOptions>? SingleInfinityFormatter; internal Action, JsonEncoderOptions>? DoubleInfinityFormatter; @@ -136,7 +137,7 @@ Action, JsonEncoderOptions> doubleNaNFormatter public JsonEncoderOptionsBuilder WithHtmlCharactorEscaping() { - this.AdditionalEscapeTargetChars = JsonCharactor.ShouldBeEscaped; + this.EscapesHtmlChars = true; return this; } diff --git a/src/MsgPack.Json/Json/JsonEscapeSequence.EscapeTables.cs b/src/MsgPack.Json/Json/JsonEscapeSequence.EscapeTables.cs new file mode 100644 index 000000000..bd248b274 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonEscapeSequence.EscapeTables.cs @@ -0,0 +1,2186 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Runtime.InteropServices; + +namespace MsgPack.Json +{ + partial class JsonEscapeSequence + { + + public static ReadOnlySpan BaseEscapeTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(BaseEscapeTableLittleEndian) : + MemoryMarshal.Cast(BaseEscapeTableBigEndian); + + public static ReadOnlySpan BaseEscapeLengthTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(BaseEscapeLengthTableLittleEndian) : + MemoryMarshal.Cast(BaseEscapeLengthTableBigEndian); + + public static ReadOnlySpan BaseEscapeTableBigEndian => + new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000000000, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000010000, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000020000, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000030000, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000040000, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000050000, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000060000, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000070000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x5C, // 0x5C62000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x5C, // 0x5C74000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x5C, // 0x5C6E000000000000, + 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000B0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x5C, // 0x5C66000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x5C, // 0x5C72000000000000, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000E0000, + 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000F0000, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000100000, + 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000110000, + 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000120000, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000130000, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000140000, + 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000150000, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000160000, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000170000, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000180000, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000190000, + 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001A0000, + 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001B0000, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001C0000, + 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001D0000, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001E0000, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001F0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, // 0x2000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, // 0x2100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5C, // 0x5C22000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, // 0x2300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, // 0x2400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, // 0x2500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, // 0x2600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, // 0x2700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, // 0x2800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, // 0x2900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, // 0x2A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, // 0x2B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, // 0x2C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, // 0x2D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, // 0x2E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2F, // 0x2F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, // 0x3000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, // 0x3100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, // 0x3200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, // 0x3300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, // 0x3400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, // 0x3500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, // 0x3600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, // 0x3700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, // 0x3800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, // 0x3900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, // 0x3A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, // 0x3B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, // 0x3C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, // 0x3D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, // 0x3E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, // 0x3F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // 0x4000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, // 0x4100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, // 0x4200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, // 0x4300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, // 0x4400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, // 0x4500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, // 0x4600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, // 0x4700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, // 0x4800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, // 0x4900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, // 0x4A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, // 0x4B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, // 0x4C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, // 0x4D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, // 0x4E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, // 0x4F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, // 0x5000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, // 0x5100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, // 0x5200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, // 0x5300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, // 0x5400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, // 0x5500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, // 0x5600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, // 0x5700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, // 0x5800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, // 0x5900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, // 0x5A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, // 0x5B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x5C, // 0x5C5C000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, // 0x5D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, // 0x5E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, // 0x5F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, // 0x6000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, // 0x6100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, // 0x6200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, // 0x6300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, // 0x6400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, // 0x6500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, // 0x6600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, // 0x6700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, // 0x6800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, // 0x6900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, // 0x6A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, // 0x6B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, // 0x6C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, // 0x6D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, // 0x6E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, // 0x6F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, // 0x7000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, // 0x7100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, // 0x7200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, // 0x7300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, // 0x7400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, // 0x7500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, // 0x7600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, // 0x7700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, // 0x7800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, // 0x7900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7A, // 0x7A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, // 0x7B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, // 0x7C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, // 0x7D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, // 0x7E00000000000000, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000007F0000, + }; + + public static ReadOnlySpan BaseEscapeTableLittleEndian => + new byte[] + { + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // 0x000001000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // 0x000002000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // 0x000003000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // 0x000004000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, // 0x000005000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // 0x000006000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // 0x000007000000755C, + 0x5C, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000625C, + 0x5C, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000745C, + 0x5C, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000006E5C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, // 0x00000B000000755C, + 0x5C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000665C, + 0x5C, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000725C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, // 0x00000E000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, // 0x00000F000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // 0x000010000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, // 0x000011000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, // 0x000012000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, // 0x000013000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, // 0x000014000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, // 0x000015000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, // 0x000016000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, // 0x000017000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, // 0x000018000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, // 0x000019000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, // 0x00001A000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, // 0x00001B000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, // 0x00001C000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, // 0x00001D000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, // 0x00001E000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, // 0x00001F000000755C, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000020, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000021, + 0x5C, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000225C, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000023, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000024, + 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000025, + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000026, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000027, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000028, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000029, + 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002A, + 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002B, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002C, + 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002D, + 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002E, + 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002F, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000030, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000031, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000032, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000033, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000034, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000035, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000036, + 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000037, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000038, + 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000039, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003A, + 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003B, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003C, + 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003D, + 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003E, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003F, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000040, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000041, + 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000042, + 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000043, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000044, + 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000045, + 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000046, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000047, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000048, + 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000049, + 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004A, + 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004B, + 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004C, + 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004D, + 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004E, + 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004F, + 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000050, + 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000051, + 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000052, + 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000053, + 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000054, + 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000055, + 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000056, + 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000057, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000058, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000059, + 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005A, + 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005B, + 0x5C, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000005C5C, + 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005D, + 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005E, + 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005F, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000060, + 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000061, + 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000062, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000063, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000064, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000065, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000066, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000067, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000068, + 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000069, + 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006A, + 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006B, + 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006C, + 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006D, + 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006E, + 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006F, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000070, + 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000071, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000072, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000073, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000074, + 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000075, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000076, + 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000077, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000078, + 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000079, + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007A, + 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007B, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007C, + 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007D, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007E, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, // 0x00007F000000755C, + }; + + public static ReadOnlySpan BaseEscapeLengthTableBigEndian => + new byte[] + { + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-00), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-01), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-02), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-03), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-04), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-05), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-06), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-07), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-62), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-74), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-6E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0B), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-66), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-72), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0F), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-10), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-11), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-12), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-13), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-14), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-15), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-16), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-17), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-18), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-19), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1A), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1B), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1C), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1D), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1F), + 0x00, 0x00, 0x00, 0x01, // 1 (20), + 0x00, 0x00, 0x00, 0x01, // 1 (21), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-22), + 0x00, 0x00, 0x00, 0x01, // 1 (23), + 0x00, 0x00, 0x00, 0x01, // 1 (24), + 0x00, 0x00, 0x00, 0x01, // 1 (25), + 0x00, 0x00, 0x00, 0x01, // 1 (26), + 0x00, 0x00, 0x00, 0x01, // 1 (27), + 0x00, 0x00, 0x00, 0x01, // 1 (28), + 0x00, 0x00, 0x00, 0x01, // 1 (29), + 0x00, 0x00, 0x00, 0x01, // 1 (2A), + 0x00, 0x00, 0x00, 0x01, // 1 (2B), + 0x00, 0x00, 0x00, 0x01, // 1 (2C), + 0x00, 0x00, 0x00, 0x01, // 1 (2D), + 0x00, 0x00, 0x00, 0x01, // 1 (2E), + 0x00, 0x00, 0x00, 0x01, // 1 (2F), + 0x00, 0x00, 0x00, 0x01, // 1 (30), + 0x00, 0x00, 0x00, 0x01, // 1 (31), + 0x00, 0x00, 0x00, 0x01, // 1 (32), + 0x00, 0x00, 0x00, 0x01, // 1 (33), + 0x00, 0x00, 0x00, 0x01, // 1 (34), + 0x00, 0x00, 0x00, 0x01, // 1 (35), + 0x00, 0x00, 0x00, 0x01, // 1 (36), + 0x00, 0x00, 0x00, 0x01, // 1 (37), + 0x00, 0x00, 0x00, 0x01, // 1 (38), + 0x00, 0x00, 0x00, 0x01, // 1 (39), + 0x00, 0x00, 0x00, 0x01, // 1 (3A), + 0x00, 0x00, 0x00, 0x01, // 1 (3B), + 0x00, 0x00, 0x00, 0x01, // 1 (3C), + 0x00, 0x00, 0x00, 0x01, // 1 (3D), + 0x00, 0x00, 0x00, 0x01, // 1 (3E), + 0x00, 0x00, 0x00, 0x01, // 1 (3F), + 0x00, 0x00, 0x00, 0x01, // 1 (40), + 0x00, 0x00, 0x00, 0x01, // 1 (41), + 0x00, 0x00, 0x00, 0x01, // 1 (42), + 0x00, 0x00, 0x00, 0x01, // 1 (43), + 0x00, 0x00, 0x00, 0x01, // 1 (44), + 0x00, 0x00, 0x00, 0x01, // 1 (45), + 0x00, 0x00, 0x00, 0x01, // 1 (46), + 0x00, 0x00, 0x00, 0x01, // 1 (47), + 0x00, 0x00, 0x00, 0x01, // 1 (48), + 0x00, 0x00, 0x00, 0x01, // 1 (49), + 0x00, 0x00, 0x00, 0x01, // 1 (4A), + 0x00, 0x00, 0x00, 0x01, // 1 (4B), + 0x00, 0x00, 0x00, 0x01, // 1 (4C), + 0x00, 0x00, 0x00, 0x01, // 1 (4D), + 0x00, 0x00, 0x00, 0x01, // 1 (4E), + 0x00, 0x00, 0x00, 0x01, // 1 (4F), + 0x00, 0x00, 0x00, 0x01, // 1 (50), + 0x00, 0x00, 0x00, 0x01, // 1 (51), + 0x00, 0x00, 0x00, 0x01, // 1 (52), + 0x00, 0x00, 0x00, 0x01, // 1 (53), + 0x00, 0x00, 0x00, 0x01, // 1 (54), + 0x00, 0x00, 0x00, 0x01, // 1 (55), + 0x00, 0x00, 0x00, 0x01, // 1 (56), + 0x00, 0x00, 0x00, 0x01, // 1 (57), + 0x00, 0x00, 0x00, 0x01, // 1 (58), + 0x00, 0x00, 0x00, 0x01, // 1 (59), + 0x00, 0x00, 0x00, 0x01, // 1 (5A), + 0x00, 0x00, 0x00, 0x01, // 1 (5B), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-5C), + 0x00, 0x00, 0x00, 0x01, // 1 (5D), + 0x00, 0x00, 0x00, 0x01, // 1 (5E), + 0x00, 0x00, 0x00, 0x01, // 1 (5F), + 0x00, 0x00, 0x00, 0x01, // 1 (60), + 0x00, 0x00, 0x00, 0x01, // 1 (61), + 0x00, 0x00, 0x00, 0x01, // 1 (62), + 0x00, 0x00, 0x00, 0x01, // 1 (63), + 0x00, 0x00, 0x00, 0x01, // 1 (64), + 0x00, 0x00, 0x00, 0x01, // 1 (65), + 0x00, 0x00, 0x00, 0x01, // 1 (66), + 0x00, 0x00, 0x00, 0x01, // 1 (67), + 0x00, 0x00, 0x00, 0x01, // 1 (68), + 0x00, 0x00, 0x00, 0x01, // 1 (69), + 0x00, 0x00, 0x00, 0x01, // 1 (6A), + 0x00, 0x00, 0x00, 0x01, // 1 (6B), + 0x00, 0x00, 0x00, 0x01, // 1 (6C), + 0x00, 0x00, 0x00, 0x01, // 1 (6D), + 0x00, 0x00, 0x00, 0x01, // 1 (6E), + 0x00, 0x00, 0x00, 0x01, // 1 (6F), + 0x00, 0x00, 0x00, 0x01, // 1 (70), + 0x00, 0x00, 0x00, 0x01, // 1 (71), + 0x00, 0x00, 0x00, 0x01, // 1 (72), + 0x00, 0x00, 0x00, 0x01, // 1 (73), + 0x00, 0x00, 0x00, 0x01, // 1 (74), + 0x00, 0x00, 0x00, 0x01, // 1 (75), + 0x00, 0x00, 0x00, 0x01, // 1 (76), + 0x00, 0x00, 0x00, 0x01, // 1 (77), + 0x00, 0x00, 0x00, 0x01, // 1 (78), + 0x00, 0x00, 0x00, 0x01, // 1 (79), + 0x00, 0x00, 0x00, 0x01, // 1 (7A), + 0x00, 0x00, 0x00, 0x01, // 1 (7B), + 0x00, 0x00, 0x00, 0x01, // 1 (7C), + 0x00, 0x00, 0x00, 0x01, // 1 (7D), + 0x00, 0x00, 0x00, 0x01, // 1 (7E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-7F), + }; + + public static ReadOnlySpan BaseEscapeLengthTableLittleEndian => + new byte[] + { + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-00), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-01), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-02), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-03), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-04), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-05), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-06), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-07), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-62), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-74), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-6E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0B), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-66), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-72), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0F), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-10), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-11), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-12), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-13), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-14), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-15), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-16), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-17), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-18), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-19), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1A), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1B), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1C), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1D), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1F), + 0x01, 0x00, 0x00, 0x00, // 1 (20), + 0x01, 0x00, 0x00, 0x00, // 1 (21), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-22), + 0x01, 0x00, 0x00, 0x00, // 1 (23), + 0x01, 0x00, 0x00, 0x00, // 1 (24), + 0x01, 0x00, 0x00, 0x00, // 1 (25), + 0x01, 0x00, 0x00, 0x00, // 1 (26), + 0x01, 0x00, 0x00, 0x00, // 1 (27), + 0x01, 0x00, 0x00, 0x00, // 1 (28), + 0x01, 0x00, 0x00, 0x00, // 1 (29), + 0x01, 0x00, 0x00, 0x00, // 1 (2A), + 0x01, 0x00, 0x00, 0x00, // 1 (2B), + 0x01, 0x00, 0x00, 0x00, // 1 (2C), + 0x01, 0x00, 0x00, 0x00, // 1 (2D), + 0x01, 0x00, 0x00, 0x00, // 1 (2E), + 0x01, 0x00, 0x00, 0x00, // 1 (2F), + 0x01, 0x00, 0x00, 0x00, // 1 (30), + 0x01, 0x00, 0x00, 0x00, // 1 (31), + 0x01, 0x00, 0x00, 0x00, // 1 (32), + 0x01, 0x00, 0x00, 0x00, // 1 (33), + 0x01, 0x00, 0x00, 0x00, // 1 (34), + 0x01, 0x00, 0x00, 0x00, // 1 (35), + 0x01, 0x00, 0x00, 0x00, // 1 (36), + 0x01, 0x00, 0x00, 0x00, // 1 (37), + 0x01, 0x00, 0x00, 0x00, // 1 (38), + 0x01, 0x00, 0x00, 0x00, // 1 (39), + 0x01, 0x00, 0x00, 0x00, // 1 (3A), + 0x01, 0x00, 0x00, 0x00, // 1 (3B), + 0x01, 0x00, 0x00, 0x00, // 1 (3C), + 0x01, 0x00, 0x00, 0x00, // 1 (3D), + 0x01, 0x00, 0x00, 0x00, // 1 (3E), + 0x01, 0x00, 0x00, 0x00, // 1 (3F), + 0x01, 0x00, 0x00, 0x00, // 1 (40), + 0x01, 0x00, 0x00, 0x00, // 1 (41), + 0x01, 0x00, 0x00, 0x00, // 1 (42), + 0x01, 0x00, 0x00, 0x00, // 1 (43), + 0x01, 0x00, 0x00, 0x00, // 1 (44), + 0x01, 0x00, 0x00, 0x00, // 1 (45), + 0x01, 0x00, 0x00, 0x00, // 1 (46), + 0x01, 0x00, 0x00, 0x00, // 1 (47), + 0x01, 0x00, 0x00, 0x00, // 1 (48), + 0x01, 0x00, 0x00, 0x00, // 1 (49), + 0x01, 0x00, 0x00, 0x00, // 1 (4A), + 0x01, 0x00, 0x00, 0x00, // 1 (4B), + 0x01, 0x00, 0x00, 0x00, // 1 (4C), + 0x01, 0x00, 0x00, 0x00, // 1 (4D), + 0x01, 0x00, 0x00, 0x00, // 1 (4E), + 0x01, 0x00, 0x00, 0x00, // 1 (4F), + 0x01, 0x00, 0x00, 0x00, // 1 (50), + 0x01, 0x00, 0x00, 0x00, // 1 (51), + 0x01, 0x00, 0x00, 0x00, // 1 (52), + 0x01, 0x00, 0x00, 0x00, // 1 (53), + 0x01, 0x00, 0x00, 0x00, // 1 (54), + 0x01, 0x00, 0x00, 0x00, // 1 (55), + 0x01, 0x00, 0x00, 0x00, // 1 (56), + 0x01, 0x00, 0x00, 0x00, // 1 (57), + 0x01, 0x00, 0x00, 0x00, // 1 (58), + 0x01, 0x00, 0x00, 0x00, // 1 (59), + 0x01, 0x00, 0x00, 0x00, // 1 (5A), + 0x01, 0x00, 0x00, 0x00, // 1 (5B), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-5C), + 0x01, 0x00, 0x00, 0x00, // 1 (5D), + 0x01, 0x00, 0x00, 0x00, // 1 (5E), + 0x01, 0x00, 0x00, 0x00, // 1 (5F), + 0x01, 0x00, 0x00, 0x00, // 1 (60), + 0x01, 0x00, 0x00, 0x00, // 1 (61), + 0x01, 0x00, 0x00, 0x00, // 1 (62), + 0x01, 0x00, 0x00, 0x00, // 1 (63), + 0x01, 0x00, 0x00, 0x00, // 1 (64), + 0x01, 0x00, 0x00, 0x00, // 1 (65), + 0x01, 0x00, 0x00, 0x00, // 1 (66), + 0x01, 0x00, 0x00, 0x00, // 1 (67), + 0x01, 0x00, 0x00, 0x00, // 1 (68), + 0x01, 0x00, 0x00, 0x00, // 1 (69), + 0x01, 0x00, 0x00, 0x00, // 1 (6A), + 0x01, 0x00, 0x00, 0x00, // 1 (6B), + 0x01, 0x00, 0x00, 0x00, // 1 (6C), + 0x01, 0x00, 0x00, 0x00, // 1 (6D), + 0x01, 0x00, 0x00, 0x00, // 1 (6E), + 0x01, 0x00, 0x00, 0x00, // 1 (6F), + 0x01, 0x00, 0x00, 0x00, // 1 (70), + 0x01, 0x00, 0x00, 0x00, // 1 (71), + 0x01, 0x00, 0x00, 0x00, // 1 (72), + 0x01, 0x00, 0x00, 0x00, // 1 (73), + 0x01, 0x00, 0x00, 0x00, // 1 (74), + 0x01, 0x00, 0x00, 0x00, // 1 (75), + 0x01, 0x00, 0x00, 0x00, // 1 (76), + 0x01, 0x00, 0x00, 0x00, // 1 (77), + 0x01, 0x00, 0x00, 0x00, // 1 (78), + 0x01, 0x00, 0x00, 0x00, // 1 (79), + 0x01, 0x00, 0x00, 0x00, // 1 (7A), + 0x01, 0x00, 0x00, 0x00, // 1 (7B), + 0x01, 0x00, 0x00, 0x00, // 1 (7C), + 0x01, 0x00, 0x00, 0x00, // 1 (7D), + 0x01, 0x00, 0x00, 0x00, // 1 (7E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-7F), + }; + + public static ReadOnlySpan BaseWithoutTabEscapeTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(BaseWithoutTabEscapeTableLittleEndian) : + MemoryMarshal.Cast(BaseWithoutTabEscapeTableBigEndian); + + public static ReadOnlySpan BaseWithoutTabEscapeLengthTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(BaseWithoutTabEscapeLengthTableLittleEndian) : + MemoryMarshal.Cast(BaseWithoutTabEscapeLengthTableBigEndian); + + public static ReadOnlySpan BaseWithoutTabEscapeTableBigEndian => + new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000000000, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000010000, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000020000, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000030000, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000040000, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000050000, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000060000, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000070000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x5C, // 0x5C62000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, // 0x0900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x5C, // 0x5C6E000000000000, + 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000B0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x5C, // 0x5C66000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x5C, // 0x5C72000000000000, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000E0000, + 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000F0000, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000100000, + 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000110000, + 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000120000, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000130000, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000140000, + 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000150000, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000160000, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000170000, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000180000, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000190000, + 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001A0000, + 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001B0000, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001C0000, + 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001D0000, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001E0000, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001F0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, // 0x2000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, // 0x2100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x5C, // 0x5C22000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, // 0x2300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, // 0x2400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, // 0x2500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, // 0x2600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, // 0x2700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, // 0x2800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, // 0x2900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, // 0x2A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, // 0x2B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, // 0x2C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, // 0x2D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, // 0x2E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2F, // 0x2F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, // 0x3000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, // 0x3100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, // 0x3200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, // 0x3300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, // 0x3400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, // 0x3500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, // 0x3600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, // 0x3700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, // 0x3800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, // 0x3900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, // 0x3A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, // 0x3B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, // 0x3C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, // 0x3D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, // 0x3E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, // 0x3F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // 0x4000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, // 0x4100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, // 0x4200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, // 0x4300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, // 0x4400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, // 0x4500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, // 0x4600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, // 0x4700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, // 0x4800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, // 0x4900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, // 0x4A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, // 0x4B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, // 0x4C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, // 0x4D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, // 0x4E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, // 0x4F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, // 0x5000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, // 0x5100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, // 0x5200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, // 0x5300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, // 0x5400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, // 0x5500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, // 0x5600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, // 0x5700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, // 0x5800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, // 0x5900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, // 0x5A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, // 0x5B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x5C, // 0x5C5C000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, // 0x5D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, // 0x5E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, // 0x5F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, // 0x6000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, // 0x6100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, // 0x6200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, // 0x6300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, // 0x6400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, // 0x6500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, // 0x6600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, // 0x6700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, // 0x6800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, // 0x6900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, // 0x6A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, // 0x6B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, // 0x6C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, // 0x6D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, // 0x6E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, // 0x6F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, // 0x7000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, // 0x7100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, // 0x7200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, // 0x7300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, // 0x7400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, // 0x7500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, // 0x7600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, // 0x7700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, // 0x7800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, // 0x7900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7A, // 0x7A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, // 0x7B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, // 0x7C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, // 0x7D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, // 0x7E00000000000000, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000007F0000, + }; + + public static ReadOnlySpan BaseWithoutTabEscapeTableLittleEndian => + new byte[] + { + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // 0x000001000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // 0x000002000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // 0x000003000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // 0x000004000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, // 0x000005000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // 0x000006000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // 0x000007000000755C, + 0x5C, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000625C, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000009, + 0x5C, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000006E5C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, // 0x00000B000000755C, + 0x5C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000665C, + 0x5C, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000725C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, // 0x00000E000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, // 0x00000F000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // 0x000010000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, // 0x000011000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, // 0x000012000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, // 0x000013000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, // 0x000014000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, // 0x000015000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, // 0x000016000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, // 0x000017000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, // 0x000018000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, // 0x000019000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, // 0x00001A000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, // 0x00001B000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, // 0x00001C000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, // 0x00001D000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, // 0x00001E000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, // 0x00001F000000755C, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000020, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000021, + 0x5C, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000225C, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000023, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000024, + 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000025, + 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000026, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000027, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000028, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000029, + 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002A, + 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002B, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002C, + 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002D, + 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002E, + 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002F, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000030, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000031, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000032, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000033, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000034, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000035, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000036, + 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000037, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000038, + 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000039, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003A, + 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003B, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003C, + 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003D, + 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003E, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003F, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000040, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000041, + 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000042, + 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000043, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000044, + 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000045, + 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000046, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000047, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000048, + 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000049, + 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004A, + 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004B, + 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004C, + 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004D, + 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004E, + 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004F, + 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000050, + 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000051, + 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000052, + 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000053, + 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000054, + 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000055, + 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000056, + 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000057, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000058, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000059, + 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005A, + 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005B, + 0x5C, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000005C5C, + 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005D, + 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005E, + 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005F, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000060, + 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000061, + 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000062, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000063, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000064, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000065, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000066, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000067, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000068, + 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000069, + 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006A, + 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006B, + 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006C, + 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006D, + 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006E, + 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006F, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000070, + 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000071, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000072, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000073, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000074, + 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000075, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000076, + 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000077, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000078, + 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000079, + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007A, + 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007B, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007C, + 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007D, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007E, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, // 0x00007F000000755C, + }; + + public static ReadOnlySpan BaseWithoutTabEscapeLengthTableBigEndian => + new byte[] + { + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-00), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-01), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-02), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-03), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-04), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-05), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-06), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-07), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-62), + 0x00, 0x00, 0x00, 0x01, // 1 (09), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-6E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0B), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-66), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-72), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0F), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-10), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-11), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-12), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-13), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-14), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-15), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-16), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-17), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-18), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-19), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1A), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1B), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1C), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1D), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1F), + 0x00, 0x00, 0x00, 0x01, // 1 (20), + 0x00, 0x00, 0x00, 0x01, // 1 (21), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-22), + 0x00, 0x00, 0x00, 0x01, // 1 (23), + 0x00, 0x00, 0x00, 0x01, // 1 (24), + 0x00, 0x00, 0x00, 0x01, // 1 (25), + 0x00, 0x00, 0x00, 0x01, // 1 (26), + 0x00, 0x00, 0x00, 0x01, // 1 (27), + 0x00, 0x00, 0x00, 0x01, // 1 (28), + 0x00, 0x00, 0x00, 0x01, // 1 (29), + 0x00, 0x00, 0x00, 0x01, // 1 (2A), + 0x00, 0x00, 0x00, 0x01, // 1 (2B), + 0x00, 0x00, 0x00, 0x01, // 1 (2C), + 0x00, 0x00, 0x00, 0x01, // 1 (2D), + 0x00, 0x00, 0x00, 0x01, // 1 (2E), + 0x00, 0x00, 0x00, 0x01, // 1 (2F), + 0x00, 0x00, 0x00, 0x01, // 1 (30), + 0x00, 0x00, 0x00, 0x01, // 1 (31), + 0x00, 0x00, 0x00, 0x01, // 1 (32), + 0x00, 0x00, 0x00, 0x01, // 1 (33), + 0x00, 0x00, 0x00, 0x01, // 1 (34), + 0x00, 0x00, 0x00, 0x01, // 1 (35), + 0x00, 0x00, 0x00, 0x01, // 1 (36), + 0x00, 0x00, 0x00, 0x01, // 1 (37), + 0x00, 0x00, 0x00, 0x01, // 1 (38), + 0x00, 0x00, 0x00, 0x01, // 1 (39), + 0x00, 0x00, 0x00, 0x01, // 1 (3A), + 0x00, 0x00, 0x00, 0x01, // 1 (3B), + 0x00, 0x00, 0x00, 0x01, // 1 (3C), + 0x00, 0x00, 0x00, 0x01, // 1 (3D), + 0x00, 0x00, 0x00, 0x01, // 1 (3E), + 0x00, 0x00, 0x00, 0x01, // 1 (3F), + 0x00, 0x00, 0x00, 0x01, // 1 (40), + 0x00, 0x00, 0x00, 0x01, // 1 (41), + 0x00, 0x00, 0x00, 0x01, // 1 (42), + 0x00, 0x00, 0x00, 0x01, // 1 (43), + 0x00, 0x00, 0x00, 0x01, // 1 (44), + 0x00, 0x00, 0x00, 0x01, // 1 (45), + 0x00, 0x00, 0x00, 0x01, // 1 (46), + 0x00, 0x00, 0x00, 0x01, // 1 (47), + 0x00, 0x00, 0x00, 0x01, // 1 (48), + 0x00, 0x00, 0x00, 0x01, // 1 (49), + 0x00, 0x00, 0x00, 0x01, // 1 (4A), + 0x00, 0x00, 0x00, 0x01, // 1 (4B), + 0x00, 0x00, 0x00, 0x01, // 1 (4C), + 0x00, 0x00, 0x00, 0x01, // 1 (4D), + 0x00, 0x00, 0x00, 0x01, // 1 (4E), + 0x00, 0x00, 0x00, 0x01, // 1 (4F), + 0x00, 0x00, 0x00, 0x01, // 1 (50), + 0x00, 0x00, 0x00, 0x01, // 1 (51), + 0x00, 0x00, 0x00, 0x01, // 1 (52), + 0x00, 0x00, 0x00, 0x01, // 1 (53), + 0x00, 0x00, 0x00, 0x01, // 1 (54), + 0x00, 0x00, 0x00, 0x01, // 1 (55), + 0x00, 0x00, 0x00, 0x01, // 1 (56), + 0x00, 0x00, 0x00, 0x01, // 1 (57), + 0x00, 0x00, 0x00, 0x01, // 1 (58), + 0x00, 0x00, 0x00, 0x01, // 1 (59), + 0x00, 0x00, 0x00, 0x01, // 1 (5A), + 0x00, 0x00, 0x00, 0x01, // 1 (5B), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-5C), + 0x00, 0x00, 0x00, 0x01, // 1 (5D), + 0x00, 0x00, 0x00, 0x01, // 1 (5E), + 0x00, 0x00, 0x00, 0x01, // 1 (5F), + 0x00, 0x00, 0x00, 0x01, // 1 (60), + 0x00, 0x00, 0x00, 0x01, // 1 (61), + 0x00, 0x00, 0x00, 0x01, // 1 (62), + 0x00, 0x00, 0x00, 0x01, // 1 (63), + 0x00, 0x00, 0x00, 0x01, // 1 (64), + 0x00, 0x00, 0x00, 0x01, // 1 (65), + 0x00, 0x00, 0x00, 0x01, // 1 (66), + 0x00, 0x00, 0x00, 0x01, // 1 (67), + 0x00, 0x00, 0x00, 0x01, // 1 (68), + 0x00, 0x00, 0x00, 0x01, // 1 (69), + 0x00, 0x00, 0x00, 0x01, // 1 (6A), + 0x00, 0x00, 0x00, 0x01, // 1 (6B), + 0x00, 0x00, 0x00, 0x01, // 1 (6C), + 0x00, 0x00, 0x00, 0x01, // 1 (6D), + 0x00, 0x00, 0x00, 0x01, // 1 (6E), + 0x00, 0x00, 0x00, 0x01, // 1 (6F), + 0x00, 0x00, 0x00, 0x01, // 1 (70), + 0x00, 0x00, 0x00, 0x01, // 1 (71), + 0x00, 0x00, 0x00, 0x01, // 1 (72), + 0x00, 0x00, 0x00, 0x01, // 1 (73), + 0x00, 0x00, 0x00, 0x01, // 1 (74), + 0x00, 0x00, 0x00, 0x01, // 1 (75), + 0x00, 0x00, 0x00, 0x01, // 1 (76), + 0x00, 0x00, 0x00, 0x01, // 1 (77), + 0x00, 0x00, 0x00, 0x01, // 1 (78), + 0x00, 0x00, 0x00, 0x01, // 1 (79), + 0x00, 0x00, 0x00, 0x01, // 1 (7A), + 0x00, 0x00, 0x00, 0x01, // 1 (7B), + 0x00, 0x00, 0x00, 0x01, // 1 (7C), + 0x00, 0x00, 0x00, 0x01, // 1 (7D), + 0x00, 0x00, 0x00, 0x01, // 1 (7E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-7F), + }; + + public static ReadOnlySpan BaseWithoutTabEscapeLengthTableLittleEndian => + new byte[] + { + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-00), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-01), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-02), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-03), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-04), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-05), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-06), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-07), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-62), + 0x01, 0x00, 0x00, 0x00, // 1 (09), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-6E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0B), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-66), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-72), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0F), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-10), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-11), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-12), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-13), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-14), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-15), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-16), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-17), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-18), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-19), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1A), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1B), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1C), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1D), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1F), + 0x01, 0x00, 0x00, 0x00, // 1 (20), + 0x01, 0x00, 0x00, 0x00, // 1 (21), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-22), + 0x01, 0x00, 0x00, 0x00, // 1 (23), + 0x01, 0x00, 0x00, 0x00, // 1 (24), + 0x01, 0x00, 0x00, 0x00, // 1 (25), + 0x01, 0x00, 0x00, 0x00, // 1 (26), + 0x01, 0x00, 0x00, 0x00, // 1 (27), + 0x01, 0x00, 0x00, 0x00, // 1 (28), + 0x01, 0x00, 0x00, 0x00, // 1 (29), + 0x01, 0x00, 0x00, 0x00, // 1 (2A), + 0x01, 0x00, 0x00, 0x00, // 1 (2B), + 0x01, 0x00, 0x00, 0x00, // 1 (2C), + 0x01, 0x00, 0x00, 0x00, // 1 (2D), + 0x01, 0x00, 0x00, 0x00, // 1 (2E), + 0x01, 0x00, 0x00, 0x00, // 1 (2F), + 0x01, 0x00, 0x00, 0x00, // 1 (30), + 0x01, 0x00, 0x00, 0x00, // 1 (31), + 0x01, 0x00, 0x00, 0x00, // 1 (32), + 0x01, 0x00, 0x00, 0x00, // 1 (33), + 0x01, 0x00, 0x00, 0x00, // 1 (34), + 0x01, 0x00, 0x00, 0x00, // 1 (35), + 0x01, 0x00, 0x00, 0x00, // 1 (36), + 0x01, 0x00, 0x00, 0x00, // 1 (37), + 0x01, 0x00, 0x00, 0x00, // 1 (38), + 0x01, 0x00, 0x00, 0x00, // 1 (39), + 0x01, 0x00, 0x00, 0x00, // 1 (3A), + 0x01, 0x00, 0x00, 0x00, // 1 (3B), + 0x01, 0x00, 0x00, 0x00, // 1 (3C), + 0x01, 0x00, 0x00, 0x00, // 1 (3D), + 0x01, 0x00, 0x00, 0x00, // 1 (3E), + 0x01, 0x00, 0x00, 0x00, // 1 (3F), + 0x01, 0x00, 0x00, 0x00, // 1 (40), + 0x01, 0x00, 0x00, 0x00, // 1 (41), + 0x01, 0x00, 0x00, 0x00, // 1 (42), + 0x01, 0x00, 0x00, 0x00, // 1 (43), + 0x01, 0x00, 0x00, 0x00, // 1 (44), + 0x01, 0x00, 0x00, 0x00, // 1 (45), + 0x01, 0x00, 0x00, 0x00, // 1 (46), + 0x01, 0x00, 0x00, 0x00, // 1 (47), + 0x01, 0x00, 0x00, 0x00, // 1 (48), + 0x01, 0x00, 0x00, 0x00, // 1 (49), + 0x01, 0x00, 0x00, 0x00, // 1 (4A), + 0x01, 0x00, 0x00, 0x00, // 1 (4B), + 0x01, 0x00, 0x00, 0x00, // 1 (4C), + 0x01, 0x00, 0x00, 0x00, // 1 (4D), + 0x01, 0x00, 0x00, 0x00, // 1 (4E), + 0x01, 0x00, 0x00, 0x00, // 1 (4F), + 0x01, 0x00, 0x00, 0x00, // 1 (50), + 0x01, 0x00, 0x00, 0x00, // 1 (51), + 0x01, 0x00, 0x00, 0x00, // 1 (52), + 0x01, 0x00, 0x00, 0x00, // 1 (53), + 0x01, 0x00, 0x00, 0x00, // 1 (54), + 0x01, 0x00, 0x00, 0x00, // 1 (55), + 0x01, 0x00, 0x00, 0x00, // 1 (56), + 0x01, 0x00, 0x00, 0x00, // 1 (57), + 0x01, 0x00, 0x00, 0x00, // 1 (58), + 0x01, 0x00, 0x00, 0x00, // 1 (59), + 0x01, 0x00, 0x00, 0x00, // 1 (5A), + 0x01, 0x00, 0x00, 0x00, // 1 (5B), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-5C), + 0x01, 0x00, 0x00, 0x00, // 1 (5D), + 0x01, 0x00, 0x00, 0x00, // 1 (5E), + 0x01, 0x00, 0x00, 0x00, // 1 (5F), + 0x01, 0x00, 0x00, 0x00, // 1 (60), + 0x01, 0x00, 0x00, 0x00, // 1 (61), + 0x01, 0x00, 0x00, 0x00, // 1 (62), + 0x01, 0x00, 0x00, 0x00, // 1 (63), + 0x01, 0x00, 0x00, 0x00, // 1 (64), + 0x01, 0x00, 0x00, 0x00, // 1 (65), + 0x01, 0x00, 0x00, 0x00, // 1 (66), + 0x01, 0x00, 0x00, 0x00, // 1 (67), + 0x01, 0x00, 0x00, 0x00, // 1 (68), + 0x01, 0x00, 0x00, 0x00, // 1 (69), + 0x01, 0x00, 0x00, 0x00, // 1 (6A), + 0x01, 0x00, 0x00, 0x00, // 1 (6B), + 0x01, 0x00, 0x00, 0x00, // 1 (6C), + 0x01, 0x00, 0x00, 0x00, // 1 (6D), + 0x01, 0x00, 0x00, 0x00, // 1 (6E), + 0x01, 0x00, 0x00, 0x00, // 1 (6F), + 0x01, 0x00, 0x00, 0x00, // 1 (70), + 0x01, 0x00, 0x00, 0x00, // 1 (71), + 0x01, 0x00, 0x00, 0x00, // 1 (72), + 0x01, 0x00, 0x00, 0x00, // 1 (73), + 0x01, 0x00, 0x00, 0x00, // 1 (74), + 0x01, 0x00, 0x00, 0x00, // 1 (75), + 0x01, 0x00, 0x00, 0x00, // 1 (76), + 0x01, 0x00, 0x00, 0x00, // 1 (77), + 0x01, 0x00, 0x00, 0x00, // 1 (78), + 0x01, 0x00, 0x00, 0x00, // 1 (79), + 0x01, 0x00, 0x00, 0x00, // 1 (7A), + 0x01, 0x00, 0x00, 0x00, // 1 (7B), + 0x01, 0x00, 0x00, 0x00, // 1 (7C), + 0x01, 0x00, 0x00, 0x00, // 1 (7D), + 0x01, 0x00, 0x00, 0x00, // 1 (7E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-7F), + }; + + public static ReadOnlySpan BaseWithHtmlCharsEscapeTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(BaseWithHtmlCharsEscapeTableLittleEndian) : + MemoryMarshal.Cast(BaseWithHtmlCharsEscapeTableBigEndian); + + public static ReadOnlySpan BaseWithHtmlCharsEscapeLengthTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(BaseWithHtmlCharsEscapeLengthTableLittleEndian) : + MemoryMarshal.Cast(BaseWithHtmlCharsEscapeLengthTableBigEndian); + + public static ReadOnlySpan BaseWithHtmlCharsEscapeTableBigEndian => + new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000000000, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000010000, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000020000, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000030000, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000040000, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000050000, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000060000, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000070000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x5C, // 0x5C62000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x5C, // 0x5C74000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x5C, // 0x5C6E000000000000, + 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000B0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x5C, // 0x5C66000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x5C, // 0x5C72000000000000, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000E0000, + 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000F0000, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000100000, + 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000110000, + 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000120000, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000130000, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000140000, + 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000150000, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000160000, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000170000, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000180000, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000190000, + 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001A0000, + 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001B0000, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001C0000, + 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001D0000, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001E0000, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001F0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, // 0x2000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, // 0x2100000000000000, + 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000220000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, // 0x2300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, // 0x2400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, // 0x2500000000000000, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000260000, + 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000270000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, // 0x2800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, // 0x2900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, // 0x2A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, // 0x2B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, // 0x2C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, // 0x2D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, // 0x2E00000000000000, + 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000002F0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, // 0x3000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, // 0x3100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, // 0x3200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, // 0x3300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, // 0x3400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, // 0x3500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, // 0x3600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, // 0x3700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, // 0x3800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, // 0x3900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, // 0x3A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, // 0x3B00000000000000, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000003C0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, // 0x3D00000000000000, + 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000003E0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, // 0x3F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // 0x4000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, // 0x4100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, // 0x4200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, // 0x4300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, // 0x4400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, // 0x4500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, // 0x4600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, // 0x4700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, // 0x4800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, // 0x4900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, // 0x4A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, // 0x4B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, // 0x4C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, // 0x4D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, // 0x4E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, // 0x4F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, // 0x5000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, // 0x5100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, // 0x5200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, // 0x5300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, // 0x5400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, // 0x5500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, // 0x5600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, // 0x5700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, // 0x5800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, // 0x5900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, // 0x5A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, // 0x5B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x5C, // 0x5C5C000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, // 0x5D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, // 0x5E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, // 0x5F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, // 0x6000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, // 0x6100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, // 0x6200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, // 0x6300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, // 0x6400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, // 0x6500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, // 0x6600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, // 0x6700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, // 0x6800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, // 0x6900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, // 0x6A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, // 0x6B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, // 0x6C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, // 0x6D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, // 0x6E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, // 0x6F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, // 0x7000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, // 0x7100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, // 0x7200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, // 0x7300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, // 0x7400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, // 0x7500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, // 0x7600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, // 0x7700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, // 0x7800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, // 0x7900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7A, // 0x7A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, // 0x7B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, // 0x7C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, // 0x7D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, // 0x7E00000000000000, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000007F0000, + }; + + public static ReadOnlySpan BaseWithHtmlCharsEscapeTableLittleEndian => + new byte[] + { + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // 0x000001000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // 0x000002000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // 0x000003000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // 0x000004000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, // 0x000005000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // 0x000006000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // 0x000007000000755C, + 0x5C, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000625C, + 0x5C, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000745C, + 0x5C, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000006E5C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, // 0x00000B000000755C, + 0x5C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000665C, + 0x5C, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000725C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, // 0x00000E000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, // 0x00000F000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // 0x000010000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, // 0x000011000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, // 0x000012000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, // 0x000013000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, // 0x000014000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, // 0x000015000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, // 0x000016000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, // 0x000017000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, // 0x000018000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, // 0x000019000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, // 0x00001A000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, // 0x00001B000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, // 0x00001C000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, // 0x00001D000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, // 0x00001E000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, // 0x00001F000000755C, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000020, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000021, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // 0x000022000000755C, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000023, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000024, + 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000025, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, // 0x000026000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, // 0x000027000000755C, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000028, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000029, + 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002A, + 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002B, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002C, + 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002D, + 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002E, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, // 0x00002F000000755C, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000030, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000031, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000032, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000033, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000034, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000035, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000036, + 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000037, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000038, + 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000039, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003A, + 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003B, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, // 0x00003C000000755C, + 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003D, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, // 0x00003E000000755C, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003F, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000040, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000041, + 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000042, + 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000043, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000044, + 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000045, + 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000046, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000047, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000048, + 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000049, + 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004A, + 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004B, + 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004C, + 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004D, + 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004E, + 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004F, + 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000050, + 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000051, + 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000052, + 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000053, + 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000054, + 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000055, + 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000056, + 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000057, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000058, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000059, + 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005A, + 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005B, + 0x5C, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000005C5C, + 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005D, + 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005E, + 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005F, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000060, + 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000061, + 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000062, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000063, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000064, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000065, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000066, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000067, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000068, + 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000069, + 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006A, + 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006B, + 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006C, + 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006D, + 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006E, + 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006F, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000070, + 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000071, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000072, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000073, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000074, + 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000075, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000076, + 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000077, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000078, + 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000079, + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007A, + 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007B, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007C, + 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007D, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007E, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, // 0x00007F000000755C, + }; + + public static ReadOnlySpan BaseWithHtmlCharsEscapeLengthTableBigEndian => + new byte[] + { + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-00), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-01), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-02), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-03), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-04), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-05), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-06), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-07), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-62), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-74), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-6E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0B), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-66), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-72), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0F), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-10), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-11), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-12), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-13), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-14), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-15), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-16), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-17), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-18), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-19), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1A), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1B), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1C), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1D), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1F), + 0x00, 0x00, 0x00, 0x01, // 1 (20), + 0x00, 0x00, 0x00, 0x01, // 1 (21), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-22), + 0x00, 0x00, 0x00, 0x01, // 1 (23), + 0x00, 0x00, 0x00, 0x01, // 1 (24), + 0x00, 0x00, 0x00, 0x01, // 1 (25), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-26), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-27), + 0x00, 0x00, 0x00, 0x01, // 1 (28), + 0x00, 0x00, 0x00, 0x01, // 1 (29), + 0x00, 0x00, 0x00, 0x01, // 1 (2A), + 0x00, 0x00, 0x00, 0x01, // 1 (2B), + 0x00, 0x00, 0x00, 0x01, // 1 (2C), + 0x00, 0x00, 0x00, 0x01, // 1 (2D), + 0x00, 0x00, 0x00, 0x01, // 1 (2E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-2F), + 0x00, 0x00, 0x00, 0x01, // 1 (30), + 0x00, 0x00, 0x00, 0x01, // 1 (31), + 0x00, 0x00, 0x00, 0x01, // 1 (32), + 0x00, 0x00, 0x00, 0x01, // 1 (33), + 0x00, 0x00, 0x00, 0x01, // 1 (34), + 0x00, 0x00, 0x00, 0x01, // 1 (35), + 0x00, 0x00, 0x00, 0x01, // 1 (36), + 0x00, 0x00, 0x00, 0x01, // 1 (37), + 0x00, 0x00, 0x00, 0x01, // 1 (38), + 0x00, 0x00, 0x00, 0x01, // 1 (39), + 0x00, 0x00, 0x00, 0x01, // 1 (3A), + 0x00, 0x00, 0x00, 0x01, // 1 (3B), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-3C), + 0x00, 0x00, 0x00, 0x01, // 1 (3D), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-3E), + 0x00, 0x00, 0x00, 0x01, // 1 (3F), + 0x00, 0x00, 0x00, 0x01, // 1 (40), + 0x00, 0x00, 0x00, 0x01, // 1 (41), + 0x00, 0x00, 0x00, 0x01, // 1 (42), + 0x00, 0x00, 0x00, 0x01, // 1 (43), + 0x00, 0x00, 0x00, 0x01, // 1 (44), + 0x00, 0x00, 0x00, 0x01, // 1 (45), + 0x00, 0x00, 0x00, 0x01, // 1 (46), + 0x00, 0x00, 0x00, 0x01, // 1 (47), + 0x00, 0x00, 0x00, 0x01, // 1 (48), + 0x00, 0x00, 0x00, 0x01, // 1 (49), + 0x00, 0x00, 0x00, 0x01, // 1 (4A), + 0x00, 0x00, 0x00, 0x01, // 1 (4B), + 0x00, 0x00, 0x00, 0x01, // 1 (4C), + 0x00, 0x00, 0x00, 0x01, // 1 (4D), + 0x00, 0x00, 0x00, 0x01, // 1 (4E), + 0x00, 0x00, 0x00, 0x01, // 1 (4F), + 0x00, 0x00, 0x00, 0x01, // 1 (50), + 0x00, 0x00, 0x00, 0x01, // 1 (51), + 0x00, 0x00, 0x00, 0x01, // 1 (52), + 0x00, 0x00, 0x00, 0x01, // 1 (53), + 0x00, 0x00, 0x00, 0x01, // 1 (54), + 0x00, 0x00, 0x00, 0x01, // 1 (55), + 0x00, 0x00, 0x00, 0x01, // 1 (56), + 0x00, 0x00, 0x00, 0x01, // 1 (57), + 0x00, 0x00, 0x00, 0x01, // 1 (58), + 0x00, 0x00, 0x00, 0x01, // 1 (59), + 0x00, 0x00, 0x00, 0x01, // 1 (5A), + 0x00, 0x00, 0x00, 0x01, // 1 (5B), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-5C), + 0x00, 0x00, 0x00, 0x01, // 1 (5D), + 0x00, 0x00, 0x00, 0x01, // 1 (5E), + 0x00, 0x00, 0x00, 0x01, // 1 (5F), + 0x00, 0x00, 0x00, 0x01, // 1 (60), + 0x00, 0x00, 0x00, 0x01, // 1 (61), + 0x00, 0x00, 0x00, 0x01, // 1 (62), + 0x00, 0x00, 0x00, 0x01, // 1 (63), + 0x00, 0x00, 0x00, 0x01, // 1 (64), + 0x00, 0x00, 0x00, 0x01, // 1 (65), + 0x00, 0x00, 0x00, 0x01, // 1 (66), + 0x00, 0x00, 0x00, 0x01, // 1 (67), + 0x00, 0x00, 0x00, 0x01, // 1 (68), + 0x00, 0x00, 0x00, 0x01, // 1 (69), + 0x00, 0x00, 0x00, 0x01, // 1 (6A), + 0x00, 0x00, 0x00, 0x01, // 1 (6B), + 0x00, 0x00, 0x00, 0x01, // 1 (6C), + 0x00, 0x00, 0x00, 0x01, // 1 (6D), + 0x00, 0x00, 0x00, 0x01, // 1 (6E), + 0x00, 0x00, 0x00, 0x01, // 1 (6F), + 0x00, 0x00, 0x00, 0x01, // 1 (70), + 0x00, 0x00, 0x00, 0x01, // 1 (71), + 0x00, 0x00, 0x00, 0x01, // 1 (72), + 0x00, 0x00, 0x00, 0x01, // 1 (73), + 0x00, 0x00, 0x00, 0x01, // 1 (74), + 0x00, 0x00, 0x00, 0x01, // 1 (75), + 0x00, 0x00, 0x00, 0x01, // 1 (76), + 0x00, 0x00, 0x00, 0x01, // 1 (77), + 0x00, 0x00, 0x00, 0x01, // 1 (78), + 0x00, 0x00, 0x00, 0x01, // 1 (79), + 0x00, 0x00, 0x00, 0x01, // 1 (7A), + 0x00, 0x00, 0x00, 0x01, // 1 (7B), + 0x00, 0x00, 0x00, 0x01, // 1 (7C), + 0x00, 0x00, 0x00, 0x01, // 1 (7D), + 0x00, 0x00, 0x00, 0x01, // 1 (7E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-7F), + }; + + public static ReadOnlySpan BaseWithHtmlCharsEscapeLengthTableLittleEndian => + new byte[] + { + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-00), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-01), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-02), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-03), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-04), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-05), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-06), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-07), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-62), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-74), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-6E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0B), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-66), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-72), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0F), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-10), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-11), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-12), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-13), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-14), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-15), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-16), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-17), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-18), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-19), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1A), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1B), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1C), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1D), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1F), + 0x01, 0x00, 0x00, 0x00, // 1 (20), + 0x01, 0x00, 0x00, 0x00, // 1 (21), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-22), + 0x01, 0x00, 0x00, 0x00, // 1 (23), + 0x01, 0x00, 0x00, 0x00, // 1 (24), + 0x01, 0x00, 0x00, 0x00, // 1 (25), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-26), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-27), + 0x01, 0x00, 0x00, 0x00, // 1 (28), + 0x01, 0x00, 0x00, 0x00, // 1 (29), + 0x01, 0x00, 0x00, 0x00, // 1 (2A), + 0x01, 0x00, 0x00, 0x00, // 1 (2B), + 0x01, 0x00, 0x00, 0x00, // 1 (2C), + 0x01, 0x00, 0x00, 0x00, // 1 (2D), + 0x01, 0x00, 0x00, 0x00, // 1 (2E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-2F), + 0x01, 0x00, 0x00, 0x00, // 1 (30), + 0x01, 0x00, 0x00, 0x00, // 1 (31), + 0x01, 0x00, 0x00, 0x00, // 1 (32), + 0x01, 0x00, 0x00, 0x00, // 1 (33), + 0x01, 0x00, 0x00, 0x00, // 1 (34), + 0x01, 0x00, 0x00, 0x00, // 1 (35), + 0x01, 0x00, 0x00, 0x00, // 1 (36), + 0x01, 0x00, 0x00, 0x00, // 1 (37), + 0x01, 0x00, 0x00, 0x00, // 1 (38), + 0x01, 0x00, 0x00, 0x00, // 1 (39), + 0x01, 0x00, 0x00, 0x00, // 1 (3A), + 0x01, 0x00, 0x00, 0x00, // 1 (3B), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-3C), + 0x01, 0x00, 0x00, 0x00, // 1 (3D), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-3E), + 0x01, 0x00, 0x00, 0x00, // 1 (3F), + 0x01, 0x00, 0x00, 0x00, // 1 (40), + 0x01, 0x00, 0x00, 0x00, // 1 (41), + 0x01, 0x00, 0x00, 0x00, // 1 (42), + 0x01, 0x00, 0x00, 0x00, // 1 (43), + 0x01, 0x00, 0x00, 0x00, // 1 (44), + 0x01, 0x00, 0x00, 0x00, // 1 (45), + 0x01, 0x00, 0x00, 0x00, // 1 (46), + 0x01, 0x00, 0x00, 0x00, // 1 (47), + 0x01, 0x00, 0x00, 0x00, // 1 (48), + 0x01, 0x00, 0x00, 0x00, // 1 (49), + 0x01, 0x00, 0x00, 0x00, // 1 (4A), + 0x01, 0x00, 0x00, 0x00, // 1 (4B), + 0x01, 0x00, 0x00, 0x00, // 1 (4C), + 0x01, 0x00, 0x00, 0x00, // 1 (4D), + 0x01, 0x00, 0x00, 0x00, // 1 (4E), + 0x01, 0x00, 0x00, 0x00, // 1 (4F), + 0x01, 0x00, 0x00, 0x00, // 1 (50), + 0x01, 0x00, 0x00, 0x00, // 1 (51), + 0x01, 0x00, 0x00, 0x00, // 1 (52), + 0x01, 0x00, 0x00, 0x00, // 1 (53), + 0x01, 0x00, 0x00, 0x00, // 1 (54), + 0x01, 0x00, 0x00, 0x00, // 1 (55), + 0x01, 0x00, 0x00, 0x00, // 1 (56), + 0x01, 0x00, 0x00, 0x00, // 1 (57), + 0x01, 0x00, 0x00, 0x00, // 1 (58), + 0x01, 0x00, 0x00, 0x00, // 1 (59), + 0x01, 0x00, 0x00, 0x00, // 1 (5A), + 0x01, 0x00, 0x00, 0x00, // 1 (5B), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-5C), + 0x01, 0x00, 0x00, 0x00, // 1 (5D), + 0x01, 0x00, 0x00, 0x00, // 1 (5E), + 0x01, 0x00, 0x00, 0x00, // 1 (5F), + 0x01, 0x00, 0x00, 0x00, // 1 (60), + 0x01, 0x00, 0x00, 0x00, // 1 (61), + 0x01, 0x00, 0x00, 0x00, // 1 (62), + 0x01, 0x00, 0x00, 0x00, // 1 (63), + 0x01, 0x00, 0x00, 0x00, // 1 (64), + 0x01, 0x00, 0x00, 0x00, // 1 (65), + 0x01, 0x00, 0x00, 0x00, // 1 (66), + 0x01, 0x00, 0x00, 0x00, // 1 (67), + 0x01, 0x00, 0x00, 0x00, // 1 (68), + 0x01, 0x00, 0x00, 0x00, // 1 (69), + 0x01, 0x00, 0x00, 0x00, // 1 (6A), + 0x01, 0x00, 0x00, 0x00, // 1 (6B), + 0x01, 0x00, 0x00, 0x00, // 1 (6C), + 0x01, 0x00, 0x00, 0x00, // 1 (6D), + 0x01, 0x00, 0x00, 0x00, // 1 (6E), + 0x01, 0x00, 0x00, 0x00, // 1 (6F), + 0x01, 0x00, 0x00, 0x00, // 1 (70), + 0x01, 0x00, 0x00, 0x00, // 1 (71), + 0x01, 0x00, 0x00, 0x00, // 1 (72), + 0x01, 0x00, 0x00, 0x00, // 1 (73), + 0x01, 0x00, 0x00, 0x00, // 1 (74), + 0x01, 0x00, 0x00, 0x00, // 1 (75), + 0x01, 0x00, 0x00, 0x00, // 1 (76), + 0x01, 0x00, 0x00, 0x00, // 1 (77), + 0x01, 0x00, 0x00, 0x00, // 1 (78), + 0x01, 0x00, 0x00, 0x00, // 1 (79), + 0x01, 0x00, 0x00, 0x00, // 1 (7A), + 0x01, 0x00, 0x00, 0x00, // 1 (7B), + 0x01, 0x00, 0x00, 0x00, // 1 (7C), + 0x01, 0x00, 0x00, 0x00, // 1 (7D), + 0x01, 0x00, 0x00, 0x00, // 1 (7E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-7F), + }; + + public static ReadOnlySpan BaseWithHtmlCharsWithoutTabEscapeTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(BaseWithHtmlCharsWithoutTabEscapeTableLittleEndian) : + MemoryMarshal.Cast(BaseWithHtmlCharsWithoutTabEscapeTableBigEndian); + + public static ReadOnlySpan BaseWithHtmlCharsWithoutTabEscapeLengthTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(BaseWithHtmlCharsWithoutTabEscapeLengthTableLittleEndian) : + MemoryMarshal.Cast(BaseWithHtmlCharsWithoutTabEscapeLengthTableBigEndian); + + public static ReadOnlySpan BaseWithHtmlCharsWithoutTabEscapeTableBigEndian => + new byte[] + { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000000000, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000010000, + 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000020000, + 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000030000, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000040000, + 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000050000, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000060000, + 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000070000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, 0x5C, // 0x5C62000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, // 0x0900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x5C, // 0x5C6E000000000000, + 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000B0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x5C, // 0x5C66000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x5C, // 0x5C72000000000000, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000E0000, + 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000000F0000, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000100000, + 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000110000, + 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000120000, + 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000130000, + 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000140000, + 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000150000, + 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000160000, + 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000170000, + 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000180000, + 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000190000, + 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001A0000, + 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001B0000, + 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001C0000, + 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001D0000, + 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001E0000, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000001F0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, // 0x2000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, // 0x2100000000000000, + 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000220000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, // 0x2300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, // 0x2400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x25, // 0x2500000000000000, + 0x00, 0x00, 0x26, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000260000, + 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C75000000270000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, // 0x2800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, // 0x2900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, // 0x2A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, // 0x2B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, // 0x2C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2D, // 0x2D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, // 0x2E00000000000000, + 0x00, 0x00, 0x2F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000002F0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, // 0x3000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, // 0x3100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, // 0x3200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, // 0x3300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, // 0x3400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, // 0x3500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, // 0x3600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, // 0x3700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, // 0x3800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, // 0x3900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, // 0x3A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, // 0x3B00000000000000, + 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000003C0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, // 0x3D00000000000000, + 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000003E0000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, // 0x3F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, // 0x4000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, // 0x4100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, // 0x4200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, // 0x4300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, // 0x4400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, // 0x4500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, // 0x4600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, // 0x4700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, // 0x4800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, // 0x4900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4A, // 0x4A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4B, // 0x4B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, // 0x4C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, // 0x4D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, // 0x4E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, // 0x4F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, // 0x5000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x51, // 0x5100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, // 0x5200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, // 0x5300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, // 0x5400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, // 0x5500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, // 0x5600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, // 0x5700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, // 0x5800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, // 0x5900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, // 0x5A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5B, // 0x5B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x5C, // 0x5C5C000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, // 0x5D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5E, // 0x5E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, // 0x5F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, // 0x6000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, // 0x6100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62, // 0x6200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, // 0x6300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, // 0x6400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, // 0x6500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, // 0x6600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, // 0x6700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, // 0x6800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, // 0x6900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, // 0x6A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, // 0x6B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6C, // 0x6C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6D, // 0x6D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6E, // 0x6E00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, // 0x6F00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, // 0x7000000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, // 0x7100000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, // 0x7200000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, // 0x7300000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, // 0x7400000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, // 0x7500000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, // 0x7600000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, // 0x7700000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, // 0x7800000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, // 0x7900000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7A, // 0x7A00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7B, // 0x7B00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, // 0x7C00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, // 0x7D00000000000000, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, // 0x7E00000000000000, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x75, 0x5C, // 0x5C750000007F0000, + }; + + public static ReadOnlySpan BaseWithHtmlCharsWithoutTabEscapeTableLittleEndian => + new byte[] + { + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // 0x000001000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, // 0x000002000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, // 0x000003000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, // 0x000004000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, // 0x000005000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // 0x000006000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, // 0x000007000000755C, + 0x5C, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000625C, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000009, + 0x5C, 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000006E5C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, // 0x00000B000000755C, + 0x5C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000665C, + 0x5C, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000725C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, // 0x00000E000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, // 0x00000F000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, // 0x000010000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, // 0x000011000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, // 0x000012000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, // 0x000013000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, // 0x000014000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, // 0x000015000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, // 0x000016000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, // 0x000017000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, // 0x000018000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, // 0x000019000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, // 0x00001A000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, // 0x00001B000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, // 0x00001C000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, // 0x00001D000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, // 0x00001E000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, // 0x00001F000000755C, + 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000020, + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000021, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, // 0x000022000000755C, + 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000023, + 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000024, + 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000025, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x26, 0x00, 0x00, // 0x000026000000755C, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, // 0x000027000000755C, + 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000028, + 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000029, + 0x2A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002A, + 0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002B, + 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002C, + 0x2D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002D, + 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000002E, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x2F, 0x00, 0x00, // 0x00002F000000755C, + 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000030, + 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000031, + 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000032, + 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000033, + 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000034, + 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000035, + 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000036, + 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000037, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000038, + 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000039, + 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003A, + 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003B, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, // 0x00003C000000755C, + 0x3D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003D, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, // 0x00003E000000755C, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000003F, + 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000040, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000041, + 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000042, + 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000043, + 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000044, + 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000045, + 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000046, + 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000047, + 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000048, + 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000049, + 0x4A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004A, + 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004B, + 0x4C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004C, + 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004D, + 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004E, + 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000004F, + 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000050, + 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000051, + 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000052, + 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000053, + 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000054, + 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000055, + 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000056, + 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000057, + 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000058, + 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000059, + 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005A, + 0x5B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005B, + 0x5C, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000005C5C, + 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005D, + 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005E, + 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000005F, + 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000060, + 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000061, + 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000062, + 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000063, + 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000064, + 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000065, + 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000066, + 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000067, + 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000068, + 0x69, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000069, + 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006A, + 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006B, + 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006C, + 0x6D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006D, + 0x6E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006E, + 0x6F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000006F, + 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000070, + 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000071, + 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000072, + 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000073, + 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000074, + 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000075, + 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000076, + 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000077, + 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000078, + 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000000000000079, + 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007A, + 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007B, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007C, + 0x7D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007D, + 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x000000000000007E, + 0x5C, 0x75, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, // 0x00007F000000755C, + }; + + public static ReadOnlySpan BaseWithHtmlCharsWithoutTabEscapeLengthTableBigEndian => + new byte[] + { + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-00), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-01), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-02), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-03), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-04), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-05), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-06), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-07), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-62), + 0x00, 0x00, 0x00, 0x01, // 1 (09), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-6E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0B), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-66), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-72), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-0F), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-10), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-11), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-12), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-13), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-14), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-15), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-16), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-17), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-18), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-19), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1A), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1B), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1C), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1D), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-1F), + 0x00, 0x00, 0x00, 0x01, // 1 (20), + 0x00, 0x00, 0x00, 0x01, // 1 (21), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-22), + 0x00, 0x00, 0x00, 0x01, // 1 (23), + 0x00, 0x00, 0x00, 0x01, // 1 (24), + 0x00, 0x00, 0x00, 0x01, // 1 (25), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-26), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-27), + 0x00, 0x00, 0x00, 0x01, // 1 (28), + 0x00, 0x00, 0x00, 0x01, // 1 (29), + 0x00, 0x00, 0x00, 0x01, // 1 (2A), + 0x00, 0x00, 0x00, 0x01, // 1 (2B), + 0x00, 0x00, 0x00, 0x01, // 1 (2C), + 0x00, 0x00, 0x00, 0x01, // 1 (2D), + 0x00, 0x00, 0x00, 0x01, // 1 (2E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-2F), + 0x00, 0x00, 0x00, 0x01, // 1 (30), + 0x00, 0x00, 0x00, 0x01, // 1 (31), + 0x00, 0x00, 0x00, 0x01, // 1 (32), + 0x00, 0x00, 0x00, 0x01, // 1 (33), + 0x00, 0x00, 0x00, 0x01, // 1 (34), + 0x00, 0x00, 0x00, 0x01, // 1 (35), + 0x00, 0x00, 0x00, 0x01, // 1 (36), + 0x00, 0x00, 0x00, 0x01, // 1 (37), + 0x00, 0x00, 0x00, 0x01, // 1 (38), + 0x00, 0x00, 0x00, 0x01, // 1 (39), + 0x00, 0x00, 0x00, 0x01, // 1 (3A), + 0x00, 0x00, 0x00, 0x01, // 1 (3B), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-3C), + 0x00, 0x00, 0x00, 0x01, // 1 (3D), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-3E), + 0x00, 0x00, 0x00, 0x01, // 1 (3F), + 0x00, 0x00, 0x00, 0x01, // 1 (40), + 0x00, 0x00, 0x00, 0x01, // 1 (41), + 0x00, 0x00, 0x00, 0x01, // 1 (42), + 0x00, 0x00, 0x00, 0x01, // 1 (43), + 0x00, 0x00, 0x00, 0x01, // 1 (44), + 0x00, 0x00, 0x00, 0x01, // 1 (45), + 0x00, 0x00, 0x00, 0x01, // 1 (46), + 0x00, 0x00, 0x00, 0x01, // 1 (47), + 0x00, 0x00, 0x00, 0x01, // 1 (48), + 0x00, 0x00, 0x00, 0x01, // 1 (49), + 0x00, 0x00, 0x00, 0x01, // 1 (4A), + 0x00, 0x00, 0x00, 0x01, // 1 (4B), + 0x00, 0x00, 0x00, 0x01, // 1 (4C), + 0x00, 0x00, 0x00, 0x01, // 1 (4D), + 0x00, 0x00, 0x00, 0x01, // 1 (4E), + 0x00, 0x00, 0x00, 0x01, // 1 (4F), + 0x00, 0x00, 0x00, 0x01, // 1 (50), + 0x00, 0x00, 0x00, 0x01, // 1 (51), + 0x00, 0x00, 0x00, 0x01, // 1 (52), + 0x00, 0x00, 0x00, 0x01, // 1 (53), + 0x00, 0x00, 0x00, 0x01, // 1 (54), + 0x00, 0x00, 0x00, 0x01, // 1 (55), + 0x00, 0x00, 0x00, 0x01, // 1 (56), + 0x00, 0x00, 0x00, 0x01, // 1 (57), + 0x00, 0x00, 0x00, 0x01, // 1 (58), + 0x00, 0x00, 0x00, 0x01, // 1 (59), + 0x00, 0x00, 0x00, 0x01, // 1 (5A), + 0x00, 0x00, 0x00, 0x01, // 1 (5B), + 0x00, 0x00, 0x00, 0x02, // 2 (5C-5C), + 0x00, 0x00, 0x00, 0x01, // 1 (5D), + 0x00, 0x00, 0x00, 0x01, // 1 (5E), + 0x00, 0x00, 0x00, 0x01, // 1 (5F), + 0x00, 0x00, 0x00, 0x01, // 1 (60), + 0x00, 0x00, 0x00, 0x01, // 1 (61), + 0x00, 0x00, 0x00, 0x01, // 1 (62), + 0x00, 0x00, 0x00, 0x01, // 1 (63), + 0x00, 0x00, 0x00, 0x01, // 1 (64), + 0x00, 0x00, 0x00, 0x01, // 1 (65), + 0x00, 0x00, 0x00, 0x01, // 1 (66), + 0x00, 0x00, 0x00, 0x01, // 1 (67), + 0x00, 0x00, 0x00, 0x01, // 1 (68), + 0x00, 0x00, 0x00, 0x01, // 1 (69), + 0x00, 0x00, 0x00, 0x01, // 1 (6A), + 0x00, 0x00, 0x00, 0x01, // 1 (6B), + 0x00, 0x00, 0x00, 0x01, // 1 (6C), + 0x00, 0x00, 0x00, 0x01, // 1 (6D), + 0x00, 0x00, 0x00, 0x01, // 1 (6E), + 0x00, 0x00, 0x00, 0x01, // 1 (6F), + 0x00, 0x00, 0x00, 0x01, // 1 (70), + 0x00, 0x00, 0x00, 0x01, // 1 (71), + 0x00, 0x00, 0x00, 0x01, // 1 (72), + 0x00, 0x00, 0x00, 0x01, // 1 (73), + 0x00, 0x00, 0x00, 0x01, // 1 (74), + 0x00, 0x00, 0x00, 0x01, // 1 (75), + 0x00, 0x00, 0x00, 0x01, // 1 (76), + 0x00, 0x00, 0x00, 0x01, // 1 (77), + 0x00, 0x00, 0x00, 0x01, // 1 (78), + 0x00, 0x00, 0x00, 0x01, // 1 (79), + 0x00, 0x00, 0x00, 0x01, // 1 (7A), + 0x00, 0x00, 0x00, 0x01, // 1 (7B), + 0x00, 0x00, 0x00, 0x01, // 1 (7C), + 0x00, 0x00, 0x00, 0x01, // 1 (7D), + 0x00, 0x00, 0x00, 0x01, // 1 (7E), + 0x00, 0x00, 0x00, 0x06, // 6 (5C-75-00-00-00-7F), + }; + + public static ReadOnlySpan BaseWithHtmlCharsWithoutTabEscapeLengthTableLittleEndian => + new byte[] + { + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-00), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-01), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-02), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-03), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-04), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-05), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-06), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-07), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-62), + 0x01, 0x00, 0x00, 0x00, // 1 (09), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-6E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0B), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-66), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-72), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-0F), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-10), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-11), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-12), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-13), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-14), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-15), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-16), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-17), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-18), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-19), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1A), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1B), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1C), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1D), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-1F), + 0x01, 0x00, 0x00, 0x00, // 1 (20), + 0x01, 0x00, 0x00, 0x00, // 1 (21), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-22), + 0x01, 0x00, 0x00, 0x00, // 1 (23), + 0x01, 0x00, 0x00, 0x00, // 1 (24), + 0x01, 0x00, 0x00, 0x00, // 1 (25), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-26), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-27), + 0x01, 0x00, 0x00, 0x00, // 1 (28), + 0x01, 0x00, 0x00, 0x00, // 1 (29), + 0x01, 0x00, 0x00, 0x00, // 1 (2A), + 0x01, 0x00, 0x00, 0x00, // 1 (2B), + 0x01, 0x00, 0x00, 0x00, // 1 (2C), + 0x01, 0x00, 0x00, 0x00, // 1 (2D), + 0x01, 0x00, 0x00, 0x00, // 1 (2E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-2F), + 0x01, 0x00, 0x00, 0x00, // 1 (30), + 0x01, 0x00, 0x00, 0x00, // 1 (31), + 0x01, 0x00, 0x00, 0x00, // 1 (32), + 0x01, 0x00, 0x00, 0x00, // 1 (33), + 0x01, 0x00, 0x00, 0x00, // 1 (34), + 0x01, 0x00, 0x00, 0x00, // 1 (35), + 0x01, 0x00, 0x00, 0x00, // 1 (36), + 0x01, 0x00, 0x00, 0x00, // 1 (37), + 0x01, 0x00, 0x00, 0x00, // 1 (38), + 0x01, 0x00, 0x00, 0x00, // 1 (39), + 0x01, 0x00, 0x00, 0x00, // 1 (3A), + 0x01, 0x00, 0x00, 0x00, // 1 (3B), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-3C), + 0x01, 0x00, 0x00, 0x00, // 1 (3D), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-3E), + 0x01, 0x00, 0x00, 0x00, // 1 (3F), + 0x01, 0x00, 0x00, 0x00, // 1 (40), + 0x01, 0x00, 0x00, 0x00, // 1 (41), + 0x01, 0x00, 0x00, 0x00, // 1 (42), + 0x01, 0x00, 0x00, 0x00, // 1 (43), + 0x01, 0x00, 0x00, 0x00, // 1 (44), + 0x01, 0x00, 0x00, 0x00, // 1 (45), + 0x01, 0x00, 0x00, 0x00, // 1 (46), + 0x01, 0x00, 0x00, 0x00, // 1 (47), + 0x01, 0x00, 0x00, 0x00, // 1 (48), + 0x01, 0x00, 0x00, 0x00, // 1 (49), + 0x01, 0x00, 0x00, 0x00, // 1 (4A), + 0x01, 0x00, 0x00, 0x00, // 1 (4B), + 0x01, 0x00, 0x00, 0x00, // 1 (4C), + 0x01, 0x00, 0x00, 0x00, // 1 (4D), + 0x01, 0x00, 0x00, 0x00, // 1 (4E), + 0x01, 0x00, 0x00, 0x00, // 1 (4F), + 0x01, 0x00, 0x00, 0x00, // 1 (50), + 0x01, 0x00, 0x00, 0x00, // 1 (51), + 0x01, 0x00, 0x00, 0x00, // 1 (52), + 0x01, 0x00, 0x00, 0x00, // 1 (53), + 0x01, 0x00, 0x00, 0x00, // 1 (54), + 0x01, 0x00, 0x00, 0x00, // 1 (55), + 0x01, 0x00, 0x00, 0x00, // 1 (56), + 0x01, 0x00, 0x00, 0x00, // 1 (57), + 0x01, 0x00, 0x00, 0x00, // 1 (58), + 0x01, 0x00, 0x00, 0x00, // 1 (59), + 0x01, 0x00, 0x00, 0x00, // 1 (5A), + 0x01, 0x00, 0x00, 0x00, // 1 (5B), + 0x02, 0x00, 0x00, 0x00, // 2 (5C-5C), + 0x01, 0x00, 0x00, 0x00, // 1 (5D), + 0x01, 0x00, 0x00, 0x00, // 1 (5E), + 0x01, 0x00, 0x00, 0x00, // 1 (5F), + 0x01, 0x00, 0x00, 0x00, // 1 (60), + 0x01, 0x00, 0x00, 0x00, // 1 (61), + 0x01, 0x00, 0x00, 0x00, // 1 (62), + 0x01, 0x00, 0x00, 0x00, // 1 (63), + 0x01, 0x00, 0x00, 0x00, // 1 (64), + 0x01, 0x00, 0x00, 0x00, // 1 (65), + 0x01, 0x00, 0x00, 0x00, // 1 (66), + 0x01, 0x00, 0x00, 0x00, // 1 (67), + 0x01, 0x00, 0x00, 0x00, // 1 (68), + 0x01, 0x00, 0x00, 0x00, // 1 (69), + 0x01, 0x00, 0x00, 0x00, // 1 (6A), + 0x01, 0x00, 0x00, 0x00, // 1 (6B), + 0x01, 0x00, 0x00, 0x00, // 1 (6C), + 0x01, 0x00, 0x00, 0x00, // 1 (6D), + 0x01, 0x00, 0x00, 0x00, // 1 (6E), + 0x01, 0x00, 0x00, 0x00, // 1 (6F), + 0x01, 0x00, 0x00, 0x00, // 1 (70), + 0x01, 0x00, 0x00, 0x00, // 1 (71), + 0x01, 0x00, 0x00, 0x00, // 1 (72), + 0x01, 0x00, 0x00, 0x00, // 1 (73), + 0x01, 0x00, 0x00, 0x00, // 1 (74), + 0x01, 0x00, 0x00, 0x00, // 1 (75), + 0x01, 0x00, 0x00, 0x00, // 1 (76), + 0x01, 0x00, 0x00, 0x00, // 1 (77), + 0x01, 0x00, 0x00, 0x00, // 1 (78), + 0x01, 0x00, 0x00, 0x00, // 1 (79), + 0x01, 0x00, 0x00, 0x00, // 1 (7A), + 0x01, 0x00, 0x00, 0x00, // 1 (7B), + 0x01, 0x00, 0x00, 0x00, // 1 (7C), + 0x01, 0x00, 0x00, 0x00, // 1 (7D), + 0x01, 0x00, 0x00, 0x00, // 1 (7E), + 0x06, 0x00, 0x00, 0x00, // 6 (5C-75-00-00-00-7F), + }; + } +} + diff --git a/src/MsgPack.Json/Json/JsonEscapeSequence.EscapeTables.tt b/src/MsgPack.Json/Json/JsonEscapeSequence.EscapeTables.tt new file mode 100644 index 000000000..5ed0fc745 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonEscapeSequence.EscapeTables.tt @@ -0,0 +1,218 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ output extension=".cs" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Runtime.InteropServices; + +namespace MsgPack.Json +{ + partial class JsonEscapeSequence + { +<# +foreach (var spec in + new [] + { + new { Label = "Base", ExcludesTab = false, ExcludesHtmlChars = true }, + new { Label = "BaseWithoutTab", ExcludesTab = true, ExcludesHtmlChars = true }, + new { Label = "BaseWithHtmlChars", ExcludesTab = false, ExcludesHtmlChars = false }, + new { Label = "BaseWithHtmlCharsWithoutTab", ExcludesTab = true, ExcludesHtmlChars = false }, + } +) +{ +#> + + public static ReadOnlySpan <#= spec.Label #>EscapeTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(<#= spec.Label #>EscapeTableLittleEndian) : + MemoryMarshal.Cast(<#= spec.Label #>EscapeTableBigEndian); + + public static ReadOnlySpan <#= spec.Label #>EscapeLengthTable => + BitConverter.IsLittleEndian ? + MemoryMarshal.Cast(<#= spec.Label #>EscapeLengthTableLittleEndian) : + MemoryMarshal.Cast(<#= spec.Label #>EscapeLengthTableBigEndian); + + public static ReadOnlySpan <#= spec.Label #>EscapeTableBigEndian => + new byte[] + { +<# + for (var i = 0; i < 0x80; i++) + { +#> + <#= Generate(i, spec.ExcludesTab, spec.ExcludesHtmlChars, forLittleEndian: false) #>, +<# + } +#> + }; + + public static ReadOnlySpan <#= spec.Label #>EscapeTableLittleEndian => + new byte[] + { +<# + for (var i = 0; i < 0x80; i++) + { +#> + <#= Generate(i, spec.ExcludesTab, spec.ExcludesHtmlChars, forLittleEndian: true) #>, +<# + } +#> + }; + + public static ReadOnlySpan <#= spec.Label #>EscapeLengthTableBigEndian => + new byte[] + { +<# + for (var i = 0; i < 0x80; i++) + { +#> + <#= GenerateLength(i, spec.ExcludesTab, spec.ExcludesHtmlChars, forLittleEndian: false) #>, +<# + } +#> + }; + + public static ReadOnlySpan <#= spec.Label #>EscapeLengthTableLittleEndian => + new byte[] + { +<# + for (var i = 0; i < 0x80; i++) + { +#> + <#= GenerateLength(i, spec.ExcludesTab, spec.ExcludesHtmlChars, forLittleEndian: true) #>, +<# + } +#> + }; +<# +} +#> + } +} + +<#+ +string Generate(int codePoint, bool excludesTab, bool excludesHtmlChars, bool forLittleEndian) +{ + var bytes = GetEscapeSequenceBytes(codePoint, excludesTab, excludesHtmlChars); + if (bytes.Length > 8) + { + throw new Exception($"U+{codePoint:X4} requires {bytes.Length} bytes."); + } + + var span = new byte[8]; + bytes.CopyTo(span, 0); + + if (BitConverter.IsLittleEndian != forLittleEndian) + { + Array.Reverse(span); + } + + var result = BitConverter.ToUInt64(span, 0); + + return $"{String.Join(", ", span.Select(b => $"0x{b:X2}"))}, // 0x{result:X16}"; +} + +string GenerateLength(int codePoint, bool excludesTab, bool excludesHtmlChars, bool forLittleEndian) +{ + var bytes = GetEscapeSequenceBytes(codePoint, excludesTab, excludesHtmlChars); + var span = BitConverter.GetBytes(bytes.Length); + + if (BitConverter.IsLittleEndian != forLittleEndian) + { + Array.Reverse(span); + } + + return $"{String.Join(", ", span.Select(b => $"0x{b:X2}"))}, // {bytes.Length} ({BitConverter.ToString(bytes)})"; +} + +byte[] GetEscapeSequenceBytes(int codePoint, bool excludesTab, bool excludesHtmlChars) +{ + if (codePoint < 0x20) + { + switch (codePoint) + { + case '\b': + { + return new [] { (byte)'\\', (byte)'b' }; + } + case '\f': + { + return new [] { (byte)'\\', (byte)'f' }; + } + case '\r': + { + return new [] { (byte)'\\', (byte)'r' }; + } + case '\n': + { + return new [] { (byte)'\\', (byte)'n' }; + } + case '\t': + { + if (excludesTab) + { + break; + } + + return new [] { (byte)'\\', (byte)'t' }; + } + default: + { + return GetUnicodeEscapeEquence(codePoint); + } + } + } + + if (codePoint == '\\') + { + return new [] { (byte)'\\', (byte)'\\' }; + } + + if (!excludesHtmlChars) + { + switch(codePoint) + { + case '"': + case '/': + case '&': + case '\'': + case '<': + case '>': + { + return GetUnicodeEscapeEquence(codePoint); + } + } + } + + if (codePoint == '"') + { + return new [] { (byte)'\\', (byte)'"' }; + } + + if (codePoint == 0x7F) + { + return GetUnicodeEscapeEquence(codePoint); + } + + return new [] { (byte)codePoint }; +} + +byte[] GetUnicodeEscapeEquence(int codePoint) +{ + var result = new byte[6]; + result[0] = (byte)'\\'; + result[1] = (byte)'u'; + result[2] = (byte)(codePoint >> 24 & 0xFF); + result[3] = (byte)(codePoint >> 16 & 0xFF); + result[4] = (byte)(codePoint >> 8 & 0xFF); + result[5] = (byte)(codePoint & 0xFF); + return result; +} +#> diff --git a/src/MsgPack.Json/Json/JsonEscapeSequence.cs b/src/MsgPack.Json/Json/JsonEscapeSequence.cs index ef8af6318..9a4c8baf6 100644 --- a/src/MsgPack.Json/Json/JsonEscapeSequence.cs +++ b/src/MsgPack.Json/Json/JsonEscapeSequence.cs @@ -10,7 +10,7 @@ namespace MsgPack.Json /// /// Defines known escape sequence int UTF-8 and related structures. /// - internal static class JsonEscapeSequence + internal static partial class JsonEscapeSequence { public static ReadOnlySpan Unicode => new[] { (byte)'\\', (byte)'u' }; public static ReadOnlySpan ReverseSolidous => new[] { (byte)'\\', (byte)'\\' }; diff --git a/src/MsgPack.Json/Json/JsonFormatter.cs b/src/MsgPack.Json/Json/JsonFormatter.cs index 084622065..61e62689f 100644 --- a/src/MsgPack.Json/Json/JsonFormatter.cs +++ b/src/MsgPack.Json/Json/JsonFormatter.cs @@ -4,6 +4,8 @@ using System.Buffers; using System.Buffers.Text; +using System.Runtime.CompilerServices; +using MsgPack.Internal; namespace MsgPack.Json { @@ -12,10 +14,19 @@ namespace MsgPack.Json /// internal static class JsonFormatter { + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public static void WriteNull(IBufferWriter buffer) - => buffer.Write(JsonTokens.Null); + { + var span = buffer.GetSpan(4); + span[0] = (byte)'n'; + span[1] = (byte)'u'; + span[2] = (byte)'l'; + span[3] = (byte)'l'; + buffer.Advance(4); + } - public static void Format(float value, IBufferWriter buffer, JsonEncoderOptions options) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static void Format(float value, IBufferWriter buffer) { var span = buffer.GetSpan(0); while (!Utf8Formatter.TryFormat(value, span, out _)) @@ -24,7 +35,8 @@ public static void Format(float value, IBufferWriter buffer, JsonEncoderOp } } - public static void Format(double value, IBufferWriter buffer, JsonEncoderOptions options) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public static void Format(double value, IBufferWriter buffer) { var span = buffer.GetSpan(0); while (!Utf8Formatter.TryFormat(value, span, out _)) diff --git a/src/MsgPack.Json/Json/JsonNumberTokens.cs b/src/MsgPack.Json/Json/JsonNumberTokens.cs deleted file mode 100644 index e8d9b1c00..000000000 --- a/src/MsgPack.Json/Json/JsonNumberTokens.cs +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. -// This file is licensed under Apache2 license. -// See the LICENSE in the project root for more information. - -using System; - -namespace MsgPack.Json -{ - /// - /// Defines JSON number decoding related binary constants which are stored as native BLOB. - /// - internal static class JsonNumberTokens - { - public static readonly byte MinusSign = (byte)'-'; - public static readonly byte PlusSign = (byte)'+'; - public static readonly byte SingleByteUnicodeMinusSign = MinusSign; - public static readonly byte[][] MultiByteUnicodeMinusSigns = - new[] - { - new byte[] { 0xE2, 0x81, 0xBB }, // U+207B SUPERSCRIPT MINUS SIGN - new byte[] { 0xE2, 0x82, 0x8B }, // U+208B SUBSCRIPT MINUS SIGN - new byte[] { 0xE2, 0x88, 0x92 }, // U+2212 MINUS SIGN - new byte[] { 0xEF, 0xB9, 0xA3 }, // U+FE63 SMALL HYPHEN-MINUS - new byte[] { 0xEF, 0xBC, 0x8D }, // U+FF0D FULLWIDTH HYPHEN-MINUS - }; - public static readonly byte SingleByteUnicodePlus = PlusSign; - public static readonly byte[][] MultiByteUnicodePlusSigns = - new[] - { - new byte[] { 0xE2, 0x81, 0xBA }, // U+207A SUPERSCRIPT PLUS SIGN - new byte[] { 0xE2, 0x82, 0x8A }, // U+208A SUBSCRIPT PLUS SIGN - new byte[] { 0xEF, 0xAC, 0xA9 }, // U+FB29 HEBREW LETTER ALTERNATIVE PLUS SIGN - new byte[] { 0xEF, 0xB9, 0xA2 }, // U+FE62 SMALL PLUS SIGN - new byte[] { 0xEF, 0xBC, 0x8B }, // U+FE0B FULLWIDTH PLUS SIGN - }; - public static ReadOnlySpan Digits => new byte[] { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; - public static ReadOnlySpan NonZeroDigits => new byte[] { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39 }; - public static ReadOnlySpan ExponentialIndicators => new[] { (byte)'E', (byte)'e' }; - public static ReadOnlySpan PlusMinusSigns => new[] { (byte)'-', (byte)'+' }; - public static readonly byte[][] MultiByteUnicodePlusMinusSigns = - new[] - { - new byte[] { 0xE2, 0x81, 0xBB }, // U+207B SUPERSCRIPT MINUS SIGN - new byte[] { 0xE2, 0x82, 0x8B }, // U+208B SUBSCRIPT MINUS SIGN - new byte[] { 0xE2, 0x88, 0x92 }, // U+2212 MINUS SIGN - new byte[] { 0xEF, 0xB9, 0xA3 }, // U+FE63 SMALL HYPHEN-MINUS - new byte[] { 0xEF, 0xBC, 0x8D }, // U+FF0D FULLWIDTH HYPHEN-MINUS - new byte[] { 0xE2, 0x81, 0xBA }, // U+207A SUPERSCRIPT PLUS SIGN - new byte[] { 0xE2, 0x82, 0x8A }, // U+208A SUBSCRIPT PLUS SIGN - new byte[] { 0xEF, 0xAC, 0xA9 }, // U+FB29 HEBREW LETTER ALTERNATIVE PLUS SIGN - new byte[] { 0xEF, 0xB9, 0xA2 }, // U+FE62 SMALL PLUS SIGN - new byte[] { 0xEF, 0xBC, 0x8B }, // U+FE0B FULLWIDTH PLUS SIGN - }; - public static ReadOnlySpan SingleByteUnicodeExponentialIndicators => ExponentialIndicators; - public static readonly byte[][] MultiByteUnicodeExponentialIndicators = - new[] - { - new byte[] { 0xE1, 0xB4, 0xB1 }, // U+1D31 MODIFIER LETTER CAPITAL E - new byte[] { 0xE1, 0xB5, 0x89 }, // U+1D49 MODIFIER LETTER SMALL E - new byte[] { 0xE2, 0x82, 0x91 }, // U+2091 LATIN SUBSCRIPT SMALL LETTER E - new byte[] { 0xE2, 0x84, 0xAF }, // U+212F SCRIPT SMALL E - new byte[] { 0xE2, 0x84, 0xB0 }, // U+2130 SCRIPT CAPITAL E - new byte[] { 0xE2, 0x85, 0x87 }, // U+2147 DOUBLE-STRUCK ITALIC SMALL E - new byte[] { 0xE2, 0x92, 0xBA }, // U+24BA CIRCLED LATIN CAPITAL LETTER E - new byte[] { 0xE2, 0x93, 0x94 }, // U+24D4 CIRCLED LATIN SMALL LETTER E - new byte[] { 0xEF, 0xBC, 0xA5 }, // U+FF25 FULLWIDTH LATIN CAPITAL LETTER E - new byte[] { 0xEF, 0xBD, 0x85 }, // U+FF45 FULLWIDTH LATIN SMALL LETTER E - new byte[] { 0xF0, 0x9D, 0x90, 0x84 }, // U+1D404 MATHEMATICAL BOLD CAPITAL E - new byte[] { 0xF0, 0x9D, 0x90, 0x9E }, // U+1D41E ATHEMATICAL BOLD SMALL E - new byte[] { 0xF0, 0x9D, 0x90, 0xB8 }, // U+1D438 MATHEMATICAL ITALIC CAPITAL E - new byte[] { 0xF0, 0x9D, 0x91, 0x92 }, // U+1D452 MATHEMATICAL ITALIC SMALL E - new byte[] { 0xF0, 0x9D, 0x91, 0xAC }, // U+1D46C MATHEMATICAL BOLD ITALIC CAPITAL E - new byte[] { 0xF0, 0x9D, 0x92, 0x86 }, // U+1D486 MATHEMATICAL BOLD ITALIC SMALL E - new byte[] { 0xF0, 0x9D, 0x93, 0x94 }, // U+1D4D4 MATHEMATICAL BOLD SCRIPT CAPITAL E - new byte[] { 0xF0, 0x9D, 0x93, 0xAE }, // U+1D4EE MATHEMATICAL BOLD SCRIPT SMALL E - new byte[] { 0xF0, 0x9D, 0x94, 0x88 }, // U+1D508 MATHEMATICAL FRAKTUR CAPITAL E - new byte[] { 0xF0, 0x9D, 0x94, 0xA2 }, // U+1D522 MATHEMATICAL FRAKTUR SMALL E - new byte[] { 0xF0, 0x9D, 0x94, 0xBC }, // U+1D53C MATHEMATICAL DOUBLE-STRUCK CAPITAL E - new byte[] { 0xF0, 0x9D, 0x95, 0x96 }, // U+1D556 MATHEMATICAL DOUBLE-STRUCK SMALL E - new byte[] { 0xF0, 0x9D, 0x95, 0xB0 }, // U+1D570 MATHEMATICAL BOLD FRAKTUR CAPITAL E - new byte[] { 0xF0, 0x9D, 0x96, 0x8A }, // U+1D58A MATHEMATICAL BOLD FRAKTUR SMALL E - new byte[] { 0xF0, 0x9D, 0x96, 0xA4 }, // U+1D5A4 MATHEMATICAL SANS-SERIF CAPITAL E - new byte[] { 0xF0, 0x9D, 0x96, 0xBE }, // U+1D5BE MATHEMATICAL SANS-SERIF SMALL E - new byte[] { 0xF0, 0x9D, 0x97, 0x98 }, // U+1D5D8 MATHEMATICAL SANS-SERIF BOLD CAPITAL E - new byte[] { 0xF0, 0x9D, 0x97, 0xB2 }, // U+1D5F2 MATHEMATICAL SANS-SERIF BOLD SMALL E - new byte[] { 0xF0, 0x9D, 0x98, 0x8C }, // U+1D60C MATHEMATICAL SANS-SERIF ITALIC CAPITAL E - new byte[] { 0xF0, 0x9D, 0x98, 0xA6 }, // U+1D626 MATHEMATICAL SANS-SERIF ITALIC SMALL E - new byte[] { 0xF0, 0x9D, 0x99, 0x80 }, // U+1D640 MATHEMATICAL SANS-SERIF BOLD ITALIC CAPITAL E - new byte[] { 0xF0, 0x9D, 0x99, 0x9A }, // U+1D65A MATHEMATICAL SANS-SERIF BOLD ITALIC SMALL E - new byte[] { 0xF0, 0x9D, 0x99, 0xB4 }, // U+1D674 MATHEMATICAL MONOSPACE CAPITAL E - new byte[] { 0xF0, 0x9D, 0x9A, 0x8E }, // U+1D68E MATHEMATICAL MONOSPACE SMALL E - new byte[] { 0xF0, 0x9F, 0x84, 0xB4 }, // U+1F134 SQUARED LATIN CAPITAL LETTER E - }; - } -} diff --git a/src/MsgPack.Json/Json/JsonStringTokens.cs b/src/MsgPack.Json/Json/JsonStringTokens.cs index 89334c75b..c3a353633 100644 --- a/src/MsgPack.Json/Json/JsonStringTokens.cs +++ b/src/MsgPack.Json/Json/JsonStringTokens.cs @@ -13,31 +13,5 @@ internal static class JsonStringTokens { public static ReadOnlySpan AnyQuotations => new[] { (byte)'"', (byte)'\'' }; public static ReadOnlySpan DoubleQuotation => AnyQuotations.Slice(0, 1); - public static ReadOnlySpan SingleQuotationDelimiters => new[] { (byte)'\'', (byte)'\\' }; - public static ReadOnlySpan DoubleQuotationDelimiters => new[] { (byte)'"', (byte)'\\' }; - public static readonly byte UnescapedCharactorsOffset = UnescapedCharactors[0]; - public static ReadOnlySpan UnescapedCharactors => - new byte[] - { - (byte)'"', // U+0022 " - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, // U+0023-U+002E - (byte)'/', // U+002F / - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, // U+0030-U+003F - 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, // U+0040-U+004F - 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, // U+0050-U+005B - (byte)'\\', // U+005C \ - 0xBD, 0xBE, 0xBF, // U+005D-U+005F - 0xC0, 0xC1, // U+0060-U+0061 - (byte)'\b', // U+0062 -> U+0008 Back Space - 0xC3, 0xC4, 0xC5, // U+0063-U+0065 - (byte)'\f', // U+0066 -> U+000C Form Feed - 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, // UL0067-U+006D - (byte)'\n', // U+006E -> U+000A Line Feed - 0xCF, // U+006F - 0xD0, 0xD1, // U+0070-U+0071 - (byte)'\r', // U+0072 -> U+000D Carriage Return - 0xD3, // U+0073 - (byte)'\t', // U+0074 -> U+0009 Tab - }; } } diff --git a/src/MsgPack.Json/Json/JsonThrow.cs b/src/MsgPack.Json/Json/JsonThrow.cs index aa6f09536..f26a1053f 100644 --- a/src/MsgPack.Json/Json/JsonThrow.cs +++ b/src/MsgPack.Json/Json/JsonThrow.cs @@ -35,7 +35,7 @@ public static void SurrogateCharInUtf8(long position, int codePoint) => throw new FormatException($"Input UTF-8 has surrogate charactor U+{codePoint:X4} at position {position:#,0}."); public static void IsNotArrayNorObject(in ReadOnlySequence sequence, long position) - => throw new FormatException($"Char '{Stringify(sequence)}' at position {position:#,0} is not start of array nor object."); + => throw new FormatException($"Char {Stringify(sequence)} at position {position:#,0} is not start of array nor object."); public static void IsNotArray(long position) => throw new FormatException($"Char '{{' at position {position:#,0} is not start of array."); @@ -44,7 +44,7 @@ public static void IsNotObject(long position) => throw new FormatException($"Char '[' at position {position:#,0} is not start of object."); public static void IsNotType(Type type, in ReadOnlySequence unit, long position) - => throw new FormatException($"Char '{Stringify(unit)}' at position {position:#,0} is not valid for {type} value."); + => throw new FormatException($"Char {Stringify(unit)} at position {position:#,0} is not valid for {type} value."); public static void TooLongNumber(long numberLength, long maxLength, long position) => throw new FormatException($"The number at position {position:#,0} has {numberLength:#,0} charactors, but maximum allowed length is {maxLength:#,0}."); diff --git a/src/MsgPack.Json/Json/JsonTokens.cs b/src/MsgPack.Json/Json/JsonTokens.cs deleted file mode 100644 index 0ac1260dd..000000000 --- a/src/MsgPack.Json/Json/JsonTokens.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. -// This file is licensed under Apache2 license. -// See the LICENSE in the project root for more information. - -using System; - -namespace MsgPack.Json -{ - /// - /// Defines JSON tokens as UTF-8 byte blob data. - /// - internal static class JsonTokens - { - // Utilizes C# optimization to read data directly from blob metadata without array allocation. - public static ReadOnlySpan Null => new[] { (byte)'n', (byte)'u', (byte)'l', (byte)'l' }; - public static ReadOnlySpan True => new[] { (byte)'t', (byte)'r', (byte)'u', (byte)'e' }; - public static ReadOnlySpan False => new[] { (byte)'f', (byte)'a', (byte)'l', (byte)'s', (byte)'e' }; - public static ReadOnlySpan ArrayStart => new[] { (byte)'[' }; - public static ReadOnlySpan ArrayEnd => new[] { (byte)']' }; - public static ReadOnlySpan MapStart => new[] { (byte)'{' }; - public static ReadOnlySpan MapEnd => new[] { (byte)'}' }; - public static ReadOnlySpan Whitespace => new[] { (byte)' ' }; - public static ReadOnlySpan Comma => new[] { (byte)',' }; - public static ReadOnlySpan Colon => new[] { (byte)':' }; - public static ReadOnlySpan Quatation => new[] { (byte)'"' }; - - public static ReadOnlySpan Undefined => new[] { (byte)'u', (byte)'n', (byte)'d', (byte)'e', (byte)'f', (byte)'i', (byte)'n', (byte)'e', (byte)'d' }; - } -} diff --git a/src/MsgPack.Json/Json/JsonTriviaTokens.cs b/src/MsgPack.Json/Json/JsonTriviaTokens.cs index 89a3552f1..c2bc1b2b4 100644 --- a/src/MsgPack.Json/Json/JsonTriviaTokens.cs +++ b/src/MsgPack.Json/Json/JsonTriviaTokens.cs @@ -2,8 +2,6 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. -using System; - namespace MsgPack.Json { /// @@ -11,13 +9,6 @@ namespace MsgPack.Json /// internal static class JsonTriviaTokens { - public static ReadOnlySpan StandardWhitespaces => new[] { (byte)' ', (byte)'\t', (byte)'\r', (byte)'\n' }; - public static ReadOnlySpan NewLine => new[] { (byte)'\r', (byte)'\n' }; - public static ReadOnlySpan SingleByteUnicodeWhitespaces => - new[] { - (byte)' ', (byte)'\t', (byte)'\r', (byte)'\n', - (byte)'\u000B',(byte)'\u000C' - }; public static readonly byte[][] MultiByteUnicodeWhitespaces = new[] { @@ -41,8 +32,5 @@ internal static class JsonTriviaTokens new byte[] { 0xE2, 0x81, 0x9F }, // U+205F MEDIUM MATHEMATICAL SPACE new byte[] { 0xEe, 0x80, 0x80 }, // U+3000 IDEOGRAPHIC SPACE }; - public static ReadOnlySpan SingleLineCommentStart2 => new byte[] { (byte)'/', (byte)'/' }; - public static ReadOnlySpan MultiLineCommentStart => new byte[] { (byte)'/', (byte)'*' }; - public static ReadOnlySpan MultiLineCommentEnd => new byte[] { (byte)'*', (byte)'/' }; } } diff --git a/src/MsgPack.Json/Json/SimpleJsonDecoder.cs b/src/MsgPack.Json/Json/SimpleJsonDecoder.cs index 9fd56f598..a5373ef1c 100644 --- a/src/MsgPack.Json/Json/SimpleJsonDecoder.cs +++ b/src/MsgPack.Json/Json/SimpleJsonDecoder.cs @@ -2,7 +2,10 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +using System; using System.Buffers; +using System.Runtime.CompilerServices; +using MsgPack.Internal; namespace MsgPack.Json { @@ -14,14 +17,25 @@ internal sealed class SimpleJsonDecoder : JsonDecoder public SimpleJsonDecoder(JsonDecoderOptions options) : base(options) { } - protected override void ReadTrivia(in SequenceReader source, out ReadOnlySequence trivia) + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + protected sealed override long ReadTrivia(ref SequenceReader source) + => source.AdvancePastAny((byte)' ', (byte)'\t', (byte)'\r', (byte)'\n'); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public sealed override unsafe bool TryDecodeNull(ref SequenceReader source, out int requestHint) { - var offset = source.Consumed; - var consumed = source.AdvancePastAny(JsonTriviaTokens.StandardWhitespaces); - trivia = source.Sequence.Slice(offset, consumed); - } + byte* pNull = stackalloc byte[] { (byte)'n', (byte)'u', (byte)'l', (byte)'l' }; + ReadOnlySpan @null = new ReadOnlySpan(pNull, 4); - protected override bool TryReadNull(in SequenceReader source) - => source.IsNext(JsonTokens.Null, advancePast: true); + if (source.IsNext(@null, advancePast: true)) + { + requestHint = 0; + source.Advance(4); + return true; + } + + requestHint = Math.Max(0, 4 - (int)source.Remaining); + return false; + } } } diff --git a/src/MsgPack.Json/Json/StringBuilderBufferWriter.cs b/src/MsgPack.Json/Json/StringBuilderBufferWriter.cs index 649d095ae..3d6330da9 100644 --- a/src/MsgPack.Json/Json/StringBuilderBufferWriter.cs +++ b/src/MsgPack.Json/Json/StringBuilderBufferWriter.cs @@ -33,7 +33,8 @@ public StringBuilderBufferWriter(StringBuilder stringBuilder, JsonDecoderOptions this._stringBuilder = stringBuilder; this._bufferPool = options.CharBufferPool; this._clearsArray = options.ClearsBuffer; - this._buffer = this._bufferPool.Rent(options.MaxCharBufferLength); +#warning TODO: OPTION + this._buffer = this._bufferPool.Rent(64/*options.MaxCharBufferLength*/); } public void Dispose() diff --git a/src/MsgPack.Json/MsgPack.Json.csproj b/src/MsgPack.Json/MsgPack.Json.csproj index d3a1baa67..d7dfa0faa 100644 --- a/src/MsgPack.Json/MsgPack.Json.csproj +++ b/src/MsgPack.Json/MsgPack.Json.csproj @@ -12,10 +12,15 @@ + - + + True + True + JsonEscapeSequence.EscapeTables.tt + @@ -27,6 +32,10 @@ TextTemplatingFileGenerator JsonDecoder.Numbers.cs + + TextTemplatingFileGenerator + JsonEscapeSequence.EscapeTables.cs + @@ -44,6 +53,15 @@ True JsonDecoder.Numbers.tt + + True + True + JsonEscapeSequence.EscapeTables.tt + + + + + From 2aa85e12e9c67173fe394cfcd8f8988741622ce6 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 21 Jun 2020 01:20:25 +0900 Subject: [PATCH 25/82] Add v2 benchmarks Note that they uses hand-made serializer yet because the purpose is PoC of new serializer primitives design. --- src/MsgPack.Core/_SampleObject.cs | 1195 ---------- .../Serializers/MsgPackCliSerializer.cs | 133 ++ test/Benchmark/Program.cs | 172 ++ test/Benchmark/_SampleObject.cs | 2003 +++++++++++++++++ 4 files changed, 2308 insertions(+), 1195 deletions(-) delete mode 100644 src/MsgPack.Core/_SampleObject.cs create mode 100644 test/Benchmark/Program.cs create mode 100644 test/Benchmark/_SampleObject.cs diff --git a/src/MsgPack.Core/_SampleObject.cs b/src/MsgPack.Core/_SampleObject.cs deleted file mode 100644 index ec77520f9..000000000 --- a/src/MsgPack.Core/_SampleObject.cs +++ /dev/null @@ -1,1195 +0,0 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. -// This file is licensed under Apache2 license. -// See the LICENSE in the project root for more information. - -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Threading.Tasks; -using MsgPack.Internal; -using MsgPack.Serialization.Internal; - -namespace MsgPack.Samples -{ - public class SampleObject - { - public int Age { get; set; } - public string Name { get; set; } = null!; - public bool IsActive { get; set; } - public IDictionary Attributes { get; } = new Dictionary(); - public IList Roles { get; } = new List(); - } - - // MP.Core.Abstraction - // MP.Core / MP.Json.Core / MP.Yaml.Core / MP.Coff.Core - // MP.Serialization.Core -> Interfaces, .... - // MP.Serialization -> Basic serializers, context, utilities, Stream adapter, byte[] adapter, etc. - // MP.Serialization.Serializers-> Extended serializers implementation. - // MP.Serialization.Reflection -> Reflection - // MP.Serialization.ILGeneration -> Reflection - // MP.Serialization.SourceGeneration -> Reflection - // MP.Extensions / MP.Json.Extensions / MP.Yaml.Extensions / MP.Coff.Extensions -> MPO, DOM, etc - // MP.Compatibility1 -> v1 compatibility shims - // MP.Cli -> v1 compatibility shims - - // Packer/Unpacker compatibility note: - // Packer -> Packer.Create(new MemoryStream()) - // Unpacker -> Unpacker.Create(new MemoryStream())??? - - // For PoC of MVP and reference for emit. - /// - /// Sample hand made serializer. - /// - public sealed class SampleSerializer : IObjectSerializer - { - private bool UseArray { get; set; } - - public void Serialize(in SerializationOperationContext context, SampleObject obj, IBufferWriter writer) - { - var encoder = context.Encoder; - if (obj == null) - { - encoder.EncodeNull(writer); - return; - } - - if (this.UseArray) - { - encoder.EncodeArrayStart(5, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeString(obj.Name, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - encoder.EncodeInt32(obj.Age, writer); - encoder.EncodeBoolean(obj.IsActive, writer); - if (obj.Attributes == null) - { - encoder.EncodeNull(writer); - } - else - { - encoder.EncodeMapStart(obj.Attributes.Count, writer, context.CollectionContext); // OMITTABLE - var i = 0; // OMITTABLE - foreach (var entry in obj.Attributes) - { - encoder.EncodeMapKeyStart(i, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeString(entry.Key, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - encoder.EncodeMapKeyEnd(i, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeMapValueStart(i, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeString(entry.Value, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - encoder.EncodeMapValueEnd(i, writer, context.CollectionContext); // OMITTABLE - } - encoder.EncodeMapEnd(obj.Attributes.Count, writer, context.CollectionContext); - } - if (obj.Roles == null) - { - encoder.EncodeNull(writer); - } - else - { - encoder.EncodeArrayStart(obj.Roles.Count, writer, context.CollectionContext); // OMITTABLE - var i = 0; // OMITTABLE - foreach (var item in obj.Roles) - { - encoder.EncodeArrayItemStart(i, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeString(item, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - encoder.EncodeArrayItemEnd(i, writer, context.CollectionContext); // OMITTABLE - } - encoder.EncodeArrayEnd(obj.Roles.Count, writer, context.CollectionContext); - } - encoder.EncodeArrayEnd(5, writer, context.CollectionContext); // OMITTABLE - } - else - { - encoder.EncodeMapStart(5, writer, context.CollectionContext); // OMITTABLE - ReadOnlySpan key0 = new[] { (byte)'N', (byte)'a', (byte)'m', (byte)'e' }; // static readonly - encoder.EncodeString(key0, 4, writer, context.CancellationToken); - encoder.EncodeString(obj.Name, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - ReadOnlySpan key1 = new[] { (byte)'A', (byte)'g', (byte)'e' }; // static readonly - encoder.EncodeString(key1, 3, writer, context.CancellationToken); - encoder.EncodeInt32(obj.Age, writer); - ReadOnlySpan key2 = new[] { (byte)'I', (byte)'s', (byte)'a', (byte)'c', (byte)'t', (byte)'i', (byte)'v', (byte)'e' }; // static readonly - encoder.EncodeString(key2, 8, writer, context.CancellationToken); - encoder.EncodeBoolean(obj.IsActive, writer); - ReadOnlySpan key3 = new[] { (byte)'A', (byte)'t', (byte)'t', (byte)'r', (byte)'i', (byte)'b', (byte)'u', (byte)'t', (byte)'e', (byte)'s' }; // static readonly - encoder.EncodeString(key3, 10, writer, context.CancellationToken); - if (obj.Attributes == null) - { - encoder.EncodeNull(writer); - } - else - { - encoder.EncodeMapStart(obj.Attributes.Count, writer, context.CollectionContext); // OMITTABLE - var i = 0; // OMITTABLE - foreach (var entry in obj.Attributes) - { - encoder.EncodeMapKeyStart(i, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeString(entry.Key, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - encoder.EncodeMapKeyEnd(i, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeMapValueStart(i, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeString(entry.Value, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - encoder.EncodeMapValueEnd(i, writer, context.CollectionContext); // OMITTABLE - } - encoder.EncodeMapEnd(obj.Attributes.Count, writer, context.CollectionContext); - } - ReadOnlySpan key4 = new[] { (byte)'R', (byte)'o', (byte)'l', (byte)'e' }; // static readonly - encoder.EncodeString(key4, 4, writer, context.CancellationToken); - if (obj.Roles == null) - { - encoder.EncodeNull(writer); - } - else - { - encoder.EncodeArrayStart(obj.Roles.Count, writer, context.CollectionContext); // OMITTABLE - var i = 0; // OMITTABLE - foreach (var item in obj.Roles) - { - encoder.EncodeArrayItemStart(i, writer, context.CollectionContext); // OMITTABLE - encoder.EncodeString(item, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - encoder.EncodeArrayItemEnd(i, writer, context.CollectionContext); // OMITTABLE - } - encoder.EncodeArrayEnd(obj.Roles.Count, writer, context.CollectionContext); - } - encoder.EncodeMapEnd(5, writer, context.CollectionContext); // OMITTABLE - } - } - - public async ValueTask SerializeAsync(SerializationOperationContext context, SampleObject obj, Stream streamSink) - { - await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) - { - this.Serialize(context, obj, writer); - } - } - - private static readonly MsgPackStringTrie DeserializationTrie = InitializeDeserializationTrie(); - private static MsgPackStringTrie InitializeDeserializationTrie() - { - ReadOnlySpan __name = new byte[] { 0xA4, (byte)'N', (byte)'a', (byte)'m', (byte)'e' }; - ReadOnlySpan __age = new byte[] { 0xA3, (byte)'A', (byte)'g', (byte)'e' }; - ReadOnlySpan __isActive = new byte[] { 0xAA, (byte)'i', (byte)'s', (byte)'A', (byte)'c', (byte)'t', (byte)'i', (byte)'v', (byte)'e' }; - ReadOnlySpan __roles = new byte[] { 0xA4, (byte)'N', (byte)'a', (byte)'m', (byte)'e', }; - ReadOnlySpan __attributes = new byte[] { 0xAC, (byte)'A', (byte)'t', (byte)'t', (byte)'r', (byte)'i', (byte)'b', (byte)'u', (byte)'t', (byte)'e', (byte)'s' }; - var trie = new MsgPackStringTrie(5); - trie.TryAdd(__name, 0); - trie.TryAdd(__age, 1); - trie.TryAdd(__isActive, 2); - trie.TryAdd(__roles, 3); - trie.TryAdd(__attributes, 4); - return trie; - } - - public SampleObject Deserialize(in DeserializationOperationContext context, in SequenceReader reader) - { - // T が参照型でデフォルトコンストラクターがある -> DeserializeTo - var obj = new SampleObject(); - this.DeserializeTo(context, reader, obj); - return obj; - - // T にデフォルトコンストラクターがない -> from DeserializeTo と同じ処理をインライン実装 - // .... - // return new SampleObject(name, age, isActive, roles, attributes); - - // T がミュータブルな値型 - // ... - // var obj = default; - // obj.Name = name; - // obj.Age = age; - // obj.IsActive = isActive; - // obj.Roles = roles; // Roles は絶対に null のはず。 - // obj.Attributes = attributes; // Attributes は絶対に null のはず。 - // return obj; - } - - public async ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource) - { - // T が参照型でデフォルトコンストラクターがある -> DeserializeToAsync - var obj = new SampleObject(); - await this.DeserializeToAsync(context, streamSource, obj).ConfigureAwait(false); - return obj; - - // T にデフォルトコンストラクターがない -> DeserializeToAsync と同じ処理をインライン実装 - // .... - // return new SampleObject(name, age, isActive, roles, attributes); - - // T がミュータブルな値型 - // ... - // var obj = default; - // obj.Name = name; - // obj.Age = age; - // obj.IsActive = isActive; - // obj.Roles = roles; // Roles は絶対に null のはず。 - // obj.Attributes = attributes; // Attributes は絶対に null のはず。 - // return obj; - } - - public void DeserializeTo(in DeserializationOperationContext context, in SequenceReader reader, in SampleObject obj) - { - string name = default!; - int age = default; - bool isActive = default; - var roles = obj.Roles; - var attributes = obj.Attributes; - - var decoder = context.Decoder; - CollectionType arrayOrMap; - long itemsCount; - CollectionItemIterator propertyIterator; - - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - arrayOrMap = decoder.DecodeArrayOrMapHeader(reader, out itemsCount); - if (itemsCount < 5) - { - throw new MessageTypeException(); // Use Throws - } - - propertyIterator = default; - } - else - { - arrayOrMap = decoder.DecodeArrayOrMap(reader, out propertyIterator); - itemsCount = -1; - } - - context.IncrementDepth(); - - // If !SerializerGenerationOptions.InfersObjectSerialization && SerializerGenerationOptions.UseArray || typeof(T).IsDefined([SerializeAs(Array)]) - if (arrayOrMap.IsArray) - { - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - CheckNextItemExists(reader, ref propertyIterator); - } - - name = decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - CheckNextItemExists(reader, ref propertyIterator); - } - - age = decoder.DecodeInt32(reader); - - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - CheckNextItemExists(reader, ref propertyIterator); - } - - isActive = decoder.DecodeBoolean(reader); - - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - CheckNextItemExists(reader, ref propertyIterator); - } - - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - var arrayLength = decoder.DecodeArrayHeader(reader); - // If settable - //if (roles == null) - //{ - // roles = new List(arrayLength); // or 0 - //} - - for (var i = 0; i < arrayLength; i++) - { - roles.Add(decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding - } - } - else - { - var iterator = decoder.DecodeArray(reader); - while (!iterator.CollectionEnds(reader)) - { - roles.Add(decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding - } - iterator.Drain(reader); - } - context.DecrementDepth(); - - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - CheckNextItemExists(reader, ref propertyIterator); - } - - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - var mapCount = decoder.DecodeMapHeader(reader); - // If settable - //if (attributes == null) - //{ - // attributes = new Dictionary(mapCount); // or 0 - //} - for (var i = 0; i < mapCount; i++) - { - attributes.Add( - decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding - decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding - ); - } - } - else - { - var iterator = decoder.DecodeMap(reader); - while (!iterator.CollectionEnds(reader)) - { - attributes.Add( - decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding - decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding - ); - } - iterator.Drain(reader); - } - context.DecrementDepth(); - } - else - { - // Map - - // if !decoder.FormatFeatures.CanCountCollectionItems // OPTIMIZABLE - // while(!propertyIterator.CollectionEnds(reader)) - for (var i = 0; i < itemsCount; i++) - { - decoder.GetRawString(reader, out ReadOnlySpan key, context.CancellationToken); - - context.ValidatePropertyKeyLength(reader.Consumed, key.Length); - - // Use inlined trie, prefixed by the count as MP uint32. - // char is UTF-8, big endian. - // 1st node: [length(1-5)][chars(7-3)] - switch (DeserializationTrie.GetOrDefault(key)) - { - case 0: - { - name = decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding - break; - } - case 1: - { - age = decoder.DecodeInt32(reader); - break; - } - case 2: - { - isActive = decoder.DecodeBoolean(reader); - break; - } - case 3: - { - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - var arrayLength = decoder.DecodeArrayHeader(reader); - // If settable - //if (roles == null) - //{ - // roles = new List(arrayLength); // or 0 - //} - for (var j = 0; j < arrayLength; j++) - { - roles.Add(decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding - } - } - else - { - var iterator = decoder.DecodeArray(reader); - while (!iterator.CollectionEnds(reader)) - { - roles.Add(decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding - } - iterator.Drain(reader); - } - context.DecrementDepth(); - break; - } - case 4: - { - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - var mapCount = decoder.DecodeMapHeader(reader); - // If settable - //if (attributes == null) - //{ - // attributes = new Dictionary(mapCount); // or 0 - //} - for (var j = 0; j < mapCount; j++) - { - attributes.Add( - decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding - decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding - ); - } - } - else - { - var iterator = decoder.DecodeMap(reader); - while (!iterator.CollectionEnds(reader)) - { - attributes.Add( - decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding - decoder.DecodeString(reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding - ); - } - iterator.Drain(reader); - } - context.DecrementDepth(); - break; - } - } - } - } - - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - decoder.Drain(reader, context.CollectionContext, itemsCount - 5, context.CancellationToken); - } - else - { - propertyIterator.Drain(reader); - } - - context.DecrementDepth(); - - obj.Name = name; - obj.Age = age; - obj.IsActive = isActive; - // If settable - // obj.Roles = roles; - // If settable - // obj.Attributes = attributes; - } - - private static void CheckNextItemExists(SequenceReader reader, ref CollectionItemIterator propertyIterator) - { - if (!propertyIterator.CollectionEnds(reader)) - { - throw new MessageTypeException(); // Use Throws - } - } - - private static bool TryDecodeArrayOrMapHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out long itemsCount, out CollectionType arrayOrMap, out CollectionItemIterator propertyIterator, out int requestHint) - { - context.IncrementDepth(); - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(reader, out itemsCount, out requestHint); - if (arrayOrMap.IsNone) - { - propertyIterator = default; - return false; - } - - if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(reader, out itemsCount); - if (itemsCount < 5) - { - throw new MessageTypeException(); // Use Throws - } - - propertyIterator = default; - } - else - { - arrayOrMap = context.Decoder.DecodeArrayOrMap(reader, out propertyIterator); - itemsCount = -1; - } - - memory = memory.Slice(unchecked((int)reader.Consumed)); - - return true; - } - - private static bool TryDecodeArrayHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out long arrayLength, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - arrayLength = context.Decoder.DecodeArrayHeader(reader, out requestHint); - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeMapHeader(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out long mapCount, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - mapCount = context.Decoder.DecodeMapHeader(reader, out requestHint); - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeValueOfName(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string name, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - name = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeValueOfAge(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out int age, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - age = context.Decoder.DecodeInt32(reader, out requestHint); - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeValueOfIsActive(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out bool isActive, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - isActive = context.Decoder.DecodeBoolean(reader, out requestHint); - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeItemOfRoles(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string item, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - item = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeKeyOfAttributes(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string key, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - key = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeValueOfAttributes(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out string value, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - value = context.Decoder.DecodeString(reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeArray(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - iterator = context.Decoder.DecodeArray(reader, out requestHint); - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryDecodeMap(in DeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - iterator = context.Decoder.DecodeMap(reader, out requestHint); - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private static bool TryGetRawString(in DeserializationOperationContext context, ref ReadOnlyMemory memory, [NotNullWhen(true)]out byte[] key, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - - ReadOnlySpan span; - if (!context.Decoder.GetRawString(reader, out span, out requestHint, context.CancellationToken)) - { - key = default!; - return false; - } - - key = context.ArrayPool.Rent(span.Length); - span.CopyTo(key); - return true; - } - - private static bool TryDrain(in DeserializationOperationContext context, ref ReadOnlyMemory memory, long remaining, out int requestHint) - { - var reader = new SequenceReader(new ReadOnlySequence(memory)); - context.Decoder.Drain(reader, context.CollectionContext, remaining, out requestHint); - if (requestHint == 0) - { - memory = memory.Slice(unchecked((int)reader.Consumed)); - } - return requestHint == 0; - } - - private bool TryCheckNextItemExists(ref CollectionItemIterator propertyIterator, ReadOnlyMemory memory, out int requestHint) - { - if (propertyIterator.CollectionEnds(memory, out requestHint)) - { - throw new MessageTypeException(); // use throws - } - - return requestHint == 0; - } - - public async ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, SampleObject obj) - { - // T が値型 - // throw new NotSupportedException(); - - var buffer = context.ArrayPool.Rent(2 * 1024 * 1024); - try - { - var provider = new StreamReadOnlyMemoryProvider(streamSource, buffer); - var memory = await provider.GetNextAsync(default, 0, context.CancellationToken).ConfigureAwait(false); - int requestHint; - - string name = default!; - int age = default; - bool isActive = default; - var roles = obj.Roles; - var attributes = obj.Attributes; - - var decoder = context.Decoder; - context.IncrementDepth(); - - long itemsCount; - CollectionType arrayOrMap; - CollectionItemIterator propertyIterator; - while (!TryDecodeArrayOrMapHeader(context, ref memory, out itemsCount, out arrayOrMap, out propertyIterator, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - // If !SerializerGenerationOptions.InfersObjectSerialization && SerializerGenerationOptions.UseArray || typeof(T).IsDefined([SerializeAs(Array)]) - if (arrayOrMap.IsArray) - { - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - - while (!TryDecodeValueOfName(context, ref memory, out name, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - - while (!TryDecodeValueOfAge(context, ref memory, out age, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - - while (!TryDecodeValueOfIsActive(context, ref memory, out isActive, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - - context.IncrementDepth(); - - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - long arrayLength; - while (!TryDecodeArrayHeader(context, ref memory, out arrayLength, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - // If settable - //if (roles == null) - //{ - // roles = new List(arrayLength); // or 0 - //} - - for (var i = 0; i < arrayLength; i++) - { - string item; - while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - roles.Add(item); - } - } - else - { - CollectionItemIterator iterator; - while (!TryDecodeArray(context, ref memory, out iterator, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - while (!iterator.CollectionEnds(memory, out requestHint)) - { - if (requestHint != 0) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - string item; - while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - roles.Add(item); - } - - while(!iterator.Drain(ref memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - context.DecrementDepth(); - - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - long mapCount; - while (!TryDecodeMapHeader(context, ref memory, out mapCount, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - // If settable - //if (attributes == null) - //{ - // attributes = new Dictionary(mapCount); // or 0 - //} - for (var i = 0; i < mapCount; i++) - { - string key; - while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - string value; - while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - attributes.Add(key, value); - } - } - else - { - CollectionItemIterator iterator; - while (!TryDecodeMap(context, ref memory, out iterator, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - while (!iterator.CollectionEnds(memory, out requestHint)) - { - if (requestHint != 0) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - string key; - while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - string value; - while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - attributes.Add(key, value); - } - - while (!iterator.Drain(ref memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - context.DecrementDepth(); - } - else - { - // Map - // #if !context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - // while (!propertyIterator.CollectionEnds(memory, out requestHint)) - // { - // if (requestHint != 0) - // { - // memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - // continue; - // } - for (var i = 0; i < itemsCount; i++) - { - byte[] propertyKey = null!; - try - { - while (!TryGetRawString(context, ref memory, out propertyKey, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - // Use inlined trie, prefixed by the count as MP uint32. - // char is UTF-8, big endian. - // 1st node: [length(1-5)][chars(7-3)] - switch (DeserializationTrie.GetOrDefault(propertyKey)) - { - case 0: - { - while (!TryDecodeValueOfName(context, ref memory, out name, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - break; - } - case 1: - { - while (!TryDecodeValueOfAge(context, ref memory, out age, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - break; - } - case 2: - { - while (!TryDecodeValueOfIsActive(context, ref memory, out isActive, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - break; - } - case 3: - { - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - long arrayLength; - while (!TryDecodeArrayHeader(context, ref memory, out arrayLength, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - // If settable - //if (roles == null) - //{ - // roles = new List(arrayLength); // or 0 - //} - for (var j = 0; j < arrayLength; j++) - { - string item; - while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - roles.Add(item); - } - } - else - { - CollectionItemIterator iterator; - while (!TryDecodeArray(context, ref memory, out iterator, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - while (!iterator.CollectionEnds(memory, out requestHint)) - { - if (requestHint != 0) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - string item; - while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - roles.Add(item); - } - - while (!iterator.Drain(ref memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - context.DecrementDepth(); - break; - } - case 4: - { - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - long mapCount; - while (!TryDecodeMapHeader(context, ref memory, out mapCount, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - // If settable - //if (attributes == null) - //{ - // attributes = new Dictionary(mapCount); // or 0 - //} - for (var j = 0; j < mapCount; j++) - { - string key; - while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - string value; - while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - attributes.Add(key, value); - } - } - else - { - CollectionItemIterator iterator; - while (!TryDecodeMap(context, ref memory, out iterator, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - while (!iterator.CollectionEnds(memory, out requestHint)) - { - if (requestHint != 0) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - string key; - while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - string value; - while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - attributes.Add(key, value); - } - - while (!iterator.Drain(ref memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - context.DecrementDepth(); - break; - } - } - } - finally - { - if (propertyKey != null) - { - context.ArrayPool.Return(propertyKey); - } - } - } - } - - if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - { - itemsCount -= 5; - while (!TryDrain(context, ref memory, itemsCount, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - else - { - while(!propertyIterator.Drain(ref memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - } - - context.DecrementDepth(); - - obj.Name = name; - obj.Age = age; - obj.IsActive = isActive; - // If settable - // obj.Roles = roles; - // If settable - // obj.Attributes = attributes; - } - finally - { - context.ArrayPool.Return(buffer, clearArray: true); - } - } - } - - public sealed class SampleInt32ArraySerializer : IObjectSerializer - { - public void Serialize(in SerializationOperationContext context, int[] obj, IBufferWriter sink) - { - if (obj is null) - { - context.Encoder.EncodeNull(sink); - return; - } - - context.Encoder.EncodeArrayStart(obj.Length, sink, context.CollectionContext); - for(var i=0; i context, int[] obj, Stream streamSink) - { - throw new NotImplementedException(); - } - - public int[] Deserialize(in DeserializationOperationContext context, in SequenceReader source) - { - if (context.Decoder.FormatFeatures.CanCountCollectionItems) - { - var length = context.Decoder.DecodeArrayHeader(source); - var result = new int[length]; - for (var i = 0; i < result.Length; i++) - { - result[i] = context.Decoder.DecodeInt32(source); - } - context.Decoder.Drain(source, context.CollectionContext, 0); - return result; - } - else - { - var result = new List(); - var iterator = context.Decoder.DecodeArray(source); - while (!iterator.CollectionEnds(source)) - { - result.Add(context.Decoder.DecodeInt32(source)); - } - iterator.Drain(source); - return result.ToArray(); - } - } - - public ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource) - { - throw new NotImplementedException(); - } - - public void DeserializeTo(in DeserializationOperationContext context, in SequenceReader source, in int[] obj) - { - throw new NotImplementedException(); - } - - public ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, int[] obj) - { - throw new NotImplementedException(); - } - } - - public sealed class SampleInt32Serializer : IObjectSerializer - { - public void Serialize(in SerializationOperationContext context, int obj, IBufferWriter sink) - { - context.Encoder.EncodeInt32(obj, sink); - } - - public ValueTask SerializeAsync(SerializationOperationContext context, int obj, Stream streamSink) - { - throw new NotImplementedException(); - } - - public int Deserialize(in DeserializationOperationContext context, in SequenceReader source) - { - return context.Decoder.DecodeInt32(source); - } - - public ValueTask DeserializeAsync(DeserializationOperationContext context, Stream streamSource) - { - throw new NotImplementedException(); - } - - public void DeserializeTo(in DeserializationOperationContext context, in SequenceReader source, in int obj) - { - throw new NotImplementedException(); - } - - public ValueTask DeserializeToAsync(DeserializationOperationContext context, Stream streamSource, int obj) - { - throw new NotImplementedException(); - } - } -} diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs index 408bd801c..ec0a34f72 100644 --- a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs @@ -2,7 +2,12 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. extern alias newmpcli; + +using System; +using System.Buffers; +using System.Threading; using Benchmark.Serializers; +using MsgPack.Samples; #pragma warning disable SA1649 // File name should match first type name @@ -35,5 +40,133 @@ internal static class MsgPackCliSerializerRepository { public static readonly MsgPack.Serialization.MessagePackSerializer V1 = SampleSerializer.SerializationContext.GetSerializer(); + public static readonly newmpcli::MsgPack.Serialization.Internal.IObjectSerializer V2 = InitV2(); + + public static readonly newmpcli::MsgPack.Serialization.Internal.IObjectSerializer Json = InitJson(); + + private static newmpcli::MsgPack.Serialization.Internal.IObjectSerializer InitV2() + { + if (typeof(MsgPack.Samples.SampleObject).IsAssignableFrom(typeof(T))) + { + return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleSerializer(); + } + else if (typeof(T) == typeof(int[])) + { + return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleInt32ArraySerializer(); + } + else if (typeof(T) == typeof(int)) + { + return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleInt32Serializer(); + } + + throw new NotSupportedException($"No {typeof(T)} serializer."); + } + + private static newmpcli::MsgPack.Serialization.Internal.IObjectSerializer InitJson() + { + if (typeof(MsgPack.Samples.SampleObject).IsAssignableFrom(typeof(T))) + { + return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleSerializer(); + } + else if (typeof(T) == typeof(int[])) + { + return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleInt32ArraySerializer(); + } + else if (typeof(T) == typeof(int)) + { + return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleInt32Serializer(); + } + + throw new NotSupportedException($"No {typeof(T)} serializer."); + } + } + +public class MsgPackCli_v2 : SerializerBase +{ + private static readonly newmpcli::MsgPack.Internal.MessagePackEncoder Encoder = newmpcli::MsgPack.Internal.MessagePackEncoder.CreateCurrent(newmpcli::MsgPack.Internal.MessagePackEncoderOptions.Default); + private static readonly newmpcli::MsgPack.Internal.MessagePackDecoder Decoder = new newmpcli::MsgPack.Internal.MessagePackDecoder(newmpcli::MsgPack.Internal.MessagePackDecoderOptions.Default); + + public override T Deserialize(object input) + { + var reader = new SequenceReader(new ReadOnlySequence((byte[])input)); + var context = + new newmpcli::MsgPack.Serialization.Internal.DeserializationOperationContext( + Decoder, + null, + CancellationToken.None + ); + return MsgPackCliSerializerRepository.V2.Deserialize(ref context, ref reader); + } + + [ThreadStatic] + private static ArrayBufferWriter t_writer; + + public override object Serialize(T input) + { + if (t_writer == null) + { + t_writer = new ArrayBufferWriter(128); + } + else + { + t_writer.Clear(); + } + + var writer = t_writer; + var context = + new newmpcli::MsgPack.Serialization.Internal.SerializationOperationContext( + Encoder, + null, + CancellationToken.None + ); + MsgPackCliSerializerRepository.V2.Serialize(ref context, input, writer); + return writer.WrittenMemory.ToArray(); + } } + + + +public class MsgPackCliJson : SerializerBase +{ + private static readonly newmpcli::MsgPack.Json.JsonEncoder Encoder = new newmpcli::MsgPack.Json.JsonEncoder(newmpcli::MsgPack.Json.JsonEncoderOptions.Default); + private static readonly newmpcli::MsgPack.Json.JsonDecoder Decoder = newmpcli::MsgPack.Json.JsonDecoder.Create(newmpcli::MsgPack.Json.JsonDecoderOptions.Default); + + public override T Deserialize(object input) + { + var reader = new SequenceReader(new ReadOnlySequence((byte[])input)); + var context = + new newmpcli::MsgPack.Serialization.Internal.DeserializationOperationContext( + Decoder, + null, + CancellationToken.None + ); + return MsgPackCliSerializerRepository.Json.Deserialize(ref context, ref reader); + } + + [ThreadStatic] + private static ArrayBufferWriter t_writer; + + public override object Serialize(T input) + { + if (t_writer == null) + { + t_writer = new ArrayBufferWriter(128); + } + else + { + t_writer.Clear(); + } + + var writer = t_writer; + var context = + new newmpcli::MsgPack.Serialization.Internal.SerializationOperationContext( + Encoder, + null, + CancellationToken.None + ); + MsgPackCliSerializerRepository.Json.Serialize(ref context, input, writer); + return writer.WrittenMemory.ToArray(); + } +} + diff --git a/test/Benchmark/Program.cs b/test/Benchmark/Program.cs new file mode 100644 index 000000000..8f436dc9e --- /dev/null +++ b/test/Benchmark/Program.cs @@ -0,0 +1,172 @@ +// Copyright (c) All contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +//#define MSGPACK +#define JSON + +extern alias newmpcli; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Benchmark.Fixture; +using Benchmark.Models; +using Benchmark.Serializers; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Running; +using MessagePack; +using MsgPack.Samples; + +namespace Benchmark +{ + internal static class Program + { + static void Main(string[] args) + { + //JsonTest(); + BenchmarkRunner.Run(); + } + + static void Test(SerializerBase serializer, SampleObject obj) + { + Console.WriteLine($"---- {serializer.GetType().Name} ----"); + var array = serializer.Serialize(obj); + Console.WriteLine((array as byte[])?.Length); +#if MSGPACK + Console.WriteLine(BitConverter.ToString(array as byte[])); +#else + Console.WriteLine(Encoding.UTF8.GetString(array as byte[])); +#endif + var result = serializer.Deserialize(array); + //Console.WriteLine($"Name : {result?.Name}"); + //Console.WriteLine($"Age : {result?.Age}"); + //Console.WriteLine($"IsActive : {result?.IsActive}"); + //Console.WriteLine($"Roles : [{String.Join(", ", result?.Roles ?? Array.Empty())}]"); + //Console.WriteLine($"Attributes : {{{String.Join(", ", (result?.Attributes ?? Enumerable.Empty>()).Select(x => $"{x.Key}: {x.Value}"))}}}"); + } + + static void JsonTest() + { + var obj = SampleObject.TestValue; + //new SampleObject { Name = "A\tあいうえお\u007F \\u3042" }; + //Test(new JsonNet(), obj); + Test(new MsgPackCliJson(), obj); + //Test(new SpanJson_(), obj); + //Test(new SystemTextJson(), obj); + //Test(new Utf8Json_(), obj); + } + } + + [Config(typeof(BenchmarkConfig))] + public class SampleBenchmark + { + [ParamsSource(nameof(Serializers))] + public SerializerBase Serializer; + + private bool isContractless; + + // Currently BenchmarkdDotNet does not detect inherited ParamsSource so use copy and paste:) + public IEnumerable Serializers => new SerializerBase[] + { +#if MSGPACK + new MessagePack_v2(), +#endif + //new MsgPack_v2_opt(), + //new MessagePackLz4_v2(), + //new MsgPack_v2_string(), + //new MsgPack_v2_str_lz4(), + //new ProtobufNet(), +#if JSON + new JsonNet(), +#endif + //new BinaryFormatter_(), + //new DataContract_(), + //new Hyperion_(), + //new Jil_(), +#if JSON + new SpanJson_(), + new Utf8Json_(), + new SystemTextJson(), + new MsgPackCliJson(), +#endif +#if MSGPACK + + new MsgPackCli(), + new MsgPackCli_v2(), +#endif + //new FsPickler_(), + //new Ceras_(), + }; + + protected static readonly ExpressionTreeFixture ExpressionTreeFixture = new ExpressionTreeFixture(); + + // primitives + protected static readonly int IntInput = ExpressionTreeFixture.Create(); + + // models + protected static readonly SampleObject AnswerInput = SampleObject.TestValue;// ExpressionTreeFixture.Create(); + // not same data so does not gurantee correctly. + protected static readonly SampleObject Answer2Input = SampleObject.TestValue;//ExpressionTreeFixture.Create(); + + private object IntOutput; + private object AnswerOutput; + + [GlobalSetup] + public void Setup() + { + this.isContractless = (Serializer is MsgPack_v2_string) || (Serializer is MsgPack_v2_str_lz4); + + // primitives + this.IntOutput = this.Serializer.Serialize(IntInput); + + // models + if (isContractless) + { + this.AnswerOutput = this.Serializer.Serialize(Answer2Input); + } + else + { + this.AnswerOutput = this.Serializer.Serialize(AnswerInput); + } + + var o = this.AnswerOutput as byte[]; + Console.WriteLine("-------------------------------------------------------------------"); + Console.WriteLine($"{this.Serializer.GetType().Name} -> {o.Length} bytes"); + Console.WriteLine(BitConverter.ToString(o)); + Console.WriteLine("-------------------------------------------------------------------"); + } + + // Serialize + /* [Benchmark] public object _PrimitiveIntSerialize() => this.Serializer.Serialize(IntInput); */ + + [Benchmark] + public object AnswerSerialize() + { + if (isContractless) + { + return this.Serializer.Serialize(Answer2Input); + } + else + { + return this.Serializer.Serialize(AnswerInput); + } + } + + // Deserialize + /* [Benchmark] public Int32 _PrimitiveIntDeserialize() => this.Serializer.Deserialize(this.IntOutput); */ + + [Benchmark] + public object AnswerDeserialize() + { + if (isContractless) + { + return this.Serializer.Deserialize(this.AnswerOutput); + } + else + { + return this.Serializer.Deserialize(this.AnswerOutput); + } + } + } +} diff --git a/test/Benchmark/_SampleObject.cs b/test/Benchmark/_SampleObject.cs new file mode 100644 index 000000000..9451254c4 --- /dev/null +++ b/test/Benchmark/_SampleObject.cs @@ -0,0 +1,2003 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +//#define USE_ARRAY +#define INT32 +#define BOOL +#define STRING +#define COLLECTION +#define NO_OPT +//#define NULL +//#define INLINE_TRIE +#define JSON + +extern alias newmpcli; + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using newmpcli::MsgPack.Internal; +using newmpcli::MsgPack.Json; +using newmpcli::MsgPack.Serialization.Internal; + +namespace MsgPack.Samples +{ + public delegate ulong TrieKeyFactory(ref ReadOnlySpan rawPropertyKey); + + public sealed class SerializationFormat + { + public Type DecoderType { get; set; } // if null, no cast will be used + public Type EncoderType { get; set; } // if null, no cast will be used + public FormatFeatures Features { get; set; } // if null, no optimization will be applied + public Func PropertyKeyEncoder { get; set; } // if null, use EncodeString + public TrieKeyFactory TrieKeyHeadFactory { get; set; } // if null, use Dictionary + public TrieKeyFactory TrieKeyRestFactory { get; set; } // if null, use Dictionary + } + + public sealed class SerializationFormatFactory + { + public static SerializationFormatFactory Instance { get; } = new SerializationFormatFactory(); + private SerializationFormatFactory() { } + } + + public static class MessagePackSerializationFormatFactoryExtensions + { + private static readonly SerializationFormat CurrentMessagePackSerializationFormat = + new SerializationFormat + { + DecoderType = typeof(MessagePackDecoder), + EncoderType = typeof(MessagePackEncoder), // typeof(CurrentMessagePackEncoder) + Features = null, // TODO + PropertyKeyEncoder = null, // CurrentMessagePackEncoder.InternalEncodeString, + TrieKeyHeadFactory = MsgPackStringTrieKey.GetRaw64, + TrieKeyRestFactory = MsgPackStringTrieKey.GetRaw64 + }; + private static readonly SerializationFormat LegacyMessagePackSerializationFormat = + new SerializationFormat + { + DecoderType = typeof(MessagePackDecoder), + EncoderType = typeof(MessagePackEncoder), // typeof(LegacyMessagePackEncoder) + Features = null, // TODO + PropertyKeyEncoder = null, // LegacyMessagePackEncoder.InternalEncodeString, + TrieKeyHeadFactory = MsgPackStringTrieKey.GetRaw64, + TrieKeyRestFactory = MsgPackStringTrieKey.GetRaw64 + }; + + public static SerializationFormat GetMessagePack(this SerializationFormatFactory factory, bool isCurrent = true) + => isCurrent ? CurrentMessagePackSerializationFormat : LegacyMessagePackSerializationFormat; + } + + + public static class JsonSerializationFormatFactoryExtensions + { + private static readonly SerializationFormat SimpleJsonSerializationFormat = + new SerializationFormat + { + DecoderType = typeof(JsonDecoder), // typeof(SimpleJsonDecoder) + EncoderType = typeof(JsonEncoder), + Features = null, // TODO + PropertyKeyEncoder = null, // JsonEncoder.InternalEncodeString, + TrieKeyHeadFactory = MsgPackStringTrieKey.GetAsMsgPackString, + TrieKeyRestFactory = MsgPackStringTrieKey.GetRaw64 + }; + private static readonly SerializationFormat FlexibleJsonSerializationFormat = + new SerializationFormat + { + DecoderType = typeof(JsonDecoder), // typeof(FlexibleJsonDecoder) + EncoderType = typeof(JsonEncoder), + Features = null, // TODO + PropertyKeyEncoder = null, // JsonEncoder.InternalEncodeString, + TrieKeyHeadFactory = MsgPackStringTrieKey.GetAsMsgPackString, + TrieKeyRestFactory = MsgPackStringTrieKey.GetRaw64 + }; + + public static SerializationFormat GetJson(this SerializationFormatFactory factory, JsonParseOptions parserOptions) + => parserOptions == JsonParseOptions.None ? SimpleJsonSerializationFormat : FlexibleJsonSerializationFormat; + } + +#if USE_ARRAY + [MessagePack.MessagePackObject(keyAsPropertyName: false)] +#else + [MessagePack.MessagePackObject(keyAsPropertyName: true)] +#endif + public class SampleObject + { + public static readonly SampleObject TestValue = +#if NULL + null; +#else + new SampleObject() + { +#if INT32 + Age = 38, +#endif +#if STRING + Name = "yfakariya", +#endif +#if BOOL + IsActive = true, +#endif +#if COLLECTION + Attributes = { ["Busy"] = "Sometimes" }, + Roles = { "Developer" } +#endif + }; +#endif // NULL + +#if INT32 +#if USE_ARRAY + [MessagePack.Key(0)] +#endif + public int Age { get; set; } +#endif + +#if STRING +#if USE_ARRAY + [MessagePack.Key(1)] +#endif + public string Name { get; set; } = null!; +#endif + +#if BOOL +#if USE_ARRAY + [MessagePack.Key(2)] +#endif + public bool IsActive { get; set; } +#endif + +#if COLLECTION +#if USE_ARRAY + [MessagePack.Key(3)] +#endif + public IDictionary Attributes { get; } = new Dictionary(); + +#if USE_ARRAY + [MessagePack.Key(4)] +#endif + public IList Roles { get; } = new List(); +#endif + } + + // MP.Core.Abstraction + // MP.Core / MP.Json.Core / MP.Yaml.Core / MP.Coff.Core + // MP.Serialization.Core -> Interfaces, .... + // MP.Serialization -> Basic serializers, context, utilities, Stream adapter, byte[] adapter, etc. + // MP.Serialization.Serializers-> Extended serializers implementation. + // MP.Serialization.Reflection -> Reflection + // MP.Serialization.ILGeneration -> Reflection + // MP.Serialization.SourceGeneration -> Reflection + // MP.Extensions / MP.Json.Extensions / MP.Yaml.Extensions / MP.Coff.Extensions -> MPO, DOM, etc + // MP.Compatibility1 -> v1 compatibility shims + // MP.Cli -> v1 compatibility shims + + // Packer/Unpacker compatibility note: + // Packer -> Packer.Create(new MemoryStream()) + // Unpacker -> Unpacker.Create(new MemoryStream())??? + + internal static class SampleSerializer + { + public static readonly MsgPack.Serialization.SerializationContext SerializationContext = +#if USE_ARRAY + new Serialization.SerializationContext() { SerializationMethod = Serialization.SerializationMethod.Array }; +#else + new Serialization.SerializationContext() { SerializationMethod = Serialization.SerializationMethod.Map }; +#endif + } + + // For PoC of MVP and reference for emit. + /// + /// Sample hand made serializer. + /// + public sealed class SampleSerializer : IObjectSerializer + { + private bool UseArray { get; set; } +#if USE_ARRAY + = true; +#endif + private FormatFeatures FormatFeatures { get; set; } = + new FormatFeaturesBuilder + { +#if MSGPACK + CanCountCollectionItems = true, + CanSpecifyStringEncoding = true, + IsContextful = false, + SupportsExtensionTypes = true +#else + CanCountCollectionItems = false, + CanSpecifyStringEncoding = false, + IsContextful = false, + SupportsExtensionTypes = false +#endif + }.Build(); + + public void Serialize(ref SerializationOperationContext context, SampleObject obj, IBufferWriter writer) + { +#if JSON + var encoder = new JsonEncoder(context.Encoder.Options as JsonEncoderOptions); +#elif NO_OPT + var encoder = context.Encoder; +#else + var encoder = context.Encoder; +#endif + if (obj == null) + { + encoder.EncodeNull(writer); + return; + } + + const int propertyCount = + 0 +#if INT32 + + 1 +#endif +#if STRING + + 1 +#endif +#if BOOL + + 1 +#endif +#if COLLECTION + + 1 + + 1 +#endif + ; + //if (this.UseArray) +#if USE_ARRAY + { + encoder.EncodeArrayStart(propertyCount, writer, context.CollectionContext); // OMITTABLE +#if STRING +#if NO_OPT + encoder.EncodeArrayItemStart(0, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeString(obj.Name, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + +#if NO_OPT + encoder.EncodeArrayItemEnd(0, writer, context.CollectionContext); // OMITTABLE +#endif +#endif +#if INT32 +#if NO_OPT + encoder.EncodeArrayItemStart(1, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeInt32(obj.Age, writer); +#if NO_OPT + encoder.EncodeArrayItemEnd(1, writer, context.CollectionContext); // OMITTABLE +#endif +#endif +#if BOOL +#if NO_OPT + encoder.EncodeArrayItemStart(2, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeBoolean(obj.IsActive, writer); +#if NO_OPT + encoder.EncodeArrayItemEnd(2, writer, context.CollectionContext); // OMITTABLE +#endif +#endif // BOOL +#if COLLECTION +#if NO_OPT + encoder.EncodeArrayItemStart(3, writer, context.CollectionContext); // OMITTABLE +#endif + if (obj.Roles == null) + { + encoder.EncodeNull(writer); + } + else + { + encoder.EncodeArrayStart(obj.Roles.Count, writer, context.CollectionContext); // OMITTABLE + context.IncrementDepth(); + var roles = obj.Roles; + var count = roles.Count; + for (var i = 0; i < count; i++) + { +#if NO_OPT + encoder.EncodeArrayItemStart(i, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeString(roles[i], writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding +#if NO_OPT + encoder.EncodeArrayItemEnd(i, writer, context.CollectionContext); // OMITTABLE +#endif + } + context.DecrementDepth(); +#if NO_OPT + encoder.EncodeArrayEnd(obj.Roles.Count, writer, context.CollectionContext); +#endif + } +#if NO_OPT + encoder.EncodeArrayItemEnd(3, writer, context.CollectionContext); // OMITTABLE + + encoder.EncodeArrayItemStart(4, writer, context.CollectionContext); // OMITTABLE +#endif + if (obj.Attributes == null) + { + encoder.EncodeNull(writer); + } + else + { + encoder.EncodeMapStart(obj.Attributes.Count, writer, context.CollectionContext); // OMITTABLE + context.IncrementDepth(); +#if NO_OPT + var i = 0; // OMITTABLE +#endif + foreach (var entry in obj.Attributes) + { +#if NO_OPT + encoder.EncodeMapKeyStart(i, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeString(entry.Key, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding +#if NO_OPT + encoder.EncodeMapKeyEnd(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(i, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeString(entry.Value, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding +#if NO_OPT + encoder.EncodeMapValueEnd(i, writer, context.CollectionContext); // OMITTABLE +#endif + } + context.DecrementDepth(); +#if NO_OPT + encoder.EncodeMapEnd(obj.Attributes.Count, writer, context.CollectionContext); // OMITTABLE +#endif + } +#if NO_OPT + encoder.EncodeArrayItemEnd(4, writer, context.CollectionContext); // OMITTABLE +#endif +#endif // COLLECTION +#if NO_OPT + encoder.EncodeArrayEnd(propertyCount, writer, context.CollectionContext); +#endif + } +#else // USEARRAY + //else + { + encoder.EncodeMapStart(propertyCount, writer, context.CollectionContext); // OMITTABLE + +#if STRING +#if JSON + ReadOnlySpan key0 = new[] { (byte)'"', (byte)'N', (byte)'a', (byte)'m', (byte)'e', (byte)'"' }; // static readonly +#else + ReadOnlySpan key0 = new[] { 0xA4, (byte)'N', (byte)'a', (byte)'m', (byte)'e' }; // static readonly +#endif +#warning TODO: Use DirectWrite +#if NO_OPT + encoder.EncodeMapKeyStart(0, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeRawString(key0, 4, writer, context.CancellationToken); +#if NO_OPT + encoder.EncodeMapKeyEnd(0, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(0, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeString(obj.Name, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding +#if NO_OPT + encoder.EncodeMapValueEnd(0, writer, context.CollectionContext); // OMITTABLE +#endif +#endif // STRING +#if INT32 +#if JSON + ReadOnlySpan key1 = new[] { (byte)'"', (byte)'A', (byte)'g', (byte)'e', (byte)'"' }; // static readonly +#else + ReadOnlySpan key1 = new[] { 0xA3, (byte)'A', (byte)'g', (byte)'e' }; // static readonly +#endif +#if NO_OPT + encoder.EncodeMapKeyStart(1, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeRawString(key1, 3, writer, context.CancellationToken); +#if NO_OPT + encoder.EncodeMapKeyEnd(1, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(1, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeInt32(obj.Age, writer); +#if NO_OPT + encoder.EncodeMapValueEnd(1, writer, context.CollectionContext); // OMITTABLE +#endif +#endif // INT32 +#if BOOL +#if JSON + ReadOnlySpan key2 = new[] { (byte)'"', (byte)'I', (byte)'s', (byte)'A', (byte)'c', (byte)'t', (byte)'i', (byte)'v', (byte)'e', (byte)'"' }; // static readonly +#else + ReadOnlySpan key2 = new[] { 0xA8, (byte)'I', (byte)'s', (byte)'A', (byte)'c', (byte)'t', (byte)'i', (byte)'v', (byte)'e' }; // static readonly +#endif +#if NO_OPT + encoder.EncodeMapKeyStart(2, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeRawString(key2, 8, writer, context.CancellationToken); +#if NO_OPT + encoder.EncodeMapKeyEnd(2, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(2, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeBoolean(obj.IsActive, writer); +#if NO_OPT + encoder.EncodeMapValueEnd(2, writer, context.CollectionContext); // OMITTABLE +#endif +#endif // BOOL +#if COLLECTION +#if JSON + ReadOnlySpan key3 = new[] { (byte)'"', (byte)'R', (byte)'o', (byte)'l', (byte)'e', (byte)'s', (byte)'"' }; // static readonly +#else + ReadOnlySpan key3 = new[] { 0xA5, (byte)'R', (byte)'o', (byte)'l', (byte)'e', (byte)'s' }; // static readonly +#endif +#if NO_OPT + encoder.EncodeMapKeyStart(3, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeRawString(key3, 5, writer, context.CancellationToken); +#if NO_OPT + encoder.EncodeMapKeyEnd(3, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(3, writer, context.CollectionContext); // OMITTABLE +#endif + if (obj.Roles == null) + { + encoder.EncodeNull(writer); + } + else + { + encoder.EncodeArrayStart(obj.Roles.Count, writer, context.CollectionContext); // OMITTABLE + context.IncrementDepth(); +#if NO_OPT + var i = 0; // OMITTABLE +#endif + foreach (var item in obj.Roles) + { +#if NO_OPT + encoder.EncodeArrayItemStart(i, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeString(item, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding +#if NO_OPT + encoder.EncodeArrayItemEnd(i, writer, context.CollectionContext); // OMITTABLE +#endif + } + context.DecrementDepth(); +#if NO_OPT + encoder.EncodeArrayEnd(obj.Roles.Count, writer, context.CollectionContext); // OPTIMIZABLE +#endif + } +#if NO_OPT + encoder.EncodeMapValueEnd(3, writer, context.CollectionContext); // OMITTABLE +#endif + +#if JSON + ReadOnlySpan key4 = new[] { (byte)'"', (byte)'A', (byte)'t', (byte)'t', (byte)'r', (byte)'i', (byte)'b', (byte)'u', (byte)'t', (byte)'e', (byte)'s', (byte)'"' }; // static readonly +#else + ReadOnlySpan key4 = new[] { 0xAA, (byte)'A', (byte)'t', (byte)'t', (byte)'r', (byte)'i', (byte)'b', (byte)'u', (byte)'t', (byte)'e', (byte)'s' }; // static readonly +#endif +#if NO_OPT + encoder.EncodeMapKeyStart(4, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeRawString(key4, 10, writer, context.CancellationToken); +#if NO_OPT + encoder.EncodeMapKeyEnd(4, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(4, writer, context.CollectionContext); // OMITTABLE +#endif + if (obj.Attributes == null) + { + encoder.EncodeNull(writer); + } + else + { + encoder.EncodeMapStart(obj.Attributes.Count, writer, context.CollectionContext); // OMITTABLE + context.IncrementDepth(); + var i = 0; // OMITTABLE + foreach (var entry in obj.Attributes) + { +#if NO_OPT + encoder.EncodeMapKeyStart(i, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeString(entry.Key, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding +#if NO_OPT + encoder.EncodeMapKeyEnd(i, writer, context.CollectionContext); // OMITTABLE + encoder.EncodeMapValueStart(i, writer, context.CollectionContext); // OMITTABLE +#endif + encoder.EncodeString(entry.Value, writer, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding +#if NO_OPT + encoder.EncodeMapValueEnd(i, writer, context.CollectionContext); // OMITTABLE +#endif + } + context.DecrementDepth(); + encoder.EncodeMapEnd(obj.Attributes.Count, writer, context.CollectionContext); + } +#if NO_OPT + encoder.EncodeMapValueEnd(4, writer, context.CollectionContext); // OMITTABLE +#endif +#endif // COLLECTION +#if NO_OPT + encoder.EncodeMapEnd(propertyCount, writer, context.CollectionContext); +#endif + } +#endif // !USE_ARRAY + } + + public async ValueTask SerializeAsync(AsyncSerializationOperationContext context, SampleObject obj, Stream streamSink) + { + await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) + { + var serializationOperationContext = context.AsSerializationOperationContext(); + this.Serialize(ref serializationOperationContext, obj, writer); + } + } + + private static readonly MsgPackStringTrie DeserializationTrie = InitializeDeserializationTrie(); + private static MsgPackStringTrie InitializeDeserializationTrie() + { + ReadOnlySpan __name = new byte[] { (byte)'N', (byte)'a', (byte)'m', (byte)'e' }; + ReadOnlySpan __age = new byte[] { (byte)'A', (byte)'g', (byte)'e' }; + ReadOnlySpan __isActive = new byte[] { (byte)'I', (byte)'s', (byte)'A', (byte)'c', (byte)'t', (byte)'i', (byte)'v', (byte)'e' }; + ReadOnlySpan __roles = new byte[] { (byte)'R', (byte)'o', (byte)'l', (byte)'e', (byte)'s' }; + ReadOnlySpan __attributes = new byte[] { (byte)'A', (byte)'t', (byte)'t', (byte)'r', (byte)'i', (byte)'b', (byte)'u', (byte)'t', (byte)'e', (byte)'s' }; + var trie = new MsgPackStringTrie(5); +#if !JSON +#if STRING + trie.TryAdd(__name, 0); +#endif +#if INT32 + trie.TryAdd(__age, 1); +#endif +#if BOOL + trie.TryAdd(__isActive, 2); +#endif +#if COLLECTION + trie.TryAdd(__roles, 3); + trie.TryAdd(__attributes, 4); +#endif +#else +#if STRING + trie.TryAddRaw(__name, 0); +#endif +#if INT32 + trie.TryAddRaw(__age, 1); +#endif +#if BOOL + trie.TryAddRaw(__isActive, 2); +#endif +#if COLLECTION + trie.TryAddRaw(__roles, 3); + trie.TryAddRaw(__attributes, 4); +#endif +#endif + // var trie32 = new MsgPackStringTrie32(5); + //#if STRING + // trie32.TryAdd(__name, 0); + //#endif + //#if INT32 + // trie32.TryAdd(__age, 1); + //#endif + //#if BOOL + // trie32.TryAdd(__isActive, 2); + //#endif + //#if COLLECTION + // trie32.TryAdd(__roles, 3); + // trie32.TryAdd(__attributes, 4); + //#endif + Console.WriteLine(String.Join(Environment.NewLine, trie.GetDebugView().Select(x => $"{String.Join('-', x.Keys.Select(l => l.ToString("X8")))} = {x.Value}"))); + + return trie; + } + + public SampleObject Deserialize(ref DeserializationOperationContext context, ref SequenceReader reader) + { +#warning TODO: Inlining to avoid allocation when null. + // WHEN T is reference type and it has a default contractor -> DeserializeToAsync + var obj = new SampleObject(); + if (this.DeserializeTo(ref context, ref reader, obj)) + { + return obj; + } + else + { + // WHEN T is not nullable, then throw. + return null; + } + + // WHEN T does not have default constructor -> Inline implementation which is same as DeserializeToAsync + // .... + // return new SampleObject(name, age, isActive, roles, attributes); + + // WHEN T is mutable value type + // ... + // var obj = default; + // obj.Name = name; + // obj.Age = age; + // obj.IsActive = isActive; + // obj.Roles = roles; // Roles must be null + // obj.Attributes = attributes; // Attributes must be null + // return obj; + } + + public async ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource) + { + var obj = new SampleObject(); + // WHEN T is reference type and it has a default contractor -> DeserializeToAsync + if (await this.DeserializeToAsync(context, streamSource, obj).ConfigureAwait(false)) + { + return obj; + } + else + { + // WHEN T is not nullable, then throw. + return null; + } + + // WHEN T does not have default constructor -> Inline implementation which is same as DeserializeToAsync + // .... + // return new SampleObject(name, age, isActive, roles, attributes); + + // WHEN T is mutable value type + // ... + // var obj = default; + // obj.Name = name; + // obj.Age = age; + // obj.IsActive = isActive; + // obj.Roles = roles; // Roles must be null + // obj.Attributes = attributes; // Attributes must be null + // return obj; + } + + private static void ThrowNotEnoughItems(long actual, int expected) + => throw new MessageTypeException(); + + public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader reader, in SampleObject obj) + { + const int propertyCount = + 0 +#if INT32 + + 1 +#endif +#if STRING + + 1 +#endif +#if BOOL + + 1 +#endif +#if USE_ARRAY + + 1 + + 1 +#endif + ; +#if STRING + string name = default!; +#endif +#if INT32 + int age = default; +#endif +#if BOOL + bool isActive = default; +#endif +#if COLLECTION + var roles = obj.Roles; + var attributes = obj.Attributes; +#endif + +#if JSON + var decoder = (JsonDecoder)(object)context.Decoder; +#elif NO_OPT + var decoder = context.Decoder; +#else + var decoder = new MessagePackDecoder(context.Decoder.Options as MessagePackDecoderOptions); +#endif + CollectionType arrayOrMap; + long itemsCount; + CollectionItemIterator propertyIterator; + + if (this.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + arrayOrMap = decoder.DecodeArrayOrMapHeader(ref reader, out itemsCount); + if (arrayOrMap.IsNull) + { +#warning TODO: result = null; + return false; + } + + if (itemsCount < propertyCount) + { + ThrowNotEnoughItems(itemsCount, propertyCount); + } + + propertyIterator = default; + } + else + { + arrayOrMap = decoder.DecodeArrayOrMap(ref reader, out propertyIterator); + if (arrayOrMap.IsNull) + { +#warning TODO: result = null; + return false; + } + itemsCount = -1; + } + + // Instantiate object for Deserialize + + context.IncrementDepth(); + + // If !SerializerGenerationOptions.InfersObjectSerialization && SerializerGenerationOptions.UseArray || typeof(T).IsDefined([SerializeAs(Array)]) + if (arrayOrMap.IsArray) + { +#if STRING +#if NO_OPT + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(ref reader, ref propertyIterator); + } +#endif + + name = decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding +#endif + +#if INT32 +#if NO_OPT + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(ref reader, ref propertyIterator); + } +#endif + + age = decoder.DecodeInt32(ref reader); +#endif + +#if BOOL +#if NO_OPT + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(ref reader, ref propertyIterator); + } +#endif + + isActive = decoder.DecodeBoolean(ref reader); +#endif + +#if COLLECTION +#if NO_OPT + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(ref reader, ref propertyIterator); + } +#endif + + context.IncrementDepth(); +#if NO_OPT + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { +#endif + var arrayLength = decoder.DecodeArrayHeader(ref reader); + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + + for (var i = 0; i < arrayLength; i++) + { + roles.Add(decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } +#if NO_OPT + } + else + { + var iterator = decoder.DecodeArray(ref reader); + while (!iterator.CollectionEnds(ref reader)) + { + roles.Add(decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + iterator.Drain(ref reader); + } +#endif + context.DecrementDepth(); + +#if NO_OPT + if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + CheckNextItemExists(ref reader, ref propertyIterator); + } +#endif + context.IncrementDepth(); +#if NO_OPT + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { +#endif + var mapCount = decoder.DecodeMapHeader(ref reader); + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var i = 0; i < mapCount; i++) + { + attributes.Add( + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } +#if NO_OPT + } + else + { + var iterator = decoder.DecodeMap(ref reader); + while (!iterator.CollectionEnds(ref reader)) + { + attributes.Add( + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + iterator.Drain(ref reader); + } +#endif + context.DecrementDepth(); +#endif + } + else + { + // Map + + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + for (var i = 0; i < itemsCount; i++) + { +#warning For msgpack, it should contain header, For JSON, it should contain ...? -> For Collection + decoder.GetRawString(ref reader, out ReadOnlySpan key, context.CancellationToken); + + context.ValidatePropertyKeyLength(reader.Consumed, key.Length); + + // Use inlined trie, prefixed by the count as MP uint32. + // char is UTF-8, big endian. + // 1st node: [length(1-5)][chars(7-3)] + +#if INLINE_TRIE + // TODO: 64bit, binary search + //417349A8-76697463-00000065 = 2 + //656741A3 = 1 + //6C6F52A5-00007365 = 3 + //6D614EA4-00000065 = 0 + //747441AA-75626972-00736574 = 4 + var intKey = MsgPackStringTrieKey.GetRaw64(ref key); + switch (intKey) +#else + switch (DeserializationTrie.GetOrDefault(key)) +#endif // INLINE_TRIE + { +#if STRING +#if INLINE_TRIE + case 0x6D614EA4: + { + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x00000065) + { + goto default; + } +#else + case 0: + { +#endif + + name = decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + break; + } +#endif +#if INT32 +#if INLINE_TRIE + case 0x656741A3: +#else + case 1: +#endif + { + age = decoder.DecodeInt32(ref reader); + break; + } +#endif +#if BOOL +#if INLINE_TRIE + case 0x417349A8: + { + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x76697463) + { + goto default; + } + + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x00000065) + { + goto default; + } +#else + case 2: + { +#endif + isActive = decoder.DecodeBoolean(ref reader); + break; + } +#endif +#if COLLECTION +#if INLINE_TRIE + case 0x6C6F52A5: + { + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x00007365) + { + goto default; + } + +#else + case 3: + { +#endif +#warning TODO: No Collection items deserialized! + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + var arrayLength = decoder.DecodeArrayHeader(ref reader); + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + for (var j = 0; j < arrayLength; j++) + { + roles.Add(decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + } + else + { + var iterator = decoder.DecodeArray(ref reader); + while (!iterator.CollectionEnds(ref reader)) + { + roles.Add(decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + iterator.Drain(ref reader); + } + context.DecrementDepth(); + break; + } +#if INLINE_TRIE + case 0x747441AA: + { + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x75626972) + { + goto default; + } + + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x00736574) + { + goto default; + } +#else + case 4: + { +#endif + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + var mapCount = decoder.DecodeMapHeader(ref reader); + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var j = 0; j < mapCount; j++) + { + attributes.Add( + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + } + else + { + var iterator = decoder.DecodeMap(ref reader); + while (!iterator.CollectionEnds(ref reader)) + { + attributes.Add( + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + iterator.Drain(ref reader); + } + context.DecrementDepth(); + break; + } +#endif + default: + { + context.Decoder.Skip(ref reader, context.CollectionContext, context.CancellationToken); + break; + } + } + } + } + else + { + while (!propertyIterator.CollectionEnds(ref reader)) + { +#warning For msgpack, it should contain header, For JSON, it should contain ...? -> For Collection + decoder.GetRawString(ref reader, out ReadOnlySpan key, context.CancellationToken); + + context.ValidatePropertyKeyLength(reader.Consumed, key.Length); + + CheckNextItemExists(ref reader, ref propertyIterator); + + // Use inlined trie, prefixed by the count as MP uint32. + // char is UTF-8, big endian. + // 1st node: [length(1-5)][chars(7-3)] + +#if INLINE_TRIE + // TODO: 64bit, binary search + //417349A8-76697463-00000065 = 2 + //656741A3 = 1 + //6C6F52A5-00007365 = 3 + //6D614EA4-00000065 = 0 + //747441AA-75626972-00736574 = 4 + var intKey = MsgPackStringTrieKey.GetRaw64(ref key); + switch (intKey) +#else + switch (DeserializationTrie.GetOrDefault(key)) +#endif // INLINE_TRIE + { +#if STRING +#if INLINE_TRIE + case 0x6D614EA4: + { + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x00000065) + { + goto default; + } +#else + case 0: + { +#endif + + name = decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + break; + } +#endif +#if INT32 +#if INLINE_TRIE + case 0x656741A3: +#else + case 1: +#endif + { + age = decoder.DecodeInt32(ref reader); + break; + } +#endif +#if BOOL +#if INLINE_TRIE + case 0x417349A8: + { + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x76697463) + { + goto default; + } + + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x00000065) + { + goto default; + } +#else + case 2: + { +#endif + isActive = decoder.DecodeBoolean(ref reader); + break; + } +#endif +#if COLLECTION +#if INLINE_TRIE + case 0x6C6F52A5: + { + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x00007365) + { + goto default; + } + +#else + case 3: + { +#endif +#warning TODO: No Collection items deserialized! + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + var arrayLength = decoder.DecodeArrayHeader(ref reader); + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + for (var j = 0; j < arrayLength; j++) + { + roles.Add(decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + } + else + { + var iterator = decoder.DecodeArray(ref reader); + while (!iterator.CollectionEnds(ref reader)) + { + roles.Add(decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken)); // <>NameEncoding ?? context.StringEncoding + } + iterator.Drain(ref reader); + } + context.DecrementDepth(); + break; + } +#if INLINE_TRIE + case 0x747441AA: + { + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x75626972) + { + goto default; + } + + intKey = MsgPackStringTrieKey.GetRaw64(ref key); + if (intKey != 0x00736574) + { + goto default; + } +#else + case 4: + { +#endif + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + var mapCount = decoder.DecodeMapHeader(ref reader); + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var j = 0; j < mapCount; j++) + { + attributes.Add( + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken), // <>NameEncoding ?? context.StringEncoding + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + } + else + { + var iterator = decoder.DecodeMap(ref reader); + while (!iterator.CollectionEnds(ref reader)) + { + var attributesKey = decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken); // <>NameEncoding ?? context.StringEncoding + CheckNextItemExists(ref reader, ref iterator); + attributes.Add( + attributesKey, + decoder.DecodeString(ref reader, context.StringEncoding, context.CancellationToken) // <>NameEncoding ?? context.StringEncoding + ); + } + iterator.Drain(ref reader); + } + context.DecrementDepth(); + break; + } +#endif + default: + { + context.Decoder.Skip(ref reader, context.CollectionContext, context.CancellationToken); + break; + } + } + } + + } + } + +#if NO_OPT + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { +#endif + decoder.Drain(ref reader, context.CollectionContext, itemsCount - 5, context.CancellationToken); +#if NO_OPT + } + else + { + propertyIterator.Drain(ref reader); + } +#endif + + context.DecrementDepth(); + + // They are verbose for Deserialize +#if STRING + obj.Name = name; +#endif +#if INT32 + obj.Age = age; +#endif +#if BOOL + obj.IsActive = isActive; +#endif + // If settable + // obj.Roles = roles; + // If settable + // obj.Attributes = attributes; + return true; + } + + private static void CheckNextItemExists(ref SequenceReader reader, ref CollectionItemIterator propertyIterator) + { + if (propertyIterator.CollectionEnds(ref reader)) + { + throw new MessageTypeException(); // Use Throws + } + } + + private static bool TryDecodeArrayOrMapHeader(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out long itemsCount, out CollectionType arrayOrMap, out CollectionItemIterator propertyIterator, out int requestHint) + { + context.IncrementDepth(); + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(ref reader, out itemsCount, out requestHint); + if (arrayOrMap.IsNone) + { + propertyIterator = default; + return false; + } + + if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(ref reader, out itemsCount); + if (itemsCount < 5) + { + throw new MessageTypeException(); // Use Throws + } + + propertyIterator = default; + } + else + { + arrayOrMap = context.Decoder.DecodeArrayOrMap(ref reader, out propertyIterator); + itemsCount = -1; + } + + memory = memory.Slice(unchecked((int)reader.Consumed)); + + return true; + } + + private static bool TryDecodeArrayHeader(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out long arrayLength, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + arrayLength = context.Decoder.DecodeArrayHeader(ref reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeMapHeader(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out long mapCount, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + mapCount = context.Decoder.DecodeMapHeader(ref reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeValueOfName(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out string name, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + name = context.Decoder.DecodeString(ref reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeValueOfAge(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out int age, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + age = context.Decoder.DecodeInt32(ref reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeValueOfIsActive(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out bool isActive, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + isActive = context.Decoder.DecodeBoolean(ref reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeItemOfRoles(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out string item, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + item = context.Decoder.DecodeString(ref reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeKeyOfAttributes(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out string key, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + key = context.Decoder.DecodeString(ref reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeValueOfAttributes(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out string value, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + value = context.Decoder.DecodeString(ref reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeArray(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + iterator = context.Decoder.DecodeArray(ref reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryDecodeMap(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + iterator = context.Decoder.DecodeMap(ref reader, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private static bool TryGetRawString(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, [NotNullWhen(true)] out byte[] key, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + + ReadOnlySpan span; + if (!context.Decoder.GetRawString(ref reader, out span, out requestHint, context.CancellationToken)) + { + key = default!; + return false; + } + + key = context.ByteBufferPool.Rent(span.Length); + span.CopyTo(key); + return true; + } + + private static bool TryDrain(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, long remaining, out int requestHint) + { + var reader = new SequenceReader(new ReadOnlySequence(memory)); + context.Decoder.Drain(ref reader, context.CollectionContext, remaining, out requestHint); + if (requestHint == 0) + { + memory = memory.Slice(unchecked((int)reader.Consumed)); + } + return requestHint == 0; + } + + private bool TryCheckNextItemExists(ref CollectionItemIterator propertyIterator, ReadOnlyMemory memory, out int requestHint) + { + if (propertyIterator.CollectionEnds(memory, out requestHint)) + { + throw new MessageTypeException(); // use throws + } + + return requestHint == 0; + } + + public async ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, SampleObject obj) + { + // T が値型 + // throw new NotSupportedException(); + + var buffer = context.ByteBufferPool.Rent(2 * 1024 * 1024); + try + { + var provider = new StreamReadOnlyMemoryProvider(streamSource, buffer); + var memory = await provider.GetNextAsync(default, 0, context.CancellationToken).ConfigureAwait(false); + int requestHint; + +#if STRING + string name = default!; +#endif +#if INT32 + int age = default; +#endif +#if BOOL + bool isActive = default; +#endif +#if COLLECTION + var roles = obj.Roles; + var attributes = obj.Attributes; +#endif + + var decoder = context.Decoder; + context.IncrementDepth(); + + long itemsCount; + CollectionType arrayOrMap; + CollectionItemIterator propertyIterator; + while (!TryDecodeArrayOrMapHeader(context, ref memory, out itemsCount, out arrayOrMap, out propertyIterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + if (arrayOrMap.IsNull) + { + return false; + } + + // Instantiate when Deserialize + + // If !SerializerGenerationOptions.InfersObjectSerialization && SerializerGenerationOptions.UseArray || typeof(T).IsDefined([SerializeAs(Array)]) + if (arrayOrMap.IsArray) + { +#if STRING + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + while (!TryDecodeValueOfName(context, ref memory, out name, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } +#endif + +#if INT32 + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + while (!TryDecodeValueOfAge(context, ref memory, out age, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } +#endif + +#if BOOL + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + while (!TryDecodeValueOfIsActive(context, ref memory, out isActive, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } +#endif + +#if COLLECTION + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + context.IncrementDepth(); + + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long arrayLength; + while (!TryDecodeArrayHeader(context, ref memory, out arrayLength, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + + for (var i = 0; i < arrayLength; i++) + { + string item; + while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + roles.Add(item); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeArray(context, ref memory, out iterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(memory, out requestHint)) + { + if (requestHint != 0) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + string item; + while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + roles.Add(item); + } + + while (!iterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + context.DecrementDepth(); + + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long mapCount; + while (!TryDecodeMapHeader(context, ref memory, out mapCount, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var i = 0; i < mapCount; i++) + { + string key; + while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + string value; + while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + attributes.Add(key, value); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeMap(context, ref memory, out iterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(memory, out requestHint)) + { + if (requestHint != 0) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + string key; + while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + string value; + while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + attributes.Add(key, value); + } + + while (!iterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + context.DecrementDepth(); +#endif + } + else + { + // Map + // #if !context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + // while (!propertyIterator.CollectionEnds(memory, out requestHint)) + // { + // if (requestHint != 0) + // { + // memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + // continue; + // } + for (var i = 0; i < itemsCount; i++) + { + byte[] propertyKey = null!; + try + { + while (!TryGetRawString(context, ref memory, out propertyKey, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // Use inlined trie, prefixed by the count as MP uint32. + // char is UTF-8, big endian. + // 1st node: [length(1-5)][chars(7-3)] + switch (DeserializationTrie.GetOrDefault(propertyKey)) + { +#if STRING + case 0: + { + while (!TryDecodeValueOfName(context, ref memory, out name, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + break; + } +#endif +#if INT32 + case 1: + { + while (!TryDecodeValueOfAge(context, ref memory, out age, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + break; + } +#endif +#if BOOL + case 2: + { + while (!TryDecodeValueOfIsActive(context, ref memory, out isActive, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + break; + } +#endif +#if COLLECTION + case 3: + { + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long arrayLength; + while (!TryDecodeArrayHeader(context, ref memory, out arrayLength, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + for (var j = 0; j < arrayLength; j++) + { + string item; + while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + roles.Add(item); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeArray(context, ref memory, out iterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(memory, out requestHint)) + { + if (requestHint != 0) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + string item; + while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + roles.Add(item); + } + + while (!iterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + context.DecrementDepth(); + break; + } + case 4: + { + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long mapCount; + while (!TryDecodeMapHeader(context, ref memory, out mapCount, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var j = 0; j < mapCount; j++) + { + string key; + while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + string value; + while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + attributes.Add(key, value); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeMap(context, ref memory, out iterator, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(memory, out requestHint)) + { + if (requestHint != 0) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + string key; + while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + string value; + while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + + attributes.Add(key, value); + } + + while (!iterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + context.DecrementDepth(); + break; + } +#endif + default: + { +#warning TODO: TrySkip loop + break; + } + } + } + finally + { + if (propertyKey != null) + { + context.ByteBufferPool.Return(propertyKey); + } + } + } + } + + if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + itemsCount -= 5; + while (!TryDrain(context, ref memory, itemsCount, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + else + { + while (!propertyIterator.Drain(ref memory, out requestHint)) + { + memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + context.DecrementDepth(); + + // Verbose for Deserialize... +#if STRING + obj.Name = name; +#endif +#if INT32 + obj.Age = age; +#endif +#if BOOL + obj.IsActive = isActive; +#endif + // If settable + // obj.Roles = roles; + // If settable + // obj.Attributes = attributes; + return true; + } + finally + { + context.ByteBufferPool.Return(buffer, clearArray: true); + } + } + } + + public sealed class SampleInt32ArraySerializer : IObjectSerializer + { + public void Serialize(ref SerializationOperationContext context, int[] obj, IBufferWriter sink) + { + if (obj is null) + { + context.Encoder.EncodeNull(sink); + return; + } + + context.Encoder.EncodeArrayStart(obj.Length, sink, context.CollectionContext); + for (var i = 0; i < obj.Length; i++) + { + context.Encoder.EncodeArrayItemStart(i, sink, context.CollectionContext); + context.Encoder.EncodeInt32(obj[i], sink); + context.Encoder.EncodeArrayItemEnd(i, sink, context.CollectionContext); + } + context.Encoder.EncodeArrayEnd(obj.Length, sink, context.CollectionContext); + } + + public ValueTask SerializeAsync(AsyncSerializationOperationContext context, int[] obj, Stream streamSink) + { + throw new NotImplementedException(); + } + + public int[] Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + { + if (context.Decoder.FormatFeatures.CanCountCollectionItems) + { + var length = context.Decoder.DecodeArrayHeader(ref source); + var result = new int[length]; + for (var i = 0; i < result.Length; i++) + { + result[i] = context.Decoder.DecodeInt32(ref source); + } + context.Decoder.Drain(ref source, context.CollectionContext, 0); + return result; + } + else + { + var result = new List(); + var iterator = context.Decoder.DecodeArray(ref source); + while (!iterator.CollectionEnds(ref source)) + { + result.Add(context.Decoder.DecodeInt32(ref source)); + } + iterator.Drain(ref source); + return result.ToArray(); + } + } + + public ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource) + { + throw new NotImplementedException(); + } + + public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in int[] obj) + { + throw new NotImplementedException(); + } + + public ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, int[] obj) + { + throw new NotImplementedException(); + } + } + + public sealed class SampleInt32Serializer : IObjectSerializer + { + public void Serialize(ref SerializationOperationContext context, int obj, IBufferWriter sink) + { + context.Encoder.EncodeInt32(obj, sink); + } + + public ValueTask SerializeAsync(AsyncSerializationOperationContext context, int obj, Stream streamSink) + { + throw new NotImplementedException(); + } + + public int Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + { + return context.Decoder.DecodeInt32(ref source); + } + + public ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource) + { + throw new NotImplementedException(); + } + + public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in int obj) + { + throw new NotImplementedException(); + } + + public ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, int obj) + { + throw new NotImplementedException(); + } + } +} From 1bd8c5673a7f0d9994bf0bf4a3a0ce3ae1081c1d Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 22 Jun 2020 19:27:57 +0900 Subject: [PATCH 26/82] Fix max size of array, map, string, binary Limit collection size with CLR/Core CLR limit. It might be different in Mono, it is not so important because CLR limit is very large enough. --- .../{Internal => }/Ensure.cs | 18 ++++++++++++- .../Internal/DecoderOptionsBuilder.cs | 6 ++--- .../Internal/OptionsDefaults.cs | 3 +++ .../DeserializationOptionsBuilder.cs | 8 +++--- .../SerializationOptionsBuilder.cs | 2 +- .../{Internal => }/Throw.cs | 25 ++++++++++++------- 6 files changed, 44 insertions(+), 18 deletions(-) rename src/MsgPack.Abstraction/{Internal => }/Ensure.cs (74%) rename src/MsgPack.Abstraction/{Internal => }/Throw.cs (87%) diff --git a/src/MsgPack.Abstraction/Internal/Ensure.cs b/src/MsgPack.Abstraction/Ensure.cs similarity index 74% rename from src/MsgPack.Abstraction/Internal/Ensure.cs rename to src/MsgPack.Abstraction/Ensure.cs index 379f8e90f..0f2f62aae 100644 --- a/src/MsgPack.Abstraction/Internal/Ensure.cs +++ b/src/MsgPack.Abstraction/Ensure.cs @@ -7,7 +7,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; -namespace MsgPack.Internal +namespace MsgPack { internal static class Ensure { @@ -54,5 +54,21 @@ public static T IsNotGreaterThan(T value, T maxInclusive, [CallerArgumentExpr return value; } + + public static T IsBetween(T value, T minInclusive, T maxInclusive, [CallerArgumentExpression("value")] string paramName = null!) + where T : struct, IComparable + { + if (value.CompareTo(minInclusive) < 0) + { + Throw.ArgumentOutOfRange(paramName, $"Value cannot be less than {minInclusive}."); + } + + if (value.CompareTo(maxInclusive) > 0) + { + Throw.ArgumentOutOfRange(paramName, $"Value cannot be greater than {maxInclusive}."); + } + + return value; + } } } diff --git a/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs b/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs index 893cff502..c1a7e9ca0 100644 --- a/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs @@ -23,7 +23,7 @@ public int CancellationSupportThreshold public int MaxNumberLengthInBytes { get => this._maxNumberLengthInBytes; - set => this._maxNumberLengthInBytes = Ensure.IsNotLessThan(value, 1); + set => this._maxNumberLengthInBytes = Ensure.IsBetween(value, 1, OptionsDefaults.MaxSingleByteCollectionLength); } private int _maxStringLengthInBytes = OptionsDefaults.MaxStringLengthInBytes; @@ -31,7 +31,7 @@ public int MaxNumberLengthInBytes public int MaxStringLengthInBytes { get => this._maxStringLengthInBytes; - set => this._maxStringLengthInBytes = Ensure.IsNotLessThan(value, 1); + set => this._maxStringLengthInBytes = Ensure.IsBetween(value, 1, OptionsDefaults.MaxMultiByteCollectionLength); } private int _naxBinaryLengthInBytes = OptionsDefaults.MaxBinaryLengthInBytes; @@ -39,7 +39,7 @@ public int MaxStringLengthInBytes public int MaxBinaryLengthInBytes { get => this._naxBinaryLengthInBytes; - set => this._naxBinaryLengthInBytes = Ensure.IsNotLessThan(value, 1); + set => this._naxBinaryLengthInBytes = Ensure.IsBetween(value, 1, OptionsDefaults.MaxSingleByteCollectionLength); } private int _maxByteBufferLength = OptionsDefaults.MaxByteBufferLength; diff --git a/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs b/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs index 7f470ff1f..a8ecbb089 100644 --- a/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs +++ b/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs @@ -9,6 +9,9 @@ namespace MsgPack.Internal #warning TODO: tuning default as backward compatible internal static class OptionsDefaults { + public const int MaxSingleByteCollectionLength = 0x7FFFFFC7; + public const int MaxMultiByteCollectionLength = 0X7FEFFFFF; + public static readonly int CancellationSupportThreshold = 128 * 1024 * 1024; // About 0.1 sec in desktop, more for IoT public static readonly int MaxNumberLengthInBytes = 32; public static readonly int MaxStringLengthInBytes = 256 * 1024 * 1024; diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs index 2bba8d676..3b965806f 100644 --- a/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs @@ -15,7 +15,7 @@ public sealed class DeserializationOptionsBuilder public int MaxArrayLength { get => this._maxArrayLength; - set => this._maxArrayLength = Ensure.IsNotLessThan(value, 0); + set => this._maxArrayLength = Ensure.IsBetween(value, 0, OptionsDefaults.MaxMultiByteCollectionLength); } private int _maxMapCount = OptionsDefaults.MaxMapCount; @@ -23,7 +23,7 @@ public int MaxArrayLength public int MaxMapCount { get => this._maxMapCount; - set => this._maxMapCount = Ensure.IsNotLessThan(value, 0); + set => this._maxMapCount = Ensure.IsBetween(value, 0, OptionsDefaults.MaxMultiByteCollectionLength); } private int _maxPropertyKeyLength = OptionsDefaults.MaxPropertyKeyLength; @@ -31,7 +31,7 @@ public int MaxMapCount public int MaxPropertyKeyLength { get => this._maxPropertyKeyLength; - set => this._maxPropertyKeyLength = Ensure.IsNotLessThan(value, 1); + set => this._maxPropertyKeyLength = Ensure.IsBetween(value, 1, OptionsDefaults.MaxMultiByteCollectionLength); } private int _maxDepth = OptionsDefaults.MaxDepth; @@ -39,7 +39,7 @@ public int MaxPropertyKeyLength public int MaxDepth { get => this._maxDepth; - set => this._maxDepth = Ensure.IsNotLessThan(value, 1); + set => this._maxDepth = Ensure.IsBetween(value, 1, OptionsDefaults.MaxMultiByteCollectionLength); } public Encoding? StringEncoding { get; set; } diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs b/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs index 90ef1bd8a..0f6475495 100644 --- a/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs @@ -13,7 +13,7 @@ public sealed class SerializationOptionsBuilder public int MaxDepth { get => this._maxDepth; - set => this._maxDepth = Ensure.IsNotLessThan(value, 1); + set => this._maxDepth = Ensure.IsBetween(value, 1, OptionsDefaults.MaxMultiByteCollectionLength); } public System.Text.Encoding? StringEncoding { get; set; } diff --git a/src/MsgPack.Abstraction/Internal/Throw.cs b/src/MsgPack.Abstraction/Throw.cs similarity index 87% rename from src/MsgPack.Abstraction/Internal/Throw.cs rename to src/MsgPack.Abstraction/Throw.cs index 929b3670d..4d67dc451 100644 --- a/src/MsgPack.Abstraction/Internal/Throw.cs +++ b/src/MsgPack.Abstraction/Throw.cs @@ -4,8 +4,9 @@ using System; using System.Text; +using MsgPack.Internal; -namespace MsgPack.Internal +namespace MsgPack { internal static class Throw { @@ -18,8 +19,17 @@ public static void ArgumentOutOfRange(string paramName, string message) public static void ExtensionsIsNotSupported() => throw new NotSupportedException($"Extension type is not supported in this encoder."); - public static void TooLargeCharLength(long size) - => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It is {size:#,0} chars, but it must be lessor than {Int32.MaxValue:#,0} chars. Use EncodeLargeString instead."); + public static void StreamMustBeAbleToRead(string paramName) + => throw new ArgumentException($"The stream must be able to read.", paramName); + + public static void TooSmallBuffer(string paramName, int minimumInclusive) + => throw new ArgumentException($"The size of buffer must be greater than or equal to {minimumInclusive:#,0}.", paramName); + + public static void TooLargeLength(int length, int requestHint) + => throw new ArgumentException($"Requested buffer size {((long)length + requestHint):#,0} is too large. It must be less than or equal to {OptionsDefaults.MaxSingleByteCollectionLength:#,0} bytes."); + + public static void EmptyObject(Type type) + => throw new InvalidOperationException($"Cannot use empty '{type}' object."); public static void DepthExeeded(long position, int maxDepth) => throw new LimitExceededException($"The depth of collection exceeds max depth {maxDepth:#,0} at {position:#,0}."); @@ -34,15 +44,12 @@ public static void DepthUnderflow() => throw new InvalidOperationException("CurrentDepth is 0."); public static void TooLargeByteLength(long size, string encodingName) - => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It will be encoded to {size:#,0} bytes with '{encodingName}' encoding, but it must be less than or equal to {UInt32.MaxValue:#,0} bytes. Use EncodeLargeString instead."); + => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It will be encoded to {size:#,0} bytes with '{encodingName}' encoding, but it must be less than or equal to {UInt32.MaxValue:#,0} bytes."); public static void TooLargeByteLength(Exception innerException, string encodingName) - => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It will be encoded to larger than {Int32.MaxValue:#,0} bytes with '{encodingName}' encoding, but it must be less than or equal to {UInt32.MaxValue:#,0} bytes. Use EncodeLargeString instead.", innerException); - - public static void TooLargeByteLengthForString(string encodingName) - => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It will be decoded to larger than maximum System.String length with '{encodingName}' encoding."); + => throw new InvalidOperationException($"Input ReadOnlySequence is too large. It will be encoded to larger than {Int32.MaxValue:#,0} bytes with '{encodingName}' encoding, but it must be less than or equal to {UInt32.MaxValue:#,0} bytes.", innerException); - internal static void TooLargePropertyKey(long position, int length, int maxPropertyKeyLength) + public static void TooLargePropertyKey(long position, int length, int maxPropertyKeyLength) => throw new InvalidOperationException($"Property key is too large. The size {length:#,0} is larger than configured limit {maxPropertyKeyLength:#,0} at {position:#,0}."); public static void InsufficientInput(long position, Type targetType, int requestHint) From 204dded3d0ccb67585980c836a52e3f474c3a7f9 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Mon, 22 Jun 2020 19:29:40 +0900 Subject: [PATCH 27/82] Code claunup --- .../Internal/DecoderOptionsBuilder.cs | 1 + .../Internal/StreamBufferWriter.cs | 2 +- .../Internal/StreamReadOnlyMemoryProvider.cs | 29 +++++++++++++++---- .../LimitExceededException.cs | 3 +- .../MsgPack.Abstraction.csproj | 23 ++++++++------- .../Properties/AssemblyInfo.cs | 4 ++- .../AsyncDeserializationOperationContext`1.cs | 2 +- .../AsyncDeserializationOperationContext`1.tt | 0 .../AsyncSerializationOperationContext`1.cs | 2 +- .../AsyncSerializationOperationContext`1.tt | 0 .../DeserializationOperationContext`1.cs | 2 +- .../DeserializationOperationContext`1.tt | 0 .../{Internal => }/OperationContext.ttinclude | 2 +- .../SerializationOperationContext`1.cs | 2 +- .../SerializationOperationContext`1.tt | 0 .../Internal/MessagePackEncoder.Strings.cs | 4 +-- 16 files changed, 49 insertions(+), 27 deletions(-) rename src/MsgPack.Abstraction/Serialization/{Internal => }/AsyncDeserializationOperationContext`1.cs (98%) rename src/MsgPack.Abstraction/Serialization/{Internal => }/AsyncDeserializationOperationContext`1.tt (100%) rename src/MsgPack.Abstraction/Serialization/{Internal => }/AsyncSerializationOperationContext`1.cs (97%) rename src/MsgPack.Abstraction/Serialization/{Internal => }/AsyncSerializationOperationContext`1.tt (100%) rename src/MsgPack.Abstraction/Serialization/{Internal => }/DeserializationOperationContext`1.cs (97%) rename src/MsgPack.Abstraction/Serialization/{Internal => }/DeserializationOperationContext`1.tt (100%) rename src/MsgPack.Abstraction/Serialization/{Internal => }/OperationContext.ttinclude (98%) rename src/MsgPack.Abstraction/Serialization/{Internal => }/SerializationOperationContext`1.cs (97%) rename src/MsgPack.Abstraction/Serialization/{Internal => }/SerializationOperationContext`1.tt (100%) diff --git a/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs b/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs index c1a7e9ca0..be520fadb 100644 --- a/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs @@ -6,6 +6,7 @@ namespace MsgPack.Internal { +#warning Remove unused buffer max pr rename to initial buffer length if appropriate. public abstract class DecoderOptionsBuilder { public bool CanTreatRealAsInteger { get; set; } = OptionsDefaults.CanTreatRealAsInteger; diff --git a/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs b/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs index ab347a216..5b94e0035 100644 --- a/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs +++ b/src/MsgPack.Abstraction/Internal/StreamBufferWriter.cs @@ -9,7 +9,7 @@ namespace MsgPack.Internal { -#warning TODO: Pubternal or CodeGen +#warning TODO: CodeGen public sealed class StreamBufferWriter : IBufferWriter, IAsyncDisposable { private readonly Stream _underlying; diff --git a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs index adfd64d77..0934bbbdf 100644 --- a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs +++ b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs @@ -9,25 +9,42 @@ namespace MsgPack.Internal { -#warning TODO: PubTernal or CodeGen - public sealed class StreamReadOnlyMemoryProvider + public struct StreamReadOnlyMemoryProvider { private readonly Stream _stream; private readonly byte[] _buffer; public StreamReadOnlyMemoryProvider(Stream stream, byte[] buffer) { + Ensure.NotNull(stream); + if (!stream.CanRead) + { + Throw.StreamMustBeAbleToRead(nameof(stream)); + } + + Ensure.NotNull(buffer); + if (buffer.Length == 0) + { + Throw.TooSmallBuffer(nameof(buffer), 1); + } + this._stream = stream; this._buffer = buffer; } public async ValueTask> GetNextAsync(ReadOnlyMemory previous, int requestHint, CancellationToken cancellationToken) { + if (this._stream is null) + { + Throw.EmptyObject(typeof(StreamReadOnlyMemoryProvider)); + // never + return default; + } + var required = (long)previous.Length + requestHint; - if (required > UInt32.MaxValue) + if (required > OptionsDefaults.MaxSingleByteCollectionLength) { -#warning TODO: - throw new Exception("Too large"); + Throw.TooLargeLength(previous.Length, requestHint); } Memory buffer; @@ -37,7 +54,7 @@ public async ValueTask> GetNextAsync(ReadOnlyMemory p { this._buffer.AsSpan().CopyTo(this._buffer.AsSpan(previous.Length)); } - + buffer = this._buffer.AsMemory(previous.Length); if (requestHint > 0) { diff --git a/src/MsgPack.Abstraction/LimitExceededException.cs b/src/MsgPack.Abstraction/LimitExceededException.cs index a49484da8..19ef6a106 100644 --- a/src/MsgPack.Abstraction/LimitExceededException.cs +++ b/src/MsgPack.Abstraction/LimitExceededException.cs @@ -6,11 +6,10 @@ namespace MsgPack { -#warning TODO: Quota? Limit? public sealed class LimitExceededException : Exception { public LimitExceededException() - : this("Sime quota is exeeded.") { } + : this("Some limit is exeeded.") { } public LimitExceededException(string? message) : base(message) { } diff --git a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj index 19123fb42..08f97e67a 100644 --- a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj +++ b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj @@ -7,6 +7,9 @@ + + + @@ -22,19 +25,19 @@ TextTemplatingFileGenerator Decoder`1.Primitives.cs - + AsyncSerializationOperationContext`1.cs TextTemplatingFileGenerator - + AsyncDeserializationOperationContext`1.cs TextTemplatingFileGenerator - + DeserializationOperationContext`1.cs TextTemplatingFileGenerator - + SerializationOperationContext`1.cs TextTemplatingFileGenerator @@ -60,25 +63,25 @@ True Encoder`1.Primitives.tt - + True True AsyncDeserializationOperationContext`1.tt - + True True AsyncSerializationOperationContext`1.tt - - DeserializationOperationContext`1.tt + True True + DeserializationOperationContext`1.tt - - SerializationOperationContext`1.tt + True True + SerializationOperationContext`1.tt diff --git a/src/MsgPack.Abstraction/Properties/AssemblyInfo.cs b/src/MsgPack.Abstraction/Properties/AssemblyInfo.cs index 4c671542f..430590546 100644 --- a/src/MsgPack.Abstraction/Properties/AssemblyInfo.cs +++ b/src/MsgPack.Abstraction/Properties/AssemblyInfo.cs @@ -1,4 +1,6 @@ -#warning TODO:HEADER +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System.Runtime.CompilerServices; diff --git a/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.cs similarity index 98% rename from src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.cs rename to src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.cs index 58b091d48..53025b8c9 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.cs @@ -14,7 +14,7 @@ using System.Threading; using MsgPack.Internal; -namespace MsgPack.Serialization.Internal +namespace MsgPack.Serialization { public sealed class AsyncDeserializationOperationContext { diff --git a/src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.tt similarity index 100% rename from src/MsgPack.Abstraction/Serialization/Internal/AsyncDeserializationOperationContext`1.tt rename to src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.tt diff --git a/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.cs similarity index 97% rename from src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.cs rename to src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.cs index 2c358a124..6fb561b90 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.cs @@ -13,7 +13,7 @@ using System.Threading; using MsgPack.Internal; -namespace MsgPack.Serialization.Internal +namespace MsgPack.Serialization { public sealed class AsyncSerializationOperationContext { diff --git a/src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.tt similarity index 100% rename from src/MsgPack.Abstraction/Serialization/Internal/AsyncSerializationOperationContext`1.tt rename to src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.tt diff --git a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.cs similarity index 97% rename from src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs rename to src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.cs index fe2014917..b0b762a74 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.cs @@ -14,7 +14,7 @@ using System.Threading; using MsgPack.Internal; -namespace MsgPack.Serialization.Internal +namespace MsgPack.Serialization { public struct DeserializationOperationContext { diff --git a/src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.tt similarity index 100% rename from src/MsgPack.Abstraction/Serialization/Internal/DeserializationOperationContext`1.tt rename to src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.tt diff --git a/src/MsgPack.Abstraction/Serialization/Internal/OperationContext.ttinclude b/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude similarity index 98% rename from src/MsgPack.Abstraction/Serialization/Internal/OperationContext.ttinclude rename to src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude index 5cdba01ee..8d886708e 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/OperationContext.ttinclude +++ b/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude @@ -30,7 +30,7 @@ using System.Text; using System.Threading; using MsgPack.Internal; -namespace MsgPack.Serialization.Internal +namespace MsgPack.Serialization { public <#= typeClass #> <#= typeName #> { diff --git a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.cs similarity index 97% rename from src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs rename to src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.cs index b1f829e15..36f7ca1fc 100644 --- a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.cs @@ -13,7 +13,7 @@ using System.Threading; using MsgPack.Internal; -namespace MsgPack.Serialization.Internal +namespace MsgPack.Serialization { public struct SerializationOperationContext { diff --git a/src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.tt similarity index 100% rename from src/MsgPack.Abstraction/Serialization/Internal/SerializationOperationContext`1.tt rename to src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.tt diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs index c5b737a4c..5af1509be 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.Strings.cs @@ -95,7 +95,7 @@ private void EncodeStringSlow(ReadOnlySpan value, IBufferWriter buff try { var totalLength = this.EncodeLargeString1Path(value, buffer, encoding, estimatedLength, encodingBuffer); - buffer.Write(encodingBuffer.AsSpan(0, totalLength)); + buffer.Write(encodingBuffer.AsMemory(0, totalLength).Span); } finally { @@ -221,7 +221,7 @@ private void EncodeStringSlow(in ReadOnlySequence value, IBufferWriter Date: Mon, 22 Jun 2020 19:30:47 +0900 Subject: [PATCH 28/82] Add initial serializer generator layer * This commit also remove TExtensions generic parameter for better polymorphism. --- Directory.Build.props | 15 +- doc/v2.md | 13 +- src/MsgPack.Abstraction/ExtensionType.cs | 117 +++ .../ExtensionTypeObject.cs | 41 + ...odeItemResult`1.cs => DecodeItemResult.cs} | 40 +- ...mitives.cs => FormatDecoder.Primitives.cs} | 2 +- ...mitives.tt => FormatDecoder.Primitives.tt} | 2 +- ...`1.Strings.cs => FormatDecoder.Strings.cs} | 2 +- ...`1.Strings.tt => FormatDecoder.Strings.tt} | 2 +- .../{Decoder`1.cs => FormatDecoder.cs} | 18 +- ...oderOptions.cs => FormatDecoderOptions.cs} | 4 +- ...lder.cs => FormatDecoderOptionsBuilder.cs} | 8 +- ...mitives.cs => FormatEncoder.Primitives.cs} | 2 +- ...mitives.tt => FormatEncoder.Primitives.tt} | 2 +- .../{Encoder`1.cs => FormatEncoder.cs} | 15 +- ...oderOptions.cs => FormatEncoderOptions.cs} | 4 +- ...lder.cs => FormatEncoderOptionsBuilder.cs} | 6 +- .../Internal/FormatFeatures.cs | 22 +- .../Internal/NullExtensionType.cs | 11 - .../MsgPack.Abstraction.csproj | 58 +- ...> AsyncDeserializationOperationContext.cs} | 10 +- ...> AsyncDeserializationOperationContext.tt} | 0 ... => AsyncSerializationOperationContext.cs} | 10 +- ... => AsyncSerializationOperationContext.tt} | 0 ....cs => DeserializationOperationContext.cs} | 6 +- ....tt => DeserializationOperationContext.tt} | 0 .../Internal/IObjectSerializer`2.cs | 20 - .../ObjectSerializationContext.cs | 17 + .../Serialization/ObjectSerializer`1.cs | 43 + .../Serialization/OperationContext.ttinclude | 15 +- ...`1.cs => SerializationOperationContext.cs} | 6 +- ...`1.tt => SerializationOperationContext.tt} | 0 .../Serialization/Serializer.cs | 29 + .../Serialization/Serializer`2.cs | 66 +- .../Internal/CurrentMessagePackEncoder.cs | 13 +- .../Internal/MessagePackDecoder.DecodeItem.cs | 26 +- .../Internal/MessagePackDecoder.Extension.cs | 15 +- .../Internal/MessagePackDecoder.cs | 2 +- .../Internal/MessagePackDecoderOptions.cs | 2 +- .../MessagePackDecoderOptionsBuilder.cs | 2 +- .../Internal/MessagePackEncoder.cs | 4 +- .../Internal/MessagePackEncoderOptions.cs | 2 +- .../MessagePackEncoderOptionsBuilder.cs | 2 +- .../Internal/MessagePackExtensionType.cs | 43 - src/MsgPack.Core/Internal/MessagePackThrow.cs | 3 + .../Json/JsonDecoder.DecodeItem.cs | 2 - src/MsgPack.Json/Json/JsonDecoder.cs | 2 +- src/MsgPack.Json/Json/JsonDecoderOptions.cs | 2 +- .../Json/JsonDecoderOptionsBuilder.cs | 2 +- src/MsgPack.Json/Json/JsonEncoder.cs | 2 +- src/MsgPack.Json/Json/JsonEncoderOptions.cs | 2 +- .../Json/JsonEncoderOptionsBuilder.cs | 4 +- .../MsgPack.Serialization.ILGeneration.csproj | 18 + .../MsgPack.Serialization.Reflection.csproj | 5 + .../ReflectionSerializer`2.cs | 126 +++ .../MsgPack.Serialization.csproj | 63 ++ .../Properties/AssemblyInfo.cs | 13 + .../MessagePackNullableAttribute.cs | 19 + .../Reflection/EncoderMethods.cs | 76 ++ .../Serialization/Reflection/SpanMethods.cs | 37 + .../Serialization/SerializerBuilder.cs | 11 + .../SerializerGenerationOptions.cs | 60 ++ .../Serialization/Throw.Internal.cs | 125 +++ .../Serialization/Throw.cs | 34 + .../Serialization/Throw.tt | 10 + .../Serialization/Throw.ttinclude | 41 + src/MsgPack.Serialization/Utf8String.cs | 40 + src/MsgPack/Serialization/BindingOptions.cs | 47 +- .../Serialization/CollectionDetailedKind.cs | 43 +- src/MsgPack/Serialization/CollectionKind.cs | 35 +- .../Serialization/CollectionTraitOptions.cs | 31 +- src/MsgPack/Serialization/CollectionTraits.cs | 86 +- .../Serialization/DataMemberContract.cs | 131 +-- .../Serialization/DateTimeConversionMethod.cs | 22 +- .../DateTimeMemberConversionMethod.cs | 22 +- .../EnumMemberSerializationMethod.cs | 26 +- .../Serialization/EnumSerializationMethod.cs | 26 +- .../Serialization/EnumSerializationOptions.cs | 14 +- .../MessagePackDateTimeMemberAttribute.cs | 26 +- ...PackDeserializationConstructorAttribute.cs | 24 +- .../Serialization/MessagePackEnumAttribute.cs | 26 +- .../MessagePackEnumMemberAttribute.cs | 26 +- .../MessagePackIgnoreAttribute.cs | 24 +- .../MessagePackMemberAttribute.cs | 52 +- src/MsgPack/Serialization/NilImplication.cs | 22 +- .../Polymorphic/IPolymorphicDeserializer.cs | 19 +- .../PolymorphismSchema.Internals.cs | 425 +++++---- .../ReflectionExtensions.CollectionTraits.cs | 661 ++++++------- ...eflectionExtensions.ConstructorDelegate.cs | 54 +- .../Serialization/ReflectionExtensions.cs | 105 ++- .../Serialization/SerializationExceptions.cs | 367 ++++---- .../SerializationMethodGeneratorOption.cs | 35 +- .../Serialization/SerializationTarget.cs | 867 +++++++----------- .../Serialization/SerializerCapabilities.cs | 47 +- .../Serialization/SerializerOptions.cs | 71 +- .../Serialization/SerializingMember.cs | 129 +-- src/MsgPack/TupleItems.cs | 111 ++- .../Serializers/MsgPackCliSerializer.cs | 28 +- test/Benchmark/_SampleObject.cs | 816 ++++++++--------- 99 files changed, 3068 insertions(+), 2764 deletions(-) create mode 100644 src/MsgPack.Abstraction/ExtensionType.cs create mode 100644 src/MsgPack.Abstraction/ExtensionTypeObject.cs rename src/MsgPack.Abstraction/Internal/{DecodeItemResult`1.cs => DecodeItemResult.cs} (50%) rename src/MsgPack.Abstraction/Internal/{Decoder`1.Primitives.cs => FormatDecoder.Primitives.cs} (99%) rename src/MsgPack.Abstraction/Internal/{Decoder`1.Primitives.tt => FormatDecoder.Primitives.tt} (99%) rename src/MsgPack.Abstraction/Internal/{Decoder`1.Strings.cs => FormatDecoder.Strings.cs} (99%) rename src/MsgPack.Abstraction/Internal/{Decoder`1.Strings.tt => FormatDecoder.Strings.tt} (99%) rename src/MsgPack.Abstraction/Internal/{Decoder`1.cs => FormatDecoder.cs} (94%) rename src/MsgPack.Abstraction/Internal/{DecoderOptions.cs => FormatDecoderOptions.cs} (92%) rename src/MsgPack.Abstraction/Internal/{DecoderOptionsBuilder.cs => FormatDecoderOptionsBuilder.cs} (92%) rename src/MsgPack.Abstraction/Internal/{Encoder`1.Primitives.cs => FormatEncoder.Primitives.cs} (99%) rename src/MsgPack.Abstraction/Internal/{Encoder`1.Primitives.tt => FormatEncoder.Primitives.tt} (98%) rename src/MsgPack.Abstraction/Internal/{Encoder`1.cs => FormatEncoder.cs} (90%) rename src/MsgPack.Abstraction/Internal/{EncoderOptions.cs => FormatEncoderOptions.cs} (88%) rename src/MsgPack.Abstraction/Internal/{EncoderOptionsBuilder.cs => FormatEncoderOptionsBuilder.cs} (90%) delete mode 100644 src/MsgPack.Abstraction/Internal/NullExtensionType.cs rename src/MsgPack.Abstraction/Serialization/{AsyncDeserializationOperationContext`1.cs => AsyncDeserializationOperationContext.cs} (77%) rename src/MsgPack.Abstraction/Serialization/{AsyncDeserializationOperationContext`1.tt => AsyncDeserializationOperationContext.tt} (100%) rename src/MsgPack.Abstraction/Serialization/{AsyncSerializationOperationContext`1.cs => AsyncSerializationOperationContext.cs} (73%) rename src/MsgPack.Abstraction/Serialization/{AsyncSerializationOperationContext`1.tt => AsyncSerializationOperationContext.tt} (100%) rename src/MsgPack.Abstraction/Serialization/{DeserializationOperationContext`1.cs => DeserializationOperationContext.cs} (86%) rename src/MsgPack.Abstraction/Serialization/{DeserializationOperationContext`1.tt => DeserializationOperationContext.tt} (100%) delete mode 100644 src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs create mode 100644 src/MsgPack.Abstraction/Serialization/ObjectSerializationContext.cs create mode 100644 src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs rename src/MsgPack.Abstraction/Serialization/{SerializationOperationContext`1.cs => SerializationOperationContext.cs} (84%) rename src/MsgPack.Abstraction/Serialization/{SerializationOperationContext`1.tt => SerializationOperationContext.tt} (100%) create mode 100644 src/MsgPack.Abstraction/Serialization/Serializer.cs delete mode 100644 src/MsgPack.Core/Internal/MessagePackExtensionType.cs create mode 100644 src/MsgPack.Serialization.ILGeneration/MsgPack.Serialization.ILGeneration.csproj create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`2.cs create mode 100644 src/MsgPack.Serialization/Properties/AssemblyInfo.cs create mode 100644 src/MsgPack.Serialization/Serialization/MessagePackNullableAttribute.cs create mode 100644 src/MsgPack.Serialization/Serialization/Reflection/EncoderMethods.cs create mode 100644 src/MsgPack.Serialization/Serialization/Reflection/SpanMethods.cs create mode 100644 src/MsgPack.Serialization/Serialization/SerializerBuilder.cs create mode 100644 src/MsgPack.Serialization/Serialization/SerializerGenerationOptions.cs create mode 100644 src/MsgPack.Serialization/Serialization/Throw.Internal.cs create mode 100644 src/MsgPack.Serialization/Serialization/Throw.cs create mode 100644 src/MsgPack.Serialization/Serialization/Throw.tt create mode 100644 src/MsgPack.Serialization/Serialization/Throw.ttinclude create mode 100644 src/MsgPack.Serialization/Utf8String.cs diff --git a/Directory.Build.props b/Directory.Build.props index 58a34002b..750f3f75c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,12 +4,25 @@ Debug AnyCPU ..\..\ + MsgPack enable + + $(DefineConstants);UNITY;AOT + + + $(DefineConstants);LIBRARY + + + $(DefineConstants);FEATURE_READ_ONLY_COLLECTION;FEATURE_TAP + + + $(DefineConstants);FEATURE_ASYNC_DISPOSABLE + $(DefineConstants);FEATURE_ASYNC_DISPOSABLE - + $(DefineConstants);FEATURE_ASYNC_DISPOSABLE;FEATURE_ENCODING_EXTENSION;FEATURE_UTF8STRING diff --git a/doc/v2.md b/doc/v2.md index 7f1c3ddd7..655032d4e 100644 --- a/doc/v2.md +++ b/doc/v2.md @@ -5,6 +5,15 @@ Goals --- * Supports .NET Framework 3.5 and 4.8, .NET Core 2.1 and 3.1, Xamarin Android and iOS, UWP, Unity. + * Supproted TFM: + * [ ] `net35` + * [ ] `netstandard1.4` (for .NET 4.8) + * [ ] `netstandard2.0` (for .NET Core 2.1) + * [ ] `netstandard2.1` (for .NET Core 3.1 and after) + * [ ] `MonoAndroid10` (for Xamarin Android AOT) + * [ ] `Xamarin.iOS10` (for Xamarin iOS AOT) + * [ ] `uap` (for .NET Native) + * [ ] (N/A) (for Unity IL2CPP) * Supports stream based serialization/deserialization for large blobs like v1. * Supports various format as possible including JSON, CBOF, protbuf, etc. * Keep high level API compatiblity with v1. @@ -24,8 +33,8 @@ Non-Goals Status --- -* [ ] Encoder/Decoder layer utlizing `ReadOnlySequence` and `IBufferWriter` -* [ ] Simple JSON serializer with some tweak points for design verification. \[in progress] +* [x] Encoder/Decoder layer utlizing `ReadOnlySequence` and `IBufferWriter` +* [x] Simple JSON serializer with some tweak points for design verification. * [ ] Debugging features * [ ] Reflection based object serializer * [ ] Code generator w/ CodDOM or Roslyn diff --git a/src/MsgPack.Abstraction/ExtensionType.cs b/src/MsgPack.Abstraction/ExtensionType.cs new file mode 100644 index 000000000..1dd4e92b7 --- /dev/null +++ b/src/MsgPack.Abstraction/ExtensionType.cs @@ -0,0 +1,117 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.ComponentModel; + +namespace MsgPack +{ + /// + /// Represents a type of extension. + /// + public readonly struct ExtensionType : IComparable, IEquatable + { + /// + /// Gets raw tag data. + /// + /// Raw tag data. Note that the codec might not allow full length. + /// + /// Valid range and sementics depend on serialization codec. + /// + public ulong Tag { get; } + + /// + /// Initializes new instance. + /// This constructor should not be called from application directly. + /// + /// Raw tag data. + [EditorBrowsable(EditorBrowsableState.Never)] + public ExtensionType(ulong tag) + { + this.Tag = tag; + } + + /// + public bool Equals(ExtensionType other) + => this.Tag == other.Tag; + + /// + public int CompareTo(ExtensionType other) + => this.Tag.CompareTo(other.Tag); + + /// + public override bool Equals(object? obj) + => (obj is ExtensionType other) ? this.Equals(other) : false; + + /// + public override int GetHashCode() + => this.Tag.GetHashCode(); + + /// + /// Determines whether two specified instances of are equal. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if and represent the same type; otherwise, false. + /// + public static bool operator ==(ExtensionType left, ExtensionType right) + => left.Tag == right.Tag; + + /// + /// Determines whether two specified instances of are not equal. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if and represent the different type; otherwise, false. + /// + public static bool operator !=(ExtensionType left, ExtensionType right) + => left.Tag != right.Tag; + + /// + /// Determines whether one specified object is less than a second specified object. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if is less than in their tag; otherwise, false. + /// + public static bool operator <(ExtensionType left, ExtensionType right) + => left.Tag < right.Tag; + + /// + /// Determines whether one specified object is greater than a second specified object. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if is greater than in their tag; otherwise, false. + /// + public static bool operator >(ExtensionType left, ExtensionType right) + => left.Tag > right.Tag; + + /// + /// Determines whether one specified object is less than or equal to a second specified object. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if is less than or equal to in their tag; otherwise, false. + /// + public static bool operator <=(ExtensionType left, ExtensionType right) + => left.Tag <= right.Tag; + + /// + /// Determines whether one specified object is greater than or equal to a second specified object. + /// + /// The first object to compare. + /// The second object to compare. + /// + /// true if is greater than or equal to in their tag; otherwise, false. + /// + public static bool operator >=(ExtensionType left, ExtensionType right) + => left.Tag >= right.Tag; + } +} diff --git a/src/MsgPack.Abstraction/ExtensionTypeObject.cs b/src/MsgPack.Abstraction/ExtensionTypeObject.cs new file mode 100644 index 000000000..e3b0b0962 --- /dev/null +++ b/src/MsgPack.Abstraction/ExtensionTypeObject.cs @@ -0,0 +1,41 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; + +namespace MsgPack +{ + /// + /// Represents extension type object data. + /// + /// + /// Valid range and sementics of depend on serialization codec. + /// In addition, some codec does not support extension type at all. + /// + public readonly struct ExtensionTypeObject + { + /// + /// Gets a type of this extension type data. + /// + /// Codec specific type of this extension type data. + public ExtensionType Type { get; } + + /// + /// Gets byte sequence which is body of this extension type data. + /// + /// Byte sequence which is body of this extension type data. + public ReadOnlySequence Body { get; } + + /// + /// Initializes a new instance. + /// + /// Codec specific type of this extension type data. + /// Byte sequence which is body of this extension type data. + public ExtensionTypeObject(ExtensionType type, ReadOnlySequence body) + { + this.Type = type; + this.Body = body; + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs b/src/MsgPack.Abstraction/Internal/DecodeItemResult.cs similarity index 50% rename from src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs rename to src/MsgPack.Abstraction/Internal/DecodeItemResult.cs index 42749053b..1f1ef3a51 100644 --- a/src/MsgPack.Abstraction/Internal/DecodeItemResult`1.cs +++ b/src/MsgPack.Abstraction/Internal/DecodeItemResult.cs @@ -9,16 +9,16 @@ namespace MsgPack.Internal { /// - /// Represents a result of . + /// Represents a result of . /// - public readonly struct DecodeItemResult + public readonly struct DecodeItemResult { public bool HasValue => this.ElementType != ElementType.None; public ElementType ElementType { get; } public ReadOnlySequence Value { get; } public CollectionItemIterator CollectionIterator { get; } public long CollectionLength { get; } - public TExtensionType ExtensionType { get; } + public ExtensionTypeObject ExtensionTypeObject { get; } public ReadOnlySequence ExtensionBody => this.Value; public long RequestHint { get; } @@ -27,7 +27,7 @@ private DecodeItemResult( in ReadOnlySequence value = default, in CollectionItemIterator collectionIterator = default, long collectionLength = default, - TExtensionType extensionType = default, + ExtensionTypeObject extensionTypeObject = default, long requestHint = default ) { @@ -35,40 +35,40 @@ private DecodeItemResult( this.Value = value; this.CollectionIterator = collectionIterator; this.CollectionLength = collectionLength; - this.ExtensionType = extensionType; + this.ExtensionTypeObject = extensionTypeObject; this.RequestHint = requestHint; } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public static DecodeItemResult CollectionHeader(ElementType elementType, in CollectionItemIterator iterator, long length = -1) - => new DecodeItemResult(elementType, collectionIterator: iterator, collectionLength: length); + public static DecodeItemResult CollectionHeader(ElementType elementType, in CollectionItemIterator iterator, long length = -1) + => new DecodeItemResult(elementType, collectionIterator: iterator, collectionLength: length); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public static DecodeItemResult ScalarOrSequence(ElementType elementType, ReadOnlyMemory value) + public static DecodeItemResult ScalarOrSequence(ElementType elementType, ReadOnlyMemory value) => ScalarOrSequence(elementType, new ReadOnlySequence(value)); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public static DecodeItemResult ScalarOrSequence(ElementType elementType, in ReadOnlySequence value) - => new DecodeItemResult(elementType, value: value); + public static DecodeItemResult ScalarOrSequence(ElementType elementType, in ReadOnlySequence value) + => new DecodeItemResult(elementType, value: value); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public static DecodeItemResult Null() - => new DecodeItemResult(ElementType.Null); + public static DecodeItemResult Null() + => new DecodeItemResult(ElementType.Null); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public static DecodeItemResult True() - => new DecodeItemResult(ElementType.True); + public static DecodeItemResult True() + => new DecodeItemResult(ElementType.True); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public static DecodeItemResult False() - => new DecodeItemResult(ElementType.False); + public static DecodeItemResult False() + => new DecodeItemResult(ElementType.False); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public static DecodeItemResult ExtensionTypeObject(TExtensionType extensionType, in ReadOnlySequence body) - => new DecodeItemResult(ElementType.Extension, extensionType : extensionType, value : body); + public static DecodeItemResult ExtensionType(ExtensionTypeObject extensionTypeObject) + => new DecodeItemResult(ElementType.Extension, extensionTypeObject : extensionTypeObject); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public static DecodeItemResult InsufficientInput(long requestHint) - => new DecodeItemResult(ElementType.None, requestHint: requestHint); + public static DecodeItemResult InsufficientInput(long requestHint) + => new DecodeItemResult(ElementType.None, requestHint: requestHint); } } diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs b/src/MsgPack.Abstraction/Internal/FormatDecoder.Primitives.cs similarity index 99% rename from src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs rename to src/MsgPack.Abstraction/Internal/FormatDecoder.Primitives.cs index 7765926f1..55630e381 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.cs +++ b/src/MsgPack.Abstraction/Internal/FormatDecoder.Primitives.cs @@ -12,7 +12,7 @@ namespace MsgPack.Internal { - partial class Decoder + partial class FormatDecoder { /// /// Decodes value from specified sequence. diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt b/src/MsgPack.Abstraction/Internal/FormatDecoder.Primitives.tt similarity index 99% rename from src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt rename to src/MsgPack.Abstraction/Internal/FormatDecoder.Primitives.tt index 0544c21c5..3eb211dcd 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.Primitives.tt +++ b/src/MsgPack.Abstraction/Internal/FormatDecoder.Primitives.tt @@ -14,7 +14,7 @@ using System.Runtime.CompilerServices; namespace MsgPack.Internal { - partial class Decoder + partial class FormatDecoder { <# foreach (var outputType in new [] { diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs b/src/MsgPack.Abstraction/Internal/FormatDecoder.Strings.cs similarity index 99% rename from src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs rename to src/MsgPack.Abstraction/Internal/FormatDecoder.Strings.cs index f9aeb3571..0bdc088f8 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.cs +++ b/src/MsgPack.Abstraction/Internal/FormatDecoder.Strings.cs @@ -16,7 +16,7 @@ namespace MsgPack.Internal { - partial class Decoder + partial class FormatDecoder { /// /// Decodes value from specified sequence. diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt b/src/MsgPack.Abstraction/Internal/FormatDecoder.Strings.tt similarity index 99% rename from src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt rename to src/MsgPack.Abstraction/Internal/FormatDecoder.Strings.tt index f731b08a9..5f4feaaf5 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.Strings.tt +++ b/src/MsgPack.Abstraction/Internal/FormatDecoder.Strings.tt @@ -18,7 +18,7 @@ using System.Threading; namespace MsgPack.Internal { - partial class Decoder + partial class FormatDecoder { <# foreach (var spec in new [] { diff --git a/src/MsgPack.Abstraction/Internal/Decoder`1.cs b/src/MsgPack.Abstraction/Internal/FormatDecoder.cs similarity index 94% rename from src/MsgPack.Abstraction/Internal/Decoder`1.cs rename to src/MsgPack.Abstraction/Internal/FormatDecoder.cs index 271fd0ee9..20e0b154e 100644 --- a/src/MsgPack.Abstraction/Internal/Decoder`1.cs +++ b/src/MsgPack.Abstraction/Internal/FormatDecoder.cs @@ -12,19 +12,18 @@ namespace MsgPack.Internal #warning TODO: Use 'Try' prefix for published APIs which can return 'requestHint'. /// - /// Defines an interface and basic functionarity of stateless . + /// Defines an interface and basic functionarity of stateless . /// - /// A type of extension type. /// - /// The is stateless, so caller (serializer, writer, etc.) can cache the instance for performance. + /// The is stateless, so caller (serializer, writer, etc.) can cache the instance for performance. /// - public abstract partial class Decoder + public abstract partial class FormatDecoder { public FormatFeatures FormatFeatures { get; } - public DecoderOptions Options { get; } + public FormatDecoderOptions Options { get; } - protected Decoder(DecoderOptions options, FormatFeatures formatFeatures) + protected FormatDecoder(FormatDecoderOptions options, FormatFeatures formatFeatures) { this.Options = Ensure.NotNull(options); this.FormatFeatures = Ensure.NotNull(formatFeatures); @@ -42,7 +41,7 @@ public void Skip(ref SequenceReader source, in CollectionContext collectio public abstract void Skip(ref SequenceReader source, in CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken = default); - public abstract bool DecodeItem(ref SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default); + public abstract bool DecodeItem(ref SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] public void GetRawString(ref SequenceReader source, out ReadOnlySpan rawString, CancellationToken cancellationToken = default) @@ -188,13 +187,12 @@ public long DecodeMapHeader(ref SequenceReader source) /// The decoded value is not a map. public abstract long DecodeMapHeader(ref SequenceReader source, out int requestHint); - public virtual void DecodeExtension(ref SequenceReader source, out TExtensionType typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) + public virtual void DecodeExtension(ref SequenceReader source, out ExtensionTypeObject result, out int requestHint, CancellationToken cancellationToken = default) { Throw.ExtensionsIsNotSupported(); // never - body = default; + result = default; requestHint = -1; - typeCode = default!; } public CollectionType DecodeArrayOrMap(ref SequenceReader source, out CollectionItemIterator iterator) diff --git a/src/MsgPack.Abstraction/Internal/DecoderOptions.cs b/src/MsgPack.Abstraction/Internal/FormatDecoderOptions.cs similarity index 92% rename from src/MsgPack.Abstraction/Internal/DecoderOptions.cs rename to src/MsgPack.Abstraction/Internal/FormatDecoderOptions.cs index 9e333bc2c..ef55e0238 100644 --- a/src/MsgPack.Abstraction/Internal/DecoderOptions.cs +++ b/src/MsgPack.Abstraction/Internal/FormatDecoderOptions.cs @@ -6,7 +6,7 @@ namespace MsgPack.Internal { - public abstract class DecoderOptions + public abstract class FormatDecoderOptions { public bool CanTreatRealAsInteger { get; } @@ -28,7 +28,7 @@ public abstract class DecoderOptions public bool ClearsBuffer { get; } - protected DecoderOptions(DecoderOptionsBuilder builder) + protected FormatDecoderOptions(FormatDecoderOptionsBuilder builder) { builder = Ensure.NotNull(builder); diff --git a/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs b/src/MsgPack.Abstraction/Internal/FormatDecoderOptionsBuilder.cs similarity index 92% rename from src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs rename to src/MsgPack.Abstraction/Internal/FormatDecoderOptionsBuilder.cs index be520fadb..766108071 100644 --- a/src/MsgPack.Abstraction/Internal/DecoderOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Internal/FormatDecoderOptionsBuilder.cs @@ -7,7 +7,7 @@ namespace MsgPack.Internal { #warning Remove unused buffer max pr rename to initial buffer length if appropriate. - public abstract class DecoderOptionsBuilder + public abstract class FormatDecoderOptionsBuilder { public bool CanTreatRealAsInteger { get; set; } = OptionsDefaults.CanTreatRealAsInteger; @@ -77,15 +77,15 @@ public ArrayPool CharBufferPool public bool ClearsBuffer { get; set; } = OptionsDefaults.ClearsBuffer; - protected DecoderOptionsBuilder() { } + protected FormatDecoderOptionsBuilder() { } - public DecoderOptionsBuilder WithoutBufferClear() + public FormatDecoderOptionsBuilder WithoutBufferClear() { this.ClearsBuffer = false; return this; } - public DecoderOptionsBuilder ProhibitTreatRealAsInteger() + public FormatDecoderOptionsBuilder ProhibitTreatRealAsInteger() { this.CanTreatRealAsInteger = false; return this; diff --git a/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs b/src/MsgPack.Abstraction/Internal/FormatEncoder.Primitives.cs similarity index 99% rename from src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs rename to src/MsgPack.Abstraction/Internal/FormatEncoder.Primitives.cs index bea877506..0c4701a06 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.cs +++ b/src/MsgPack.Abstraction/Internal/FormatEncoder.Primitives.cs @@ -12,7 +12,7 @@ namespace MsgPack.Internal { - partial class Encoder + partial class FormatEncoder { /// /// Encodes value to specified buffer. diff --git a/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt b/src/MsgPack.Abstraction/Internal/FormatEncoder.Primitives.tt similarity index 98% rename from src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt rename to src/MsgPack.Abstraction/Internal/FormatEncoder.Primitives.tt index 84556bc5c..939f76871 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder`1.Primitives.tt +++ b/src/MsgPack.Abstraction/Internal/FormatEncoder.Primitives.tt @@ -14,7 +14,7 @@ using System.Runtime.CompilerServices; namespace MsgPack.Internal { - partial class Encoder + partial class FormatEncoder { <# foreach (var inputType in new [] { diff --git a/src/MsgPack.Abstraction/Internal/Encoder`1.cs b/src/MsgPack.Abstraction/Internal/FormatEncoder.cs similarity index 90% rename from src/MsgPack.Abstraction/Internal/Encoder`1.cs rename to src/MsgPack.Abstraction/Internal/FormatEncoder.cs index 709e4f1f9..6e85ef2a1 100644 --- a/src/MsgPack.Abstraction/Internal/Encoder`1.cs +++ b/src/MsgPack.Abstraction/Internal/FormatEncoder.cs @@ -11,17 +11,16 @@ namespace MsgPack.Internal { /// - /// Defines an interface and basic functionarity of stateless . + /// Defines an interface and basic functionarity of stateless . /// - /// A type of extension type. /// - /// The is stateless, so caller (serializer, reader, etc.) can cache the instance for performance. + /// The is stateless, so caller (serializer, reader, etc.) can cache the instance for performance. /// - public abstract partial class Encoder + public abstract partial class FormatEncoder { - public EncoderOptions Options { get; } + public FormatEncoderOptions Options { get; } - protected Encoder(EncoderOptions options) + protected FormatEncoder(FormatEncoderOptions options) { this.Options = Ensure.NotNull(options); } @@ -84,10 +83,10 @@ public void EncodeString(string? value, IBufferWriter buffer, Encoding? en public abstract void EncodeMapValueEnd(int index, IBufferWriter buffer, in CollectionContext collectionContext); - public virtual void EncodeExtension(TExtensionType typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) + public virtual void EncodeExtension(ExtensionType typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) => Throw.ExtensionsIsNotSupported(); - public virtual void EncodeExtension(TExtensionType typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) + public virtual void EncodeExtension(ExtensionType typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) => Throw.ExtensionsIsNotSupported(); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] diff --git a/src/MsgPack.Abstraction/Internal/EncoderOptions.cs b/src/MsgPack.Abstraction/Internal/FormatEncoderOptions.cs similarity index 88% rename from src/MsgPack.Abstraction/Internal/EncoderOptions.cs rename to src/MsgPack.Abstraction/Internal/FormatEncoderOptions.cs index 8ec9bcd3b..45a60a7d1 100644 --- a/src/MsgPack.Abstraction/Internal/EncoderOptions.cs +++ b/src/MsgPack.Abstraction/Internal/FormatEncoderOptions.cs @@ -6,7 +6,7 @@ namespace MsgPack.Internal { - public abstract class EncoderOptions + public abstract class FormatEncoderOptions { public int CancellationSupportThreshold { get; } @@ -20,7 +20,7 @@ public abstract class EncoderOptions public bool ClearsBuffer { get; } - protected EncoderOptions(EncoderOptionsBuilder builder) + protected FormatEncoderOptions(FormatEncoderOptionsBuilder builder) { builder = Ensure.NotNull(builder); diff --git a/src/MsgPack.Abstraction/Internal/EncoderOptionsBuilder.cs b/src/MsgPack.Abstraction/Internal/FormatEncoderOptionsBuilder.cs similarity index 90% rename from src/MsgPack.Abstraction/Internal/EncoderOptionsBuilder.cs rename to src/MsgPack.Abstraction/Internal/FormatEncoderOptionsBuilder.cs index 7fa2d2ef6..569966abc 100644 --- a/src/MsgPack.Abstraction/Internal/EncoderOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Internal/FormatEncoderOptionsBuilder.cs @@ -6,7 +6,7 @@ namespace MsgPack.Internal { - public abstract class EncoderOptionsBuilder + public abstract class FormatEncoderOptionsBuilder { private int _cancellationSupportThreshold = OptionsDefaults.CancellationSupportThreshold; @@ -50,9 +50,9 @@ public ArrayPool CharBufferPool public bool ClearsBuffer { get; set; } = OptionsDefaults.ClearsBuffer; - protected EncoderOptionsBuilder() { } + protected FormatEncoderOptionsBuilder() { } - public EncoderOptionsBuilder WithoutBufferClear() + public FormatEncoderOptionsBuilder WithoutBufferClear() { this.ClearsBuffer = false; return this; diff --git a/src/MsgPack.Abstraction/Internal/FormatFeatures.cs b/src/MsgPack.Abstraction/Internal/FormatFeatures.cs index 57a1c8645..4b490fa21 100644 --- a/src/MsgPack.Abstraction/Internal/FormatFeatures.cs +++ b/src/MsgPack.Abstraction/Internal/FormatFeatures.cs @@ -18,14 +18,14 @@ public sealed class FormatFeatures /// /// When this property returns false, following methods should throw . /// - /// - /// - /// - /// - /// - /// - /// - /// + /// + /// + /// + /// + /// + /// + /// + /// /// /// public bool CanCountCollectionItems { get; } @@ -50,9 +50,9 @@ public sealed class FormatFeatures /// /// When this property returns false, following methods should throw . /// - /// - /// - /// + /// + /// + /// /// /// In additon, TExtentionType type parameter should be . /// diff --git a/src/MsgPack.Abstraction/Internal/NullExtensionType.cs b/src/MsgPack.Abstraction/Internal/NullExtensionType.cs deleted file mode 100644 index 0de495693..000000000 --- a/src/MsgPack.Abstraction/Internal/NullExtensionType.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. -// This file is licensed under Apache2 license. -// See the LICENSE in the project root for more information. - -namespace MsgPack.Internal -{ - /// - /// Represents null extension type for underlying formats which do not support extension type. - /// - public struct NullExtensionType { } -} diff --git a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj index 08f97e67a..965c43fb5 100644 --- a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj +++ b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj @@ -7,38 +7,36 @@ - - - + TextTemplatingFileGenerator - Decoder`1.Strings.cs + FormatDecoder.Strings.cs - - Encoder`1.Primitives.cs + + FormatEncoder.Primitives.cs TextTemplatingFileGenerator - + TextTemplatingFileGenerator - Decoder`1.Primitives.cs + FormatDecoder.Primitives.cs - - AsyncSerializationOperationContext`1.cs + + AsyncSerializationOperationContext.cs TextTemplatingFileGenerator - - AsyncDeserializationOperationContext`1.cs + + AsyncDeserializationOperationContext.cs TextTemplatingFileGenerator - - DeserializationOperationContext`1.cs + + DeserializationOperationContext.cs TextTemplatingFileGenerator - - SerializationOperationContext`1.cs + + SerializationOperationContext.cs TextTemplatingFileGenerator @@ -48,40 +46,40 @@ - + True True - Decoder`1.Primitives.tt + FormatDecoder.Primitives.tt - + True True - Decoder`1.Strings.tt + FormatDecoder.Strings.tt - + True True - Encoder`1.Primitives.tt + FormatEncoder.Primitives.tt - + True True - AsyncDeserializationOperationContext`1.tt + AsyncDeserializationOperationContext.tt - + True True - AsyncSerializationOperationContext`1.tt + AsyncSerializationOperationContext.tt - + True True - DeserializationOperationContext`1.tt + DeserializationOperationContext.tt - + True True - SerializationOperationContext`1.tt + SerializationOperationContext.tt diff --git a/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.cs similarity index 77% rename from src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.cs rename to src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.cs index 53025b8c9..70a1d3911 100644 --- a/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.cs @@ -16,16 +16,16 @@ namespace MsgPack.Serialization { - public sealed class AsyncDeserializationOperationContext + public sealed class AsyncDeserializationOperationContext { - public Decoder Decoder { get; } + public FormatDecoder Decoder { get; } public DeserializationOptions Options { get; } public Encoding? StringEncoding => this.Options.StringEncoding; public ArrayPool ByteBufferPool => this.Options.ByteBufferPool; public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } - public AsyncDeserializationOperationContext(Decoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) + public AsyncDeserializationOperationContext(FormatDecoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) { this.Decoder = Ensure.NotNull(decoder); this.Options = options ?? DeserializationOptions.Default; @@ -33,8 +33,8 @@ public AsyncDeserializationOperationContext(Decoder decoder, Des this.CancellationToken = cancellationToken; } - public DeserializationOperationContext AsDeserializationOperationContext() - => new DeserializationOperationContext(this.Decoder, this.Options, this.CancellationToken); + public DeserializationOperationContext AsDeserializationOperationContext() + => new DeserializationOperationContext(this.Decoder, this.Options, this.CancellationToken); public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this.CurrentDepth); diff --git a/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.tt similarity index 100% rename from src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext`1.tt rename to src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.tt diff --git a/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs similarity index 73% rename from src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.cs rename to src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs index 6fb561b90..0bd040e1f 100644 --- a/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs @@ -15,15 +15,15 @@ namespace MsgPack.Serialization { - public sealed class AsyncSerializationOperationContext + public sealed class AsyncSerializationOperationContext { - public Encoder Encoder { get; } + public FormatEncoder Encoder { get; } public SerializationOptions Options { get; } public Encoding? StringEncoding => this.Options.StringEncoding; public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } - public AsyncSerializationOperationContext(Encoder encoder, SerializationOptions? options, CancellationToken cancellationToken) + public AsyncSerializationOperationContext(FormatEncoder encoder, SerializationOptions? options, CancellationToken cancellationToken) { this.Encoder = Ensure.NotNull(encoder); this.Options = options ?? SerializationOptions.Default; @@ -31,8 +31,8 @@ public AsyncSerializationOperationContext(Encoder encoder, Seria this.CancellationToken = cancellationToken; } - public SerializationOperationContext AsSerializationOperationContext() - => new SerializationOperationContext(this.Encoder, this.Options, this.CancellationToken); + public SerializationOperationContext AsSerializationOperationContext() + => new SerializationOperationContext(this.Encoder, this.Options, this.CancellationToken); public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this.CurrentDepth); diff --git a/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.tt similarity index 100% rename from src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext`1.tt rename to src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.tt diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext.cs similarity index 86% rename from src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.cs rename to src/MsgPack.Abstraction/Serialization/DeserializationOperationContext.cs index b0b762a74..6e95b0627 100644 --- a/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext.cs @@ -16,16 +16,16 @@ namespace MsgPack.Serialization { - public struct DeserializationOperationContext + public struct DeserializationOperationContext { - public Decoder Decoder { get; } + public FormatDecoder Decoder { get; } public DeserializationOptions Options { get; } public Encoding? StringEncoding => this.Options.StringEncoding; public ArrayPool ByteBufferPool => this.Options.ByteBufferPool; public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } - public DeserializationOperationContext(Decoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) + public DeserializationOperationContext(FormatDecoder decoder, DeserializationOptions? options, CancellationToken cancellationToken) { this.Decoder = Ensure.NotNull(decoder); this.Options = options ?? DeserializationOptions.Default; diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/DeserializationOperationContext.tt similarity index 100% rename from src/MsgPack.Abstraction/Serialization/DeserializationOperationContext`1.tt rename to src/MsgPack.Abstraction/Serialization/DeserializationOperationContext.tt diff --git a/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs b/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs deleted file mode 100644 index 762b962da..000000000 --- a/src/MsgPack.Abstraction/Serialization/Internal/IObjectSerializer`2.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. -// This file is licensed under Apache2 license. -// See the LICENSE in the project root for more information. - -using System.Buffers; -using System.IO; -using System.Threading.Tasks; - -namespace MsgPack.Serialization.Internal -{ - public interface IObjectSerializer - { - void Serialize(ref SerializationOperationContext context, T obj, IBufferWriter sink); - ValueTask SerializeAsync(AsyncSerializationOperationContext context, T obj, Stream streamSink); - T Deserialize(ref DeserializationOperationContext context, ref SequenceReader source); - ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource); - bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in T obj); - ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, T obj); - } -} diff --git a/src/MsgPack.Abstraction/Serialization/ObjectSerializationContext.cs b/src/MsgPack.Abstraction/Serialization/ObjectSerializationContext.cs new file mode 100644 index 000000000..d4f86ba0b --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/ObjectSerializationContext.cs @@ -0,0 +1,17 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack.Serialization +{ +#warning TODO: Remove UGLY TExtensionType + public sealed class ObjectSerializationContext + { + public ObjectSerializer GetSerializer(object providerParameter) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs b/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs new file mode 100644 index 000000000..2cdd62ec6 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs @@ -0,0 +1,43 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading.Tasks; +using MsgPack.Internal; + +namespace MsgPack.Serialization +{ + public abstract class ObjectSerializer + { + protected ObjectSerializationContext OwnerContext { get; } + public SerializerCapabilities Capabilities { get; } + public bool CanSerialize => (this.Capabilities & SerializerCapabilities.Serialize) != 0; + public bool CanDeserialize => (this.Capabilities & SerializerCapabilities.Deserialize) != 0; + public bool CanDeserializeTo => (this.Capabilities & SerializerCapabilities.DeserializeTo) != 0; + + protected ObjectSerializer(ObjectSerializationContext ownerContext, SerializerCapabilities capabilities) + { + this.OwnerContext = Ensure.NotNull(ownerContext); + this.Capabilities = capabilities; + } + + public abstract void Serialize(ref SerializationOperationContext context, [AllowNull] T obj, IBufferWriter sink); + public async ValueTask SerializeAsync(AsyncSerializationOperationContext context, [AllowNull] T obj, Stream streamSink) + { + await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) + { + var serializationOperationContext = context.AsSerializationOperationContext(); + this.Serialize(ref serializationOperationContext, obj, writer); + } + } + + [return: MaybeNull] + public abstract T Deserialize(ref DeserializationOperationContext context, ref SequenceReader source); + public abstract ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource); + public abstract bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in T obj); + public abstract ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, T obj); + } +} diff --git a/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude b/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude index 8d886708e..9335f56b1 100644 --- a/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude +++ b/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude @@ -5,7 +5,8 @@ void WriteContext(bool isSerialization, bool isAsync) var typePrefix = isSerialization ? "Serialization" : "Deserialization"; var typeName = $"{(isAsync ? "Async" : String.Empty)}{typePrefix}OperationContext"; var optionTypeName = $"{typePrefix}Options"; - var primitive = isSerialization ? "Encoder" : "Decoder"; + var primitiveType = isSerialization ? "FormatEncoder" : "FormatDecoder"; + var primitiveName = isSerialization ? "Encoder" : "Decoder"; #> // Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under Apache2 license. @@ -32,9 +33,9 @@ using MsgPack.Internal; namespace MsgPack.Serialization { - public <#= typeClass #> <#= typeName #> + public <#= typeClass #> <#= typeName #> { - public <#= primitive #> <#= primitive #> { get; } + public <#= primitiveType #> <#= primitiveName #> { get; } public <#= optionTypeName #> Options { get; } public Encoding? StringEncoding => this.Options.StringEncoding; <#+ @@ -48,9 +49,9 @@ namespace MsgPack.Serialization public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } - public <#= typeName #>(<#= primitive #> <#= primitive.ToLowerInvariant() #>, <#= optionTypeName #>? options, CancellationToken cancellationToken) + public <#= typeName #>(<#= primitiveType #> <#= primitiveName.ToLowerInvariant() #>, <#= optionTypeName #>? options, CancellationToken cancellationToken) { - this.<#= primitive #> = Ensure.NotNull(<#= primitive.ToLowerInvariant() #>); + this.<#= primitiveName #> = Ensure.NotNull(<#= primitiveName.ToLowerInvariant() #>); this.Options = options ?? <#= optionTypeName #>.Default; this.CurrentDepth = 0; this.CancellationToken = cancellationToken; @@ -61,8 +62,8 @@ namespace MsgPack.Serialization { var syncTypeName = typeName.Substring("Async".Length); #> - public <#= syncTypeName #> As<#= syncTypeName #>() - => new <#= syncTypeName #>(this.<#= primitive #>, this.Options, this.CancellationToken); + public <#= syncTypeName #> As<#= syncTypeName #>() + => new <#= syncTypeName #>(this.<#= primitiveName #>, this.Options, this.CancellationToken); <#+ } diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.cs b/src/MsgPack.Abstraction/Serialization/SerializationOperationContext.cs similarity index 84% rename from src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.cs rename to src/MsgPack.Abstraction/Serialization/SerializationOperationContext.cs index 36f7ca1fc..75d24d1c2 100644 --- a/src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.cs +++ b/src/MsgPack.Abstraction/Serialization/SerializationOperationContext.cs @@ -15,15 +15,15 @@ namespace MsgPack.Serialization { - public struct SerializationOperationContext + public struct SerializationOperationContext { - public Encoder Encoder { get; } + public FormatEncoder Encoder { get; } public SerializationOptions Options { get; } public Encoding? StringEncoding => this.Options.StringEncoding; public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } - public SerializationOperationContext(Encoder encoder, SerializationOptions? options, CancellationToken cancellationToken) + public SerializationOperationContext(FormatEncoder encoder, SerializationOptions? options, CancellationToken cancellationToken) { this.Encoder = Ensure.NotNull(encoder); this.Options = options ?? SerializationOptions.Default; diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.tt b/src/MsgPack.Abstraction/Serialization/SerializationOperationContext.tt similarity index 100% rename from src/MsgPack.Abstraction/Serialization/SerializationOperationContext`1.tt rename to src/MsgPack.Abstraction/Serialization/SerializationOperationContext.tt diff --git a/src/MsgPack.Abstraction/Serialization/Serializer.cs b/src/MsgPack.Abstraction/Serialization/Serializer.cs new file mode 100644 index 000000000..a07d09f2f --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/Serializer.cs @@ -0,0 +1,29 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MsgPack.Serialization +{ + public abstract class Serializer + { + public abstract void SerializeObject(object? obj, IBufferWriter sink, CancellationToken cancellationToken = default); + + public abstract ReadOnlyMemory SerializeObject(object? obj, CancellationToken cancellationToken = default); + + public abstract ValueTask SerializeObjectAsync(object? obj, Stream streamSink, CancellationToken cancellationToken = default); + + public abstract object? DeserializeObject(ref SequenceReader reader, CancellationToken cancellationToken = default); + + public abstract object? DeserializeObject(ref ReadOnlySequence source, CancellationToken cancellationToken = default); + + public abstract object? DeserializeObject(ref ReadOnlyMemory memorySource, CancellationToken cancellationToken = default); + + public abstract ValueTask DeserializeObjectAsync(Stream streamSource, CancellationToken cancellationToken = default); + } +} diff --git a/src/MsgPack.Abstraction/Serialization/Serializer`2.cs b/src/MsgPack.Abstraction/Serialization/Serializer`2.cs index 07abafe57..f0b9fd72f 100644 --- a/src/MsgPack.Abstraction/Serialization/Serializer`2.cs +++ b/src/MsgPack.Abstraction/Serialization/Serializer`2.cs @@ -4,26 +4,26 @@ using System; using System.Buffers; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Threading; using System.Threading.Tasks; using MsgPack.Internal; -using MsgPack.Serialization.Internal; namespace MsgPack.Serialization { - public abstract class Serializer + public abstract class Serializer : Serializer { - private readonly Func> _encoderFactory; - private readonly Func> _decoderFactory; - private readonly IObjectSerializer _underlying; + private readonly Func _encoderFactory; + private readonly Func _decoderFactory; + private readonly ObjectSerializer _underlying; private readonly SerializationOptions _serializationOptions; private readonly DeserializationOptions _deserializationOptions; protected Serializer( - Func> encoderFactory, - Func> decoderFactory, - IObjectSerializer underlying, + Func encoderFactory, + Func decoderFactory, + ObjectSerializer underlying, SerializationOptions serializationOptions, DeserializationOptions deserializationOptions ) @@ -35,17 +35,21 @@ DeserializationOptions deserializationOptions this._deserializationOptions = Ensure.NotNull(deserializationOptions); } - private void InitializeSerializationOperationContext(CancellationToken cancellationToken, out SerializationOperationContext context) - => context = new SerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); + private void InitializeSerializationOperationContext(CancellationToken cancellationToken, out SerializationOperationContext context) + => context = new SerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); - private void InitializeAsyncSerializationOperationContext(CancellationToken cancellationToken, out AsyncSerializationOperationContext context) - => context = new AsyncSerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); + private void InitializeAsyncSerializationOperationContext(CancellationToken cancellationToken, out AsyncSerializationOperationContext context) + => context = new AsyncSerializationOperationContext(this._encoderFactory(), this._serializationOptions, cancellationToken); - private void InitializeDeserializationOperationContext(CancellationToken cancellationToken, out DeserializationOperationContext context) - => context = new DeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); + private void InitializeDeserializationOperationContext(CancellationToken cancellationToken, out DeserializationOperationContext context) + => context = new DeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); - private void InitializeAsyncDeserializationOperationContext(CancellationToken cancellationToken, out AsyncDeserializationOperationContext context) - => context = new AsyncDeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); + private void InitializeAsyncDeserializationOperationContext(CancellationToken cancellationToken, out AsyncDeserializationOperationContext context) + => context = new AsyncDeserializationOperationContext(this._decoderFactory(), this._deserializationOptions, cancellationToken); + + /// + public sealed override void SerializeObject(object? obj, IBufferWriter sink, CancellationToken cancellationToken = default) + => this.Serialize((T)obj, cancellationToken); public void Serialize(T obj, IBufferWriter sink, CancellationToken cancellationToken = default) { @@ -53,7 +57,11 @@ public void Serialize(T obj, IBufferWriter sink, CancellationToken cancell this._underlying.Serialize(ref context, obj, sink); } - public ReadOnlyMemory Serialize(T obj, CancellationToken cancellationToken = default) + /// + public sealed override ReadOnlyMemory SerializeObject(object? obj, CancellationToken cancellationToken = default) + => this.Serialize((T)obj, cancellationToken); + + public ReadOnlyMemory Serialize([AllowNull]T obj, CancellationToken cancellationToken = default) { this.InitializeSerializationOperationContext(cancellationToken, out var context); var writer = new ArrayBufferWriter(); @@ -61,18 +69,31 @@ public ReadOnlyMemory Serialize(T obj, CancellationToken cancellationToken return writer.WrittenMemory; } - public ValueTask SerializeAsync(T obj, Stream streamSink, CancellationToken cancellationToken = default) + /// + public sealed override ValueTask SerializeObjectAsync(object? obj, Stream streamSink, CancellationToken cancellationToken = default) + => this.SerializeAsync((T)obj, streamSink, cancellationToken); + + public ValueTask SerializeAsync([AllowNull]T obj, Stream streamSink, CancellationToken cancellationToken = default) { this.InitializeAsyncSerializationOperationContext(cancellationToken, out var context); return this._underlying.SerializeAsync(context, obj, streamSink); } + /// + public sealed override object? DeserializeObject(ref SequenceReader reader, CancellationToken cancellationToken = default) + => this.Deserialize(ref reader, cancellationToken); + + [return:MaybeNull] public T Deserialize(ref SequenceReader reader, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); return this._underlying.Deserialize(ref context, ref reader); } + public sealed override object? DeserializeObject(ref ReadOnlySequence source, CancellationToken cancellationToken = default) + => this.Deserialize(ref source, cancellationToken); + + [return:MaybeNull] public T Deserialize(ref ReadOnlySequence source, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); @@ -82,6 +103,11 @@ public T Deserialize(ref ReadOnlySequence source, CancellationToken cancel return result; } + /// + public sealed override object? DeserializeObject(ref ReadOnlyMemory memorySource, CancellationToken cancellationToken = default) + => this.Deserialize(ref memorySource, cancellationToken); + + [return:MaybeNull] public T Deserialize(ref ReadOnlyMemory memorySource, CancellationToken cancellationToken = default) { this.InitializeDeserializationOperationContext(cancellationToken, out var context); @@ -91,6 +117,10 @@ public T Deserialize(ref ReadOnlyMemory memorySource, CancellationToken ca return result; } + /// + public sealed override async ValueTask DeserializeObjectAsync(Stream streamSource, CancellationToken cancellationToken = default) + => await this.DeserializeAsync(streamSource, cancellationToken).ConfigureAwait(false); + public ValueTask DeserializeAsync(Stream streamSource, CancellationToken cancellationToken = default) { this.InitializeAsyncDeserializationOperationContext(cancellationToken, out var context); diff --git a/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs b/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs index a41c8c45e..d95d12eb1 100644 --- a/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs +++ b/src/MsgPack.Core/Internal/CurrentMessagePackEncoder.cs @@ -78,21 +78,26 @@ protected sealed override int EncodeBinaryHeader(uint length, Span buffer) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override void EncodeExtension(MessagePackExtensionType typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) + public sealed override void EncodeExtension(ExtensionType typeCode, ReadOnlySpan serializedValue, IBufferWriter buffer) { EncodeExtensionHeader(typeCode, unchecked((uint)serializedValue.Length), buffer); buffer.Write(serializedValue); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public sealed override void EncodeExtension(MessagePackExtensionType typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) + public sealed override void EncodeExtension(ExtensionType typeCode, in ReadOnlySequence serializedValue, IBufferWriter buffer) { EncodeExtensionHeader(typeCode, unchecked((uint)serializedValue.Length), buffer); this.WriteRaw(serializedValue, buffer); } - private static void EncodeExtensionHeader(MessagePackExtensionType typeCode, uint serializedValueLength, IBufferWriter buffer) + private static void EncodeExtensionHeader(ExtensionType typeCode, uint serializedValueLength, IBufferWriter buffer) { + if (typeCode.Tag > Byte.MaxValue) + { + MessagePackThrow.InvalidTypeCode(typeCode.Tag); + } + switch (serializedValueLength) { case 1: @@ -163,7 +168,7 @@ private static void EncodeExtensionHeader(MessagePackExtensionType typeCode, uin // type code { var span = buffer.GetSpan(1); - span[0] = unchecked(typeCode.TypeCode); + span[0] = unchecked((byte)typeCode.Tag); buffer.Advance(1); } } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs index 71c6ac50d..790ba708f 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.DecodeItem.cs @@ -12,12 +12,12 @@ namespace MsgPack.Internal { public partial class MessagePackDecoder { - public sealed override bool DecodeItem(ref SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default) + public sealed override bool DecodeItem(ref SequenceReader source, out DecodeItemResult result, CancellationToken cancellationToken = default) { var elementType = this.ReadHeader(ref source, out var consumed, out var valueOrLength, out var requestHint); if (requestHint != 0) { - result = DecodeItemResult.InsufficientInput(requestHint); + result = DecodeItemResult.InsufficientInput(requestHint); return false; } @@ -27,17 +27,17 @@ public sealed override bool DecodeItem(ref SequenceReader source, out Deco { case ElementType.True: { - result = DecodeItemResult.True(); + result = DecodeItemResult.True(); break; } case ElementType.False: { - result = DecodeItemResult.False(); + result = DecodeItemResult.False(); break; } case ElementType.Null: { - result = DecodeItemResult.Null(); + result = DecodeItemResult.Null(); break; } case ElementType.Int32: @@ -46,14 +46,14 @@ public sealed override bool DecodeItem(ref SequenceReader source, out Deco var buffer = new byte[sizeof(int)]; var span = MemoryMarshal.Cast(buffer); span[0] = (int)valueOrLength; - result = DecodeItemResult.ScalarOrSequence(elementType, buffer); + result = DecodeItemResult.ScalarOrSequence(elementType, buffer); break; } case ElementType.Array: case ElementType.Map: { this.DecodeArrayOrMap(ref source, out var iterator); - result = DecodeItemResult.CollectionHeader(elementType, this.CreateIterator((uint)valueOrLength), valueOrLength); + result = DecodeItemResult.CollectionHeader(elementType, this.CreateIterator((uint)valueOrLength), valueOrLength); break; } case ElementType.Int64: @@ -63,7 +63,7 @@ public sealed override bool DecodeItem(ref SequenceReader source, out Deco var buffer = new byte[sizeof(long)]; var span = MemoryMarshal.Cast(buffer); span[0] = valueOrLength; - result = DecodeItemResult.ScalarOrSequence(elementType, buffer); + result = DecodeItemResult.ScalarOrSequence(elementType, buffer); break; } case ElementType.String: @@ -72,13 +72,13 @@ public sealed override bool DecodeItem(ref SequenceReader source, out Deco if (source.Remaining < valueOrLength + consumed) { result = - DecodeItemResult < MessagePackExtensionType >.InsufficientInput( + DecodeItemResult.InsufficientInput( (int)((valueOrLength + consumed - source.Remaining) & Int32.MaxValue) ); return false; } - result = DecodeItemResult.ScalarOrSequence(elementType, source.Sequence.Slice(source.Position).Slice(consumed, valueOrLength)); + result = DecodeItemResult.ScalarOrSequence(elementType, source.Sequence.Slice(source.Position).Slice(consumed, valueOrLength)); consumed += valueOrLength; break; } @@ -89,17 +89,17 @@ public sealed override bool DecodeItem(ref SequenceReader source, out Deco if(source.Remaining < valueOrLength + consumed) { result = - DecodeItemResult.InsufficientInput( + DecodeItemResult.InsufficientInput( (int)((valueOrLength + consumed - source.Remaining) & Int32.MaxValue) ); return false; } var extensionSlice = source.Sequence.Slice(source.Position).Slice(consumed - 1); - var typeCode = new MessagePackExtensionType(extensionSlice.FirstSpan[0]); + var typeCode = extensionSlice.FirstSpan[0]; var body = extensionSlice.Slice(1, valueOrLength); - result = DecodeItemResult.ExtensionTypeObject(typeCode, body); + result = DecodeItemResult.ExtensionType(new ExtensionTypeObject(new ExtensionType(typeCode), body)); consumed += valueOrLength; break; } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs index 603775c8c..902e51454 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.Extension.cs @@ -104,36 +104,33 @@ private uint ReadExtensionHeader(ref SequenceReader source, out int consum return length; } - public override void DecodeExtension(ref SequenceReader source, out MessagePackExtensionType typeCode, out ReadOnlySequence body, out int requestHint, CancellationToken cancellationToken = default) + public override void DecodeExtension(ref SequenceReader source, out ExtensionTypeObject result, out int requestHint, CancellationToken cancellationToken = default) { var bodyLength = this.ReadExtensionHeader(ref source, out var consumed, out requestHint); if (requestHint != 0) { - typeCode = default; - body = default; + result = default; return; } - if (!source.TryPeek(out byte typeCodeByte)) + if (!source.TryPeek(out byte typeCode)) { requestHint = 1; - typeCode = default; - body = default; + result = default; return; } consumed++; - typeCode = new MessagePackExtensionType(typeCodeByte); if (source.Remaining < consumed + bodyLength) { requestHint = (int)((consumed + bodyLength - (int)source.Remaining) & Int32.MaxValue); - body = default; + result = default; return; } requestHint = 0; - body = source.Sequence.Slice(consumed, bodyLength); + result = new ExtensionTypeObject(new ExtensionType(typeCode), source.Sequence.Slice(consumed, bodyLength)); source.Advance(consumed + bodyLength); } } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.cs index f08ec168e..4bd24d4ca 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.cs @@ -9,7 +9,7 @@ namespace MsgPack.Internal { - public sealed partial class MessagePackDecoder : Decoder + public sealed partial class MessagePackDecoder : FormatDecoder { private static readonly FormatFeatures MessagePackFormatFeatures = new FormatFeaturesBuilder diff --git a/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs b/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs index 9472d0358..c608c8cc4 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoderOptions.cs @@ -4,7 +4,7 @@ namespace MsgPack.Internal { - public sealed class MessagePackDecoderOptions : DecoderOptions + public sealed class MessagePackDecoderOptions : FormatDecoderOptions { public static MessagePackDecoderOptions Default { get; } = new MessagePackDecoderOptionsBuilder().Build(); diff --git a/src/MsgPack.Core/Internal/MessagePackDecoderOptionsBuilder.cs b/src/MsgPack.Core/Internal/MessagePackDecoderOptionsBuilder.cs index 291e429b8..aab974591 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoderOptionsBuilder.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoderOptionsBuilder.cs @@ -4,7 +4,7 @@ namespace MsgPack.Internal { - public sealed class MessagePackDecoderOptionsBuilder : DecoderOptionsBuilder + public sealed class MessagePackDecoderOptionsBuilder : FormatDecoderOptionsBuilder { public MessagePackDecoderOptionsBuilder() { } diff --git a/src/MsgPack.Core/Internal/MessagePackEncoder.cs b/src/MsgPack.Core/Internal/MessagePackEncoder.cs index 153da2005..b3647aac9 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoder.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoder.cs @@ -14,9 +14,9 @@ namespace MsgPack.Internal /// /// Common encoder implementation between legacy and current MessagePack format enconders. /// - public abstract partial class MessagePackEncoder : Encoder + public abstract partial class MessagePackEncoder : FormatEncoder { -#warning TODO: Can devirt? +#warning TODO: Can devirt? If not, change to Create(MessagePackEncoderOptions) public static MessagePackEncoder CreateLegacy(MessagePackEncoderOptions options) => new LegacyMessagePackEncoder(options); public static MessagePackEncoder CreateCurrent(MessagePackEncoderOptions options) => new CurrentMessagePackEncoder(options); diff --git a/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs b/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs index dfc160c3b..a36d7e9ed 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoderOptions.cs @@ -4,7 +4,7 @@ namespace MsgPack.Internal { - public sealed class MessagePackEncoderOptions : EncoderOptions + public sealed class MessagePackEncoderOptions : FormatEncoderOptions { public static MessagePackEncoderOptions Default { get; } = new MessagePackEncoderOptionsBuilder().Build(); diff --git a/src/MsgPack.Core/Internal/MessagePackEncoderOptionsBuilder.cs b/src/MsgPack.Core/Internal/MessagePackEncoderOptionsBuilder.cs index 0f343c3c2..7ceeba6a1 100644 --- a/src/MsgPack.Core/Internal/MessagePackEncoderOptionsBuilder.cs +++ b/src/MsgPack.Core/Internal/MessagePackEncoderOptionsBuilder.cs @@ -4,7 +4,7 @@ namespace MsgPack.Internal { - public sealed class MessagePackEncoderOptionsBuilder : EncoderOptionsBuilder + public sealed class MessagePackEncoderOptionsBuilder : FormatEncoderOptionsBuilder { public MessagePackEncoderOptionsBuilder() { } diff --git a/src/MsgPack.Core/Internal/MessagePackExtensionType.cs b/src/MsgPack.Core/Internal/MessagePackExtensionType.cs deleted file mode 100644 index 0287bf17b..000000000 --- a/src/MsgPack.Core/Internal/MessagePackExtensionType.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. -// This file is licensed under Apache2 license. -// See the LICENSE in the project root for more information. - -using System; - -namespace MsgPack.Internal -{ - /// - /// Represents type code of extention types. - /// - public readonly struct MessagePackExtensionType : IEquatable - { - public byte TypeCode { get; } - public bool IsSystemType => this.TypeCode < 0; - - internal MessagePackExtensionType(byte typeCode) - { - this.TypeCode = typeCode; - } - - public static MessagePackExtensionType Define(byte typeCode) - => new MessagePackExtensionType(Ensure.IsNotGreaterThan(typeCode, (byte)SByte.MaxValue)); - - /// - public bool Equals(MessagePackExtensionType other) - => this.TypeCode == other.TypeCode; - - /// - public override bool Equals(object? obj) - => (obj is MessagePackExtensionType other) ? this.Equals(other) : false; - - /// - public override int GetHashCode() - => this.TypeCode.GetHashCode(); - - public static bool operator ==(MessagePackExtensionType left, MessagePackExtensionType right) - => left.Equals(right); - - public static bool operator !=(MessagePackExtensionType left, MessagePackExtensionType right) - => !left.Equals(right); - } -} diff --git a/src/MsgPack.Core/Internal/MessagePackThrow.cs b/src/MsgPack.Core/Internal/MessagePackThrow.cs index c9c18f9b4..7e1665bd5 100644 --- a/src/MsgPack.Core/Internal/MessagePackThrow.cs +++ b/src/MsgPack.Core/Internal/MessagePackThrow.cs @@ -41,5 +41,8 @@ public static void IsNotUtf8String(byte header, long position) public static void IsNotExtension(byte header, long position) => throw new MessageTypeException($"The type is {MessagePackCode.ToString(header)}(0x{header:X2}) but it is not extension at {position:#,0}."); + + public static void InvalidTypeCode(ulong typeCode) + => throw new ArgumentOutOfRangeException("typeCode", $"Extension type code for MessagePack must be 1 byte, but 0x{typeCode:X} is specified."); } } diff --git a/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs b/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs index 9fe873247..a72edb68d 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.DecodeItem.cs @@ -9,8 +9,6 @@ using System.Threading; using MsgPack.Internal; -using DecodeItemResult = MsgPack.Internal.DecodeItemResult; - namespace MsgPack.Json { public partial class JsonDecoder diff --git a/src/MsgPack.Json/Json/JsonDecoder.cs b/src/MsgPack.Json/Json/JsonDecoder.cs index aa17d1b2d..537d503fe 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.cs @@ -12,7 +12,7 @@ namespace MsgPack.Json /// /// A decoder for JSON format. /// - public abstract partial class JsonDecoder : Decoder + public abstract partial class JsonDecoder : FormatDecoder { private static readonly FormatFeatures JsonFormatFeatures = new FormatFeaturesBuilder diff --git a/src/MsgPack.Json/Json/JsonDecoderOptions.cs b/src/MsgPack.Json/Json/JsonDecoderOptions.cs index 6ff0dc68e..43f77b5e7 100644 --- a/src/MsgPack.Json/Json/JsonDecoderOptions.cs +++ b/src/MsgPack.Json/Json/JsonDecoderOptions.cs @@ -9,7 +9,7 @@ namespace MsgPack.Json /// /// Defines decoder options for . /// - public class JsonDecoderOptions : DecoderOptions + public class JsonDecoderOptions : FormatDecoderOptions { public static JsonDecoderOptions Default { get; } = new JsonDecoderOptionsBuilder().Build(); diff --git a/src/MsgPack.Json/Json/JsonDecoderOptionsBuilder.cs b/src/MsgPack.Json/Json/JsonDecoderOptionsBuilder.cs index 2ac9b8d4a..7bb7bffcb 100644 --- a/src/MsgPack.Json/Json/JsonDecoderOptionsBuilder.cs +++ b/src/MsgPack.Json/Json/JsonDecoderOptionsBuilder.cs @@ -9,7 +9,7 @@ namespace MsgPack.Json /// /// A builder object for immutable . /// - public class JsonDecoderOptionsBuilder : DecoderOptionsBuilder + public class JsonDecoderOptionsBuilder : FormatDecoderOptionsBuilder { public JsonParseOptions ParseOptions { get; set; } diff --git a/src/MsgPack.Json/Json/JsonEncoder.cs b/src/MsgPack.Json/Json/JsonEncoder.cs index d70a1bfa3..b4109419d 100644 --- a/src/MsgPack.Json/Json/JsonEncoder.cs +++ b/src/MsgPack.Json/Json/JsonEncoder.cs @@ -17,7 +17,7 @@ namespace MsgPack.Json /// /// An encoder for JSON format. /// - public sealed class JsonEncoder : Encoder + public sealed class JsonEncoder : FormatEncoder { private readonly Action, JsonEncoderOptions> _singleInfinityFormatter; private readonly Action, JsonEncoderOptions> _singleNanFormatter; diff --git a/src/MsgPack.Json/Json/JsonEncoderOptions.cs b/src/MsgPack.Json/Json/JsonEncoderOptions.cs index 0fa00f966..cc12fa8f3 100644 --- a/src/MsgPack.Json/Json/JsonEncoderOptions.cs +++ b/src/MsgPack.Json/Json/JsonEncoderOptions.cs @@ -13,7 +13,7 @@ namespace MsgPack.Json /// /// Defines encoder options for . /// - public sealed class JsonEncoderOptions : EncoderOptions + public sealed class JsonEncoderOptions : FormatEncoderOptions { public static JsonEncoderOptions Default { get; } = new JsonEncoderOptionsBuilder().Build(); diff --git a/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs b/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs index 812dc7680..04634193c 100644 --- a/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs +++ b/src/MsgPack.Json/Json/JsonEncoderOptionsBuilder.cs @@ -10,9 +10,9 @@ namespace MsgPack.Json { /// - /// A builder object to construct immutable object. + /// A builder object to construct immutable object. /// - public class JsonEncoderOptionsBuilder : EncoderOptionsBuilder + public class JsonEncoderOptionsBuilder : FormatEncoderOptionsBuilder { private static readonly ReadOnlyMemory DefaultIndentChars = new byte[] { (byte)' ', (byte)' ' }; diff --git a/src/MsgPack.Serialization.ILGeneration/MsgPack.Serialization.ILGeneration.csproj b/src/MsgPack.Serialization.ILGeneration/MsgPack.Serialization.ILGeneration.csproj new file mode 100644 index 000000000..a20cb1dcc --- /dev/null +++ b/src/MsgPack.Serialization.ILGeneration/MsgPack.Serialization.ILGeneration.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj b/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj index cb6319069..1f7897a73 100644 --- a/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj +++ b/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj @@ -4,4 +4,9 @@ netcoreapp3.1 + + + + + diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`2.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`2.cs new file mode 100644 index 000000000..aded3dd65 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`2.cs @@ -0,0 +1,126 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MsgPack.Internal; +using MsgPack.Serialization; +using MsgPack.Serialization.Internal; + +namespace Msgpack.Serialization.ReflectionSerializers +{ + internal abstract class SerializerBuilder + { + public abstract IObjectSerializer Build(SerializationTarget target); + } + + internal sealed class ReflectionSerializerBuilder : SerializerBuilder + { + public sealed override IObjectSerializer Build(SerializationTarget target, SerializerGenerationOptions options) + => new ReflectionSerializer(target, options); + } + + /// + /// Implements non optimized, reflection based serializer. + /// + /// Type of serialization target. + /// Type of extention type specific to codec. + internal sealed class ReflectionSerializer : IObjectSerializer + { + private readonly SerializerGenerationOptions _options; +#warning TODO: Use _options + private bool UsesArray => true; + // T is PrimitiveEncoder | Stringncoder | BinaryEncoder | ExtensionEncoder | ArrayEncoderToken | DictionaryEncoderToken + private readonly IReadOnlyList _memberValueEncoders; + private readonly IReadOnlyList _memberUtf8Names; + private readonly IReadOnlyList _memberValueDecoders; + + private delegate void PrimitiveEncoder(Encoder encoder, object value, IBufferWriter writer); + private delegate void Stringncoder(Encoder encoder, object value, IBufferWriter writer, Encoding? encoding, CancellationToken cancellationToken); + private delegate void BinaryEncoder(Encoder encoder, object ovaluebj, IBufferWriter writer, CancellationToken cancellationToken); + private delegate void ExtensionEncoder(Encoder encoder, TExtensionType extensionType, in ReadOnlySequence body, IBufferWriter writer, CancellationToken cancellationToken); + + public void Serialize(ref SerializationOperationContext context, [AllowNull] T obj, IBufferWriter sink) + { + var encoder = context.Encoder; + if (obj is null) + { + encoder.EncodeNull(sink); + return; + } + + if (this.UsesArray) + { + encoder.EncodeArrayStart(this._memberValueEncoders.Count, sink, context.CollectionContext); + + for(var i = 0; i < this._memberValueEncoders.Count;i++) + { + encoder.EncodeArrayItemStart(i, sink, context.CollectionContext); + if (this._memberValueEncoders[i] is Action> primitiveEncoder) + { + encoder.EncodeBinary() + } + else if (this._memberValueEncoders[i] is Action, Encoding?, CancellationToken> stringEncoder) + { + + } + else if (this._memberValueEncoders[i] is Action, CancellationToken> binaryEncoder) + { + + } + else if (this._memberValueEncoders[i] is Action, IBufferWriter> extensionEncoder) + { + encoder.EncodeExtension() + } + else + { + Debug.Fail(); + } + encoder.EncodeArrayItemEnd(i, sink, context.CollectionContext); + } + + encoder.EncodeArrayEnd(this._memberValueEncoders.Count, sink, context.CollectionContext); + + } + + + + throw new NotImplementedException(); + } + + public async ValueTask SerializeAsync(AsyncSerializationOperationContext context, [AllowNull] T obj, Stream streamSink) + { + await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) + { + var serializationOperationContext = context.AsSerializationOperationContext(); + this.Serialize(ref serializationOperationContext, obj, writer); + } + } + + [return: MaybeNull] + public T Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + { + throw new NotImplementedException(); + } + + public ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource) + { + throw new NotImplementedException(); + } + + public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in T obj) + { + throw new NotImplementedException(); + } + + public ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, T obj) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/MsgPack.Serialization/MsgPack.Serialization.csproj b/src/MsgPack.Serialization/MsgPack.Serialization.csproj index d7a9b6293..870dcf339 100644 --- a/src/MsgPack.Serialization/MsgPack.Serialization.csproj +++ b/src/MsgPack.Serialization/MsgPack.Serialization.csproj @@ -4,6 +4,49 @@ netcoreapp3.1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -12,4 +55,24 @@ + + + + + + + + True + True + Throw.tt + + + + + + TextTemplatingFileGenerator + Throw.cs + + + diff --git a/src/MsgPack.Serialization/Properties/AssemblyInfo.cs b/src/MsgPack.Serialization/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..ddec96c21 --- /dev/null +++ b/src/MsgPack.Serialization/Properties/AssemblyInfo.cs @@ -0,0 +1,13 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Runtime.CompilerServices; + +#if DEBUG +[assembly: InternalsVisibleTo("MsgPack.Serialization.UnitTest")] +[assembly: InternalsVisibleTo("MsgPack.Serialization.ILGeneration.UnitTest")] +[assembly: InternalsVisibleTo("MsgPack.Serialization.Reflection.UnitTest")] +#endif // DEBUG +[assembly: InternalsVisibleTo("MsgPack.Serialization.ILGeneration")] +[assembly: InternalsVisibleTo("MsgPack.Serialization.Reflection")] diff --git a/src/MsgPack.Serialization/Serialization/MessagePackNullableAttribute.cs b/src/MsgPack.Serialization/Serialization/MessagePackNullableAttribute.cs new file mode 100644 index 000000000..246c1f0dd --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/MessagePackNullableAttribute.cs @@ -0,0 +1,19 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +#if FEATURE_MPCONTRACT +using Contract = MsgPack.MPContract; +#else +#endif // FEATURE_MPCONTRACT + +namespace MsgPack.Serialization +{ +#warning TODO: Qualify MessagsePackObject with this attribute. + /// + /// Indicates that the default value of this value type means null even if the declared member type is not . + /// + [AttributeUsage(AttributeTargets.Enum | AttributeTargets.Struct)] + public sealed class MessagePackNullableAttribute : Attribute { } +} diff --git a/src/MsgPack.Serialization/Serialization/Reflection/EncoderMethods.cs b/src/MsgPack.Serialization/Serialization/Reflection/EncoderMethods.cs new file mode 100644 index 000000000..a6c01206a --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/Reflection/EncoderMethods.cs @@ -0,0 +1,76 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Reflection; +using System.Text; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Serialization.Reflection +{ + internal static class EncoderMethods + { + public static readonly MethodInfo EncodeInt32NonNull = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeInt32), new[] { typeof(int), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeInt32Nullable = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeInt32), new[] { typeof(int?), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeInt64NonNull = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeInt64), new[] { typeof(long), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeInt64Nullable = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeInt64), new[] { typeof(long?), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeUInt32NonNull = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeUInt32), new[] { typeof(uint), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeUInt32Nullable = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeUInt32), new[] { typeof(uint?), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeUInt64NonNull = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeUInt64), new[] { typeof(ulong), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeUInt64Nullable = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeUInt64), new[] { typeof(ulong?), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeBooleanNonNull = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeBoolean), new[] { typeof(bool), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeBooleanNullable = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeBoolean), new[] { typeof(bool?), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeSingleNonNull = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeSingle), new[] { typeof(float), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeSingleNullable = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeSingle), new[] { typeof(float?), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeDoubleNonNull = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeDouble), new[] { typeof(double), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeDoubleNullable = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeDouble), new[] { typeof(double?), typeof(IBufferWriter) })!; + + public static readonly MethodInfo EncodeStringString = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeString), new[] { typeof(string), typeof(IBufferWriter), typeof(Encoding), typeof(CancellationToken) })!; + + public static readonly MethodInfo EncodeStringStringBuilder = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeString), new[] { typeof(StringBuilder), typeof(IBufferWriter), typeof(Encoding), typeof(CancellationToken) })!; + + public static readonly MethodInfo EncodeStringSpan = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeString), new[] { typeof(ReadOnlySpan), typeof(IBufferWriter), typeof(Encoding), typeof(CancellationToken) })!; + + public static readonly MethodInfo EncodeStringSequence = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeString), new[] { typeof(ReadOnlySequence), typeof(IBufferWriter), typeof(Encoding), typeof(CancellationToken) })!; + + public static readonly MethodInfo EncodeBinarySpan = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeBinary), new[] { typeof(ReadOnlySpan), typeof(IBufferWriter), typeof(CancellationToken) })!; + + public static readonly MethodInfo EncodeBinarySequence = + typeof(FormatEncoder).GetMethod(nameof(FormatEncoder.EncodeBinary), new[] { typeof(ReadOnlySequence), typeof(IBufferWriter), typeof(CancellationToken) })!; + } +} diff --git a/src/MsgPack.Serialization/Serialization/Reflection/SpanMethods.cs b/src/MsgPack.Serialization/Serialization/Reflection/SpanMethods.cs new file mode 100644 index 000000000..e52f683cf --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/Reflection/SpanMethods.cs @@ -0,0 +1,37 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Linq; +using System.Reflection; + +namespace MsgPack.Serialization.Reflection +{ + internal static class SpanMethods + { + private static MethodInfo GetAsReadOnlySpan() + { + var parameterTypes = ArrayPool.Shared.Rent(1); + try + { + return + typeof(Span<>).MakeGenericType(typeof(T)).GetMethods(BindingFlags.Static | BindingFlags.Public) + .Single(m => + m.ReturnType == typeof(ReadOnlySpan) + && m.Name == "op_Implicit" + && m.GetParameterTypes().SequenceEqual(parameterTypes) + ); + } + finally + { + ArrayPool.Shared.Return(parameterTypes); + } + } + + public static readonly MethodInfo AsReadOnlySpanByte = GetAsReadOnlySpan(); + + public static readonly MethodInfo AsReadOnlySpanChar = GetAsReadOnlySpan(); + } +} diff --git a/src/MsgPack.Serialization/Serialization/SerializerBuilder.cs b/src/MsgPack.Serialization/Serialization/SerializerBuilder.cs new file mode 100644 index 000000000..03092fc25 --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/SerializerBuilder.cs @@ -0,0 +1,11 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Serialization.ReflectionSerializers +{ + internal abstract class SerializerBuilder + { + public abstract ObjectSerializer Build(ObjectSerializationContext context, SerializationTarget target); + } +} diff --git a/src/MsgPack.Serialization/Serialization/SerializerGenerationOptions.cs b/src/MsgPack.Serialization/Serialization/SerializerGenerationOptions.cs new file mode 100644 index 000000000..820c44f71 --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/SerializerGenerationOptions.cs @@ -0,0 +1,60 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace MsgPack.Serialization +{ + public readonly struct MessagePackMemberAttributeData + { + public int? Id { get; } + public string? Name { get; } + + public MessagePackMemberAttributeData(int? id, string? name) + { + this.Id = id == null ? id : Ensure.IsNotLessThan(id.GetValueOrDefault(), 0, nameof(id)); + this.Name = name; + } + } + + internal sealed class SerializerGenerationOptions + { + public bool DisablesPrivilegedAccess { get; } + public bool OneBoundDataMemberOrder { get; } + public bool AllowsAsymmetricSerializer { get; } + public ISet IgnoreAttributeTypeNames { get; } + public Func SerializableAnywayInterfaceDetector { get; } + public Func DeserializableInterfaceDetector { get; } + public Func, IEnumerable, MessagePackMemberAttributeData?>? MessagePackMemberAttributeCompatibilityProvider { get; } + public IReadOnlyDictionary> IgnoringMembers { get; } + } + + public sealed class SerializerGenerationOptionsBuilder + { + private static readonly string[] DefaultIgnoreAttributeTypeNames = + new[] + { + "MsgPack.Serialization.MessagePackIgnoreAttribute", + "System.Runtime.Serialization.IgnoreDataMemberAttribute" + }; + +#warning TODO: CompatiblityPackage: typeof(IPackable).IsAssignableFrom(t) || typeof(IUnpackable).IsAssignableFrom(t) || (!o.WithAsync || typeof(IAsyncPackable).IsAssignableFrom(t) || typeof(IAsyncUnpackable).IsAssignableFrom(t)) + private static readonly Func[] DefaultSerializableAnywayInterfaceDetectors = Array.Empty>(); + +#warning TODO: CompatiblityPackage: typeof(IUnpackable).IsAssignableFrom(t) && (!o.WithAsync || typeof(IAsyncUnpackable).IsAssignable(t)) + private static readonly Func[] DefaultDeserializableInterfaceDetectors = Array.Empty>(); + + internal IList> SerializableAnywayInterfaceDetectors { get; } = new List>(DefaultSerializableAnywayInterfaceDetectors); + internal IList> DeserializableInterfaceDetectors { get; } = new List>(DefaultDeserializableInterfaceDetectors); + + public bool DisablesPrivilegedAccess { get; set; } + public bool OneBoundDataMemberOrder { get; set; } + public bool AllowsAsymmetricSerializer { get; set; } + public ISet IgnoreAttributeTypeNames { get; } = new HashSet(DefaultIgnoreAttributeTypeNames, StringComparer.Ordinal); + public Func, IEnumerable, MessagePackMemberAttributeData?>? MessagePackMemberAttributeCompatibilityProvider { get; set; } + } +} diff --git a/src/MsgPack.Serialization/Serialization/Throw.Internal.cs b/src/MsgPack.Serialization/Serialization/Throw.Internal.cs new file mode 100644 index 000000000..c622df9dd --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/Throw.Internal.cs @@ -0,0 +1,125 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; + +namespace MsgPack.Serialization +{ + internal partial class Throw + { + public static void NotSupportedBecauseCannotInstanciateAbstractType(Type type) + => throw new NotSupportedException($"This operation is not supported because '{type}' cannot be instanciated."); + + public static void DuplicatedAttributes(Dictionary> duplicated) + => throw new InvalidOperationException( + $"Some member keys specified with custom attributes are duplicated. " + + $"Details: {{{String.Join(", ", duplicated.Select(kv => $"\"{kv.Key}\":[{String.Join(", ", kv.Value.Select(m => $"{m.DeclaringType}.{m.Name}({m.MemberType})"))}]"))}}}" + ); + + public static void MemberIdIsDuplicated(int id, Type targetType) + => throw new SerializationException($"The member ID '{id}' is duplicated in the '{targetType}' elementType."); + + public static void DataMemberAttributeCannotBeZeroWhenOneBoundDataMemberOrderIsTrue(Type declaringType, string? memberName) + => throw new NotSupportedException( + $"Cannot specify order value 0 on DataMemberAttribute for '{declaringType}.{memberName}' " + + $"when SerializationContext.CompatibilityOptions.OneBoundDataMemberOrder is set to true." + ); + + public static void TypeCannotBeSerializedBecauseNoMembersAndNoParameterizedPublicConstructors(Type targetType) + => new SerializationException( + $"Cannot serialize type '{targetType}' because it does not have any serializable fields nor properties, " + + $"and it does not have any public constructors with parameters." + ); + + public static void TypeCannotBeSerializedBecauseNoMembersAndAmbigiousConstructors(Type targetType, IEnumerable mostRichConstructors) + => throw new SerializationException( + $"Cannot serialize type '{targetType}' because it does not have any serializable fields nor properties, " + + $"and serializer generator failed to determine constructor to deserialize among declared constructors" + + $"({String.Join(", ", mostRichConstructors.Select(ctor => ctor.ToString()).ToArray())})." + ); + + public static void TypeCannotBeSerializedBecauseThereAreMultipleMessagePackDeserializationConstrutorAttribute(Type targetType) + => throw new SerializationException( + $"There are multiple constructors marked with MessagePackDeserializationConstrutorAttribute in type '{targetType}'." + ); + + public static void FailedToGetProperty(string propertyName, string? assemblyQualifiedName) + => throw new SerializationException($"Failed to get {propertyName} property from {assemblyQualifiedName} type."); + + public static void MemberIsMarkedWithMemberAndIgnoreAttribute(Type type, string memberName) + => throw new SerializationException($"A member '{memberName}' of type '{type}' is marked with both MessagePackMemberAttribute and MessagePackIgnoreAttribute."); + + public static void TargetDoesNotHavePublicDefaultConstructor(Type targetType) + => throw new SerializationException($"Type '{targetType}' does not have default (parameterless) public constructor."); + + public static void IsNotSerializableAnyway(Type targetType) + => throw new SerializationException($"Cannot serialize type '{targetType}' because it does not have any serializable fields nor properties."); + + public static void UnpackToIsNotSupported(Type type, Exception inner) + => throw new NotSupportedException($"This operation is not supported for '{type}' because it does not have accessible Add(T) method.", inner); + + public static void UnexpectedBinaryType(object? value) + => throw new NotSupportedException($"Type '{value?.GetType()}' is unexpected for binary value."); + + public static void UnexpectedStringType(object? value) + => throw new NotSupportedException($"Type '{value?.GetType()}' is unexpected for string value."); + + public static void CannotSerializeNonPublicTypeUnlessPrivledgedAccessEnabled(Type targetType) + => throw new SerializationException($"Cannot serialize type '{targetType}' because it is not public to the serializer."); + + public static void NoConstructorForCollection(Type targetType) + => throw new SerializationException($"Cannot deserialize collection type '{targetType}' because it does not have both of public default constructor and public consctructor with Int32 capacity parameter."); + + public static object? UndeserializableCollection(Type targetType) + => throw new NotSupportedException($"A generated serialzier for '{targetType}' does not supprot deserialization because the type does not expose Add method."); + } + +#warning EXTRACT + internal static class DiagnosticsSourceExtensions + { + private const string Prefix = "MsgPack.Serialization."; + private const string PolymorphicPrefix = Prefix + "Polymorphic."; + + public static void DeserializationConstructorFound(this DiagnosticSource source, object value) + => source.Write(Prefix + "DeserializationConstructorFound", value); + + public static void DetectedAsDeserializable(this DiagnosticSource source, object value) + => source.Write(Prefix + "DetectedAsDeserializable", value); + + public static void DefaultPolymorphicSchemaForValueType(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "DefaultSchemaForValueType", value); + + public static void PolymorphicSchemaCreatedForRootType(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "SchemaCreatedForRootType", value); + + public static void DefaultPolymorphicSchemaForUnqualifiedCollectionMember(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "DefaultSchemaForUnqualifiedCollectionMember", value); + + public static void PolymorphicSchemaCreatedForCollectionMember(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "SchemaCreatedForCollectionMember", value); + + public static void DefaultPolymorphicSchemaForUnqualifiedDictionaryMember(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "DefaultSchemaForUnqualifiedDictionaryMember", value); + + public static void PolymorphicSchemaCreatedForDictionaryMember(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "SchemaCreatedForDictionaryMember", value); + + public static void DefaultPolymorphicSchemaForUnqualifiedTupleMember(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "DefaultSchemaForUnqualifiedTupleMember", value); + + public static void PolymorphicSchemaCreatedForTupleMember(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "SchemaCreatedForTupleMember", value); + + public static void DefaultPolymorphicSchemaForUnqualifiedObjectMember(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "DefaultSchemaForUnqualifiedObjectMember", value); + + public static void PolymorphicSchemaCreatedForObjectMember(this DiagnosticSource source, object value) + => source.Write(PolymorphicPrefix + "SchemaCreatedForObjectMember", value); + } +} diff --git a/src/MsgPack.Serialization/Serialization/Throw.cs b/src/MsgPack.Serialization/Serialization/Throw.cs new file mode 100644 index 000000000..2afefd965 --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/Throw.cs @@ -0,0 +1,34 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Reflection; +using System.Runtime.Serialization; + +namespace MsgPack.Serialization +{ + internal static partial class Throw + { + public static void ValueTypeCannotBeNull(string? name, Type memberType, Type declaringType) + => throw new SerializationException($"Member '{name}' of type '{declaringType}' cannot be null because the member type is non-nullable value type('{memberType}')."); + + public static void NullIsProhibitedForReadOnlyMember(string? memberName) + => throw new SerializationException($"The member '{memberName}' cannot be set null because it is read only."); + + public static void UnsettableCollectionMemberMustNotBeNull(MemberInfo member) + => throw new SerializationException($"Unsettable member '{member}' in '{member?.DeclaringType}' returns null collection."); + + public static void UnsettableCollectionMemberTypeMustNotBeReadOnly(MemberInfo member) + => throw new SerializationException($"Type of unsettable member '{member}' in '{member?.DeclaringType}' is read only collection type ('{member?.GetMemberValueType()}')."); + + public static void DeserializeToOnlyAvailableForMutableCollection(Type targetType) + => throw new NotSupportedException($"DeserializeTo is not available because '{targetType}' is not a mutable collection."); + } +} diff --git a/src/MsgPack.Serialization/Serialization/Throw.tt b/src/MsgPack.Serialization/Serialization/Throw.tt new file mode 100644 index 000000000..bdad36598 --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/Throw.tt @@ -0,0 +1,10 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ include file="./Throw.ttinclude" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ output extension=".cs" #> +<# + WriteSerializationExceptions(); +#> diff --git a/src/MsgPack.Serialization/Serialization/Throw.ttinclude b/src/MsgPack.Serialization/Serialization/Throw.ttinclude new file mode 100644 index 000000000..42bb6b028 --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/Throw.ttinclude @@ -0,0 +1,41 @@ +<#+ +void WriteSerializationExceptions() +{ +#> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Reflection; +using System.Runtime.Serialization; + +namespace MsgPack.Serialization +{ + internal static partial class Throw + { + public static void ValueTypeCannotBeNull(string? name, Type memberType, Type declaringType) + => throw new SerializationException($"Member '{name}' of type '{declaringType}' cannot be null because the member type is non-nullable value type('{memberType}')."); + + public static void NullIsProhibitedForReadOnlyMember(string? memberName) + => throw new SerializationException($"The member '{memberName}' cannot be set null because it is read only."); + + public static void UnsettableCollectionMemberMustNotBeNull(MemberInfo member) + => throw new SerializationException($"Unsettable member '{member}' in '{member?.DeclaringType}' returns null collection."); + + public static void UnsettableCollectionMemberTypeMustNotBeReadOnly(MemberInfo member) + => throw new SerializationException($"Type of unsettable member '{member}' in '{member?.DeclaringType}' is read only collection type ('{member?.GetMemberValueType()}')."); + + public static void DeserializeToOnlyAvailableForMutableCollection(Type targetType) + => throw new NotSupportedException($"DeserializeTo is not available because '{targetType}' is not a mutable collection."); + } +} +<#+ +} +#> diff --git a/src/MsgPack.Serialization/Utf8String.cs b/src/MsgPack.Serialization/Utf8String.cs new file mode 100644 index 000000000..79ed68136 --- /dev/null +++ b/src/MsgPack.Serialization/Utf8String.cs @@ -0,0 +1,40 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace MsgPack +{ +#if !FEATURE_UTF8_STRING + + internal sealed class Utf8String + { + private readonly byte[] _bytes; + + public Utf8String(ReadOnlySpan value) + { + var bytes = new byte[Encoding.UTF8.GetByteCount(value)]; + + var used = Encoding.UTF8.GetBytes(value, bytes); + this._bytes = bytes; + } + + public Utf8Span AsSpan() => new Utf8Span(this._bytes); + } + + [StructLayout(LayoutKind.Auto)] + internal readonly ref struct Utf8Span + { + public ReadOnlySpan Bytes { get; } + + internal Utf8Span(byte[] bytes) + { + this.Bytes = bytes; + } + } + +#endif +} diff --git a/src/MsgPack/Serialization/BindingOptions.cs b/src/MsgPack/Serialization/BindingOptions.cs index 29090576d..f0b2fd696 100644 --- a/src/MsgPack/Serialization/BindingOptions.cs +++ b/src/MsgPack/Serialization/BindingOptions.cs @@ -1,25 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2016 FUJIWARA, Yusuke and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Contributors: -// Shrenik Jhaveri (ShrenikOne) -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; using System.Collections.Generic; @@ -42,17 +23,17 @@ public class BindingOptions /// /// Type of the target. /// The member skip list. - public void SetIgnoringMembers( Type targetType, IEnumerable memberSkipList ) + public void SetIgnoringMembers(Type targetType, IEnumerable memberSkipList) { - lock ( this._typeIgnoringMembersMap ) + lock (this._typeIgnoringMembersMap) { - if ( this._typeIgnoringMembersMap.ContainsKey( targetType ) ) + if (this._typeIgnoringMembersMap.ContainsKey(targetType)) { - this._typeIgnoringMembersMap[ targetType ] = memberSkipList; + this._typeIgnoringMembersMap[targetType] = memberSkipList; } else { - this._typeIgnoringMembersMap.Add( targetType, memberSkipList ); + this._typeIgnoringMembersMap.Add(targetType, memberSkipList); } } } @@ -62,13 +43,13 @@ public void SetIgnoringMembers( Type targetType, IEnumerable memberSkipL /// /// Type of the target. /// Returns member skip list for a specific target type. - public IEnumerable GetIgnoringMembers( Type targetType ) + public IEnumerable GetIgnoringMembers(Type targetType) { - lock ( this._typeIgnoringMembersMap ) + lock (this._typeIgnoringMembersMap) { - if ( this._typeIgnoringMembersMap.ContainsKey( targetType ) ) + if (this._typeIgnoringMembersMap.ContainsKey(targetType)) { - return this._typeIgnoringMembersMap[ targetType ]; + return this._typeIgnoringMembersMap[targetType]; } else { @@ -83,9 +64,9 @@ public IEnumerable GetIgnoringMembers( Type targetType ) /// Returns all registered types specific ignoring members. public IDictionary> GetAllIgnoringMembers() { - lock ( this._typeIgnoringMembersMap ) + lock (this._typeIgnoringMembersMap) { - return this._typeIgnoringMembersMap.ToDictionary( item => item.Key, item => ( IEnumerable )item.Value.ToArray() ); + return this._typeIgnoringMembersMap.ToDictionary(item => item.Key, item => (IEnumerable)item.Value.ToArray()); } } } diff --git a/src/MsgPack/Serialization/CollectionDetailedKind.cs b/src/MsgPack/Serialization/CollectionDetailedKind.cs index d35962f82..a3cd59df0 100644 --- a/src/MsgPack/Serialization/CollectionDetailedKind.cs +++ b/src/MsgPack/Serialization/CollectionDetailedKind.cs @@ -1,37 +1,10 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2014-2018 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif - -using System; +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. namespace MsgPack.Serialization { -#if UNITY && DEBUG - public -#else - internal -#endif - enum CollectionDetailedKind + internal enum CollectionDetailedKind { NotCollection = 0, Array, @@ -39,18 +12,18 @@ enum CollectionDetailedKind NonGenericList, GenericDictionary, NonGenericDictionary, -#if !NET35 && !UNITY +#if !NET35 GenericSet, -#endif // !NET35 && !UNITY +#endif // !NET35 GenericCollection, NonGenericCollection, GenericEnumerable, NonGenericEnumerable, -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) +#if !NET35 && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) GenericReadOnlyList, GenericReadOnlyCollection, GenericReadOnlyDictionary, -#endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) +#endif // !NET35 && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) Unserializable } } diff --git a/src/MsgPack/Serialization/CollectionKind.cs b/src/MsgPack/Serialization/CollectionKind.cs index 4072c364b..31e2dbe7c 100644 --- a/src/MsgPack/Serialization/CollectionKind.cs +++ b/src/MsgPack/Serialization/CollectionKind.cs @@ -1,37 +1,10 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2018 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif - -using System; +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. namespace MsgPack.Serialization { -#if UNITY && DEBUG - public -#else - internal -#endif - enum CollectionKind + internal enum CollectionKind { NotCollection = 0, Array, diff --git a/src/MsgPack/Serialization/CollectionTraitOptions.cs b/src/MsgPack/Serialization/CollectionTraitOptions.cs index 8c11b59a2..6113c0d9b 100644 --- a/src/MsgPack/Serialization/CollectionTraitOptions.cs +++ b/src/MsgPack/Serialization/CollectionTraitOptions.cs @@ -1,30 +1,12 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2016 FUJIWARA, Yusuke and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Contributors: -// Samuel Cragg -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; namespace MsgPack.Serialization { +#warning TODO: NonPublic [Flags] internal enum CollectionTraitOptions { @@ -32,7 +14,8 @@ internal enum CollectionTraitOptions WithAddMethod = 0x1, WithCountPropertyGetter = 0x2, WithGetEnumeratorMethod = 0x4, + WithConstructor = 0x8, AllowNonCollectionEnumerableTypes = 0x800, - Full = WithAddMethod | WithCountPropertyGetter | WithGetEnumeratorMethod + Full = WithAddMethod | WithCountPropertyGetter | WithGetEnumeratorMethod | WithConstructor } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/CollectionTraits.cs b/src/MsgPack/Serialization/CollectionTraits.cs index 8496d1107..470114353 100644 --- a/src/MsgPack/Serialization/CollectionTraits.cs +++ b/src/MsgPack/Serialization/CollectionTraits.cs @@ -1,57 +1,35 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2018 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; using System.Reflection; namespace MsgPack.Serialization { -#if UNITY && DEBUG - public -#else - internal -#endif - struct CollectionTraits + internal readonly struct CollectionTraits { - public static readonly CollectionTraits NotCollection = new CollectionTraits( CollectionDetailedKind.NotCollection, null, null, null, null ); - public static readonly CollectionTraits Unserializable = new CollectionTraits( CollectionDetailedKind.Unserializable, null, null, null, null ); + public static readonly CollectionTraits NotCollection = new CollectionTraits(CollectionDetailedKind.NotCollection, null, null, null, null, null, null); + public static readonly CollectionTraits Unserializable = new CollectionTraits(CollectionDetailedKind.Unserializable, null, null, null, null, null, null); - public readonly Type ElementType; + public Type? ElementType { get; } - public readonly CollectionDetailedKind DetailedCollectionType; + public CollectionDetailedKind DetailedCollectionType { get; } public CollectionKind CollectionType { get { - switch ( this.DetailedCollectionType ) + switch (this.DetailedCollectionType) { case CollectionDetailedKind.Array: case CollectionDetailedKind.GenericCollection: case CollectionDetailedKind.GenericEnumerable: case CollectionDetailedKind.GenericList: -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) case CollectionDetailedKind.GenericReadOnlyCollection: case CollectionDetailedKind.GenericReadOnlyList: #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) @@ -65,7 +43,7 @@ public CollectionKind CollectionType return CollectionKind.Array; } case CollectionDetailedKind.GenericDictionary: -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) case CollectionDetailedKind.GenericReadOnlyDictionary: #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) case CollectionDetailedKind.NonGenericDictionary: @@ -82,23 +60,49 @@ public CollectionKind CollectionType } default: { - throw new NotSupportedException( "Unknown detailed type:" + this.DetailedCollectionType ); + throw new NotSupportedException("Unknown detailed type:" + this.DetailedCollectionType); } } } } - public readonly MethodInfo GetEnumeratorMethod; - public readonly MethodInfo AddMethod; - public readonly MethodInfo CountPropertyGetter; + public MethodInfo? GetEnumeratorMethod { get; } + public MethodInfo? AddMethod { get; } + public MethodInfo? CountPropertyGetter { get; } + + public ConstructorInfo? ConstructorWithCapacity { get; } + public ConstructorInfo? DefaultConstructor { get; } - public CollectionTraits( CollectionDetailedKind type, Type elementType, MethodInfo getEnumeratorMethod, MethodInfo addMethod, MethodInfo countPropertyGetter ) + public CollectionTraits(CollectionDetailedKind type, ConstructorInfo? constructorWithCapacity, ConstructorInfo? defaultConstructor, Type? elementType, MethodInfo? getEnumeratorMethod, MethodInfo? addMethod, MethodInfo? countPropertyGetter) { this.DetailedCollectionType = type; this.ElementType = elementType; this.GetEnumeratorMethod = getEnumeratorMethod; this.AddMethod = addMethod; this.CountPropertyGetter = countPropertyGetter; + this.ConstructorWithCapacity = constructorWithCapacity; + this.DefaultConstructor = defaultConstructor; + } + + public (Type? KeyType, Type? ValueType) GetKeyValueType() + { + if (this.ElementType == null || this.CollectionType != CollectionKind.Map) + { + return (null, null); + } + + if (this.ElementType == typeof(DictionaryEntry)) + { + return (typeof(object), typeof(object)); + } + + Debug.Assert(this.ElementType.IsGenericType, $"this.ElementType.IsGenericType ({this.ElementType})"); + Debug.Assert(this.ElementType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>), $"this.ElementType.GetGenericTypeDefinition()({this.ElementType}) == typeof(KeyValuePair<,>)"); + + var typeArguments = this.ElementType.GetGenericTypeParameters(); + Debug.Assert(typeArguments.Length == 2, $"typeArguments.Length ({typeArguments.Length}) == 2"); + + return (typeArguments[0], typeArguments[1]); } } } diff --git a/src/MsgPack/Serialization/DataMemberContract.cs b/src/MsgPack/Serialization/DataMemberContract.cs index 8ff6f5abc..bdab6b1a0 100644 --- a/src/MsgPack/Serialization/DataMemberContract.cs +++ b/src/MsgPack/Serialization/DataMemberContract.cs @@ -1,33 +1,13 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2018 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; #if FEATURE_MPCONTRACT using Contract = MsgPack.MPContract; #else using System.Diagnostics.Contracts; -#endif // FEATURE_MPCONTRACT || NETSTANDARD1_1 +#endif // FEATURE_MPCONTRACT using System.Globalization; using System.Reflection; using System.Runtime.Serialization; @@ -37,48 +17,17 @@ namespace MsgPack.Serialization /// /// Represents member's data contract. /// -#if !UNITY - internal struct DataMemberContract -#else -#warning TODO: To struct if possible -#if UNITY && DEBUG - public -#else - internal -#endif - sealed class DataMemberContract -#endif // !UNITY + internal readonly struct DataMemberContract { -#if UNITY - /// - /// Null object. - /// - public static readonly DataMemberContract Null = new DataMemberContract(); -#endif - internal const int UnspecifiedId = -1; - private readonly string _name; - /// /// Gets the name of the member. /// /// /// The name of the member. /// - public string Name - { - get - { -#if DEBUG - Contract.Ensures( !String.IsNullOrEmpty( Contract.Result() ) ); -#endif // DEBUG - - return this._name; - } - } - - private readonly int _id; + public string Name { get; } /// /// Gets the ID of the member. @@ -86,19 +35,7 @@ public string Name /// /// The ID of the member. Default is -1. /// - public int Id - { - get - { -#if DEBUG - Contract.Ensures( Contract.Result() >= -1 ); -#endif // DEBUG - - return this._id; - } - } - - private readonly NilImplication _nilImplication; + public int Id { get; } /// /// Gets the nil implication. @@ -106,31 +43,19 @@ public int Id /// /// The nil implication. /// - public NilImplication NilImplication - { - get { return this._nilImplication; } - } - -#if UNITY - public DataMemberContract() - { - this._name = null; - this._nilImplication = NilImplication.MemberDefault; - this._id = UnspecifiedId; - } -#endif // UNITY + public NilImplication NilImplication { get; } /// /// Initializes a new instance of the struct. /// /// The target member. - public DataMemberContract( MemberInfo member ) + public DataMemberContract(MemberInfo member) { - Contract.Assert( member != null ); + Contract.Assert(member != null); - this._name = member.Name; - this._nilImplication = NilImplication.MemberDefault; - this._id = UnspecifiedId; + this.Name = member.Name; + this.NilImplication = NilImplication.MemberDefault; + this.Id = UnspecifiedId; } /// @@ -140,18 +65,18 @@ public DataMemberContract( MemberInfo member ) /// The name of member. /// The implication of the nil value for the member. /// The ID of the member. This value cannot be negative and must be unique in the type. - public DataMemberContract( MemberInfo member, string name, NilImplication nilImplication, int? id ) + public DataMemberContract(MemberInfo member, string? name, NilImplication nilImplication, int? id) { - Contract.Assert( member != null ); + Contract.Assert(member != null); - if ( id < 0 ) + if (id < 0) { - throw new SerializationException( String.Format( CultureInfo.CurrentCulture, "The member ID cannot be negative. The member is '{0}' in the '{1}' type.", member.Name, member.DeclaringType ) ); + throw new SerializationException(String.Format(CultureInfo.CurrentCulture, "The member ID cannot be negative. The member is '{0}' in the '{1}' type.", member.Name, member.DeclaringType)); } - this._name = String.IsNullOrEmpty( name ) ? member.Name : name; - this._nilImplication = nilImplication; - this._id = id ?? UnspecifiedId; + this.Name = String.IsNullOrEmpty(name) ? member.Name : name; + this.NilImplication = nilImplication; + this.Id = id ?? UnspecifiedId; } /// @@ -159,19 +84,19 @@ public DataMemberContract( MemberInfo member, string name, NilImplication nilImp /// /// The target member. /// The MessagePack member attribute. - public DataMemberContract( MemberInfo member, MessagePackMemberAttribute attribute ) + public DataMemberContract(MemberInfo member, MessagePackMemberAttribute attribute) { - Contract.Assert( member != null ); - Contract.Assert( attribute != null ); + Contract.Assert(member != null); + Contract.Assert(attribute != null); - if ( attribute.Id < 0 ) + if (attribute.Id < 0) { - throw new SerializationException( String.Format( CultureInfo.CurrentCulture, "The member ID cannot be negative. The member is '{0}' in the '{1}' type.", member.Name, member.DeclaringType ) ); + throw new SerializationException(String.Format(CultureInfo.CurrentCulture, "The member ID cannot be negative. The member is '{0}' in the '{1}' type.", member.Name, member.DeclaringType)); } - this._name = String.IsNullOrEmpty( attribute.Name ) ? member.Name : attribute.Name; - this._nilImplication = attribute.NilImplication; - this._id = attribute.Id; + this.Name = String.IsNullOrEmpty(attribute.Name) ? member.Name : attribute.Name; + this.NilImplication = attribute.NilImplication; + this.Id = attribute.Id; } } } diff --git a/src/MsgPack/Serialization/DateTimeConversionMethod.cs b/src/MsgPack/Serialization/DateTimeConversionMethod.cs index 47e82fac8..9f317fda5 100644 --- a/src/MsgPack/Serialization/DateTimeConversionMethod.cs +++ b/src/MsgPack/Serialization/DateTimeConversionMethod.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2015 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; diff --git a/src/MsgPack/Serialization/DateTimeMemberConversionMethod.cs b/src/MsgPack/Serialization/DateTimeMemberConversionMethod.cs index 27dd26a69..8ade1933b 100644 --- a/src/MsgPack/Serialization/DateTimeMemberConversionMethod.cs +++ b/src/MsgPack/Serialization/DateTimeMemberConversionMethod.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2015 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; diff --git a/src/MsgPack/Serialization/EnumMemberSerializationMethod.cs b/src/MsgPack/Serialization/EnumMemberSerializationMethod.cs index c605f80a2..40d1fcb96 100644 --- a/src/MsgPack/Serialization/EnumMemberSerializationMethod.cs +++ b/src/MsgPack/Serialization/EnumMemberSerializationMethod.cs @@ -1,24 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2014 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -using System; +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. namespace MsgPack.Serialization { @@ -42,4 +24,4 @@ public enum EnumMemberSerializationMethod /// ByUnderlyingValue } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/EnumSerializationMethod.cs b/src/MsgPack/Serialization/EnumSerializationMethod.cs index 13c75dd5b..ed2ab1839 100644 --- a/src/MsgPack/Serialization/EnumSerializationMethod.cs +++ b/src/MsgPack/Serialization/EnumSerializationMethod.cs @@ -1,24 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2014 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -using System; +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. namespace MsgPack.Serialization { @@ -37,4 +19,4 @@ public enum EnumSerializationMethod /// ByUnderlyingValue } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/EnumSerializationOptions.cs b/src/MsgPack/Serialization/EnumSerializationOptions.cs index 369fb46cd..8e6bd8e5c 100644 --- a/src/MsgPack/Serialization/EnumSerializationOptions.cs +++ b/src/MsgPack/Serialization/EnumSerializationOptions.cs @@ -18,10 +18,6 @@ // #endregion -- License Terms -- -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif - using System; #if FEATURE_MPCONTRACT using Contract = MsgPack.MPContract; @@ -63,11 +59,11 @@ public EnumSerializationMethod SerializationMethod Contract.Ensures( Enum.IsDefined( typeof( EnumSerializationMethod ), Contract.Result() ) ); #endif // DEBUG - return ( EnumSerializationMethod )Volatile.Read( ref this._serializationMethod ); + return (EnumSerializationMethod)Volatile.Read(ref this._serializationMethod); } set { - switch ( value ) + switch (value) { case EnumSerializationMethod.ByName: case EnumSerializationMethod.ByUnderlyingValue: @@ -76,13 +72,13 @@ public EnumSerializationMethod SerializationMethod } default: { - throw new ArgumentOutOfRangeException( "value" ); + throw new ArgumentOutOfRangeException("value"); } } Contract.EndContractBlock(); - Volatile.Write( ref this._serializationMethod, ( int )value ); + Volatile.Write(ref this._serializationMethod, (int)value); } } @@ -126,4 +122,4 @@ internal Func SafeNameTransformer get { return this.NameTransformer ?? KeyNameTransformers.AsIs; } } } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/MessagePackDateTimeMemberAttribute.cs b/src/MsgPack/Serialization/MessagePackDateTimeMemberAttribute.cs index 07c5b5911..484cfeebc 100644 --- a/src/MsgPack/Serialization/MessagePackDateTimeMemberAttribute.cs +++ b/src/MsgPack/Serialization/MessagePackDateTimeMemberAttribute.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2015 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; @@ -28,7 +12,7 @@ namespace MsgPack.Serialization /// /// If this attributes is used for incompatible typed members, this attribute will be ignored. /// - [AttributeUsage( AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false )] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false)] public sealed class MessagePackDateTimeMemberAttribute : Attribute { /// @@ -45,4 +29,4 @@ public sealed class MessagePackDateTimeMemberAttribute : Attribute /// public MessagePackDateTimeMemberAttribute() { } } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/MessagePackDeserializationConstructorAttribute.cs b/src/MsgPack/Serialization/MessagePackDeserializationConstructorAttribute.cs index 734d0783d..23b4ea739 100644 --- a/src/MsgPack/Serialization/MessagePackDeserializationConstructorAttribute.cs +++ b/src/MsgPack/Serialization/MessagePackDeserializationConstructorAttribute.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2015-2015 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; @@ -63,4 +47,4 @@ public sealed class MessagePackDeserializationConstructorAttribute : Attribute /// public MessagePackDeserializationConstructorAttribute() { } } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/MessagePackEnumAttribute.cs b/src/MsgPack/Serialization/MessagePackEnumAttribute.cs index 8ce3551e3..05c3ccf9c 100644 --- a/src/MsgPack/Serialization/MessagePackEnumAttribute.cs +++ b/src/MsgPack/Serialization/MessagePackEnumAttribute.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2014 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; @@ -28,7 +12,7 @@ namespace MsgPack.Serialization /// /// Enum types which are not marked with this attribute will be serialized as value. /// - [AttributeUsage( AttributeTargets.Enum, Inherited = false, AllowMultiple = false )] + [AttributeUsage(AttributeTargets.Enum, Inherited = false, AllowMultiple = false)] public sealed class MessagePackEnumAttribute : Attribute { /// @@ -45,4 +29,4 @@ public sealed class MessagePackEnumAttribute : Attribute /// public MessagePackEnumAttribute() { } } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/MessagePackEnumMemberAttribute.cs b/src/MsgPack/Serialization/MessagePackEnumMemberAttribute.cs index 530161c20..7a9f4661f 100644 --- a/src/MsgPack/Serialization/MessagePackEnumMemberAttribute.cs +++ b/src/MsgPack/Serialization/MessagePackEnumMemberAttribute.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2014 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; @@ -28,7 +12,7 @@ namespace MsgPack.Serialization /// /// If this attributes is used for non-enum typed members, this attribute will be ignored. /// - [AttributeUsage( AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false )] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = true, AllowMultiple = false)] public sealed class MessagePackEnumMemberAttribute : Attribute { /// @@ -45,4 +29,4 @@ public sealed class MessagePackEnumMemberAttribute : Attribute /// public MessagePackEnumMemberAttribute() { } } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/MessagePackIgnoreAttribute.cs b/src/MsgPack/Serialization/MessagePackIgnoreAttribute.cs index 3e3254317..9167bc8aa 100644 --- a/src/MsgPack/Serialization/MessagePackIgnoreAttribute.cs +++ b/src/MsgPack/Serialization/MessagePackIgnoreAttribute.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2014 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; @@ -25,7 +9,7 @@ namespace MsgPack.Serialization /// /// Marks the field or the property should not be serialized/deserialized with MessagePack for CLI serialization mechanism. /// - [AttributeUsage( AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] public sealed class MessagePackIgnoreAttribute : Attribute { /// diff --git a/src/MsgPack/Serialization/MessagePackMemberAttribute.cs b/src/MsgPack/Serialization/MessagePackMemberAttribute.cs index 2a8a78457..a5608da56 100644 --- a/src/MsgPack/Serialization/MessagePackMemberAttribute.cs +++ b/src/MsgPack/Serialization/MessagePackMemberAttribute.cs @@ -1,26 +1,7 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2015 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif using System; #if FEATURE_MPCONTRACT @@ -34,23 +15,16 @@ namespace MsgPack.Serialization /// /// Marks a field or a property to be serialized with MessagePack Serializer and defines some required informations to serialize. /// - [AttributeUsage( AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true )] + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)] public sealed class MessagePackMemberAttribute : Attribute { - private readonly int _id; - /// /// Gets the ID of the member. /// /// /// The ID of the member. /// - public int Id - { - get { return this._id; } - } - - private string _name; + public int Id { get; } /// /// Gets or sets the name of this member. @@ -58,11 +32,7 @@ public int Id /// /// The name which will be used in map key on serialized MessagePack stream. /// - public string Name - { - get { return this._name; } - set { this._name = value; } - } + public string? Name { get; set; } private NilImplication _nilImplication; @@ -85,7 +55,7 @@ public NilImplication NilImplication } set { - switch ( value ) + switch (value) { case NilImplication.MemberDefault: case NilImplication.Null: @@ -95,7 +65,7 @@ public NilImplication NilImplication } default: { - throw new ArgumentOutOfRangeException( "value" ); + throw new ArgumentOutOfRangeException(nameof(value)); } } @@ -111,9 +81,9 @@ public NilImplication NilImplication /// /// The ID of the member. This value cannot be negative and must be unique in the type. /// - public MessagePackMemberAttribute( int id ) + public MessagePackMemberAttribute(int id) { - this._id = id; + this.Id = id; } } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/NilImplication.cs b/src/MsgPack/Serialization/NilImplication.cs index c338304c5..8fb9501c6 100644 --- a/src/MsgPack/Serialization/NilImplication.cs +++ b/src/MsgPack/Serialization/NilImplication.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2012 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; diff --git a/src/MsgPack/Serialization/Polymorphic/IPolymorphicDeserializer.cs b/src/MsgPack/Serialization/Polymorphic/IPolymorphicDeserializer.cs index 26f96d1fa..77d935007 100644 --- a/src/MsgPack/Serialization/Polymorphic/IPolymorphicDeserializer.cs +++ b/src/MsgPack/Serialization/Polymorphic/IPolymorphicDeserializer.cs @@ -19,9 +19,12 @@ #endregion -- License Terms -- using System; +using System.Buffers; +using System.Runtime.CompilerServices; #if FEATURE_TAP using System.Threading; using System.Threading.Tasks; +using MsgPack.Internal; #endif // FEATURE_TAP namespace MsgPack.Serialization.Polymorphic @@ -31,10 +34,20 @@ namespace MsgPack.Serialization.Polymorphic /// internal interface IPolymorphicDeserializer { - object PolymorphicUnpackFrom( Unpacker unpacker ); + object? DeserializePolymorphic(ref DeserializationOperationContext context, ref SequenceReader reader); #if FEATURE_TAP - Task PolymorphicUnpackFromAsync( Unpacker unpacker, CancellationToken cancellationToken ); + ValueTask DeserializePolymorphicAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence sequence, CancellationToken cancellationToken ); #endif // FEATURE_TAP } -} \ No newline at end of file + +#warning TODO: Compat + //internal static class PolymorphicDeserializerCompatibilityExtensions + //{ + // public static object PolymorphicUnpackFrom(this IPolymorphicDeserializer deserializer, Unpacker unpacker) + // => deserializer.DeserializePolymorphic(unpacker.Sequence); + + // public static ValueTask PolymorphicUnpackFromAsync(this IPolymorphicDeserializer deserializer, Unpacker unpacker) + // => deserializer.PolymorphicDeserializeAsync(unpacker.Stream); + //} +} diff --git a/src/MsgPack/Serialization/PolymorphismSchema.Internals.cs b/src/MsgPack/Serialization/PolymorphismSchema.Internals.cs index df29d8e02..62da7a124 100644 --- a/src/MsgPack/Serialization/PolymorphismSchema.Internals.cs +++ b/src/MsgPack/Serialization/PolymorphismSchema.Internals.cs @@ -1,31 +1,11 @@ -#region -- License Terms -- -// MessagePack for CLI -// -// Copyright (C) 2015-2018 FUJIWARA, Yusuke and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Contributors: -// Samuel Cragg -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; #if FEATURE_MPCONTRACT using Contract = MsgPack.MPContract; #else @@ -46,78 +26,73 @@ partial class PolymorphismSchema private static readonly Func DefaultTypeVerfiier = _ => true; /// - /// Default instance (null object). + /// Gets a default instance. /// -#if UNITY && DEBUG - public -#else - internal -#endif - static readonly PolymorphismSchema Default = new PolymorphismSchema(); + public static PolymorphismSchema Default { get; } = new PolymorphismSchema(); /// /// ForPolymorphicObject( Type targetType ) /// internal static readonly MethodInfo ForPolymorphicObjectTypeEmbeddingMethod = - typeof( PolymorphismSchema ).GetMethod( "ForPolymorphicObject", new[] { typeof( Type ) } ); + typeof(PolymorphismSchema).GetMethod("ForPolymorphicObject", new[] { typeof(Type) }); /// /// ForPolymorphicObject( Type targetType, IDictionary{byte, Type} codeTypeMapping ) /// internal static readonly MethodInfo ForPolymorphicObjectCodeTypeMappingMethod = - typeof( PolymorphismSchema ).GetMethod( "ForPolymorphicObject", new[] { typeof( Type ), typeof( IDictionary ) } ); + typeof(PolymorphismSchema).GetMethod("ForPolymorphicObject", new[] { typeof(Type), typeof(IDictionary) }); /// /// ForContextSpecifiedCollection( Type targetType, PolymorphismSchema itemsSchema ) /// internal static readonly MethodInfo ForContextSpecifiedCollectionMethod = - typeof( PolymorphismSchema ).GetMethod( "ForContextSpecifiedCollection", new[] { typeof( Type ), typeof( PolymorphismSchema ) } ); + typeof(PolymorphismSchema).GetMethod("ForContextSpecifiedCollection", new[] { typeof(Type), typeof(PolymorphismSchema) }); /// /// ForPolymorphicCollection( Type targetType, PolymorphismSchema itemsSchema ) /// internal static readonly MethodInfo ForPolymorphicCollectionTypeEmbeddingMethod = - typeof( PolymorphismSchema ).GetMethod( "ForPolymorphicCollection", new[] { typeof( Type ), typeof( PolymorphismSchema ) } ); + typeof(PolymorphismSchema).GetMethod("ForPolymorphicCollection", new[] { typeof(Type), typeof(PolymorphismSchema) }); /// /// ForPolymorphicCollection( Type targetType, IDictionary{byte, Type} codeTypeMapping, PolymorphismSchema itemsSchema ) /// internal static readonly MethodInfo ForPolymorphicCollectionCodeTypeMappingMethod = - typeof( PolymorphismSchema ).GetMethod( "ForPolymorphicCollection", new[] { typeof( Type ), typeof( IDictionary ), typeof( PolymorphismSchema ) } ); + typeof(PolymorphismSchema).GetMethod("ForPolymorphicCollection", new[] { typeof(Type), typeof(IDictionary), typeof(PolymorphismSchema) }); /// /// ForContextSpecifiedDictionary( Type targetType, PolymorphismSchema keysSchema, PolymorphismSchema valuesSchema ) /// internal static readonly MethodInfo ForContextSpecifiedDictionaryMethod = - typeof( PolymorphismSchema ).GetMethod( "ForContextSpecifiedDictionary", new[] { typeof( Type ), typeof( PolymorphismSchema ), typeof( PolymorphismSchema ) } ); + typeof(PolymorphismSchema).GetMethod("ForContextSpecifiedDictionary", new[] { typeof(Type), typeof(PolymorphismSchema), typeof(PolymorphismSchema) }); /// /// ForPolymorphicDictionary( Type targetType, PolymorphismSchema keysSchema, PolymorphismSchema valuesSchema ) /// internal static readonly MethodInfo ForPolymorphicDictionaryTypeEmbeddingMethod = - typeof( PolymorphismSchema ).GetMethod( "ForPolymorphicDictionary", new[] { typeof( Type ), typeof( PolymorphismSchema ), typeof( PolymorphismSchema ) } ); + typeof(PolymorphismSchema).GetMethod("ForPolymorphicDictionary", new[] { typeof(Type), typeof(PolymorphismSchema), typeof(PolymorphismSchema) }); /// /// ForPolymorphicDictionary( Type targetType, IDictionary{byte, Type} codeTypeMapping, PolymorphismSchema keysSchema, PolymorphismSchema valuesSchema ) /// internal static readonly MethodInfo ForPolymorphicDictionaryCodeTypeMappingMethod = - typeof( PolymorphismSchema ).GetMethod( "ForPolymorphicDictionary", new[] { typeof( Type ), typeof( IDictionary ), typeof( PolymorphismSchema ), typeof( PolymorphismSchema ) } ); + typeof(PolymorphismSchema).GetMethod("ForPolymorphicDictionary", new[] { typeof(Type), typeof(IDictionary), typeof(PolymorphismSchema), typeof(PolymorphismSchema) }); #if !NET35 && !UNITY /// /// ForPolymorphicTuple( Type targetType, PolymorphismSchema[] itemSchemaList ) /// internal static readonly MethodInfo ForPolymorphicTupleMethod = - typeof( PolymorphismSchema ).GetMethod( "ForPolymorphicTuple", new[] { typeof( Type ), typeof( PolymorphismSchema[]) } ); + typeof(PolymorphismSchema).GetMethod("ForPolymorphicTuple", new[] { typeof(Type), typeof(PolymorphismSchema[]) }); #endif // !NET35 && !UNITY internal static readonly ConstructorInfo CodeTypeMapConstructor = - typeof( Dictionary<,> ).MakeGenericType( typeof( string ), typeof( Type ) ) - .GetConstructor( new[] { typeof( int ) } ); + typeof(Dictionary<,>).MakeGenericType(typeof(string), typeof(Type)) + .GetConstructor(new[] { typeof(int) }); internal static readonly MethodInfo AddToCodeTypeMapMethod = - typeof( Dictionary<,> ).MakeGenericType( typeof( string ), typeof( Type ) ) - .GetMethod( "Add", new[] { typeof( string ), typeof( Type ) } ); + typeof(Dictionary<,>).MakeGenericType(typeof(string), typeof(Type)) + .GetMethod("Add", new[] { typeof(string), typeof(Type) }); internal string DebugString @@ -125,50 +100,50 @@ internal string DebugString get { var buffer = new StringBuilder(); - this.ToDebugString( buffer ); + this.ToDebugString(buffer); return buffer.ToString(); } } - private void ToDebugString( StringBuilder buffer ) + private void ToDebugString(StringBuilder buffer) { - buffer.Append( "{TargetType:" ).Append( this.TargetType ).Append( ", SchemaType:" ).Append( this.PolymorphismType ); - switch ( this.ChildrenType ) + buffer.Append("{TargetType:").Append(this.TargetType).Append(", SchemaType:").Append(this.PolymorphismType); + switch (this.ChildrenType) { case PolymorphismSchemaChildrenType.CollectionItems: { - buffer.Append( ", CollectionItemsSchema:" ); - if ( this.ItemSchema == null ) + buffer.Append(", CollectionItemsSchema:"); + if (this.ItemSchema == null) { - buffer.Append( "null" ); + buffer.Append("null"); } else { - this.ItemSchema.ToDebugString( buffer ); + this.ItemSchema.ToDebugString(buffer); } break; } case PolymorphismSchemaChildrenType.DictionaryKeyValues: { - buffer.Append( ", DictinoaryKeysSchema:" ); - if ( this.KeySchema == null ) + buffer.Append(", DictinoaryKeysSchema:"); + if (this.KeySchema == null) { - buffer.Append( "null" ); + buffer.Append("null"); } else { - this.KeySchema.ToDebugString( buffer ); + this.KeySchema.ToDebugString(buffer); } - buffer.Append( ", DictinoaryValuesSchema:" ); - if ( this.ItemSchema == null ) + buffer.Append(", DictinoaryValuesSchema:"); + if (this.ItemSchema == null) { - buffer.Append( "null" ); + buffer.Append("null"); } else { - this.ItemSchema.ToDebugString( buffer ); + this.ItemSchema.ToDebugString(buffer); } break; @@ -176,26 +151,26 @@ private void ToDebugString( StringBuilder buffer ) #if !NET35 && !UNITY case PolymorphismSchemaChildrenType.TupleItems: { - buffer.Append( ", TupleItemsSchema:[" ); + buffer.Append(", TupleItemsSchema:["); var isFirst = true; - foreach ( var child in this._children ) + foreach (var child in this._children) { - if ( isFirst ) + if (isFirst) { isFirst = false; } else { - buffer.Append( ", " ); + buffer.Append(", "); } - if ( child == null ) + if (child == null) { - buffer.Append( "null" ); + buffer.Append("null"); } else { - child.ToDebugString( buffer ); + child.ToDebugString(buffer); } } @@ -204,10 +179,11 @@ private void ToDebugString( StringBuilder buffer ) #endif // !NET35 && !UNITY } - buffer.Append( '}' ); + buffer.Append('}'); } internal static PolymorphismSchema Create( + DiagnosticSource diag, Type type, #if !UNITY SerializingMember? memberMayBeNull @@ -216,31 +192,35 @@ SerializingMember memberMayBeNull #endif // !UNITY ) { - if ( type.GetIsValueType() ) + if (type.GetIsValueType()) { - SerializerDebugging.TracePolimorphicSchemaEvent( - "Returns default because '{0}' is value type: {1}", - memberMayBeNull == null + diag.DefaultPolymorphicSchemaForValueType( + new + { + targetType = + memberMayBeNull == null ? #if !NETFX_CORE && !NETSTANDARD1_1 && !NETSTANDARD1_3 - ? type + type : #else - ? type.GetTypeInfo() + type.GetTypeInfo() : #endif // !NETFX_CORE && !NETSTANDARD1_1 && !NETSTANDARD1_3 #if !UNITY - : memberMayBeNull.Value.Member, + memberMayBeNull.Value.Member, #else - : memberMayBeNull.Member, + memberMayBeNull.Member, #endif - Default + schema = Default + } ); // Value types will never be polymorphic. return Default; } - if ( memberMayBeNull == null ) + if (memberMayBeNull == null) { var schema = CreateCore( + diag, #if !NETFX_CORE && !NETSTANDARD1_1 && !NETSTANDARD1_3 type, #else @@ -248,14 +228,17 @@ SerializingMember memberMayBeNull #endif // !NETFX_CORE && !NETSTANDARD1_1 && !NETSTANDARD1_3 Default ); - SerializerDebugging.TracePolimorphicSchemaEvent( - "Returns root type schema for '{0}': {1}", + diag.PolymorphicSchemaCreatedForRootType( + new + { + targetType = #if !NETFX_CORE && !NETSTANDARD1_1 && !NETSTANDARD1_3 - type, + type, #else - type.GetTypeInfo(), + type.GetTypeInfo(), #endif // !NETFX_CORE && !NETSTANDARD1_1 && !NETSTANDARD1_3 - schema + schema = schema + } ); return schema; } @@ -268,8 +251,10 @@ SerializingMember memberMayBeNull return CreateCore( + diag, member.Member, CreateCore( + diag, #if !NETFX_CORE && !NETSTANDARD1_1 && !NETSTANDARD1_3 type, #else @@ -280,23 +265,32 @@ SerializingMember memberMayBeNull ); } - private static PolymorphismSchema CreateCore( MemberInfo member, PolymorphismSchema defaultSchema ) + private static PolymorphismSchema CreateCore(DiagnosticSource diag, MemberInfo member, PolymorphismSchema defaultSchema) { - var table = TypeTable.Create( member, defaultSchema ); + var table = TypeTable.Create(member, defaultSchema); - var traits = member.GetMemberValueType().GetCollectionTraits( CollectionTraitOptions.None, allowNonCollectionEnumerableTypes: false ); - switch ( traits.CollectionType ) +#warning TODO: CollectionTraits can be taken from SerializingMember. + var traits = member.GetMemberValueType().GetCollectionTraits(CollectionTraitOptions.None, allowNonCollectionEnumerableTypes: false); + switch (traits.CollectionType) { case CollectionKind.Array: { - if ( !table.Member.Exists && !table.CollectionItem.Exists ) + if (!table.Member.Exists && !table.CollectionItem.Exists) { - SerializerDebugging.TracePolimorphicSchemaEvent( "Returns default because '{0}' does not have own nor items schema: {1}", member, defaultSchema ); + diag.DefaultPolymorphicSchemaForUnqualifiedCollectionMember( + new + { + targetMember = member, + schema = defaultSchema + } + ); return defaultSchema; } - SerializerDebugging.TracePolimorphicSchemaEvent( "Returns collection schema for '{0}': {1}", member, defaultSchema ); - return + Debug.Assert(traits.ElementType != null, "traits.ElementType != null"); + +#warning TODO: Non-Generic? + var result = new PolymorphismSchema( member.GetMemberValueType(), table.Member.PolymorphismType, @@ -311,17 +305,34 @@ private static PolymorphismSchema CreateCore( MemberInfo member, PolymorphismSch PolymorphismSchemaChildrenType.None ) ); + diag.PolymorphicSchemaCreatedForCollectionMember( + new + { + targetMember = member, + schema = result + } + ); + return result; } case CollectionKind.Map: { - if ( !table.Member.Exists && !table.DictionaryKey.Exists && !table.CollectionItem.Exists ) + if (!table.Member.Exists && !table.DictionaryKey.Exists && !table.CollectionItem.Exists) { - SerializerDebugging.TracePolimorphicSchemaEvent( "Returns default because '{0}' does not have own, keys, nor items schema: {1}", member, defaultSchema ); + diag.DefaultPolymorphicSchemaForUnqualifiedDictionaryMember( + new + { + targetMember = member, + schema = defaultSchema + } + ); return defaultSchema; } - SerializerDebugging.TracePolimorphicSchemaEvent( "Returns dictionary schema for '{0}': {1}", member, defaultSchema ); - return + Debug.Assert(traits.ElementType != null, "traits.ElementType != null"); + var genericArguments = traits.ElementType.GetGenericArguments(); + +#warning TODO: Non-Generic? + var result = new PolymorphismSchema( member.GetMemberValueType(), table.Member.PolymorphismType, @@ -329,35 +340,49 @@ private static PolymorphismSchema CreateCore( MemberInfo member, PolymorphismSch table.Member.TypeVerifier, PolymorphismSchemaChildrenType.DictionaryKeyValues, new PolymorphismSchema( - traits.ElementType.GetGenericArguments()[ 0 ], + genericArguments[0], table.DictionaryKey.PolymorphismType, table.DictionaryKey.CodeTypeMapping, table.DictionaryKey.TypeVerifier, PolymorphismSchemaChildrenType.None ), new PolymorphismSchema( - traits.ElementType.GetGenericArguments()[ 1 ], + genericArguments[1], table.CollectionItem.PolymorphismType, table.CollectionItem.CodeTypeMapping, table.CollectionItem.TypeVerifier, PolymorphismSchemaChildrenType.None ) ); + + diag.PolymorphicSchemaCreatedForDictionaryMember( + new + { + targetMember = member, + schema = result + } + ); + return result; } default: { #if !NET35 && !UNITY - if ( TupleItems.IsTuple( member.GetMemberValueType() ) ) + if (TupleItems.IsTuple(member.GetMemberValueType())) { - if ( table.TupleItems.Count == 0 ) + if (table.TupleItems.Count == 0) { - SerializerDebugging.TracePolimorphicSchemaEvent( "Returns default because '{0}' does not have any tuple items schema: {1}", member, defaultSchema ); + diag.DefaultPolymorphicSchemaForUnqualifiedTupleMember( + new + { + targetMember = member, + schema = defaultSchema + } + ); return defaultSchema; } - var tupleItemTypes = TupleItems.GetTupleItemTypes( member.GetMemberValueType() ); - SerializerDebugging.TracePolimorphicSchemaEvent( "Returns tuple items schema for '{0}': {1}", member, defaultSchema ); - return + var tupleItemTypes = TupleItems.GetTupleItemTypes(member.GetMemberValueType()); + var result = new PolymorphismSchema( member.GetMemberValueType(), PolymorphismType.None, @@ -365,29 +390,43 @@ private static PolymorphismSchema CreateCore( MemberInfo member, PolymorphismSch DefaultTypeVerfiier, PolymorphismSchemaChildrenType.TupleItems, table.TupleItems - .Zip( tupleItemTypes, ( e, t ) => new { Entry = e, ItemType = t } ) - .Select( e => - new PolymorphismSchema( - e.ItemType, - e.Entry.PolymorphismType, - e.Entry.CodeTypeMapping, - e.Entry.TypeVerifier, - PolymorphismSchemaChildrenType.None - ) + .Zip(tupleItemTypes, (e, t) => new { Entry = e, ItemType = t }) + .Select(e => + new PolymorphismSchema( + e.ItemType, + e.Entry.PolymorphismType, + e.Entry.CodeTypeMapping, + e.Entry.TypeVerifier, + PolymorphismSchemaChildrenType.None + ) ).ToArray() ); + + diag.PolymorphicSchemaCreatedForTupleMember( + new + { + targetMember = member, + schema = result + } + ); + return result; } else #endif // !NET35 && !UNITY { - if ( !table.Member.Exists ) + if (!table.Member.Exists) { - SerializerDebugging.TracePolimorphicSchemaEvent( "Returns default because '{0}' does not have own schema: {1}", member, defaultSchema ); + diag.DefaultPolymorphicSchemaForUnqualifiedObjectMember( + new + { + targetMember = member, + schema = defaultSchema + } + ); return defaultSchema; } - SerializerDebugging.TracePolimorphicSchemaEvent( "Returns type of member schema for '{0}'.", member, defaultSchema ); - return + var result = new PolymorphismSchema( member.GetMemberValueType(), table.Member.PolymorphismType, @@ -395,6 +434,14 @@ private static PolymorphismSchema CreateCore( MemberInfo member, PolymorphismSch table.Member.TypeVerifier, PolymorphismSchemaChildrenType.None ); + diag.PolymorphicSchemaCreatedForObjectMember( + new + { + targetMember = member, + schema = result + } + ); + return result; } } } @@ -426,15 +473,15 @@ TypeTableEntry dictionaryKey #endif // !NET35 && !UNITY } - public static TypeTable Create( MemberInfo member, PolymorphismSchema defaultSchema ) + public static TypeTable Create(MemberInfo member, PolymorphismSchema defaultSchema) { return new TypeTable( - TypeTableEntry.Create( member, PolymorphismTarget.Member, defaultSchema ), - TypeTableEntry.Create( member, PolymorphismTarget.CollectionItem, defaultSchema.TryGetItemSchema() ), - TypeTableEntry.Create( member, PolymorphismTarget.DictionaryKey, defaultSchema.TryGetKeySchema() ) + TypeTableEntry.Create(member, PolymorphismTarget.Member, defaultSchema), + TypeTableEntry.Create(member, PolymorphismTarget.CollectionItem, defaultSchema.TryGetItemSchema()), + TypeTableEntry.Create(member, PolymorphismTarget.DictionaryKey, defaultSchema.TryGetKeySchema()) #if !NET35 && !UNITY - , TypeTableEntry.CreateTupleItems( member ) + , TypeTableEntry.CreateTupleItems(member) #endif // !NET35 && !UNITY ); } @@ -443,7 +490,7 @@ public static TypeTable Create( MemberInfo member, PolymorphismSchema defaultSch private sealed class TypeTableEntry { #if !NET35 && !UNITY - private static readonly TypeTableEntry[] EmptyEntries = new TypeTableEntry[ 0 ]; + private static readonly TypeTableEntry[] EmptyEntries = new TypeTableEntry[0]; #endif // !NET35 && !UNITY private readonly Dictionary _knownTypeMapping = new Dictionary(); @@ -471,149 +518,149 @@ public PolymorphismType PolymorphismType private TypeTableEntry() { } - public static TypeTableEntry Create( MemberInfo member, PolymorphismTarget targetType, PolymorphismSchema defaultSchema ) + public static TypeTableEntry Create(MemberInfo member, PolymorphismTarget targetType, PolymorphismSchema defaultSchema) { var result = new TypeTableEntry(); var memberName = member.ToString(); foreach ( var attribute in - member.GetCustomAttributes( false ) + member.GetCustomAttributes(false) .OfType() - .Where( a => a.Target == targetType ) + .Where(a => a.Target == targetType) ) { // TupleItem schema should never come here, so passing -1 as tupleItemNumber is OK. - result.Interpret( attribute, memberName, -1 ); + result.Interpret(attribute, memberName, -1); } - if ( defaultSchema != null ) + if (defaultSchema != null) { // TupleItem schema should never come here, so passing -1 as tupleItemNumber is OK. - result.SetDefault( targetType, memberName, -1, defaultSchema ); + result.SetDefault(targetType, memberName, -1, defaultSchema); } return result; } #if !NET35 && !UNITY - public static TypeTableEntry[] CreateTupleItems( MemberInfo member ) + public static TypeTableEntry[] CreateTupleItems(MemberInfo member) { - if ( !TupleItems.IsTuple( member.GetMemberValueType() ) ) + if (!TupleItems.IsTuple(member.GetMemberValueType())) { return EmptyEntries; } - var tupleItems = TupleItems.GetTupleItemTypes( member.GetMemberValueType() ); - var result = tupleItems.Select( _ => new TypeTableEntry() ).ToArray(); + var tupleItems = TupleItems.GetTupleItemTypes(member.GetMemberValueType()); + var result = tupleItems.Select(_ => new TypeTableEntry()).ToArray(); foreach ( var attribute in - member.GetCustomAttributes( false ) + member.GetCustomAttributes(false) .OfType() - .OrderBy( a => a.ItemNumber ) + .OrderBy(a => a.ItemNumber) ) { - result[ attribute.ItemNumber - 1 ].Interpret( attribute, member.ToString(), attribute.ItemNumber ); + result[attribute.ItemNumber - 1].Interpret(attribute, member.ToString(), attribute.ItemNumber); } return result; } #endif // !NET35 && !UNITY - private void Interpret( IPolymorphicHelperAttribute attribute, string memberName, int tupleItemNumber ) + private void Interpret(IPolymorphicHelperAttribute attribute, string memberName, int tupleItemNumber) { var asKnown = attribute as IPolymorphicKnownTypeAttribute; - if ( asKnown != null ) + if (asKnown != null) { - this.SetKnownType( attribute.Target, memberName, tupleItemNumber, asKnown.TypeCode, asKnown.BindingType ); + this.SetKnownType(attribute.Target, memberName, tupleItemNumber, asKnown.TypeCode, asKnown.BindingType); return; } #if DEBUG - Contract.Assert( attribute is IPolymorphicRuntimeTypeAttribute, attribute + " is IPolymorphicRuntimeTypeAttribute" ); + Contract.Assert(attribute is IPolymorphicRuntimeTypeAttribute, attribute + " is IPolymorphicRuntimeTypeAttribute"); #endif // DEBUG - if ( this._useTypeEmbedding ) + if (this._useTypeEmbedding) { #if DEBUG - Contract.Assert( attribute.Target == PolymorphismTarget.TupleItem, attribute.Target + " == PolymorphismTarget.TupleItem" ); + Contract.Assert(attribute.Target == PolymorphismTarget.TupleItem, attribute.Target + " == PolymorphismTarget.TupleItem"); #endif // DEBUG throw new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot specify multiple '{0}' to #{1} item of tuple type member '{2}'.", - typeof( MessagePackRuntimeTupleItemTypeAttribute ), + typeof(MessagePackRuntimeTupleItemTypeAttribute), tupleItemNumber, memberName ) ); } - this.SetRuntimeType( attribute.Target, memberName, tupleItemNumber, GetVerifier( attribute as IPolymorphicRuntimeTypeAttribute ) ); + this.SetRuntimeType(attribute.Target, memberName, tupleItemNumber, GetVerifier(attribute as IPolymorphicRuntimeTypeAttribute)); } - private void SetDefault( PolymorphismTarget target, string memberName, int tupleItemNumber, PolymorphismSchema defaultSchema ) + private void SetDefault(PolymorphismTarget target, string memberName, int tupleItemNumber, PolymorphismSchema defaultSchema) { - if ( this._useTypeEmbedding || this._knownTypeMapping.Count > 0 ) + if (this._useTypeEmbedding || this._knownTypeMapping.Count > 0) { // Default is not required. return; } - switch ( defaultSchema.PolymorphismType ) + switch (defaultSchema.PolymorphismType) { case PolymorphismType.KnownTypes: { - foreach ( var typeMapping in defaultSchema.CodeTypeMapping ) + foreach (var typeMapping in defaultSchema.CodeTypeMapping) { - this.SetKnownType( target, memberName, tupleItemNumber, typeMapping.Key, typeMapping.Value ); + this.SetKnownType(target, memberName, tupleItemNumber, typeMapping.Key, typeMapping.Value); } break; } case PolymorphismType.RuntimeType: { - this.SetRuntimeType( target, memberName, tupleItemNumber, defaultSchema.TypeVerifier ); + this.SetRuntimeType(target, memberName, tupleItemNumber, defaultSchema.TypeVerifier); break; } } } - private void SetKnownType( PolymorphismTarget target, string memberName, int tupleItemNumber, string typeCode, Type bindingType ) + private void SetKnownType(PolymorphismTarget target, string memberName, int tupleItemNumber, string typeCode, Type bindingType) { - if ( this._useTypeEmbedding ) + if (this._useTypeEmbedding) { throw new SerializationException( - GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage( target, memberName, tupleItemNumber ) + GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage(target, memberName, tupleItemNumber) ); } try { - this._knownTypeMapping.Add( typeCode, bindingType ); + this._knownTypeMapping.Add(typeCode, bindingType); this.TypeVerifier = DefaultTypeVerfiier; } - catch ( ArgumentException ) + catch (ArgumentException) { throw new SerializationException( - GetCannotDuplicateKnownTypeCodeErrorMessage( target, typeCode, memberName, tupleItemNumber ) + GetCannotDuplicateKnownTypeCodeErrorMessage(target, typeCode, memberName, tupleItemNumber) ); } } - private void SetRuntimeType( PolymorphismTarget target, string memberName, int tupleItemNumber, Func typeVerifier ) + private void SetRuntimeType(PolymorphismTarget target, string memberName, int tupleItemNumber, Func typeVerifier) { - if ( this._knownTypeMapping.Count > 0 ) + if (this._knownTypeMapping.Count > 0) { throw new SerializationException( - GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage( target, memberName, tupleItemNumber ) + GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage(target, memberName, tupleItemNumber) ); } this.TypeVerifier = typeVerifier; this._useTypeEmbedding = true; } - private static string GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage( PolymorphismTarget target, string memberName, int? tupleItemNumber ) + private static string GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage(PolymorphismTarget target, string memberName, int? tupleItemNumber) { - switch ( target ) + switch (target) { case PolymorphismTarget.CollectionItem: { @@ -621,8 +668,8 @@ private static string GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage( Polym String.Format( CultureInfo.CurrentCulture, "Cannot specify '{0}' and '{1}' simultaneously to collection items of member '{2}' itself.", - typeof( MessagePackRuntimeCollectionItemTypeAttribute ), - typeof( MessagePackKnownCollectionItemTypeAttribute ), + typeof(MessagePackRuntimeCollectionItemTypeAttribute), + typeof(MessagePackKnownCollectionItemTypeAttribute), memberName ); } @@ -632,8 +679,8 @@ private static string GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage( Polym String.Format( CultureInfo.CurrentCulture, "Cannot specify '{0}' and '{1}' simultaneously to dictionary keys of member '{2}' itself.", - typeof( MessagePackRuntimeDictionaryKeyTypeAttribute ), - typeof( MessagePackKnownDictionaryKeyTypeAttribute ), + typeof(MessagePackRuntimeDictionaryKeyTypeAttribute), + typeof(MessagePackKnownDictionaryKeyTypeAttribute), memberName ); } @@ -643,8 +690,8 @@ private static string GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage( Polym String.Format( CultureInfo.CurrentCulture, "Cannot specify '{0}' and '{1}' simultaneously to #{2} item of tuple type member '{3}' itself.", - typeof( MessagePackRuntimeTupleItemTypeAttribute ), - typeof( MessagePackKnownTupleItemTypeAttribute ), + typeof(MessagePackRuntimeTupleItemTypeAttribute), + typeof(MessagePackKnownTupleItemTypeAttribute), tupleItemNumber, memberName ); @@ -655,17 +702,17 @@ private static string GetCannotSpecifyKnownTypeAndRuntimeTypeErrorMessage( Polym String.Format( CultureInfo.CurrentCulture, "Cannot specify '{0}' and '{1}' simultaneously to member '{2}' itself.", - typeof( MessagePackRuntimeTypeAttribute ), - typeof( MessagePackKnownTypeAttribute ), + typeof(MessagePackRuntimeTypeAttribute), + typeof(MessagePackKnownTypeAttribute), memberName ); } } } - private static string GetCannotDuplicateKnownTypeCodeErrorMessage( PolymorphismTarget target, string typeCode, string memberName, int tupleItemNumber ) + private static string GetCannotDuplicateKnownTypeCodeErrorMessage(PolymorphismTarget target, string typeCode, string memberName, int tupleItemNumber) { - switch ( target ) + switch (target) { case PolymorphismTarget.CollectionItem: { @@ -673,7 +720,7 @@ private static string GetCannotDuplicateKnownTypeCodeErrorMessage( PolymorphismT String.Format( CultureInfo.CurrentCulture, "Cannot specify multiple types for ext-type code '{0}' for collection items of member '{1}'.", - StringEscape.ForDisplay( typeCode ), + StringEscape.ForDisplay(typeCode), memberName ); } @@ -683,7 +730,7 @@ private static string GetCannotDuplicateKnownTypeCodeErrorMessage( PolymorphismT String.Format( CultureInfo.CurrentCulture, "Cannot specify multiple types for ext-type code '{0}' for dictionary keys of member '{1}'.", - StringEscape.ForDisplay( typeCode ), + StringEscape.ForDisplay(typeCode), memberName ); } @@ -693,7 +740,7 @@ private static string GetCannotDuplicateKnownTypeCodeErrorMessage( PolymorphismT String.Format( CultureInfo.CurrentCulture, "Cannot specify multiple types for ext-type code '{0}' for #{1} item of tuple type member '{2}'.", - StringEscape.ForDisplay( typeCode ), + StringEscape.ForDisplay(typeCode), tupleItemNumber, memberName ); @@ -704,57 +751,57 @@ private static string GetCannotDuplicateKnownTypeCodeErrorMessage( PolymorphismT String.Format( CultureInfo.CurrentCulture, "Cannot specify multiple types for ext-type code '{0}' for member '{1}'.", - StringEscape.ForDisplay( typeCode ), + StringEscape.ForDisplay(typeCode), memberName ); } } } - private static Func GetVerifier( IPolymorphicRuntimeTypeAttribute attribute ) + private static Func GetVerifier(IPolymorphicRuntimeTypeAttribute attribute) { - if ( attribute.VerifierType == null ) + if (attribute.VerifierType == null) { // Use default. return DefaultTypeVerfiier; } - if ( String.IsNullOrEmpty( attribute.VerifierMethodName ) ) + if (String.IsNullOrEmpty(attribute.VerifierMethodName)) { - throw new SerializationException( "VerifierMethodName cannot be null nor empty if VerifierType is specified." ); + throw new SerializationException("VerifierMethodName cannot be null nor empty if VerifierType is specified."); } // Explore [static] bool X(PolymorphicTypeVerificationContext) - var method = attribute.VerifierType.GetRuntimeMethods().SingleOrDefault( m => IsVerificationMethod( m, attribute.VerifierMethodName ) ); - if ( method == null ) + var method = attribute.VerifierType.GetRuntimeMethods().SingleOrDefault(m => IsVerificationMethod(m, attribute.VerifierMethodName)); + if (method == null) { - throw new SerializationException( String.Format( CultureInfo.CurrentCulture, "A public static or instance method named '{0}' with single parameter typed PolymorphicTypeVerificationContext in type '{1}'.", attribute.VerifierMethodName, attribute.VerifierMethodName ) ); + throw new SerializationException(String.Format(CultureInfo.CurrentCulture, "A public static or instance method named '{0}' with single parameter typed PolymorphicTypeVerificationContext in type '{1}'.", attribute.VerifierMethodName, attribute.VerifierMethodName)); } - if ( method.IsStatic ) + if (method.IsStatic) { - return method.CreateDelegate( typeof( Func ) ) as Func; + return method.CreateDelegate(typeof(Func)) as Func; } else { - return method.CreateDelegate( typeof( Func ), Activator.CreateInstance( attribute.VerifierType ) ) as Func; + return method.CreateDelegate(typeof(Func), Activator.CreateInstance(attribute.VerifierType)) as Func; } } - private static bool IsVerificationMethod( MethodInfo method, string name ) + private static bool IsVerificationMethod(MethodInfo method, string name) { - if ( method.ReturnType != typeof( bool ) ) + if (method.ReturnType != typeof(bool)) { return false; } - if ( method.Name != name ) + if (method.Name != name) { return false; } var parameters = method.GetParameters(); - return parameters.Length == 1 && parameters[ 0 ].ParameterType.IsAssignableFrom( typeof( PolymorphicTypeVerificationContext ) ); + return parameters.Length == 1 && parameters[0].ParameterType.IsAssignableFrom(typeof(PolymorphicTypeVerificationContext)); } } } diff --git a/src/MsgPack/Serialization/ReflectionExtensions.CollectionTraits.cs b/src/MsgPack/Serialization/ReflectionExtensions.CollectionTraits.cs index 7ab397a1a..c4c87ff9f 100644 --- a/src/MsgPack/Serialization/ReflectionExtensions.CollectionTraits.cs +++ b/src/MsgPack/Serialization/ReflectionExtensions.CollectionTraits.cs @@ -1,29 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2016 FUJIWARA, Yusuke and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Contributors: -// Samuel Cragg -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; using System.Collections; @@ -40,94 +17,101 @@ namespace MsgPack.Serialization { - partial class ReflectionExtensions + internal partial class ReflectionExtensions { - internal static CollectionTraits GetCollectionTraits( this Type source, CollectionTraitOptions options, bool allowNonCollectionEnumerableTypes ) + private static readonly Type[] WithCapacityConstructorParameterTypes = new[] { typeof(int) }; + private static readonly (ConstructorInfo? WithCapacity, ConstructorInfo? Default) ArrayListConstructors = + (typeof(ArrayList).GetConstructor(WithCapacityConstructorParameterTypes), + typeof(ArrayList).GetConstructor(ReflectionAbstractions.EmptyTypes)); + private static readonly (ConstructorInfo? WithCapacity, ConstructorInfo? Default) HashtableConstructors = + (typeof(Hashtable).GetConstructor(WithCapacityConstructorParameterTypes), + typeof(Hashtable).GetConstructor(ReflectionAbstractions.EmptyTypes)); + + public static CollectionTraits GetCollectionTraits(this Type source, CollectionTraitOptions options, bool allowNonCollectionEnumerableTypes) { #if DEBUG - Contract.Assert( !source.GetContainsGenericParameters(), "!source.GetContainsGenericParameters()" ); + Contract.Assert(!source.GetContainsGenericParameters(), "!source.GetContainsGenericParameters()"); #endif // DEBUG /* * SPEC - * If the object has single public method TEnumerator GetEnumerator() ( where TEnumerator implements IEnumerator), - * then the object is considered as the collection of TItem. - * When the object is considered as the collection of TItem, TItem is KeyValuePair, - * and the object implements IDictionary, then the object is considered as dictionary of TKey and TValue. - * Else, if the object has single public method IEnumerator GetEnumerator(), then the object is considered as the collection of Object. - * When it also implements IDictionary, however, it is considered dictionary of Object and Object. - * Otherwise, that means it implements multiple collection interface, is following. - * First, if the object implements IDictionary, then it is considered as MPO dictionary. - * Second, if the object implements IEnumerable, then it is considered as MPO dictionary. - * Third, if the object implement SINGLE IDictionary and multiple IEnumerable, then it is considered as dictionary of TKey and TValue. - * Fourth, the object is considered as UNSERIALIZABLE member. This behavior similer to DataContract serialization behavor - * (see http://msdn.microsoft.com/en-us/library/aa347850.aspx ). + * 1. If the object has single public method TEnumerator GetEnumerator() (where TEnumerator implements IEnumerator), + * then the object is considered as the collection of TItem. + * 1-1. If the object is considered as the collection of TItem, TItem is KeyValuePair, + * and the object implements IDictionary, + * then the object is considered as dictionary of TKey and TValue. + * 1-2. Else, if the object has single public method IEnumerator GetEnumerator(), + * then the object is considered as the collection of Object. + * 1-2-1. If it also implements IDictionary, + * then it is considered dictionary of Object and Object. + * 1-2-2. Otherwise, that means it implements multiple collection interface. + * 1-2-2-1. First, if the object implements IDictionary, then it is considered as MPO dictionary. + * 1-2-2-2. Second, if the object implements IEnumerable, then it is considered as MPO collection. + * 1-2-2-3. Third, if the object implement SINGLE IDictionary and multiple IEnumerable, then it is considered as dictionary of TKey and TValue. + * 1-2-2-4. Fourth, the object is considered as UNSERIALIZABLE member. This behavior similer to DataContract serialization behavor + * (see http://msdn.microsoft.com/en-us/library/aa347850.aspx ). */ - if ( !source.IsAssignableTo( typeof( IEnumerable ) ) ) + if (!source.IsAssignableTo(typeof(IEnumerable))) { return CollectionTraits.NotCollection; } - if ( source.IsArray ) + if (source.IsArray) { return new CollectionTraits( CollectionDetailedKind.Array, + constructorWithCapacity: null, // Never used for array. + defaultConstructor: null, // Never used for array. source.GetElementType(), - null, // Never used for array. - null, // Never used for array. - null // Never used for array. + getEnumeratorMethod: null, // Never used for array. + addMethod: null, // Never used for array. + countPropertyGetter: null // Never used for array. ); } // If the type is an interface then a concrete collection has to be // made for it (if the interface is a collection type), therefore, // ignore the check for an add method - if ( !source.GetIsInterface() && allowNonCollectionEnumerableTypes ) + if (!source.GetIsInterface() && allowNonCollectionEnumerableTypes) { options = options | CollectionTraitOptions.AllowNonCollectionEnumerableTypes; } - MethodInfo getEnumerator = source.GetMethod( "GetEnumerator", ReflectionAbstractions.EmptyTypes ); - if ( getEnumerator != null && getEnumerator.ReturnType.IsAssignableTo( typeof( IEnumerator ) ) ) + var getEnumerator = source.GetMethod("GetEnumerator", ReflectionAbstractions.EmptyTypes); + if (getEnumerator != null && getEnumerator.ReturnType.IsAssignableTo(typeof(IEnumerator))) { // If public 'GetEnumerator' is found, it is primary collection traits. CollectionTraits result; - if ( TryCreateCollectionTraitsForHasGetEnumeratorType( source, options, getEnumerator, out result ) ) + if (TryCreateCollectionTraitsForHasGetEnumeratorType(source, options, getEnumerator, out result)) { return result; } } - GenericCollectionTypes genericTypes = new GenericCollectionTypes(); - Type ienumerable = null; - Type icollection = null; - Type ilist = null; - Type idictionary = null; + var genericTypes = new GenericCollectionTypes(); + Type? ienumerable = null; + Type? icollection = null; + Type? ilist = null; + Type? idictionary = null; - var sourceInterfaces = source.FindInterfaces( FilterCollectionType, null ); - if ( source.GetIsInterface() && FilterCollectionType( source, null ) ) + var sourceInterfaces = source.FindInterfaces(FilterCollectionType, null); + if (source.GetIsInterface() && FilterCollectionType(source, null)) { var originalSourceInterfaces = sourceInterfaces.ToArray(); - var concatenatedSourceInterface = new Type[ originalSourceInterfaces.Length + 1 ]; - concatenatedSourceInterface[ 0 ] = source; - for ( int i = 0; i < originalSourceInterfaces.Length; i++ ) + var concatenatedSourceInterface = new Type[originalSourceInterfaces.Length + 1]; + concatenatedSourceInterface[0] = source; + for (int i = 0; i < originalSourceInterfaces.Length; i++) { - concatenatedSourceInterface[ i + 1 ] = originalSourceInterfaces[ i ]; + concatenatedSourceInterface[i + 1] = originalSourceInterfaces[i]; } sourceInterfaces = concatenatedSourceInterface; } - foreach ( var type in sourceInterfaces ) + foreach (var type in sourceInterfaces) { - CollectionTraits result; - if ( TryCreateGenericCollectionTraits( source, type, options, out result ) ) - { - return result; - } - - if ( !DetermineCollectionInterfaces( + if (!DetermineCollectionInterfaces( type, ref genericTypes, ref idictionary, @@ -141,74 +125,88 @@ ref ienumerable } } - if ( genericTypes.IDictionaryT != null ) + if (genericTypes.IDictionaryT != null) { var genericArguments = genericTypes.IDictionaryT.GetGenericArguments(); - var elementType = typeof( KeyValuePair<,> ).MakeGenericType( genericArguments ); + var elementType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments); + var constructor = GetGenericDictionaryConstructor(source, genericArguments[0], genericArguments[1], options); - return + return new CollectionTraits( CollectionDetailedKind.GenericDictionary, + constructor.WithCapacity, + constructor.Default, elementType, - GetGetEnumeratorMethodFromElementType( source, elementType, options ), - GetAddMethod( source, genericArguments[ 0 ], genericArguments[ 1 ], options ), - GetCountGetterMethod( source, elementType, options ) + GetGetEnumeratorMethodFromElementType(source, elementType, options), + GetAddMethod(source, genericArguments[0], genericArguments[1], options), + GetCountGetterMethod(source, elementType, options) ); } -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - if ( genericTypes.IReadOnlyDictionaryT != null ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) + if (genericTypes.IReadOnlyDictionaryT != null) { - var elementType = typeof( KeyValuePair<,> ).MakeGenericType( genericTypes.IReadOnlyDictionaryT.GetGenericArguments() ); + var genericArguments = genericTypes.IReadOnlyDictionaryT.GetGenericArguments(); + var elementType = typeof(KeyValuePair<,>).MakeGenericType(genericArguments); + var constructor = GetGenericDictionaryConstructor(source, genericArguments[0], genericArguments[1], options); + return new CollectionTraits( CollectionDetailedKind.GenericReadOnlyDictionary, + constructor.WithCapacity, + constructor.Default, elementType, - GetGetEnumeratorMethodFromElementType( source, elementType, options ), - null, // add - GetCountGetterMethod( source, elementType, options ) + GetGetEnumeratorMethodFromElementType(source, elementType, options), + addMethod: null, // add + GetCountGetterMethod(source, elementType, options) ); } #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - if ( genericTypes.IEnumerableT != null ) + if (genericTypes.IEnumerableT != null) { CollectionTraits traits; - if ( TryCreateCollectionTraitsForIEnumerableT( source, genericTypes, options, null, out traits ) ) + if (TryCreateCollectionTraitsForIEnumerableT(source, genericTypes, options, null, out traits)) { return traits; } } - if ( idictionary != null ) + if (idictionary != null) { + var constructor = GetNonGenericDictionaryConstructor(source, options); return new CollectionTraits( CollectionDetailedKind.NonGenericDictionary, - typeof( object ), - GetGetEnumeratorMethodFromEnumerableType( source, idictionary, options ), - GetAddMethod( source, typeof( object ), typeof( object ), options ), - GetCountGetterMethod( source, typeof( object ), options ) + constructor.WithCapacity, + constructor.Default, + typeof(object), + GetGetEnumeratorMethodFromEnumerableType(source, idictionary, options), + GetAddMethod(source, typeof(object), typeof(object), options), + GetCountGetterMethod(source, typeof(object), options) ); } - if ( ienumerable != null ) + if (ienumerable != null) { - var addMethod = GetAddMethod( source, typeof( object ), options | CollectionTraitOptions.WithAddMethod ); - if ( addMethod != null || ( ( options & CollectionTraitOptions.AllowNonCollectionEnumerableTypes ) == 0 ) ) + var addMethod = GetAddMethod(source, typeof(object), options | CollectionTraitOptions.WithAddMethod); + if (addMethod != null || ((options & CollectionTraitOptions.AllowNonCollectionEnumerableTypes) == 0)) { + var constructor = GetNonGenericCollectionConstructor(source, options); // This should be appendable or unappendable collection return new CollectionTraits( - ( ilist != null ) + (ilist != null) ? CollectionDetailedKind.NonGenericList - : ( icollection != null ) + : (icollection != null) ? CollectionDetailedKind.NonGenericCollection : CollectionDetailedKind.NonGenericEnumerable, - typeof( object ), - GetGetEnumeratorMethodFromEnumerableType( source, ienumerable, options ), + constructor.WithCapacity, + constructor.Default, + typeof(object), + GetGetEnumeratorMethodFromEnumerableType(source, ienumerable, options), addMethod, - GetCountGetterMethod( source, typeof( object ), options ) + GetCountGetterMethod(source, typeof(object), options) ); } } @@ -220,52 +218,58 @@ private static bool TryCreateCollectionTraitsForIEnumerableT( Type source, GenericCollectionTypes genericTypes, CollectionTraitOptions options, - MethodInfo getMethod, + MethodInfo? getMethod, out CollectionTraits result ) { - var elementType = genericTypes.IEnumerableT.GetGenericArguments()[ 0 ]; - var addMethod = GetAddMethod( source, elementType, options ); - if ( addMethod == null && ( ( options & CollectionTraitOptions.AllowNonCollectionEnumerableTypes ) != 0 ) ) + var elementType = genericTypes.IEnumerableT.GetGenericArguments()[0]; + var addMethod = GetAddMethod(source, elementType, options); + if (addMethod == null && ((options & CollectionTraitOptions.AllowNonCollectionEnumerableTypes) != 0)) { - // This should be non collection object isntead of "unappendable" collection. - result = default( CollectionTraits ); + // This should be non collection object instead of "unappendable" collection. + result = default(CollectionTraits); return false; } CollectionDetailedKind kind = CollectionDetailedKind.GenericEnumerable; - if ( genericTypes.IListT != null ) + if (genericTypes.IListT != null) { kind = CollectionDetailedKind.GenericList; } #if !NET35 && !UNITY - else if ( genericTypes.ISetT != null ) + else if (genericTypes.ISetT != null) { kind = CollectionDetailedKind.GenericSet; } #endif // !NET35 && !UNITY - else if ( genericTypes.ICollectionT != null ) + else if (genericTypes.ICollectionT != null) { kind = CollectionDetailedKind.GenericCollection; } -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - else if ( genericTypes.IReadOnlyListT != null ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) + else if (genericTypes.IReadOnlyListT != null) { kind = CollectionDetailedKind.GenericReadOnlyList; } - else if ( genericTypes.IReadOnlyCollectionT != null ) + else if (genericTypes.IReadOnlyCollectionT != null) { kind = CollectionDetailedKind.GenericReadOnlyCollection; } #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - + + var constructor = + kind == CollectionDetailedKind.GenericSet ? + GetGenericSetConstructor(source, elementType, options) : + GetGenericCollectionConstructor(source, elementType, options); result = new CollectionTraits( kind, + constructor.WithCapacity, + constructor.Default, elementType, - getMethod ?? GetGetEnumeratorMethodFromElementType( source, elementType, options ), + getMethod ?? GetGetEnumeratorMethodFromElementType(source, elementType, options), addMethod, - GetCountGetterMethod( source, elementType, options ) + GetCountGetterMethod(source, elementType, options) ); return true; } @@ -277,50 +281,56 @@ private static bool TryCreateCollectionTraitsForHasGetEnumeratorType( out CollectionTraits result ) { - if ( source.Implements( typeof( IDictionary<,> ) ) -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - || source.Implements( typeof( IReadOnlyDictionary<,> ) ) + if (source.Implements(typeof(IDictionary<,>)) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) + || source.Implements(typeof(IReadOnlyDictionary<,>)) #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) ) { var ienumetaorT = getEnumerator.ReturnType.GetInterfaces() - .FirstOrDefault( @interface => - @interface.GetIsGenericType() && @interface.GetGenericTypeDefinition() == typeof( IEnumerator<> ) + .FirstOrDefault(@interface => + @interface.GetIsGenericType() && @interface.GetGenericTypeDefinition() == typeof(IEnumerator<>) ); - if ( ienumetaorT != null ) + if (ienumetaorT != null) { - var elementType = ienumetaorT.GetGenericArguments()[ 0 ]; + var elementType = ienumetaorT.GetGenericArguments()[0]; var elementTypeGenericArguments = elementType.GetGenericArguments(); + var constructor = GetGenericDictionaryConstructor(source, elementTypeGenericArguments[0], elementTypeGenericArguments[1], options); - result = + result = new CollectionTraits( -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - source.Implements( typeof( IDictionary<,> ) ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) + source.Implements(typeof(IDictionary<,>)) ? CollectionDetailedKind.GenericDictionary : CollectionDetailedKind.GenericReadOnlyDictionary, #else CollectionDetailedKind.GenericDictionary, #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) + constructor.WithCapacity, + constructor.Default, elementType, getEnumerator, - GetAddMethod( source, elementTypeGenericArguments[ 0 ], elementTypeGenericArguments [ 1 ], options ), - GetCountGetterMethod( source, elementType, options ) + GetAddMethod(source, elementTypeGenericArguments[0], elementTypeGenericArguments[1], options), + GetCountGetterMethod(source, elementType, options) ); return true; } } - if ( source.IsAssignableTo( typeof( IDictionary ) ) ) + if (source.IsAssignableTo(typeof(IDictionary))) { - result = + var constructor = GetNonGenericDictionaryConstructor(source, options); + result = new CollectionTraits( CollectionDetailedKind.NonGenericDictionary, - typeof( DictionaryEntry ), + constructor.WithCapacity, + constructor.Default, + typeof(DictionaryEntry), getEnumerator, - GetAddMethod( source, typeof( object ), typeof( object ), options ), - GetCountGetterMethod( source, typeof( object ), options ) + GetAddMethod(source, typeof(object), typeof(object), options), + GetCountGetterMethod(source, typeof(object), options) ); return true; @@ -329,154 +339,90 @@ out CollectionTraits result // Block to limit variable scope { var ienumetaorT = - IsIEnumeratorT( getEnumerator.ReturnType ) + IsIEnumeratorT(getEnumerator.ReturnType) ? getEnumerator.ReturnType - : getEnumerator.ReturnType.GetInterfaces().FirstOrDefault( IsIEnumeratorT ); + : getEnumerator.ReturnType.GetInterfaces().FirstOrDefault(IsIEnumeratorT); - if ( ienumetaorT != null ) + if (ienumetaorT != null) { // Get the open generic types once Type[] genericInterfaces = source.GetInterfaces() - .Where( i => i.GetIsGenericType() ) - .Select( i => i.GetGenericTypeDefinition() ) + .Where(i => i.GetIsGenericType()) + .Select(i => i.GetGenericTypeDefinition()) .ToArray(); var genericTypes = new GenericCollectionTypes(); genericTypes.IEnumerableT = ienumetaorT; - genericTypes.ICollectionT = genericInterfaces.FirstOrDefault( i => i == typeof(ICollection<>) ); - genericTypes.IListT = genericInterfaces.FirstOrDefault( i => i == typeof(IList<>) ); + genericTypes.ICollectionT = genericInterfaces.FirstOrDefault(i => i == typeof(ICollection<>)); + genericTypes.IListT = genericInterfaces.FirstOrDefault(i => i == typeof(IList<>)); #if !NET35 && !UNITY - genericTypes.ISetT = genericInterfaces.FirstOrDefault( i => i == typeof(ISet<>) ); + genericTypes.ISetT = genericInterfaces.FirstOrDefault(i => i == typeof(ISet<>)); #endif // !NET35 && !UNITY -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - genericTypes.IReadOnlyCollectionT = genericInterfaces.FirstOrDefault( i => i == typeof(IReadOnlyCollection<>) ); - genericTypes.IReadOnlyListT = genericInterfaces.FirstOrDefault( i => i == typeof(IReadOnlyList<>) ); +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) + genericTypes.IReadOnlyCollectionT = genericInterfaces.FirstOrDefault(i => i == typeof(IReadOnlyCollection<>)); + genericTypes.IReadOnlyListT = genericInterfaces.FirstOrDefault(i => i == typeof(IReadOnlyList<>)); #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - return TryCreateCollectionTraitsForIEnumerableT( source, genericTypes, options, getEnumerator, out result ); + return TryCreateCollectionTraitsForIEnumerableT(source, genericTypes, options, getEnumerator, out result); } } - result = default( CollectionTraits ); - return false; - } - - - private static bool TryCreateGenericCollectionTraits( Type source, Type type, CollectionTraitOptions options, out CollectionTraits result ) - { - if ( type == typeof( IDictionary ) -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - || type == typeof( IReadOnlyDictionary ) -#endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - ) - { - result = - new CollectionTraits( -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - ( source == typeof( IDictionary ) || source.Implements( typeof( IDictionary ) ) ) - ? CollectionDetailedKind.GenericDictionary - : CollectionDetailedKind.GenericReadOnlyDictionary, -#else - CollectionDetailedKind.GenericDictionary, -#endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - typeof( KeyValuePair ), - GetGetEnumeratorMethodFromEnumerableType( source, typeof( IEnumerable> ), options ), - GetAddMethod( source, typeof( MessagePackObject ), typeof( MessagePackObject ), options ), - GetCountGetterMethod( source, typeof( KeyValuePair ), options ) - ); - - return true; - } - - if ( type == typeof( IEnumerable ) ) - { - var addMethod = GetAddMethod( source, typeof( MessagePackObject ), options | CollectionTraitOptions.WithAddMethod ); - if ( addMethod != null ) - { - { - result = - new CollectionTraits( - ( source == typeof( IList ) || source.Implements( typeof( IList ) ) ) - ? CollectionDetailedKind.GenericList -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - : ( source == typeof( IReadOnlyList ) || source.Implements( typeof( IReadOnlyList ) ) ) - ? CollectionDetailedKind.GenericReadOnlyList -#endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) -#if !NET35 && !UNITY - : ( source == typeof( ISet ) || source.Implements( typeof( ISet ) ) ) - ? CollectionDetailedKind.GenericSet -#endif // !NET35 && !UNITY - : ( source == typeof( ICollection ) || - source.Implements( typeof( ICollection ) ) ) - ? CollectionDetailedKind.GenericCollection -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - : ( source == typeof( IReadOnlyCollection ) || source.Implements( typeof( IReadOnlyCollection ) ) ) - ? CollectionDetailedKind.GenericReadOnlyCollection -#endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - : CollectionDetailedKind.GenericEnumerable, - typeof( MessagePackObject ), - GetGetEnumeratorMethodFromEnumerableType( source, typeof( IEnumerable ), options ), - addMethod, - GetCountGetterMethod( source, typeof( MessagePackObject ), options ) - ); - - return true; - } - } - } - - result = default( CollectionTraits ); + result = default(CollectionTraits); return false; } private static bool DetermineCollectionInterfaces( Type type, ref GenericCollectionTypes genericTypes, - ref Type idictionary, - ref Type ilist, - ref Type icollection, - ref Type ienumerable + ref Type? idictionary, + ref Type? ilist, + ref Type? icollection, + ref Type? ienumerable ) { - if ( type.GetIsGenericType() ) + if (type.GetIsGenericType()) { var genericTypeDefinition = type.GetGenericTypeDefinition(); - if ( genericTypeDefinition == typeof( IDictionary<,> ) ) + if (genericTypeDefinition == typeof(IDictionary<,>)) { - if ( genericTypes.IDictionaryT != null ) + if (genericTypes.IDictionaryT != null) { + // multiple candidates return false; } genericTypes.IDictionaryT = type; } -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - else if ( genericTypeDefinition == typeof( IReadOnlyDictionary<,> ) ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) + else if (genericTypeDefinition == typeof(IReadOnlyDictionary<,>)) { - if ( genericTypes.IReadOnlyDictionaryT != null ) + if (genericTypes.IReadOnlyDictionaryT != null) { + // multiple candidates return false; } genericTypes.IReadOnlyDictionaryT = type; } #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - else if ( genericTypeDefinition == typeof( IList<> ) ) + else if (genericTypeDefinition == typeof(IList<>)) { - if ( genericTypes.IListT != null ) + if (genericTypes.IListT != null) { + // multiple candidates return false; } genericTypes.IListT = type; } -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - else if ( genericTypeDefinition == typeof( IReadOnlyList<> ) ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) + else if (genericTypeDefinition == typeof(IReadOnlyList<>)) { - if ( genericTypes.IReadOnlyListT != null ) + if (genericTypes.IReadOnlyListT != null) { + // multiple candidates return false; } @@ -484,40 +430,44 @@ ref Type ienumerable } #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) #if !NET35 && !UNITY - else if ( genericTypeDefinition == typeof( ISet<> ) ) + else if (genericTypeDefinition == typeof(ISet<>)) { - if ( genericTypes.ISetT != null ) + if (genericTypes.ISetT != null) { + // multiple candidates return false; } genericTypes.ISetT = type; } #endif // !NET35 && !UNITY - else if ( genericTypeDefinition == typeof( ICollection<> ) ) + else if (genericTypeDefinition == typeof(ICollection<>)) { - if ( genericTypes.ICollectionT != null ) + if (genericTypes.ICollectionT != null) { + // multiple candidates return false; } genericTypes.ICollectionT = type; } -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - else if ( genericTypeDefinition == typeof( IReadOnlyCollection<> ) ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) + else if (genericTypeDefinition == typeof(IReadOnlyCollection<>)) { - if ( genericTypes.IReadOnlyCollectionT != null ) + if (genericTypes.IReadOnlyCollectionT != null) { + // multiple candidates return false; } genericTypes.IReadOnlyCollectionT = type; } #endif // !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) - else if ( genericTypeDefinition == typeof( IEnumerable<> ) ) + else if (genericTypeDefinition == typeof(IEnumerable<>)) { - if ( genericTypes.IEnumerableT != null ) + if (genericTypes.IEnumerableT != null) { + // multiple candidates return false; } @@ -526,19 +476,19 @@ ref Type ienumerable } else { - if ( type == typeof( IDictionary ) ) + if (type == typeof(IDictionary)) { idictionary = type; } - else if ( type == typeof( IList ) ) + else if (type == typeof(IList)) { ilist = type; } - else if ( type == typeof( ICollection ) ) + else if (type == typeof(ICollection)) { icollection = type; } - else if ( type == typeof( IEnumerable ) ) + else if (type == typeof(IEnumerable)) { ienumerable = type; } @@ -547,46 +497,46 @@ ref Type ienumerable return true; } - private static MethodInfo GetGetEnumeratorMethodFromElementType( Type targetType, Type elementType, CollectionTraitOptions options ) + private static MethodInfo? GetGetEnumeratorMethodFromElementType(Type targetType, Type elementType, CollectionTraitOptions options) { - if ( ( options | CollectionTraitOptions.WithGetEnumeratorMethod ) == 0 ) + if ((options | CollectionTraitOptions.WithGetEnumeratorMethod) == 0) { return null; } - return FindInterfaceMethod( targetType, typeof( IEnumerable<> ).MakeGenericType( elementType ), "GetEnumerator", ReflectionAbstractions.EmptyTypes ); + return FindInterfaceMethod(targetType, typeof(IEnumerable<>).MakeGenericType(elementType), "GetEnumerator", ReflectionAbstractions.EmptyTypes); } - private static MethodInfo GetGetEnumeratorMethodFromEnumerableType( Type targetType, Type enumerableType, CollectionTraitOptions options ) + private static MethodInfo? GetGetEnumeratorMethodFromEnumerableType(Type targetType, Type enumerableType, CollectionTraitOptions options) { - if ( ( options | CollectionTraitOptions.WithGetEnumeratorMethod ) == 0 ) + if ((options | CollectionTraitOptions.WithGetEnumeratorMethod) == 0) { return null; } - return FindInterfaceMethod( targetType, enumerableType, "GetEnumerator", ReflectionAbstractions.EmptyTypes ); + return FindInterfaceMethod(targetType, enumerableType, "GetEnumerator", ReflectionAbstractions.EmptyTypes); } - private static MethodInfo FindInterfaceMethod( Type targetType, Type interfaceType, string name, Type[] parameterTypes ) + private static MethodInfo? FindInterfaceMethod(Type targetType, Type interfaceType, string name, Type[] parameterTypes) { - if ( targetType.GetIsInterface() ) + if (targetType.GetIsInterface()) { - return targetType.FindInterfaces( ( type, _ ) => type == interfaceType, null ).Single().GetMethod( name, parameterTypes ); + return targetType.FindInterfaces((type, _) => type == interfaceType, null).Single().GetMethod(name, parameterTypes); } - var map = targetType.GetInterfaceMap( interfaceType ); + var map = targetType.GetInterfaceMap(interfaceType); #if !SILVERLIGHT || WINDOWS_PHONE - int index = Array.FindIndex( map.InterfaceMethods, method => method.Name == name && method.GetParameters().Select( p => p.ParameterType ).SequenceEqual( parameterTypes ) ); + int index = Array.FindIndex(map.InterfaceMethods, method => method.Name == name && method.GetParameters().Select(p => p.ParameterType).SequenceEqual(parameterTypes)); #else int index = map.InterfaceMethods.FindIndex( method => method.Name == name && method.GetParameters().Select( p => p.ParameterType ).SequenceEqual( parameterTypes ) ); #endif - if ( index < 0 ) + if (index < 0) { #if DEBUG #if !NET35 && !UNITY - Contract.Assert( false, interfaceType + "::" + name + "(" + String.Join( ", ", parameterTypes ) + ") is not found in " + targetType ); + Contract.Assert(false, interfaceType + "::" + name + "(" + String.Join(", ", parameterTypes) + ") is not found in " + targetType); #else Contract.Assert( false, interfaceType + "::" + name + "(" + String.Join( ", ", parameterTypes.Select( t => t.ToString() ).ToArray() ) + ") is not found in " + targetType ); #endif // !NET35 @@ -595,50 +545,50 @@ private static MethodInfo FindInterfaceMethod( Type targetType, Type interfaceTy return null; } - return map.TargetMethods[ index ]; + return map.TargetMethods[index]; } - private static MethodInfo GetAddMethod( Type targetType, Type argumentType, CollectionTraitOptions options ) + private static MethodInfo? GetAddMethod(Type targetType, Type argumentType, CollectionTraitOptions options) { - if ( ( options | CollectionTraitOptions.WithAddMethod ) == 0 ) + if ((options | CollectionTraitOptions.WithAddMethod) == 0) { return null; } var argumentTypes = new[] { argumentType }; - var typedAdd = targetType.GetMethod( "Add", argumentTypes ); - if ( typedAdd != null ) + var typedAdd = targetType.GetMethod("Add", argumentTypes); + if (typedAdd != null) { return typedAdd; } - var icollectionT = typeof( ICollection<> ).MakeGenericType( argumentType ); - if ( targetType.IsAssignableTo( icollectionT ) ) + var icollectionT = typeof(ICollection<>).MakeGenericType(argumentType); + if (targetType.IsAssignableTo(icollectionT)) { - return icollectionT.GetMethod( "Add", argumentTypes ); + return icollectionT.GetMethod("Add", argumentTypes); } // It ensures .NET Framework and .NET Core compatibility and provides "natural" feel. - var objectAdd = targetType.GetMethod( "Add", ObjectAddParameterTypes ); - if ( objectAdd != null ) + var objectAdd = targetType.GetMethod("Add", ObjectAddParameterTypes); + if (objectAdd != null) { return objectAdd; } - if ( targetType.IsAssignableTo( typeof( IList ) ) ) + if (targetType.IsAssignableTo(typeof(IList))) { - return typeof( IList ).GetMethod( "Add", ObjectAddParameterTypes ); + return typeof(IList).GetMethod("Add", ObjectAddParameterTypes); } return null; } // ReSharper disable UnusedParameter.Local - [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "targetType", Justification = "For Unity compatibility" )] - [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "elementType", Justification = "For Unity compatibility" )] - [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "options", Justification = "For Unity compatibility" )] - private static MethodInfo GetCountGetterMethod( Type targetType, Type elementType, CollectionTraitOptions options ) - // ReSharper restore UnusedParameter.Local + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "targetType", Justification = "For Unity compatibility")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "elementType", Justification = "For Unity compatibility")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "options", Justification = "For Unity compatibility")] + private static MethodInfo? GetCountGetterMethod(Type targetType, Type elementType, CollectionTraitOptions options) + // ReSharper restore UnusedParameter.Local { #if !UNITY // get_Count is not used other than Unity. @@ -661,7 +611,7 @@ private static MethodInfo GetCountGetterMethod( Type targetType, Type elementTyp return icollectionT.GetProperty( "Count" ).GetGetMethod(); } -#if !NET35 && !UNITY && !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) +#if !NET35 && !UNITY && !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) var ireadOnlyCollectionT = typeof( IReadOnlyCollection<> ).MakeGenericType( elementType ); if ( targetType.IsAssignableTo( ireadOnlyCollectionT ) ) { @@ -678,30 +628,30 @@ private static MethodInfo GetCountGetterMethod( Type targetType, Type elementTyp #endif // !UNITY } - private static MethodInfo GetAddMethod( Type targetType, Type keyType, Type valueType, CollectionTraitOptions options ) + private static MethodInfo? GetAddMethod(Type targetType, Type keyType, Type valueType, CollectionTraitOptions options) { - if ( ( options | CollectionTraitOptions.WithAddMethod ) == 0 ) + if ((options | CollectionTraitOptions.WithAddMethod) == 0) { return null; } var argumentTypes = new[] { keyType, valueType }; - var result = targetType.GetMethod( "Add", argumentTypes ); - if ( result != null ) + var result = targetType.GetMethod("Add", argumentTypes); + if (result != null) { return result; } - return typeof( IDictionary<,> ).MakeGenericType( argumentTypes ).GetMethod( "Add", argumentTypes ); + return typeof(IDictionary<,>).MakeGenericType(argumentTypes).GetMethod("Add", argumentTypes); } - private static bool FilterCollectionType( Type type, object filterCriteria ) + private static bool FilterCollectionType(Type type, object? filterCriteria) { #if !NETSTANDARD1_1 && !NETSTANDARD1_3 #if DEBUG - Contract.Assert( type.GetIsInterface(), "type.IsInterface" ); + Contract.Assert(type.GetIsInterface(), "type.IsInterface"); #endif // DEBUG - return type.GetAssembly().Equals( typeof( Array ).GetAssembly() ) && ( type.Namespace == "System.Collections" || type.Namespace == "System.Collections.Generic" ); + return type.GetAssembly().Equals(typeof(Array).GetAssembly()) && (type.Namespace == "System.Collections" || type.Namespace == "System.Collections.Generic"); #else var typeInfo = type.GetTypeInfo(); Contract.Assert( typeInfo.IsInterface ); @@ -709,9 +659,9 @@ private static bool FilterCollectionType( Type type, object filterCriteria ) #endif // !NETSTANDARD1_1 && !NETSTANDARD1_3 } - private static bool IsIEnumeratorT( Type @interface ) + private static bool IsIEnumeratorT(Type @interface) { - return @interface.GetIsGenericType() && @interface.GetGenericTypeDefinition() == typeof( IEnumerator<> ); + return @interface.GetIsGenericType() && @interface.GetGenericTypeDefinition() == typeof(IEnumerator<>); } #if WINDOWS_PHONE public static IEnumerable FindInterfaces( this Type source, Func filter, object criterion ) @@ -726,11 +676,9 @@ public static IEnumerable FindInterfaces( this Type source, Func a.ReturnType != typeof( void ) ).All( a => a.IsPublic ); -#else - return - ( asProperty.GetMethod == null || asProperty.GetMethod.IsPublic ); -#endif // !NETSTANDARD1_1 && !NETSTANDARD1_3 + return asProperty.GetAccessors(true).Where(a => a.ReturnType != typeof(void)).All(a => a.IsPublic); } - else if ( ( asField = source as FieldInfo ) != null ) + else if (source is FieldInfo asField) { return asField.IsPublic; } - else if ( ( asMethod = source as MethodBase ) != null ) + else if (source is MethodBase asMethod) { return asMethod.IsPublic; } -#if !NETSTANDARD1_1 && !NETSTANDARD1_3 - else if ( ( asType = source as Type ) != null ) + else if (source is Type asType) { return asType.IsPublic; } -#endif // !NETSTANDARD1_1 && !NETSTANDARD1_3 else { - throw new NotSupportedException( source.GetType() + " is not supported." ); + throw new NotSupportedException(source.GetType() + " is not supported."); + } + } + + private static (ConstructorInfo? WithCapacity, ConstructorInfo? Default) GetGenericCollectionConstructor(Type source, Type elementType, CollectionTraitOptions options) + { + if ((options & CollectionTraitOptions.WithConstructor) == 0) + { + // skip + return (null, null); } + + if (source.GetIsInterface()) + { + var listOfT = typeof(List<>).MakeGenericType(elementType); + return + (listOfT.GetConstructor(WithCapacityConstructorParameterTypes), + listOfT.GetConstructor(ReflectionAbstractions.EmptyTypes)); + } + + return GetConcreteCollectionConstructor(source); + } + + private static (ConstructorInfo? WithCapacity, ConstructorInfo? Default) GetGenericSetConstructor(Type source, Type elementType, CollectionTraitOptions options) + { + if ((options & CollectionTraitOptions.WithConstructor) == 0) + { + // skip + return (null, null); + } + + if (source.GetIsInterface()) + { + var hashSetOfT = typeof(HashSet<>).MakeGenericType(elementType); + return + (hashSetOfT.GetConstructor(WithCapacityConstructorParameterTypes), + hashSetOfT.GetConstructor(ReflectionAbstractions.EmptyTypes)); + } + + return GetConcreteCollectionConstructor(source); + } + + private static (ConstructorInfo? WithCapacity, ConstructorInfo? Default) GetGenericDictionaryConstructor(Type source, Type keyType, Type valueType, CollectionTraitOptions options) + { + if ((options & CollectionTraitOptions.WithConstructor) == 0) + { + // skip + return (null, null); + } + + if (source.GetIsInterface()) + { + var dictionaryOfTkeyAndTvalue = typeof(Dictionary<,>).MakeGenericType(keyType, valueType); + return + (dictionaryOfTkeyAndTvalue.GetConstructor(WithCapacityConstructorParameterTypes), + dictionaryOfTkeyAndTvalue.GetConstructor(ReflectionAbstractions.EmptyTypes)); + } + + return GetConcreteCollectionConstructor(source); + } + + private static (ConstructorInfo? WithCapacity, ConstructorInfo? Default) GetNonGenericCollectionConstructor(Type source, CollectionTraitOptions options) + { + if ((options & CollectionTraitOptions.WithConstructor) == 0) + { + // skip + return (null, null); + } + + if (source.GetIsInterface()) + { + return ArrayListConstructors; + } + + return GetConcreteCollectionConstructor(source); + } + + private static (ConstructorInfo? WithCapacity, ConstructorInfo? Default) GetNonGenericDictionaryConstructor(Type source, CollectionTraitOptions options) + { + if ((options & CollectionTraitOptions.WithConstructor) == 0) + { + // skip + return (null, null); + } + + if (source.GetIsInterface()) + { + return HashtableConstructors; + } + + return GetConcreteCollectionConstructor(source); + } + + private static (ConstructorInfo? WithCapacity, ConstructorInfo? Default) GetConcreteCollectionConstructor(Type source) + { + var @default = source.GetConstructor(ReflectionAbstractions.EmptyTypes); + var withCapacity = source.GetConstructor(BindingFlags.Public | BindingFlags.Instance, binder: null, WithCapacityConstructorParameterTypes, null); + if (withCapacity != null) + { + var shouldBeCapacity = withCapacity.GetParameters()[0].Name; + if (!String.Equals(shouldBeCapacity, "capacity", StringComparison.OrdinalIgnoreCase) + && !String.Equals(shouldBeCapacity, "initialCapacity", StringComparison.OrdinalIgnoreCase)) + { + shouldBeCapacity = null; + } + } + + return (withCapacity, @default); } private struct GenericCollectionTypes @@ -817,7 +856,7 @@ private struct GenericCollectionTypes #if !NET35 && !UNITY internal Type ISetT; -#if !NET40 && !( SILVERLIGHT && !WINDOWS_PHONE ) +#if !NET40 && !(SILVERLIGHT && !WINDOWS_PHONE) internal Type IReadOnlyCollectionT; internal Type IReadOnlyListT; internal Type IReadOnlyDictionaryT; diff --git a/src/MsgPack/Serialization/ReflectionExtensions.ConstructorDelegate.cs b/src/MsgPack/Serialization/ReflectionExtensions.ConstructorDelegate.cs index edb877716..7a48f920f 100644 --- a/src/MsgPack/Serialization/ReflectionExtensions.ConstructorDelegate.cs +++ b/src/MsgPack/Serialization/ReflectionExtensions.ConstructorDelegate.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2016 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; using System.Reflection; @@ -26,42 +10,42 @@ namespace MsgPack.Serialization { - partial class ReflectionExtensions + internal partial class ReflectionExtensions { - public static TDelegate CreateConstructorDelegate( this ConstructorInfo constructor ) + public static TDelegate CreateConstructorDelegate(this ConstructorInfo constructor) { - return ( TDelegate )CreateDelegate( typeof( TDelegate ), constructor.DeclaringType, constructor, constructor.GetParameterTypes() ); + return (TDelegate)CreateDelegate(typeof(TDelegate), constructor.DeclaringType, constructor, constructor.GetParameterTypes()); } - private static object CreateDelegate( Type delegateType, Type targetType, ConstructorInfo constructor, Type[] parameterTypes ) + private static object CreateDelegate(Type delegateType, Type targetType, ConstructorInfo constructor, Type[] parameterTypes) { var dynamicMethod = #if !SILVERLIGHT - new DynamicMethod( "Create" + targetType.Name, targetType, parameterTypes, restrictedSkipVisibility: true ); + new DynamicMethod("Create" + targetType.Name, targetType, parameterTypes, restrictedSkipVisibility: true); #else new DynamicMethod( "Create" + targetType.Name, targetType, parameterTypes ); #endif // !SILVERLIGHT - var il = new TracingILGenerator( dynamicMethod, NullTextWriter.Instance, isDebuggable: false ); - if ( constructor == null ) + var il = new TracingILGenerator(dynamicMethod, NullTextWriter.Instance, isDebuggable: false); + if (constructor == null) { // Value type's init. - il.DeclareLocal( targetType ); - il.EmitAnyLdloca( 0 ); - il.EmitInitobj( targetType ); - il.EmitAnyLdloc( 0 ); + il.DeclareLocal(targetType); + il.EmitAnyLdloca(0); + il.EmitInitobj(targetType); + il.EmitAnyLdloc(0); } else { - for ( var i = 0; i < parameterTypes.Length; i++ ) + for (var i = 0; i < parameterTypes.Length; i++) { - il.EmitAnyLdarg( i ); + il.EmitAnyLdarg(i); } - il.EmitNewobj( constructor ); + il.EmitNewobj(constructor); } il.EmitRet(); - return dynamicMethod.CreateDelegate( delegateType ); + return dynamicMethod.CreateDelegate(delegateType); } } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/ReflectionExtensions.cs b/src/MsgPack/Serialization/ReflectionExtensions.cs index e195da43b..f4d671b1c 100644 --- a/src/MsgPack/Serialization/ReflectionExtensions.cs +++ b/src/MsgPack/Serialization/ReflectionExtensions.cs @@ -1,29 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2018 FUJIWARA, Yusuke and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Contributors: -// Samuel Cragg -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; #if FEATURE_MPCONTRACT @@ -32,6 +9,7 @@ using System.Diagnostics.Contracts; #endif // FEATURE_MPCONTRACT using System.Globalization; +using System.Linq; using System.Reflection; namespace MsgPack.Serialization @@ -43,31 +21,82 @@ namespace MsgPack.Serialization #endif static partial class ReflectionExtensions { - private static readonly Type[] ExceptionConstructorWithInnerParameterTypes = { typeof( string ), typeof( Exception ) }; - private static readonly Type[] ObjectAddParameterTypes = { typeof( object ) }; + private const string NullableAttributeTypeName = "System.CompilerServices.NullableAttribute"; + private const string NullableContextAttributeTypeName = "System.CompilerServices.NullableContextAttribute"; + + private static readonly Type[] ExceptionConstructorWithInnerParameterTypes = { typeof(string), typeof(Exception) }; + private static readonly Type[] ObjectAddParameterTypes = { typeof(object) }; + + public static bool IsNullableType(this Type type) + => !type.GetIsValueType() || Nullable.GetUnderlyingType(type) != null; + + public static bool IsNullable(this MemberInfo member) + { + // see https://github.com/dotnet/roslyn/blob/master/docs/features/nullable-metadata.md + var nullabileArguments = member.GetCustomAttributesData().SingleOrDefault(a => a.AttributeType.FullName == NullableAttributeTypeName)?.GetConstructorArguments(); + if (nullabileArguments?[0].ArgumentType == typeof(byte)) + { + var nullability = (byte)nullabileArguments[0].Value!; + if (nullability!=0) + { + return nullability == 2; + } + } + else if (nullabileArguments?[0].ArgumentType == typeof(byte[])) + { + var nullability = ((byte[])nullabileArguments[0].Value!)[0]; + if (nullability != 0) + { + return nullability == 2; + } + } + + // Note: oblivious (0) will be treated as nullable in this context. + return + (byte)member.DeclaringType! + .GetCustomAttributesData() + .SingleOrDefault(a => a.AttributeType.FullName == NullableContextAttributeTypeName)? + .GetConstructorArguments()[0].Value! + != 1; + } + + public static bool IsCodecPrimitive(this Type type) + => type.GetIsPrimitive() + && (Type.GetTypeCode(type) switch + { + TypeCode.Char | TypeCode.Object => false, + _ => true + }); + + public static bool IsAssignableTo(this Type type, Type t) + => t.IsAssignableFrom(type); + + public static TDelegate CreateDelegate(this MethodInfo method) + where TDelegate : Delegate + => (TDelegate)method.CreateDelegate(typeof(TDelegate)); - public static Type[] GetParameterTypes( this MethodBase source ) + public static Type[] GetParameterTypes(this MethodBase source) { var parameters = source.GetParameters(); - Type[] parameterTypes = new Type[ parameters.Length ]; - for ( var i = 0; i < parameters.Length; i++ ) + Type[] parameterTypes = new Type[parameters.Length]; + for (var i = 0; i < parameters.Length; i++) { - parameterTypes[ i ] = parameters[ i ].ParameterType; + parameterTypes[i] = parameters[i].ParameterType; } return parameterTypes; } - public static Type GetMemberValueType( this MemberInfo source ) + public static Type GetMemberValueType(this MemberInfo source) { - if ( source == null ) + if (source == null) { - throw new ArgumentNullException( "source" ); + throw new ArgumentNullException("source"); } #if !NETFX_CORE && !NETSTANDARD1_1 && !NETSTANDARD1_3 var asType = source as Type; - if ( asType != null ) + if (asType != null) { // Nested type. return asType; @@ -88,9 +117,9 @@ public static Type GetMemberValueType( this MemberInfo source ) var asProperty = source as PropertyInfo; var asField = source as FieldInfo; - if ( asProperty == null && asField == null ) + if (asProperty == null && asField == null) { - throw new InvalidOperationException( String.Format( CultureInfo.CurrentCulture, "'{0}'({1}) is not field nor property.", source, source.GetType() ) ); + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, "'{0}'({1}) is not field nor property.", source, source.GetType())); } return asProperty != null ? asProperty.PropertyType : asField.FieldType; diff --git a/src/MsgPack/Serialization/SerializationExceptions.cs b/src/MsgPack/Serialization/SerializationExceptions.cs index e60e686ad..9a5402c80 100644 --- a/src/MsgPack/Serialization/SerializationExceptions.cs +++ b/src/MsgPack/Serialization/SerializationExceptions.cs @@ -1,27 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2018 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#define AOT -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; #if !UNITY || MSGPACK_UNITY_FULL @@ -48,12 +27,12 @@ namespace MsgPack.Serialization /// Defines common exception factory methods. /// #if !UNITY || MSGPACK_UNITY_FULL - [EditorBrowsable( EditorBrowsableState.Never )] + [EditorBrowsable(EditorBrowsableState.Never)] #endif // !UNITY || MSGPACK_UNITY_FULL public static class SerializationExceptions { #if !AOT - internal static readonly MethodInfo ThrowValueTypeCannotBeNull3Method = typeof( SerializationExceptions ).GetMethod( nameof( ThrowValueTypeCannotBeNull ), new[] { typeof( string ), typeof( Type ), typeof( Type ) } ); + internal static readonly MethodInfo ThrowValueTypeCannotBeNull3Method = typeof(SerializationExceptions).GetMethod(nameof(ThrowValueTypeCannotBeNull), new[] { typeof(string), typeof(Type), typeof(Type) }); #endif // !AOT /// @@ -64,16 +43,16 @@ public static class SerializationExceptions /// The type of the member. /// The type that declares the member. /// instance. It will not be null. - public static Exception NewValueTypeCannotBeNull( string name, Type memberType, Type declaringType ) + public static Exception NewValueTypeCannotBeNull(string name, Type memberType, Type declaringType) { #if DEBUG - Contract.Requires( !String.IsNullOrEmpty( name ) ); - Contract.Requires( memberType != null ); - Contract.Requires( declaringType != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(!String.IsNullOrEmpty(name)); + Contract.Requires(memberType != null); + Contract.Requires(declaringType != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Member '{0}' of type '{1}' cannot be null because it is value type('{2}').", name, declaringType, memberType ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Member '{0}' of type '{1}' cannot be null because it is value type('{2}').", name, declaringType, memberType)); } /// @@ -85,9 +64,9 @@ public static Exception NewValueTypeCannotBeNull( string name, Type memberType, /// The type that declares the member. /// Always thrown. /// instance. It will not be null. - public static void ThrowValueTypeCannotBeNull( string name, Type memberType, Type declaringType ) + public static void ThrowValueTypeCannotBeNull(string name, Type memberType, Type declaringType) { - throw NewValueTypeCannotBeNull( name, memberType, declaringType ); + throw NewValueTypeCannotBeNull(name, memberType, declaringType); } /// @@ -96,14 +75,14 @@ public static void ThrowValueTypeCannotBeNull( string name, Type memberType, Typ /// /// The target type. /// instance. It will not be null. - public static Exception NewValueTypeCannotBeNull( Type type ) + public static Exception NewValueTypeCannotBeNull(Type type) { #if DEBUG - Contract.Requires( type != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(type != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot be null '{0}' type value.", type ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Cannot be null '{0}' type value.", type)); } /// @@ -112,14 +91,14 @@ public static Exception NewValueTypeCannotBeNull( Type type ) /// /// The target type. /// instance. It will not be null. - public static Exception NewTypeCannotSerialize( Type type ) + public static Exception NewTypeCannotSerialize(Type type) { #if DEBUG - Contract.Requires( type != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(type != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot serialize '{0}' type.", type ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Cannot serialize '{0}' type.", type)); } /// @@ -128,14 +107,14 @@ public static Exception NewTypeCannotSerialize( Type type ) /// /// The target type. /// instance. It will not be null. - public static Exception NewTypeCannotDeserialize( Type type ) + public static Exception NewTypeCannotDeserialize(Type type) { #if DEBUG - Contract.Requires( type != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(type != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot deserialize '{0}' type.", type ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Cannot deserialize '{0}' type.", type)); } /// @@ -146,16 +125,16 @@ public static Exception NewTypeCannotDeserialize( Type type ) /// The name of deserializing member. /// The inner exception. /// instance. It will not be null. - public static Exception NewTypeCannotDeserialize( Type type, string memberName, Exception inner ) + public static Exception NewTypeCannotDeserialize(Type type, string memberName, Exception inner) { #if DEBUG - Contract.Requires( type != null ); - Contract.Requires( !String.IsNullOrEmpty( memberName ) ); - Contract.Requires( inner != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(type != null); + Contract.Requires(!String.IsNullOrEmpty(memberName)); + Contract.Requires(inner != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot deserialize member '{1}' of type '{0}'.", type, memberName ), inner ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Cannot deserialize member '{1}' of type '{0}'.", type, memberName), inner); } /// @@ -165,16 +144,16 @@ public static Exception NewTypeCannotDeserialize( Type type, string memberName, /// The index to be unpacking. /// instance. It will not be null. #if DEBUG - [Obsolete( "Use ThrowMissingItem(int, Unpacker) instead." )] + [Obsolete("Use ThrowMissingItem(int, Unpacker) instead.")] #endif - public static Exception NewMissingItem( int index ) // For compatibility only. + public static Exception NewMissingItem(int index) // For compatibility only. { #if DEBUG - Contract.Requires( index >= 0 ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(index >= 0); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new InvalidMessagePackStreamException( String.Format( CultureInfo.CurrentCulture, "Items at index '{0}' is missing.", index ) ); + return new InvalidMessagePackStreamException(String.Format(CultureInfo.CurrentCulture, "Items at index '{0}' is missing.", index)); } /// @@ -184,9 +163,9 @@ public static Exception NewMissingItem( int index ) // For compatibility only. /// The index to be unpacking. /// The unpacker for pretty message. /// Always thrown. - public static void ThrowMissingItem( int index, Unpacker unpacker ) + public static void ThrowMissingItem(int index, Unpacker unpacker) { - ThrowMissingItem( index, null, unpacker ); + ThrowMissingItem(index, null, unpacker); } /// @@ -197,20 +176,20 @@ public static void ThrowMissingItem( int index, Unpacker unpacker ) /// The name of the item to be unpacking. /// The unpacker for pretty message. /// Always thrown. - public static void ThrowMissingItem( int index, string name, Unpacker unpacker ) + public static void ThrowMissingItem(int index, string name, Unpacker unpacker) { long offsetOrPosition = -1; bool isRealPosition = false; - if ( unpacker != null ) + if (unpacker != null) { - isRealPosition = unpacker.GetPreviousPosition( out offsetOrPosition ); + isRealPosition = unpacker.GetPreviousPosition(out offsetOrPosition); } - if ( String.IsNullOrEmpty( name ) ) + if (String.IsNullOrEmpty(name)) { - if ( offsetOrPosition >= 0L ) + if (offsetOrPosition >= 0L) { - if ( isRealPosition ) + if (isRealPosition) { throw new InvalidMessagePackStreamException( String.Format( @@ -238,15 +217,15 @@ public static void ThrowMissingItem( int index, string name, Unpacker unpacker ) else { throw new InvalidMessagePackStreamException( - String.Format( CultureInfo.CurrentCulture, "Value for '{0}' at index {1} is missing.", name, index ) + String.Format(CultureInfo.CurrentCulture, "Value for '{0}' at index {1} is missing.", name, index) ); } } else { - if ( offsetOrPosition >= 0L ) + if (offsetOrPosition >= 0L) { - if ( isRealPosition ) + if (isRealPosition) { throw new InvalidMessagePackStreamException( String.Format( @@ -272,19 +251,19 @@ public static void ThrowMissingItem( int index, string name, Unpacker unpacker ) else { throw new InvalidMessagePackStreamException( - String.Format( CultureInfo.CurrentCulture, "Item at index '{0}' is missing.", index ) + String.Format(CultureInfo.CurrentCulture, "Item at index '{0}' is missing.", index) ); } } } - internal static void ThrowMissingKey( int index, Unpacker unpacker ) + internal static void ThrowMissingKey(int index, Unpacker unpacker) { long offsetOrPosition; - var isRealPosition = unpacker.GetPreviousPosition( out offsetOrPosition ); - if ( offsetOrPosition >= 0L ) + var isRealPosition = unpacker.GetPreviousPosition(out offsetOrPosition); + if (offsetOrPosition >= 0L) { - if ( isRealPosition ) + if (isRealPosition) { throw new InvalidMessagePackStreamException( String.Format( @@ -325,14 +304,14 @@ internal static void ThrowMissingKey( int index, Unpacker unpacker ) /// /// The target type. /// instance. It will not be null. - internal static Exception NewTargetDoesNotHavePublicDefaultConstructor( Type type ) + internal static Exception NewTargetDoesNotHavePublicDefaultConstructor(Type type) { #if DEBUG - Contract.Requires( type != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(type != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Type '{0}' does not have default (parameterless) public constructor.", type ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Type '{0}' does not have default (parameterless) public constructor.", type)); } /// @@ -341,19 +320,19 @@ internal static Exception NewTargetDoesNotHavePublicDefaultConstructor( Type typ /// /// The target type. /// instance. It will not be null. - internal static Exception NewTargetDoesNotHavePublicDefaultConstructorNorInitialCapacity( Type type ) + internal static Exception NewTargetDoesNotHavePublicDefaultConstructorNorInitialCapacity(Type type) { #if DEBUG - Contract.Requires( type != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(type != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Type '{0}' does not have both of default (parameterless) public constructor and public constructor with an Int32 parameter.", type ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Type '{0}' does not have both of default (parameterless) public constructor and public constructor with an Int32 parameter.", type)); } - internal static void ThrowTargetDoesNotHavePublicDefaultConstructorNorInitialCapacity( Type type ) + internal static void ThrowTargetDoesNotHavePublicDefaultConstructorNorInitialCapacity(Type type) { - throw NewTargetDoesNotHavePublicDefaultConstructorNorInitialCapacity( type ); + throw NewTargetDoesNotHavePublicDefaultConstructorNorInitialCapacity(type); } /// @@ -362,20 +341,20 @@ internal static void ThrowTargetDoesNotHavePublicDefaultConstructorNorInitialCap /// /// The name of the property. /// instance. It will not be null. - public static Exception NewMissingProperty( string name ) + public static Exception NewMissingProperty(string name) { #if DEBUG - Contract.Requires( !String.IsNullOrEmpty( name ) ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(!String.IsNullOrEmpty(name)); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Property '{0}' is missing.", name ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Property '{0}' is missing.", name)); } - internal static void ThrowMissingProperty( string name ) + internal static void ThrowMissingProperty(string name) { #pragma warning disable 612 - throw NewMissingProperty( name ); + throw NewMissingProperty(name); #pragma warning restore 612 } @@ -384,23 +363,23 @@ internal static void ThrowMissingProperty( string name ) /// Returns new exception to notify that unpacking stream ends on unexpectedly position. /// /// instance. It will not be null. - [Obsolete( "This method is no longer used internally. So this internal API will be removed in future." )] + [Obsolete("This method is no longer used internally. So this internal API will be removed in future.")] public static Exception NewUnexpectedEndOfStream() { #if DEBUG - Contract.Ensures( Contract.Result() != null ); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( "Stream unexpectedly ends." ); + return new SerializationException("Stream unexpectedly ends."); } - internal static void ThrowUnexpectedEndOfStream( Unpacker unpacker ) + internal static void ThrowUnexpectedEndOfStream(Unpacker unpacker) { long offsetOrPosition; - var isRealPosition = unpacker.GetPreviousPosition( out offsetOrPosition ); - if ( offsetOrPosition >= 0L ) + var isRealPosition = unpacker.GetPreviousPosition(out offsetOrPosition); + if (offsetOrPosition >= 0L) { - if ( isRealPosition ) + if (isRealPosition) { throw new InvalidMessagePackStreamException( String.Format( @@ -423,7 +402,7 @@ internal static void ThrowUnexpectedEndOfStream( Unpacker unpacker ) } else { - throw new InvalidMessagePackStreamException( "Stream unexpectedly ends." ); + throw new InvalidMessagePackStreamException("Stream unexpectedly ends."); } } @@ -433,19 +412,19 @@ internal static void ThrowUnexpectedEndOfStream( Unpacker unpacker ) /// /// The target type. /// instance. It will not be null. - public static Exception NewMissingAddMethod( Type type ) + public static Exception NewMissingAddMethod(Type type) { #if DEBUG - Contract.Requires( type != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(type != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Type '{0}' does not have appropriate Add method.", type ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Type '{0}' does not have appropriate Add method.", type)); } #if !AOT internal static readonly MethodInfo ThrowIsNotArrayHeaderMethod = - typeof( SerializationExceptions ).GetMethod( nameof( ThrowIsNotArrayHeader ), new[] { typeof( Unpacker ) } ); + typeof(SerializationExceptions).GetMethod(nameof(ThrowIsNotArrayHeader), new[] { typeof(Unpacker) }); #endif // !AOT /// @@ -453,10 +432,10 @@ public static Exception NewMissingAddMethod( Type type ) /// Returns new exception to notify that unpacker is not in the array header, that is the state is invalid. /// /// instance. It will not be null. - [Obsolete( "This method is no longer used internally. So this internal API will be removed in future." )] + [Obsolete("This method is no longer used internally. So this internal API will be removed in future.")] public static Exception NewIsNotArrayHeader() { - return new SerializationException( "Unpacker is not in the array header. The stream may not be array." ); + return new SerializationException("Unpacker is not in the array header. The stream may not be array."); } /// @@ -465,12 +444,12 @@ public static Exception NewIsNotArrayHeader() /// /// The unpacker for pretty message. /// Always thrown. - public static void ThrowIsNotArrayHeader( Unpacker unpacker ) + public static void ThrowIsNotArrayHeader(Unpacker unpacker) { long offsetOrPosition; - if ( unpacker != null ) + if (unpacker != null) { - if ( unpacker.GetPreviousPosition( out offsetOrPosition ) ) + if (unpacker.GetPreviousPosition(out offsetOrPosition)) { throw new SerializationException( String.Format( @@ -501,7 +480,7 @@ public static void ThrowIsNotArrayHeader( Unpacker unpacker ) #if !AOT internal static readonly MethodInfo ThrowIsNotMapHeaderMethod = - typeof( SerializationExceptions ).GetMethod( nameof( ThrowIsNotMapHeader ), new[] { typeof( Unpacker ) } ); + typeof(SerializationExceptions).GetMethod(nameof(ThrowIsNotMapHeader), new[] { typeof(Unpacker) }); #endif // !AOT /// @@ -515,10 +494,10 @@ public static void ThrowIsNotArrayHeader( Unpacker unpacker ) public static Exception NewIsNotMapHeader() { #if DEBUG - Contract.Ensures( Contract.Result() != null ); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( "Unpacker is not in the map header. The stream may not be map." ); + return new SerializationException("Unpacker is not in the map header. The stream may not be map."); } /// @@ -527,12 +506,12 @@ public static Exception NewIsNotMapHeader() /// /// The unpacker for pretty message. /// Always thrown. - public static void ThrowIsNotMapHeader( Unpacker unpacker ) + public static void ThrowIsNotMapHeader(Unpacker unpacker) { long offsetOrPosition; - if ( unpacker != null ) + if (unpacker != null) { - if ( unpacker.GetPreviousPosition( out offsetOrPosition ) ) + if (unpacker.GetPreviousPosition(out offsetOrPosition)) { throw new SerializationException( String.Format( @@ -567,14 +546,14 @@ public static void ThrowIsNotMapHeader( Unpacker unpacker ) /// /// Type. /// instance. It will not be null. - public static Exception NewNotSupportedBecauseCannotInstanciateAbstractType( Type type ) + public static Exception NewNotSupportedBecauseCannotInstanciateAbstractType(Type type) { #if DEBUG - Contract.Requires( type != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(type != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new NotSupportedException( String.Format( CultureInfo.CurrentCulture, "This operation is not supported because '{0}' cannot be instanciated.", type ) ); + return new NotSupportedException(String.Format(CultureInfo.CurrentCulture, "This operation is not supported because '{0}' cannot be instanciated.", type)); } #if !AOT @@ -582,7 +561,7 @@ public static Exception NewNotSupportedBecauseCannotInstanciateAbstractType( Typ /// /// internal static readonly MethodInfo ThrowTupleCardinarityIsNotMatchMethod = - typeof( SerializationExceptions ).GetMethod( nameof( ThrowTupleCardinarityIsNotMatch ), new[] { typeof( int ), typeof( long ), typeof( Unpacker ) } ); + typeof(SerializationExceptions).GetMethod(nameof(ThrowTupleCardinarityIsNotMatch), new[] { typeof(int), typeof(long), typeof(Unpacker) }); #endif // !AOT /// @@ -595,14 +574,14 @@ public static Exception NewNotSupportedBecauseCannotInstanciateAbstractType( Typ #if DEBUG [Obsolete] #endif // DEBUG - public static Exception NewTupleCardinarityIsNotMatch( int expectedTupleCardinality, int actualArrayLength ) + public static Exception NewTupleCardinarityIsNotMatch(int expectedTupleCardinality, int actualArrayLength) { #if DEBUG - Contract.Requires( expectedTupleCardinality > 0 ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(expectedTupleCardinality > 0); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "The length of array ({0}) does not match to tuple cardinality ({1}).", actualArrayLength, expectedTupleCardinality ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "The length of array ({0}) does not match to tuple cardinality ({1}).", actualArrayLength, expectedTupleCardinality)); } /// @@ -617,22 +596,22 @@ public static Exception NewTupleCardinarityIsNotMatch( int expectedTupleCardinal public static void ThrowTupleCardinarityIsNotMatch( int expectedTupleCardinality, long actualArrayLength, - Unpacker unpacker + Unpacker unpacker ) { #if DEBUG - Contract.Requires( expectedTupleCardinality > 0 ); + Contract.Requires(expectedTupleCardinality > 0); #endif // DEBUG long offsetOrPosition = -1; bool isRealPosition = false; - if ( unpacker != null ) + if (unpacker != null) { - isRealPosition = unpacker.GetPreviousPosition( out offsetOrPosition ); + isRealPosition = unpacker.GetPreviousPosition(out offsetOrPosition); } - if ( offsetOrPosition >= 0L ) + if (offsetOrPosition >= 0L) { - if ( isRealPosition ) + if (isRealPosition) { throw new InvalidMessagePackStreamException( String.Format( @@ -676,18 +655,18 @@ Unpacker unpacker /// /// The inner exception for the debug. The value is implementation specific. /// instance. It will not be null. - public static Exception NewIsIncorrectStream( Exception innerException ) + public static Exception NewIsIncorrectStream(Exception innerException) { #if DEBUG - Contract.Ensures( Contract.Result() != null ); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( "Failed to unpack items count of the collection.", innerException ); + return new SerializationException("Failed to unpack items count of the collection.", innerException); } - internal static void ThrowIsIncorrectStream( Exception innerException ) + internal static void ThrowIsIncorrectStream(Exception innerException) { - throw NewIsIncorrectStream( innerException ); + throw NewIsIncorrectStream(innerException); } /// @@ -698,10 +677,10 @@ internal static void ThrowIsIncorrectStream( Exception innerException ) public static Exception NewIsTooLargeCollection() { #if DEBUG - Contract.Ensures( Contract.Result() != null ); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new MessageNotSupportedException( "The collection which has more than Int32.MaxValue items is not supported." ); + return new MessageNotSupportedException("The collection which has more than Int32.MaxValue items is not supported."); } internal static void ThrowIsTooLargeCollection() @@ -710,7 +689,7 @@ internal static void ThrowIsTooLargeCollection() } #if !AOT - internal static readonly MethodInfo ThrowNullIsProhibitedMethod = typeof( SerializationExceptions ).GetMethod( nameof( ThrowNullIsProhibited ), new[] { typeof( string ) } ); + internal static readonly MethodInfo ThrowNullIsProhibitedMethod = typeof(SerializationExceptions).GetMethod(nameof(ThrowNullIsProhibited), new[] { typeof(string) }); #endif // !AOT /// @@ -719,14 +698,14 @@ internal static void ThrowIsTooLargeCollection() /// /// The name of the member. /// instance. It will not be null. - public static Exception NewNullIsProhibited( string memberName ) + public static Exception NewNullIsProhibited(string memberName) { #if DEBUG - Contract.Requires( !String.IsNullOrEmpty( memberName ) ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(!String.IsNullOrEmpty(memberName)); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "The member '{0}' cannot be nil.", memberName ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "The member '{0}' cannot be nil.", memberName)); } /// @@ -735,9 +714,9 @@ public static Exception NewNullIsProhibited( string memberName ) /// /// The name of the member. /// Always thrown. - public static void ThrowNullIsProhibited( string memberName ) + public static void ThrowNullIsProhibited(string memberName) { - throw NewNullIsProhibited( memberName ); + throw NewNullIsProhibited(memberName); } /// @@ -746,14 +725,14 @@ public static void ThrowNullIsProhibited( string memberName ) /// /// The name of the member. /// instance. It will not be null. - public static Exception NewReadOnlyMemberItemsMustNotBeNull( string memberName ) + public static Exception NewReadOnlyMemberItemsMustNotBeNull(string memberName) { #if DEBUG - Contract.Requires( !String.IsNullOrEmpty( memberName ) ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(!String.IsNullOrEmpty(memberName)); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "The member '{0}' cannot be nil because it is read only member.", memberName ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "The member '{0}' cannot be nil because it is read only member.", memberName)); } /// @@ -762,14 +741,14 @@ public static Exception NewReadOnlyMemberItemsMustNotBeNull( string memberName ) /// /// The name of the member. /// instance. It will not be null. - public static Exception NewStreamDoesNotContainCollectionForMember( string memberName ) + public static Exception NewStreamDoesNotContainCollectionForMember(string memberName) { #if DEBUG - Contract.Requires( !String.IsNullOrEmpty( memberName ) ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(!String.IsNullOrEmpty(memberName)); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot deserialize member '{0}' because the underlying stream does not contain collection.", memberName ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Cannot deserialize member '{0}' because the underlying stream does not contain collection.", memberName)); } /// @@ -779,15 +758,15 @@ public static Exception NewStreamDoesNotContainCollectionForMember( string membe /// Expected, required for deserialization array length. /// Actual array length. /// instance. It will not be null. - public static Exception NewUnexpectedArrayLength( int expectedLength, int actualLength ) + public static Exception NewUnexpectedArrayLength(int expectedLength, int actualLength) { #if DEBUG - Contract.Requires( expectedLength >= 0 ); - Contract.Requires( actualLength >= 0 ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(expectedLength >= 0); + Contract.Requires(actualLength >= 0); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "The MessagePack stream is invalid. Expected array length is {0}, but actual is {1}.", expectedLength, actualLength ) ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "The MessagePack stream is invalid. Expected array length is {0}, but actual is {1}.", expectedLength, actualLength)); } /// @@ -798,16 +777,16 @@ public static Exception NewUnexpectedArrayLength( int expectedLength, int actual /// The name of the deserializing member. /// The exception which caused current error. /// instance. It will not be null. - public static Exception NewFailedToDeserializeMember( Type targetType, string memberName, Exception inner ) + public static Exception NewFailedToDeserializeMember(Type targetType, string memberName, Exception inner) { #if DEBUG - Contract.Requires( targetType != null ); - Contract.Requires( !String.IsNullOrEmpty( memberName ) ); - Contract.Requires( inner != null ); - Contract.Ensures( Contract.Result() != null ); + Contract.Requires(targetType != null); + Contract.Requires(!String.IsNullOrEmpty(memberName)); + Contract.Requires(inner != null); + Contract.Ensures(Contract.Result() != null); #endif // DEBUG - return new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot deserialize member '{0}' of type '{1}'.", memberName, targetType ), inner ); + return new SerializationException(String.Format(CultureInfo.CurrentCulture, "Cannot deserialize member '{0}' of type '{1}'.", memberName, targetType), inner); } /// @@ -816,9 +795,9 @@ public static Exception NewFailedToDeserializeMember( Type targetType, string me /// Deserializing type. /// The name of the deserializing member. /// The exception which caused current error. - internal static void ThrowFailedToDeserializeMember( Type targetType, string memberName, Exception inner ) + internal static void ThrowFailedToDeserializeMember(Type targetType, string memberName, Exception inner) { - throw NewFailedToDeserializeMember( targetType, memberName, inner ); + throw NewFailedToDeserializeMember(targetType, memberName, inner); } #if !AOT @@ -826,7 +805,7 @@ internal static void ThrowFailedToDeserializeMember( Type targetType, string mem /// /// internal static readonly MethodInfo NewUnpackFromIsNotSupportedMethod = - typeof( SerializationExceptions ).GetMethod( nameof( NewUnpackFromIsNotSupported ), new[] { typeof( Type ) } ); + typeof(SerializationExceptions).GetMethod(nameof(NewUnpackFromIsNotSupported), new[] { typeof(Type) }); #endif // !AOT /// @@ -834,12 +813,12 @@ internal static void ThrowFailedToDeserializeMember( Type targetType, string mem /// /// Deserializing type. /// The exception. This value will not be null. - public static Exception NewUnpackFromIsNotSupported( Type targetType ) + public static Exception NewUnpackFromIsNotSupported(Type targetType) { #if DEBUG - Contract.Requires( targetType != null ); + Contract.Requires(targetType != null); #endif // DEBUG - return new NotSupportedException( String.Format( CultureInfo.CurrentCulture, "This operation is not supported for '{0}' because the serializer does not support UnpackFrom method.", targetType ) ); + return new NotSupportedException(String.Format(CultureInfo.CurrentCulture, "This operation is not supported for '{0}' because the serializer does not support UnpackFrom method.", targetType)); } #if !AOT @@ -847,7 +826,7 @@ public static Exception NewUnpackFromIsNotSupported( Type targetType ) /// /// internal static readonly MethodInfo NewCreateInstanceIsNotSupportedMethod = - typeof( SerializationExceptions ).GetMethod( nameof( NewCreateInstanceIsNotSupported ), new[] { typeof( Type ) } ); + typeof(SerializationExceptions).GetMethod(nameof(NewCreateInstanceIsNotSupported), new[] { typeof(Type) }); #endif // !AOT /// @@ -855,36 +834,36 @@ public static Exception NewUnpackFromIsNotSupported( Type targetType ) /// /// Deserializing type. /// The exception. This value will not be null. - public static Exception NewCreateInstanceIsNotSupported( Type targetType ) + public static Exception NewCreateInstanceIsNotSupported(Type targetType) { #if DEBUG - Contract.Requires( targetType != null ); + Contract.Requires(targetType != null); #endif // DEBUG - return new NotSupportedException( String.Format( CultureInfo.CurrentCulture, "This operation is not supported for '{0}' because the serializer does not support CreateInstance method.", targetType ) ); + return new NotSupportedException(String.Format(CultureInfo.CurrentCulture, "This operation is not supported for '{0}' because the serializer does not support CreateInstance method.", targetType)); } - internal static Exception NewUnpackToIsNotSupported( Type type, Exception inner ) + internal static Exception NewUnpackToIsNotSupported(Type type, Exception inner) { #if DEBUG - Contract.Requires( type != null ); + Contract.Requires(type != null); #endif // DEBUG - return new NotSupportedException( String.Format( CultureInfo.CurrentCulture, "This operation is not supported for '{0}' because it does not have accesible Add(T) method.", type ), inner ); + return new NotSupportedException(String.Format(CultureInfo.CurrentCulture, "This operation is not supported for '{0}' because it does not have accesible Add(T) method.", type), inner); } - internal static Exception NewValueTypeCannotBePolymorphic( Type type ) + internal static Exception NewValueTypeCannotBePolymorphic(Type type) { return new SerializationException( - String.Format( CultureInfo.CurrentCulture, "Value type '{0}' cannot be polymorphic.", type ) + String.Format(CultureInfo.CurrentCulture, "Value type '{0}' cannot be polymorphic.", type) ); } internal static Exception NewUnknownTypeEmbedding() { - return new SerializationException( "Cannot deserialize with type-embedding based serializer. Root object must be 3 element array." ); + return new SerializationException("Cannot deserialize with type-embedding based serializer. Root object must be 3 element array."); } - internal static Exception NewIncompatibleCollectionSerializer( Type targetType, Type incompatibleType, Type exampleClass ) + internal static Exception NewIncompatibleCollectionSerializer(Type targetType, Type incompatibleType, Type exampleClass) { return new SerializationException( @@ -893,45 +872,45 @@ internal static Exception NewIncompatibleCollectionSerializer( Type targetType, "Cannot serialize type '{0}' because registered or generated serializer '{1}' does not implement '{2}', which is implemented by '{3}', for example.", targetType.GetFullName(), incompatibleType.GetFullName(), - typeof( ICollectionInstanceFactory ), + typeof(ICollectionInstanceFactory), exampleClass.GetFullName() ) ); } - internal static void ThrowArgumentNullException( string parameterName ) + internal static void ThrowArgumentNullException(string parameterName) { - throw new ArgumentNullException( parameterName ); + throw new ArgumentNullException(parameterName); } - internal static void ThrowArgumentNullException( string parameterName, string fieldName ) + internal static void ThrowArgumentNullException(string parameterName, string fieldName) { - throw new ArgumentNullException( parameterName, String.Format( CultureInfo.CurrentCulture, "Field '{0}' of parameter '{1}' cannot be null.", fieldName, parameterName ) ); + throw new ArgumentNullException(parameterName, String.Format(CultureInfo.CurrentCulture, "Field '{0}' of parameter '{1}' cannot be null.", fieldName, parameterName)); } - internal static void ThrowArgumentCannotBeNegativeException( string parameterName ) + internal static void ThrowArgumentCannotBeNegativeException(string parameterName) { - throw new ArgumentOutOfRangeException( parameterName, "The value cannot be negative number." ); + throw new ArgumentOutOfRangeException(parameterName, "The value cannot be negative number."); } - internal static void ThrowArgumentCannotBeNegativeException( string parameterName, string fieldName ) + internal static void ThrowArgumentCannotBeNegativeException(string parameterName, string fieldName) { - throw new ArgumentOutOfRangeException( parameterName, String.Format( CultureInfo.CurrentCulture, "Field '{0}' of parameter '{1}' cannot be negative number.", fieldName, parameterName ) ); + throw new ArgumentOutOfRangeException(parameterName, String.Format(CultureInfo.CurrentCulture, "Field '{0}' of parameter '{1}' cannot be negative number.", fieldName, parameterName)); } - internal static void ThrowArgumentException( string parameterName, string message ) + internal static void ThrowArgumentException(string parameterName, string message) { - throw new ArgumentException( message, parameterName ); + throw new ArgumentException(message, parameterName); } - internal static void ThrowSerializationException( string message ) + internal static void ThrowSerializationException(string message) { - throw new SerializationException( message ); + throw new SerializationException(message); } - internal static void ThrowSerializationException( string message, Exception innerException ) + internal static void ThrowSerializationException(string message, Exception innerException) { - throw new SerializationException( message, innerException ); + throw new SerializationException(message, innerException); } #if UNITY && DEBUG @@ -939,7 +918,7 @@ internal static void ThrowSerializationException( string message, Exception inne #else internal #endif - static void ThrowInvalidArrayItemsCount( Unpacker unpacker, Type targetType, int requiredCount ) + static void ThrowInvalidArrayItemsCount(Unpacker unpacker, Type targetType, int requiredCount) { throw unpacker.IsCollectionHeader diff --git a/src/MsgPack/Serialization/SerializationMethodGeneratorOption.cs b/src/MsgPack/Serialization/SerializationMethodGeneratorOption.cs index 00aa73fad..5c0fec167 100644 --- a/src/MsgPack/Serialization/SerializationMethodGeneratorOption.cs +++ b/src/MsgPack/Serialization/SerializationMethodGeneratorOption.cs @@ -1,26 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2016 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; #if !UNITY || MSGPACK_UNITY_FULL @@ -32,25 +12,22 @@ namespace MsgPack.Serialization /// /// Define options of serializer generation. /// + [Obsolete("This type has not been used.")] + [EditorBrowsable(EditorBrowsableState.Never)] public enum SerializationMethodGeneratorOption { -#if !SILVERLIGHT -#if !NETSTANDARD1_1 && !NETSTANDARD1_3 /// /// The generated method IL can be dumped to the current directory. /// It is intended for the runtime, you cannot use this option. /// -#if !UNITY || MSGPACK_UNITY_FULL [EditorBrowsable( EditorBrowsableState.Never )] -#endif // !UNITY || MSGPACK_UNITY_FULL CanDump, -#endif // !NETSTANDARD1_1 && !NETSTANDARD1_3 + // TODO: AssemblyLoadContext support for CanCollect work properly. /// /// The entire generated method can be collected by GC when it is no longer used. /// CanCollect, -#endif // !SILVERLIGHT /// /// Prefer performance. This options is default. diff --git a/src/MsgPack/Serialization/SerializationTarget.cs b/src/MsgPack/Serialization/SerializationTarget.cs index 8efe8ab1b..20a9e77bd 100644 --- a/src/MsgPack/Serialization/SerializationTarget.cs +++ b/src/MsgPack/Serialization/SerializationTarget.cs @@ -1,125 +1,98 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2014-2018 FUJIWARA, Yusuke and contributors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// Contributors: -// Takeshi KIRIYA -// Odyth -// Roman-Blinkov -// Samuel Cragg -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; using System.Collections.Generic; using System.Diagnostics; -#if FEATURE_MPCONTRACT -using Contract = MsgPack.MPContract; -#else -using System.Diagnostics.Contracts; -#endif // FEATURE_MPCONTRACT -using System.Globalization; using System.Linq; using System.Reflection; -using System.Runtime.Serialization; -using MsgPack.Serialization.DefaultSerializers; +#warning TODO: Implement generator and reflection with this type! +#warning TODO: Separate factory method from this file for maintenancibility. namespace MsgPack.Serialization { /// /// Implements serialization target member extraction logics. /// -#if UNITY && DEBUG - public -#else - internal -#endif - class SerializationTarget + internal readonly struct SerializationTarget { // Type names to avoid user who doesn't embed "message pack assembly's attributes" in their code directly. - private static readonly string MessagePackMemberAttributeTypeName = typeof( MessagePackMemberAttribute ).FullName; - private static readonly string MessagePackIgnoreAttributeTypeName = typeof( MessagePackIgnoreAttribute ).FullName; - private static readonly string MessagePackDeserializationConstructorAttributeTypeName = typeof( MessagePackDeserializationConstructorAttribute ).FullName; - private static readonly string[] EmptyStrings = new string[ 0 ]; - private static readonly SerializingMember[] EmptyMembers = new SerializingMember[ 0 ]; - private static readonly Assembly ThisAssembly = typeof( SerializationTarget ).GetAssembly(); + private static readonly string MessagePackMemberAttributeTypeName = typeof(MessagePackMemberAttribute).FullName!; + private static readonly string MessagePackIgnoreAttributeTypeName = typeof(MessagePackIgnoreAttribute).FullName!; + private static readonly string MessagePackDeserializationConstructorAttributeTypeName = typeof(MessagePackDeserializationConstructorAttribute).FullName!; + private static readonly Assembly ThisAssembly = typeof(SerializationTarget).GetAssembly(); + + public Type Type { get; } - public IList Members { get; private set; } + public CollectionTraits CollectionTraits { get; } - public ConstructorInfo DeserializationConstructor { get; private set; } + public IReadOnlyList Members { get; } - private readonly string[] _correspondingMemberNames; + public ConstructorInfo? DeserializationConstructor { get; } - public bool IsConstructorDeserialization { get; private set; } + private readonly string?[]? _correspondentMemberNames; - public bool CanDeserialize { get; private set; } + public bool IsConstructorDeserialization => (this.DeserializationConstructor?.GetParameters()?.Length).GetValueOrDefault() > 0; - private SerializationTarget( IList members, ConstructorInfo constructor, string[] correspondingMemberNames, bool canDeserialize ) + public bool CanDeserialize { get; } + + public bool IsCollection => this.CollectionTraits.CollectionType != CollectionKind.NotCollection; + + private SerializationTarget(Type type, CollectionTraits traits, IReadOnlyList members, ConstructorInfo? constructor, string?[]? correspondentMemberNames, bool canDeserialize) { - Trace( "SerializationTarget::ctor(canDeserialize: {0})", canDeserialize ); + this.Type = type; + this.CollectionTraits = traits; this.Members = members; this.DeserializationConstructor = constructor; - this.IsConstructorDeserialization = constructor != null && constructor.GetParameters().Any(); this.CanDeserialize = canDeserialize; - this._correspondingMemberNames = correspondingMemberNames ?? EmptyStrings; - } - - public SerializerCapabilities GetCapabilitiesForObject() - { - return this.CanDeserialize ? ( SerializerCapabilities.PackTo | SerializerCapabilities.UnpackFrom ) : SerializerCapabilities.PackTo; - } - - public SerializerCapabilities GetCapabilitiesForCollection( CollectionTraits traits ) - { - return - !this.CanDeserialize - ? SerializerCapabilities.PackTo - : traits.AddMethod == null - ? ( SerializerCapabilities.PackTo | SerializerCapabilities.UnpackFrom ) - : ( SerializerCapabilities.PackTo | SerializerCapabilities.UnpackFrom | SerializerCapabilities.UnpackTo ); - } + this._correspondentMemberNames = correspondentMemberNames; + } + + public SerializerCapabilities GetCapabilities() + => TupleItems.IsTuple(this.Type) ? + (SerializerCapabilities.Serialize | SerializerCapabilities.Deserialize) : + this.IsCollection ? + ( + !this.CanDeserialize ? + SerializerCapabilities.Serialize : + this.CollectionTraits.AddMethod == null ? + (SerializerCapabilities.Serialize | SerializerCapabilities.Deserialize) : + (SerializerCapabilities.Serialize | SerializerCapabilities.Deserialize | SerializerCapabilities.DeserializeTo) + ) : + ( + !this.CanDeserialize? + SerializerCapabilities.Serialize : + this.Type.GetIsValueType() ? + (SerializerCapabilities.Serialize | SerializerCapabilities.Deserialize) : + (SerializerCapabilities.Serialize | SerializerCapabilities.Deserialize | SerializerCapabilities.DeserializeTo) + ); - private static string[] FindCorrespondingMemberNames( IList members, ConstructorInfo constructor ) + private static string?[]? FindCorrespondentMemberNames(IReadOnlyList members, ConstructorInfo? constructor) { - if ( constructor == null ) + if (constructor == null) { return null; } + Debug.Assert(!members.Any(m => m.Member == null), "members contains Tuple item."); + var constructorParameters = constructor.GetParameters(); return constructorParameters.GroupJoin( members, - p => new KeyValuePair( p.Name, p.ParameterType ), - m => new KeyValuePair( m.Contract.Name, m.Member == null ? null : m.Member.GetMemberValueType() ), - ( p, ms ) => DetermineCorrespondingMemberName( p, ms ), + p => new KeyValuePair(p.Name!, p.ParameterType), + m => new KeyValuePair(m.Contract.Name, m.Member!.GetMemberValueType()), + (p, ms) => DetermineCorrespondentMemberName(p, ms), MemberConstructorParameterEqualityComparer.Instance ).ToArray(); } - private static string DetermineCorrespondingMemberName( ParameterInfo parameterInfo, IEnumerable members ) + private static string? DetermineCorrespondentMemberName(ParameterInfo parameterInfo, IEnumerable members) { var membersArray = members.ToArray(); - switch ( membersArray.Length ) + switch (membersArray.Length) { case 0: { @@ -127,88 +100,72 @@ private static string DetermineCorrespondingMemberName( ParameterInfo parameterI } case 1: { - return membersArray[ 0 ].MemberName; + return membersArray[0].MemberName; } default: { - ThrowAmbigiousMatchException( parameterInfo, membersArray ); - return null; + ThrowAmbigiousMatchException(parameterInfo, membersArray); + // never + return default!; } } } - private static void ThrowAmbigiousMatchException( ParameterInfo parameterInfo, ICollection members ) - { + private static void ThrowAmbigiousMatchException(ParameterInfo parameterInfo, ICollection members) => throw new AmbiguousMatchException( - String.Format( - CultureInfo.CurrentCulture, - "There are multiple candiates for corresponding member for parameter '{0}' of constructor. [{1}]", - parameterInfo, - String.Join( ", ", members.Select( x => x.ToString() ).ToArray() ) - ) + $"There are multiple candiates for corresponding member for parameter '{parameterInfo}' of constructor. [{String.Join(", ", members.Select(x => x.ToString()))}]" ); - } - public string GetCorrespondingMemberName( int constructorParameterIndex ) - { - return this._correspondingMemberNames[ constructorParameterIndex ]; - } + public string? GetCorrespondentMemberName(int constructorParameterIndex) + => this._correspondentMemberNames?[constructorParameterIndex]; - public static void VerifyType( Type targetType ) + public static void VerifyType(Type targetType) { - if ( targetType.GetIsInterface() || targetType.GetIsAbstract() ) + if (targetType.GetIsInterface() || targetType.GetIsAbstract()) { - throw SerializationExceptions.NewNotSupportedBecauseCannotInstanciateAbstractType( targetType ); + Throw.NotSupportedBecauseCannotInstanciateAbstractType(targetType); } } - public static void VerifyCanSerializeTargetType( SerializationContext context, Type targetType ) + public static void VerifyCanSerializeTargetType(SerializerGenerationOptions options, Type targetType) { - if ( context.SerializerOptions.DisablePrivilegedAccess && !targetType.GetIsPublic() && !targetType.GetIsNestedPublic() && !ThisAssembly.Equals( targetType.GetAssembly() ) ) + if (options.DisablesPrivilegedAccess && !targetType.GetIsPublic() && !targetType.GetIsNestedPublic() && !ThisAssembly.Equals(targetType.GetAssembly())) { - throw new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot serialize type '{0}' because it is not public to the serializer.", targetType ) ); + Throw.CannotSerializeNonPublicTypeUnlessPrivledgedAccessEnabled(targetType); } } - public static SerializationTarget Prepare( SerializationContext context, Type targetType ) + public static SerializationTarget Prepare(DiagnosticListener diag, SerializerGenerationOptions options, Type targetType) { - VerifyCanSerializeTargetType( context, targetType ); + VerifyCanSerializeTargetType(options, targetType); - IEnumerable memberIgnoreList = context.BindingOptions.GetIgnoringMembers( targetType ); - var getters = GetTargetMembers( targetType ) - .Where( getter => !memberIgnoreList.Contains( getter.MemberName, StringComparer.Ordinal ) ) - .OrderBy( entry => entry.Contract.Id ) + var memberIgnoreList = options.IgnoringMembers.TryGetValue(targetType, out var ignoringMembers) ? ignoringMembers : Enumerable.Empty(); + var getters = GetTargetMembers(targetType, options) + .Where(getter => !memberIgnoreList.Contains(getter.MemberName, StringComparer.Ordinal)) + .OrderBy(entry => entry.Contract.Id) .ToArray(); - if ( getters.Length == 0 - && !typeof( IPackable ).IsAssignableFrom( targetType ) - && !typeof( IUnpackable ).IsAssignableFrom( targetType ) -#if FEATURE_TAP - && ( context.SerializerOptions.WithAsync - && ( !typeof( IAsyncPackable ).IsAssignableFrom( targetType ) - && !typeof( IAsyncUnpackable ).IsAssignableFrom( targetType ) - ) - ) -#endif // FEATURE_TAP - ) + if (getters.Length == 0 && !options.SerializableAnywayInterfaceDetector(targetType, options)) { - throw new SerializationException( String.Format( CultureInfo.CurrentCulture, "Cannot serialize type '{0}' because it does not have any serializable fields nor properties.", targetType ) ); + Throw.IsNotSerializableAnyway(targetType); } - var memberCandidates = getters.Where( entry => CheckTargetEligibility( context, entry.Member ) ).ToArray(); + var memberCandidates = getters.Where(entry => CheckTargetEligibility(options, entry.Member!)).ToArray(); - if ( memberCandidates.Length == 0 && !context.CompatibilityOptions.AllowAsymmetricSerializer ) + if (memberCandidates.Length == 0 && !options.AllowsAsymmetricSerializer) { ConstructorKind constructorKind; - var deserializationConstructor = FindDeserializationConstructor( context, targetType, out constructorKind ); - var complementedMembers = ComplementMembers( getters, context, targetType ); - var correspondingMemberNames = FindCorrespondingMemberNames( complementedMembers, deserializationConstructor ); + var deserializationConstructor = FindDeserializationConstructor(diag, options, targetType, out constructorKind); + var complementedMembers = ComplementMembers(getters, options, targetType); + var correspondingMemberNames = FindCorrespondentMemberNames(complementedMembers, deserializationConstructor); return new SerializationTarget( + targetType, + CollectionTraits.NotCollection, complementedMembers, deserializationConstructor, correspondingMemberNames, - DetermineCanDeserialize( constructorKind, context, targetType, correspondingMemberNames, allowDefault: false ) + DetermineCanDeserialize(diag, constructorKind, options, targetType, correspondingMemberNames, allowDefault: false) ); } else @@ -217,24 +174,22 @@ public static SerializationTarget Prepare( SerializationContext context, Type ta ConstructorKind constructorKind; // Try to get default constructor. - var constructor = targetType.GetConstructor( ReflectionAbstractions.EmptyTypes ); - if ( constructor == null && !targetType.GetIsValueType() ) + var constructor = targetType.GetConstructor(ReflectionAbstractions.EmptyTypes); + if (constructor == null && !targetType.GetIsValueType()) { // Try to get deserialization constructor. - var deserializationConstructor = FindDeserializationConstructor( context, targetType, out constructorKind ); - if ( deserializationConstructor == null && !context.CompatibilityOptions.AllowAsymmetricSerializer ) + var deserializationConstructor = FindDeserializationConstructor(diag, options, targetType, out constructorKind); + if (deserializationConstructor == null && !options.AllowsAsymmetricSerializer) { - throw SerializationExceptions.NewTargetDoesNotHavePublicDefaultConstructor( targetType ); + Throw.TargetDoesNotHavePublicDefaultConstructor(targetType); } constructor = deserializationConstructor; canDeserialize = null; } - else if ( memberCandidates.Length == 0 ) + else if (memberCandidates.Length == 0) { -#if DEBUG - Contract.Assert( context.CompatibilityOptions.AllowAsymmetricSerializer ); -#endif // DEBUG + Debug.Assert(options.AllowsAsymmetricSerializer); // Absolutely cannot deserialize in this case. canDeserialize = false; constructorKind = ConstructorKind.Ambiguous; @@ -243,12 +198,12 @@ public static SerializationTarget Prepare( SerializationContext context, Type ta { constructorKind = ConstructorKind.Default; // Let's prefer annotated constructor here. - var markedConstructors = FindExplicitDeserializationConstructors( targetType.GetConstructors() ); - if ( markedConstructors.Count == 1 ) + var markedConstructors = FindExplicitDeserializationConstructors(targetType.GetConstructors()); + if (markedConstructors.Count == 1) { // For backward compatibility, no exceptions are thrown here even if mulitiple deserialization constructor attributes in the type // just use default constructor for it. - constructor = markedConstructors[ 0 ]; + constructor = markedConstructors[0]; constructorKind = ConstructorKind.Marked; } @@ -256,108 +211,124 @@ public static SerializationTarget Prepare( SerializationContext context, Type ta canDeserialize = true; } - if ( constructor != null && constructor.GetParameters().Any() || context.CompatibilityOptions.AllowAsymmetricSerializer ) + if (constructor != null && constructor.GetParameters().Any() || options.AllowsAsymmetricSerializer) { // Recalculate members because getter-only/readonly members should be included for constructor deserialization. memberCandidates = getters; } // Because members' order is equal to declared order is NOT guaranteed, so explicit ordering is required. - IList members; - if ( memberCandidates.All( item => item.Contract.Id == DataMemberContract.UnspecifiedId ) ) + IReadOnlyList members; + if (memberCandidates.All(item => item.Contract.Id == DataMemberContract.UnspecifiedId)) { // Alphabetical order. - members = memberCandidates.OrderBy( item => item.Contract.Name ).ToArray(); + members = memberCandidates.OrderBy(item => item.Contract.Name).ToArray(); } else { // ID order. - members = ComplementMembers( memberCandidates, context, targetType ); + members = ComplementMembers(memberCandidates, options, targetType); } - var correspondingMemberNames = FindCorrespondingMemberNames( members, constructor ); + var correspondingMemberNames = FindCorrespondentMemberNames(members, constructor); return new SerializationTarget( + targetType, + CollectionTraits.NotCollection, members, constructor, correspondingMemberNames, - canDeserialize ?? DetermineCanDeserialize( constructorKind, context, targetType, correspondingMemberNames, allowDefault: true ) + canDeserialize ?? DetermineCanDeserialize(diag, constructorKind, options, targetType, correspondingMemberNames, allowDefault: true) ); } } - private static bool HasAnyCorrespondingMembers( IEnumerable correspondingMemberNames ) - { - return correspondingMemberNames.Count( x => !String.IsNullOrEmpty( x ) ) > 0; - } + private static bool HasAnyCorrespondingMembers(IEnumerable correspondingMemberNames) + => correspondingMemberNames.Count(x => !String.IsNullOrEmpty(x)) > 0; - private static bool HasUnpackableInterface( Type targetType, SerializationContext context ) - { - return - typeof( IUnpackable ).IsAssignableFrom( targetType ) -#if FEATURE_TAP - && ( !context.SerializerOptions.WithAsync || typeof( IAsyncUnpackable ).IsAssignableFrom( targetType ) ) -#endif // FEATURE_TAP - ; - } + private static bool HasDeserializableInterface(Type targetType, SerializerGenerationOptions options) + => options.DeserializableInterfaceDetector(targetType, options); - private static bool DetermineCanDeserialize( ConstructorKind kind, SerializationContext context, Type targetType, IEnumerable correspondingMemberNames, bool allowDefault ) + private static bool DetermineCanDeserialize(DiagnosticSource diag, ConstructorKind kind, SerializerGenerationOptions options, Type targetType, IEnumerable correspondingMemberNames, bool allowDefault) { - if ( HasUnpackableInterface( targetType, context ) ) + if (HasDeserializableInterface(targetType, options)) { - Trace( "SerializationTarget::DetermineCanDeserialize({0}, {1}) -> true: HasUnpackableInterface", targetType, kind ); + diag.DetectedAsDeserializable( + new + { + targetType, + constructorKind = "ImplementsInterface", + allowsDefault = allowDefault + } + ); return true; } - switch ( kind ) + switch (kind) { case ConstructorKind.Marked: { - Trace( "SerializationTarget::DetermineCanDeserialize({0}, {1}) -> true: Marked", targetType, kind ); + diag.DetectedAsDeserializable( + new + { + targetType, + constructorKind = "MarkedWithAttribute", + allowsDefault = allowDefault + } + ); return true; } case ConstructorKind.Parameterful: { - var result = HasAnyCorrespondingMembers( correspondingMemberNames ); - Trace( "SerializationTarget::DetermineCanDeserialize({0}, {1}) -> {2}: HasAnyCorrespondingMembers", targetType, kind, result ); + var result = HasAnyCorrespondingMembers(correspondingMemberNames); + diag.DetectedAsDeserializable( + new + { + targetType, + constructorKind = "Parameterful", + allowsDefault = allowDefault + } + ); return result; } case ConstructorKind.Default: { - Trace( "SerializationTarget::DetermineCanDeserialize({0}, {1}) -> {2}: Default", targetType, kind, allowDefault ); + diag.DetectedAsDeserializable( + new + { + targetType, + constructorKind = "Default", + allowsDefault = allowDefault + } + ); return allowDefault; } default: { - Contract.Assert( kind == ConstructorKind.None || kind == ConstructorKind.Ambiguous, "kind == ConstructorKind.None || kind == ConstructorKind.Ambiguous : " + kind ); + Debug.Assert(kind == ConstructorKind.None || kind == ConstructorKind.Ambiguous, "kind == ConstructorKind.None || kind == ConstructorKind.Ambiguous : " + kind); return false; } } } - private static MemberInfo[] GetDistinctMembers( Type type ) + private static MemberInfo[] GetDistinctMembers(Type type) { var distinctMembers = new List(); var returningMemberNamesSet = new HashSet(); - while ( type != typeof( object ) && type != null ) + while (type != typeof(object) && type != null) { var members = -#if !NETSTANDARD1_1 && !NETSTANDARD1_3 type.FindMembers( MemberTypes.Field | MemberTypes.Property, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, null ); -#else - type.GetTypeInfo().DeclaredFields.Where( f => !f.IsStatic ).OfType() - .Concat( type.GetTypeInfo().DeclaredProperties.Where( p => p.GetMethod != null && !p.GetMethod.IsStatic ) ); -#endif // !NETSTANDARD1_1 && !NETSTANDARD1_3 - foreach ( var memberInfo in members ) + foreach (var memberInfo in members) { - if ( returningMemberNamesSet.Add( memberInfo.Name ) ) //HashSet returns true is new key was added + if (returningMemberNamesSet.Add(memberInfo.Name)) //HashSet returns true is new key was added { - distinctMembers.Add( memberInfo ); + distinctMembers.Add(memberInfo); } } @@ -367,124 +338,77 @@ private static MemberInfo[] GetDistinctMembers( Type type ) return distinctMembers.ToArray(); } - private static IEnumerable GetTargetMembers( Type type ) + private static IEnumerable GetTargetMembers(Type type, SerializerGenerationOptions options) { - Contract.Assert( type != null, "type != null" ); + Debug.Assert(type != null, "type != null"); - var members = GetDistinctMembers( type ); - var filtered = members.Where( item => -#if !UNITY - item.GetCustomAttributesData().Any( a => a.GetAttributeType().FullName == MessagePackMemberAttributeTypeName ) -#else - item.GetCustomAttributes( typeof( MessagePackMemberAttribute ), true ).Any() -#endif // !UNITY - ).ToArray(); + var members = GetDistinctMembers(type); + var filtered = + members.Where(item => + item.GetCustomAttributesData().Any(a => a.GetAttributeType().FullName == MessagePackMemberAttributeTypeName) + ).ToArray(); - if ( filtered.Length > 0 ) + if (filtered.Length > 0) { - return GetAnnotatedMembersWithDuplicationDetection( type, filtered ); + return GetAnnotatedMembersWithDuplicationDetection(type, filtered); } - if ( -#if !UNITY - type.GetCustomAttributesData().Any( attr => - attr.GetAttributeType().FullName == "System.Runtime.Serialization.DataContractAttribute" ) -#else - type.GetCustomAttributes( true ).Any( attr => - attr.GetType().FullName == "System.Runtime.Serialization.DataContractAttribute" ) -#endif // !UNITY - ) + var compatibleMembers = GetCompatibleMembers(type.GetCustomAttributesData(), members, options).ToArray(); + if (compatibleMembers.Any()) { - return GetSystemRuntimeSerializationCompatibleMembers( members ); + return compatibleMembers; } - return GetPublicUnpreventedMembers( members ); + return GetPublicUnpreventedMembers(members, options); } - private static IEnumerable GetAnnotatedMembersWithDuplicationDetection( Type type, MemberInfo[] filtered ) + private static IEnumerable GetAnnotatedMembersWithDuplicationDetection(Type type, MemberInfo[] filtered) { var duplicated = filtered.FirstOrDefault( -#if !UNITY - member => member.GetCustomAttributesData().Any( a => a.GetAttributeType().FullName == MessagePackIgnoreAttributeTypeName ) -#else - member => member.GetCustomAttributes( typeof( MessagePackIgnoreAttribute ), true ).Any() -#endif // !UNITY + member => member.GetCustomAttributesData().Any(a => a.GetAttributeType().FullName == MessagePackIgnoreAttributeTypeName) ); - if ( duplicated != null ) + if (duplicated != null) { - throw new SerializationException( - String.Format( - CultureInfo.CurrentCulture, - "A member '{0}' of type '{1}' is marked with both MessagePackMemberAttribute and MessagePackIgnoreAttribute.", - duplicated.Name, - type - ) - ); + Throw.MemberIsMarkedWithMemberAndIgnoreAttribute(type, duplicated.Name); } return filtered.Select( member => { -#if !UNITY - var attribute = member.GetCustomAttributesData().Single( a => a.GetAttributeType().FullName == MessagePackMemberAttributeTypeName ); + var attribute = member.GetCustomAttributesData().Single(a => a.GetAttributeType().FullName == MessagePackMemberAttributeTypeName); return new SerializingMember( member, new DataMemberContract( member, - ( string )GetAttributeProperty( MessagePackMemberAttributeTypeName, attribute, "Name" ), -#if !SILVERLIGHT - ( NilImplication )( ( int? )GetAttributeProperty( MessagePackMemberAttributeTypeName, attribute, "NilImplication" ) ).GetValueOrDefault(), - ( int? )GetAttributeArgument( MessagePackMemberAttributeTypeName, attribute, 0 ) -#else - ( NilImplication )GetAttributeProperty( MessagePackMemberAttributeTypeName, attribute, "NilImplication" ), - ( int? )GetAttributeProperty( MessagePackMemberAttributeTypeName, attribute, "Id" ) -#endif // !SILVERLIGHT - ) - ); -#else - var attribute = member.GetCustomAttributes( typeof( MessagePackMemberAttribute ), true ).Single() as MessagePackMemberAttribute; - return - new SerializingMember( - member, - new DataMemberContract( - member, - attribute.Name, - attribute.NilImplication, - attribute.Id + (string?)GetAttributeProperty(MessagePackMemberAttributeTypeName, attribute, "Name"), + (NilImplication)((int?)GetAttributeProperty(MessagePackMemberAttributeTypeName, attribute, "NilImplication")).GetValueOrDefault(), + (int?)GetAttributeArgument(MessagePackMemberAttributeTypeName, attribute, 0) ) ); -#endif // !UNITY } ); } -#if !UNITY -#if !SILVERLIGHT - private static object GetAttributeArgument( string attributeName, CustomAttributeData attribute, int index ) + private static object? GetAttributeArgument(string attributeName, CustomAttributeData attribute, int index) { var arguments = attribute.GetConstructorArguments(); - if ( arguments.Count < index ) + if (arguments.Count < index) { // Use default. return null; } - return arguments[ index ].Value; + return arguments[index].Value; } -#endif // !SILVERLIGHT -#if !SILVERLIGHT - private static object GetAttributeProperty( string attributeName, CustomAttributeData attribute, string propertyName ) -#else - private static object GetAttributeProperty( string attributeName, Attribute attribute, string propertyName ) -#endif // !SILVERLIGHT + private static object? GetAttributeProperty(string attributeName, CustomAttributeData attribute, string propertyName) { - var property = attribute.GetNamedArguments().SingleOrDefault( a => a.GetMemberName() == propertyName ); - if ( property.GetMemberName() == null ) + var property = attribute.GetNamedArguments().SingleOrDefault(a => a.GetMemberName() == propertyName); + if (property.GetMemberName() == null) { // Use default. return null; @@ -492,98 +416,63 @@ private static object GetAttributeProperty( string attributeName, Attribute attr return property.GetTypedValue().Value; } -#endif // !UNITY - private static IEnumerable GetSystemRuntimeSerializationCompatibleMembers( MemberInfo[] members ) + private static MessagePackMemberAttributeData? ProvideDefaultMessagePackMemberAttributeCompatibility( + IEnumerable typeAttributes, + IEnumerable memberAttributes + ) { - return - members.Select( - item => - new - { - member = item, - data = -#if !UNITY - item.GetCustomAttributesData() - .FirstOrDefault( - data => data.GetAttributeType().FullName == "System.Runtime.Serialization.DataMemberAttribute" - ) -#else - item.GetCustomAttributes( true ) - .FirstOrDefault( - data => data.GetType().FullName == "System.Runtime.Serialization.DataMemberAttribute" - ) -#endif // !UNITY - } - ).Where( item => item.data != null ) - .Select( - item => - { -#if !UNITY - var name = - item.data.GetNamedArguments() - .Where( arg => arg.GetMemberName() == "Name" ) - .Select( arg => ( string )arg.GetTypedValue().Value ) - .FirstOrDefault(); - var id = - item.data.GetNamedArguments() - .Where( arg => arg.GetMemberName() == "Order" ) - .Select( arg => ( int? )arg.GetTypedValue().Value ) - .FirstOrDefault(); - if ( id == -1 ) - { - id = null; - } - - return - new SerializingMember( - item.member, - new DataMemberContract( item.member, name, NilImplication.MemberDefault, id ) - ); -#else - var nameProperty = item.data.GetType().GetProperty( "Name" ); - var orderProperty = item.data.GetType().GetProperty( "Order" ); - - if ( nameProperty == null || !nameProperty.CanRead || nameProperty.GetGetMethod().GetParameters().Length > 0 ) - { - throw new SerializationException( - String.Format( - CultureInfo.CurrentCulture, - "Failed to get Name property from {0} type.", - item.data.GetType().AssemblyQualifiedName - ) - ); - } - - if( orderProperty == null || !orderProperty.CanRead || orderProperty.GetGetMethod().GetParameters().Length > 0 ) - { - throw new SerializationException( - String.Format( - CultureInfo.CurrentCulture, - "Failed to get Order property from {0} type.", - item.data.GetType().AssemblyQualifiedName - ) - ); - } - - var name = ( string )nameProperty.GetValue( item.data, null ); - var id = ( int? )orderProperty.GetValue( item.data, null ); - if ( id == -1 ) - { - id = null; - } - - return - new SerializingMember( - item.member, - new DataMemberContract( item.member, name, NilImplication.MemberDefault, id ) - ); -#endif // !UNITY - } - ); + if (!typeAttributes.Any(a => a.GetAttributeType().FullName == "System.Runtime.Serialization.DataContractAttribute")) + { + return null; + } + + var dataMemberAttribute = + memberAttributes + .FirstOrDefault(a => + a.GetAttributeType() + .FullName == "System.Runtime.Serialization.DataMemberAttribute" + ); + if (dataMemberAttribute == null) + { + return null; + } + + var name = default(string?); + var order = default(int?); + foreach(var namedArgument in dataMemberAttribute.GetNamedArguments()) + { + var memberName = namedArgument.GetMemberName(); + if (memberName == "Name") + { + name = (string?)namedArgument.GetTypedValue().Value; + } + else if(memberName == "Order") + { + order = (int?)namedArgument.GetTypedValue().Value; + } + } + + return new MessagePackMemberAttributeData(order < 0 ? null : order, name); } - private static IEnumerable GetPublicUnpreventedMembers( MemberInfo[] members ) + private static IEnumerable GetCompatibleMembers(IEnumerable typeAttributes, MemberInfo[] members, SerializerGenerationOptions options) + => members.Select(m => + ( + Member: m, + Attribute: (options.MessagePackMemberAttributeCompatibilityProvider ?? ProvideDefaultMessagePackMemberAttributeCompatibility) + .Invoke(typeAttributes, m.GetCustomAttributesData()) + ) + ).Where(x => x.Attribute != null) + .Select(x => + new SerializingMember( + x.Member, + // TODO: NilImplication + new DataMemberContract(x.Member, x.Attribute.GetValueOrDefault().Name, NilImplication.MemberDefault, x.Attribute.GetValueOrDefault().Id) + ) + ); + + private static IEnumerable GetPublicUnpreventedMembers(MemberInfo[] members, SerializerGenerationOptions options) { return members.Where( member => @@ -591,50 +480,50 @@ private static IEnumerable GetPublicUnpreventedMembers( Membe && !member #if !UNITY .GetCustomAttributesData() - .Select( data => data.GetAttributeType().FullName ) + .Select(data => data.GetAttributeType().FullName!) #else .GetCustomAttributes( true ) - .Select( data => data.GetType().FullName ) + .Select(data => data.GetType().FullName) #endif // !UNITY - .Any( attr => - attr == "MsgPack.Serialization.MessagePackIgnoreAttribute" - || attr == "System.Runtime.Serialization.IgnoreDataMemberAttribute" - ) - && !IsNonSerializedField( member ) - ).Select( member => new SerializingMember( member, new DataMemberContract( member ) ) ); + .Any(t => options.IgnoreAttributeTypeNames.Contains(t)) + && !IsNonSerializedField(member) + ).Select(member => new SerializingMember(member, new DataMemberContract(member))); } - private static bool IsNonSerializedField( MemberInfo member ) + private static bool IsNonSerializedField(MemberInfo member) { var asField = member as FieldInfo; - if ( asField == null ) + if (asField == null) { return false; } - return ( asField.Attributes & FieldAttributes.NotSerialized ) != 0; + return (asField.Attributes & FieldAttributes.NotSerialized) != 0; } - private static ConstructorInfo FindDeserializationConstructor( SerializationContext context, Type targetType, out ConstructorKind constructorKind ) + private static ConstructorInfo? FindDeserializationConstructor(DiagnosticSource diag, SerializerGenerationOptions options, Type targetType, out ConstructorKind constructorKind) { var constructors = targetType.GetConstructors().ToArray(); - if ( constructors.Length == 0 ) + if (constructors.Length == 0) { - if ( context.CompatibilityOptions.AllowAsymmetricSerializer ) + if (options.AllowsAsymmetricSerializer) { constructorKind = ConstructorKind.None; return null; } else { - throw NewTypeCannotBeSerializedException( targetType ); + Throw.TypeCannotBeSerializedBecauseNoMembersAndNoParameterizedPublicConstructors(targetType); + // never + constructorKind = default; + return default; } } // The marked construtor is always preferred. - var markedConstructors = FindExplicitDeserializationConstructors( constructors ); - switch ( markedConstructors.Count ) + var markedConstructors = FindExplicitDeserializationConstructors(constructors); + switch (markedConstructors.Count) { case 0: { @@ -644,126 +533,114 @@ private static ConstructorInfo FindDeserializationConstructor( SerializationCont { // OK use it for deserialization. constructorKind = ConstructorKind.Marked; - return markedConstructors[ 0 ]; + return markedConstructors[0]; } default: { - throw new SerializationException( - String.Format( - CultureInfo.CurrentCulture, - "There are multiple constructors marked with MessagePackDeserializationConstrutorAttribute in type '{0}'.", - targetType - ) - ); + Throw.TypeCannotBeSerializedBecauseThereAreMultipleMessagePackDeserializationConstrutorAttribute(targetType); + // never + constructorKind = default; + return default; } } // A constructor which has most parameters will be used. var mostRichConstructors = - constructors.GroupBy( ctor => ctor.GetParameters().Length ).OrderByDescending( g => g.Key ).First().ToArray(); -#if DEBUG - Trace( "SerializationTarget::FindDeserializationConstructor.MostRich({0}) -> {1}", targetType, String.Join( ";", mostRichConstructors.Select( x => x.ToString() ).ToArray() ) ); -#endif // DEBUG - switch ( mostRichConstructors.Length ) + constructors.GroupBy(ctor => ctor.GetParameters().Length).OrderByDescending(g => g.Key).First().ToArray(); + + diag.DeserializationConstructorFound( + new + { + targetType = targetType, + constructors = mostRichConstructors.Select(x => x.ToString()).ToArray() + } + ); + + switch (mostRichConstructors.Length) { case 1: { - if ( mostRichConstructors[ 0 ].GetParameters().Length == 0 ) + if (mostRichConstructors[0].GetParameters().Length == 0) { - if ( context.CompatibilityOptions.AllowAsymmetricSerializer ) + if (options.AllowsAsymmetricSerializer) { constructorKind = ConstructorKind.Default; - return mostRichConstructors[ 0 ]; + return mostRichConstructors[0]; } else { - throw NewTypeCannotBeSerializedException( targetType ); + Throw.TypeCannotBeSerializedBecauseNoMembersAndNoParameterizedPublicConstructors(targetType); + // never + constructorKind = default; + return default; } } // OK try use it but it may not handle deserialization correctly. constructorKind = ConstructorKind.Parameterful; - return mostRichConstructors[ 0 ]; + return mostRichConstructors[0]; } default: { - if ( context.CompatibilityOptions.AllowAsymmetricSerializer ) + if (options.AllowsAsymmetricSerializer) { constructorKind = ConstructorKind.Ambiguous; return null; } else { - throw new SerializationException( - String.Format( - CultureInfo.CurrentCulture, - "Cannot serialize type '{0}' because it does not have any serializable fields nor properties, and serializer generator failed to determine constructor to deserialize among({1}).", - targetType, - String.Join( ", ", mostRichConstructors.Select( ctor => ctor.ToString() ).ToArray() ) - ) - ); + Throw.TypeCannotBeSerializedBecauseNoMembersAndAmbigiousConstructors(targetType, mostRichConstructors); + // never + constructorKind = default; + return default; } } } } - private static IList FindExplicitDeserializationConstructors( IEnumerable construtors ) + private static IList FindExplicitDeserializationConstructors(IEnumerable construtors) { return construtors - .Where( ctor => + .Where(ctor => #if !UNITY - ctor.GetCustomAttributesData().Any( a => - a.GetAttributeType().FullName + ctor.GetCustomAttributesData().Any(a => + a.GetAttributeType().FullName #else ctor.GetCustomAttributes( true ).Any( a => a.GetType().FullName #endif // !UNITY == MessagePackDeserializationConstructorAttributeTypeName - ) + ) ).ToArray(); } - private static SerializationException NewTypeCannotBeSerializedException( Type targetType ) - { - return - new SerializationException( - String.Format( - CultureInfo.CurrentCulture, - "Cannot serialize type '{0}' because it does not have any serializable fields nor properties, and it does not have any public constructors with parameters.", - targetType - ) - ); - } - - private static bool CheckTargetEligibility( SerializationContext context, MemberInfo member ) + private static bool CheckTargetEligibility(SerializerGenerationOptions options, MemberInfo member) { - var asProperty = member as PropertyInfo; - var asField = member as FieldInfo; Type returnType; - if ( asProperty != null ) + if (member is PropertyInfo asProperty) { - if ( asProperty.GetIndexParameters().Length > 0 ) + if (asProperty.GetIndexParameters().Length > 0) { // Indexer cannot be target except the type itself implements IDictionary or IDictionary return false; } #if !NETSTANDARD1_1 && !NETSTANDARD1_3 - var setter = asProperty.GetSetMethod( true ); + var setter = asProperty.GetSetMethod(true); #else var setter = asProperty.SetMethod; #endif // !NETSTANDARD1_1 && !NETSTANDARD1_3 - if ( setter != null ) + if (setter != null) { - if ( setter.GetIsPublic() ) + if (setter.GetIsPublic()) { return true; } - if ( !context.SerializerOptions.DisablePrivilegedAccess ) + if (!options.DisablesPrivilegedAccess) { // Can deserialize non-public setter if privileged. return true; @@ -772,9 +649,9 @@ private static bool CheckTargetEligibility( SerializationContext context, Member returnType = asProperty.PropertyType; } - else if ( asField != null ) + else if (member is FieldInfo asField) { - if ( !asField.IsInitOnly ) + if (!asField.IsInitOnly) { return true; } @@ -783,15 +660,12 @@ private static bool CheckTargetEligibility( SerializationContext context, Member } else { -#if DEBUG - Contract.Assert( false, "Unknown type member " + member ); -#endif // DEBUG - // ReSharper disable once HeuristicUnreachableCode + Debug.Fail($"Unknown type member '{member}'"); return true; } - var traits = returnType.GetCollectionTraits( CollectionTraitOptions.WithAddMethod, allowNonCollectionEnumerableTypes: false ); - switch ( traits.CollectionType ) + var traits = returnType.GetCollectionTraits(CollectionTraitOptions.WithAddMethod, allowNonCollectionEnumerableTypes: false); + switch (traits.CollectionType) { case CollectionKind.Array: case CollectionKind.Map: @@ -805,27 +679,25 @@ private static bool CheckTargetEligibility( SerializationContext context, Member } } - private static IList ComplementMembers( IList candidates, SerializationContext context, Type targetType ) + private static IReadOnlyList ComplementMembers(IReadOnlyList candidates, SerializerGenerationOptions options, Type targetType) { - if ( candidates.Count == 0 ) + if (candidates.Count == 0) { return candidates; } - if ( candidates[ 0 ].Contract.Id < 0 ) + if (candidates[0].Contract.Id < 0) { return candidates; } - if ( context.CompatibilityOptions.OneBoundDataMemberOrder && candidates[ 0 ].Contract.Id == 0 ) + if (options.OneBoundDataMemberOrder && candidates[0].Contract.Id == 0) { - throw new NotSupportedException( - "Cannot specify order value 0 on DataMemberAttribute when SerializationContext.CompatibilityOptions.OneBoundDataMemberOrder is set to true." - ); + Throw.DataMemberAttributeCannotBeZeroWhenOneBoundDataMemberOrderIsTrue(targetType, candidates[0].MemberName); } #if !UNITY - var maxId = candidates.Max( item => item.Contract.Id ); + var maxId = candidates.Max(item => item.Contract.Id); #else int maxId = -1; foreach ( var id in candidates.Select( item => item.Contract.Id ) ) @@ -833,148 +705,121 @@ private static IList ComplementMembers( IList( maxId + 1 ); - for ( int source = 0, destination = context.CompatibilityOptions.OneBoundDataMemberOrder ? 1 : 0; + var result = new List(maxId + 1); + for (int source = 0, destination = options.OneBoundDataMemberOrder ? 1 : 0; source < candidates.Count; - source++, destination++ ) + source++, destination++) { -#if DEBUG - Contract.Assert( candidates[ source ].Contract.Id >= 0, "candidates[ source ].Contract.Id >= 0" ); -#endif // DEBUG + Debug.Assert(candidates[source].Contract.Id >= 0, $"candidates[source].Contract.Id ({candidates[source].Contract.Id}) >= 0"); - if ( candidates[ source ].Contract.Id < destination ) + if (candidates[source].Contract.Id < destination) { - throw new SerializationException( - String.Format( - CultureInfo.CurrentCulture, - "The member ID '{0}' is duplicated in the '{1}' elementType.", - candidates[ source ].Contract.Id, - targetType - ) - ); + Throw.MemberIdIsDuplicated(candidates[source].Contract.Id, targetType); } - while ( candidates[ source ].Contract.Id > destination ) + while (candidates[source].Contract.Id > destination) { - result.Add( new SerializingMember() ); + result.Add(new SerializingMember()); destination++; } - result.Add( candidates[ source ] ); + result.Add(candidates[source]); } - VerifyNilImplication( targetType, result ); - VerifyKeyUniqueness( result ); + VerifyNilImplication(targetType, result); + VerifyKeyUniqueness(result); return result; } - private static void VerifyNilImplication( Type type, IEnumerable entries ) + private static void VerifyNilImplication(Type type, IEnumerable entries) { - foreach ( var serializingMember in entries ) + foreach (var serializingMember in entries) { - if ( serializingMember.Contract.NilImplication == NilImplication.Null ) + Debug.Assert(serializingMember.Member != null, "VerifyNilImplication is called for Tuple."); + + if (serializingMember.Contract.NilImplication == NilImplication.Null) { var itemType = serializingMember.Member.GetMemberValueType(); - if ( itemType != typeof( MessagePackObject ) - && itemType.GetIsValueType() - && Nullable.GetUnderlyingType( itemType ) == null ) + if (!itemType.IsDefined(typeof(MessagePackNullableAttribute)) + && !itemType.IsNullableType()) { - SerializationExceptions.ThrowValueTypeCannotBeNull( serializingMember.Member.ToString(), itemType, type ); + Throw.ValueTypeCannotBeNull(serializingMember.Member.ToString(), itemType, type); } bool isReadOnly; - FieldInfo asField; - PropertyInfo asProperty; - if ( ( asField = serializingMember.Member as FieldInfo ) != null ) + if (serializingMember.Member is FieldInfo asField) { isReadOnly = asField.IsInitOnly; } else { - asProperty = serializingMember.Member as PropertyInfo; -#if DEBUG - Contract.Assert( asProperty != null, serializingMember.Member.ToString() ); -#endif // DEBUG + var asProperty = serializingMember.Member as PropertyInfo; + Debug.Assert(asProperty != null, serializingMember.Member.ToString()); isReadOnly = asProperty.GetSetMethod() == null; } - if ( isReadOnly ) + if (isReadOnly) { - SerializationExceptions.ThrowNullIsProhibited( serializingMember.Member.ToString() ); + Throw.NullIsProhibitedForReadOnlyMember(serializingMember.Member.ToString()); } } } } - private static void VerifyKeyUniqueness( IList result ) + private static void VerifyKeyUniqueness(IList result) { var duplicated = new Dictionary>(); var existents = new Dictionary(); - foreach ( var member in result ) + foreach (var member in result) { - if ( member.Contract.Name == null ) + if (member.Contract.Name == null) { continue; } + Debug.Assert(member.Member != null, "VerifyKeyUniqueness is called for Tuple."); + try { - existents.Add( member.Contract.Name, member ); + existents.Add(member.Contract.Name, member); } - catch ( ArgumentException ) + catch (ArgumentException) { - List list; - if ( duplicated.TryGetValue( member.Contract.Name, out list ) ) + if (duplicated.TryGetValue(member.Contract.Name, out var list)) { - list.Add( member.Member ); + list.Add(member.Member); } else { - duplicated.Add( member.Contract.Name, new List { existents[ member.Contract.Name ].Member, member.Member } ); + duplicated.Add(member.Contract.Name, new List { existents[member.Contract.Name].Member!, member.Member }); } } } - if ( duplicated.Count > 0 ) + if (duplicated.Count > 0) { - throw new InvalidOperationException( - String.Format( - CultureInfo.CurrentCulture, - "Some member keys specified with custom attributes are duplicated. Details: {{{0}}}", - String.Join( - ",", - duplicated.Select( - kv => String.Format( - CultureInfo.CurrentCulture, - "\"{0}\":[{1}]", - kv.Key, - String.Join( ",", kv.Value.Select( m => String.Format( CultureInfo.InvariantCulture, "{0}.{1}({2})", m.DeclaringType, m.Name, ( m is FieldInfo ) ? "Field" : "Property" ) ).ToArray() ) - ) - ).ToArray() - ) - ) - ); + Throw.DuplicatedAttributes(duplicated); } } - public static SerializationTarget CreateForCollection( ConstructorInfo collectionConstructor, bool canDeserialize ) - { - return new SerializationTarget( EmptyMembers, collectionConstructor, EmptyStrings, canDeserialize ); - } + public static SerializationTarget CreateForCollection(Type targetType, CollectionTraits traits, ConstructorInfo collectionConstructor, bool canDeserialize) + => new SerializationTarget(targetType, traits, Array.Empty(), collectionConstructor, Array.Empty(), canDeserialize); -#if !NET35 - public static SerializationTarget CreateForTuple( IList itemTypes ) - { - return new SerializationTarget( itemTypes.Select( ( _, i ) => new SerializingMember( GetTupleItemNameFromIndex( i ) ) ).ToArray(), null, null, true ); - } + public static SerializationTarget CreateForTuple(Type targetType) + => new SerializationTarget( + targetType, + CollectionTraits.NotCollection, + TupleItems.GetTupleItemMembers(targetType).Select((m, i) => new SerializingMember(m, GetTupleItemNameFromIndex(i))).ToArray(), + constructor: null, + correspondentMemberNames: null, + canDeserialize: true + ); - public static string GetTupleItemNameFromIndex( int i ) - { - return "Item" + ( i + 1 ).ToString( "D", CultureInfo.InvariantCulture ); - } -#endif // !NET35 + public static string GetTupleItemNameFromIndex(int i) + => $"Item{i + 1:D}"; +#warning TODO: REMOVE_CODEGEN #if FEATURE_CODEGEN public static bool BuiltInSerializerExists( ISerializerGeneratorConfiguration configuration, Type type, CollectionTraits traits ) { @@ -988,25 +833,17 @@ private sealed class MemberConstructorParameterEqualityComparer : EqualityCompar private MemberConstructorParameterEqualityComparer() { } - public override bool Equals( KeyValuePair x, KeyValuePair y ) + public override bool Equals(KeyValuePair x, KeyValuePair y) { - return String.Equals( x.Key, y.Key, StringComparison.OrdinalIgnoreCase ) && x.Value == y.Value; + return String.Equals(x.Key, y.Key, StringComparison.OrdinalIgnoreCase) && x.Value == y.Value; } - public override int GetHashCode( KeyValuePair obj ) + public override int GetHashCode(KeyValuePair obj) { - return ( obj.Key == null ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode( obj.Key ) ) ^ ( obj.Value == null ? 0 : obj.Value.GetHashCode() ); + return (obj.Key == null ? 0 : StringComparer.OrdinalIgnoreCase.GetHashCode(obj.Key)) ^ (obj.Value == null ? 0 : obj.Value.GetHashCode()); } } - [Conditional( "DEBUG" )] - private static void Trace( string format, params object[] args ) - { -#if !SILVERLIGHT && !WINDOWS_PHONE && !NETFX_CORE - Tracer.Binding.TraceEvent( Tracer.EventType.Trace, Tracer.EventId.Trace, format, args ); -#endif // !SILVERLIGHT && !WINDOWS_PHONE && !NETFX_CORE - } - private enum ConstructorKind { None = 0, diff --git a/src/MsgPack/Serialization/SerializerCapabilities.cs b/src/MsgPack/Serialization/SerializerCapabilities.cs index a6a941f2c..3b184dd1e 100644 --- a/src/MsgPack/Serialization/SerializerCapabilities.cs +++ b/src/MsgPack/Serialization/SerializerCapabilities.cs @@ -1,22 +1,6 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2015 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; @@ -36,19 +20,40 @@ public enum SerializerCapabilities /// /// Caller can call Pack and PackTo method safely. /// If this flag is not set, the serializer may be deserialize only serializer. + /// (v2) This value is synonym of . /// PackTo = 0x1, + /// + /// Caller can call Serialize method safely. + /// If this flag is not set, the serializer may be deserialize only serializer. + /// + Serialize = 0x1, + /// /// Caller can call Unpack and UnpackFrom method safely. /// If this flag is not set, the serializer may be serialize only serializer. + /// (v2) This value is synonym of . /// UnpackFrom = 0x10, + /// + /// Caller can call Deserialize method safely. + /// If this flag is not set, the serializer may be serialize only serializer. + /// + Deserialize = 0x10, + /// /// Caller can call UnpackTo method safely. /// If this flag is not set, the serializer should not be for mutable collection type. + /// (v2) This value is synonym of . + /// + UnpackTo = 0x20, + + /// + /// Caller can call DeserializeTo method safely. + /// If this flag is not set, the serializer should not be for mutable collection type. /// - UnpackTo = 0x20 + DeserializeTo = 0x20 } -} \ No newline at end of file +} diff --git a/src/MsgPack/Serialization/SerializerOptions.cs b/src/MsgPack/Serialization/SerializerOptions.cs index 156e36970..ce3e7c2eb 100644 --- a/src/MsgPack/Serialization/SerializerOptions.cs +++ b/src/MsgPack/Serialization/SerializerOptions.cs @@ -1,28 +1,9 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2012-2018 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; +using System.ComponentModel; #if FEATURE_MPCONTRACT using Contract = MsgPack.MPContract; #else @@ -34,7 +15,7 @@ #endif // NETSTANDARD1_3 || NETSTANDARD2_0 using System.Runtime.CompilerServices; using System.Threading; -#if ( NETSTANDARD1_3 || NETSTANDARD2_0 ) && !WINDOWS_UWP +#if (NETSTANDARD1_3 || NETSTANDARD2_0) && !WINDOWS_UWP using MsgPack.Serialization.EmittingSerializers; #endif // ( NETSTANDARD1_3 || NETSTANDARD2_0 ) && !WINDOWS_UWP @@ -45,32 +26,6 @@ namespace MsgPack.Serialization /// public sealed class SerializerOptions { -#if UNITY || SILVERLIGHT - private int _emitterFlavor = ( int )EmitterFlavor.ReflectionBased; -#else - private int _emitterFlavor = ( int )EmitterFlavor.FieldBased; -#endif - - /// - /// Gets or sets the . - /// - /// - /// The - /// - /// - /// For testing purposes. - /// -#if UNITY && DEBUG - public -#else - internal -#endif - EmitterFlavor EmitterFlavor - { - get { return ( EmitterFlavor )Volatile.Read( ref this._emitterFlavor ); } - set { Volatile.Write( ref this._emitterFlavor, ( int )value ); } - } - #if !UNITY private int _generatorOption; @@ -86,13 +41,13 @@ public SerializationMethodGeneratorOption GeneratorOption { get { - Contract.Ensures( Enum.IsDefined( typeof( SerializationMethod ), Contract.Result() ) ); + Contract.Ensures(Enum.IsDefined(typeof(SerializationMethod), Contract.Result())); - return ( SerializationMethodGeneratorOption )Volatile.Read( ref this._generatorOption ); + return (SerializationMethodGeneratorOption)Volatile.Read(ref this._generatorOption); } set { - switch ( value ) + switch (value) { case SerializationMethodGeneratorOption.Fast: #if !SILVERLIGHT @@ -106,13 +61,13 @@ public SerializationMethodGeneratorOption GeneratorOption } default: { - throw new ArgumentOutOfRangeException( "value" ); + throw new ArgumentOutOfRangeException("value"); } } Contract.EndContractBlock(); - Volatile.Write( ref this._generatorOption, ( int )value ); + Volatile.Write(ref this._generatorOption, (int)value); } } @@ -150,10 +105,10 @@ public bool DisableRuntimeCodeGeneration internal static readonly bool CanEmit = DetermineCanEmit(); - [MethodImpl( MethodImplOptions.NoInlining )] + [MethodImpl(MethodImplOptions.NoInlining)] private static bool DetermineCanEmit() { -#if ( NETSTANDARD1_3 || NETSTANDARD2_0 ) && !WINDOWS_UWP +#if (NETSTANDARD1_3 || NETSTANDARD2_0) && !WINDOWS_UWP try { return DetermineCanEmitCore(); @@ -170,7 +125,7 @@ private static bool DetermineCanEmit() #endif } -#if ( NETSTANDARD1_3 || NETSTANDARD2_0 ) && !WINDOWS_UWP +#if (NETSTANDARD1_3 || NETSTANDARD2_0) && !WINDOWS_UWP [MethodImpl( MethodImplOptions.NoInlining )] private static bool DetermineCanEmitCore() diff --git a/src/MsgPack/Serialization/SerializingMember.cs b/src/MsgPack/Serialization/SerializingMember.cs index f6e77e838..912f10069 100644 --- a/src/MsgPack/Serialization/SerializingMember.cs +++ b/src/MsgPack/Serialization/SerializingMember.cs @@ -1,34 +1,9 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2018 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- - -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. using System; -using System.Globalization; -#if FEATURE_MPCONTRACT -using MPContract = MsgPack.MPContract; -#else -using MPContract = System.Diagnostics.Contracts.Contract; -#endif // FEATURE_MPCONTRACT +using System.Diagnostics; using System.Reflection; namespace MsgPack.Serialization @@ -36,105 +11,47 @@ namespace MsgPack.Serialization /// /// Represents serializing member information. /// -#if !UNITY - internal struct SerializingMember -#else -#if UNITY && DEBUG - public -#else - internal -#endif - sealed class SerializingMember -#endif // !UNITY + internal readonly struct SerializingMember { - public readonly MemberInfo Member; - public readonly DataMemberContract Contract; - public readonly string MemberName; + public MemberInfo Member { get; } + public DataMemberContract Contract { get; } + public string MemberName { get; } + public Utf8String Utf8MemberName { get; } -#if UNITY - public SerializingMember() + public SerializingMember(MemberInfo member, DataMemberContract contract) { - this.Member = null; - this.Contract = new DataMemberContract (); - } -#endif // UNITY - - public SerializingMember( MemberInfo member, DataMemberContract contract ) - { -#if DEBUG - MPContract.Assert( member != null ); -#endif // DEBUG this.Member = member; this.Contract = contract; // Use contract name for aliased map serialization. - this.MemberName = member == null ? null : contract.Name; + this.MemberName = contract.Name; + this.Utf8MemberName = new Utf8String(contract.Name); } -#if !NET35 - // For Tuple - public SerializingMember( string name ) + // for tuple + public SerializingMember(MemberInfo member, string name) { -#if DEBUG - MPContract.Assert( name.StartsWith( "Item" ), name + ".StartsWith(\"Item\")" ); -#endif // DEBUG - this.Member = null; - this.Contract = default ( DataMemberContract ); + this.Member = member; + this.Contract = default; + // Use contract name for aliased map serialization. this.MemberName = name; + this.Utf8MemberName = new Utf8String(name); } -#endif // !NET35 public EnumMemberSerializationMethod GetEnumMemberSerializationMethod() - { -#if NETSTANDARD1_1 || NETSTANDARD1_3 - var messagePackEnumMemberAttribute = - this.Member.GetCustomAttribute(); - if ( messagePackEnumMemberAttribute != null) - { - return messagePackEnumMemberAttribute.SerializationMethod; -#else - var messagePackEnumMemberAttributes = - this.Member.GetCustomAttributes( typeof( MessagePackEnumMemberAttribute ), true ); - if ( messagePackEnumMemberAttributes.Length > 0 ) - { - return - // ReSharper disable once PossibleNullReferenceException - ( messagePackEnumMemberAttributes[ 0 ] as MessagePackEnumMemberAttribute ).SerializationMethod; -#endif // NETSTANDARD1_1 || NETSTANDARD1_3 - } - - return EnumMemberSerializationMethod.Default; - } + => this.Member.GetCustomAttribute(inherit: true)?.SerializationMethod ?? EnumMemberSerializationMethod.Default; public DateTimeMemberConversionMethod GetDateTimeMemberConversionMethod() - { -#if NETSTANDARD1_1 || NETSTANDARD1_3 - var messagePackDateTimeMemberAttribute = - this.Member.GetCustomAttribute(); - if ( messagePackDateTimeMemberAttribute != null) - { - return messagePackDateTimeMemberAttribute.DateTimeConversionMethod; -#else - var messagePackDateTimeMemberAttribute = - this.Member.GetCustomAttributes( typeof( MessagePackDateTimeMemberAttribute ), true ); - if ( messagePackDateTimeMemberAttribute.Length > 0 ) - { - return - // ReSharper disable once PossibleNullReferenceException - ( messagePackDateTimeMemberAttribute[ 0 ] as MessagePackDateTimeMemberAttribute ).DateTimeConversionMethod; -#endif // NETSTANDARD1_1 || NETSTANDARD1_3 - } - - return DateTimeMemberConversionMethod.Default; - } + => this.Member.GetCustomAttribute(inherit: true)?.DateTimeConversionMethod ?? DateTimeMemberConversionMethod.Default; public override string ToString() { - if ( this.MemberName == null ) + if (this.MemberName == null) { + // default instance. return String.Empty; } - return String.Format( CultureInfo.InvariantCulture, "{{\"Name\": \"{0}\", \"Id\": {1}, \"Member\": \"{2}\", \"NilImplication\": \"{3}\" }}", this.MemberName, this.Contract.Id, this.MemberName, this.Contract.NilImplication ); + return $"{{\"Name\": \"{this.MemberName}\", \"Id\": {this.Contract.Id}, \"Member\": \"{this.Member}\", \"NilImplication\": \"{this.Contract.NilImplication}\" }}"; } } } diff --git a/src/MsgPack/TupleItems.cs b/src/MsgPack/TupleItems.cs index 37963d470..3920a8dd1 100644 --- a/src/MsgPack/TupleItems.cs +++ b/src/MsgPack/TupleItems.cs @@ -1,36 +1,12 @@ -#region -- License Terms -- -// -// MessagePack for CLI -// -// Copyright (C) 2010-2017 FUJIWARA, Yusuke -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -#endregion -- License Terms -- +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. -#if UNITY_5 || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_WII || UNITY_IPHONE || UNITY_ANDROID || UNITY_PS3 || UNITY_XBOX360 || UNITY_FLASH || UNITY_BKACKBERRY || UNITY_WINRT -#define UNITY -#endif - -#if !UNITY using System; using System.Collections.Generic; -#if FEATURE_MPCONTRACT -using Contract = MsgPack.MPContract; -#else -using System.Diagnostics.Contracts; -#endif // FEATURE_MPCONTRACT +using System.Diagnostics; using System.Linq; +using System.Reflection; namespace MsgPack { @@ -47,29 +23,29 @@ internal static class TupleItems /// The type list for nested tuples. /// The order is from outer to inner. /// - public static List CreateTupleTypeList( Type rootTupleType ) + public static List CreateTupleTypeList(Type rootTupleType) { - if ( !rootTupleType.GetIsGenericType() ) + if (!rootTupleType.GetIsGenericType()) { // arity 0 value tuple - return new List( 1 ) { rootTupleType }; + return new List(1) { rootTupleType }; } var assembly = rootTupleType.GetAssembly(); - var baseName = rootTupleType.FullName.Remove( rootTupleType.FullName.IndexOf( '`' ) + 1 ); + var baseName = rootTupleType.FullName!.Remove(rootTupleType.FullName.IndexOf('`') + 1); var result = new List(); var tupleType = rootTupleType; - while ( true ) + while (true) { - result.Add( tupleType ); - if ( !tupleType.GetIsGenericType() ) + result.Add(tupleType); + if (!tupleType.GetIsGenericType()) { // arity 0 break; } var itemTypes = tupleType.GetGenericArguments(); - if ( itemTypes.Length < 8 ) + if (itemTypes.Length < 8) { // leaf tuple break; @@ -81,45 +57,66 @@ public static List CreateTupleTypeList( Type rootTupleType ) return result; } - public static IList GetTupleItemTypes( Type tupleType ) + public static IReadOnlyList GetTupleItemMembers(Type tupleType) + { + Debug.Assert(IsTuple(tupleType), $"IsTuple({tupleType.AssemblyQualifiedName})"); + var members = new List(tupleType.GetGenericArguments().Length); + GetTupleItemMembers(tupleType, members); + return members; + } + + private static void GetTupleItemMembers(Type tupleType, IList result) + { + var properties = tupleType.GetProperties(BindingFlags.Public | BindingFlags.Instance); + var count = properties.Length == 8 ? 7 : properties.Length; + for (var i = 0; i < count; i++) + { + result.Add(properties[i]); + } + + if (properties.Length == 8) + { + var rest = properties[7]; + Debug.Assert(IsTuple(rest.PropertyType), $"IsTuple({rest.PropertyType.AssemblyQualifiedName})"); + // Put nested tuple's item types recursively. + GetTupleItemMembers(rest.PropertyType, result); + } + } + + public static IReadOnlyList GetTupleItemTypes(Type tupleType) { -#if DEBUG - Contract.Assert( IsTuple( tupleType ), "IsTuple( "+ tupleType.AssemblyQualifiedName + " )" ); -#endif // DEBUG + Debug.Assert(IsTuple(tupleType), $"IsTuple({tupleType.AssemblyQualifiedName})"); var arguments = tupleType.GetGenericArguments(); - var itemTypes = new List( tupleType.GetGenericArguments().Length ); - GetTupleItemTypes( arguments, itemTypes ); + var itemTypes = new List(tupleType.GetGenericArguments().Length); + GetTupleItemTypes(arguments, itemTypes); return itemTypes; } - private static void GetTupleItemTypes( IList itemTypes, IList result ) + private static void GetTupleItemTypes(IList itemTypes, IList result) { var count = itemTypes.Count == 8 ? 7 : itemTypes.Count; - for ( var i = 0; i < count; i++ ) + for (var i = 0; i < count; i++) { - result.Add( itemTypes[ i ] ); + result.Add(itemTypes[i]); } - if ( itemTypes.Count == 8 ) + if (itemTypes.Count == 8) { - var trest = itemTypes[ 7 ]; -#if DEBUG - Contract.Assert( IsTuple( trest ), "IsTuple( " + trest.AssemblyQualifiedName + " )" ); -#endif // DEBUG + var trest = itemTypes[7]; + Debug.Assert(IsTuple(trest), $"IsTuple({trest.AssemblyQualifiedName})"); // Put nested tuple's item types recursively. - GetTupleItemTypes( trest.GetGenericArguments(), result ); + GetTupleItemTypes(trest.GetGenericArguments(), result); } } - public static bool IsTuple( Type type ) + public static bool IsTuple(Type type) { return type.GetIsPublic() - && ( ( type.FullName.StartsWith( "System.ValueTuple`", StringComparison.Ordinal ) && type.GetIsValueType() ) - || ( type.FullName.StartsWith( "System.Tuple`", StringComparison.Ordinal ) && !type.GetIsValueType() ) - || ( type.FullName == "System.ValueTuple" && type.GetIsValueType() ) + && ((type.FullName!.StartsWith("System.ValueTuple`", StringComparison.Ordinal) && type.GetIsValueType()) + || (type.FullName.StartsWith("System.Tuple`", StringComparison.Ordinal) && !type.GetIsValueType()) + || (type.FullName == "System.ValueTuple" && type.GetIsValueType()) ); } } } -#endif // !UNITY diff --git a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs index ec0a34f72..78875f3b1 100644 --- a/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs +++ b/test/Benchmark/MessagePackCsharp/Benchmark/Serializers/MsgPackCliSerializer.cs @@ -40,41 +40,41 @@ internal static class MsgPackCliSerializerRepository { public static readonly MsgPack.Serialization.MessagePackSerializer V1 = SampleSerializer.SerializationContext.GetSerializer(); - public static readonly newmpcli::MsgPack.Serialization.Internal.IObjectSerializer V2 = InitV2(); + public static readonly newmpcli::MsgPack.Serialization.ObjectSerializer V2 = InitV2(); - public static readonly newmpcli::MsgPack.Serialization.Internal.IObjectSerializer Json = InitJson(); + public static readonly newmpcli::MsgPack.Serialization.ObjectSerializer Json = InitJson(); - private static newmpcli::MsgPack.Serialization.Internal.IObjectSerializer InitV2() + private static newmpcli::MsgPack.Serialization.ObjectSerializer InitV2() { if (typeof(MsgPack.Samples.SampleObject).IsAssignableFrom(typeof(T))) { - return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleSerializer(); + return (newmpcli::MsgPack.Serialization.ObjectSerializer)(object)new MsgPack.Samples.SampleSerializer(new newmpcli::MsgPack.Serialization.ObjectSerializationContext()); } else if (typeof(T) == typeof(int[])) { - return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleInt32ArraySerializer(); + return (newmpcli::MsgPack.Serialization.ObjectSerializer)(object)new MsgPack.Samples.SampleInt32ArraySerializer(new newmpcli::MsgPack.Serialization.ObjectSerializationContext()); } else if (typeof(T) == typeof(int)) { - return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleInt32Serializer(); + return (newmpcli::MsgPack.Serialization.ObjectSerializer)(object)new MsgPack.Samples.SampleInt32Serializer(new newmpcli::MsgPack.Serialization.ObjectSerializationContext()); } throw new NotSupportedException($"No {typeof(T)} serializer."); } - private static newmpcli::MsgPack.Serialization.Internal.IObjectSerializer InitJson() + private static newmpcli::MsgPack.Serialization.ObjectSerializer InitJson() { if (typeof(MsgPack.Samples.SampleObject).IsAssignableFrom(typeof(T))) { - return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleSerializer(); + return (newmpcli::MsgPack.Serialization.ObjectSerializer)(object)new MsgPack.Samples.SampleSerializer(new newmpcli::MsgPack.Serialization.ObjectSerializationContext()); } else if (typeof(T) == typeof(int[])) { - return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleInt32ArraySerializer(); + return (newmpcli::MsgPack.Serialization.ObjectSerializer)(object)new MsgPack.Samples.SampleInt32ArraySerializer(new newmpcli::MsgPack.Serialization.ObjectSerializationContext()); } else if (typeof(T) == typeof(int)) { - return (newmpcli::MsgPack.Serialization.Internal.IObjectSerializer)(object)new MsgPack.Samples.SampleInt32Serializer(); + return (newmpcli::MsgPack.Serialization.ObjectSerializer)(object)new MsgPack.Samples.SampleInt32Serializer(new newmpcli::MsgPack.Serialization.ObjectSerializationContext()); } throw new NotSupportedException($"No {typeof(T)} serializer."); @@ -91,7 +91,7 @@ public override T Deserialize(object input) { var reader = new SequenceReader(new ReadOnlySequence((byte[])input)); var context = - new newmpcli::MsgPack.Serialization.Internal.DeserializationOperationContext( + new newmpcli::MsgPack.Serialization.DeserializationOperationContext( Decoder, null, CancellationToken.None @@ -115,7 +115,7 @@ public override object Serialize(T input) var writer = t_writer; var context = - new newmpcli::MsgPack.Serialization.Internal.SerializationOperationContext( + new newmpcli::MsgPack.Serialization.SerializationOperationContext( Encoder, null, CancellationToken.None @@ -136,7 +136,7 @@ public override T Deserialize(object input) { var reader = new SequenceReader(new ReadOnlySequence((byte[])input)); var context = - new newmpcli::MsgPack.Serialization.Internal.DeserializationOperationContext( + new newmpcli::MsgPack.Serialization.DeserializationOperationContext( Decoder, null, CancellationToken.None @@ -160,7 +160,7 @@ public override object Serialize(T input) var writer = t_writer; var context = - new newmpcli::MsgPack.Serialization.Internal.SerializationOperationContext( + new newmpcli::MsgPack.Serialization.SerializationOperationContext( Encoder, null, CancellationToken.None diff --git a/test/Benchmark/_SampleObject.cs b/test/Benchmark/_SampleObject.cs index 9451254c4..4675f9997 100644 --- a/test/Benchmark/_SampleObject.cs +++ b/test/Benchmark/_SampleObject.cs @@ -21,9 +21,9 @@ using System.IO; using System.Linq; using System.Threading.Tasks; +using newmpcli::MsgPack.Serialization; using newmpcli::MsgPack.Internal; using newmpcli::MsgPack.Json; -using newmpcli::MsgPack.Serialization.Internal; namespace MsgPack.Samples { @@ -179,7 +179,11 @@ public class SampleObject // Packer -> Packer.Create(new MemoryStream()) // Unpacker -> Unpacker.Create(new MemoryStream())??? - internal static class SampleSerializer + // For PoC of MVP and reference for emit. + /// + /// Sample hand made serializer. + /// + public sealed class SampleSerializer : ObjectSerializer { public static readonly MsgPack.Serialization.SerializationContext SerializationContext = #if USE_ARRAY @@ -187,14 +191,10 @@ internal static class SampleSerializer #else new Serialization.SerializationContext() { SerializationMethod = Serialization.SerializationMethod.Map }; #endif - } + public SampleSerializer(ObjectSerializationContext ownedContext) + : base(ownedContext, SerializerCapabilities.Serialize | SerializerCapabilities.Deserialize | SerializerCapabilities.DeserializeTo) { } + - // For PoC of MVP and reference for emit. - /// - /// Sample hand made serializer. - /// - public sealed class SampleSerializer : IObjectSerializer - { private bool UseArray { get; set; } #if USE_ARRAY = true; @@ -215,7 +215,7 @@ public sealed class SampleSerializer : IObjectSerializer context, SampleObject obj, IBufferWriter writer) + public sealed override void Serialize(ref SerializationOperationContext context, SampleObject obj, IBufferWriter writer) { #if JSON var encoder = new JsonEncoder(context.Encoder.Options as JsonEncoderOptions); @@ -509,15 +509,6 @@ public void Serialize(ref SerializationOperationContext context, #endif // !USE_ARRAY } - public async ValueTask SerializeAsync(AsyncSerializationOperationContext context, SampleObject obj, Stream streamSink) - { - await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) - { - var serializationOperationContext = context.AsSerializationOperationContext(); - this.Serialize(ref serializationOperationContext, obj, writer); - } - } - private static readonly MsgPackStringTrie DeserializationTrie = InitializeDeserializationTrie(); private static MsgPackStringTrie InitializeDeserializationTrie() { @@ -575,7 +566,7 @@ private static MsgPackStringTrie InitializeDeserializationTrie() return trie; } - public SampleObject Deserialize(ref DeserializationOperationContext context, ref SequenceReader reader) + public sealed override SampleObject Deserialize(ref DeserializationOperationContext context, ref SequenceReader reader) { #warning TODO: Inlining to avoid allocation when null. // WHEN T is reference type and it has a default contractor -> DeserializeToAsync @@ -605,7 +596,7 @@ public SampleObject Deserialize(ref DeserializationOperationContext DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource) + public sealed override async ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence streamSource) { var obj = new SampleObject(); // WHEN T is reference type and it has a default contractor -> DeserializeToAsync @@ -637,7 +628,7 @@ public async ValueTask DeserializeAsync(AsyncDeserializationOperat private static void ThrowNotEnoughItems(long actual, int expected) => throw new MessageTypeException(); - public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader reader, in SampleObject obj) + public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader reader, in SampleObject obj) { const int propertyCount = 0 @@ -1234,10 +1225,10 @@ private static void CheckNextItemExists(ref SequenceReader reader, ref Col } } - private static bool TryDecodeArrayOrMapHeader(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out long itemsCount, out CollectionType arrayOrMap, out CollectionItemIterator propertyIterator, out int requestHint) + private static bool TryDecodeArrayOrMapHeader(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out long itemsCount, out CollectionType arrayOrMap, out CollectionItemIterator propertyIterator, out int requestHint) { context.IncrementDepth(); - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(ref reader, out itemsCount, out requestHint); if (arrayOrMap.IsNone) @@ -1262,161 +1253,163 @@ private static bool TryDecodeArrayOrMapHeader(AsyncDeserializationOperationConte itemsCount = -1; } - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); return true; } - private static bool TryDecodeArrayHeader(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out long arrayLength, out int requestHint) + private static bool TryDecodeArrayHeader(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out long arrayLength, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); arrayLength = context.Decoder.DecodeArrayHeader(ref reader, out requestHint); if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeMapHeader(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out long mapCount, out int requestHint) + private static bool TryDecodeMapHeader(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out long mapCount, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); mapCount = context.Decoder.DecodeMapHeader(ref reader, out requestHint); if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeValueOfName(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out string name, out int requestHint) + private static bool TryDecodeValueOfName(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out string name, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); name = context.Decoder.DecodeString(ref reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeValueOfAge(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out int age, out int requestHint) + private static bool TryDecodeValueOfAge(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out int age, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); age = context.Decoder.DecodeInt32(ref reader, out requestHint); if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeValueOfIsActive(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out bool isActive, out int requestHint) + private static bool TryDecodeValueOfIsActive(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out bool isActive, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); isActive = context.Decoder.DecodeBoolean(ref reader, out requestHint); if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeItemOfRoles(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out string item, out int requestHint) + private static bool TryDecodeItemOfRoles(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out string item, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); item = context.Decoder.DecodeString(ref reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeKeyOfAttributes(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out string key, out int requestHint) + private static bool TryDecodeKeyOfAttributes(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out string key, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); key = context.Decoder.DecodeString(ref reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeValueOfAttributes(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out string value, out int requestHint) + private static bool TryDecodeValueOfAttributes(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out string value, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); value = context.Decoder.DecodeString(ref reader, out requestHint, context.StringEncoding, context.CancellationToken)!; // <>NameEncoding ?? context.StringEncoding if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeArray(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) + private static bool TryDecodeArray(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out CollectionItemIterator iterator, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); iterator = context.Decoder.DecodeArray(ref reader, out requestHint); if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryDecodeMap(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, out CollectionItemIterator iterator, out int requestHint) + private static bool TryDecodeMap(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out CollectionItemIterator iterator, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); iterator = context.Decoder.DecodeMap(ref reader, out requestHint); if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private static bool TryGetRawString(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, [NotNullWhen(true)] out byte[] key, out int requestHint) + private static bool TryGetRawString(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, [NotNullWhen(true)] out byte[] keyArray, out ReadOnlyMemory key, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); ReadOnlySpan span; if (!context.Decoder.GetRawString(ref reader, out span, out requestHint, context.CancellationToken)) { - key = default!; + keyArray = default!; + key = default; return false; } - key = context.ByteBufferPool.Rent(span.Length); - span.CopyTo(key); + keyArray = context.ByteBufferPool.Rent(span.Length); + span.CopyTo(keyArray); + key = keyArray.AsMemory(0, span.Length); return true; } - private static bool TryDrain(AsyncDeserializationOperationContext context, ref ReadOnlyMemory memory, long remaining, out int requestHint) + private static bool TryDrain(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, long remaining, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(memory)); + var reader = new SequenceReader(sequence); context.Decoder.Drain(ref reader, context.CollectionContext, remaining, out requestHint); if (requestHint == 0) { - memory = memory.Slice(unchecked((int)reader.Consumed)); + sequence = sequence.Slice(reader.Consumed); } return requestHint == 0; } - private bool TryCheckNextItemExists(ref CollectionItemIterator propertyIterator, ReadOnlyMemory memory, out int requestHint) + private bool TryCheckNextItemExists(ref CollectionItemIterator propertyIterator, ReadOnlySequence sequence, out int requestHint) { - if (propertyIterator.CollectionEnds(memory, out requestHint)) + if (propertyIterator.CollectionEnds(sequence, out requestHint)) { throw new MessageTypeException(); // use throws } @@ -1424,486 +1417,482 @@ private bool TryCheckNextItemExists(ref CollectionItemIterator propertyIterator, return requestHint == 0; } - public async ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, SampleObject obj) + public sealed override async ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, SampleObject obj) { - // T が値型 + // T が値 // throw new NotSupportedException(); - var buffer = context.ByteBufferPool.Rent(2 * 1024 * 1024); - try - { - var provider = new StreamReadOnlyMemoryProvider(streamSource, buffer); - var memory = await provider.GetNextAsync(default, 0, context.CancellationToken).ConfigureAwait(false); - int requestHint; + await source.FetchAsync(context.CancellationToken).ConfigureAwait(false); + var sequence = source.Sequence; + int requestHint; #if STRING - string name = default!; + string name = default!; #endif #if INT32 - int age = default; + int age = default; #endif #if BOOL - bool isActive = default; + bool isActive = default; #endif #if COLLECTION - var roles = obj.Roles; - var attributes = obj.Attributes; + var roles = obj.Roles; + var attributes = obj.Attributes; #endif - var decoder = context.Decoder; - context.IncrementDepth(); + var decoder = context.Decoder; + context.IncrementDepth(); - long itemsCount; - CollectionType arrayOrMap; - CollectionItemIterator propertyIterator; - while (!TryDecodeArrayOrMapHeader(context, ref memory, out itemsCount, out arrayOrMap, out propertyIterator, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + long itemsCount; + CollectionType arrayOrMap; + CollectionItemIterator propertyIterator; + while (!TryDecodeArrayOrMapHeader(context, ref sequence, out itemsCount, out arrayOrMap, out propertyIterator, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } - if (arrayOrMap.IsNull) - { - return false; - } + if (arrayOrMap.IsNull) + { + return false; + } - // Instantiate when Deserialize + // Instantiate when Deserialize - // If !SerializerGenerationOptions.InfersObjectSerialization && SerializerGenerationOptions.UseArray || typeof(T).IsDefined([SerializeAs(Array)]) - if (arrayOrMap.IsArray) - { + // If !SerializerGenerationOptions.InfersObjectSerialization && SerializerGenerationOptions.UseArray || typeof(T).IsDefined([SerializeAs(Array)]) + if (arrayOrMap.IsArray) + { #if STRING - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + } - while (!TryDecodeValueOfName(context, ref memory, out name, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + while (!TryDecodeValueOfName(context, ref sequence, out name, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } #endif #if INT32 - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + } - while (!TryDecodeValueOfAge(context, ref memory, out age, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + while (!TryDecodeValueOfAge(context, ref sequence, out age, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } #endif #if BOOL - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + } - while (!TryDecodeValueOfIsActive(context, ref memory, out isActive, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + while (!TryDecodeValueOfIsActive(context, ref sequence, out isActive, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } #endif #if COLLECTION - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + } - context.IncrementDepth(); + context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long arrayLength; + while (!TryDecodeArrayHeader(context, ref sequence, out arrayLength, out requestHint)) { - long arrayLength; - while (!TryDecodeArrayHeader(context, ref memory, out arrayLength, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } - // If settable - //if (roles == null) - //{ - // roles = new List(arrayLength); // or 0 - //} + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} - for (var i = 0; i < arrayLength; i++) + for (var i = 0; i < arrayLength; i++) + { + string item; + while (!TryDecodeItemOfRoles(context, ref sequence, out item, out requestHint)) { - string item; - while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - roles.Add(item); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + roles.Add(item); } - else + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeArray(context, ref sequence, out iterator, out requestHint)) { - CollectionItemIterator iterator; - while (!TryDecodeArray(context, ref memory, out iterator, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } - while (!iterator.CollectionEnds(memory, out requestHint)) + while (!iterator.CollectionEnds(sequence, out requestHint)) + { + if (requestHint != 0) { - if (requestHint != 0) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - string item; - while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - roles.Add(item); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - while (!iterator.Drain(ref memory, out requestHint)) + string item; + while (!TryDecodeItemOfRoles(context, ref sequence, out item, out requestHint)) { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + roles.Add(item); } - context.DecrementDepth(); - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + while (!iterator.Drain(ref sequence, out requestHint)) { - while (!this.TryCheckNextItemExists(ref propertyIterator, memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + } + context.DecrementDepth(); - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } + } + + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + long mapCount; + while (!TryDecodeMapHeader(context, ref sequence, out mapCount, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var i = 0; i < mapCount; i++) { - long mapCount; - while (!TryDecodeMapHeader(context, ref memory, out mapCount, out requestHint)) + string key; + while (!TryDecodeKeyOfAttributes(context, ref sequence, out key, out requestHint)) { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - - // If settable - //if (attributes == null) - //{ - // attributes = new Dictionary(mapCount); // or 0 - //} - for (var i = 0; i < mapCount; i++) + string value; + while (!TryDecodeValueOfAttributes(context, ref sequence, out value, out requestHint)) { - string key; - while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - string value; - while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - attributes.Add(key, value); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + attributes.Add(key, value); } - else + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeMap(context, ref sequence, out iterator, out requestHint)) { - CollectionItemIterator iterator; - while (!TryDecodeMap(context, ref memory, out iterator, out requestHint)) + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } + + while (!iterator.CollectionEnds(sequence, out requestHint)) + { + if (requestHint != 0) { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - while (!iterator.CollectionEnds(memory, out requestHint)) + string key; + while (!TryDecodeKeyOfAttributes(context, ref sequence, out key, out requestHint)) { - if (requestHint != 0) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - string key; - while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - string value; - while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - attributes.Add(key, value); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - - while (!iterator.Drain(ref memory, out requestHint)) + string value; + while (!TryDecodeValueOfAttributes(context, ref sequence, out value, out requestHint)) { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + attributes.Add(key, value); + } + + while (!iterator.Drain(ref sequence, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - context.DecrementDepth(); -#endif } - else + context.DecrementDepth(); +#endif + } + else + { + // Map + // #if !context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + // while (!propertyIterator.CollectionEnds(memory, out requestHint)) + // { + // if (requestHint != 0) + // { + // memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + // continue; + // } + for (var i = 0; i < itemsCount; i++) { - // Map - // #if !context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE - // while (!propertyIterator.CollectionEnds(memory, out requestHint)) - // { - // if (requestHint != 0) - // { - // memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - // continue; - // } - for (var i = 0; i < itemsCount; i++) + byte[] propertyKeyArray = null!; + try { - byte[] propertyKey = null!; - try + ReadOnlyMemory propertyKey; + while (!TryGetRawString(context, ref sequence, out propertyKeyArray, out propertyKey, out requestHint)) { - while (!TryGetRawString(context, ref memory, out propertyKey, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } - // Use inlined trie, prefixed by the count as MP uint32. - // char is UTF-8, big endian. - // 1st node: [length(1-5)][chars(7-3)] - switch (DeserializationTrie.GetOrDefault(propertyKey)) - { + // Use inlined trie, prefixed by the count as MP uint32. + // char is UTF-8, big endian. + // 1st node: [length(1-5)][chars(7-3)] + switch (DeserializationTrie.GetOrDefault(propertyKey.Span)) + { #if STRING - case 0: + case 0: + { + while (!TryDecodeValueOfName(context, ref sequence, out name, out requestHint)) { - while (!TryDecodeValueOfName(context, ref memory, out name, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - break; + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + + break; + } #endif #if INT32 - case 1: + case 1: + { + while (!TryDecodeValueOfAge(context, ref sequence, out age, out requestHint)) { - while (!TryDecodeValueOfAge(context, ref memory, out age, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - break; + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + + break; + } #endif #if BOOL - case 2: + case 2: + { + while (!TryDecodeValueOfIsActive(context, ref sequence, out isActive, out requestHint)) { - while (!TryDecodeValueOfIsActive(context, ref memory, out isActive, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - break; + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + + break; + } #endif #if COLLECTION - case 3: + case 3: + { + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE { - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + long arrayLength; + while (!TryDecodeArrayHeader(context, ref sequence, out arrayLength, out requestHint)) { - long arrayLength; - while (!TryDecodeArrayHeader(context, ref memory, out arrayLength, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } - // If settable - //if (roles == null) - //{ - // roles = new List(arrayLength); // or 0 - //} - for (var j = 0; j < arrayLength; j++) + // If settable + //if (roles == null) + //{ + // roles = new List(arrayLength); // or 0 + //} + for (var j = 0; j < arrayLength; j++) + { + string item; + while (!TryDecodeItemOfRoles(context, ref sequence, out item, out requestHint)) { - string item; - while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - roles.Add(item); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + + roles.Add(item); } - else + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeArray(context, ref sequence, out iterator, out requestHint)) { - CollectionItemIterator iterator; - while (!TryDecodeArray(context, ref memory, out iterator, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } - while (!iterator.CollectionEnds(memory, out requestHint)) + while (!iterator.CollectionEnds(sequence, out requestHint)) + { + if (requestHint != 0) { - if (requestHint != 0) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - string item; - while (!TryDecodeItemOfRoles(context, ref memory, out item, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - roles.Add(item); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - while (!iterator.Drain(ref memory, out requestHint)) + string item; + while (!TryDecodeItemOfRoles(context, ref sequence, out item, out requestHint)) { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + + roles.Add(item); + } + + while (!iterator.Drain(ref sequence, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - context.DecrementDepth(); - break; } - case 4: + context.DecrementDepth(); + break; + } + case 4: + { + context.IncrementDepth(); + if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE { - context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + long mapCount; + while (!TryDecodeMapHeader(context, ref sequence, out mapCount, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); + } + + // If settable + //if (attributes == null) + //{ + // attributes = new Dictionary(mapCount); // or 0 + //} + for (var j = 0; j < mapCount; j++) { - long mapCount; - while (!TryDecodeMapHeader(context, ref memory, out mapCount, out requestHint)) + string key; + while (!TryDecodeKeyOfAttributes(context, ref sequence, out key, out requestHint)) { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - - // If settable - //if (attributes == null) - //{ - // attributes = new Dictionary(mapCount); // or 0 - //} - for (var j = 0; j < mapCount; j++) + string value; + while (!TryDecodeValueOfAttributes(context, ref sequence, out value, out requestHint)) { - string key; - while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - string value; - while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - attributes.Add(key, value); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + + attributes.Add(key, value); + } + } + else + { + CollectionItemIterator iterator; + while (!TryDecodeMap(context, ref sequence, out iterator, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - else + + while (!iterator.CollectionEnds(sequence, out requestHint)) { - CollectionItemIterator iterator; - while (!TryDecodeMap(context, ref memory, out iterator, out requestHint)) + if (requestHint != 0) { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - while (!iterator.CollectionEnds(memory, out requestHint)) + string key; + while (!TryDecodeKeyOfAttributes(context, ref sequence, out key, out requestHint)) { - if (requestHint != 0) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - string key; - while (!TryDecodeKeyOfAttributes(context, ref memory, out key, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - string value; - while (!TryDecodeValueOfAttributes(context, ref memory, out value, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } - - attributes.Add(key, value); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - - while (!iterator.Drain(ref memory, out requestHint)) + string value; + while (!TryDecodeValueOfAttributes(context, ref sequence, out value, out requestHint)) { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + + attributes.Add(key, value); + } + + while (!iterator.Drain(ref sequence, out requestHint)) + { + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - context.DecrementDepth(); - break; } + context.DecrementDepth(); + break; + } #endif - default: - { + default: + { #warning TODO: TrySkip loop - break; - } + break; } } - finally + } + finally + { + if (propertyKeyArray != null) { - if (propertyKey != null) - { - context.ByteBufferPool.Return(propertyKey); - } + context.ByteBufferPool.Return(propertyKeyArray); } } } + } - if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + { + itemsCount -= 5; + while (!TryDrain(context, ref sequence, itemsCount, out requestHint)) { - itemsCount -= 5; - while (!TryDrain(context, ref memory, itemsCount, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } - else + } + else + { + while (!propertyIterator.Drain(ref sequence, out requestHint)) { - while (!propertyIterator.Drain(ref memory, out requestHint)) - { - memory = await provider.GetNextAsync(memory, requestHint, context.CancellationToken).ConfigureAwait(false); - } + await source.FetchAsync(requestHint, context.CancellationToken).ConfigureAwait(false); } + } - context.DecrementDepth(); + context.DecrementDepth(); - // Verbose for Deserialize... + // Verbose for Deserialize... #if STRING - obj.Name = name; + obj.Name = name; #endif #if INT32 - obj.Age = age; + obj.Age = age; #endif #if BOOL - obj.IsActive = isActive; + obj.IsActive = isActive; #endif - // If settable - // obj.Roles = roles; - // If settable - // obj.Attributes = attributes; - return true; - } - finally - { - context.ByteBufferPool.Return(buffer, clearArray: true); - } + // If settable + // obj.Roles = roles; + // If settable + // obj.Attributes = attributes; + return true; } } - public sealed class SampleInt32ArraySerializer : IObjectSerializer + public sealed class SampleInt32ArraySerializer : ObjectSerializer { - public void Serialize(ref SerializationOperationContext context, int[] obj, IBufferWriter sink) + public SampleInt32ArraySerializer(ObjectSerializationContext ownedContext) + : base(ownedContext, SerializerCapabilities.Serialize | SerializerCapabilities.Deserialize) { } + + public sealed override void Serialize(ref SerializationOperationContext context, int[] obj, IBufferWriter sink) { if (obj is null) { @@ -1921,12 +1910,7 @@ public void Serialize(ref SerializationOperationContext context, context.Encoder.EncodeArrayEnd(obj.Length, sink, context.CollectionContext); } - public ValueTask SerializeAsync(AsyncSerializationOperationContext context, int[] obj, Stream streamSink) - { - throw new NotImplementedException(); - } - - public int[] Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + public sealed override int[] Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) { if (context.Decoder.FormatFeatures.CanCountCollectionItems) { @@ -1952,50 +1936,48 @@ public int[] Deserialize(ref DeserializationOperationContext con } } - public ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource) + public sealed override ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence streamSource) { throw new NotImplementedException(); } - public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in int[] obj) + public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in int[] obj) { throw new NotImplementedException(); } - public ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, int[] obj) + public sealed override ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence streamSource, int[] obj) { throw new NotImplementedException(); } } - public sealed class SampleInt32Serializer : IObjectSerializer + public sealed class SampleInt32Serializer : ObjectSerializer { - public void Serialize(ref SerializationOperationContext context, int obj, IBufferWriter sink) - { - context.Encoder.EncodeInt32(obj, sink); - } + public SampleInt32Serializer(ObjectSerializationContext ownedContext) + : base(ownedContext, SerializerCapabilities.Serialize | SerializerCapabilities.Deserialize) { } - public ValueTask SerializeAsync(AsyncSerializationOperationContext context, int obj, Stream streamSink) + public sealed override void Serialize(ref SerializationOperationContext context, int obj, IBufferWriter sink) { - throw new NotImplementedException(); + context.Encoder.EncodeInt32(obj, sink); } - public int Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + public sealed override int Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) { return context.Decoder.DecodeInt32(ref source); } - public ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource) + public sealed override ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence streamSource) { throw new NotImplementedException(); } - public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in int obj) + public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in int obj) { throw new NotImplementedException(); } - public ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, int obj) + public sealed override ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence streamSource, int obj) { throw new NotImplementedException(); } From 4be2eeadae2d24ffe0673f6e091c5c05105a05c8 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Tue, 23 Jun 2020 19:25:37 +0900 Subject: [PATCH 29/82] Fix collection count values types because it can never exceed Int32.MaxValue in .net --- .../Internal/FormatDecoder.cs | 12 +++---- .../MessagePackDecoder.CollectionHeaders.cs | 32 ++++++++----------- .../Json/JsonDecoder.CollectionHeaders.cs | 6 ++-- src/MsgPack.Json/Json/JsonThrow.cs | 4 +-- test/Benchmark/_SampleObject.cs | 7 ++-- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/src/MsgPack.Abstraction/Internal/FormatDecoder.cs b/src/MsgPack.Abstraction/Internal/FormatDecoder.cs index 20e0b154e..70ff70f0d 100644 --- a/src/MsgPack.Abstraction/Internal/FormatDecoder.cs +++ b/src/MsgPack.Abstraction/Internal/FormatDecoder.cs @@ -85,7 +85,7 @@ public bool TryDecodeNull(ref SequenceReader source) /// The decoded value is not an array nor a map. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out long itemsCount) + public CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out int itemsCount) { var result = this.DecodeArrayOrMapHeader(ref source, out itemsCount, out var requestHint); if (requestHint != 0) @@ -110,7 +110,7 @@ public CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, ou /// This method does not return anything else, but may throw an exception. /// /// The decoded value is not an array nor a map. - public abstract CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out long itemsCount, out int requestHint); + public abstract CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out int itemsCount, out int requestHint); /// /// Decodes current data as array header, and returns the items count if known. @@ -123,7 +123,7 @@ public CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, ou /// The decoded value is not an array. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public long DecodeArrayHeader(ref SequenceReader source) + public int DecodeArrayHeader(ref SequenceReader source) { var result = this.DecodeArrayHeader(ref source, out var requestHint); if (requestHint != 0) @@ -148,7 +148,7 @@ public long DecodeArrayHeader(ref SequenceReader source) /// /// The decoded value is not an array. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public abstract long DecodeArrayHeader(ref SequenceReader source, out int requestHint); + public abstract int DecodeArrayHeader(ref SequenceReader source, out int requestHint); /// /// Decodes current data as map header, and returns the items count if known. @@ -161,7 +161,7 @@ public long DecodeArrayHeader(ref SequenceReader source) /// The decoded value is not a map. /// does not contain enough bytes to decode. [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public long DecodeMapHeader(ref SequenceReader source) + public int DecodeMapHeader(ref SequenceReader source) { var result = this.DecodeMapHeader(ref source, out var requestHint); if (requestHint != 0) @@ -185,7 +185,7 @@ public long DecodeMapHeader(ref SequenceReader source) /// Note that 0 is valid value when the map is empty. /// /// The decoded value is not a map. - public abstract long DecodeMapHeader(ref SequenceReader source, out int requestHint); + public abstract int DecodeMapHeader(ref SequenceReader source, out int requestHint); public virtual void DecodeExtension(ref SequenceReader source, out ExtensionTypeObject result, out int requestHint, CancellationToken cancellationToken = default) { diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs index e2f6bf09a..7b2436854 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.CollectionHeaders.cs @@ -9,7 +9,7 @@ namespace MsgPack.Internal { public partial class MessagePackDecoder { - public sealed override CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out long itemsCount, out int requestHint) + public sealed override CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out int itemsCount, out int requestHint) { var startOffset = source.Consumed; var result = this.PrivateDecodeArrayOrMapHeader(ref source, out var header, out itemsCount, out requestHint); @@ -22,7 +22,7 @@ public sealed override CollectionType DecodeArrayOrMapHeader(ref SequenceReader< return result; } - private CollectionType PrivateDecodeArrayOrMapHeader(ref SequenceReader source, out byte header, out long itemsCount, out int requestHint) + private CollectionType PrivateDecodeArrayOrMapHeader(ref SequenceReader source, out byte header, out int itemsCount, out int requestHint) { var startOffset = source.Consumed; var result = this.DecodeArrayOrMapHeaderCore(ref source, out header, out itemsCount, out requestHint); @@ -34,7 +34,7 @@ private CollectionType PrivateDecodeArrayOrMapHeader(ref SequenceReader so return result; } - private CollectionType DecodeArrayOrMapHeaderCore(ref SequenceReader source, out byte header, out long itemsCount, out int requestHint) + private CollectionType DecodeArrayOrMapHeaderCore(ref SequenceReader source, out byte header, out int itemsCount, out int requestHint) { if (!source.TryRead(out header)) { @@ -53,13 +53,13 @@ private CollectionType DecodeArrayOrMapHeaderCore(ref SequenceReader sourc if ((header & MessagePackCode.MinimumFixedArray) == MessagePackCode.MinimumFixedArray) { - itemsCount = (uint)header - MessagePackCode.MinimumFixedArray; + itemsCount = (int)header - MessagePackCode.MinimumFixedArray; return CollectionType.Array; } if ((header & MessagePackCode.MinimumFixedMap) == MessagePackCode.MinimumFixedMap) { - itemsCount = (uint)header - MessagePackCode.MinimumFixedMap; + itemsCount = (int)header - MessagePackCode.MinimumFixedMap; return CollectionType.Map; } @@ -108,7 +108,13 @@ private CollectionType DecodeArrayOrMapHeaderCore(ref SequenceReader sourc } default: // 4 { - itemsCount = ReadValue(ref source, offset: 1, out requestHint); + var count = ReadValue(ref source, offset: 1, out requestHint); + if (count > OptionsDefaults.MaxMultiByteCollectionLength) + { + MessagePackThrow.TooLargeArrayOrMapLength(header, source.Consumed - 1, count); + } + + itemsCount = unchecked((int)count); break; } } @@ -122,7 +128,7 @@ private CollectionType DecodeArrayOrMapHeaderCore(ref SequenceReader sourc return type; } - public sealed override long DecodeArrayHeader(ref SequenceReader source, out int requestHint) + public sealed override int DecodeArrayHeader(ref SequenceReader source, out int requestHint) { var startOffset = source.Consumed; var type = this.DecodeArrayOrMapHeaderCore(ref source, out var header, out var itemsCount, out requestHint); @@ -131,11 +137,6 @@ public sealed override long DecodeArrayHeader(ref SequenceReader source, o return 0; } - if (itemsCount > Int32.MaxValue) - { - MessagePackThrow.TooLargeArrayOrMapLength(header, startOffset, itemsCount); - } - if (type.IsMap) { MessagePackThrow.TypeIsNotArray(header, startOffset); @@ -144,7 +145,7 @@ public sealed override long DecodeArrayHeader(ref SequenceReader source, o return (int)itemsCount; } - public sealed override long DecodeMapHeader(ref SequenceReader source, out int requestHint) + public sealed override int DecodeMapHeader(ref SequenceReader source, out int requestHint) { var startOffset = source.Consumed; var type = this.DecodeArrayOrMapHeaderCore(ref source, out var header, out var itemsCount, out requestHint); @@ -153,11 +154,6 @@ public sealed override long DecodeMapHeader(ref SequenceReader source, out return 0; } - if (itemsCount > Int32.MaxValue) - { - MessagePackThrow.TooLargeArrayOrMapLength(header, startOffset, itemsCount); - } - if (type.IsArray) { MessagePackThrow.TypeIsNotMap(header, startOffset); diff --git a/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs b/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs index c5f94307c..afe416a8d 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.CollectionHeaders.cs @@ -9,13 +9,13 @@ namespace MsgPack.Json { public partial class JsonDecoder { - public sealed override CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out long itemsCount, out int requestHint) + public sealed override CollectionType DecodeArrayOrMapHeader(ref SequenceReader source, out int itemsCount, out int requestHint) => JsonThrow.CollectionHeaderDecodingIsNotSupported(out itemsCount, out requestHint); - public sealed override long DecodeArrayHeader(ref SequenceReader source, out int requestHint) + public sealed override int DecodeArrayHeader(ref SequenceReader source, out int requestHint) => JsonThrow.CollectionHeaderDecodingIsNotSupported(out requestHint); - public sealed override long DecodeMapHeader(ref SequenceReader source, out int requestHint) + public sealed override int DecodeMapHeader(ref SequenceReader source, out int requestHint) => JsonThrow.CollectionHeaderDecodingIsNotSupported(out requestHint); } } diff --git a/src/MsgPack.Json/Json/JsonThrow.cs b/src/MsgPack.Json/Json/JsonThrow.cs index f26a1053f..96beb6918 100644 --- a/src/MsgPack.Json/Json/JsonThrow.cs +++ b/src/MsgPack.Json/Json/JsonThrow.cs @@ -16,10 +16,10 @@ internal static class JsonThrow public static void TooShortUtf8() => throw new FormatException($"Input UTF-8 sequence is invalid. The sequence unexpectedly ends."); - public static CollectionType CollectionHeaderDecodingIsNotSupported(out long itemsCount, out int requestHint) + public static CollectionType CollectionHeaderDecodingIsNotSupported(out int itemsCount, out int requestHint) => throw new NotSupportedException("JSON does not support collection length."); - public static long CollectionHeaderDecodingIsNotSupported(out int requestHint) + public static int CollectionHeaderDecodingIsNotSupported(out int requestHint) => throw new NotSupportedException("JSON does not support collection length."); public static void DrainIsNotSupported(out int requestHint) diff --git a/test/Benchmark/_SampleObject.cs b/test/Benchmark/_SampleObject.cs index 4675f9997..f2562501d 100644 --- a/test/Benchmark/_SampleObject.cs +++ b/test/Benchmark/_SampleObject.cs @@ -668,7 +668,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co var decoder = new MessagePackDecoder(context.Decoder.Options as MessagePackDecoderOptions); #endif CollectionType arrayOrMap; - long itemsCount; + int itemsCount; CollectionItemIterator propertyIterator; if (this.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE @@ -1225,7 +1225,7 @@ private static void CheckNextItemExists(ref SequenceReader reader, ref Col } } - private static bool TryDecodeArrayOrMapHeader(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out long itemsCount, out CollectionType arrayOrMap, out CollectionItemIterator propertyIterator, out int requestHint) + private static bool TryDecodeArrayOrMapHeader(AsyncDeserializationOperationContext context, ref ReadOnlySequence sequence, out int itemsCount, out CollectionType arrayOrMap, out CollectionItemIterator propertyIterator, out int requestHint) { context.IncrementDepth(); var reader = new SequenceReader(sequence); @@ -1442,8 +1442,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa var decoder = context.Decoder; context.IncrementDepth(); - - long itemsCount; + int itemsCount; CollectionType arrayOrMap; CollectionItemIterator propertyIterator; while (!TryDecodeArrayOrMapHeader(context, ref sequence, out itemsCount, out arrayOrMap, out propertyIterator, out requestHint)) From 9dfdf3eba5a55bd21113dd47682775042db50b82 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Tue, 23 Jun 2020 23:56:40 +0900 Subject: [PATCH 30/82] Change StreamReadOnlyMemoryProvider struct to ReadOnlyStreamSequence This change ease async serializer implementation so much. --- .../Internal/CollectionItemIterator.cs | 9 +- .../Internal/ReadOnlyStreamSequence.cs | 122 ++++++++++++++++++ .../Internal/StreamReadOnlyMemoryProvider.cs | 75 ----------- ...lizationOperationContext.StreamSequence.cs | 22 ++++ .../AsyncDeserializationOperationContext.cs | 2 +- .../AsyncSerializationOperationContext.cs | 2 +- .../Serialization/DeserializationOptions.cs | 5 +- .../DeserializationOptionsBuilder.cs | 4 +- .../Serialization/ObjectSerializer`1.cs | 4 +- .../Serialization/OperationContext.ttinclude | 2 +- .../Serialization/Serializer.cs | 2 +- .../Serialization/Serializer`2.cs | 4 +- src/MsgPack.Abstraction/Throw.cs | 3 + 13 files changed, 167 insertions(+), 89 deletions(-) create mode 100644 src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs delete mode 100644 src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs create mode 100644 src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.StreamSequence.cs diff --git a/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs b/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs index 9fa613652..9af56e2cb 100644 --- a/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs +++ b/src/MsgPack.Abstraction/Internal/CollectionItemIterator.cs @@ -10,6 +10,7 @@ namespace MsgPack.Internal { public struct CollectionItemIterator { +#warning TODO: in SequenceReader ?? public delegate bool CollectionEndDetection(ref SequenceReader source, ref long nextItemIndex, long itemsCount, out int requestHint); private readonly long _itemsCount; @@ -43,9 +44,9 @@ public bool CollectionEnds(ref SequenceReader source, out int requestHint) => this._collectionEnds(ref source, ref this._nextItemIndex, this._itemsCount, out requestHint); [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public bool CollectionEnds(ReadOnlyMemory source, out int requestHint) + public bool CollectionEnds(in ReadOnlySequence source, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(source)); + var reader = new SequenceReader(source); return this.CollectionEnds(ref reader, out requestHint); } @@ -73,9 +74,9 @@ public bool Drain(ref SequenceReader source, out int requestHint) } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] - public bool Drain(ref ReadOnlyMemory source, out int requestHint) + public bool Drain(ref ReadOnlySequence source, out int requestHint) { - var reader = new SequenceReader(new ReadOnlySequence(source)); + var reader = new SequenceReader(source); var ends = this.Drain(ref reader, out requestHint); source = source.Slice((int)reader.Consumed); return ends; diff --git a/src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs b/src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs new file mode 100644 index 000000000..e7c53f904 --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs @@ -0,0 +1,122 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MsgPack.Internal +{ + /// + /// Wraps and provides via internal buffer. + /// + /// + /// User of this object must be call when this sequence used. + /// Note that the will NOT be closed when is called. + /// + public sealed class ReadOnlyStreamSequence : IDisposable + { + private readonly ArrayPool _arrayPool; + private readonly bool _clearsBuffer; + private readonly Stream _stream; // DO NOT Dispose this in this class. + private byte[] _array; + + /// + /// Gets internal buffer as which holds last fetched data. + /// + public ReadOnlyMemory Memory { get; private set; } + + /// + /// Gets internal buffer as which holds last fetched data. + /// + public ReadOnlySequence Sequence => new ReadOnlySequence(this.Memory); + + internal ReadOnlyStreamSequence(Stream stream, ArrayPool arrayPool, int bufferSize, bool clearsBuffer) + { + this._stream = stream; + this._arrayPool = arrayPool; + this._array = arrayPool.Rent(bufferSize); + this.Memory = this._array; + this._clearsBuffer = clearsBuffer; + } + + /// + public void Dispose() + { + var array = Interlocked.Exchange(ref this._array, null!); + if (array != null) + { + this._arrayPool.Return(this._array, this._clearsBuffer); + } + } + + /// + /// Fetches first data from underlying and fills internal buffer. + /// Remaining data in the current buffer will be included new buffer. + /// + /// to cancel read operation. + /// Async operation state. + /// This object is already disposed. + public ValueTask FetchAsync(CancellationToken cancellationToken) + => this.FetchAsync(0, cancellationToken); + + /// + /// Fetches next data from underlying and fills internal buffer. + /// Remaining data in the current buffer will be included new buffer. + /// + /// + /// Hint size to fetch from . + /// 0 is valid value which means that the implementation fetches bytes from to fill out current buffer. + /// Note that it is not guaranteed that contains this length because underlying may not have enough data. + /// + /// to cancel read operation. + /// Async operation state. + /// This object is already disposed. + public async ValueTask FetchAsync(int requestHint, CancellationToken cancellationToken) + { + if (this._array == null) + { + Throw.ObjectDisposed(this.ToString()); + // never + return; + } + + var existingLength = this.Memory.Length; + if (existingLength + requestHint > this._array.Length) + { + // realloc + var newArray = this._arrayPool.Rent(Math.Max(this._array.Length * 2, existingLength + requestHint)); + this.Memory.CopyTo(newArray); + this._arrayPool.Return(this._array, this._clearsBuffer); + this._array = newArray; + } + else + { + // comact + this.Memory.CopyTo(this._array); + } + + var readLength = await this._stream.ReadAsync(this._array.AsMemory(existingLength), cancellationToken).ConfigureAwait(false); + this.Memory = this._array.AsMemory(existingLength + readLength); + } + + /// + /// Advances internal buffer position. + /// + /// Length to be advanced. + /// is greater than length of . + /// This object is already disposed. + public void Advance(int length) + { + if (this._array == null) + { + Throw.ObjectDisposed(this.ToString()); + } + + this.Memory = this.Memory.Slice(Ensure.IsNotGreaterThan(length, this.Memory.Length)); + } + } +} diff --git a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs b/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs deleted file mode 100644 index 0934bbbdf..000000000 --- a/src/MsgPack.Abstraction/Internal/StreamReadOnlyMemoryProvider.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. -// This file is licensed under Apache2 license. -// See the LICENSE in the project root for more information. - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MsgPack.Internal -{ - public struct StreamReadOnlyMemoryProvider - { - private readonly Stream _stream; - private readonly byte[] _buffer; - - public StreamReadOnlyMemoryProvider(Stream stream, byte[] buffer) - { - Ensure.NotNull(stream); - if (!stream.CanRead) - { - Throw.StreamMustBeAbleToRead(nameof(stream)); - } - - Ensure.NotNull(buffer); - if (buffer.Length == 0) - { - Throw.TooSmallBuffer(nameof(buffer), 1); - } - - this._stream = stream; - this._buffer = buffer; - } - - public async ValueTask> GetNextAsync(ReadOnlyMemory previous, int requestHint, CancellationToken cancellationToken) - { - if (this._stream is null) - { - Throw.EmptyObject(typeof(StreamReadOnlyMemoryProvider)); - // never - return default; - } - - var required = (long)previous.Length + requestHint; - if (required > OptionsDefaults.MaxSingleByteCollectionLength) - { - Throw.TooLargeLength(previous.Length, requestHint); - } - - Memory buffer; - if (required < this._buffer.Length) - { - if (previous.Length > 0) - { - this._buffer.AsSpan().CopyTo(this._buffer.AsSpan(previous.Length)); - } - - buffer = this._buffer.AsMemory(previous.Length); - if (requestHint > 0) - { - buffer = buffer.Slice(0, requestHint); - } - } - else - { - buffer = new byte[required]; - previous.CopyTo(buffer); - buffer = buffer.Slice(previous.Length); - } - - await this._stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); - return buffer; - } - } -} diff --git a/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.StreamSequence.cs b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.StreamSequence.cs new file mode 100644 index 000000000..f2a0fc553 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.StreamSequence.cs @@ -0,0 +1,22 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.IO; +using MsgPack.Internal; + +namespace MsgPack.Serialization +{ + public partial class AsyncDeserializationOperationContext + { + /// + /// Returns new which wraps specified . + /// + /// which holds input data, it may have over 32bit length. + /// + /// is null. + public ReadOnlyStreamSequence CreateSequence(Stream stream) + => new ReadOnlyStreamSequence(Ensure.NotNull(stream), this.Options.ByteBufferPool, 64 * 1024, this.Options.ClearsBuffer); + } +} diff --git a/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.cs b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.cs index 70a1d3911..3df936d93 100644 --- a/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.cs +++ b/src/MsgPack.Abstraction/Serialization/AsyncDeserializationOperationContext.cs @@ -16,7 +16,7 @@ namespace MsgPack.Serialization { - public sealed class AsyncDeserializationOperationContext + public sealed partial class AsyncDeserializationOperationContext { public FormatDecoder Decoder { get; } public DeserializationOptions Options { get; } diff --git a/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs index 0bd040e1f..9b2ffe7fb 100644 --- a/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs +++ b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs @@ -15,7 +15,7 @@ namespace MsgPack.Serialization { - public sealed class AsyncSerializationOperationContext + public sealed partial class AsyncSerializationOperationContext { public FormatEncoder Encoder { get; } public SerializationOptions Options { get; } diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs index 7f7f81ccf..350b97c29 100644 --- a/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOptions.cs @@ -17,6 +17,7 @@ public sealed class DeserializationOptions public int MaxDepth { get; } public Encoding? StringEncoding { get; } public ArrayPool ByteBufferPool { get; } + public bool ClearsBuffer { get; } internal DeserializationOptions( int maxArrayLength, @@ -24,7 +25,8 @@ internal DeserializationOptions( int maxPropertyKeyLength, int maxDepth, Encoding? stringEncoding, - ArrayPool arrayPool + ArrayPool arrayPool, + bool clearsBuffer ) { this.MaxArrayLength =maxArrayLength; @@ -33,6 +35,7 @@ ArrayPool arrayPool this.MaxDepth = maxDepth; this.StringEncoding = stringEncoding; this.ByteBufferPool = arrayPool; + this.ClearsBuffer = clearsBuffer; } } } diff --git a/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs b/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs index 3b965806f..a83ea3310 100644 --- a/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Serialization/DeserializationOptionsBuilder.cs @@ -46,7 +46,9 @@ public int MaxDepth public ArrayPool? ArrayPool { get; set; } + public bool ClearsBuffer { get; set; } = OptionsDefaults.ClearsBuffer; + public DeserializationOptions Create() - => new DeserializationOptions(this.MaxArrayLength, this.MaxMapCount, this.MaxPropertyKeyLength, this.MaxDepth, this.StringEncoding, this.ArrayPool ?? ArrayPool.Shared); + => new DeserializationOptions(this.MaxArrayLength, this.MaxMapCount, this.MaxPropertyKeyLength, this.MaxDepth, this.StringEncoding, this.ArrayPool ?? ArrayPool.Shared, this.ClearsBuffer); } } diff --git a/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs b/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs index 2cdd62ec6..2775ab89d 100644 --- a/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs +++ b/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs @@ -36,8 +36,8 @@ public async ValueTask SerializeAsync(AsyncSerializationOperationContext context [return: MaybeNull] public abstract T Deserialize(ref DeserializationOperationContext context, ref SequenceReader source); - public abstract ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource); + public abstract ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source); public abstract bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in T obj); - public abstract ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, T obj); + public abstract ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, T obj); } } diff --git a/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude b/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude index 9335f56b1..e4c516f34 100644 --- a/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude +++ b/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude @@ -1,7 +1,7 @@ <#+ void WriteContext(bool isSerialization, bool isAsync) { - var typeClass = isAsync ? "sealed class" : "struct"; + var typeClass = isAsync ? "sealed partial class" : "struct"; var typePrefix = isSerialization ? "Serialization" : "Deserialization"; var typeName = $"{(isAsync ? "Async" : String.Empty)}{typePrefix}OperationContext"; var optionTypeName = $"{typePrefix}Options"; diff --git a/src/MsgPack.Abstraction/Serialization/Serializer.cs b/src/MsgPack.Abstraction/Serialization/Serializer.cs index a07d09f2f..c43c4e611 100644 --- a/src/MsgPack.Abstraction/Serialization/Serializer.cs +++ b/src/MsgPack.Abstraction/Serialization/Serializer.cs @@ -1,4 +1,4 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. +// Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. diff --git a/src/MsgPack.Abstraction/Serialization/Serializer`2.cs b/src/MsgPack.Abstraction/Serialization/Serializer`2.cs index f0b9fd72f..a33e61069 100644 --- a/src/MsgPack.Abstraction/Serialization/Serializer`2.cs +++ b/src/MsgPack.Abstraction/Serialization/Serializer`2.cs @@ -124,7 +124,7 @@ public T Deserialize(ref ReadOnlyMemory memorySource, CancellationToken ca public ValueTask DeserializeAsync(Stream streamSource, CancellationToken cancellationToken = default) { this.InitializeAsyncDeserializationOperationContext(cancellationToken, out var context); - return this._underlying.DeserializeAsync(context, streamSource); + return this._underlying.DeserializeAsync(context, context.CreateSequence(streamSource)); } public bool DeserializeTo(ref SequenceReader reader, T obj, CancellationToken cancellationToken = default) @@ -154,7 +154,7 @@ public bool DeserializeTo(ref ReadOnlyMemory memorySource, T obj, Cancella public ValueTask DeserializeToAsync(Stream streamSource, T obj, CancellationToken cancellationToken = default) { this.InitializeAsyncDeserializationOperationContext(cancellationToken, out var context); - return this._underlying.DeserializeToAsync(context, streamSource, obj); + return this._underlying.DeserializeToAsync(context, context.CreateSequence(streamSource), obj); } } } diff --git a/src/MsgPack.Abstraction/Throw.cs b/src/MsgPack.Abstraction/Throw.cs index 4d67dc451..3ef1c8d47 100644 --- a/src/MsgPack.Abstraction/Throw.cs +++ b/src/MsgPack.Abstraction/Throw.cs @@ -16,6 +16,9 @@ public static void ArgumentNull(string paramName) public static void ArgumentOutOfRange(string paramName, string message) => throw new ArgumentOutOfRangeException(paramName, message); + public static void ObjectDisposed(string? name) + => throw new ObjectDisposedException(name); + public static void ExtensionsIsNotSupported() => throw new NotSupportedException($"Extension type is not supported in this encoder."); From 4abac45973c9d63ed56bee3f4787aee887eaf697 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Fri, 26 Jun 2020 20:18:57 +0900 Subject: [PATCH 31/82] Change Advance method's parameter type to Int64 --- src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs b/src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs index e7c53f904..e5e9acaa7 100644 --- a/src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs +++ b/src/MsgPack.Abstraction/Internal/ReadOnlyStreamSequence.cs @@ -109,14 +109,14 @@ public async ValueTask FetchAsync(int requestHint, CancellationToken cancellatio /// Length to be advanced. /// is greater than length of . /// This object is already disposed. - public void Advance(int length) + public void Advance(long length) { if (this._array == null) { Throw.ObjectDisposed(this.ToString()); } - this.Memory = this.Memory.Slice(Ensure.IsNotGreaterThan(length, this.Memory.Length)); + this.Memory = this.Memory.Slice((int)Ensure.IsNotGreaterThan(length, (long)this.Memory.Length)); } } } From c749e4cfb734774b5b931c7a806176688a31436d Mon Sep 17 00:00:00 2001 From: yfakariya Date: Sun, 28 Jun 2020 11:43:00 +0900 Subject: [PATCH 32/82] Add StringEncodingAttribute This class enable individual encoding specification for msgpack. --- .../Serialization/StringEncodingAttribute.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 src/MsgPack.Serialization/Serialization/StringEncodingAttribute.cs diff --git a/src/MsgPack.Serialization/Serialization/StringEncodingAttribute.cs b/src/MsgPack.Serialization/Serialization/StringEncodingAttribute.cs new file mode 100644 index 000000000..54f0fd666 --- /dev/null +++ b/src/MsgPack.Serialization/Serialization/StringEncodingAttribute.cs @@ -0,0 +1,35 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Text; + +namespace MsgPack.Serialization +{ + /// + /// Specifies for this member's value. + /// + public sealed class StringEncodingAttribute : Attribute + { + /// + /// Gets name of the encoding. + /// + /// Name of the encoding. + public string Name { get; } + + internal Encoding Encoding { get; } + + /// + /// Initializes new instance. + /// + /// + /// Name of the encoding. This value must be valid name for . + /// + public StringEncodingAttribute(string name) + { + this.Name = Ensure.NotBlank(name); + this.Encoding = Encoding.GetEncoding(name, EncoderFallback.ExceptionFallback, DecoderFallback.ReplacementFallback); + } + } +} From 7cdfb7824185b31ffdcb2f1eaa75493c330d2e61 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Tue, 7 Jul 2020 21:16:14 +0900 Subject: [PATCH 33/82] Refine Ensure * Add additional members. * Independent from Throw for portability. --- src/MsgPack.Abstraction/Ensure.cs | 54 ++++++++++++++++++++++++++----- src/MsgPack.Abstraction/Throw.cs | 6 ---- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/MsgPack.Abstraction/Ensure.cs b/src/MsgPack.Abstraction/Ensure.cs index 0f2f62aae..18517a85d 100644 --- a/src/MsgPack.Abstraction/Ensure.cs +++ b/src/MsgPack.Abstraction/Ensure.cs @@ -3,7 +3,6 @@ // See the LICENSE in the project root for more information. using System; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; @@ -16,8 +15,31 @@ public static T NotNull([NotNull]T value, [CallerArgumentExpression("value")] { if (value == null) { - Throw.ArgumentNull(paramName); - Debug.Assert(value != null); + ThrowArgumentNull(paramName); + } + + return value; + } + + public static string NotNullNorEmpty([NotNull]string? value, [CallerArgumentExpression("value")] string paramName = null!) + { + NotNull(value); + + if (value.Length ==0) + { + ThrowArgumentEmptyString(paramName); + } + + return value; + } + + public static string NotBlank([NotNull] string? value, [CallerArgumentExpression("value")] string paramName = null!) + { + NotNull(value); + + if (String.IsNullOrWhiteSpace(value)) + { + ThrowArgumentBlankString(paramName); } return value; @@ -27,7 +49,7 @@ public static int IsNotNegative(int value, [CallerArgumentExpression("value")]st { if (value < 0) { - Throw.ArgumentOutOfRange(paramName, "Value cannot be negative number."); + ThrowArgumentOutOfRange(paramName, "Value cannot be negative number."); } return value; @@ -38,7 +60,7 @@ public static T IsNotLessThan(T value, T minInclusive, [CallerArgumentExpress { if (value.CompareTo(minInclusive) < 0) { - Throw.ArgumentOutOfRange(paramName, $"Value cannot be less than {minInclusive}."); + ThrowArgumentOutOfRange(paramName, $"Value cannot be less than {minInclusive}."); } return value; @@ -49,7 +71,7 @@ public static T IsNotGreaterThan(T value, T maxInclusive, [CallerArgumentExpr { if (value.CompareTo(maxInclusive) > 0) { - Throw.ArgumentOutOfRange(paramName, $"Value cannot be greater than {maxInclusive}."); + ThrowArgumentOutOfRange(paramName, $"Value cannot be greater than {maxInclusive}."); } return value; @@ -60,15 +82,31 @@ public static T IsBetween(T value, T minInclusive, T maxInclusive, [CallerArg { if (value.CompareTo(minInclusive) < 0) { - Throw.ArgumentOutOfRange(paramName, $"Value cannot be less than {minInclusive}."); + ThrowArgumentOutOfRange(paramName, $"Value cannot be less than {minInclusive}."); } if (value.CompareTo(maxInclusive) > 0) { - Throw.ArgumentOutOfRange(paramName, $"Value cannot be greater than {maxInclusive}."); + ThrowArgumentOutOfRange(paramName, $"Value cannot be greater than {maxInclusive}."); } return value; } + + [DoesNotReturn] + private static void ThrowArgumentNull(string paramName) + => throw new ArgumentNullException(paramName); + + [DoesNotReturn] + private static void ThrowArgumentOutOfRange(string paramName, string message) + => throw new ArgumentOutOfRangeException(paramName, message); + + [DoesNotReturn] + private static void ThrowArgumentEmptyString(string paramName) + => throw new ArgumentException("Value cannot be empty string.", paramName); + + [DoesNotReturn] + private static void ThrowArgumentBlankString(string paramName) + => throw new ArgumentException("Value cannot be empty or blank string.", paramName); } } diff --git a/src/MsgPack.Abstraction/Throw.cs b/src/MsgPack.Abstraction/Throw.cs index 3ef1c8d47..3519f13f4 100644 --- a/src/MsgPack.Abstraction/Throw.cs +++ b/src/MsgPack.Abstraction/Throw.cs @@ -10,12 +10,6 @@ namespace MsgPack { internal static class Throw { - public static void ArgumentNull(string paramName) - => throw new ArgumentNullException(paramName); - - public static void ArgumentOutOfRange(string paramName, string message) - => throw new ArgumentOutOfRangeException(paramName, message); - public static void ObjectDisposed(string? name) => throw new ObjectDisposedException(name); From bbcf644c1e31d4ae02a394d773de1582f283367f Mon Sep 17 00:00:00 2001 From: yfakariya Date: Tue, 7 Jul 2020 21:18:53 +0900 Subject: [PATCH 34/82] Move FormatFeatures from decoder to encoder/decoder options --- .../Internal/FormatDecoder.cs | 5 +- .../Internal/FormatDecoderOptions.cs | 5 +- .../Internal/FormatEncoderOptions.cs | 5 +- .../Internal/MessagePackDecoder.cs | 10 +--- src/MsgPack.Json/Json/JsonDecoder.cs | 11 +--- src/MsgPack.Json/Json/JsonDecoderOptions.cs | 2 +- src/MsgPack.Json/Json/JsonEncoderOptions.cs | 2 +- src/MsgPack.Json/Json/JsonFormatFeatures.cs | 23 +++++++++ test/Benchmark/_SampleObject.cs | 50 +++++++++---------- 9 files changed, 61 insertions(+), 52 deletions(-) create mode 100644 src/MsgPack.Json/Json/JsonFormatFeatures.cs diff --git a/src/MsgPack.Abstraction/Internal/FormatDecoder.cs b/src/MsgPack.Abstraction/Internal/FormatDecoder.cs index 70ff70f0d..632f3f7cc 100644 --- a/src/MsgPack.Abstraction/Internal/FormatDecoder.cs +++ b/src/MsgPack.Abstraction/Internal/FormatDecoder.cs @@ -19,14 +19,11 @@ namespace MsgPack.Internal /// public abstract partial class FormatDecoder { - public FormatFeatures FormatFeatures { get; } - public FormatDecoderOptions Options { get; } - protected FormatDecoder(FormatDecoderOptions options, FormatFeatures formatFeatures) + protected FormatDecoder(FormatDecoderOptions options) { this.Options = Ensure.NotNull(options); - this.FormatFeatures = Ensure.NotNull(formatFeatures); } [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] diff --git a/src/MsgPack.Abstraction/Internal/FormatDecoderOptions.cs b/src/MsgPack.Abstraction/Internal/FormatDecoderOptions.cs index ef55e0238..59b6aac3a 100644 --- a/src/MsgPack.Abstraction/Internal/FormatDecoderOptions.cs +++ b/src/MsgPack.Abstraction/Internal/FormatDecoderOptions.cs @@ -28,7 +28,9 @@ public abstract class FormatDecoderOptions public bool ClearsBuffer { get; } - protected FormatDecoderOptions(FormatDecoderOptionsBuilder builder) + public FormatFeatures Features { get; } + + protected FormatDecoderOptions(FormatDecoderOptionsBuilder builder, FormatFeatures features) { builder = Ensure.NotNull(builder); @@ -42,6 +44,7 @@ protected FormatDecoderOptions(FormatDecoderOptionsBuilder builder) this.MaxNumberLengthInBytes = builder.MaxNumberLengthInBytes; this.MaxStringLengthInBytes = builder.MaxStringLengthInBytes; this.MaxBinaryLengthInBytes = builder.MaxBinaryLengthInBytes; + this.Features = Ensure.NotNull(features); } } } diff --git a/src/MsgPack.Abstraction/Internal/FormatEncoderOptions.cs b/src/MsgPack.Abstraction/Internal/FormatEncoderOptions.cs index 45a60a7d1..b78dd8720 100644 --- a/src/MsgPack.Abstraction/Internal/FormatEncoderOptions.cs +++ b/src/MsgPack.Abstraction/Internal/FormatEncoderOptions.cs @@ -20,7 +20,9 @@ public abstract class FormatEncoderOptions public bool ClearsBuffer { get; } - protected FormatEncoderOptions(FormatEncoderOptionsBuilder builder) + public FormatFeatures Features { get; } + + protected FormatEncoderOptions(FormatEncoderOptionsBuilder builder, FormatFeatures features) { builder = Ensure.NotNull(builder); @@ -30,6 +32,7 @@ protected FormatEncoderOptions(FormatEncoderOptionsBuilder builder) this.ByteBufferPool = builder.ByteBufferPool; this.CharBufferPool = builder.CharBufferPool; this.ClearsBuffer = builder.ClearsBuffer; + this.Features = Ensure.NotNull(features); } } } diff --git a/src/MsgPack.Core/Internal/MessagePackDecoder.cs b/src/MsgPack.Core/Internal/MessagePackDecoder.cs index 4bd24d4ca..f9dbd80d6 100644 --- a/src/MsgPack.Core/Internal/MessagePackDecoder.cs +++ b/src/MsgPack.Core/Internal/MessagePackDecoder.cs @@ -11,16 +11,8 @@ namespace MsgPack.Internal { public sealed partial class MessagePackDecoder : FormatDecoder { - private static readonly FormatFeatures MessagePackFormatFeatures = - new FormatFeaturesBuilder - { - IsContextful = false, - CanCountCollectionItems = true, - CanSpecifyStringEncoding = true - }.Build(); - public MessagePackDecoder(MessagePackDecoderOptions options) - : base(options, MessagePackFormatFeatures) + : base(options) { this._detectCollectionEnds = this.DetectCollectionEnds; } diff --git a/src/MsgPack.Json/Json/JsonDecoder.cs b/src/MsgPack.Json/Json/JsonDecoder.cs index 537d503fe..4de440a9e 100644 --- a/src/MsgPack.Json/Json/JsonDecoder.cs +++ b/src/MsgPack.Json/Json/JsonDecoder.cs @@ -14,19 +14,10 @@ namespace MsgPack.Json /// public abstract partial class JsonDecoder : FormatDecoder { - private static readonly FormatFeatures JsonFormatFeatures = - new FormatFeaturesBuilder - { - CanCountCollectionItems = false, - CanSpecifyStringEncoding = false, - SupportsExtensionTypes = false, - IsContextful = true - }.Build(); - public new JsonDecoderOptions Options { get; } protected JsonDecoder(JsonDecoderOptions options) - : base(options, JsonFormatFeatures) + : base(options) { this.Options = options; } diff --git a/src/MsgPack.Json/Json/JsonDecoderOptions.cs b/src/MsgPack.Json/Json/JsonDecoderOptions.cs index 43f77b5e7..bbb295938 100644 --- a/src/MsgPack.Json/Json/JsonDecoderOptions.cs +++ b/src/MsgPack.Json/Json/JsonDecoderOptions.cs @@ -16,7 +16,7 @@ public class JsonDecoderOptions : FormatDecoderOptions public JsonParseOptions ParseOptions { get; } public JsonDecoderOptions(JsonDecoderOptionsBuilder builder) - : base(builder) + : base(builder, JsonFormatFeatures.Value) { this.ParseOptions = builder.ParseOptions; } diff --git a/src/MsgPack.Json/Json/JsonEncoderOptions.cs b/src/MsgPack.Json/Json/JsonEncoderOptions.cs index cc12fa8f3..0399e13b9 100644 --- a/src/MsgPack.Json/Json/JsonEncoderOptions.cs +++ b/src/MsgPack.Json/Json/JsonEncoderOptions.cs @@ -57,7 +57,7 @@ public sealed class JsonEncoderOptions : FormatEncoderOptions internal ReadOnlyMemory EscapeTargetChars4Byte { get; } public JsonEncoderOptions(JsonEncoderOptionsBuilder builder) - : base(builder) + : base(builder, JsonFormatFeatures.Value) { this._singleInfinityFormatter = builder.InfinityHandling switch diff --git a/src/MsgPack.Json/Json/JsonFormatFeatures.cs b/src/MsgPack.Json/Json/JsonFormatFeatures.cs new file mode 100644 index 000000000..d17149e58 --- /dev/null +++ b/src/MsgPack.Json/Json/JsonFormatFeatures.cs @@ -0,0 +1,23 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using MsgPack.Internal; +using MsgPack.Serialization; + +namespace MsgPack.Json +{ + internal static class JsonFormatFeatures + { + public static FormatFeatures Value { get; } = + new FormatFeaturesBuilder("Json") + { + CanCountCollectionItems = false, + CanSpecifyStringEncoding = false, + IsContextful = false, + PreferredSerializationMethod = SerializationMethod.Map, + AvailableSerializationMethods = AvailableSerializationMethods.Map, + SupportsExtensionTypes = false + }.Build(); + } +} diff --git a/test/Benchmark/_SampleObject.cs b/test/Benchmark/_SampleObject.cs index f2562501d..fcd7bf93e 100644 --- a/test/Benchmark/_SampleObject.cs +++ b/test/Benchmark/_SampleObject.cs @@ -707,7 +707,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co { #if STRING #if NO_OPT - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { CheckNextItemExists(ref reader, ref propertyIterator); } @@ -718,7 +718,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co #if INT32 #if NO_OPT - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { CheckNextItemExists(ref reader, ref propertyIterator); } @@ -729,7 +729,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co #if BOOL #if NO_OPT - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { CheckNextItemExists(ref reader, ref propertyIterator); } @@ -740,7 +740,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co #if COLLECTION #if NO_OPT - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { CheckNextItemExists(ref reader, ref propertyIterator); } @@ -748,7 +748,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co context.IncrementDepth(); #if NO_OPT - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { #endif var arrayLength = decoder.DecodeArrayHeader(ref reader); @@ -777,14 +777,14 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co context.DecrementDepth(); #if NO_OPT - if (!decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { CheckNextItemExists(ref reader, ref propertyIterator); } #endif context.IncrementDepth(); #if NO_OPT - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { #endif var mapCount = decoder.DecodeMapHeader(ref reader); @@ -822,7 +822,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co { // Map - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { for (var i = 0; i < itemsCount; i++) { @@ -916,7 +916,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co #endif #warning TODO: No Collection items deserialized! context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { var arrayLength = decoder.DecodeArrayHeader(ref reader); // If settable @@ -960,7 +960,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co { #endif context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { var mapCount = decoder.DecodeMapHeader(ref reader); // If settable @@ -1096,7 +1096,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co #endif #warning TODO: No Collection items deserialized! context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { var arrayLength = decoder.DecodeArrayHeader(ref reader); // If settable @@ -1140,7 +1140,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co { #endif context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { var mapCount = decoder.DecodeMapHeader(ref reader); // If settable @@ -1186,7 +1186,7 @@ public sealed override bool DeserializeTo(ref DeserializationOperationContext co } #if NO_OPT - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { #endif decoder.Drain(ref reader, context.CollectionContext, itemsCount - 5, context.CancellationToken); @@ -1237,7 +1237,7 @@ private static bool TryDecodeArrayOrMapHeader(AsyncDeserializationOperationConte return false; } - if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (context.Decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { arrayOrMap = context.Decoder.DecodeArrayOrMapHeader(ref reader, out itemsCount); if (itemsCount < 5) @@ -1461,7 +1461,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa if (arrayOrMap.IsArray) { #if STRING - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { @@ -1476,7 +1476,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa #endif #if INT32 - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { @@ -1491,7 +1491,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa #endif #if BOOL - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { @@ -1506,7 +1506,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa #endif #if COLLECTION - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { @@ -1516,7 +1516,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { long arrayLength; while (!TryDecodeArrayHeader(context, ref sequence, out arrayLength, out requestHint)) @@ -1570,7 +1570,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa } context.DecrementDepth(); - if (!context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (!context.Decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { while (!this.TryCheckNextItemExists(ref propertyIterator, sequence, out requestHint)) { @@ -1579,7 +1579,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa } context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { long mapCount; while (!TryDecodeMapHeader(context, ref sequence, out mapCount, out requestHint)) @@ -1707,7 +1707,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa case 3: { context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { long arrayLength; while (!TryDecodeArrayHeader(context, ref sequence, out arrayLength, out requestHint)) @@ -1766,7 +1766,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa case 4: { context.IncrementDepth(); - if (decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { long mapCount; while (!TryDecodeMapHeader(context, ref sequence, out mapCount, out requestHint)) @@ -1850,7 +1850,7 @@ public sealed override async ValueTask DeserializeToAsync(AsyncDeserializa } } - if (context.Decoder.FormatFeatures.CanCountCollectionItems) // OPTIMIZABLE + if (context.Decoder.Options.Features.CanCountCollectionItems) // OPTIMIZABLE { itemsCount -= 5; while (!TryDrain(context, ref sequence, itemsCount, out requestHint)) @@ -1911,7 +1911,7 @@ public sealed override void Serialize(ref SerializationOperationContext context, public sealed override int[] Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) { - if (context.Decoder.FormatFeatures.CanCountCollectionItems) + if (context.Decoder.Options.Features.CanCountCollectionItems) { var length = context.Decoder.DecodeArrayHeader(ref source); var result = new int[length]; From fbc8f0257709a27271476fcae2d751ce47880d9c Mon Sep 17 00:00:00 2001 From: yfakariya Date: Tue, 7 Jul 2020 21:20:05 +0900 Subject: [PATCH 35/82] Add SerializationMethod for options This commit also refactor builder and constructor relationship. --- .../Internal/FormatFeatures.cs | 29 +++++++++++++++++++ .../Internal/FormatFeaturesBuilder.cs | 10 +++++++ .../Internal/OptionsDefaults.cs | 2 ++ .../MsgPack.Abstraction.csproj | 3 +- .../AsyncSerializationOperationContext.cs | 2 ++ .../AvailableSerializationMethods.cs | 16 ++++++++++ .../ObjectSerializationContext.cs | 12 ++++++-- .../Serialization/OperationContext.ttinclude | 21 ++++++++++++-- .../SerializationOperationContext.cs | 2 ++ .../Serialization/SerializationOptions.cs | 29 ++++++++++++++++--- .../SerializationOptionsBuilder.cs | 9 ++++-- test/Benchmark/_SampleObject.cs | 9 ++++-- 12 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 src/MsgPack.Abstraction/Serialization/AvailableSerializationMethods.cs diff --git a/src/MsgPack.Abstraction/Internal/FormatFeatures.cs b/src/MsgPack.Abstraction/Internal/FormatFeatures.cs index 4b490fa21..03cd0439e 100644 --- a/src/MsgPack.Abstraction/Internal/FormatFeatures.cs +++ b/src/MsgPack.Abstraction/Internal/FormatFeatures.cs @@ -2,6 +2,8 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +using MsgPack.Serialization; + namespace MsgPack.Internal { public sealed class FormatFeatures @@ -9,6 +11,8 @@ public sealed class FormatFeatures #warning TODO: REMOVE public bool IsContextful { get; } + public string Name { get; } + /// /// Gets a value which indicates the underlying format supports collection length. /// @@ -58,12 +62,37 @@ public sealed class FormatFeatures /// public bool SupportsExtensionTypes { get; } + public SerializationMethod PreferredSerializationMethod { get; } + public AvailableSerializationMethods AvailableSerializationMethods { get; } + internal FormatFeatures(FormatFeaturesBuilder builder) { + this.Name = builder.Name; this.IsContextful = builder.IsContextful; this.CanCountCollectionItems = builder.CanCountCollectionItems; this.CanSpecifyStringEncoding = builder.CanSpecifyStringEncoding; this.SupportsExtensionTypes = builder.SupportsExtensionTypes; + this.PreferredSerializationMethod = builder.PreferredSerializationMethod; + this.AvailableSerializationMethods = builder.AvailableSerializationMethods; + } + + internal SerializationMethod GetSerializationMethod(SerializationMethod? contextPreferredMethod) + { + if (contextPreferredMethod == null) + { + return this.PreferredSerializationMethod; + } + + if (contextPreferredMethod == SerializationMethod.Array && (this.AvailableSerializationMethods & AvailableSerializationMethods.Array) == 0) + { + Throw.UnavailableMethod(this.Name, SerializationMethod.Array); + } + else if ((this.AvailableSerializationMethods & AvailableSerializationMethods.Map) == 0) + { + Throw.UnavailableMethod(this.Name, SerializationMethod.Map); + } + + return contextPreferredMethod.GetValueOrDefault(); } } } diff --git a/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs b/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs index 66aa16c24..d08d617f9 100644 --- a/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs +++ b/src/MsgPack.Abstraction/Internal/FormatFeaturesBuilder.cs @@ -2,14 +2,24 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +using MsgPack.Serialization; + namespace MsgPack.Internal { public sealed class FormatFeaturesBuilder { + public string Name { get; } public bool IsContextful { get; set; } public bool CanCountCollectionItems { get; set; } public bool CanSpecifyStringEncoding { get; set; } public bool SupportsExtensionTypes { get; set; } + public SerializationMethod PreferredSerializationMethod { get; set; } + public AvailableSerializationMethods AvailableSerializationMethods { get; set; } + + public FormatFeaturesBuilder(string name) + { + this.Name = Ensure.NotBlank(name).Trim(); + } public FormatFeatures Build() => new FormatFeatures(this); } diff --git a/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs b/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs index a8ecbb089..84ddac7cf 100644 --- a/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs +++ b/src/MsgPack.Abstraction/Internal/OptionsDefaults.cs @@ -3,6 +3,7 @@ // See the LICENSE in the project root for more information. using System.Buffers; +using MsgPack.Serialization; namespace MsgPack.Internal { @@ -26,5 +27,6 @@ internal static class OptionsDefaults public static readonly ArrayPool CharBufferPool = ArrayPool.Shared; public static readonly bool ClearsBuffer = true; public static readonly bool CanTreatRealAsInteger = true; + public static readonly SerializationMethod? PreferredSerializationMethod = null; } } diff --git a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj index 965c43fb5..f5cc62568 100644 --- a/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj +++ b/src/MsgPack.Abstraction/MsgPack.Abstraction.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -7,6 +7,7 @@ + diff --git a/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs index 9b2ffe7fb..5f5a6ad23 100644 --- a/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs +++ b/src/MsgPack.Abstraction/Serialization/AsyncSerializationOperationContext.cs @@ -20,6 +20,7 @@ public sealed partial class AsyncSerializationOperationContext public FormatEncoder Encoder { get; } public SerializationOptions Options { get; } public Encoding? StringEncoding => this.Options.StringEncoding; + public SerializationMethod SerializationMethod { get; } public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } @@ -29,6 +30,7 @@ public AsyncSerializationOperationContext(FormatEncoder encoder, SerializationOp this.Options = options ?? SerializationOptions.Default; this.CurrentDepth = 0; this.CancellationToken = cancellationToken; + this.SerializationMethod = encoder.Options.Features.GetSerializationMethod(this.Options.PreferredSerializationMethod); } public SerializationOperationContext AsSerializationOperationContext() diff --git a/src/MsgPack.Abstraction/Serialization/AvailableSerializationMethods.cs b/src/MsgPack.Abstraction/Serialization/AvailableSerializationMethods.cs new file mode 100644 index 000000000..e0efe48c3 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/AvailableSerializationMethods.cs @@ -0,0 +1,16 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; + +namespace MsgPack.Serialization +{ + [Flags] + public enum AvailableSerializationMethods + { + None = 0, + Array = 0x1, + Map = 0x2 + } +} diff --git a/src/MsgPack.Abstraction/Serialization/ObjectSerializationContext.cs b/src/MsgPack.Abstraction/Serialization/ObjectSerializationContext.cs index d4f86ba0b..fe78f0051 100644 --- a/src/MsgPack.Abstraction/Serialization/ObjectSerializationContext.cs +++ b/src/MsgPack.Abstraction/Serialization/ObjectSerializationContext.cs @@ -1,4 +1,4 @@ -// Copyright (c) FUJIWARA, Yusuke and all contributors. +// Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. @@ -6,10 +6,16 @@ namespace MsgPack.Serialization { -#warning TODO: Remove UGLY TExtensionType public sealed class ObjectSerializationContext { - public ObjectSerializer GetSerializer(object providerParameter) + public SerializationOptions Options { get; } + + public ObjectSerializer GetSerializer(object? providerParameter) + { + throw new NotImplementedException(); + } + + public ObjectSerializer GetSerializer(Type targetType, object? providerParameter) { throw new NotImplementedException(); } diff --git a/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude b/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude index e4c516f34..511ab8e92 100644 --- a/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude +++ b/src/MsgPack.Abstraction/Serialization/OperationContext.ttinclude @@ -7,6 +7,7 @@ void WriteContext(bool isSerialization, bool isAsync) var optionTypeName = $"{typePrefix}Options"; var primitiveType = isSerialization ? "FormatEncoder" : "FormatDecoder"; var primitiveName = isSerialization ? "Encoder" : "Decoder"; + var primitiveVariable = primitiveName.ToLowerInvariant(); #> // Copyright (c) FUJIWARA, Yusuke and all contributors. // This file is licensed under Apache2 license. @@ -39,7 +40,13 @@ namespace MsgPack.Serialization public <#= optionTypeName #> Options { get; } public Encoding? StringEncoding => this.Options.StringEncoding; <#+ - if (!isSerialization) + if (isSerialization) + { +#> + public SerializationMethod SerializationMethod { get; } +<#+ + } + else { #> public ArrayPool ByteBufferPool => this.Options.ByteBufferPool; @@ -49,12 +56,20 @@ namespace MsgPack.Serialization public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } - public <#= typeName #>(<#= primitiveType #> <#= primitiveName.ToLowerInvariant() #>, <#= optionTypeName #>? options, CancellationToken cancellationToken) + public <#= typeName #>(<#= primitiveType #> <#= primitiveVariable #>, <#= optionTypeName #>? options, CancellationToken cancellationToken) { - this.<#= primitiveName #> = Ensure.NotNull(<#= primitiveName.ToLowerInvariant() #>); + this.<#= primitiveName #> = Ensure.NotNull(<#= primitiveVariable #>); this.Options = options ?? <#= optionTypeName #>.Default; this.CurrentDepth = 0; this.CancellationToken = cancellationToken; +<#+ + if (isSerialization) + { +#> + this.SerializationMethod = <#= primitiveVariable #>.Options.Features.GetSerializationMethod(this.Options.PreferredSerializationMethod); +<#+ + } +#> } <#+ diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOperationContext.cs b/src/MsgPack.Abstraction/Serialization/SerializationOperationContext.cs index 75d24d1c2..c96a2e500 100644 --- a/src/MsgPack.Abstraction/Serialization/SerializationOperationContext.cs +++ b/src/MsgPack.Abstraction/Serialization/SerializationOperationContext.cs @@ -20,6 +20,7 @@ public struct SerializationOperationContext public FormatEncoder Encoder { get; } public SerializationOptions Options { get; } public Encoding? StringEncoding => this.Options.StringEncoding; + public SerializationMethod SerializationMethod { get; } public int CurrentDepth { get; private set; } public CancellationToken CancellationToken { get; } @@ -29,6 +30,7 @@ public SerializationOperationContext(FormatEncoder encoder, SerializationOptions this.Options = options ?? SerializationOptions.Default; this.CurrentDepth = 0; this.CancellationToken = cancellationToken; + this.SerializationMethod = encoder.Options.Features.GetSerializationMethod(this.Options.PreferredSerializationMethod); } public CollectionContext CollectionContext => new CollectionContext(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, this.CurrentDepth); diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOptions.cs b/src/MsgPack.Abstraction/Serialization/SerializationOptions.cs index 0b9d8d13d..5aab3b4f2 100644 --- a/src/MsgPack.Abstraction/Serialization/SerializationOptions.cs +++ b/src/MsgPack.Abstraction/Serialization/SerializationOptions.cs @@ -14,13 +14,34 @@ public sealed class SerializationOptions public Encoding? StringEncoding { get; } + public SerializationMethod? PreferredSerializationMethod { get; } + + /// + /// Gets or sets a value indicating whether generated and/or reflection serializers should not access non public members via privileged reflection. + /// + /// + /// true if privileged reflection access is disabled; otherwise, false. Defaults to false. + /// + /// + /// The privileged reflection means: + /// + /// Access for non-public fields or property accessors via reflection. This operation requires ReflectionPermission of MemberAccess or RestrictedMemberAccess. + /// Writing values for init only fields via reflection. This operation requires SecurityPermission of SerializationFormatter. + /// + /// If the program run on non-privileged Silverlight environment or restricted desktop CLR, + /// serialization and deserialization should fail with SecurityException. + /// + public bool DisablePrivilegedAccess { get; } + + internal SerializationOptions( - int maxDepth, - Encoding? stringEncoding + SerializationOptionsBuilder builder ) { - this.MaxDepth = maxDepth; - this.StringEncoding = stringEncoding; + this.MaxDepth = builder.MaxDepth; + this.StringEncoding = builder.StringEncoding; + this.PreferredSerializationMethod = builder.PreferredSerializationMethod; + this.DisablePrivilegedAccess = builder.DisablePrivilegedAccess; } } } diff --git a/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs b/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs index 0f6475495..2e9d82986 100644 --- a/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs +++ b/src/MsgPack.Abstraction/Serialization/SerializationOptionsBuilder.cs @@ -2,6 +2,7 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +using System.Text; using MsgPack.Internal; namespace MsgPack.Serialization @@ -16,9 +17,13 @@ public int MaxDepth set => this._maxDepth = Ensure.IsBetween(value, 1, OptionsDefaults.MaxMultiByteCollectionLength); } - public System.Text.Encoding? StringEncoding { get; set; } + public Encoding? StringEncoding { get; set; } + + public SerializationMethod? PreferredSerializationMethod { get; set; } = OptionsDefaults.PreferredSerializationMethod; + + public bool DisablePrivilegedAccess { get; set; } public SerializationOptions Create() - => new SerializationOptions(this.MaxDepth, this.StringEncoding); + => new SerializationOptions(this); } } diff --git a/test/Benchmark/_SampleObject.cs b/test/Benchmark/_SampleObject.cs index fcd7bf93e..5d3e547a7 100644 --- a/test/Benchmark/_SampleObject.cs +++ b/test/Benchmark/_SampleObject.cs @@ -72,7 +72,6 @@ public static SerializationFormat GetMessagePack(this SerializationFormatFactory => isCurrent ? CurrentMessagePackSerializationFormat : LegacyMessagePackSerializationFormat; } - public static class JsonSerializationFormatFactoryExtensions { private static readonly SerializationFormat SimpleJsonSerializationFormat = @@ -200,7 +199,13 @@ public SampleSerializer(ObjectSerializationContext ownedContext) = true; #endif private FormatFeatures FormatFeatures { get; set; } = - new FormatFeaturesBuilder + new FormatFeaturesBuilder( +#if MSGPACK + "msgpack" +#else + "json" +#endif + ) { #if MSGPACK CanCountCollectionItems = true, From b189d681f0416aa251a78cee939e74f14cbe39cb Mon Sep 17 00:00:00 2001 From: yfakariya Date: Tue, 7 Jul 2020 21:21:21 +0900 Subject: [PATCH 36/82] Add StringBuilder overload for EncodeString --- .../Internal/FormatEncoder.cs | 12 ++++ .../Internal/StringBuilderExtensions.cs | 60 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/MsgPack.Abstraction/Internal/StringBuilderExtensions.cs diff --git a/src/MsgPack.Abstraction/Internal/FormatEncoder.cs b/src/MsgPack.Abstraction/Internal/FormatEncoder.cs index 6e85ef2a1..928c1e26c 100644 --- a/src/MsgPack.Abstraction/Internal/FormatEncoder.cs +++ b/src/MsgPack.Abstraction/Internal/FormatEncoder.cs @@ -49,6 +49,18 @@ public void EncodeString(string? value, IBufferWriter buffer, Encoding? en this.EncodeString(value.AsSpan(), buffer, encoding, cancellationToken); } + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + public void EncodeString(StringBuilder? value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default) + { + if (value == null) + { + this.EncodeNull(buffer); + return; + } + + this.EncodeString(value.ToSequence(), buffer, encoding, cancellationToken); + } + public abstract void EncodeString(ReadOnlySpan value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default); public abstract void EncodeString(in ReadOnlySequence value, IBufferWriter buffer, Encoding? encoding = null, CancellationToken cancellationToken = default); diff --git a/src/MsgPack.Abstraction/Internal/StringBuilderExtensions.cs b/src/MsgPack.Abstraction/Internal/StringBuilderExtensions.cs new file mode 100644 index 000000000..9b3e380ec --- /dev/null +++ b/src/MsgPack.Abstraction/Internal/StringBuilderExtensions.cs @@ -0,0 +1,60 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Text; + +namespace MsgPack.Internal +{ + internal static partial class StringBuilderExtensions + { + public static ReadOnlySequence ToSequence(this StringBuilder builder) + { + var runningIndex = 0L; + var firstSegment = default(StringBuilderChunkSegment); + var lastSegment = default(StringBuilderChunkSegment); + foreach(var chunk in builder.GetChunks()) + { + if (chunk.IsEmpty) + { + continue; + } + + var segment = new StringBuilderChunkSegment(chunk, runningIndex); + runningIndex += chunk.Length; + if (lastSegment == null) + { + firstSegment = segment; + } + else + { + lastSegment.SetNext(segment); + } + + lastSegment = segment; + } + + if (lastSegment == null) + { + return ReadOnlySequence.Empty; + } + + return new ReadOnlySequence(firstSegment!, 0, lastSegment, lastSegment.Memory.Length - 1); + } + + private sealed class StringBuilderChunkSegment : ReadOnlySequenceSegment + { + public void SetNext(StringBuilderChunkSegment next) + => this.Next = next; + + public StringBuilderChunkSegment(ReadOnlyMemory chunk, long runningIndex) + { + this.Memory = chunk; + this.RunningIndex = runningIndex; + } + } + } +} From 5370aeb9673b31f260a66a93b1e6c051c95a46aa Mon Sep 17 00:00:00 2001 From: yfakariya Date: Tue, 7 Jul 2020 21:22:01 +0900 Subject: [PATCH 37/82] Define object based serializer interface This commit also fix DeserializeTo method design. It should only available for mutable collection, so `in` modifier is not suitable. --- .../Serialization/ObjectSerializer.cs | 43 ++++++++++++++ .../Serialization/ObjectSerializer`1.cs | 57 +++++++++++++++---- src/MsgPack.Abstraction/Throw.cs | 8 +++ test/Benchmark/_SampleObject.cs | 6 +- 4 files changed, 100 insertions(+), 14 deletions(-) create mode 100644 src/MsgPack.Abstraction/Serialization/ObjectSerializer.cs diff --git a/src/MsgPack.Abstraction/Serialization/ObjectSerializer.cs b/src/MsgPack.Abstraction/Serialization/ObjectSerializer.cs new file mode 100644 index 000000000..bc65229c1 --- /dev/null +++ b/src/MsgPack.Abstraction/Serialization/ObjectSerializer.cs @@ -0,0 +1,43 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Threading.Tasks; +using MsgPack.Internal; + +namespace MsgPack.Serialization +{ + public abstract class ObjectSerializer + { + protected ObjectSerializationContext OwnerContext { get; } + public SerializerCapabilities Capabilities { get; } + public bool CanSerialize => (this.Capabilities & SerializerCapabilities.Serialize) != 0; + public bool CanDeserialize => (this.Capabilities & SerializerCapabilities.Deserialize) != 0; + public bool CanDeserializeTo => (this.Capabilities & SerializerCapabilities.DeserializeTo) != 0; + + protected ObjectSerializer(ObjectSerializationContext ownerContext, SerializerCapabilities capabilities) + { + this.OwnerContext = Ensure.NotNull(ownerContext); + this.Capabilities = capabilities; + } + + public abstract void SerializeObject(ref SerializationOperationContext context, object? obj, IBufferWriter sink); + public async ValueTask SerializeObjectAsync(AsyncSerializationOperationContext context, object? obj, Stream streamSink) + { + await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) + { + var serializationOperationContext = context.AsSerializationOperationContext(); + this.SerializeObject(ref serializationOperationContext, obj, writer); + } + } + + [return: MaybeNull] + public abstract object? DeserializeObject(ref DeserializationOperationContext context, ref SequenceReader source); + public abstract ValueTask DeserializeObjectAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source); + public abstract bool DeserializeObjectTo(ref DeserializationOperationContext context, ref SequenceReader source, object obj); + public abstract ValueTask DeserializeObjectToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj); + } +} diff --git a/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs b/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs index 2775ab89d..67f688870 100644 --- a/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs +++ b/src/MsgPack.Abstraction/Serialization/ObjectSerializer`1.cs @@ -2,29 +2,28 @@ // This file is licensed under Apache2 license. // See the LICENSE in the project root for more information. +using System; using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using MsgPack.Internal; namespace MsgPack.Serialization { - public abstract class ObjectSerializer + public abstract class ObjectSerializer : ObjectSerializer { - protected ObjectSerializationContext OwnerContext { get; } - public SerializerCapabilities Capabilities { get; } - public bool CanSerialize => (this.Capabilities & SerializerCapabilities.Serialize) != 0; - public bool CanDeserialize => (this.Capabilities & SerializerCapabilities.Deserialize) != 0; - public bool CanDeserializeTo => (this.Capabilities & SerializerCapabilities.DeserializeTo) != 0; + private static readonly bool CanBeNull = !typeof(T).IsValueType || Nullable.GetUnderlyingType(typeof(T)) != null; protected ObjectSerializer(ObjectSerializationContext ownerContext, SerializerCapabilities capabilities) - { - this.OwnerContext = Ensure.NotNull(ownerContext); - this.Capabilities = capabilities; - } + : base(ownerContext, capabilities) { } public abstract void Serialize(ref SerializationOperationContext context, [AllowNull] T obj, IBufferWriter sink); + + public sealed override void SerializeObject(ref SerializationOperationContext context, object? obj, IBufferWriter sink) + => this.Serialize(ref context, Cast(obj), sink); + public async ValueTask SerializeAsync(AsyncSerializationOperationContext context, [AllowNull] T obj, Stream streamSink) { await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) @@ -36,8 +35,44 @@ public async ValueTask SerializeAsync(AsyncSerializationOperationContext context [return: MaybeNull] public abstract T Deserialize(ref DeserializationOperationContext context, ref SequenceReader source); + + public sealed override object? DeserializeObject(ref DeserializationOperationContext context, ref SequenceReader source) + => this.Deserialize(ref context, ref source); + public abstract ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source); - public abstract bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in T obj); + + public sealed override async ValueTask DeserializeObjectAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + => await this.DeserializeAsync(context, source).ConfigureAwait(false); + + public abstract bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, T obj); + + public sealed override bool DeserializeObjectTo(ref DeserializationOperationContext context, ref SequenceReader source, object obj) + => this.DeserializeTo(ref context, ref source, Cast(obj)!); + public abstract ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, T obj); + + public sealed override async ValueTask DeserializeObjectToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + => await this.DeserializeObjectToAsync(context, source, Cast(obj)).ConfigureAwait(false); + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + [return: MaybeNull] + [return: NotNullIfNotNull("obj")] + private static T Cast(object? obj) + { + if (!(obj is null)) + { + return (T)obj; + } + else if (CanBeNull) + { + return (T)obj; + } + else + { + Throw.CannotBeNull(typeof(T)); + // never + return default; + } + } } } diff --git a/src/MsgPack.Abstraction/Throw.cs b/src/MsgPack.Abstraction/Throw.cs index 3519f13f4..7e86a40d0 100644 --- a/src/MsgPack.Abstraction/Throw.cs +++ b/src/MsgPack.Abstraction/Throw.cs @@ -3,8 +3,10 @@ // See the LICENSE in the project root for more information. using System; +using System.Runtime.Serialization; using System.Text; using MsgPack.Internal; +using MsgPack.Serialization; namespace MsgPack { @@ -86,6 +88,9 @@ public static void InsufficientInputForDecodeArrayOrMapHeader(long position, int } } + public static void CannotBeNull(Type type) + => throw new SerializationException($"Value of type '{type}' cannot be null."); + public static void InsufficientInputForDecodeArrayHeader(long position, int requestHint) { if (requestHint < 0) @@ -98,6 +103,9 @@ public static void InsufficientInputForDecodeArrayHeader(long position, int requ } } + public static void UnavailableMethod(string name, SerializationMethod array) + => throw new NotSupportedException($"SerializationMethod '{array}' is not supported in '{name}' format."); + public static void InsufficientInputForDecodeMapHeader(long position, int requestHint) { if (requestHint < 0) diff --git a/test/Benchmark/_SampleObject.cs b/test/Benchmark/_SampleObject.cs index 5d3e547a7..d174c920f 100644 --- a/test/Benchmark/_SampleObject.cs +++ b/test/Benchmark/_SampleObject.cs @@ -633,7 +633,7 @@ public sealed override async ValueTask DeserializeAsync(AsyncDeser private static void ThrowNotEnoughItems(long actual, int expected) => throw new MessageTypeException(); - public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader reader, in SampleObject obj) + public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader reader, SampleObject obj) { const int propertyCount = 0 @@ -1945,7 +1945,7 @@ public sealed override ValueTask DeserializeAsync(AsyncDeserializationOpe throw new NotImplementedException(); } - public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in int[] obj) + public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, int[] obj) { throw new NotImplementedException(); } @@ -1976,7 +1976,7 @@ public sealed override ValueTask DeserializeAsync(AsyncDeserializationOpera throw new NotImplementedException(); } - public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in int obj) + public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, int obj) { throw new NotImplementedException(); } From 1791e9a2aa3f0695c6bb0ca585845f577178c5e0 Mon Sep 17 00:00:00 2001 From: yfakariya Date: Tue, 7 Jul 2020 21:47:19 +0900 Subject: [PATCH 38/82] Add initial Reflection based serializer (builder) --- .../MsgPack.Serialization.Reflection.csproj | 21 +- .../Internal/AsyncDecodeHelpers.Primitives.cs | 567 ++++++++++++ .../Internal/AsyncDecodeHelpers.Primitives.tt | 87 ++ .../Internal/AsyncDecodeHelpers.cs | 224 +++++ .../Internal/CollectionTypeAndCount.cs | 20 + .../Internal/CollectionTypeAndIterator.cs | 20 + .../ArrayDelegateSerializer.cs | 211 +++++ .../CollectionDelegateSerializer.cs | 414 +++++++++ .../ReflectionSerializers/Delegates.cs | 25 + .../DictionaryDelegateSerializer.cs | 550 ++++++++++++ .../IReflectionObjectSerializer.cs | 19 + .../ObjectDelegateSerializer.AsyncDecoders.cs | 125 +++ .../ObjectDelegateSerializer.Decoders.cs | 137 +++ .../ObjectDelegateSerializer.Encoders.cs | 128 +++ .../ObjectDelegateSerializer.cs | 834 ++++++++++++++++++ .../ReflectionSerializerBuilder.cs | 12 + .../ReflectionSerializerThrow.cs | 25 + .../ReflectionSerializer`1.cs | 113 +++ .../ReflectionSerializer`2.cs | 126 --- 19 files changed, 3531 insertions(+), 127 deletions(-) create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.Primitives.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.Primitives.tt create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/Internal/CollectionTypeAndCount.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/Internal/CollectionTypeAndIterator.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ArrayDelegateSerializer.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/CollectionDelegateSerializer.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/Delegates.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/DictionaryDelegateSerializer.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/IReflectionObjectSerializer.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.AsyncDecoders.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.Decoders.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.Encoders.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializerBuilder.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializerThrow.cs create mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`1.cs delete mode 100644 src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`2.cs diff --git a/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj b/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj index 1f7897a73..da2a63db6 100644 --- a/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj +++ b/src/MsgPack.Serialization.Reflection/MsgPack.Serialization.Reflection.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1 @@ -9,4 +9,23 @@ + + + TextTemplatingFileGenerator + AsyncDecodeHelpers.Primitives.cs + + + + + + + + + + True + True + AsyncDecodeHelpers.Primitives.tt + + + diff --git a/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.Primitives.cs b/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.Primitives.cs new file mode 100644 index 000000000..8cc846f88 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.Primitives.cs @@ -0,0 +1,567 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + partial class AsyncDecodeHelpers + { + public static async ValueTask DecodeSByteAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + SByte result; + while (!TryDecodeSByte(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeSByte(FormatDecoder decoder, ReadOnlyStreamSequence source, out SByte result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeSByte(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableSByteAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + SByte? result; + while (!TryDecodeNullableSByte(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableSByte(FormatDecoder decoder, ReadOnlyStreamSequence source, out SByte? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableSByte(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeInt16Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Int16 result; + while (!TryDecodeInt16(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeInt16(FormatDecoder decoder, ReadOnlyStreamSequence source, out Int16 result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeInt16(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableInt16Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Int16? result; + while (!TryDecodeNullableInt16(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableInt16(FormatDecoder decoder, ReadOnlyStreamSequence source, out Int16? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableInt16(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeInt32Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Int32 result; + while (!TryDecodeInt32(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeInt32(FormatDecoder decoder, ReadOnlyStreamSequence source, out Int32 result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeInt32(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableInt32Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Int32? result; + while (!TryDecodeNullableInt32(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableInt32(FormatDecoder decoder, ReadOnlyStreamSequence source, out Int32? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableInt32(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeInt64Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Int64 result; + while (!TryDecodeInt64(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeInt64(FormatDecoder decoder, ReadOnlyStreamSequence source, out Int64 result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeInt64(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableInt64Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Int64? result; + while (!TryDecodeNullableInt64(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableInt64(FormatDecoder decoder, ReadOnlyStreamSequence source, out Int64? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableInt64(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeByteAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Byte result; + while (!TryDecodeByte(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeByte(FormatDecoder decoder, ReadOnlyStreamSequence source, out Byte result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeByte(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableByteAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Byte? result; + while (!TryDecodeNullableByte(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableByte(FormatDecoder decoder, ReadOnlyStreamSequence source, out Byte? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableByte(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeUInt16Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + UInt16 result; + while (!TryDecodeUInt16(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeUInt16(FormatDecoder decoder, ReadOnlyStreamSequence source, out UInt16 result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeUInt16(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableUInt16Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + UInt16? result; + while (!TryDecodeNullableUInt16(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableUInt16(FormatDecoder decoder, ReadOnlyStreamSequence source, out UInt16? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableUInt16(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeUInt32Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + UInt32 result; + while (!TryDecodeUInt32(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeUInt32(FormatDecoder decoder, ReadOnlyStreamSequence source, out UInt32 result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeUInt32(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableUInt32Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + UInt32? result; + while (!TryDecodeNullableUInt32(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableUInt32(FormatDecoder decoder, ReadOnlyStreamSequence source, out UInt32? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableUInt32(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeUInt64Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + UInt64 result; + while (!TryDecodeUInt64(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeUInt64(FormatDecoder decoder, ReadOnlyStreamSequence source, out UInt64 result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeUInt64(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableUInt64Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + UInt64? result; + while (!TryDecodeNullableUInt64(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableUInt64(FormatDecoder decoder, ReadOnlyStreamSequence source, out UInt64? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableUInt64(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeBooleanAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Boolean result; + while (!TryDecodeBoolean(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeBoolean(FormatDecoder decoder, ReadOnlyStreamSequence source, out Boolean result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeBoolean(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableBooleanAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Boolean? result; + while (!TryDecodeNullableBoolean(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableBoolean(FormatDecoder decoder, ReadOnlyStreamSequence source, out Boolean? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableBoolean(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeSingleAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Single result; + while (!TryDecodeSingle(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeSingle(FormatDecoder decoder, ReadOnlyStreamSequence source, out Single result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeSingle(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableSingleAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Single? result; + while (!TryDecodeNullableSingle(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableSingle(FormatDecoder decoder, ReadOnlyStreamSequence source, out Single? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableSingle(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeDoubleAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Double result; + while (!TryDecodeDouble(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeDouble(FormatDecoder decoder, ReadOnlyStreamSequence source, out Double result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeDouble(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableDoubleAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + Double? result; + while (!TryDecodeNullableDouble(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableDouble(FormatDecoder decoder, ReadOnlyStreamSequence source, out Double? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableDouble(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeStringAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + String result; + while (!TryDecodeString(decoder, source, encoding: null, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + public static async ValueTask DecodeStringAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, Encoding? encoding, CancellationToken cancellationToken) + { + String result; + while (!TryDecodeString(decoder, source, encoding, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeString(FormatDecoder decoder, ReadOnlyStreamSequence source, Encoding? encoding, out String result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeString(ref reader, out requestHint, encoding, cancellationToken)!; + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableStringAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + String? result; + while (!TryDecodeNullableString(decoder, source, encoding: null, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + public static async ValueTask DecodeNullableStringAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, Encoding? encoding, CancellationToken cancellationToken) + { + String? result; + while (!TryDecodeNullableString(decoder, source, encoding, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableString(FormatDecoder decoder, ReadOnlyStreamSequence source, Encoding? encoding, out String? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableString(ref reader, out requestHint, encoding, cancellationToken); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeBinaryAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + byte[] result; + while (!TryDecodeBinary(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeBinary(FormatDecoder decoder, ReadOnlyStreamSequence source, out byte[] result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeBinary(ref reader, out requestHint)!; + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeNullableBinaryAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + byte[]? result; + while (!TryDecodeNullableBinary(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNullableBinary(FormatDecoder decoder, ReadOnlyStreamSequence source, out byte[]? result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeNullableBinary(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.Primitives.tt b/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.Primitives.tt new file mode 100644 index 000000000..0fb1e8a09 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.Primitives.tt @@ -0,0 +1,87 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Reflection" #> +<#@ import namespace="System.Reflection.Emit" #> +<#@ output extension=".cs" #> +<# +var types = + new [] + { + typeof(sbyte), + typeof(short), + typeof(int), + typeof(long), + typeof(byte), + typeof(ushort), + typeof(uint), + typeof(ulong), + typeof(bool), + typeof(float), + typeof(double), + }.Select(t => (Name: t.Name, Type: t.Name, IsReferenceType: false)) + .Concat( + new [] + { + (Name: "String", Type: "String", IsReferenceType: true), + (Name: "Binary", Type: "byte[]", IsReferenceType: true) + } + ).ToArray(); +#> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +#nullable enable + +using System; +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; + +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + partial class AsyncDecodeHelpers + { +<# +foreach (var type in types) +{ + foreach (var isNullable in new [] { false, true }) + { + var methodName = $"Decode{(isNullable ? "Nullable" : String.Empty)}{type.Name}"; + var returnType = $"{type.Type}{(isNullable ? "?" : String.Empty)}"; +#> + public static async ValueTask<<#= returnType #>> <#= methodName #>Async(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + <#= returnType #> result; + while (!Try<#= methodName #>(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool Try<#= methodName #>(FormatDecoder decoder, ReadOnlyStreamSequence source, out <#= returnType #> result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.<#= methodName #>(ref reader, out requestHint)<#= (!isNullable && type.IsReferenceType) ? "!" : String.Empty #>; + source.Advance(reader.Consumed); + return requestHint == 0; + } + +<# + } +} +#> + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.cs b/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.cs new file mode 100644 index 000000000..b38b22f28 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/Internal/AsyncDecodeHelpers.cs @@ -0,0 +1,224 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + internal static partial class AsyncDecodeHelpers + { + public static async ValueTask TryDecodeNullAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + bool result; + while (!TryDecodeNull(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeNull(FormatDecoder decoder, ReadOnlyStreamSequence source, out bool result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.TryDecodeNull(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeArrayHeaderAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + int result; + while (!TryDecodeArrayHeader(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeArrayHeader(FormatDecoder decoder, ReadOnlyStreamSequence source, out int result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeArrayHeader(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeMapHeaderAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + int result; + while (!TryDecodeMapHeader(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeMapHeader(FormatDecoder decoder, ReadOnlyStreamSequence source, out int result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeArrayHeader(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeArrayOrMapHeaderAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + CollectionTypeAndCount result; + while (!TryDecodeArrayOrMapHeader(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeArrayOrMapHeader(FormatDecoder decoder, ReadOnlyStreamSequence source, out CollectionTypeAndCount result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + var type = decoder.DecodeArrayOrMapHeader(ref reader, out var itemsCount, out requestHint); + result = new CollectionTypeAndCount(type, itemsCount); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeArrayAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + CollectionItemIterator result; + while (!TryDecodeArray(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeArray(FormatDecoder decoder, ReadOnlyStreamSequence source, out CollectionItemIterator result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeArray(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeMapAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + CollectionItemIterator result; + while (!TryDecodeMap(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeMap(FormatDecoder decoder, ReadOnlyStreamSequence source, out CollectionItemIterator result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + result = decoder.DecodeArray(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DecodeArrayOrMapAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + CollectionTypeAndIterator result; + while (!TryDecodeArrayOrMap(decoder, source, out result, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + [MethodImpl(MethodImplOptionsShim.AggressiveInlining)] + private static bool TryDecodeArrayOrMap(FormatDecoder decoder, ReadOnlyStreamSequence source, out CollectionTypeAndIterator result, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + var type = decoder.DecodeArrayOrMap(ref reader, out var iterator, out requestHint); + result = new CollectionTypeAndIterator(type, iterator); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask SkipAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CollectionContext collectionContext, CancellationToken cancellationToken) + { + while (!TrySkip(decoder, source, collectionContext, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + } + + private static bool TrySkip(FormatDecoder decoder, ReadOnlyStreamSequence source, CollectionContext collectionContext, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + decoder.Skip(ref reader, collectionContext, out requestHint, cancellationToken); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DrainAsync(this FormatDecoder decoder, ReadOnlyStreamSequence source, CollectionContext collectionContext, long itemsCount, CancellationToken cancellationToken) + { + while (!TryDrain(decoder, source, collectionContext, itemsCount, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + } + + private static bool TryDrain(FormatDecoder decoder, ReadOnlyStreamSequence source, CollectionContext collectionContext, long itemsCount, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + decoder.Drain(ref reader, collectionContext, itemsCount, out requestHint, cancellationToken); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask CollectionEndsAsync(this CollectionItemIterator iterator, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + bool result; + while (!TryCollectionEnds(iterator, source, out result, out var requestHint)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + + return result; + } + + private static bool TryCollectionEnds(CollectionItemIterator iterator, ReadOnlyStreamSequence source, out bool result, out int requestHint) + { + var reader = new SequenceReader(source.Sequence); + result = iterator.CollectionEnds(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + + public static async ValueTask DrainAsync(this CollectionItemIterator iterator, ReadOnlyStreamSequence source, CancellationToken cancellationToken) + { + while (!TryDrain(iterator, source, out var requestHint, cancellationToken)) + { + await source.FetchAsync(requestHint, cancellationToken).ConfigureAwait(false); + } + } + + private static bool TryDrain(CollectionItemIterator iterator, ReadOnlyStreamSequence source, out int requestHint, CancellationToken cancellationToken) + { + var reader = new SequenceReader(source.Sequence); + iterator.Drain(ref reader, out requestHint); + source.Advance(reader.Consumed); + return requestHint == 0; + } + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/Internal/CollectionTypeAndCount.cs b/src/MsgPack.Serialization.Reflection/Serialization/Internal/CollectionTypeAndCount.cs new file mode 100644 index 000000000..0f444d866 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/Internal/CollectionTypeAndCount.cs @@ -0,0 +1,20 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + internal struct CollectionTypeAndCount + { + public CollectionType CollectionType { get; } + public int ItemsCount { get; } + + public CollectionTypeAndCount(CollectionType collectionType, int itemsCount) + { + this.CollectionType = collectionType; + this.ItemsCount = itemsCount; + } + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/Internal/CollectionTypeAndIterator.cs b/src/MsgPack.Serialization.Reflection/Serialization/Internal/CollectionTypeAndIterator.cs new file mode 100644 index 000000000..7f5e4be9e --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/Internal/CollectionTypeAndIterator.cs @@ -0,0 +1,20 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using MsgPack.Internal; + +namespace MsgPack.Serialization.Internal +{ + internal struct CollectionTypeAndIterator + { + public CollectionType CollectionType { get; } + public CollectionItemIterator Iterator { get; } + + public CollectionTypeAndIterator(CollectionType collectionType, CollectionItemIterator iterator) + { + this.CollectionType = collectionType; + this.Iterator = iterator; + } + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ArrayDelegateSerializer.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ArrayDelegateSerializer.cs new file mode 100644 index 000000000..aed418e19 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ArrayDelegateSerializer.cs @@ -0,0 +1,211 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Threading.Tasks; +using MsgPack.Internal; +using MsgPack.Serialization.Internal; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + /// + /// Specialized implementation of for arrays. + /// + internal sealed class ArrayDelegateSerializer : IReflectionObjectSerializer + { + private readonly Type _targetType; + private readonly bool _itemIsCollection; + private readonly Serialization _serializeItem; + private readonly Deserialization _deserializeItem; +#if FEATURE_TAP + private readonly AsyncDeserialization _deserializeItemAsync; +#endif // FEATURE_TAP + + public ArrayDelegateSerializer( + Serialization serializeItem, + Deserialization deserializeItem, +#if FEATURE_TAP + AsyncDeserialization deserializeItemAsync, +#endif // FEATURE_TAP + Type targetType, + in CollectionTraits traits + ) + { + this._targetType = targetType; + this._serializeItem = serializeItem; + this._deserializeItem = deserializeItem; +#if FEATURE_TAP + this._deserializeItemAsync = deserializeItemAsync; +#endif // FEATURE_TAP +#warning TODO: allowNonCollectionEnumerableTypes + this._itemIsCollection = traits.ElementType!.GetCollectionTraits(CollectionTraitOptions.None, allowNonCollectionEnumerableTypes: false).CollectionType != CollectionKind.NotCollection; + } + + public void Serialize(ref SerializationOperationContext context, object? obj, IBufferWriter sink) + { + var encoder = context.Encoder; + + if (obj is null) + { + context.Encoder.EncodeNull(sink); + return; + } + + var array = (object?[])obj; + + encoder.EncodeArrayStart(array.Length, sink, context.CollectionContext); + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + for (var i = 0; i < array.Length; i++) + { + encoder.EncodeArrayItemStart(i, sink, context.CollectionContext); + this._serializeItem(ref context, array[i], sink); + encoder.EncodeArrayItemEnd(i, sink, context.CollectionContext); + + i++; + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + encoder.EncodeArrayEnd(array.Length, sink, context.CollectionContext); + } + + public object? Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + { + var decoder = context.Decoder; + if (decoder.TryDecodeNull(ref source)) + { + return null; + } + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + object? result; + if (decoder.Options.Features.CanCountCollectionItems) + { + result = this.Deserialize(decoder.DecodeArrayHeader(ref source), ref context, ref source); + } + else + { + result = this.Deserialize(decoder, ref context, ref source); + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + return result; + } + + private object?[] Deserialize(int itemsCount, ref DeserializationOperationContext context, ref SequenceReader source) + { + var result = new object?[itemsCount]; + + for (var i = 0; i < itemsCount; i++) + { + result[i] = this._deserializeItem(ref context, ref source); + } + + return result; + } + + private object?[] Deserialize(FormatDecoder decoder, ref DeserializationOperationContext context, ref SequenceReader source) + { + var result = new List(); + + var iterator = decoder.DecodeArray(ref source); + while (!iterator.CollectionEnds(ref source)) + { + result.Add(this._deserializeItem(ref context, ref source)); + } + iterator.Drain(ref source); + + return result.ToArray(); + } + + public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, object obj) + => throw new NotSupportedException($"Array serializer does not support DeserializeTo."); + +#if FEATURE_TAP + + public async ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var decoder = context.Decoder; + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + object? result; + if (decoder.Options.Features.CanCountCollectionItems) + { + result = + await this.DeserializeAsync( + await decoder.DecodeArrayHeaderAsync(source, context.CancellationToken).ConfigureAwait(false), + context, + source + ).ConfigureAwait(false); + } + else + { + result = await this.DeserializeAsync(decoder, context, source).ConfigureAwait(false); + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + return result; + } + + private async ValueTask DeserializeAsync(int itemsCount, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var result = new object?[itemsCount]; + + for (var i = 0; i < itemsCount; i++) + { + result[i] = await this._deserializeItemAsync(context, source).ConfigureAwait(false); + } + + return result; + } + + private async ValueTask DeserializeAsync(FormatDecoder decoder, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var result = new List(); + + var iterator = await decoder.DecodeArrayAsync(source, context.CancellationToken).ConfigureAwait(false); + while (!await iterator.CollectionEndsAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + result.Add(await this._deserializeItemAsync(context, source).ConfigureAwait(false)); + } + await iterator.DrainAsync(source, context.CancellationToken).ConfigureAwait(false); + + return result.ToArray(); + } + + public ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + { + Throw.DeserializeToOnlyAvailableForMutableCollection(this._targetType); + // never + return default; + } +#endif // FEATURE_TAP + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/CollectionDelegateSerializer.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/CollectionDelegateSerializer.cs new file mode 100644 index 000000000..cf9f6fd32 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/CollectionDelegateSerializer.cs @@ -0,0 +1,414 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using MsgPack.Internal; +using MsgPack.Serialization.Internal; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + /// + /// Implements for , , + /// , , , , + /// , , and except array. + /// + internal sealed class CollectionDelegateSerializer : IReflectionObjectSerializer + { + private readonly bool _itemIsCollection; + private readonly Func? _getCount; + private readonly Func _getEnumerator; + private readonly Serialization _serializeItem; + private readonly Func? _constructorWithCapacity; + private readonly Func? _defaultConstructor; + private readonly Action _add; + private readonly Deserialization _deserializeItem; +#if FEATURE_TAP + private readonly AsyncDeserialization _deserializeItemAsync; +#endif // FEATURE_TAP + + public CollectionDelegateSerializer( + Serialization serializeItem, + Deserialization deserializeItem, +#if FEATURE_TAP + AsyncDeserialization deserializeItemAsync, +#endif // FEATURE_TAP + Type targetType, + in CollectionTraits traits + ) + { + var countPropertyGetter = traits.CountPropertyGetter!; + var getEnumeratorMethod = traits.GetEnumeratorMethod!; + var addMethod = traits.AddMethod!; + var constructorWithCapacity = traits.ConstructorWithCapacity; + var defaultConstructor = traits.DefaultConstructor; + + this._getEnumerator = c => (IEnumerator)getEnumeratorMethod.Invoke(c, BindingFlags.DoNotWrapExceptions, null, null, null)!; + this._getCount = c => (int)countPropertyGetter.Invoke(c, BindingFlags.DoNotWrapExceptions, null, null, null)!; + this._add = + addMethod == null ? + new Action((c, i) => Throw.UndeserializableCollection(targetType)) : + new Action((c, i) => addMethod.Invoke(c, BindingFlags.DoNotWrapExceptions, null, new object?[] { i }, null)); + this._constructorWithCapacity = + constructorWithCapacity == null ? + default(Func) : + c => constructorWithCapacity!.Invoke(BindingFlags.DoNotWrapExceptions, new object?[] { c })!; + this._defaultConstructor = () => defaultConstructor!.Invoke(BindingFlags.DoNotWrapExceptions, null)!; + if (this._constructorWithCapacity == null && this._defaultConstructor == null) + { + Throw.NoConstructorForCollection(targetType); + } + + this._serializeItem = serializeItem; + this._deserializeItem = deserializeItem; +#if FEATURE_TAP + this._deserializeItemAsync = deserializeItemAsync; +#endif // FEATURE_TAP + +#warning TODO: allowNonCollectionEnumerableTypes + this._itemIsCollection = traits.ElementType!.GetCollectionTraits(CollectionTraitOptions.None, allowNonCollectionEnumerableTypes: false).CollectionType != CollectionKind.NotCollection; + } + + public void Serialize(ref SerializationOperationContext context, object? obj, IBufferWriter sink) + { + var encoder = context.Encoder; + + if (obj is null) + { + encoder.EncodeNull(sink); + return; + } + + if (this._getCount != null) + { + this.SerializeCountable(ref context, obj, sink, this._getCount); + } + else + { + this.SerializeUncountable(ref context, obj, sink); + } + } + + private void SerializeCountable(ref SerializationOperationContext context, object obj, IBufferWriter sink, Func getCount) + { + var encoder = context.Encoder; + + var count = getCount(obj); + var i = 0; + var enumerator = this._getEnumerator(obj); + try + { + encoder.EncodeArrayStart(count, sink, context.CollectionContext); + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + while (enumerator.MoveNext()) + { + encoder.EncodeArrayItemStart(i, sink, context.CollectionContext); + this._serializeItem(ref context, enumerator.Current, sink); + encoder.EncodeArrayItemEnd(i, sink, context.CollectionContext); + i++; + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + encoder.EncodeArrayEnd(count, sink, context.CollectionContext); + } + finally + { + if (enumerator is IDisposable disposable) + { + disposable.Dispose(); + } + } + } + + private void SerializeUncountable(ref SerializationOperationContext context, object obj, IBufferWriter sink) + { + var encoder = context.Encoder; + + var asList = new List(); + var enumerator = this._getEnumerator(obj); + try + { + while (enumerator.MoveNext()) + { + asList.Add(enumerator.Current); + } + } + finally + { + if (enumerator is IDisposable disposable) + { + disposable.Dispose(); + } + } + + var i = 0; + encoder.EncodeArrayStart(asList.Count, sink, context.CollectionContext); + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + foreach (var item in asList) + { + encoder.EncodeArrayItemStart(i, sink, context.CollectionContext); + this._serializeItem(ref context, item, sink); + encoder.EncodeArrayItemEnd(i, sink, context.CollectionContext); + i++; + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + encoder.EncodeArrayEnd(asList.Count, sink, context.CollectionContext); + } + + public object? Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + { + var decoder = context.Decoder; + if (decoder.TryDecodeNull(ref source)) + { + return null; + } + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + object? result; + if (decoder.Options.Features.CanCountCollectionItems) + { + result = this.Deserialize(decoder.DecodeArrayHeader(ref source), ref context, ref source); + } + else + { + result = this.Deserialize(decoder, ref context, ref source); + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + return result; + } + + private object Deserialize(int itemsCount, ref DeserializationOperationContext context, ref SequenceReader source) + { + var result = this._constructorWithCapacity?.Invoke(itemsCount) ?? this._defaultConstructor!(); + + for (var i = 0; i < itemsCount; i++) + { + this._add(result, this._deserializeItem(ref context, ref source)); + } + + return result; + } + + private object Deserialize(FormatDecoder decoder, ref DeserializationOperationContext context, ref SequenceReader source) + { + var result = this._defaultConstructor?.Invoke() ?? this._constructorWithCapacity!(0); + + var iterator = decoder.DecodeArray(ref source); + while (!iterator.CollectionEnds(ref source)) + { + this._add(result, this._deserializeItem(ref context, ref source)); + } + iterator.Drain(ref source); + + return result; + } + + public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, object obj) + { + var decoder = context.Decoder; + if (decoder.TryDecodeNull(ref source)) + { + return false; + } + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + if (decoder.Options.Features.CanCountCollectionItems) + { + this.DeserializeTo(decoder.DecodeArrayHeader(ref source), ref context, ref source, obj); + } + else + { + this.DeserializeTo(decoder, ref context, ref source, obj); + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + return true; + } + + private void DeserializeTo(int itemsCount, ref DeserializationOperationContext context, ref SequenceReader source, object obj) + { + for (var i = 0; i < itemsCount; i++) + { + this._add(obj, this._deserializeItem(ref context, ref source)); + } + } + + private void DeserializeTo(FormatDecoder decoder, ref DeserializationOperationContext context, ref SequenceReader source, object obj) + { + var iterator = decoder.DecodeArray(ref source); + while (!iterator.CollectionEnds(ref source)) + { + this._add(obj, this._deserializeItem(ref context, ref source)); + } + iterator.Drain(ref source); + } + +#if FEATURE_TAP + + public async ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var decoder = context.Decoder; + + if (await decoder.TryDecodeNullAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + return null; + } + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + object? result; + if (decoder.Options.Features.CanCountCollectionItems) + { + result = + await this.DeserializeAsync( + await decoder.DecodeArrayHeaderAsync(source, context.CancellationToken).ConfigureAwait(false), + context, + source + ).ConfigureAwait(false); + } + else + { + result = await this.DeserializeAsync(decoder, context, source).ConfigureAwait(false); + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + return result; + } + + private async ValueTask DeserializeAsync(int itemsCount, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var result = this._constructorWithCapacity?.Invoke(itemsCount) ?? this._defaultConstructor!.Invoke(); + + for (var i = 0; i < itemsCount; i++) + { + this._add(result, await this._deserializeItemAsync(context, source).ConfigureAwait(false)); + } + + return result; + } + + private async ValueTask DeserializeAsync(FormatDecoder decoder, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var result = this._defaultConstructor?.Invoke() ?? this._constructorWithCapacity!.Invoke(0); + + var iterator = await decoder.DecodeArrayAsync(source, context.CancellationToken).ConfigureAwait(false); + + while (!await iterator.CollectionEndsAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + this._add(result, await this._deserializeItemAsync(context, source).ConfigureAwait(false)); + } + + await iterator.DrainAsync(source, context.CancellationToken).ConfigureAwait(false); + + return result; + } + + public async ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + { + var decoder = context.Decoder; + + if (await decoder.TryDecodeNullAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + return false; + } + + if (this._itemIsCollection) + { + context.IncrementDepth(); + } + + if (decoder.Options.Features.CanCountCollectionItems) + { + await this.DeserializeToAsync( + await decoder.DecodeArrayHeaderAsync(source, context.CancellationToken).ConfigureAwait(false), + context, + source, + obj + ).ConfigureAwait(false); + } + else + { + await this.DeserializeToAsync(decoder, context, source, obj).ConfigureAwait(false); + } + + if (this._itemIsCollection) + { + context.DecrementDepth(); + } + + return true; + } + + private async ValueTask DeserializeToAsync(int itemsCount, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + { + for (var i = 0; i < itemsCount; i++) + { + this._add(obj, await this._deserializeItemAsync(context, source).ConfigureAwait(false)); + } + } + + private async ValueTask DeserializeToAsync(FormatDecoder decoder, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + { + var result = this._defaultConstructor?.Invoke() ?? this._constructorWithCapacity!.Invoke(0); + + var iterator = await decoder.DecodeArrayAsync(source, context.CancellationToken).ConfigureAwait(false); + + while (!await iterator.CollectionEndsAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + this._add(result, await this._deserializeItemAsync(context, source).ConfigureAwait(false)); + } + + await iterator.DrainAsync(source, context.CancellationToken).ConfigureAwait(false); + } + +#endif // FEATURE_TAP + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/Delegates.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/Delegates.cs new file mode 100644 index 000000000..6445e39fe --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/Delegates.cs @@ -0,0 +1,25 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MsgPack.Internal; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + internal delegate void Serialization(ref SerializationOperationContext context, object? obj, IBufferWriter sink); + internal delegate object? Deserialization(ref DeserializationOperationContext context, ref SequenceReader source); + internal delegate ValueTask AsyncDeserialization(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source); + internal delegate bool DeserializingFill(ref DeserializationOperationContext context, ref SequenceReader source, object obj); + internal delegate ValueTask AsyncDeserializingFill(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj); + + internal delegate object? PrimitiveDecoder(FormatDecoder decoder, ref SequenceReader source); + internal delegate object? StringDecoder(FormatDecoder decoder, ref SequenceReader source, Encoding? encoding, CancellationToken cancellationToken); + internal delegate object? BinaryDecoder(FormatDecoder decoder, ref SequenceReader source, CancellationToken cancellationToken); + internal delegate ValueTask AsyncPrimitiveDecoder(FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken); + internal delegate ValueTask AsyncStringDecoder(FormatDecoder decoder, ReadOnlyStreamSequence source, Encoding? encoding, CancellationToken cancellationToken); + internal delegate ValueTask AsyncBinaryDecoder(FormatDecoder decoder, ReadOnlyStreamSequence source, CancellationToken cancellationToken); +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/DictionaryDelegateSerializer.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/DictionaryDelegateSerializer.cs new file mode 100644 index 000000000..a6ca895d2 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/DictionaryDelegateSerializer.cs @@ -0,0 +1,550 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using MsgPack.Internal; +using MsgPack.Serialization.Internal; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + /// + /// Implements for , , and . + /// + internal sealed class DictionaryDelegateSerializer : IReflectionObjectSerializer + { + private readonly bool _keyIsCollection; + private readonly bool _valueIsCollection; + private readonly Func _getCount; + private readonly Func _getEnumerator; + private readonly Func _getKey; + private readonly Func _getValue; + private readonly Serialization _serializeKey; + private readonly Serialization _serializeValue; + private readonly Func? _constructorWithCapacity; + private readonly Func? _defaultConstructor; + private readonly Action _add; + private readonly Deserialization _deserializeKey; + private readonly Deserialization _deserializeValue; +#if FEATURE_TAP + private readonly AsyncDeserialization _deserializeKeyAsync; + private readonly AsyncDeserialization _deserializeValueAsync; +#endif // FEATURE_TAP + + public DictionaryDelegateSerializer( + Serialization serializeKey, + Serialization serializeValue, + Deserialization deserializeKey, + Deserialization deserializeValue, +#if FEATURE_TAP + AsyncDeserialization deserializeKeyAsync, + AsyncDeserialization deserializeValueAsync, +#endif // FEATURE_TAP + Type targetType, + in CollectionTraits traits + ) + { + var countPropertyGetter = traits.CountPropertyGetter!; + var getEnumeratorMethod = traits.GetEnumeratorMethod!; + var addMethod = traits.AddMethod; + var constructorWithCapacity = traits.ConstructorWithCapacity; + var defaultConstructor = traits.DefaultConstructor; + + var keyPropertyGetter = traits.ElementType!.GetProperty("Key")!.GetGetMethod()!; + var valuePropertyGetter = traits.ElementType!.GetProperty("Value")!.GetGetMethod()!; + + this._getEnumerator = c => (IEnumerator)getEnumeratorMethod.Invoke(c, BindingFlags.DoNotWrapExceptions, null, null, null)!; + this._getCount = c => (int)countPropertyGetter.Invoke(c, BindingFlags.DoNotWrapExceptions, null, null, null)!; + this._getKey = e => (string)keyPropertyGetter.Invoke(e, BindingFlags.DoNotWrapExceptions, null, null, null)!; + this._getValue = e => valuePropertyGetter.Invoke(e, BindingFlags.DoNotWrapExceptions, null, null, null)!; + + this._add = + addMethod == null ? + new Action((c, k, v) => Throw.UndeserializableCollection(targetType)) : + new Action((c, k, v) => addMethod.Invoke(c, BindingFlags.DoNotWrapExceptions, null, new object?[] { k, v }, null)); + var (keyType, valueType) = traits.GetKeyValueType(); + + this._constructorWithCapacity = + constructorWithCapacity == null ? + default(Func) : + c => constructorWithCapacity!.Invoke(BindingFlags.DoNotWrapExceptions, new object?[] { c })!; + this._defaultConstructor = () => defaultConstructor!.Invoke(BindingFlags.DoNotWrapExceptions, null)!; + if (this._constructorWithCapacity == null && this._defaultConstructor == null) + { + Throw.NoConstructorForCollection(targetType); + } + + this._serializeKey = serializeKey; + this._serializeValue = serializeValue; + + this._deserializeKey = deserializeKey; + this._deserializeValue = deserializeValue; +#if FEATURE_TAP + this._deserializeKeyAsync = deserializeKeyAsync; + this._deserializeValueAsync = deserializeValueAsync; +#endif // FEATURE_TAP + +#warning TODO: allowNonCollectionEnumerableTypes + this._keyIsCollection = keyType!.GetCollectionTraits(CollectionTraitOptions.None, allowNonCollectionEnumerableTypes: false).CollectionType != CollectionKind.NotCollection; + this._valueIsCollection = valueType!.GetCollectionTraits(CollectionTraitOptions.None, allowNonCollectionEnumerableTypes: false).CollectionType != CollectionKind.NotCollection; + } + + public void Serialize(ref SerializationOperationContext context, object? obj, IBufferWriter sink) + { + var encoder = context.Encoder; + if (obj is null) + { + encoder.EncodeNull(sink); + return; + } + + var count = this._getCount(obj); + var i = 0; + var enumerator = this._getEnumerator(obj); + try + { + encoder.EncodeMapStart(count, sink, context.CollectionContext); + while (enumerator.MoveNext()) + { + var entry = enumerator.Current; + var key = this._getKey(entry!); + var value = this._getValue(entry!); + + encoder.EncodeMapKeyStart(i, sink, context.CollectionContext); + + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + this._serializeKey(ref context, key, sink); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + encoder.EncodeMapKeyEnd(i, sink, context.CollectionContext); + + encoder.EncodeMapValueStart(i, sink, context.CollectionContext); + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + this._serializeValue(ref context, key, sink); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + encoder.EncodeMapValueEnd(i, sink, context.CollectionContext); + + i++; + } + encoder.EncodeMapEnd(count, sink, context.CollectionContext); + } + finally + { + if (enumerator is IDisposable disposable) + { + disposable.Dispose(); + } + } + } + + public object? Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + { + var decoder = context.Decoder; + if (decoder.TryDecodeNull(ref source)) + { + return null; + } + + object? result; + if (decoder.Options.Features.CanCountCollectionItems) + { + result = this.Deserialize(decoder.DecodeArrayHeader(ref source), ref context, ref source); + } + else + { + result = this.Deserialize(decoder, ref context, ref source); + } + + return result; + } + + private object Deserialize(int itemsCount, ref DeserializationOperationContext context, ref SequenceReader source) + { + var result = this._constructorWithCapacity?.Invoke(itemsCount) ?? this._defaultConstructor!(); + + for (var i = 0; i < itemsCount; i++) + { + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + var key = this._deserializeKey(ref context, ref source); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + var value = this._deserializeValue(ref context, ref source); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + this._add(result, key, value); + } + + return result; + } + + private object Deserialize(FormatDecoder decoder, ref DeserializationOperationContext context, ref SequenceReader source) + { + var result = this._defaultConstructor?.Invoke() ?? this._constructorWithCapacity!(0); + + var iterator = decoder.DecodeArray(ref source); + + while (!iterator.CollectionEnds(ref source)) + { + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + var key = this._deserializeKey(ref context, ref source); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + var value = this._deserializeValue(ref context, ref source); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + this._add(result, key, value); + } + + iterator.Drain(ref source); + + return result; + } + + public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, object obj) + { + var decoder = context.Decoder; + if (decoder.TryDecodeNull(ref source)) + { + return false; + } + + if (decoder.Options.Features.CanCountCollectionItems) + { + this.DeserializeTo(decoder.DecodeArrayHeader(ref source), ref context, ref source, obj); + } + else + { + this.DeserializeTo(decoder, ref context, ref source, obj); + } + + return true; + } + + private void DeserializeTo(int itemsCount, ref DeserializationOperationContext context, ref SequenceReader source, object obj) + { + for (var i = 0; i < itemsCount; i++) + { + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + var key = this._deserializeKey(ref context, ref source); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + var value = this._deserializeValue(ref context, ref source); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + this._add(obj, key, value); + } + } + + private void DeserializeTo(FormatDecoder decoder, ref DeserializationOperationContext context, ref SequenceReader source, object obj) + { + var iterator = decoder.DecodeArray(ref source); + while (!iterator.CollectionEnds(ref source)) + { + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + var key = this._deserializeKey(ref context, ref source); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + var value = this._deserializeValue(ref context, ref source); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + this._add(obj, key, value); + } + + iterator.Drain(ref source); + } + +#if FEATURE_TAP + + public async ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var decoder = context.Decoder; + + if (await decoder.TryDecodeNullAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + return null; + } + + object? result; + if (decoder.Options.Features.CanCountCollectionItems) + { + result = + await this.DeserializeAsync( + await decoder.DecodeArrayHeaderAsync(source, context.CancellationToken).ConfigureAwait(false), + context, + source + ).ConfigureAwait(false); + } + else + { + result = await this.DeserializeAsync(decoder, context, source).ConfigureAwait(false); + } + + return result; + } + + private async ValueTask DeserializeAsync(int itemsCount, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var result = this._constructorWithCapacity?.Invoke(itemsCount) ?? this._defaultConstructor!.Invoke(); + + for (var i = 0; i < itemsCount; i++) + { + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + var key = await this._deserializeKeyAsync(context, source).ConfigureAwait(false); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + var value = await this._deserializeValueAsync(context, source).ConfigureAwait(false); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + this._add(result, key, value); + } + + return result; + } + + private async ValueTask DeserializeAsync(FormatDecoder decoder, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + var result = this._defaultConstructor?.Invoke() ?? this._constructorWithCapacity!.Invoke(0); + + var iterator = await decoder.DecodeArrayAsync(source, context.CancellationToken).ConfigureAwait(false); + + while (!await iterator.CollectionEndsAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + var key = await this._deserializeKeyAsync(context, source).ConfigureAwait(false); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + var value = await this._deserializeValueAsync(context, source).ConfigureAwait(false); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + this._add(result, key, value); + } + + await iterator.DrainAsync(source, context.CancellationToken).ConfigureAwait(false); + + return result; + } + + public async ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + { + var decoder = context.Decoder; + + if (await decoder.TryDecodeNullAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + return false; + } + + if (decoder.Options.Features.CanCountCollectionItems) + { + await this.DeserializeToAsync( + await decoder.DecodeArrayHeaderAsync(source, context.CancellationToken).ConfigureAwait(false), + context, + source, + obj + ).ConfigureAwait(false); + } + else + { + await this.DeserializeToAsync(decoder, context, source, obj).ConfigureAwait(false); + } + + return true; + } + + private async ValueTask DeserializeToAsync(int itemsCount, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + { + for (var i = 0; i < itemsCount; i++) + { + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + var key = await this._deserializeKeyAsync(context, source).ConfigureAwait(false); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + var value = await this._deserializeValueAsync(context, source).ConfigureAwait(false); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + this._add(obj, key, value); + } + } + + private async ValueTask DeserializeToAsync(FormatDecoder decoder, AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + { + var result = this._defaultConstructor?.Invoke() ?? this._constructorWithCapacity!.Invoke(0); + + var iterator = await decoder.DecodeArrayAsync(source, context.CancellationToken).ConfigureAwait(false); + + while (!await iterator.CollectionEndsAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + if (this._keyIsCollection) + { + context.IncrementDepth(); + } + + var key = await this._deserializeKeyAsync(context, source).ConfigureAwait(false); + + if (this._keyIsCollection) + { + context.DecrementDepth(); + } + + if (this._valueIsCollection) + { + context.IncrementDepth(); + } + + var value = await this._deserializeValueAsync(context, source).ConfigureAwait(false); + + if (this._valueIsCollection) + { + context.DecrementDepth(); + } + + this._add(result, key, value); + } + + await iterator.DrainAsync(source, context.CancellationToken).ConfigureAwait(false); + } + +#endif // FEATURE_TAP + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/IReflectionObjectSerializer.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/IReflectionObjectSerializer.cs new file mode 100644 index 000000000..25144c7ae --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/IReflectionObjectSerializer.cs @@ -0,0 +1,19 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Threading.Tasks; +using MsgPack.Internal; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + internal interface IReflectionObjectSerializer + { + void Serialize(ref SerializationOperationContext context, object? obj, IBufferWriter sink); + object? Deserialize(ref DeserializationOperationContext context, ref SequenceReader source); + bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, object obj); + ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source); + ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj); + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.AsyncDecoders.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.AsyncDecoders.cs new file mode 100644 index 000000000..5ff4c8169 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.AsyncDecoders.cs @@ -0,0 +1,125 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Text; +using System.Threading; +using MsgPack.Internal; +using MsgPack.Serialization.Internal; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + internal partial class ObjectDelegateSerializer + { + private static readonly AsyncPrimitiveDecoder DecodeSByteNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeSByteAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeSByteNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableSByteAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeInt16NonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeInt16Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeInt16NullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableInt16Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeInt32NonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeInt32Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeInt32NullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableInt32Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeInt64NonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeInt64Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeInt64NullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableInt64Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeByteNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeByteAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeByteNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableByteAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeUInt16NonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeUInt16Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeUInt16NullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableUInt16Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeUInt32NonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeUInt32Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeUInt32NullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableUInt32Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeUInt64NonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeUInt64Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeUInt64NullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableUInt64Async(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeBooleanNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeBooleanAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeBooleanNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableBooleanAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeSingleNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeSingleAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeSingleNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableSingleAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeDoubleNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeDoubleAsync(s, c).ConfigureAwait(false); + private static readonly AsyncPrimitiveDecoder DecodeDoubleNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableDoubleAsync(s, c).ConfigureAwait(false); + + // Also for IEnumerable + private static readonly AsyncStringDecoder DecodeStringNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => await d.DecodeStringAsync(s, e, c).ConfigureAwait(false); + + // Also for IEnumerable? + private static readonly AsyncStringDecoder DecodeStringNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => await d.DecodeNullableStringAsync(s, e, c).ConfigureAwait(false); + + private static readonly AsyncStringDecoder DecodeStringBuilderNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => AsStringBuilder(await d.DecodeStringAsync(s, e, c).ConfigureAwait(false)); + + private static readonly AsyncStringDecoder DecodeStringBuilderNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => AsStringBuilder(await d.DecodeNullableStringAsync(s, e, c).ConfigureAwait(false)); + + private static readonly AsyncStringDecoder DecodeCharArrayNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => (await d.DecodeStringAsync(s, e, c).ConfigureAwait(false)).ToCharArray(); + + private static readonly AsyncStringDecoder DecodeCharArrayNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => (await d.DecodeNullableStringAsync(s, e, c).ConfigureAwait(false))?.ToCharArray(); + + private static readonly AsyncStringDecoder DecodeReadOnlyMemoryOfCharNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => (await d.DecodeStringAsync(s, e, c).ConfigureAwait(false)).AsMemory(); + + private static readonly AsyncStringDecoder DecodeReadOnlyMemoryOfCharNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => (await d.DecodeNullableStringAsync(s, e, c).ConfigureAwait(false))?.AsMemory(); + + private static readonly AsyncStringDecoder DecodeMemoryOfCharNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => (await d.DecodeStringAsync(s, e, c).ConfigureAwait(false)).ToCharArray().AsMemory(); + + private static readonly AsyncStringDecoder DecodeMemoryOfCharNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => (await d.DecodeNullableStringAsync(s, e, c).ConfigureAwait(false))?.ToCharArray()?.AsMemory(); + + private static readonly AsyncStringDecoder DecodeReadOnlySequenceOfCharNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => new ReadOnlySequence((await d.DecodeStringAsync(s, e, c).ConfigureAwait(false)).AsMemory()); + + private static readonly AsyncStringDecoder DecodeReadOnlySequenceOfCharNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, Encoding? e, CancellationToken c) => AsSequence(await d.DecodeNullableStringAsync(s, e, c).ConfigureAwait(false)); + + private static readonly AsyncBinaryDecoder DecodeByteArrayNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeBinaryAsync(s, c).ConfigureAwait(false); + + private static readonly AsyncBinaryDecoder DecodeByteArrayNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => await d.DecodeNullableBinaryAsync(s, c).ConfigureAwait(false); + + private static readonly AsyncBinaryDecoder DecodeReadOnlyMemoryOfByteNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => AsReadOnlyMemory(await d.DecodeBinaryAsync(s, c).ConfigureAwait(false)); + + private static readonly AsyncBinaryDecoder DecodeReadOnlyMemoryOfByteNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => AsReadOnlyMemory(await d.DecodeNullableBinaryAsync(s, c).ConfigureAwait(false)); + + private static readonly AsyncBinaryDecoder DecodeMemoryOfByteNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => (await d.DecodeBinaryAsync(s, c).ConfigureAwait(false)).AsMemory(); + + private static readonly AsyncBinaryDecoder DecodeMemoryOfByteNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => (await d.DecodeNullableBinaryAsync(s, c).ConfigureAwait(false))?.AsMemory(); + + private static readonly AsyncBinaryDecoder DecodeReadOnlySequenceOfByteNonNullAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => new ReadOnlySequence(await d.DecodeBinaryAsync(s, c).ConfigureAwait(false)); + + private static readonly AsyncBinaryDecoder DecodeReadOnlySequenceOfByteNullableAsync = + async (FormatDecoder d, ReadOnlyStreamSequence s, CancellationToken c) => AsSequence(await d.DecodeNullableBinaryAsync(s, c).ConfigureAwait(false)); + + // Span/ReadOnlySpan is not supported. + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.Decoders.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.Decoders.cs new file mode 100644 index 000000000..9092aec07 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.Decoders.cs @@ -0,0 +1,137 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Threading; +using MsgPack.Internal; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + internal partial class ObjectDelegateSerializer + { + private static readonly PrimitiveDecoder DecodeSByteNonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeSByte(ref s); + private static readonly PrimitiveDecoder DecodeSByteNullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableSByte(ref s); + private static readonly PrimitiveDecoder DecodeInt16NonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeInt16(ref s); + private static readonly PrimitiveDecoder DecodeInt16Nullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableInt16(ref s); + private static readonly PrimitiveDecoder DecodeInt32NonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeInt32(ref s); + private static readonly PrimitiveDecoder DecodeInt32Nullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableInt32(ref s); + private static readonly PrimitiveDecoder DecodeInt64NonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeInt64(ref s); + private static readonly PrimitiveDecoder DecodeInt64Nullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableInt64(ref s); + private static readonly PrimitiveDecoder DecodeByteNonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeByte(ref s); + private static readonly PrimitiveDecoder DecodeByteNullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableByte(ref s); + private static readonly PrimitiveDecoder DecodeUInt16NonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeUInt16(ref s); + private static readonly PrimitiveDecoder DecodeUInt16Nullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableUInt16(ref s); + private static readonly PrimitiveDecoder DecodeUInt32NonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeUInt32(ref s); + private static readonly PrimitiveDecoder DecodeUInt32Nullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableUInt32(ref s); + private static readonly PrimitiveDecoder DecodeUInt64NonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeUInt64(ref s); + private static readonly PrimitiveDecoder DecodeUInt64Nullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableUInt64(ref s); + private static readonly PrimitiveDecoder DecodeBooleanNonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeBoolean(ref s); + private static readonly PrimitiveDecoder DecodeBooleanNullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableBoolean(ref s); + private static readonly PrimitiveDecoder DecodeSingleNonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeSingle(ref s); + private static readonly PrimitiveDecoder DecodeSingleNullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableSingle(ref s); + private static readonly PrimitiveDecoder DecodeDoubleNonNull = + (FormatDecoder d, ref SequenceReader s) => d.DecodeDouble(ref s); + private static readonly PrimitiveDecoder DecodeDoubleNullable = + (FormatDecoder d, ref SequenceReader s) => d.DecodeNullableDouble(ref s); + + // Also for IEnumerable + private static readonly StringDecoder DecodeStringNonNull = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => d.DecodeString(ref s, e, c); + + // Also for IEnumerable? + private static readonly StringDecoder DecodeStringNullable = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => d.DecodeNullableString(ref s, e, c); + + private static readonly StringDecoder DecodeStringBuilderNonNull = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => AsStringBuilder(d.DecodeString(ref s, e, c)); + + private static readonly StringDecoder DecodeStringBuilderNullable = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => AsStringBuilder(d.DecodeNullableString(ref s, e, c)); + + private static readonly StringDecoder DecodeCharArrayNonNull = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => d.DecodeString(ref s, e, c).ToCharArray(); + + private static readonly StringDecoder DecodeCharArrayNullable = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => d.DecodeNullableString(ref s, e, c)?.ToCharArray(); + + private static readonly StringDecoder DecodeReadOnlyMemoryOfCharNonNull = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => d.DecodeString(ref s, e, c).AsMemory(); + + private static readonly StringDecoder DecodeReadOnlyMemoryOfCharNullable = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => d.DecodeNullableString(ref s, e, c)?.AsMemory(); + + private static readonly StringDecoder DecodeMemoryOfCharNonNull = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => d.DecodeString(ref s, e, c).ToCharArray().AsMemory(); + + private static readonly StringDecoder DecodeMemoryOfCharNullable = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => d.DecodeNullableString(ref s, e, c)?.ToCharArray()?.AsMemory(); + + private static readonly StringDecoder DecodeReadOnlySequenceOfCharNonNull = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => new ReadOnlySequence(d.DecodeString(ref s, e, c).AsMemory()); + + private static readonly StringDecoder DecodeReadOnlySequenceOfCharNullable = + (FormatDecoder d, ref SequenceReader s, Encoding? e, CancellationToken c) => AsSequence(d.DecodeNullableString(ref s, e, c)); + + private static readonly BinaryDecoder DecodeByteArrayNonNull = + (FormatDecoder d, ref SequenceReader s, CancellationToken c) => d.DecodeBinary(ref s, c); + + private static readonly BinaryDecoder DecodeByteArrayNullable = + (FormatDecoder d, ref SequenceReader s, CancellationToken c) => d.DecodeNullableBinary(ref s, c); + + private static readonly BinaryDecoder DecodeReadOnlyMemoryOfByteNonNull = + (FormatDecoder d, ref SequenceReader s, CancellationToken c) => AsReadOnlyMemory(d.DecodeBinary(ref s, c)); + + private static readonly BinaryDecoder DecodeReadOnlyMemoryOfByteNullable = + (FormatDecoder d, ref SequenceReader s, CancellationToken c) => AsReadOnlyMemory(d.DecodeNullableBinary(ref s, c)); + + private static readonly BinaryDecoder DecodeMemoryOfByteNonNull = + (FormatDecoder d, ref SequenceReader s, CancellationToken c) => d.DecodeBinary(ref s, c).AsMemory(); + + private static readonly BinaryDecoder DecodeMemoryOfByteNullable = + (FormatDecoder d, ref SequenceReader s, CancellationToken c) => d.DecodeNullableBinary(ref s, c)?.AsMemory(); + + private static readonly BinaryDecoder DecodeReadOnlySequenceOfByteNonNull = + (FormatDecoder d, ref SequenceReader s, CancellationToken c) => new ReadOnlySequence(d.DecodeBinary(ref s, c)); + + private static readonly BinaryDecoder DecodeReadOnlySequenceOfByteNullable = + (FormatDecoder d, ref SequenceReader s, CancellationToken c) => AsSequence(d.DecodeNullableBinary(ref s, c)); + + [return: NotNullIfNotNull("value")] + private static StringBuilder? AsStringBuilder(string? value) => value == null ? null : new StringBuilder(value); + + [return: NotNullIfNotNull("value")] + private static ReadOnlyMemory? AsReadOnlyMemory(byte[]? value) => value == null ? default(ReadOnlyMemory?) : new ReadOnlyMemory(value); + + [return: NotNullIfNotNull("value")] + private static ReadOnlySequence? AsSequence(string? value) => value == null ? default(ReadOnlySequence?) : new ReadOnlySequence(value.AsMemory()); + + [return: NotNullIfNotNull("value")] + private static ReadOnlySequence? AsSequence(byte[]? value) => value == null ? default(ReadOnlySequence?) : new ReadOnlySequence(value); + + // Span/ReadOnlySpan is not supported. + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.Encoders.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.Encoders.cs new file mode 100644 index 000000000..1b7c36ff9 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.Encoders.cs @@ -0,0 +1,128 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using MsgPack.Internal; + +using PrimitiveEncoder = System.Action>; +using StringEncoder = System.Action, System.Text.Encoding?, System.Threading.CancellationToken>; +using BinaryEncoder = System.Action, System.Threading.CancellationToken>; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + internal partial class ObjectDelegateSerializer + { + private static readonly PrimitiveEncoder EncodeInt32NonNull = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeInt32((int)v!, s); + private static readonly PrimitiveEncoder EncodeInt32Nullable = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeInt32((int?)v, s); + private static readonly PrimitiveEncoder EncodeInt64NonNull = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeInt64((long)v!, s); + private static readonly PrimitiveEncoder EncodeInt64Nullable = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeInt64((long?)v, s); + private static readonly PrimitiveEncoder EncodeUInt32NonNull = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeUInt32((uint)v!, s); + private static readonly PrimitiveEncoder EncodeUInt32Nullable = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeUInt32((uint?)v, s); + private static readonly PrimitiveEncoder EncodeUInt64NonNull = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeUInt64((ulong)v!, s); + private static readonly PrimitiveEncoder EncodeUInt64Nullable = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeUInt64((ulong?)v, s); + private static readonly PrimitiveEncoder EncodeBooleanNonNull = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeBoolean((bool)v!, s); + private static readonly PrimitiveEncoder EncodeBooleanNullable = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeBoolean((bool?)v, s); + private static readonly PrimitiveEncoder EncodeSingleNonNull = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeSingle((float)v!, s); + private static readonly PrimitiveEncoder EncodeSingleNullable = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeSingle((float?)v, s); + private static readonly PrimitiveEncoder EncodeDoubleNonNull = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeDouble((double)v!, s); + private static readonly PrimitiveEncoder EncodeDoubleNullable = + (FormatEncoder e, object? v, IBufferWriter s) => e.EncodeDouble((double?)v, s); + private static readonly StringEncoder EncodeString = + (FormatEncoder e, object? v, IBufferWriter s, Encoding? n, CancellationToken c) => + { + if (v is null) + { + e.EncodeNull(s); + return; + } + + if (v is string str) + { + e.EncodeString(str, s, n, c); + } + else if (v is StringBuilder builder) + { + e.EncodeString(builder, s, n, c); + } + else if (v is char[] array) + { + e.EncodeString(array, s, n, c); + } + else if (v is ReadOnlyMemory rmemory) + { + e.EncodeString(rmemory.Span, s, n, c); + } + else if (v is Memory memory) + { + e.EncodeString(memory.Span, s, n, c); + } + else if (v is ReadOnlySequence sequence) + { + e.EncodeString(sequence, s, n, c); + } + else if (v is IEnumerable collection) + { + e.EncodeString(collection.ToArray(), s, n, c); + } + else + { + Throw.UnexpectedStringType(v); + } + }; + + private static readonly BinaryEncoder EncodeBinary = + (FormatEncoder e, object? v, IBufferWriter s, CancellationToken c) => + { + if (v is null) + { + e.EncodeNull(s); + return; + } + + if (v is byte[] array) + { + e.EncodeBinary(array, s, c); + } + else if (v is ReadOnlyMemory rmemory) + { + e.EncodeBinary(rmemory.Span, s, c); + } + else if (v is Memory memory) + { + e.EncodeBinary(memory.Span, s, c); + } + else if (v is ReadOnlySequence sequence) + { + e.EncodeBinary(sequence, s, c); + } + else if (v is IEnumerable collection) + { + e.EncodeBinary(collection.ToArray(), s, c); + } + else + { + Throw.UnexpectedBinaryType(v); + sequence = default; + } + }; + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.cs new file mode 100644 index 000000000..e812a712d --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ObjectDelegateSerializer.cs @@ -0,0 +1,834 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using MsgPack.Internal; +using MsgPack.Serialization.Internal; + +using PrimitiveEncoder = System.Action>; +using StringEncoder = System.Action, System.Text.Encoding?, System.Threading.CancellationToken>; +using BinaryEncoder = System.Action, System.Threading.CancellationToken>; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + /// + /// Implements for non collection objects and tuples. + /// + internal sealed partial class ObjectDelegateSerializer : IReflectionObjectSerializer + { + private readonly IReadOnlyList _memberValueEncoders; + private readonly IReadOnlyList _memberValueDecoders; + private readonly IReadOnlyList _memberIsCollections; + private readonly IReadOnlyList> _memberValueGetters; + private readonly IReadOnlyList>? _memberValueSetters; + private readonly IReadOnlyDictionary _memberIndexes; + private readonly SerializationTarget _target; + private readonly IReadOnlyList? _tupleTypes; +#if FEATURE_TAP + private readonly IReadOnlyList _asyncMemberValueDecoders; +#endif // FEATURE_TAP + + // object + // tuple + // value tuple + public ObjectDelegateSerializer(ObjectSerializationContext context, SerializationTarget target) + { + this._target = target; + var isTuple = TupleItems.IsTuple(target.Type); + this._tupleTypes = isTuple ? null : TupleItems.CreateTupleTypeList(target.Type); + this._memberValueEncoders = CreateMemberValueEncoders(context, target.Members).ToList(); + this._memberValueDecoders = + CreateMemberValueDecoders( + context, + target.Members, + hasDeserializationConstructor: target.DeserializationConstructor != null, + hasPriviledgedAccess: !context.Options.DisablePrivilegedAccess + ).ToList(); + this._memberIsCollections = DetermineWhetherIndividualMembersAreCollection(target.Members).ToList(); + this._memberValueGetters = CreateMemberValueGetters(target.Members, this._tupleTypes).ToList(); + this._memberValueSetters = this._tupleTypes == null ? null : CreateMemberValueSetters(target.Members).ToList(); + this._memberIndexes = target.Members.Select((m, i) => (Name: m.MemberName, Index: i)).ToDictionary(x => x.Name ?? $"Item{x.Index}", x => x.Index); +#if FEATURE_TAP + this._asyncMemberValueDecoders = + CreateAsyncMemberValueDecoders( + context, + target.Members, + hasDeserializationConstructor: target.DeserializationConstructor != null, + hasPriviledgedAccess: !context.Options.DisablePrivilegedAccess + ).ToList(); +#endif // FEATURE_TAP + } + + private static IEnumerable CreateMemberValueEncoders(ObjectSerializationContext context, IEnumerable members) + { + foreach (var member in members) + { + var type = member.Member.GetMemberValueType(); + var memberInfo = member.Member; + yield return + type switch + { + var t when t == typeof(sbyte) || t == typeof(short) || t == typeof(int) => EncodeInt32NonNull, + var t when t == typeof(sbyte?) || t == typeof(short?) || t == typeof(int?) => EncodeInt32Nullable, + var t when t == typeof(long) => EncodeInt64NonNull, + var t when t == typeof(long?) => EncodeInt64Nullable, + var t when t == typeof(byte) || t == typeof(ushort) || t == typeof(uint) => EncodeUInt32NonNull, + var t when t == typeof(byte?) || t == typeof(ushort?) || t == typeof(uint?) => EncodeUInt32Nullable, + var t when t == typeof(ulong) => EncodeUInt64NonNull, + var t when t == typeof(ulong?) => EncodeUInt64Nullable, + var t when t == typeof(bool) => EncodeBooleanNonNull, + var t when t == typeof(bool?) => EncodeBooleanNullable, + var t when t == typeof(float) => EncodeSingleNonNull, + var t when t == typeof(float?) => EncodeSingleNullable, + var t when t == typeof(double) => EncodeDoubleNonNull, + var t when t == typeof(double?) => EncodeDoubleNullable, + var t + when t == typeof(string) || t == typeof(StringBuilder) || t == typeof(char[]) + || t == typeof(ReadOnlyMemory) || t == typeof(Memory) || t == typeof(ReadOnlySequence) || t == typeof(IEnumerable) + => EncodeString, + var t when t == typeof(ReadOnlySpan) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var t when t == typeof(Span) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var t when t == typeof(byte[]) || t == typeof(ReadOnlyMemory) || t == typeof(Memory) || t == typeof(ReadOnlySequence) || t == typeof(IEnumerable) + => EncodeBinary, + var t when t == typeof(ReadOnlySpan) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var t when t == typeof(Span) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), +#warning TODO: PolymorphismSchema + _ => new Serialization(context.GetSerializer(type, null).SerializeObject) + }; + } + } + + private static IEnumerable CreateMemberValueDecoders(ObjectSerializationContext context, IEnumerable members, bool hasDeserializationConstructor, bool hasPriviledgedAccess) + { + foreach (var member in members) + { + var type = member.Member.GetMemberValueType(); + var item = (Type: type, IsNullable: member.Member!.IsNullable()); + var memberInfo = member.Member; + yield return + item switch + { + var x when x.Type == typeof(sbyte) => DecodeSByteNonNull, + var x when x.Type == typeof(short) => DecodeInt16NonNull, + var x when x.Type == typeof(int) => DecodeInt32NonNull, + var x when x.Type == typeof(sbyte?) => DecodeSByteNullable, + var x when x.Type == typeof(short?) => DecodeInt16Nullable, + var x when x.Type == typeof(int?) => DecodeInt32Nullable, + var x when x.Type == typeof(long) => DecodeInt64NonNull, + var x when x.Type == typeof(long?) => DecodeInt64Nullable, + var x when x.Type == typeof(byte) => DecodeByteNonNull, + var x when x.Type == typeof(ushort) => DecodeUInt16NonNull, + var x when x.Type == typeof(uint) => DecodeUInt32NonNull, + var x when x.Type == typeof(byte?) => DecodeByteNullable, + var x when x.Type == typeof(ushort?) => DecodeUInt16Nullable, + var x when x.Type == typeof(uint?) => DecodeUInt32Nullable, + var x when x.Type == typeof(ulong) => DecodeUInt64NonNull, + var x when x.Type == typeof(ulong?) => DecodeUInt64Nullable, + var x when x.Type == typeof(bool) => DecodeBooleanNonNull, + var x when x.Type == typeof(bool?) => DecodeBooleanNullable, + var x when x.Type == typeof(float) => DecodeSingleNonNull, + var x when x.Type == typeof(float?) => DecodeSingleNullable, + var x when x.Type == typeof(double) => DecodeDoubleNonNull, + var x when x.Type == typeof(double?) => DecodeDoubleNullable, + var x when (x.Type == typeof(string) || x.Type == typeof(IEnumerable)) && !x.IsNullable => DecodeStringNonNull, + var x when (x.Type == typeof(string) || x.Type == typeof(IEnumerable)) && x.IsNullable => DecodeStringNullable, + var x when x.Type == typeof(StringBuilder) && !x.IsNullable => DecodeStringBuilderNonNull, + var x when x.Type == typeof(StringBuilder) && x.IsNullable => DecodeStringBuilderNullable, + var x when x.Type == typeof(char[]) && !x.IsNullable => DecodeCharArrayNonNull, + var x when x.Type == typeof(char[]) && x.IsNullable => DecodeCharArrayNullable, + var x when x.Type == typeof(ReadOnlyMemory) && !x.IsNullable => DecodeReadOnlyMemoryOfCharNonNull, + var x when x.Type == typeof(ReadOnlyMemory) && x.IsNullable => DecodeReadOnlyMemoryOfCharNullable, + var x when x.Type == typeof(Memory) && !x.IsNullable => DecodeMemoryOfCharNonNull, + var x when x.Type == typeof(Memory) && x.IsNullable => DecodeMemoryOfCharNullable, + var x when x.Type == typeof(ReadOnlySequence) && !x.IsNullable => DecodeReadOnlySequenceOfCharNonNull, + var x when x.Type == typeof(ReadOnlySequence) && x.IsNullable => DecodeReadOnlySequenceOfCharNullable, + var x when x.Type == typeof(ReadOnlySpan) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var x when x.Type == typeof(Span) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var x when (x.Type == typeof(byte[]) || x.Type == typeof(IEnumerable)) && !x.IsNullable => DecodeByteArrayNonNull, + var x when (x.Type == typeof(byte[]) || x.Type == typeof(IEnumerable)) && x.IsNullable => DecodeByteArrayNullable, + var x when x.Type == typeof(ReadOnlyMemory) && !x.IsNullable => DecodeReadOnlyMemoryOfByteNonNull, + var x when x.Type == typeof(ReadOnlyMemory) && x.IsNullable => DecodeReadOnlyMemoryOfByteNullable, + var x when x.Type == typeof(Memory) && !x.IsNullable => DecodeMemoryOfCharNonNull, + var x when x.Type == typeof(Memory) && x.IsNullable => DecodeMemoryOfCharNullable, + var x when x.Type == typeof(ReadOnlySequence) && !x.IsNullable => DecodeReadOnlySequenceOfByteNonNull, + var x when x.Type == typeof(ReadOnlySequence) && x.IsNullable => DecodeReadOnlySequenceOfByteNullable, + var x when x.Type == typeof(ReadOnlySpan) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var x when x.Type == typeof(Span) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), +#warning TODO: PolymorphismSchema + _ => + hasDeserializationConstructor ? + new Deserialization(context.GetSerializer(type, null).DeserializeObject) : + memberInfo switch + { + PropertyInfo p => + p switch + { + var pi when pi.GetSetMethod(nonPublic: hasPriviledgedAccess) != null => new Deserialization(context.GetSerializer(type, null).DeserializeObject), + _ => new DeserializingFill(context.GetSerializer(type, null).DeserializeObjectTo), + }, + FieldInfo f => + f switch + { + var fi when !fi.IsInitOnly => new Deserialization(context.GetSerializer(type, null).DeserializeObject), + _ => new DeserializingFill(context.GetSerializer(type, null).DeserializeObjectTo), + }, + _ => ReflectionSerializerThrow.UnexpectedMemberType(memberInfo) + } + }; + } + } + +#if FEATURE_TAP + + private static IEnumerable CreateAsyncMemberValueDecoders(ObjectSerializationContext context, IEnumerable members, bool hasDeserializationConstructor, bool hasPriviledgedAccess) + { + foreach (var member in members) + { + var type = member.Member.GetMemberValueType(); + var item = (Type: type, IsNullable: member.Member!.IsNullable()); + var memberInfo = member.Member; + yield return + item switch + { + var x when x.Type == typeof(sbyte) => DecodeSByteNonNullAsync, + var x when x.Type == typeof(short) => DecodeInt16NonNullAsync, + var x when x.Type == typeof(int) => DecodeInt32NonNullAsync, + var x when x.Type == typeof(sbyte?) => DecodeSByteNullableAsync, + var x when x.Type == typeof(short?) => DecodeInt16NullableAsync, + var x when x.Type == typeof(int?) => DecodeInt32NullableAsync, + var x when x.Type == typeof(long) => DecodeInt64NonNullAsync, + var x when x.Type == typeof(long?) => DecodeInt64NullableAsync, + var x when x.Type == typeof(byte) => DecodeByteNonNullAsync, + var x when x.Type == typeof(ushort) => DecodeUInt16NonNullAsync, + var x when x.Type == typeof(uint) => DecodeUInt32NonNullAsync, + var x when x.Type == typeof(byte?) => DecodeByteNullableAsync, + var x when x.Type == typeof(ushort?) => DecodeUInt16NullableAsync, + var x when x.Type == typeof(uint?) => DecodeUInt32NullableAsync, + var x when x.Type == typeof(ulong) => DecodeUInt64NonNullAsync, + var x when x.Type == typeof(ulong?) => DecodeUInt64NullableAsync, + var x when x.Type == typeof(bool) => DecodeBooleanNonNullAsync, + var x when x.Type == typeof(bool?) => DecodeBooleanNullableAsync, + var x when x.Type == typeof(float) => DecodeSingleNonNullAsync, + var x when x.Type == typeof(float?) => DecodeSingleNullableAsync, + var x when x.Type == typeof(double) => DecodeDoubleNonNullAsync, + var x when x.Type == typeof(double?) => DecodeDoubleNullableAsync, + var x when (x.Type == typeof(string) || x.Type == typeof(IEnumerable)) && !x.IsNullable => DecodeStringNonNullAsync, + var x when (x.Type == typeof(string) || x.Type == typeof(IEnumerable)) && x.IsNullable => DecodeStringNullableAsync, + var x when x.Type == typeof(StringBuilder) && !x.IsNullable => DecodeStringBuilderNonNullAsync, + var x when x.Type == typeof(StringBuilder) && x.IsNullable => DecodeStringBuilderNullableAsync, + var x when x.Type == typeof(char[]) && !x.IsNullable => DecodeCharArrayNonNullAsync, + var x when x.Type == typeof(char[]) && x.IsNullable => DecodeCharArrayNullableAsync, + var x when x.Type == typeof(ReadOnlyMemory) && !x.IsNullable => DecodeReadOnlyMemoryOfCharNonNullAsync, + var x when x.Type == typeof(ReadOnlyMemory) && x.IsNullable => DecodeReadOnlyMemoryOfCharNullableAsync, + var x when x.Type == typeof(Memory) && !x.IsNullable => DecodeMemoryOfCharNonNullAsync, + var x when x.Type == typeof(Memory) && x.IsNullable => DecodeMemoryOfCharNullableAsync, + var x when x.Type == typeof(ReadOnlySequence) && !x.IsNullable => DecodeReadOnlySequenceOfCharNonNullAsync, + var x when x.Type == typeof(ReadOnlySequence) && x.IsNullable => DecodeReadOnlySequenceOfCharNullableAsync, + var x when x.Type == typeof(ReadOnlySpan) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var x when x.Type == typeof(Span) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var x when (x.Type == typeof(byte[]) || x.Type == typeof(IEnumerable)) && !x.IsNullable => DecodeByteArrayNonNullAsync, + var x when (x.Type == typeof(byte[]) || x.Type == typeof(IEnumerable)) && x.IsNullable => DecodeByteArrayNullableAsync, + var x when x.Type == typeof(ReadOnlyMemory) && !x.IsNullable => DecodeReadOnlyMemoryOfByteNonNullAsync, + var x when x.Type == typeof(ReadOnlyMemory) && x.IsNullable => DecodeReadOnlyMemoryOfByteNullableAsync, + var x when x.Type == typeof(Memory) && !x.IsNullable => DecodeMemoryOfCharNonNullAsync, + var x when x.Type == typeof(Memory) && x.IsNullable => DecodeMemoryOfCharNullableAsync, + var x when x.Type == typeof(ReadOnlySequence) && !x.IsNullable => DecodeReadOnlySequenceOfByteNonNullAsync, + var x when x.Type == typeof(ReadOnlySequence) && x.IsNullable => DecodeReadOnlySequenceOfByteNullableAsync, + var x when x.Type == typeof(ReadOnlySpan) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), + var x when x.Type == typeof(Span) => ReflectionSerializerThrow.ByRefLikeIsNotSupported(memberInfo), +#warning TODO: PolymorphismSchema + _ => + hasDeserializationConstructor ? + new AsyncDeserialization(context.GetSerializer(type, null).DeserializeObjectAsync) : + memberInfo switch + { + PropertyInfo p => + p switch + { + var pi when pi.GetSetMethod(nonPublic: hasPriviledgedAccess) != null => new Deserialization(context.GetSerializer(type, null).DeserializeObject), + _ => new AsyncDeserializingFill(context.GetSerializer(type, null).DeserializeObjectToAsync), + }, + FieldInfo f => + f switch + { + var fi when !fi.IsInitOnly => new Deserialization(context.GetSerializer(type, null).DeserializeObject), + _ => new AsyncDeserializingFill(context.GetSerializer(type, null).DeserializeObjectToAsync), + }, + _ => ReflectionSerializerThrow.UnexpectedMemberType(memberInfo) + } + }; + } + } + +#endif // FEATURE_TAP + + private static IEnumerable DetermineWhetherIndividualMembersAreCollection(IEnumerable members) + { + foreach (var member in members) + { + yield return member.Member?.GetMemberValueType()?.GetCollectionTraits(CollectionTraitOptions.None, allowNonCollectionEnumerableTypes: false).CollectionType != CollectionKind.NotCollection; + } + } + + private static IEnumerable> CreateMemberValueGetters(IReadOnlyList members, IReadOnlyList? tupleTypes) + { + var nestAccessorList = new List>(tupleTypes?.Count ?? 0); + // immutable array for closure. + var nestAccessors = Array.Empty>(); + + for (var i = 0; i < members.Count; i++) + { + Func? itemAccessor = null; + + if (tupleTypes != null) + { + var itemIndex = Math.DivRem(i, 7, out var depth) + 1; + if (itemIndex == 1 && depth > 0) + { + var restProperty = tupleTypes[depth - 1].GetProperty("Rest")!; + nestAccessorList.Add(t => restProperty.GetValue(t)!); + nestAccessors = nestAccessorList.ToArray(); + } + + var itemProperty = tupleTypes[depth].GetProperty($"Item{itemIndex}")!; + itemAccessor = t => itemProperty.GetValue(t)!; + } + + var member = members[i]; + + if (tupleTypes != null) + { + Debug.Assert(itemAccessor != null); + + yield return + t => + { + foreach (var nestAccessor in nestAccessors) + { + t = nestAccessor(t); + } + + return itemAccessor!(t); + }; + } + else if (member.Member is PropertyInfo asProperty) + { + yield return o => asProperty.GetValue(o, BindingFlags.DoNotWrapExceptions, binder: null, index: null, culture: null); + } + else if (member.Member is FieldInfo asField) + { + yield return o => asField.GetValue(o); + } + else + { + ReflectionSerializerThrow.UnexpectedMemberType(member.Member); + } + } + } + + private static IEnumerable> CreateMemberValueSetters(IReadOnlyList members) + { + // Tuple is deserialized via constructor, so this method will not be called. + + for (var i = 0; i < members.Count; i++) + { + var member = members[i]; + + if (member.Member is PropertyInfo asProperty) + { + yield return (o, v) => asProperty.SetValue(o, v, BindingFlags.DoNotWrapExceptions, binder: null, index: null, culture: null); + } + else if (member.Member is FieldInfo asField) + { + yield return (o, v) => asField.SetValue(o, v); + } + else + { + ReflectionSerializerThrow.UnexpectedMemberType(member.Member); + } + } + } + + public void Serialize(ref SerializationOperationContext context, object? obj, IBufferWriter sink) + { + var encoder = context.Encoder; + + if (obj is null) + { + context.Encoder.EncodeNull(sink); + return; + } + + if (context.SerializationMethod == SerializationMethod.Array) + { + encoder.EncodeArrayStart(this._memberValueEncoders.Count, sink, context.CollectionContext); + + for (var i = 0; i < this._memberValueEncoders.Count; i++) + { + var value = this._memberValueGetters[i](obj); + + encoder.EncodeArrayItemStart(i, sink, context.CollectionContext); + if (this._memberValueEncoders[i] is PrimitiveEncoder primitiveEncoder) + { + primitiveEncoder(encoder, value, sink); + } + else if (this._memberValueEncoders[i] is StringEncoder stringEncoder) + { + var encoding = this._target.Members[i].Member!.GetCustomAttribute()?.Encoding ?? context.StringEncoding; + stringEncoder(encoder, value, sink, encoding, context.CancellationToken); + } + else if (this._memberValueEncoders[i] is BinaryEncoder binaryEncoder) + { + binaryEncoder(encoder, value, sink, context.CancellationToken); + } + else if (this._memberValueEncoders[i] is Serialization serializer) + { + if (this._memberIsCollections[i]) + { + context.IncrementDepth(); + } + + serializer(ref context, value, sink); + + if (this._memberIsCollections[i]) + { + context.DecrementDepth(); + } + } + else + { + ReflectionSerializerThrow.UnexpectedEncoderDelegate(this._memberValueEncoders[i]); + } + + encoder.EncodeArrayItemEnd(i, sink, context.CollectionContext); + } + + encoder.EncodeArrayEnd(this._memberValueEncoders.Count, sink, context.CollectionContext); + } + else + { + encoder.EncodeMapStart(this._memberValueEncoders.Count, sink, context.CollectionContext); + + for (var i = 0; i < this._memberValueEncoders.Count; i++) + { + var value = this._memberValueGetters[i](obj); + + encoder.EncodeMapKeyStart(i, sink, context.CollectionContext); + encoder.EncodeRawString(this._target.Members[i].Utf8MemberName!.AsSpan().Bytes, this._target.Members[i].MemberName!.Length, sink, context.CancellationToken); + encoder.EncodeMapKeyStart(i, sink, context.CollectionContext); + + encoder.EncodeMapValueStart(i, sink, context.CollectionContext); + if (this._memberValueEncoders[i] is PrimitiveEncoder primitiveEncoder) + { + primitiveEncoder(encoder, value, sink); + } + else if (this._memberValueEncoders[i] is StringEncoder stringEncoder) + { + var encoding = this._target.Members[i].Member!.GetCustomAttribute()?.Encoding ?? context.StringEncoding; + stringEncoder(encoder, value, sink, encoding, context.CancellationToken); + } + else if (this._memberValueEncoders[i] is BinaryEncoder binaryEncoder) + { + binaryEncoder(encoder, value, sink, context.CancellationToken); + } + else if (this._memberValueEncoders[i] is Serialization serializer) + { + serializer(ref context, value, sink); + } + else + { + ReflectionSerializerThrow.UnexpectedEncoderDelegate(this._memberValueEncoders[i]); + } + + encoder.EncodeMapValueEnd(i, sink, context.CollectionContext); + } + + encoder.EncodeMapEnd(this._memberValueEncoders.Count, sink, context.CollectionContext); + } + } + + public object? Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + { + if (context.Decoder.TryDecodeNull(ref source)) + { + return null; + } + + object? result = null; + var constructorParameters = this._target.DeserializationConstructor?.GetParameters() ?? Array.Empty(); + Dictionary? deserializedMembers = null; + + if (this._target.DeserializationConstructor == null) + { + result = Activator.CreateInstance(this._target.Type); + } + else + { + deserializedMembers = new Dictionary(constructorParameters.Length); + } + + var decoder = context.Decoder; + + if (decoder.Options.Features.CanCountCollectionItems) + { + var collectionType = decoder.DecodeArrayOrMapHeader(ref source, out var itemsCount); + for (var i = 0; i < itemsCount; i++) + { + this.DeserializeMember(ref context, ref source, result, deserializedMembers, i, collectionType); + } + } + else + { + var collectionType = decoder.DecodeArrayOrMap(ref source, out var iterator); + for (var i = 0; !iterator.CollectionEnds(ref source); i++) + { + this.DeserializeMember(ref context, ref source, result, deserializedMembers, i, collectionType); + i++; + } + } + + if (result == null) + { + Debug.Assert(deserializedMembers != null); + return this.CreateInstanceFromDeserializedMembers(constructorParameters, deserializedMembers); + } + else + { + return result; + } + } + + public async ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + { + await source.FetchAsync(context.CancellationToken).ConfigureAwait(false); + + if (await context.Decoder.TryDecodeNullAsync(source, context.CancellationToken).ConfigureAwait(false)) + { + return null; + } + + object? result = null; + var constructorParameters = this._target.DeserializationConstructor?.GetParameters() ?? Array.Empty(); + Dictionary? deserializedMembers = null; + + if (this._target.DeserializationConstructor == null) + { + result = Activator.CreateInstance(this._target.Type); + } + else + { + deserializedMembers = new Dictionary(constructorParameters.Length); + } + + var decoder = context.Decoder; + + if (decoder.Options.Features.CanCountCollectionItems) + { + var collectionTypeAndCount = await decoder.DecodeArrayOrMapHeaderAsync(source, context.CancellationToken).ConfigureAwait(false); + var collectionType = collectionTypeAndCount.CollectionType; + var itemsCount = collectionTypeAndCount.ItemsCount; + for (var i = 0; i < itemsCount; i++) + { + await this.DeserializeMemberAsync(context, source, result, deserializedMembers, i, collectionType).ConfigureAwait(false); + } + } + else + { + var collectionTypeAndIterator = await decoder.DecodeArrayOrMapAsync(source, context.CancellationToken).ConfigureAwait(false); + var collectionType = collectionTypeAndIterator.CollectionType; + var iterator = collectionTypeAndIterator.Iterator; + for (var i = 0; !(await iterator.CollectionEndsAsync(source, context.CancellationToken).ConfigureAwait(false)); i++) + { + await this.DeserializeMemberAsync(context, source, result, deserializedMembers, i, collectionType).ConfigureAwait(false); + i++; + } + } + + if (result == null) + { + Debug.Assert(deserializedMembers != null); + return this.CreateInstanceFromDeserializedMembers(constructorParameters, deserializedMembers); + } + else + { + return result; + } + } + + private void DeserializeMember( + ref DeserializationOperationContext context, + ref SequenceReader source, + object? result, + Dictionary? deserializedMembers, + int i, + CollectionType collectionType + ) + { + var decoder = context.Decoder; + string property; + int index; + if (collectionType.IsMap) + { + property = decoder.DecodeString(ref source, encoding: null, context.CancellationToken); + if (!this._memberIndexes.TryGetValue(property, out index)) + { + // skip value of unknown member. + decoder.Skip(ref source, context.CollectionContext, context.CancellationToken); + return; + } + } + else + { + if (i > this._target.Members.Count) + { + // skip extra member. + decoder.Skip(ref source, context.CollectionContext, context.CancellationToken); + return; + } + + index = i; + property = this._target.Members[i].MemberName ?? $"Item{i}"; + } + + object? value; + if (this._memberValueDecoders[index] is PrimitiveDecoder primitiveDecoder) + { + value = primitiveDecoder(decoder, ref source); + } + else if (this._memberValueDecoders[index] is StringDecoder stringDecoder) + { + value = stringDecoder(decoder, ref source, context.StringEncoding, context.CancellationToken); + } + else if (this._memberValueDecoders[index] is BinaryDecoder binaryDecoder) + { + value = binaryDecoder(decoder, ref source, context.CancellationToken); + } + else if (this._memberValueDecoders[index] is Deserialization deserializer) + { + if (this._memberIsCollections[index]) + { + context.IncrementDepth(); + } + + value = deserializer(ref context, ref source); + + if (this._memberIsCollections[index]) + { + context.DecrementDepth(); + } + } + else if (this._memberValueDecoders[index] is DeserializingFill filler) + { + Debug.Assert(result != null); + + if (this._memberIsCollections[index]) + { + context.IncrementDepth(); + } + + value = this._memberValueGetters[index](result); + if (value == null) + { + Throw.UnsettableCollectionMemberMustNotBeNull(this._target.Members[i].Member); + } + + filler(ref context, ref source, value!); + + + if (this._memberIsCollections[index]) + { + context.DecrementDepth(); + } + + // Assign is not required. + return; + } + else + { + ReflectionSerializerThrow.UnexpectedDecoderDelegate(this._memberValueDecoders[i]); + value = null; + } + + if (result != null) + { + Debug.Assert(this._memberValueSetters != null); + this._memberValueSetters[index](result, value); + } + else + { + Debug.Assert(deserializedMembers != null); + deserializedMembers.Add(property, value); + } + } + + + private async ValueTask DeserializeMemberAsync( + AsyncDeserializationOperationContext context, + ReadOnlyStreamSequence source, + object? result, + Dictionary? deserializedMembers, + int i, + CollectionType collectionType + ) + { + var decoder = context.Decoder; + string property; + int index; + if (collectionType.IsMap) + { + property = await decoder.DecodeStringAsync(source, context.CancellationToken).ConfigureAwait(false); + if (!this._memberIndexes.TryGetValue(property, out index)) + { + // skip value of unknown member. + await decoder.SkipAsync(source, context.CollectionContext, context.CancellationToken).ConfigureAwait(false); + return; + } + } + else + { + if (i > this._target.Members.Count) + { + // skip extra member. + await decoder.SkipAsync(source, context.CollectionContext, context.CancellationToken).ConfigureAwait(false); + return; + } + + index = i; + property = this._target.Members[i].MemberName ?? $"Item{i}"; + } + + object? value; + if (this._asyncMemberValueDecoders[index] is AsyncPrimitiveDecoder asyncPrimitiveDecoder) + { + value = await asyncPrimitiveDecoder(decoder, source, context.CancellationToken).ConfigureAwait(false); + } + else if (this._asyncMemberValueDecoders[index] is AsyncStringDecoder asyncStringDecoder) + { + value = await asyncStringDecoder(decoder, source, context.StringEncoding, context.CancellationToken).ConfigureAwait(false); + } + else if (this._asyncMemberValueDecoders[index] is AsyncBinaryDecoder asyncBinaryDecoder) + { + value = await asyncBinaryDecoder(decoder, source, context.CancellationToken).ConfigureAwait(false); + } + else if (this._asyncMemberValueDecoders[index] is AsyncDeserialization asyncDeserializer) + { + if (this._memberIsCollections[index]) + { + context.IncrementDepth(); + } + + value = await asyncDeserializer(context, source).ConfigureAwait(false); + + if (this._memberIsCollections[index]) + { + context.DecrementDepth(); + } + } + else if (this._asyncMemberValueDecoders[index] is AsyncDeserializingFill asyncFiller) + { + Debug.Assert(result != null); + + if (this._memberIsCollections[index]) + { + context.IncrementDepth(); + } + + value = this._memberValueGetters[index](result); + if (value == null) + { + Throw.UnsettableCollectionMemberMustNotBeNull(this._target.Members[i].Member); + } + + await asyncFiller(context, source, value!).ConfigureAwait(false); + + + if (this._memberIsCollections[index]) + { + context.DecrementDepth(); + } + + // Assign is not required. + return; + } + else + { + ReflectionSerializerThrow.UnexpectedDecoderDelegate(this._memberValueDecoders[i]); + value = null; + } + + if (result != null) + { + Debug.Assert(this._memberValueSetters != null); + this._memberValueSetters[index](result, value); + } + else + { + Debug.Assert(deserializedMembers != null); + deserializedMembers.Add(property, value); + } + } + + private object? CreateInstanceFromDeserializedMembers(ParameterInfo[] constructorParameters, Dictionary deserializedMembers) + { + var constructorArguments = + constructorParameters + .Select((p, i) => (Type: p.ParameterType, CorrespondentMemberName: this._target.GetCorrespondentMemberName(i))) + .Select(x => deserializedMembers.TryGetValue(x.CorrespondentMemberName ?? String.Empty, out var value) ? value : Activator.CreateInstance(x.Type)) + .ToList(); + + if (this._tupleTypes != null) + { + object? rest = null; + var tupleTypeStack = new Stack(this._tupleTypes); + while (tupleTypeStack.Any()) + { + var currentType = tupleTypeStack.Pop(); + var currentParameters = currentType.GetConstructors().Where(x => x.IsPublic && !x.IsStatic).Select(x => x.GetParameters()).SingleOrDefault(x => x.Length > 0); + var fillingArgumentsCount = currentParameters.Length == 8 ? 7 : currentParameters.Length; + var currentArguments = new object?[currentParameters.Length]; + var offset = constructorArguments.Count - fillingArgumentsCount; + for (var i = 0; i < fillingArgumentsCount; i++) + { + currentArguments[i] = constructorArguments[offset + i]; + } + constructorArguments.RemoveRange(offset, fillingArgumentsCount); + + if (currentArguments.Length == 8) + { + currentArguments[7] = rest; + } + + rest = Activator.CreateInstance(currentType, currentArguments); + } + + return rest; + } + else + { + return Activator.CreateInstance(this._target.Type, constructorArguments)!; + } + } + + public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, object obj) + { + Throw.DeserializeToOnlyAvailableForMutableCollection(this._target.Type); + // never + return default; + } + + public ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, object obj) + { + Throw.DeserializeToOnlyAvailableForMutableCollection(this._target.Type); + // never + return default; + } + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializerBuilder.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializerBuilder.cs new file mode 100644 index 000000000..ef9c744f5 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializerBuilder.cs @@ -0,0 +1,12 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +namespace MsgPack.Serialization.ReflectionSerializers +{ + internal sealed class ReflectionSerializerBuilder : SerializerBuilder + { + public sealed override ObjectSerializer Build(ObjectSerializationContext context, SerializationTarget target) + => new ReflectionSerializer(context, target); + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializerThrow.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializerThrow.cs new file mode 100644 index 000000000..efc46a790 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializerThrow.cs @@ -0,0 +1,25 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Reflection; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + internal static class ReflectionSerializerThrow + { + public static Delegate UnexpectedMemberType(MemberInfo member) + => throw new Exception($"Assertion failure: unexpected MemberInfo type '{member}'({member.GetType()})"); + + public static void UnexpectedEncoderDelegate(Delegate @delegate) + => throw new Exception($"Assertion failure: unexpected encoder delegate '{@delegate}'."); + + public static void UnexpectedDecoderDelegate(Delegate @delegate) + => throw new Exception($"Assertion failure: unexpected encoder delegate '{@delegate}'."); + + public static Delegate ByRefLikeIsNotSupported(MemberInfo member) + // This should be able to be handled in future runtime... + => throw new PlatformNotSupportedException($"Cannot deserialize {member} because its type ({member.GetMemberValueType()}) is \"by-ref\" type. Reflection based serializer cannot handle it in current runtime."); + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`1.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`1.cs new file mode 100644 index 000000000..7f3e8bff2 --- /dev/null +++ b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`1.cs @@ -0,0 +1,113 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System.Buffers; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using MsgPack.Internal; + +namespace MsgPack.Serialization.ReflectionSerializers +{ + /// + /// Implements non optimized, reflection based serializer. + /// + /// Type of serialization target. + internal sealed partial class ReflectionSerializer : ObjectSerializer + { + // T is PrimitiveEncoder | Stringncoder | BinaryEncoder | ExtensionEncoder | ArrayEncoderToken | DictionaryEncoderToken + private readonly Serialization _serialize; + private readonly Deserialization _deserialize; + private readonly DeserializingFill _deserializeTo; +#if FEATURE_TAP + private readonly AsyncDeserialization _deserializeAsync; + private readonly AsyncDeserializingFill _deserializeToAsync; +#endif // FEATURE_TAP + + public ReflectionSerializer(ObjectSerializationContext ownerContext, SerializationTarget target) + : base(ownerContext, target.GetCapabilities()) + { +#warning TODO: PolymorphismSchema + + IReflectionObjectSerializer serializer; + if (!target.IsCollection) + { + serializer = new ObjectDelegateSerializer(ownerContext, target); + } + else if (target.CollectionTraits.CollectionType == CollectionKind.Array) + { +#warning TODO: PolymorphismSchema + var itemSerializer = ownerContext.GetSerializer(target.CollectionTraits.ElementType!, null); + serializer = + target.CollectionTraits.DetailedCollectionType switch + { + CollectionDetailedKind.Array + => new ArrayDelegateSerializer( + itemSerializer.SerializeObject, + itemSerializer.DeserializeObject, +#if FEATURE_TAP + itemSerializer.DeserializeObjectAsync, +#endif // FEATURE_TAP + target.Type, + target.CollectionTraits + ), + _ => new CollectionDelegateSerializer( + itemSerializer.SerializeObject, + itemSerializer.DeserializeObject, +#if FEATURE_TAP + itemSerializer.DeserializeObjectAsync, +#endif // FEATURE_TAP + target.Type, + target.CollectionTraits + ) + }; + } + else + { + // Map + var (keyType, valueType) = target.CollectionTraits.GetKeyValueType(); +#warning TODO: PolymorphismSchema + var keySerializer = ownerContext.GetSerializer(keyType!, null); +#warning TODO: PolymorphismSchema + var valueSerializer = ownerContext.GetSerializer(valueType!, null); + serializer = + new DictionaryDelegateSerializer( + keySerializer.SerializeObject, + valueSerializer.SerializeObject, + keySerializer.DeserializeObject, + valueSerializer.DeserializeObject, +#if FEATURE_TAP + keySerializer.DeserializeObjectAsync, + valueSerializer.DeserializeObjectAsync, +#endif // FEATURE_TAP + target.Type, + target.CollectionTraits + ); + } + + this._serialize = serializer.Serialize; + this._deserialize = serializer.Deserialize; + this._deserializeTo = serializer.DeserializeTo; +#if FEATURE_TAP + this._deserializeAsync = serializer.DeserializeAsync; + this._deserializeToAsync = serializer.DeserializeToAsync; +#endif // FEATURE_TAP + } + + public sealed override void Serialize(ref SerializationOperationContext context, [AllowNull] T obj, IBufferWriter sink) + => this._serialize(ref context, obj, sink); + + [return: MaybeNull] + public sealed override T Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) + => (T)this._deserialize(ref context, ref source); + + public sealed override async ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source) + => (T)(await this._deserializeAsync(context, source).ConfigureAwait(false))!; + + public sealed override bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, T obj) + => this._deserializeTo(ref context, ref source, Ensure.NotNull(obj)); + + public sealed override async ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, ReadOnlyStreamSequence source, T obj) + => await this._deserializeToAsync(context, source, Ensure.NotNull(obj)).ConfigureAwait(false); + } +} diff --git a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`2.cs b/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`2.cs deleted file mode 100644 index aded3dd65..000000000 --- a/src/MsgPack.Serialization.Reflection/Serialization/ReflectionSerializers/ReflectionSerializer`2.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MsgPack.Internal; -using MsgPack.Serialization; -using MsgPack.Serialization.Internal; - -namespace Msgpack.Serialization.ReflectionSerializers -{ - internal abstract class SerializerBuilder - { - public abstract IObjectSerializer Build(SerializationTarget target); - } - - internal sealed class ReflectionSerializerBuilder : SerializerBuilder - { - public sealed override IObjectSerializer Build(SerializationTarget target, SerializerGenerationOptions options) - => new ReflectionSerializer(target, options); - } - - /// - /// Implements non optimized, reflection based serializer. - /// - /// Type of serialization target. - /// Type of extention type specific to codec. - internal sealed class ReflectionSerializer : IObjectSerializer - { - private readonly SerializerGenerationOptions _options; -#warning TODO: Use _options - private bool UsesArray => true; - // T is PrimitiveEncoder | Stringncoder | BinaryEncoder | ExtensionEncoder | ArrayEncoderToken | DictionaryEncoderToken - private readonly IReadOnlyList _memberValueEncoders; - private readonly IReadOnlyList _memberUtf8Names; - private readonly IReadOnlyList _memberValueDecoders; - - private delegate void PrimitiveEncoder(Encoder encoder, object value, IBufferWriter writer); - private delegate void Stringncoder(Encoder encoder, object value, IBufferWriter writer, Encoding? encoding, CancellationToken cancellationToken); - private delegate void BinaryEncoder(Encoder encoder, object ovaluebj, IBufferWriter writer, CancellationToken cancellationToken); - private delegate void ExtensionEncoder(Encoder encoder, TExtensionType extensionType, in ReadOnlySequence body, IBufferWriter writer, CancellationToken cancellationToken); - - public void Serialize(ref SerializationOperationContext context, [AllowNull] T obj, IBufferWriter sink) - { - var encoder = context.Encoder; - if (obj is null) - { - encoder.EncodeNull(sink); - return; - } - - if (this.UsesArray) - { - encoder.EncodeArrayStart(this._memberValueEncoders.Count, sink, context.CollectionContext); - - for(var i = 0; i < this._memberValueEncoders.Count;i++) - { - encoder.EncodeArrayItemStart(i, sink, context.CollectionContext); - if (this._memberValueEncoders[i] is Action> primitiveEncoder) - { - encoder.EncodeBinary() - } - else if (this._memberValueEncoders[i] is Action, Encoding?, CancellationToken> stringEncoder) - { - - } - else if (this._memberValueEncoders[i] is Action, CancellationToken> binaryEncoder) - { - - } - else if (this._memberValueEncoders[i] is Action, IBufferWriter> extensionEncoder) - { - encoder.EncodeExtension() - } - else - { - Debug.Fail(); - } - encoder.EncodeArrayItemEnd(i, sink, context.CollectionContext); - } - - encoder.EncodeArrayEnd(this._memberValueEncoders.Count, sink, context.CollectionContext); - - } - - - - throw new NotImplementedException(); - } - - public async ValueTask SerializeAsync(AsyncSerializationOperationContext context, [AllowNull] T obj, Stream streamSink) - { - await using (var writer = new StreamBufferWriter(streamSink, ownsStream: false, ArrayPool.Shared, cleansBuffer: true)) - { - var serializationOperationContext = context.AsSerializationOperationContext(); - this.Serialize(ref serializationOperationContext, obj, writer); - } - } - - [return: MaybeNull] - public T Deserialize(ref DeserializationOperationContext context, ref SequenceReader source) - { - throw new NotImplementedException(); - } - - public ValueTask DeserializeAsync(AsyncDeserializationOperationContext context, Stream streamSource) - { - throw new NotImplementedException(); - } - - public bool DeserializeTo(ref DeserializationOperationContext context, ref SequenceReader source, in T obj) - { - throw new NotImplementedException(); - } - - public ValueTask DeserializeToAsync(AsyncDeserializationOperationContext context, Stream streamSource, T obj) - { - throw new NotImplementedException(); - } - } -} From dcc171a6b26b5a27cc250fbb653d738561f179fc Mon Sep 17 00:00:00 2001 From: yfakariya Date: Fri, 26 Jun 2020 20:18:20 +0900 Subject: [PATCH 39/82] Add ILTranspiler --- .../MsgPack.Serialization.ILGeneration.csproj | 26 +- .../Reflection/ILGenerationContext.cs | 434 +++++++++ .../Reflection/ILTranslator.Parse.cs | 907 ++++++++++++++++++ .../Reflection/ILTranslator.Parse.tt | 82 ++ .../Serialization/Reflection/ILTranspiler.cs | 308 ++++++ .../Reflection/MethodSignature.cs | 43 + 6 files changed, 1799 insertions(+), 1 deletion(-) create mode 100644 src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILGenerationContext.cs create mode 100644 src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranslator.Parse.cs create mode 100644 src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranslator.Parse.tt create mode 100644 src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranspiler.cs create mode 100644 src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/MethodSignature.cs diff --git a/src/MsgPack.Serialization.ILGeneration/MsgPack.Serialization.ILGeneration.csproj b/src/MsgPack.Serialization.ILGeneration/MsgPack.Serialization.ILGeneration.csproj index a20cb1dcc..336aa7704 100644 --- a/src/MsgPack.Serialization.ILGeneration/MsgPack.Serialization.ILGeneration.csproj +++ b/src/MsgPack.Serialization.ILGeneration/MsgPack.Serialization.ILGeneration.csproj @@ -2,6 +2,7 @@ netcoreapp3.1 + true @@ -12,7 +13,30 @@ - + + True + True + ILTranslator.Parse.tt + + + + + + TextTemplatingFileGenerator + ILTranslator.Parse.cs + + + + + + + + + + True + True + ILTranslator.Parse.tt + diff --git a/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILGenerationContext.cs b/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILGenerationContext.cs new file mode 100644 index 000000000..15d6eaf6e --- /dev/null +++ b/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILGenerationContext.cs @@ -0,0 +1,434 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace MsgPack.Serialization.Reflection +{ + internal sealed class ILGenerationContext + { + private struct TypeEntry + { + public TypeBuilder TypeBuilder { get; } + public IReadOnlyDictionary Fields { get; } + public IReadOnlyDictionary Constructors { get; } + public IReadOnlyDictionary Methods { get; } + + public TypeEntry( + TypeBuilder typeBuilder, + Dictionary fields, + Dictionary constructors, + Dictionary methods + ) + { + this.TypeBuilder = typeBuilder; + this.Fields = fields; + this.Constructors = constructors; + this.Methods = methods; + } + } + + private readonly AssemblyBuilder _assemblyBuilder; + private readonly ModuleBuilder _moduleBuilder; + private readonly Dictionary _typeBuilders = new Dictionary(); + private readonly Dictionary _globalMethods = new Dictionary(); + private readonly Dictionary _globalFields = new Dictionary(); + + public ILGenerationContext(string assemblyName, AssemblyBuilderAccess access) + { + this._assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), access); + this._moduleBuilder = this._assemblyBuilder.DefineDynamicModule($"{assemblyName}.dll"); + } + +#warning TODO: ILGeneration + + public IEnumerable MakeTypes() + { + foreach (var typeBuilder in this._typeBuilders) + { + if (typeBuilder.Value.TypeBuilder.IsCreated()) + { + continue; + } + + var type = typeBuilder.Value.TypeBuilder.CreateType(); + // Type may be null if typeB + if (type != null) + { + yield return type; + } + } + } + + private static CustomAttributeBuilder ToAttributeBuilder(CustomAttributeData attributeData) + { + if (attributeData.NamedArguments.Count == 0) + { + return new CustomAttributeBuilder(attributeData.Constructor, attributeData.ConstructorArguments.Select(a => a.Value).ToArray()); + } + else + { + var fields = new List(); + var fieldValues = new List(); + var properties = new List(); + var propertyValues = new List(); + + foreach (var argument in attributeData.NamedArguments) + { + if (argument.TypedValue.Value is null) + { + continue; + } + + if (argument.IsField) + { + fields.Add((argument.MemberInfo as FieldInfo)!); + fieldValues.Add(argument.TypedValue.Value); + } + else + { + properties.Add((argument.MemberInfo as PropertyInfo)!); + propertyValues.Add(argument.TypedValue.Value); + } + } + + return + new CustomAttributeBuilder( + attributeData.Constructor, + attributeData.ConstructorArguments.Select(a => a.Value).ToArray(), + properties.ToArray(), + propertyValues.ToArray(), + fields.ToArray(), + fieldValues.ToArray() + ); + } + } + + private TypeEntry GetSharedType(Type type) + { + const BindingFlags AllFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly; + + if (this._typeBuilders.TryGetValue(type.FullName!, out var typeEntry)) + { + return typeEntry; + } + + var fields = new Dictionary(); + var constructors = new Dictionary(); + var methods = new Dictionary(); + + var typeBuilder = + this._moduleBuilder.DefineType( + type.FullName!, + type.Attributes, + type.BaseType, + type.GetInterfaces() + ); + + foreach (var attribute in type.GetCustomAttributesData()) + { + typeBuilder.SetCustomAttribute(ToAttributeBuilder(attribute)); + } + + foreach (var field in type.GetFields(AllFlags)) + { + if ((field.Attributes & FieldAttributes.HasFieldRVA) == 0) + { + // normal field + var builder = typeBuilder.DefineField(field.Name, field.FieldType, field.GetRequiredCustomModifiers(), field.GetOptionalCustomModifiers(), field.Attributes); + var constant = field.GetRawConstantValue(); + if (constant != null) + { + builder.SetConstant(constant); + } + + foreach (var attribute in field.GetCustomAttributesData()) + { + builder.SetCustomAttribute(ToAttributeBuilder(attribute)); + } + + fields[field.Name] = builder; + } + else + { + // RVA field -- for example, back-end of ReadOnlySpan S => new byte[] { 1, 2, 3 } to avoid newarr. + var value = field.GetValue(null); + if (value == null) + { + throw new NotSupportedException($"RVA field must have value type object."); + } + + // Value is non public struct which has size that is equal to .sdata section bytes. + // So, reinterpret it as byte span and get data as byte array thorugh the span. + + // First, pin boxed value object in GC heap. + var valueHandle = GCHandle.Alloc(value, GCHandleType.Pinned); + try + { + ReadOnlySpan data; + unsafe + { + // Reinterpret pinned object reference as pointer. + void* pValue = valueHandle.AddrOfPinnedObject().ToPointer(); + // Reinterpret boxed object reference as byte span for unmanaged memory. + // Note that value type's size can be gotten from Marshal.SizeOf(object). + data = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(pValue), Marshal.SizeOf(value)); + } + + var builder = typeBuilder.DefineInitializedData(field.Name, data.ToArray(), field.Attributes); + foreach (var attribute in field.GetCustomAttributesData()) + { + builder.SetCustomAttribute(ToAttributeBuilder(attribute)); + } + + fields[field.Name] = builder; + } + finally + { + valueHandle.Free(); + } + } + } + + foreach (var constructor in type.GetConstructors(AllFlags)) + { + var builder = + typeBuilder.DefineConstructor( + constructor.Attributes, + constructor.CallingConvention, + constructor.GetParameters().Select(p => p.ParameterType).ToArray() + ); + builder.SetImplementationFlags(builder.GetMethodImplementationFlags()); + + foreach (var attribute in builder.GetCustomAttributesData()) + { + builder.SetCustomAttribute(ToAttributeBuilder(attribute)); + } + + constructors[new MethodSignature(constructor)] = (constructor, builder); + } + + foreach (var method in type.GetMethods(AllFlags)) + { + var parameters = method.GetParameters(); + var builder = + typeBuilder.DefineMethod( + method.Name, + method.Attributes, + method.CallingConvention, + method.ReturnParameter.ParameterType, + method.ReturnParameter.GetRequiredCustomModifiers(), + method.ReturnParameter.GetOptionalCustomModifiers(), + parameters.Select(p => p.ParameterType).ToArray(), + parameters.Select(p => p.GetRequiredCustomModifiers()).ToArray(), + parameters.Select(p => p.GetOptionalCustomModifiers()).ToArray() + ); + builder.SetImplementationFlags(builder.GetMethodImplementationFlags()); + + foreach (var attribute in builder.GetCustomAttributesData()) + { + builder.SetCustomAttribute(ToAttributeBuilder(attribute)); + } + + methods[new MethodSignature(method)] = (method, builder); + } + + foreach (var property in type.GetProperties(AllFlags)) + { + var parameters = property.GetIndexParameters(); + var builder = + typeBuilder.DefineProperty( + property.Name, + property.Attributes, + property.PropertyType, + property.GetRequiredCustomModifiers(), + property.GetOptionalCustomModifiers(), + parameters.Select(p => p.ParameterType).ToArray(), + parameters.Select(p => p.GetRequiredCustomModifiers()).ToArray(), + parameters.Select(p => p.GetOptionalCustomModifiers()).ToArray() + ); + + var constant = property.GetRawConstantValue(); + if (constant != null) + { + builder.SetConstant(constant); + } + + foreach (var attribute in property.GetCustomAttributesData()) + { + builder.SetCustomAttribute(ToAttributeBuilder(attribute)); + } + + var getter = property.GetGetMethod(nonPublic: true); + if (getter != null) + { + builder.SetGetMethod(methods[new MethodSignature(getter)].Builder); + } + + var setter = property.GetSetMethod(nonPublic: true); + if (setter != null) + { + builder.SetGetMethod(methods[new MethodSignature(setter)].Builder); + } + } + + + foreach (var @event in type.GetEvents(AllFlags)) + { + var builder = + typeBuilder.DefineEvent( + @event.Name, + @event.Attributes, + @event.EventHandlerType! + ); + + foreach (var attribute in @event.GetCustomAttributesData()) + { + builder.SetCustomAttribute(ToAttributeBuilder(attribute)); + } + + var getter = @event.GetAddMethod(nonPublic: true); + if (getter != null) + { + builder.SetAddOnMethod(methods[new MethodSignature(getter)].Builder); + } + + var setter = @event.GetRemoveMethod(nonPublic: true); + if (setter != null) + { + builder.SetRemoveOnMethod(methods[new MethodSignature(setter)].Builder); + } + + var raise = @event.GetRaiseMethod(nonPublic: true); + if (raise != null) + { + builder.SetRaiseMethod(methods[new MethodSignature(raise)].Builder); + } + + foreach (var other in @event.GetOtherMethods(nonPublic: true)) + { + builder.AddOtherMethod(methods[new MethodSignature(other)].Builder); + } + } + + typeEntry = new TypeEntry(typeBuilder, fields, constructors, methods); + this._typeBuilders[type.FullName!] = typeEntry; + + return typeEntry; + } + + public MethodInfo ResolveMethod(MethodBase declaringMethod, int token) + { + var typeArguments = declaringMethod.DeclaringType?.GetGenericArguments(); + var methodArguments = declaringMethod.GetGenericArguments(); + var result = declaringMethod.Module.ResolveMethod(token, (typeArguments ?? Array.Empty()).Length == 0 ? null : typeArguments, methodArguments.Length == 0 ? null : methodArguments); + if (result == null) + { + // result may be null when the token indicates unresolvable DynamicMethod + throw new NotSupportedException($"Cannot resolve dynamic method token 0x{token:X8} in module '{declaringMethod.Module}'."); + } + + if (!result.IsPublic && !result.IsFamily) + { + throw new InvalidOperationException($"{result.DeclaringType}.{result} is not public."); + } + + if (!(result is MethodInfo method)) + { + throw new InvalidOperationException($"{result.DeclaringType}.{result} is not a method."); + } + + var declaringType = result.DeclaringType; + + if (declaringType == null) + { + throw new NotImplementedException("Global function is not implemented."); + } + + if (declaringType.IsPublic) + { + return method; + } + + return this.GetSharedType(declaringType).Methods[new MethodSignature(method)].Builder; + } + + public ConstructorInfo ResolveConstructor(MethodBase declaringMethod, int token) + { + var typeArguments = declaringMethod.DeclaringType?.GetGenericArguments(); + var methodArguments = declaringMethod.GetGenericArguments(); + var result = declaringMethod.Module.ResolveMethod(token, (typeArguments ?? Array.Empty()).Length == 0 ? null : typeArguments, methodArguments.Length == 0 ? null : methodArguments); + if (!(result is ConstructorInfo constructor)) + { + throw new InvalidOperationException($"Method token 0x{token:X8} in module '{declaringMethod.Module}' may be dynamic method, it is not a constructor."); + } + + if (!result.IsPublic && !result.IsFamily) + { + throw new InvalidOperationException($"{result.DeclaringType}.{result} is not public."); + } + + var declaringType = constructor.DeclaringType!; + + if (declaringType.IsPublic) + { + return constructor; + } + + return this.GetSharedType(declaringType).Constructors[new MethodSignature(constructor)].Builder; + } + + public FieldInfo ResolveField(MethodBase declaringMethod, int token) + { + var typeArguments = declaringMethod.DeclaringType?.GetGenericArguments(); + var methodArguments = declaringMethod.GetGenericArguments(); + var result = declaringMethod.Module.ResolveField(token, (typeArguments ?? Array.Empty()).Length == 0 ? null : typeArguments, methodArguments.Length == 0 ? null : methodArguments); + + // FieldInfo should not be null here. + if (!result!.IsPublic && !result.IsFamily) + { + throw new InvalidOperationException($"{result.DeclaringType}.{result} is not public."); + } + + var declaringType = result.DeclaringType; + + if (declaringType == null) + { + throw new NotImplementedException("Global field is not implemented."); + } + + if (declaringType.IsPublic) + { + return result; + } + + return this.GetSharedType(declaringType).Fields[result.Name]; + } + + public Type ResolveType(MethodBase declaringMethod, int token) + { + var typeArguments = declaringMethod.DeclaringType?.GetGenericArguments(); + var methodArguments = declaringMethod.GetGenericArguments(); + var result = declaringMethod.Module.ResolveType(token, (typeArguments ?? Array.Empty()).Length == 0 ? null : typeArguments, methodArguments.Length == 0 ? null : methodArguments); + + if (result.IsNested) + { + throw new NotImplementedException("Not implemented yet."); + } + + if (result.IsPublic) + { + return result; + } + + return this.GetSharedType(result).TypeBuilder; + } + } +} diff --git a/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranslator.Parse.cs b/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranslator.Parse.cs new file mode 100644 index 000000000..5ce88fa2f --- /dev/null +++ b/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranslator.Parse.cs @@ -0,0 +1,907 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers.Binary; +using System.Reflection.Emit; + +namespace MsgPack.Serialization.Reflection +{ + partial class ILTranspiler + { + private static OpCode Parse(Span il) + { + switch(il[0]) + { + case 0x00: + { + return OpCodes.Nop; // nop InlineNone + } + case 0x01: + { + return OpCodes.Break; // break InlineNone + } + case 0x02: + { + return OpCodes.Ldarg_0; // ldarg.0 InlineNone + } + case 0x03: + { + return OpCodes.Ldarg_1; // ldarg.1 InlineNone + } + case 0x04: + { + return OpCodes.Ldarg_2; // ldarg.2 InlineNone + } + case 0x05: + { + return OpCodes.Ldarg_3; // ldarg.3 InlineNone + } + case 0x06: + { + return OpCodes.Ldloc_0; // ldloc.0 InlineNone + } + case 0x07: + { + return OpCodes.Ldloc_1; // ldloc.1 InlineNone + } + case 0x08: + { + return OpCodes.Ldloc_2; // ldloc.2 InlineNone + } + case 0x09: + { + return OpCodes.Ldloc_3; // ldloc.3 InlineNone + } + case 0x0A: + { + return OpCodes.Stloc_0; // stloc.0 InlineNone + } + case 0x0B: + { + return OpCodes.Stloc_1; // stloc.1 InlineNone + } + case 0x0C: + { + return OpCodes.Stloc_2; // stloc.2 InlineNone + } + case 0x0D: + { + return OpCodes.Stloc_3; // stloc.3 InlineNone + } + case 0x0E: + { + return OpCodes.Ldarg_S; // ldarg.s ShortInlineVar + } + case 0x0F: + { + return OpCodes.Ldarga_S; // ldarga.s ShortInlineVar + } + case 0x10: + { + return OpCodes.Starg_S; // starg.s ShortInlineVar + } + case 0x11: + { + return OpCodes.Ldloc_S; // ldloc.s ShortInlineVar + } + case 0x12: + { + return OpCodes.Ldloca_S; // ldloca.s ShortInlineVar + } + case 0x13: + { + return OpCodes.Stloc_S; // stloc.s ShortInlineVar + } + case 0x14: + { + return OpCodes.Ldnull; // ldnull InlineNone + } + case 0x15: + { + return OpCodes.Ldc_I4_M1; // ldc.i4.m1 InlineNone + } + case 0x16: + { + return OpCodes.Ldc_I4_0; // ldc.i4.0 InlineNone + } + case 0x17: + { + return OpCodes.Ldc_I4_1; // ldc.i4.1 InlineNone + } + case 0x18: + { + return OpCodes.Ldc_I4_2; // ldc.i4.2 InlineNone + } + case 0x19: + { + return OpCodes.Ldc_I4_3; // ldc.i4.3 InlineNone + } + case 0x1A: + { + return OpCodes.Ldc_I4_4; // ldc.i4.4 InlineNone + } + case 0x1B: + { + return OpCodes.Ldc_I4_5; // ldc.i4.5 InlineNone + } + case 0x1C: + { + return OpCodes.Ldc_I4_6; // ldc.i4.6 InlineNone + } + case 0x1D: + { + return OpCodes.Ldc_I4_7; // ldc.i4.7 InlineNone + } + case 0x1E: + { + return OpCodes.Ldc_I4_8; // ldc.i4.8 InlineNone + } + case 0x1F: + { + return OpCodes.Ldc_I4_S; // ldc.i4.s ShortInlineI + } + case 0x20: + { + return OpCodes.Ldc_I4; // ldc.i4 InlineI + } + case 0x21: + { + return OpCodes.Ldc_I8; // ldc.i8 InlineI8 + } + case 0x22: + { + return OpCodes.Ldc_R4; // ldc.r4 ShortInlineR + } + case 0x23: + { + return OpCodes.Ldc_R8; // ldc.r8 InlineR + } + case 0x25: + { + return OpCodes.Dup; // dup InlineNone + } + case 0x26: + { + return OpCodes.Pop; // pop InlineNone + } + case 0x27: + { + return OpCodes.Jmp; // jmp InlineMethod + } + case 0x28: + { + return OpCodes.Call; // call InlineMethod + } + case 0x29: + { + return OpCodes.Calli; // calli InlineSig + } + case 0x2A: + { + return OpCodes.Ret; // ret InlineNone + } + case 0x2B: + { + return OpCodes.Br_S; // br.s ShortInlineBrTarget + } + case 0x2C: + { + return OpCodes.Brfalse_S; // brfalse.s ShortInlineBrTarget + } + case 0x2D: + { + return OpCodes.Brtrue_S; // brtrue.s ShortInlineBrTarget + } + case 0x2E: + { + return OpCodes.Beq_S; // beq.s ShortInlineBrTarget + } + case 0x2F: + { + return OpCodes.Bge_S; // bge.s ShortInlineBrTarget + } + case 0x30: + { + return OpCodes.Bgt_S; // bgt.s ShortInlineBrTarget + } + case 0x31: + { + return OpCodes.Ble_S; // ble.s ShortInlineBrTarget + } + case 0x32: + { + return OpCodes.Blt_S; // blt.s ShortInlineBrTarget + } + case 0x33: + { + return OpCodes.Bne_Un_S; // bne.un.s ShortInlineBrTarget + } + case 0x34: + { + return OpCodes.Bge_Un_S; // bge.un.s ShortInlineBrTarget + } + case 0x35: + { + return OpCodes.Bgt_Un_S; // bgt.un.s ShortInlineBrTarget + } + case 0x36: + { + return OpCodes.Ble_Un_S; // ble.un.s ShortInlineBrTarget + } + case 0x37: + { + return OpCodes.Blt_Un_S; // blt.un.s ShortInlineBrTarget + } + case 0x38: + { + return OpCodes.Br; // br InlineBrTarget + } + case 0x39: + { + return OpCodes.Brfalse; // brfalse InlineBrTarget + } + case 0x3A: + { + return OpCodes.Brtrue; // brtrue InlineBrTarget + } + case 0x3B: + { + return OpCodes.Beq; // beq InlineBrTarget + } + case 0x3C: + { + return OpCodes.Bge; // bge InlineBrTarget + } + case 0x3D: + { + return OpCodes.Bgt; // bgt InlineBrTarget + } + case 0x3E: + { + return OpCodes.Ble; // ble InlineBrTarget + } + case 0x3F: + { + return OpCodes.Blt; // blt InlineBrTarget + } + case 0x40: + { + return OpCodes.Bne_Un; // bne.un InlineBrTarget + } + case 0x41: + { + return OpCodes.Bge_Un; // bge.un InlineBrTarget + } + case 0x42: + { + return OpCodes.Bgt_Un; // bgt.un InlineBrTarget + } + case 0x43: + { + return OpCodes.Ble_Un; // ble.un InlineBrTarget + } + case 0x44: + { + return OpCodes.Blt_Un; // blt.un InlineBrTarget + } + case 0x45: + { + return OpCodes.Switch; // switch InlineSwitch + } + case 0x46: + { + return OpCodes.Ldind_I1; // ldind.i1 InlineNone + } + case 0x47: + { + return OpCodes.Ldind_U1; // ldind.u1 InlineNone + } + case 0x48: + { + return OpCodes.Ldind_I2; // ldind.i2 InlineNone + } + case 0x49: + { + return OpCodes.Ldind_U2; // ldind.u2 InlineNone + } + case 0x4A: + { + return OpCodes.Ldind_I4; // ldind.i4 InlineNone + } + case 0x4B: + { + return OpCodes.Ldind_U4; // ldind.u4 InlineNone + } + case 0x4C: + { + return OpCodes.Ldind_I8; // ldind.i8 InlineNone + } + case 0x4D: + { + return OpCodes.Ldind_I; // ldind.i InlineNone + } + case 0x4E: + { + return OpCodes.Ldind_R4; // ldind.r4 InlineNone + } + case 0x4F: + { + return OpCodes.Ldind_R8; // ldind.r8 InlineNone + } + case 0x50: + { + return OpCodes.Ldind_Ref; // ldind.ref InlineNone + } + case 0x51: + { + return OpCodes.Stind_Ref; // stind.ref InlineNone + } + case 0x52: + { + return OpCodes.Stind_I1; // stind.i1 InlineNone + } + case 0x53: + { + return OpCodes.Stind_I2; // stind.i2 InlineNone + } + case 0x54: + { + return OpCodes.Stind_I4; // stind.i4 InlineNone + } + case 0x55: + { + return OpCodes.Stind_I8; // stind.i8 InlineNone + } + case 0x56: + { + return OpCodes.Stind_R4; // stind.r4 InlineNone + } + case 0x57: + { + return OpCodes.Stind_R8; // stind.r8 InlineNone + } + case 0x58: + { + return OpCodes.Add; // add InlineNone + } + case 0x59: + { + return OpCodes.Sub; // sub InlineNone + } + case 0x5A: + { + return OpCodes.Mul; // mul InlineNone + } + case 0x5B: + { + return OpCodes.Div; // div InlineNone + } + case 0x5C: + { + return OpCodes.Div_Un; // div.un InlineNone + } + case 0x5D: + { + return OpCodes.Rem; // rem InlineNone + } + case 0x5E: + { + return OpCodes.Rem_Un; // rem.un InlineNone + } + case 0x5F: + { + return OpCodes.And; // and InlineNone + } + case 0x60: + { + return OpCodes.Or; // or InlineNone + } + case 0x61: + { + return OpCodes.Xor; // xor InlineNone + } + case 0x62: + { + return OpCodes.Shl; // shl InlineNone + } + case 0x63: + { + return OpCodes.Shr; // shr InlineNone + } + case 0x64: + { + return OpCodes.Shr_Un; // shr.un InlineNone + } + case 0x65: + { + return OpCodes.Neg; // neg InlineNone + } + case 0x66: + { + return OpCodes.Not; // not InlineNone + } + case 0x67: + { + return OpCodes.Conv_I1; // conv.i1 InlineNone + } + case 0x68: + { + return OpCodes.Conv_I2; // conv.i2 InlineNone + } + case 0x69: + { + return OpCodes.Conv_I4; // conv.i4 InlineNone + } + case 0x6A: + { + return OpCodes.Conv_I8; // conv.i8 InlineNone + } + case 0x6B: + { + return OpCodes.Conv_R4; // conv.r4 InlineNone + } + case 0x6C: + { + return OpCodes.Conv_R8; // conv.r8 InlineNone + } + case 0x6D: + { + return OpCodes.Conv_U4; // conv.u4 InlineNone + } + case 0x6E: + { + return OpCodes.Conv_U8; // conv.u8 InlineNone + } + case 0x6F: + { + return OpCodes.Callvirt; // callvirt InlineMethod + } + case 0x70: + { + return OpCodes.Cpobj; // cpobj InlineType + } + case 0x71: + { + return OpCodes.Ldobj; // ldobj InlineType + } + case 0x72: + { + return OpCodes.Ldstr; // ldstr InlineString + } + case 0x73: + { + return OpCodes.Newobj; // newobj InlineMethod + } + case 0x74: + { + return OpCodes.Castclass; // castclass InlineType + } + case 0x75: + { + return OpCodes.Isinst; // isinst InlineType + } + case 0x76: + { + return OpCodes.Conv_R_Un; // conv.r.un InlineNone + } + case 0x79: + { + return OpCodes.Unbox; // unbox InlineType + } + case 0x7A: + { + return OpCodes.Throw; // throw InlineNone + } + case 0x7B: + { + return OpCodes.Ldfld; // ldfld InlineField + } + case 0x7C: + { + return OpCodes.Ldflda; // ldflda InlineField + } + case 0x7D: + { + return OpCodes.Stfld; // stfld InlineField + } + case 0x7E: + { + return OpCodes.Ldsfld; // ldsfld InlineField + } + case 0x7F: + { + return OpCodes.Ldsflda; // ldsflda InlineField + } + case 0x80: + { + return OpCodes.Stsfld; // stsfld InlineField + } + case 0x81: + { + return OpCodes.Stobj; // stobj InlineType + } + case 0x82: + { + return OpCodes.Conv_Ovf_I1_Un; // conv.ovf.i1.un InlineNone + } + case 0x83: + { + return OpCodes.Conv_Ovf_I2_Un; // conv.ovf.i2.un InlineNone + } + case 0x84: + { + return OpCodes.Conv_Ovf_I4_Un; // conv.ovf.i4.un InlineNone + } + case 0x85: + { + return OpCodes.Conv_Ovf_I8_Un; // conv.ovf.i8.un InlineNone + } + case 0x86: + { + return OpCodes.Conv_Ovf_U1_Un; // conv.ovf.u1.un InlineNone + } + case 0x87: + { + return OpCodes.Conv_Ovf_U2_Un; // conv.ovf.u2.un InlineNone + } + case 0x88: + { + return OpCodes.Conv_Ovf_U4_Un; // conv.ovf.u4.un InlineNone + } + case 0x89: + { + return OpCodes.Conv_Ovf_U8_Un; // conv.ovf.u8.un InlineNone + } + case 0x8A: + { + return OpCodes.Conv_Ovf_I_Un; // conv.ovf.i.un InlineNone + } + case 0x8B: + { + return OpCodes.Conv_Ovf_U_Un; // conv.ovf.u.un InlineNone + } + case 0x8C: + { + return OpCodes.Box; // box InlineType + } + case 0x8D: + { + return OpCodes.Newarr; // newarr InlineType + } + case 0x8E: + { + return OpCodes.Ldlen; // ldlen InlineNone + } + case 0x8F: + { + return OpCodes.Ldelema; // ldelema InlineType + } + case 0x90: + { + return OpCodes.Ldelem_I1; // ldelem.i1 InlineNone + } + case 0x91: + { + return OpCodes.Ldelem_U1; // ldelem.u1 InlineNone + } + case 0x92: + { + return OpCodes.Ldelem_I2; // ldelem.i2 InlineNone + } + case 0x93: + { + return OpCodes.Ldelem_U2; // ldelem.u2 InlineNone + } + case 0x94: + { + return OpCodes.Ldelem_I4; // ldelem.i4 InlineNone + } + case 0x95: + { + return OpCodes.Ldelem_U4; // ldelem.u4 InlineNone + } + case 0x96: + { + return OpCodes.Ldelem_I8; // ldelem.i8 InlineNone + } + case 0x97: + { + return OpCodes.Ldelem_I; // ldelem.i InlineNone + } + case 0x98: + { + return OpCodes.Ldelem_R4; // ldelem.r4 InlineNone + } + case 0x99: + { + return OpCodes.Ldelem_R8; // ldelem.r8 InlineNone + } + case 0x9A: + { + return OpCodes.Ldelem_Ref; // ldelem.ref InlineNone + } + case 0x9B: + { + return OpCodes.Stelem_I; // stelem.i InlineNone + } + case 0x9C: + { + return OpCodes.Stelem_I1; // stelem.i1 InlineNone + } + case 0x9D: + { + return OpCodes.Stelem_I2; // stelem.i2 InlineNone + } + case 0x9E: + { + return OpCodes.Stelem_I4; // stelem.i4 InlineNone + } + case 0x9F: + { + return OpCodes.Stelem_I8; // stelem.i8 InlineNone + } + case 0xA0: + { + return OpCodes.Stelem_R4; // stelem.r4 InlineNone + } + case 0xA1: + { + return OpCodes.Stelem_R8; // stelem.r8 InlineNone + } + case 0xA2: + { + return OpCodes.Stelem_Ref; // stelem.ref InlineNone + } + case 0xA3: + { + return OpCodes.Ldelem; // ldelem InlineType + } + case 0xA4: + { + return OpCodes.Stelem; // stelem InlineType + } + case 0xA5: + { + return OpCodes.Unbox_Any; // unbox.any InlineType + } + case 0xB3: + { + return OpCodes.Conv_Ovf_I1; // conv.ovf.i1 InlineNone + } + case 0xB4: + { + return OpCodes.Conv_Ovf_U1; // conv.ovf.u1 InlineNone + } + case 0xB5: + { + return OpCodes.Conv_Ovf_I2; // conv.ovf.i2 InlineNone + } + case 0xB6: + { + return OpCodes.Conv_Ovf_U2; // conv.ovf.u2 InlineNone + } + case 0xB7: + { + return OpCodes.Conv_Ovf_I4; // conv.ovf.i4 InlineNone + } + case 0xB8: + { + return OpCodes.Conv_Ovf_U4; // conv.ovf.u4 InlineNone + } + case 0xB9: + { + return OpCodes.Conv_Ovf_I8; // conv.ovf.i8 InlineNone + } + case 0xBA: + { + return OpCodes.Conv_Ovf_U8; // conv.ovf.u8 InlineNone + } + case 0xC2: + { + return OpCodes.Refanyval; // refanyval InlineType + } + case 0xC3: + { + return OpCodes.Ckfinite; // ckfinite InlineNone + } + case 0xC6: + { + return OpCodes.Mkrefany; // mkrefany InlineType + } + case 0xD0: + { + return OpCodes.Ldtoken; // ldtoken InlineTok + } + case 0xD1: + { + return OpCodes.Conv_U2; // conv.u2 InlineNone + } + case 0xD2: + { + return OpCodes.Conv_U1; // conv.u1 InlineNone + } + case 0xD3: + { + return OpCodes.Conv_I; // conv.i InlineNone + } + case 0xD4: + { + return OpCodes.Conv_Ovf_I; // conv.ovf.i InlineNone + } + case 0xD5: + { + return OpCodes.Conv_Ovf_U; // conv.ovf.u InlineNone + } + case 0xD6: + { + return OpCodes.Add_Ovf; // add.ovf InlineNone + } + case 0xD7: + { + return OpCodes.Add_Ovf_Un; // add.ovf.un InlineNone + } + case 0xD8: + { + return OpCodes.Mul_Ovf; // mul.ovf InlineNone + } + case 0xD9: + { + return OpCodes.Mul_Ovf_Un; // mul.ovf.un InlineNone + } + case 0xDA: + { + return OpCodes.Sub_Ovf; // sub.ovf InlineNone + } + case 0xDB: + { + return OpCodes.Sub_Ovf_Un; // sub.ovf.un InlineNone + } + case 0xDC: + { + return OpCodes.Endfinally; // endfinally InlineNone + } + case 0xDD: + { + return OpCodes.Leave; // leave InlineBrTarget + } + case 0xDE: + { + return OpCodes.Leave_S; // leave.s ShortInlineBrTarget + } + case 0xDF: + { + return OpCodes.Stind_I; // stind.i InlineNone + } + case 0xE0: + { + return OpCodes.Conv_U; // conv.u InlineNone + } + default: + { + var opCode = BinaryPrimitives.ReadUInt16BigEndian(il); + switch (opCode) + { + case 0xFE00: + { + return OpCodes.Arglist; // arglist InlineNone + } + case 0xFE01: + { + return OpCodes.Ceq; // ceq InlineNone + } + case 0xFE02: + { + return OpCodes.Cgt; // cgt InlineNone + } + case 0xFE03: + { + return OpCodes.Cgt_Un; // cgt.un InlineNone + } + case 0xFE04: + { + return OpCodes.Clt; // clt InlineNone + } + case 0xFE05: + { + return OpCodes.Clt_Un; // clt.un InlineNone + } + case 0xFE06: + { + return OpCodes.Ldftn; // ldftn InlineMethod + } + case 0xFE07: + { + return OpCodes.Ldvirtftn; // ldvirtftn InlineMethod + } + case 0xFE09: + { + return OpCodes.Ldarg; // ldarg InlineVar + } + case 0xFE0A: + { + return OpCodes.Ldarga; // ldarga InlineVar + } + case 0xFE0B: + { + return OpCodes.Starg; // starg InlineVar + } + case 0xFE0C: + { + return OpCodes.Ldloc; // ldloc InlineVar + } + case 0xFE0D: + { + return OpCodes.Ldloca; // ldloca InlineVar + } + case 0xFE0E: + { + return OpCodes.Stloc; // stloc InlineVar + } + case 0xFE0F: + { + return OpCodes.Localloc; // localloc InlineNone + } + case 0xFE11: + { + return OpCodes.Endfilter; // endfilter InlineNone + } + case 0xFE12: + { + return OpCodes.Unaligned; // unaligned. ShortInlineI + } + case 0xFE13: + { + return OpCodes.Volatile; // volatile. InlineNone + } + case 0xFE14: + { + return OpCodes.Tailcall; // tail. InlineNone + } + case 0xFE15: + { + return OpCodes.Initobj; // initobj InlineType + } + case 0xFE16: + { + return OpCodes.Constrained; // constrained. InlineType + } + case 0xFE17: + { + return OpCodes.Cpblk; // cpblk InlineNone + } + case 0xFE18: + { + return OpCodes.Initblk; // initblk InlineNone + } + case 0xFE1A: + { + return OpCodes.Rethrow; // rethrow InlineNone + } + case 0xFE1C: + { + return OpCodes.Sizeof; // sizeof InlineType + } + case 0xFE1D: + { + return OpCodes.Refanytype; // refanytype InlineNone + } + case 0xFE1E: + { + return OpCodes.Readonly; // readonly. InlineNone + } + default: + { + throw new Exception($"Unknown opcode 0x{opCode}."); + } + } + } + } + } + } +} diff --git a/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranslator.Parse.tt b/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranslator.Parse.tt new file mode 100644 index 000000000..36c108a0e --- /dev/null +++ b/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranslator.Parse.tt @@ -0,0 +1,82 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Reflection" #> +<#@ import namespace="System.Reflection.Emit" #> +<#@ output extension=".cs" #> +<# +var opCodes = typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static).Select(f => (OpCode)f.GetValue(null)).ToArray(); +var opCode1Bytes = opCodes.Where(o => o.Value >= 0 && !o.Name.StartsWith("prefix")).ToArray(); +var opCode2Bytes = opCodes.Where(o => o.Value < 0).ToArray(); +#> +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +// +// This file is generated from acompanying .tt file. +// DO NOT edit this file directly, edit .tt file instead. + +using System; +using System.Buffers.Binary; +using System.Reflection.Emit; + +namespace MsgPack.Serialization.Reflection +{ + partial class ILTranspiler + { + private static OpCode Parse(Span il) + { + switch(il[0]) + { +<# +foreach (var opCode in opCode1Bytes) +{ +#> + case 0x<#= opCode.Value.ToString("X2") #>: + { + return OpCodes.<#= Pascalize(opCode.Name) #>; // <#= opCode.Name #> <#= opCode.OperandType #> + } +<# +} +#> + default: + { + var opCode = BinaryPrimitives.ReadUInt16BigEndian(il); + switch (opCode) + { +<# +foreach (var opCode in opCode2Bytes) +{ +#> + case 0x<#= opCode.Value.ToString("X4") #>: + { + return OpCodes.<#= Pascalize(opCode.Name) #>; // <#= opCode.Name #> <#= opCode.OperandType #> + } +<# +} +#> + default: + { + throw new Exception($"Unknown opcode 0x{opCode}."); + } + } + } + } + } + } +} +<#+ +static readonly char[] SplitDelimiters = { '.' }; +static readonly Dictionary OpCodeNameMap = + new Dictionary + { + ["tail."] = "Tailcall", + }; + +string Pascalize(string name) + => OpCodeNameMap.TryGetValue(name, out var mapped) ? + mapped : + String.Join("_", name.Split(SplitDelimiters, StringSplitOptions.RemoveEmptyEntries).Select(n => Char.ToUpperInvariant(n[0]) + n.Substring(1))); +#> diff --git a/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranspiler.cs b/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranspiler.cs new file mode 100644 index 000000000..ca208fbad --- /dev/null +++ b/src/MsgPack.Serialization.ILGeneration/Serialization/Reflection/ILTranspiler.cs @@ -0,0 +1,308 @@ +// Copyright (c) FUJIWARA, Yusuke and all contributors. +// This file is licensed under Apache2 license. +// See the LICENSE in the project root for more information. + +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; + +namespace MsgPack.Serialization.Reflection +{ + /// + /// Defines IL transpilation logics. + /// + internal static partial class ILTranspiler + { + /// + /// Ports specified method IL instructions to the specified in context of specified . + /// + /// A context for current code generation. + /// Real which contains IL source. + /// which will be emitted ported IL instructions. + public static void Port(ILGenerationContext context, MethodBase source, ILGenerator destination) + { + var methodBody = source.GetMethodBody(); + if (methodBody == null) + { + throw new ArgumentException($"The method {source} does not have method body.", nameof(source)); + } + + var tries = methodBody.ExceptionHandlingClauses.ToLookup(e => e.TryOffset); + var filters = methodBody.ExceptionHandlingClauses.ToLookup(e => e.FilterOffset); + var handlers = methodBody.ExceptionHandlingClauses.ToLookup(e => e.HandlerOffset); + var endClauses = methodBody.ExceptionHandlingClauses.ToLookup(e => e.HandlerOffset); + + var locals = methodBody.LocalVariables.OrderBy(l => l.LocalIndex).Select(l => destination.DeclareLocal(l.LocalType, l.IsPinned)).ToArray(); + var labels = new Dictionary>(); + + var ilStream = methodBody.GetILAsByteArray()!.AsSpan(); + var emits = new List<(int Offset, List> Emitters)>(); + + // Scan + ParseILStream(context, source, destination, locals, labels, ilStream, emits); + + // Real emit + EmitILStream(destination, tries, filters, handlers, endClauses, labels, emits); + } + + private static void ParseILStream(ILGenerationContext context, MethodBase source, ILGenerator destination, LocalBuilder[] locals, Dictionary> labels, Span ilStream, List<(int Offset, List> Emitters)> emits) + { + var offset = 0; + while (offset < ilStream.Length) + { + var currentOffset = offset; + var currentEmits = new List>(); + + var opCode = Parse(ilStream); + ilStream = ilStream.Slice(1); + offset++; + switch (opCode.OperandType) + { + case OperandType.InlineBrTarget: + { + var branchOffset = BinaryPrimitives.ReadInt32LittleEndian(ilStream); + var label = destination.DefineLabel(); + ilStream = ilStream.Slice(sizeof(int)); + offset += sizeof(int); + AddLabel(labels, branchOffset + offset, label); + currentEmits.Add(il => il.Emit(opCode, label)); + break; + } + case OperandType.InlineField: + { + var token = BinaryPrimitives.ReadInt32LittleEndian(ilStream); + var field = context.ResolveField(source, token); + ilStream = ilStream.Slice(sizeof(int)); + offset += sizeof(int); + currentEmits.Add(il => il.Emit(opCode, field!)); + break; + } + case OperandType.InlineI: + { + var immediate = BinaryPrimitives.ReadInt32LittleEndian(ilStream); + ilStream = ilStream.Slice(sizeof(int)); + offset += sizeof(int); + currentEmits.Add(il => il.Emit(opCode, immediate)); + break; + } + case OperandType.InlineI8: + { + var immediate = BinaryPrimitives.ReadInt64LittleEndian(ilStream); + ilStream = ilStream.Slice(sizeof(long)); + offset += sizeof(long); + currentEmits.Add(il => il.Emit(opCode, immediate)); + break; + } + case OperandType.InlineMethod: + { + var token = BinaryPrimitives.ReadInt32LittleEndian(ilStream); + ilStream = ilStream.Slice(sizeof(int)); + offset += sizeof(int); + + if (opCode == OpCodes.Call || opCode == OpCodes.Callvirt) + { + var method = context.ResolveMethod(source, token); + currentEmits.Add(il => il.EmitCall(opCode, method, null)); + } + else if (opCode == OpCodes.Newobj) + { + var constructor = context.ResolveConstructor(source, token); + currentEmits.Add(il => il.Emit(opCode, constructor)); + } + else if (opCode == OpCodes.Ldftn || opCode == OpCodes.Ldvirtftn || opCode == OpCodes.Jmp) + { + var method = context.ResolveMethod(source, token); + currentEmits.Add(il => il.Emit(opCode, method)); + } + else + { + throw new NotSupportedException($"Unknown opcode '{opCode.Name}'(0x{opCode.Value:X}) at offset {offset}."); + } + + break; + } + case OperandType.InlineNone: + { + currentEmits.Add(il => il.Emit(opCode)); + break; + } + case OperandType.InlineR: + { + var immediate = BitConverter.Int64BitsToDouble(BinaryPrimitives.ReadInt64LittleEndian(ilStream)); + ilStream = ilStream.Slice(sizeof(long)); + offset += sizeof(long); + currentEmits.Add(il => il.Emit(opCode, immediate)); + break; + } + case OperandType.InlineSig: + { + throw new NotImplementedException($"OpCode '{opCode.Name}' at offset {offset} in {source} is not implemented."); + } + case OperandType.InlineString: + { + var token = BinaryPrimitives.ReadInt32LittleEndian(ilStream); + ilStream = ilStream.Slice(sizeof(int)); + offset += sizeof(int); + var literal = source.Module.ResolveString(token); + currentEmits.Add(il => il.Emit(opCode, literal)); + break; + } + case OperandType.InlineSwitch: + { + var caseCount = BinaryPrimitives.ReadInt32LittleEndian(ilStream); + ilStream = ilStream.Slice(sizeof(int)); + offset += sizeof(int); + + var caseLabels = new List