Fixes formatting (#200)
This commit is contained in:
parent
1f651992d7
commit
86b7b3aed1
209 changed files with 51326 additions and 50397 deletions
|
|
@ -1,306 +1,309 @@
|
|||
// Copyright (c) Harry Pierson. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public ref struct BufferReader<T>
|
||||
{
|
||||
private readonly bool usingSequence;
|
||||
private readonly ReadOnlySequence<T> sequence;
|
||||
private SequencePosition currentPosition;
|
||||
private SequencePosition nextPosition;
|
||||
private bool moreData;
|
||||
private readonly long length;
|
||||
|
||||
public BufferReader(ReadOnlySpan<T> span)
|
||||
{
|
||||
usingSequence = false;
|
||||
CurrentSpanIndex = 0;
|
||||
Consumed = 0;
|
||||
sequence = default;
|
||||
currentPosition = default;
|
||||
length = span.Length;
|
||||
|
||||
CurrentSpan = span;
|
||||
nextPosition = default;
|
||||
moreData = span.Length > 0;
|
||||
}
|
||||
|
||||
public BufferReader(in ReadOnlySequence<T> sequence)
|
||||
{
|
||||
usingSequence = true;
|
||||
CurrentSpanIndex = 0;
|
||||
Consumed = 0;
|
||||
this.sequence = sequence;
|
||||
currentPosition = sequence.Start;
|
||||
length = -1;
|
||||
|
||||
var first = sequence.First.Span;
|
||||
CurrentSpan = first;
|
||||
nextPosition = sequence.GetPosition(first.Length);
|
||||
moreData = first.Length > 0;
|
||||
|
||||
if (!moreData && !sequence.IsSingleSegment)
|
||||
{
|
||||
moreData = true;
|
||||
GetNextSpan();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly bool End => !moreData;
|
||||
|
||||
public ReadOnlySpan<T> CurrentSpan { get; private set; }
|
||||
|
||||
public int CurrentSpanIndex { get; private set; }
|
||||
|
||||
public readonly ReadOnlySpan<T> UnreadSpan
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => CurrentSpan.Slice(CurrentSpanIndex);
|
||||
}
|
||||
|
||||
public long Consumed { get; private set; }
|
||||
|
||||
public readonly long Remaining => Length - Consumed;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryPeek([MaybeNullWhen(false)] out T value)
|
||||
{
|
||||
if (moreData)
|
||||
{
|
||||
value = CurrentSpan[CurrentSpanIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryRead([MaybeNullWhen(false)] out T value)
|
||||
{
|
||||
if (End)
|
||||
{
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = CurrentSpan[CurrentSpanIndex];
|
||||
CurrentSpanIndex++;
|
||||
Consumed++;
|
||||
|
||||
if (CurrentSpanIndex >= CurrentSpan.Length)
|
||||
{
|
||||
if (usingSequence)
|
||||
GetNextSpan();
|
||||
else
|
||||
moreData = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Rewind(long count)
|
||||
{
|
||||
if ((ulong)count > (ulong)Consumed) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
Consumed -= count;
|
||||
|
||||
if (CurrentSpanIndex >= count)
|
||||
{
|
||||
CurrentSpanIndex -= (int)count;
|
||||
moreData = true;
|
||||
}
|
||||
else if (usingSequence)
|
||||
{
|
||||
// Current segment doesn't have enough data, scan backward through segments
|
||||
RetreatToPreviousSpan(Consumed);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count), $"Rewind went past the start of the memory by {count}.");
|
||||
}
|
||||
}
|
||||
|
||||
[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 var memory))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private void GetNextSpan()
|
||||
{
|
||||
if (!sequence.IsSingleSegment)
|
||||
{
|
||||
var previousNextPosition = nextPosition;
|
||||
while (sequence.TryGet(ref nextPosition, out var memory))
|
||||
{
|
||||
currentPosition = previousNextPosition;
|
||||
if (memory.Length > 0)
|
||||
{
|
||||
CurrentSpan = memory.Span;
|
||||
CurrentSpanIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentSpan = default;
|
||||
CurrentSpanIndex = 0;
|
||||
previousNextPosition = nextPosition;
|
||||
}
|
||||
}
|
||||
|
||||
moreData = false;
|
||||
}
|
||||
|
||||
[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 if (usingSequence)
|
||||
{
|
||||
// Can't satisfy from the current span
|
||||
AdvanceToNextSpan(count);
|
||||
}
|
||||
else if (CurrentSpan.Length - CurrentSpanIndex == (int)count)
|
||||
{
|
||||
CurrentSpanIndex += (int)count;
|
||||
Consumed += count;
|
||||
moreData = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
}
|
||||
|
||||
private void AdvanceToNextSpan(long count)
|
||||
{
|
||||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
Consumed += count;
|
||||
while (moreData)
|
||||
{
|
||||
var 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;
|
||||
|
||||
GetNextSpan();
|
||||
|
||||
if (count == 0) break;
|
||||
}
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
// Not enough data left- adjust for where we actually ended and throw
|
||||
Consumed -= count;
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryCopyTo(Span<T> 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.)
|
||||
|
||||
var 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<T> destination)
|
||||
{
|
||||
// If we don't have enough to fill the requested buffer, return false
|
||||
if (Remaining < destination.Length)
|
||||
return false;
|
||||
|
||||
var firstSpan = UnreadSpan;
|
||||
firstSpan.CopyTo(destination);
|
||||
var copied = firstSpan.Length;
|
||||
|
||||
var next = nextPosition;
|
||||
while (sequence.TryGet(ref next, out var nextSegment))
|
||||
if (nextSegment.Length > 0)
|
||||
{
|
||||
var nextSpan = nextSegment.Span;
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) Harry Pierson. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public ref struct BufferReader<T>
|
||||
{
|
||||
private readonly bool usingSequence;
|
||||
private readonly ReadOnlySequence<T> sequence;
|
||||
private SequencePosition currentPosition;
|
||||
private SequencePosition nextPosition;
|
||||
private bool moreData;
|
||||
private readonly long length;
|
||||
|
||||
public BufferReader(ReadOnlySpan<T> span)
|
||||
{
|
||||
usingSequence = false;
|
||||
CurrentSpanIndex = 0;
|
||||
Consumed = 0;
|
||||
sequence = default;
|
||||
currentPosition = default;
|
||||
length = span.Length;
|
||||
|
||||
CurrentSpan = span;
|
||||
nextPosition = default;
|
||||
moreData = span.Length > 0;
|
||||
}
|
||||
|
||||
public BufferReader(in ReadOnlySequence<T> sequence)
|
||||
{
|
||||
usingSequence = true;
|
||||
CurrentSpanIndex = 0;
|
||||
Consumed = 0;
|
||||
this.sequence = sequence;
|
||||
currentPosition = sequence.Start;
|
||||
length = -1;
|
||||
|
||||
var first = sequence.First.Span;
|
||||
CurrentSpan = first;
|
||||
nextPosition = sequence.GetPosition(first.Length);
|
||||
moreData = first.Length > 0;
|
||||
|
||||
if (!moreData && !sequence.IsSingleSegment)
|
||||
{
|
||||
moreData = true;
|
||||
GetNextSpan();
|
||||
}
|
||||
}
|
||||
|
||||
public readonly bool End => !moreData;
|
||||
|
||||
public ReadOnlySpan<T> CurrentSpan { get; private set; }
|
||||
|
||||
public int CurrentSpanIndex { get; private set; }
|
||||
|
||||
public readonly ReadOnlySpan<T> UnreadSpan
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => CurrentSpan.Slice(CurrentSpanIndex);
|
||||
}
|
||||
|
||||
public long Consumed { get; private set; }
|
||||
|
||||
public readonly long Remaining => Length - Consumed;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryPeek([MaybeNullWhen(false)] out T value)
|
||||
{
|
||||
if (moreData)
|
||||
{
|
||||
value = CurrentSpan[CurrentSpanIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryRead([MaybeNullWhen(false)] out T value)
|
||||
{
|
||||
if (End)
|
||||
{
|
||||
value = default!;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = CurrentSpan[CurrentSpanIndex];
|
||||
CurrentSpanIndex++;
|
||||
Consumed++;
|
||||
|
||||
if (CurrentSpanIndex >= CurrentSpan.Length)
|
||||
{
|
||||
if (usingSequence)
|
||||
GetNextSpan();
|
||||
else
|
||||
moreData = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Rewind(long count)
|
||||
{
|
||||
if ((ulong)count > (ulong)Consumed) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
Consumed -= count;
|
||||
|
||||
if (CurrentSpanIndex >= count)
|
||||
{
|
||||
CurrentSpanIndex -= (int)count;
|
||||
moreData = true;
|
||||
}
|
||||
else if (usingSequence)
|
||||
{
|
||||
// Current segment doesn't have enough data, scan backward through segments
|
||||
RetreatToPreviousSpan(Consumed);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(
|
||||
nameof(count),
|
||||
$"Rewind went past the start of the memory by {count}."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
[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 var memory))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private void GetNextSpan()
|
||||
{
|
||||
if (!sequence.IsSingleSegment)
|
||||
{
|
||||
var previousNextPosition = nextPosition;
|
||||
while (sequence.TryGet(ref nextPosition, out var memory))
|
||||
{
|
||||
currentPosition = previousNextPosition;
|
||||
if (memory.Length > 0)
|
||||
{
|
||||
CurrentSpan = memory.Span;
|
||||
CurrentSpanIndex = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentSpan = default;
|
||||
CurrentSpanIndex = 0;
|
||||
previousNextPosition = nextPosition;
|
||||
}
|
||||
}
|
||||
|
||||
moreData = false;
|
||||
}
|
||||
|
||||
[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 if (usingSequence)
|
||||
{
|
||||
// Can't satisfy from the current span
|
||||
AdvanceToNextSpan(count);
|
||||
}
|
||||
else if (CurrentSpan.Length - CurrentSpanIndex == (int)count)
|
||||
{
|
||||
CurrentSpanIndex += (int)count;
|
||||
Consumed += count;
|
||||
moreData = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
}
|
||||
|
||||
private void AdvanceToNextSpan(long count)
|
||||
{
|
||||
if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
|
||||
|
||||
Consumed += count;
|
||||
while (moreData)
|
||||
{
|
||||
var 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;
|
||||
|
||||
GetNextSpan();
|
||||
|
||||
if (count == 0) break;
|
||||
}
|
||||
|
||||
if (count != 0)
|
||||
{
|
||||
// Not enough data left- adjust for where we actually ended and throw
|
||||
Consumed -= count;
|
||||
throw new ArgumentOutOfRangeException(nameof(count));
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly bool TryCopyTo(Span<T> 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.)
|
||||
|
||||
var 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<T> destination)
|
||||
{
|
||||
// If we don't have enough to fill the requested buffer, return false
|
||||
if (Remaining < destination.Length)
|
||||
return false;
|
||||
|
||||
var firstSpan = UnreadSpan;
|
||||
firstSpan.CopyTo(destination);
|
||||
var copied = firstSpan.Length;
|
||||
|
||||
var next = nextPosition;
|
||||
while (sequence.TryGet(ref next, out var nextSegment))
|
||||
if (nextSegment.Length > 0)
|
||||
{
|
||||
var nextSpan = nextSegment.Span;
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,177 +1,177 @@
|
|||
// Copyright (c) Harry Pierson. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public static class BufferReaderExtensions
|
||||
{
|
||||
private static unsafe bool TryRead<T>(ref this BufferReader<byte> reader, out T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
var span = reader.UnreadSpan;
|
||||
if (span.Length < sizeof(T)) return TryReadMultisegment(ref reader, out value);
|
||||
|
||||
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(span));
|
||||
reader.Advance(sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static unsafe bool TryReadMultisegment<T>(ref BufferReader<byte> reader, out T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
// Not enough data in the current segment, try to peek for the data we need.
|
||||
T buffer = default;
|
||||
var tempSpan = new Span<byte>(&buffer, sizeof(T));
|
||||
|
||||
if (!reader.TryCopyTo(tempSpan))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(tempSpan));
|
||||
reader.Advance(sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryRead(ref this BufferReader<byte> reader, out sbyte value)
|
||||
{
|
||||
if (TryRead(ref reader, out byte byteValue))
|
||||
{
|
||||
value = unchecked((sbyte)byteValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out short value) =>
|
||||
BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out short value) =>
|
||||
!BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
private static bool TryReadReverseEndianness(ref BufferReader<byte> reader, out short value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out ushort value)
|
||||
{
|
||||
if (TryReadLittleEndian(ref reader, out short signedvalue))
|
||||
{
|
||||
value = unchecked((ushort)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out ushort value)
|
||||
{
|
||||
if (TryReadBigEndian(ref reader, out short signedvalue))
|
||||
{
|
||||
value = unchecked((ushort)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out int value) =>
|
||||
BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out int value) =>
|
||||
!BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
private static bool TryReadReverseEndianness(ref BufferReader<byte> reader, out int value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out uint value)
|
||||
{
|
||||
if (TryReadLittleEndian(ref reader, out int signedvalue))
|
||||
{
|
||||
value = unchecked((uint)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out uint value)
|
||||
{
|
||||
if (TryReadBigEndian(ref reader, out int signedvalue))
|
||||
{
|
||||
value = unchecked((uint)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out long value) =>
|
||||
BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out long value) =>
|
||||
!BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
private static bool TryReadReverseEndianness(ref BufferReader<byte> reader, out long value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out ulong value)
|
||||
{
|
||||
if (TryReadLittleEndian(ref reader, out long signedvalue))
|
||||
{
|
||||
value = unchecked((ulong)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out ulong value)
|
||||
{
|
||||
if (TryReadBigEndian(ref reader, out long signedvalue))
|
||||
{
|
||||
value = unchecked((ulong)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) Harry Pierson. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
// See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public static class BufferReaderExtensions
|
||||
{
|
||||
private static unsafe bool TryRead<T>(ref this BufferReader<byte> reader, out T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
var span = reader.UnreadSpan;
|
||||
if (span.Length < sizeof(T)) return TryReadMultisegment(ref reader, out value);
|
||||
|
||||
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(span));
|
||||
reader.Advance(sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static unsafe bool TryReadMultisegment<T>(ref BufferReader<byte> reader, out T value)
|
||||
where T : unmanaged
|
||||
{
|
||||
// Not enough data in the current segment, try to peek for the data we need.
|
||||
T buffer = default;
|
||||
var tempSpan = new Span<byte>(&buffer, sizeof(T));
|
||||
|
||||
if (!reader.TryCopyTo(tempSpan))
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
value = Unsafe.ReadUnaligned<T>(ref MemoryMarshal.GetReference(tempSpan));
|
||||
reader.Advance(sizeof(T));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool TryRead(ref this BufferReader<byte> reader, out sbyte value)
|
||||
{
|
||||
if (TryRead(ref reader, out byte byteValue))
|
||||
{
|
||||
value = unchecked((sbyte)byteValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out short value) =>
|
||||
BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out short value) =>
|
||||
!BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
private static bool TryReadReverseEndianness(ref BufferReader<byte> reader, out short value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out ushort value)
|
||||
{
|
||||
if (TryReadLittleEndian(ref reader, out short signedvalue))
|
||||
{
|
||||
value = unchecked((ushort)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out ushort value)
|
||||
{
|
||||
if (TryReadBigEndian(ref reader, out short signedvalue))
|
||||
{
|
||||
value = unchecked((ushort)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out int value) =>
|
||||
BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out int value) =>
|
||||
!BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
private static bool TryReadReverseEndianness(ref BufferReader<byte> reader, out int value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out uint value)
|
||||
{
|
||||
if (TryReadLittleEndian(ref reader, out int signedvalue))
|
||||
{
|
||||
value = unchecked((uint)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out uint value)
|
||||
{
|
||||
if (TryReadBigEndian(ref reader, out int signedvalue))
|
||||
{
|
||||
value = unchecked((uint)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out long value) =>
|
||||
BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out long value) =>
|
||||
!BitConverter.IsLittleEndian ? reader.TryRead(out value) : TryReadReverseEndianness(ref reader, out value);
|
||||
|
||||
private static bool TryReadReverseEndianness(ref BufferReader<byte> reader, out long value)
|
||||
{
|
||||
if (reader.TryRead(out value))
|
||||
{
|
||||
value = BinaryPrimitives.ReverseEndianness(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadLittleEndian(ref this BufferReader<byte> reader, out ulong value)
|
||||
{
|
||||
if (TryReadLittleEndian(ref reader, out long signedvalue))
|
||||
{
|
||||
value = unchecked((ulong)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryReadBigEndian(ref this BufferReader<byte> reader, out ulong value)
|
||||
{
|
||||
if (TryReadBigEndian(ref reader, out long signedvalue))
|
||||
{
|
||||
value = unchecked((ulong)signedvalue);
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,143 +1,145 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// A fast access struct that wraps <see cref="IBufferWriter{T}"/>.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of element to be written.</typeparam>
|
||||
internal ref struct BufferWriter<T> where T : IBufferWriter<byte>
|
||||
{
|
||||
/// <summary>
|
||||
/// The underlying <see cref="IBufferWriter{T}"/>.
|
||||
/// </summary>
|
||||
private T _output;
|
||||
|
||||
/// <summary>
|
||||
/// The result of the last call to <see cref="IBufferWriter{T}.GetSpan(int)"/>, less any bytes already "consumed" with <see cref="Advance(int)"/>.
|
||||
/// Backing field for the <see cref="Span"/> property.
|
||||
/// </summary>
|
||||
private Span<byte> _span;
|
||||
|
||||
/// <summary>
|
||||
/// The number of uncommitted bytes (all the calls to <see cref="Advance(int)"/> since the last call to <see cref="Commit"/>).
|
||||
/// </summary>
|
||||
private int _buffered;
|
||||
|
||||
/// <summary>
|
||||
/// The total number of bytes written with this writer.
|
||||
/// Backing field for the <see cref="BytesCommitted"/> property.
|
||||
/// </summary>
|
||||
private long _bytesCommitted;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BufferWriter{T}"/> struct.
|
||||
/// </summary>
|
||||
/// <param name="output">The <see cref="IBufferWriter{T}"/> to be wrapped.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public BufferWriter(T output)
|
||||
{
|
||||
_buffered = 0;
|
||||
_bytesCommitted = 0;
|
||||
_output = output;
|
||||
_span = output.GetSpan();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the result of the last call to <see cref="IBufferWriter{T}.GetSpan(int)"/>.
|
||||
/// </summary>
|
||||
public Span<byte> Span => _span;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of bytes written with this writer.
|
||||
/// </summary>
|
||||
public long BytesCommitted => _bytesCommitted;
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="IBufferWriter{T}.Advance(int)"/> on the underlying writer
|
||||
/// with the number of uncommitted bytes.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Commit()
|
||||
{
|
||||
var buffered = _buffered;
|
||||
if (buffered > 0)
|
||||
{
|
||||
_bytesCommitted += buffered;
|
||||
_buffered = 0;
|
||||
_output.Advance(buffered);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to indicate that part of the buffer has been written to.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bytes written to.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Advance(int count)
|
||||
{
|
||||
_buffered += count;
|
||||
_span = _span.Slice(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the caller's buffer into this writer and calls <see cref="Advance(int)"/> with the length of the source buffer.
|
||||
/// </summary>
|
||||
/// <param name="source">The buffer to copy in.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(ReadOnlySpan<byte> source)
|
||||
{
|
||||
if (_span.Length >= source.Length)
|
||||
{
|
||||
source.CopyTo(_span);
|
||||
Advance(source.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMultiBuffer(source);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Acquires a new buffer if necessary to ensure that some given number of bytes can be written to a single buffer.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bytes that must be allocated in a single buffer.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Ensure(int count = 1)
|
||||
{
|
||||
if (_span.Length < count) EnsureMore(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a fresh span to write to, with an optional minimum size.
|
||||
/// </summary>
|
||||
/// <param name="count">The minimum size for the next requested buffer.</param>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void EnsureMore(int count = 0)
|
||||
{
|
||||
if (_buffered > 0) Commit();
|
||||
|
||||
_span = _output.GetSpan(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the caller's buffer into this writer, potentially across multiple buffers from the underlying writer.
|
||||
/// </summary>
|
||||
/// <param name="source">The buffer to copy into this writer.</param>
|
||||
private void WriteMultiBuffer(ReadOnlySpan<byte> source)
|
||||
{
|
||||
while (source.Length > 0)
|
||||
{
|
||||
if (_span.Length == 0) EnsureMore();
|
||||
|
||||
var writable = Math.Min(source.Length, _span.Length);
|
||||
source.Slice(0, writable).CopyTo(_span);
|
||||
source = source.Slice(writable);
|
||||
Advance(writable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// A fast access struct that wraps <see cref="IBufferWriter{T}" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of element to be written.</typeparam>
|
||||
internal ref struct BufferWriter<T> where T : IBufferWriter<byte>
|
||||
{
|
||||
/// <summary>
|
||||
/// The underlying <see cref="IBufferWriter{T}" />.
|
||||
/// </summary>
|
||||
private T _output;
|
||||
|
||||
/// <summary>
|
||||
/// The result of the last call to <see cref="IBufferWriter{T}.GetSpan(int)" />, less any bytes already "consumed" with
|
||||
/// <see cref="Advance(int)" />.
|
||||
/// Backing field for the <see cref="Span" /> property.
|
||||
/// </summary>
|
||||
private Span<byte> _span;
|
||||
|
||||
/// <summary>
|
||||
/// The number of uncommitted bytes (all the calls to <see cref="Advance(int)" /> since the last call to
|
||||
/// <see cref="Commit" />).
|
||||
/// </summary>
|
||||
private int _buffered;
|
||||
|
||||
/// <summary>
|
||||
/// The total number of bytes written with this writer.
|
||||
/// Backing field for the <see cref="BytesCommitted" /> property.
|
||||
/// </summary>
|
||||
private long _bytesCommitted;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BufferWriter{T}" /> struct.
|
||||
/// </summary>
|
||||
/// <param name="output">The <see cref="IBufferWriter{T}" /> to be wrapped.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public BufferWriter(T output)
|
||||
{
|
||||
_buffered = 0;
|
||||
_bytesCommitted = 0;
|
||||
_output = output;
|
||||
_span = output.GetSpan();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the result of the last call to <see cref="IBufferWriter{T}.GetSpan(int)" />.
|
||||
/// </summary>
|
||||
public Span<byte> Span => _span;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of bytes written with this writer.
|
||||
/// </summary>
|
||||
public long BytesCommitted => _bytesCommitted;
|
||||
|
||||
/// <summary>
|
||||
/// Calls <see cref="IBufferWriter{T}.Advance(int)" /> on the underlying writer
|
||||
/// with the number of uncommitted bytes.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Commit()
|
||||
{
|
||||
var buffered = _buffered;
|
||||
if (buffered > 0)
|
||||
{
|
||||
_bytesCommitted += buffered;
|
||||
_buffered = 0;
|
||||
_output.Advance(buffered);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to indicate that part of the buffer has been written to.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bytes written to.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Advance(int count)
|
||||
{
|
||||
_buffered += count;
|
||||
_span = _span.Slice(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the caller's buffer into this writer and calls <see cref="Advance(int)" /> with the length of the source buffer.
|
||||
/// </summary>
|
||||
/// <param name="source">The buffer to copy in.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Write(ReadOnlySpan<byte> source)
|
||||
{
|
||||
if (_span.Length >= source.Length)
|
||||
{
|
||||
source.CopyTo(_span);
|
||||
Advance(source.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteMultiBuffer(source);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Acquires a new buffer if necessary to ensure that some given number of bytes can be written to a single buffer.
|
||||
/// </summary>
|
||||
/// <param name="count">The number of bytes that must be allocated in a single buffer.</param>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Ensure(int count = 1)
|
||||
{
|
||||
if (_span.Length < count) EnsureMore(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a fresh span to write to, with an optional minimum size.
|
||||
/// </summary>
|
||||
/// <param name="count">The minimum size for the next requested buffer.</param>
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void EnsureMore(int count = 0)
|
||||
{
|
||||
if (_buffered > 0) Commit();
|
||||
|
||||
_span = _output.GetSpan(count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the caller's buffer into this writer, potentially across multiple buffers from the underlying writer.
|
||||
/// </summary>
|
||||
/// <param name="source">The buffer to copy into this writer.</param>
|
||||
private void WriteMultiBuffer(ReadOnlySpan<byte> source)
|
||||
{
|
||||
while (source.Length > 0)
|
||||
{
|
||||
if (_span.Length == 0) EnsureMore();
|
||||
|
||||
var writable = Math.Min(source.Length, _span.Length);
|
||||
source.Slice(0, writable).CopyTo(_span);
|
||||
source = source.Slice(writable);
|
||||
Advance(writable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,57 +1,58 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// Block tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The
|
||||
/// individual blocks are then treated as independent array segments.
|
||||
/// </summary>
|
||||
public sealed class MemoryPoolBlock : IMemoryOwner<byte>
|
||||
{
|
||||
private readonly int _offset;
|
||||
private readonly int _length;
|
||||
|
||||
/// <summary>
|
||||
/// This object cannot be instantiated outside of the static Create method
|
||||
/// </summary>
|
||||
internal MemoryPoolBlock(SlabMemoryPool pool, MemoryPoolSlab slab, int offset, int length)
|
||||
{
|
||||
_offset = offset;
|
||||
_length = length;
|
||||
|
||||
Pool = pool;
|
||||
Slab = slab;
|
||||
|
||||
Memory = MemoryMarshal.CreateFromPinnedArray(slab.Array, _offset, _length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool.
|
||||
/// </summary>
|
||||
public SlabMemoryPool Pool { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Back-reference to the slab from which this block was taken, or null if it is one-time-use memory.
|
||||
/// </summary>
|
||||
public MemoryPoolSlab Slab { get; }
|
||||
|
||||
public Memory<byte> Memory { get; }
|
||||
|
||||
~MemoryPoolBlock()
|
||||
{
|
||||
Pool.RefreshBlock(Slab, _offset, _length);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Pool.Return(this);
|
||||
}
|
||||
|
||||
public void Lease()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// Block tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller
|
||||
/// blocks. The
|
||||
/// individual blocks are then treated as independent array segments.
|
||||
/// </summary>
|
||||
public sealed class MemoryPoolBlock : IMemoryOwner<byte>
|
||||
{
|
||||
private readonly int _length;
|
||||
private readonly int _offset;
|
||||
|
||||
/// <summary>
|
||||
/// This object cannot be instantiated outside of the static Create method
|
||||
/// </summary>
|
||||
internal MemoryPoolBlock(SlabMemoryPool pool, MemoryPoolSlab slab, int offset, int length)
|
||||
{
|
||||
_offset = offset;
|
||||
_length = length;
|
||||
|
||||
Pool = pool;
|
||||
Slab = slab;
|
||||
|
||||
Memory = MemoryMarshal.CreateFromPinnedArray(slab.Array, _offset, _length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool.
|
||||
/// </summary>
|
||||
public SlabMemoryPool Pool { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Back-reference to the slab from which this block was taken, or null if it is one-time-use memory.
|
||||
/// </summary>
|
||||
public MemoryPoolSlab Slab { get; }
|
||||
|
||||
public Memory<byte> Memory { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Pool.Return(this);
|
||||
}
|
||||
|
||||
~MemoryPoolBlock()
|
||||
{
|
||||
Pool.RefreshBlock(Slab, _offset, _length);
|
||||
}
|
||||
|
||||
public void Lease()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public static class SlabMemoryPoolFactory
|
||||
{
|
||||
public static MemoryPool<byte> Create() => CreateSlabMemoryPool();
|
||||
public static MemoryPool<byte> CreateSlabMemoryPool() => new SlabMemoryPool();
|
||||
}
|
||||
}
|
||||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public static class SlabMemoryPoolFactory
|
||||
{
|
||||
public static MemoryPool<byte> Create() => CreateSlabMemoryPool();
|
||||
public static MemoryPool<byte> CreateSlabMemoryPool() => new SlabMemoryPool();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,75 +1,76 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// Slab tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The
|
||||
/// individual blocks are then treated as independent array segments.
|
||||
/// </summary>
|
||||
public class MemoryPoolSlab : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// This handle pins the managed array in memory until the slab is disposed. This prevents it from being
|
||||
/// relocated and enables any subsections of the array to be used as native memory pointers to P/Invoked API calls.
|
||||
/// </summary>
|
||||
private GCHandle _gcHandle;
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
public MemoryPoolSlab(byte[] data)
|
||||
{
|
||||
Array = data;
|
||||
_gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
NativePointer = _gcHandle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True as long as the blocks from this slab are to be considered returnable to the pool. In order to shrink the
|
||||
/// memory pool size an entire slab must be removed. That is done by (1) setting IsActive to false and removing the
|
||||
/// slab from the pool's _slabs collection, (2) as each block currently in use is Return()ed to the pool it will
|
||||
/// be allowed to be garbage collected rather than re-pooled, and (3) when all block tracking objects are garbage
|
||||
/// collected and the slab is no longer references the slab will be garbage collected and the memory unpinned will
|
||||
/// be unpinned by the slab's Dispose.
|
||||
/// </summary>
|
||||
public bool IsActive => !_isDisposed;
|
||||
|
||||
public IntPtr NativePointer { get; private set; }
|
||||
|
||||
public byte[] Array { get; private set; }
|
||||
|
||||
public static MemoryPoolSlab Create(int length)
|
||||
{
|
||||
// allocate and pin requested memory length
|
||||
var array = new byte[length];
|
||||
|
||||
// allocate and return slab tracking object
|
||||
return new MemoryPoolSlab(array);
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
Array = null;
|
||||
NativePointer = IntPtr.Zero;
|
||||
|
||||
if (_gcHandle.IsAllocated) _gcHandle.Free();
|
||||
}
|
||||
|
||||
~MemoryPoolSlab()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// Slab tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller
|
||||
/// blocks. The
|
||||
/// individual blocks are then treated as independent array segments.
|
||||
/// </summary>
|
||||
public class MemoryPoolSlab : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// This handle pins the managed array in memory until the slab is disposed. This prevents it from being
|
||||
/// relocated and enables any subsections of the array to be used as native memory pointers to P/Invoked API calls.
|
||||
/// </summary>
|
||||
private GCHandle _gcHandle;
|
||||
|
||||
private bool _isDisposed;
|
||||
|
||||
public MemoryPoolSlab(byte[] data)
|
||||
{
|
||||
Array = data;
|
||||
_gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
|
||||
NativePointer = _gcHandle.AddrOfPinnedObject();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True as long as the blocks from this slab are to be considered returnable to the pool. In order to shrink the
|
||||
/// memory pool size an entire slab must be removed. That is done by (1) setting IsActive to false and removing the
|
||||
/// slab from the pool's _slabs collection, (2) as each block currently in use is Return()ed to the pool it will
|
||||
/// be allowed to be garbage collected rather than re-pooled, and (3) when all block tracking objects are garbage
|
||||
/// collected and the slab is no longer references the slab will be garbage collected and the memory unpinned will
|
||||
/// be unpinned by the slab's Dispose.
|
||||
/// </summary>
|
||||
public bool IsActive => !_isDisposed;
|
||||
|
||||
public IntPtr NativePointer { get; private set; }
|
||||
|
||||
public byte[] Array { get; private set; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
public static MemoryPoolSlab Create(int length)
|
||||
{
|
||||
// allocate and pin requested memory length
|
||||
var array = new byte[length];
|
||||
|
||||
// allocate and return slab tracking object
|
||||
return new MemoryPoolSlab(array);
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
Array = null;
|
||||
NativePointer = IntPtr.Zero;
|
||||
|
||||
if (_gcHandle.IsAllocated) _gcHandle.Free();
|
||||
}
|
||||
|
||||
~MemoryPoolSlab()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,51 +1,53 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public static class MemoryPoolThrowHelper
|
||||
{
|
||||
public static void ThrowArgumentOutOfRangeException(int sourceLength, int offset)
|
||||
{
|
||||
throw GetArgumentOutOfRangeException(sourceLength, offset);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(int sourceLength, int offset) =>
|
||||
(uint)offset > (uint)sourceLength
|
||||
? new ArgumentOutOfRangeException(GetArgumentName(ExceptionArgument.offset))
|
||||
: new ArgumentOutOfRangeException(GetArgumentName(ExceptionArgument.length));
|
||||
|
||||
public static void ThrowArgumentOutOfRangeException_BufferRequestTooLarge(int maxSize)
|
||||
{
|
||||
throw GetArgumentOutOfRangeException_BufferRequestTooLarge(maxSize);
|
||||
}
|
||||
|
||||
public static void ThrowObjectDisposedException(ExceptionArgument argument)
|
||||
{
|
||||
throw GetObjectDisposedException(argument);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static ArgumentOutOfRangeException GetArgumentOutOfRangeException_BufferRequestTooLarge(int maxSize) =>
|
||||
new ArgumentOutOfRangeException(GetArgumentName(ExceptionArgument.size),
|
||||
$"Cannot allocate more than {maxSize} bytes in a single buffer");
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static ObjectDisposedException GetObjectDisposedException(ExceptionArgument argument) =>
|
||||
new ObjectDisposedException(GetArgumentName(argument));
|
||||
|
||||
private static string GetArgumentName(ExceptionArgument argument) => argument.ToString();
|
||||
|
||||
public enum ExceptionArgument
|
||||
{
|
||||
size,
|
||||
offset,
|
||||
length,
|
||||
MemoryPoolBlock,
|
||||
MemoryPool
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public static class MemoryPoolThrowHelper
|
||||
{
|
||||
public enum ExceptionArgument
|
||||
{
|
||||
size,
|
||||
offset,
|
||||
length,
|
||||
MemoryPoolBlock,
|
||||
MemoryPool
|
||||
}
|
||||
|
||||
public static void ThrowArgumentOutOfRangeException(int sourceLength, int offset)
|
||||
{
|
||||
throw GetArgumentOutOfRangeException(sourceLength, offset);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static ArgumentOutOfRangeException GetArgumentOutOfRangeException(int sourceLength, int offset) =>
|
||||
(uint)offset > (uint)sourceLength
|
||||
? new ArgumentOutOfRangeException(GetArgumentName(ExceptionArgument.offset))
|
||||
: new ArgumentOutOfRangeException(GetArgumentName(ExceptionArgument.length));
|
||||
|
||||
public static void ThrowArgumentOutOfRangeException_BufferRequestTooLarge(int maxSize)
|
||||
{
|
||||
throw GetArgumentOutOfRangeException_BufferRequestTooLarge(maxSize);
|
||||
}
|
||||
|
||||
public static void ThrowObjectDisposedException(ExceptionArgument argument)
|
||||
{
|
||||
throw GetObjectDisposedException(argument);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static ArgumentOutOfRangeException GetArgumentOutOfRangeException_BufferRequestTooLarge(int maxSize) =>
|
||||
new ArgumentOutOfRangeException(
|
||||
GetArgumentName(ExceptionArgument.size),
|
||||
$"Cannot allocate more than {maxSize} bytes in a single buffer"
|
||||
);
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static ObjectDisposedException GetObjectDisposedException(ExceptionArgument argument) =>
|
||||
new ObjectDisposedException(GetArgumentName(argument));
|
||||
|
||||
private static string GetArgumentName(ExceptionArgument argument) => argument.ToString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,179 +1,187 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to allocate and distribute re-usable blocks of memory.
|
||||
/// </summary>
|
||||
public sealed class SlabMemoryPool : MemoryPool<byte>
|
||||
{
|
||||
/// <summary>
|
||||
/// The size of a block. 4096 is chosen because most operating systems use 4k pages.
|
||||
/// </summary>
|
||||
private const int _blockSize = 4096;
|
||||
|
||||
/// <summary>
|
||||
/// Allocating 32 contiguous blocks per slab makes the slab size 128k. This is larger than the 85k size which will place the memory
|
||||
/// in the large object heap. This means the GC will not try to relocate this array, so the fact it remains pinned does not negatively
|
||||
/// affect memory management's compactification.
|
||||
/// </summary>
|
||||
private const int _blockCount = 32;
|
||||
|
||||
/// <summary>
|
||||
/// Max allocation block size for pooled blocks,
|
||||
/// larger values can be leased but they will be disposed after use rather than returned to the pool.
|
||||
/// </summary>
|
||||
public override int MaxBufferSize { get; } = _blockSize;
|
||||
|
||||
/// <summary>
|
||||
/// The size of a block. 4096 is chosen because most operating systems use 4k pages.
|
||||
/// </summary>
|
||||
public static int BlockSize => _blockSize;
|
||||
|
||||
/// <summary>
|
||||
/// 4096 * 32 gives you a slabLength of 128k contiguous bytes allocated per slab
|
||||
/// </summary>
|
||||
private static readonly int _slabLength = _blockSize * _blockCount;
|
||||
|
||||
/// <summary>
|
||||
/// Thread-safe collection of blocks which are currently in the pool. A slab will pre-allocate all of the block tracking objects
|
||||
/// and add them to this collection. When memory is requested it is taken from here first, and when it is returned it is re-added.
|
||||
/// </summary>
|
||||
private readonly ConcurrentQueue<MemoryPoolBlock> _blocks = new ConcurrentQueue<MemoryPoolBlock>();
|
||||
|
||||
/// <summary>
|
||||
/// Thread-safe collection of slabs which have been allocated by this pool. As long as a slab is in this collection and slab.IsActive,
|
||||
/// the blocks will be added to _blocks when returned.
|
||||
/// </summary>
|
||||
private readonly ConcurrentStack<MemoryPoolSlab> _slabs = new ConcurrentStack<MemoryPoolSlab>();
|
||||
|
||||
/// <summary>
|
||||
/// This is part of implementing the IDisposable pattern.
|
||||
/// </summary>
|
||||
private bool _isDisposed; // To detect redundant calls
|
||||
|
||||
private int _totalAllocatedBlocks;
|
||||
|
||||
private readonly object _disposeSync = new object();
|
||||
|
||||
/// <summary>
|
||||
/// This default value passed in to Rent to use the default value for the pool.
|
||||
/// </summary>
|
||||
private const int AnySize = -1;
|
||||
|
||||
public override IMemoryOwner<byte> Rent(int size = AnySize)
|
||||
{
|
||||
if (size > _blockSize) MemoryPoolThrowHelper.ThrowArgumentOutOfRangeException_BufferRequestTooLarge(_blockSize);
|
||||
|
||||
var block = Lease();
|
||||
return block;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to take a block from the pool.
|
||||
/// </summary>
|
||||
/// <returns>The block that is reserved for the called. It must be passed to Return when it is no longer being used.</returns>
|
||||
private MemoryPoolBlock Lease()
|
||||
{
|
||||
if (_isDisposed)
|
||||
MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPool);
|
||||
|
||||
if (_blocks.TryDequeue(out var block))
|
||||
{
|
||||
// block successfully taken from the stack - return it
|
||||
|
||||
block.Lease();
|
||||
return block;
|
||||
}
|
||||
|
||||
// no blocks available - grow the pool
|
||||
block = AllocateSlab();
|
||||
block.Lease();
|
||||
return block;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of the
|
||||
/// block tracking objects, and adds them all to the pool.
|
||||
/// </summary>
|
||||
private MemoryPoolBlock AllocateSlab()
|
||||
{
|
||||
#pragma warning disable CA2000 // Dispose objects before losing scope
|
||||
var slab = MemoryPoolSlab.Create(_slabLength);
|
||||
#pragma warning restore CA2000 // Dispose objects before losing scope
|
||||
_slabs.Push(slab);
|
||||
|
||||
var basePtr = slab.NativePointer;
|
||||
// Page align the blocks
|
||||
var offset = (int)((((ulong)basePtr + _blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr);
|
||||
|
||||
var blockCount = (_slabLength - offset) / _blockSize;
|
||||
Interlocked.Add(ref _totalAllocatedBlocks, blockCount);
|
||||
|
||||
MemoryPoolBlock block = null;
|
||||
|
||||
for (var i = 0; i < blockCount; i++)
|
||||
{
|
||||
block = new MemoryPoolBlock(this, slab, offset, _blockSize);
|
||||
|
||||
if (i != blockCount - 1) // last block
|
||||
Return(block);
|
||||
|
||||
offset += _blockSize;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to return a block to the pool. Once Return has been called the memory no longer belongs to the caller, and
|
||||
/// Very Bad Things will happen if the memory is read of modified subsequently. If a caller fails to call Return and the
|
||||
/// block tracking object is garbage collected, the block tracking object's finalizer will automatically re-create and return
|
||||
/// a new tracking object into the pool. This will only happen if there is a bug in the server, however it is necessary to avoid
|
||||
/// leaving "dead zones" in the slab due to lost block tracking objects.
|
||||
/// </summary>
|
||||
/// <param name="block">The block to return. It must have been acquired by calling Lease on the same memory pool instance.</param>
|
||||
internal void Return(MemoryPoolBlock block)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
_blocks.Enqueue(block);
|
||||
else
|
||||
GC.SuppressFinalize(block);
|
||||
}
|
||||
|
||||
// This method can ONLY be called from the finalizer of MemoryPoolBlock
|
||||
internal void RefreshBlock(MemoryPoolSlab slab, int offset, int length)
|
||||
{
|
||||
lock (_disposeSync)
|
||||
{
|
||||
if (!_isDisposed && slab?.IsActive == true)
|
||||
// Need to make a new object because this one is being finalized
|
||||
// Note, this must be called within the _disposeSync lock because the block
|
||||
// could be disposed at the same time as the finalizer.
|
||||
Return(new MemoryPoolBlock(this, slab, offset, length));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
lock (_disposeSync)
|
||||
{
|
||||
_isDisposed = true;
|
||||
|
||||
if (disposing)
|
||||
while (_slabs.TryPop(out var slab))
|
||||
// dispose managed state (managed objects).
|
||||
slab.Dispose();
|
||||
|
||||
// Discard blocks in pool
|
||||
while (_blocks.TryDequeue(out var block)) GC.SuppressFinalize(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to allocate and distribute re-usable blocks of memory.
|
||||
/// </summary>
|
||||
public sealed class SlabMemoryPool : MemoryPool<byte>
|
||||
{
|
||||
/// <summary>
|
||||
/// The size of a block. 4096 is chosen because most operating systems use 4k pages.
|
||||
/// </summary>
|
||||
private const int _blockSize = 4096;
|
||||
|
||||
/// <summary>
|
||||
/// Allocating 32 contiguous blocks per slab makes the slab size 128k. This is larger than the 85k size which will place the
|
||||
/// memory
|
||||
/// in the large object heap. This means the GC will not try to relocate this array, so the fact it remains pinned does not
|
||||
/// negatively
|
||||
/// affect memory management's compactification.
|
||||
/// </summary>
|
||||
private const int _blockCount = 32;
|
||||
|
||||
/// <summary>
|
||||
/// This default value passed in to Rent to use the default value for the pool.
|
||||
/// </summary>
|
||||
private const int AnySize = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 4096 * 32 gives you a slabLength of 128k contiguous bytes allocated per slab
|
||||
/// </summary>
|
||||
private static readonly int _slabLength = _blockSize * _blockCount;
|
||||
|
||||
/// <summary>
|
||||
/// Thread-safe collection of blocks which are currently in the pool. A slab will pre-allocate all of the block tracking
|
||||
/// objects
|
||||
/// and add them to this collection. When memory is requested it is taken from here first, and when it is returned it is
|
||||
/// re-added.
|
||||
/// </summary>
|
||||
private readonly ConcurrentQueue<MemoryPoolBlock> _blocks = new ConcurrentQueue<MemoryPoolBlock>();
|
||||
|
||||
private readonly object _disposeSync = new object();
|
||||
|
||||
/// <summary>
|
||||
/// Thread-safe collection of slabs which have been allocated by this pool. As long as a slab is in this collection and
|
||||
/// slab.IsActive,
|
||||
/// the blocks will be added to _blocks when returned.
|
||||
/// </summary>
|
||||
private readonly ConcurrentStack<MemoryPoolSlab> _slabs = new ConcurrentStack<MemoryPoolSlab>();
|
||||
|
||||
/// <summary>
|
||||
/// This is part of implementing the IDisposable pattern.
|
||||
/// </summary>
|
||||
private bool _isDisposed; // To detect redundant calls
|
||||
|
||||
private int _totalAllocatedBlocks;
|
||||
|
||||
/// <summary>
|
||||
/// Max allocation block size for pooled blocks,
|
||||
/// larger values can be leased but they will be disposed after use rather than returned to the pool.
|
||||
/// </summary>
|
||||
public override int MaxBufferSize { get; } = _blockSize;
|
||||
|
||||
/// <summary>
|
||||
/// The size of a block. 4096 is chosen because most operating systems use 4k pages.
|
||||
/// </summary>
|
||||
public static int BlockSize => _blockSize;
|
||||
|
||||
public override IMemoryOwner<byte> Rent(int size = AnySize)
|
||||
{
|
||||
if (size > _blockSize) MemoryPoolThrowHelper.ThrowArgumentOutOfRangeException_BufferRequestTooLarge(_blockSize);
|
||||
|
||||
var block = Lease();
|
||||
return block;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to take a block from the pool.
|
||||
/// </summary>
|
||||
/// <returns>The block that is reserved for the called. It must be passed to Return when it is no longer being used.</returns>
|
||||
private MemoryPoolBlock Lease()
|
||||
{
|
||||
if (_isDisposed)
|
||||
MemoryPoolThrowHelper.ThrowObjectDisposedException(MemoryPoolThrowHelper.ExceptionArgument.MemoryPool);
|
||||
|
||||
if (_blocks.TryDequeue(out var block))
|
||||
{
|
||||
// block successfully taken from the stack - return it
|
||||
|
||||
block.Lease();
|
||||
return block;
|
||||
}
|
||||
|
||||
// no blocks available - grow the pool
|
||||
block = AllocateSlab();
|
||||
block.Lease();
|
||||
return block;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method called when a block is requested and the pool is empty. It allocates one additional slab, creates all of
|
||||
/// the
|
||||
/// block tracking objects, and adds them all to the pool.
|
||||
/// </summary>
|
||||
private MemoryPoolBlock AllocateSlab()
|
||||
{
|
||||
#pragma warning disable CA2000 // Dispose objects before losing scope
|
||||
var slab = MemoryPoolSlab.Create(_slabLength);
|
||||
#pragma warning restore CA2000 // Dispose objects before losing scope
|
||||
_slabs.Push(slab);
|
||||
|
||||
var basePtr = slab.NativePointer;
|
||||
// Page align the blocks
|
||||
var offset = (int)((((ulong)basePtr + _blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr);
|
||||
|
||||
var blockCount = (_slabLength - offset) / _blockSize;
|
||||
Interlocked.Add(ref _totalAllocatedBlocks, blockCount);
|
||||
|
||||
MemoryPoolBlock block = null;
|
||||
|
||||
for (var i = 0; i < blockCount; i++)
|
||||
{
|
||||
block = new MemoryPoolBlock(this, slab, offset, _blockSize);
|
||||
|
||||
if (i != blockCount - 1) // last block
|
||||
Return(block);
|
||||
|
||||
offset += _blockSize;
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to return a block to the pool. Once Return has been called the memory no longer belongs to the caller, and
|
||||
/// Very Bad Things will happen if the memory is read of modified subsequently. If a caller fails to call Return and the
|
||||
/// block tracking object is garbage collected, the block tracking object's finalizer will automatically re-create and
|
||||
/// return
|
||||
/// a new tracking object into the pool. This will only happen if there is a bug in the server, however it is necessary to
|
||||
/// avoid
|
||||
/// leaving "dead zones" in the slab due to lost block tracking objects.
|
||||
/// </summary>
|
||||
/// <param name="block">The block to return. It must have been acquired by calling Lease on the same memory pool instance.</param>
|
||||
internal void Return(MemoryPoolBlock block)
|
||||
{
|
||||
if (!_isDisposed)
|
||||
_blocks.Enqueue(block);
|
||||
else
|
||||
GC.SuppressFinalize(block);
|
||||
}
|
||||
|
||||
// This method can ONLY be called from the finalizer of MemoryPoolBlock
|
||||
internal void RefreshBlock(MemoryPoolSlab slab, int offset, int length)
|
||||
{
|
||||
lock (_disposeSync)
|
||||
{
|
||||
if (!_isDisposed && slab?.IsActive == true)
|
||||
// Need to make a new object because this one is being finalized
|
||||
// Note, this must be called within the _disposeSync lock because the block
|
||||
// could be disposed at the same time as the finalizer.
|
||||
Return(new MemoryPoolBlock(this, slab, offset, length));
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_isDisposed) return;
|
||||
|
||||
lock (_disposeSync)
|
||||
{
|
||||
_isDisposed = true;
|
||||
|
||||
if (disposing)
|
||||
while (_slabs.TryPop(out var slab))
|
||||
// dispose managed state (managed objects).
|
||||
slab.Dispose();
|
||||
|
||||
// Discard blocks in pool
|
||||
while (_blocks.TryDequeue(out var block)) GC.SuppressFinalize(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,214 +1,214 @@
|
|||
using System.Buffers.Binary;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Server;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public static class SpanExtensions
|
||||
{
|
||||
// Extensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ushort value) => BinaryPrimitives.WriteUInt16BigEndian(span, value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, short value) => BinaryPrimitives.WriteInt16BigEndian(span, value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, uint value) => BinaryPrimitives.WriteUInt32BigEndian(span, value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, int value) => BinaryPrimitives.WriteInt32BigEndian(span, value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, byte value) => span[0] = value;
|
||||
|
||||
// Ref Extensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, ReadOnlySpan<byte> data)
|
||||
{
|
||||
data.CopyTo(span.Slice(pos, data.Length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, bool value)
|
||||
{
|
||||
span[pos++] = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, byte value) => span[pos++] = value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, ushort value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos, 2), value);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, short value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt16BigEndian(span.Slice(pos, 2), value);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, uint value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32BigEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLE(this Span<byte> span, ref int pos, uint value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, int value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt32BigEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLE(this Span<byte> span, ref int pos, int value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, Serial value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32BigEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, ulong value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt64BigEndian(span.Slice(pos, 8), value);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, long value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt64BigEndian(span.Slice(pos, 8), value);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLE(this Span<byte> span, ref int pos, ulong value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(span.Slice(pos, 8), value);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLE(this Span<byte> span, ref int pos, long value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt64LittleEndian(span.Slice(pos, 8), value);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAscii(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
int length = value.Length;
|
||||
pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAscii(this Span<byte> span, ref int pos, string value, int max)
|
||||
{
|
||||
int length = value.Length <= max ? value.Length : max;
|
||||
|
||||
pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAsciiNull(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
int length = value.Length;
|
||||
pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
#if NO_LOCAL_INIT
|
||||
span[pos] = 0; // Null terminator
|
||||
#endif
|
||||
pos++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAsciiNull(this Span<byte> span, ref int pos, string value, int max)
|
||||
{
|
||||
var length = value.Length < max ? value.Length : max - 1;
|
||||
|
||||
pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
#if NO_LOCAL_INIT
|
||||
span[pos] = 0; // Null terminator
|
||||
#endif
|
||||
pos++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAsciiFixed(this Span<byte> span, ref int pos, string value, int amount)
|
||||
{
|
||||
var length = value.Length <= amount ? value.Length : amount;
|
||||
#if NO_LOCAL_INIT
|
||||
int bytesWritten = Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
|
||||
if (bytesWritten < amount)
|
||||
span.Slice(pos + bytesWritten, amount - bytesWritten).Clear();
|
||||
#else
|
||||
Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
#endif
|
||||
|
||||
pos += amount;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBigUni(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
pos += Encoding.BigEndianUnicode.GetBytes(value, span.Slice(pos));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLittleUni(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
pos += Encoding.Unicode.GetBytes(value, span.Slice(pos));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBigUniNull(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
pos += Encoding.BigEndianUnicode.GetBytes(value, span.Slice(pos));
|
||||
#if NO_LOCAL_INIT
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos, 2), 0); // Null terminator
|
||||
#endif
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLittleUniNull(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
pos += Encoding.Unicode.GetBytes(value, span.Slice(pos));
|
||||
#if NO_LOCAL_INIT
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos, 2), 0); // Null terminator
|
||||
#endif
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, Point3D p)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos, 2), (ushort)p.X);
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos + 2, 2), (ushort)p.Y);
|
||||
span[pos + 4] = (byte)p.Z;
|
||||
pos += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using Server;
|
||||
|
||||
namespace System.Buffers
|
||||
{
|
||||
public static class SpanExtensions
|
||||
{
|
||||
// Extensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ushort value) => BinaryPrimitives.WriteUInt16BigEndian(span, value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, short value) => BinaryPrimitives.WriteInt16BigEndian(span, value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, uint value) => BinaryPrimitives.WriteUInt32BigEndian(span, value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, int value) => BinaryPrimitives.WriteInt32BigEndian(span, value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, byte value) => span[0] = value;
|
||||
|
||||
// Ref Extensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, ReadOnlySpan<byte> data)
|
||||
{
|
||||
data.CopyTo(span.Slice(pos, data.Length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, bool value)
|
||||
{
|
||||
span[pos++] = value ? (byte)1 : (byte)0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, byte value) => span[pos++] = value;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, ushort value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos, 2), value);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, short value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt16BigEndian(span.Slice(pos, 2), value);
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, uint value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32BigEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLE(this Span<byte> span, ref int pos, uint value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, int value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt32BigEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLE(this Span<byte> span, ref int pos, int value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt32LittleEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, Serial value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt32BigEndian(span.Slice(pos, 4), value);
|
||||
pos += 4;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, ulong value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt64BigEndian(span.Slice(pos, 8), value);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, long value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt64BigEndian(span.Slice(pos, 8), value);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLE(this Span<byte> span, ref int pos, ulong value)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(span.Slice(pos, 8), value);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLE(this Span<byte> span, ref int pos, long value)
|
||||
{
|
||||
BinaryPrimitives.WriteInt64LittleEndian(span.Slice(pos, 8), value);
|
||||
pos += 8;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAscii(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
var length = value.Length;
|
||||
pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAscii(this Span<byte> span, ref int pos, string value, int max)
|
||||
{
|
||||
var length = value.Length <= max ? value.Length : max;
|
||||
|
||||
pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAsciiNull(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
var length = value.Length;
|
||||
pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
#if NO_LOCAL_INIT
|
||||
span[pos] = 0; // Null terminator
|
||||
#endif
|
||||
pos++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAsciiNull(this Span<byte> span, ref int pos, string value, int max)
|
||||
{
|
||||
var length = value.Length < max ? value.Length : max - 1;
|
||||
|
||||
pos += Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
#if NO_LOCAL_INIT
|
||||
span[pos] = 0; // Null terminator
|
||||
#endif
|
||||
pos++;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteAsciiFixed(this Span<byte> span, ref int pos, string value, int amount)
|
||||
{
|
||||
var length = value.Length <= amount ? value.Length : amount;
|
||||
#if NO_LOCAL_INIT
|
||||
int bytesWritten = Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
|
||||
if (bytesWritten < amount)
|
||||
span.Slice(pos + bytesWritten, amount - bytesWritten).Clear();
|
||||
#else
|
||||
Encoding.ASCII.GetBytes(value.AsSpan(0, length), span.Slice(pos, length));
|
||||
#endif
|
||||
|
||||
pos += amount;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBigUni(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
pos += Encoding.BigEndianUnicode.GetBytes(value, span.Slice(pos));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLittleUni(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
pos += Encoding.Unicode.GetBytes(value, span.Slice(pos));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteBigUniNull(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
pos += Encoding.BigEndianUnicode.GetBytes(value, span.Slice(pos));
|
||||
#if NO_LOCAL_INIT
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos, 2), 0); // Null terminator
|
||||
#endif
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteLittleUniNull(this Span<byte> span, ref int pos, string value)
|
||||
{
|
||||
pos += Encoding.Unicode.GetBytes(value, span.Slice(pos));
|
||||
#if NO_LOCAL_INIT
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos, 2), 0); // Null terminator
|
||||
#endif
|
||||
pos += 2;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Write(this Span<byte> span, ref int pos, Point3D p)
|
||||
{
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos, 2), (ushort)p.X);
|
||||
BinaryPrimitives.WriteUInt16BigEndian(span.Slice(pos + 2, 2), (ushort)p.Y);
|
||||
span[pos + 4] = (byte)p.Z;
|
||||
pos += 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue