From 091084cfe241d4b17372486624716e036f471f54 Mon Sep 17 00:00:00 2001 From: Louis DeJardin Date: Fri, 18 Sep 2015 22:59:19 -0700 Subject: [PATCH] Refactoring MemoryPool class locations Moving Iterator out into its own file rather than being a nested class Moving pool classes into Infrastructure namespace instead of Http --- .../Http/MemoryPoolBlock2.cs | 630 ------------------ .../Http/SocketInput.cs | 11 +- .../{Http => Infrastructure}/MemoryPool2.cs | 2 +- .../Infrastructure/MemoryPoolBlock2.cs | 184 +++++ .../Infrastructure/MemoryPoolIterator2.cs | 452 +++++++++++++ .../MemoryPoolSlab2.cs | 4 +- .../MemoryPoolBlock2Tests.cs | 9 +- .../MemoryPoolExtensions.cs | 4 +- .../TestInput.cs | 1 + 9 files changed, 649 insertions(+), 648 deletions(-) delete mode 100644 src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolBlock2.cs rename src/Microsoft.AspNet.Server.Kestrel/{Http => Infrastructure}/MemoryPool2.cs (99%) create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs create mode 100644 src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs rename src/Microsoft.AspNet.Server.Kestrel/{Http => Infrastructure}/MemoryPoolSlab2.cs (97%) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolBlock2.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolBlock2.cs deleted file mode 100644 index 681b476758..0000000000 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolBlock2.cs +++ /dev/null @@ -1,630 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Linq; -using System.Numerics; -using System.Runtime.InteropServices; -using System.Text; - -namespace Microsoft.AspNet.Server.Kestrel.Http -{ - /// - /// 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 independant array segments. - /// - public class MemoryPoolBlock2 - { - /// - /// Array of "minus one" bytes of the length of SIMD operations on the current hardware. Used as an argument in the - /// vector dot product that counts matching character occurence. - /// - private static Vector _dotCount = new Vector(Byte.MaxValue); - - /// - /// Array of negative numbers starting at 0 and continuing for the length of SIMD operations on the current hardware. - /// Used as an argument in the vector dot product that determines matching character index. - /// - private static Vector _dotIndex = new Vector(Enumerable.Range(0, Vector.Count).Select(x => (byte)-x).ToArray()); - - /// - /// If this block represents a one-time-use memory object, this GCHandle will hold that memory object at a fixed address - /// so it can be used in native operations. - /// - private GCHandle _pinHandle; - - /// - /// Native address of the first byte of this block's Data memory. It is null for one-time-use memory, or copied from - /// the Slab's ArrayPtr for a slab-block segment. The byte it points to corresponds to Data.Array[0], and in practice you will always - /// use the _dataArrayPtr + Start or _dataArrayPtr + End, which point to the start of "active" bytes, or point to just after the "active" bytes. - /// - private IntPtr _dataArrayPtr; - - /// - /// The array segment describing the range of memory this block is tracking. The caller which has leased this block may only read and - /// modify the memory in this range. - /// - public ArraySegment Data; - - /// - /// This object cannot be instantiated outside of the static Create method - /// - protected MemoryPoolBlock2() - { - } - - /// - /// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool. - /// - public MemoryPool2 Pool { get; private set; } - - /// - /// Back-reference to the slab from which this block was taken, or null if it is one-time-use memory. - /// - public MemoryPoolSlab2 Slab { get; private set; } - - /// - /// Convenience accessor - /// - public byte[] Array => Data.Array; - - /// - /// The Start represents the offset into Array where the range of "active" bytes begins. At the point when the block is leased - /// the Start is guaranteed to be equal to Array.Offset. The value of Start may be assigned anywhere between Data.Offset and - /// Data.Offset + Data.Count, and must be equal to or less than End. - /// - public int Start { get; set; } - - /// - /// The End represents the offset into Array where the range of "active" bytes ends. At the point when the block is leased - /// the End is guaranteed to be equal to Array.Offset. The value of Start may be assigned anywhere between Data.Offset and - /// Data.Offset + Data.Count, and must be equal to or less than End. - /// - public int End { get; set; } - - - /// - /// Reference to the next block of data when the overall "active" bytes spans multiple blocks. At the point when the block is - /// leased Next is guaranteed to be null. Start, End, and Next are used together in order to create a linked-list of discontiguous - /// working memory. The "active" memory is grown when bytes are copied in, End is increased, and Next is assigned. The "active" - /// memory is shrunk when bytes are consumed, Start is increased, and blocks are returned to the pool. - /// - public MemoryPoolBlock2 Next { get; set; } - - ~MemoryPoolBlock2() - { - if (_pinHandle.IsAllocated) - { - // if this is a one-time-use block, ensure that the GCHandle does not leak - _pinHandle.Free(); - } - - if (Slab != null && Slab.IsActive) - { - Pool.Return(new MemoryPoolBlock2 - { - _dataArrayPtr = _dataArrayPtr, - Data = Data, - Pool = Pool, - Slab = Slab, - }); - } - } - - /// - /// Called to ensure that a block is pinned, and return the pointer to native memory just after - /// the range of "active" bytes. This is where arriving data is read into. - /// - /// - public IntPtr Pin() - { - Debug.Assert(!_pinHandle.IsAllocated); - - if (_dataArrayPtr != IntPtr.Zero) - { - // this is a slab managed block - use the native address of the slab which is always locked - return _dataArrayPtr + End; - } - else - { - // this is one-time-use memory - lock the managed memory until Unpin is called - _pinHandle = GCHandle.Alloc(Data.Array, GCHandleType.Pinned); - return _pinHandle.AddrOfPinnedObject() + End; - } - } - - public void Unpin() - { - if (_dataArrayPtr == IntPtr.Zero) - { - // this is one-time-use memory - unlock the managed memory - Debug.Assert(_pinHandle.IsAllocated); - _pinHandle.Free(); - } - } - - public static MemoryPoolBlock2 Create(int size, MemoryPool2 pool) - { - return new MemoryPoolBlock2 - { - Data = new ArraySegment(new byte[size]), - Pool = pool - }; - } - - public static MemoryPoolBlock2 Create( - ArraySegment data, - IntPtr dataPtr, - MemoryPool2 pool, - MemoryPoolSlab2 slab) - { - return new MemoryPoolBlock2 - { - Data = data, - _dataArrayPtr = dataPtr, - Pool = pool, - Slab = slab, - Start = data.Offset, - End = data.Offset, - }; - } - - /// - /// called when the block is returned to the pool. mutable values are re-assigned to their guaranteed initialized state. - /// - public void Reset() - { - Next = null; - Start = Data.Offset; - End = Data.Offset; - } - - /// - /// ToString overridden for debugger convenience. This displays the "active" byte information in this block as ASCII characters. - /// - /// - public override string ToString() - { - return Encoding.ASCII.GetString(Array, Start, End - Start); - } - - /// - /// acquires a cursor pointing into this block at the Start of "active" byte information - /// - /// - public Iterator GetIterator() - { - return new Iterator(this); - } - - public struct Iterator - { - private MemoryPoolBlock2 _block; - private int _index; - - public Iterator(MemoryPoolBlock2 block) - { - _block = block; - _index = _block?.Start ?? 0; - } - public Iterator(MemoryPoolBlock2 block, int index) - { - _block = block; - _index = index; - } - - public bool IsDefault => _block == null; - - public bool IsEnd - { - get - { - if (_block == null) - { - return true; - } - else if (_index < _block.End) - { - return false; - } - else - { - for (var block = _block.Next; block != null; block = block.Next) - { - if (block.Start < block.End) - { - return true; - } - } - return true; - } - } - } - - public MemoryPoolBlock2 Block => _block; - - public int Index => _index; - - public int Take() - { - if (_block == null) - { - return -1; - } - else if (_index < _block.End) - { - return _block.Array[_index++]; - } - - var block = _block; - var index = _index; - while (true) - { - if (index < block.End) - { - _block = block; - _index = index + 1; - return block.Array[index]; - } - else if (block.Next == null) - { - return -1; - } - else - { - block = block.Next; - index = block.Start; - } - } - } - - public int Peek() - { - if (_block == null) - { - return -1; - } - else if (_index < _block.End) - { - return _block.Array[_index]; - } - else if (_block.Next == null) - { - return -1; - } - - var block = _block.Next; - var index = block.Start; - while (true) - { - if (index < block.End) - { - return block.Array[index]; - } - else if (block.Next == null) - { - return -1; - } - else - { - block = block.Next; - index = block.Start; - } - } - } - - public int Seek(int char0) - { - if (IsDefault) - { - return -1; - } - - var byte0 = (byte)char0; - var vectorStride = Vector.Count; - var ch0Vector = new Vector(byte0); - - var block = _block; - var index = _index; - var array = block.Array; - while (true) - { - while (block.End == index) - { - if (block.Next == null) - { - _block = block; - _index = index; - return -1; - } - block = block.Next; - index = block.Start; - array = block.Array; - } - while (block.End != index) - { - var following = block.End - index; - if (following >= vectorStride) - { - var data = new Vector(array, index); - var ch0Equals = Vector.Equals(data, ch0Vector); - var ch0Count = Vector.Dot(ch0Equals, _dotCount); - - if (ch0Count == 0) - { - index += vectorStride; - continue; - } - else if (ch0Count == 1) - { - _block = block; - _index = index + Vector.Dot(ch0Equals, _dotIndex); - return char0; - } - else - { - following = vectorStride; - } - } - for (; following != 0; following--, index++) - { - if (block.Array[index] == byte0) - { - _block = block; - _index = index; - return char0; - } - } - } - } - } - - public int Seek(int char0, int char1) - { - if (IsDefault) - { - return -1; - } - - var byte0 = (byte)char0; - var byte1 = (byte)char1; - var vectorStride = Vector.Count; - var ch0Vector = new Vector(byte0); - var ch1Vector = new Vector(byte1); - - var block = _block; - var index = _index; - var array = block.Array; - while (true) - { - while (block.End == index) - { - if (block.Next == null) - { - _block = block; - _index = index; - return -1; - } - block = block.Next; - index = block.Start; - array = block.Array; - } - while (block.End != index) - { - var following = block.End - index; - if (following >= vectorStride) - { - var data = new Vector(array, index); - var ch0Equals = Vector.Equals(data, ch0Vector); - var ch0Count = Vector.Dot(ch0Equals, _dotCount); - var ch1Equals = Vector.Equals(data, ch1Vector); - var ch1Count = Vector.Dot(ch1Equals, _dotCount); - - if (ch0Count == 0 && ch1Count == 0) - { - index += vectorStride; - continue; - } - else if (ch0Count < 2 && ch1Count < 2) - { - var ch0Index = ch0Count == 1 ? Vector.Dot(ch0Equals, _dotIndex) : byte.MaxValue; - var ch1Index = ch1Count == 1 ? Vector.Dot(ch1Equals, _dotIndex) : byte.MaxValue; - if (ch0Index < ch1Index) - { - _block = block; - _index = index + ch0Index; - return char0; - } - else - { - _block = block; - _index = index + ch1Index; - return char1; - } - } - else - { - following = vectorStride; - } - } - for (; following != 0; following--, index++) - { - var byteIndex = block.Array[index]; - if (byteIndex == byte0) - { - _block = block; - _index = index; - return char0; - } - else if (byteIndex == byte1) - { - _block = block; - _index = index; - return char1; - } - } - } - } - } - - public int GetLength(Iterator end) - { - if (IsDefault || end.IsDefault) - { - return -1; - } - - var block = _block; - var index = _index; - var length = 0; - while (true) - { - if (block == end._block) - { - return length + end._index - index; - } - else if (block.Next == null) - { - throw new InvalidOperationException("end did not follow iterator"); - } - else - { - length += block.End - index; - block = block.Next; - index = block.Start; - } - } - } - - public string GetString(Iterator end) - { - if (IsDefault || end.IsDefault) - { - return default(string); - } - if (end._block == _block) - { - return Encoding.UTF8.GetString(_block.Array, _index, end._index - _index); - } - - var decoder = Encoding.ASCII.GetDecoder(); - - var length = GetLength(end); - var charLength = length * 2; - var chars = new char[charLength]; - var charIndex = 0; - - var block = _block; - var index = _index; - var remaining = length; - while (true) - { - int bytesUsed; - int charsUsed; - bool completed; - var following = block.End - index; - if (remaining <= following) - { - decoder.Convert( - block.Array, - index, - remaining, - chars, - charIndex, - charLength - charIndex, - true, - out bytesUsed, - out charsUsed, - out completed); - return new string(chars, 0, charIndex + charsUsed); - } - else if (block.Next == null) - { - decoder.Convert( - block.Array, - index, - following, - chars, - charIndex, - charLength - charIndex, - true, - out bytesUsed, - out charsUsed, - out completed); - return new string(chars, 0, charIndex + charsUsed); - } - else - { - decoder.Convert( - block.Array, - index, - following, - chars, - charIndex, - charLength - charIndex, - false, - out bytesUsed, - out charsUsed, - out completed); - charIndex += charsUsed; - remaining -= following; - block = block.Next; - index = block.Start; - } - } - } - - public ArraySegment GetArraySegment(Iterator end) - { - if (IsDefault || end.IsDefault) - { - return default(ArraySegment); - } - if (end._block == _block) - { - return new ArraySegment(_block.Array, _index, end._index - _index); - } - - var length = GetLength(end); - var array = new byte[length]; - CopyTo(array, 0, length, out length); - return new ArraySegment(array, 0, length); - } - - public Iterator CopyTo(byte[] array, int offset, int count, out int actual) - { - if (IsDefault) - { - actual = 0; - return this; - } - - var block = _block; - var index = _index; - var remaining = count; - while (true) - { - var following = block.End - index; - if (remaining <= following) - { - actual = count; - Buffer.BlockCopy(block.Array, index, array, offset, remaining); - return new Iterator(block, index + remaining); - } - else if (block.Next == null) - { - actual = count - remaining + following; - Buffer.BlockCopy(block.Array, index, array, offset, following); - return new Iterator(block, index + following); - } - else - { - Buffer.BlockCopy(block.Array, index, array, offset, following); - remaining -= following; - block = block.Next; - index = block.Start; - } - } - } - } - } -} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs index 03ef803e9c..6352b742ef 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs @@ -2,12 +2,11 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; namespace Microsoft.AspNet.Server.Kestrel.Http { @@ -131,17 +130,17 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - public MemoryPoolBlock2.Iterator ConsumingStart() + public MemoryPoolIterator2 ConsumingStart() { lock (_sync) { - return new MemoryPoolBlock2.Iterator(_head); + return new MemoryPoolIterator2(_head); } } public void ConsumingComplete( - MemoryPoolBlock2.Iterator consumed, - MemoryPoolBlock2.Iterator examined) + MemoryPoolIterator2 consumed, + MemoryPoolIterator2 examined) { MemoryPoolBlock2 returnStart = null; MemoryPoolBlock2 returnEnd = null; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs similarity index 99% rename from src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPool2.cs rename to src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index 941fb9b796..a0fd3e4a7c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Concurrent; -namespace Microsoft.AspNet.Server.Kestrel.Http +namespace Microsoft.AspNet.Server.Kestrel.Infrastructure { /// /// Used to allocate and distribute re-usable blocks of memory. diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs new file mode 100644 index 0000000000..bb7eb5758c --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs @@ -0,0 +1,184 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.AspNet.Server.Kestrel.Infrastructure +{ + /// + /// 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 independant array segments. + /// + public class MemoryPoolBlock2 + { + /// + /// If this block represents a one-time-use memory object, this GCHandle will hold that memory object at a fixed address + /// so it can be used in native operations. + /// + private GCHandle _pinHandle; + + /// + /// Native address of the first byte of this block's Data memory. It is null for one-time-use memory, or copied from + /// the Slab's ArrayPtr for a slab-block segment. The byte it points to corresponds to Data.Array[0], and in practice you will always + /// use the _dataArrayPtr + Start or _dataArrayPtr + End, which point to the start of "active" bytes, or point to just after the "active" bytes. + /// + private IntPtr _dataArrayPtr; + + /// + /// The array segment describing the range of memory this block is tracking. The caller which has leased this block may only read and + /// modify the memory in this range. + /// + public ArraySegment Data; + + /// + /// This object cannot be instantiated outside of the static Create method + /// + protected MemoryPoolBlock2() + { + } + + /// + /// Back-reference to the memory pool which this block was allocated from. It may only be returned to this pool. + /// + public MemoryPool2 Pool { get; private set; } + + /// + /// Back-reference to the slab from which this block was taken, or null if it is one-time-use memory. + /// + public MemoryPoolSlab2 Slab { get; private set; } + + /// + /// Convenience accessor + /// + public byte[] Array => Data.Array; + + /// + /// The Start represents the offset into Array where the range of "active" bytes begins. At the point when the block is leased + /// the Start is guaranteed to be equal to Array.Offset. The value of Start may be assigned anywhere between Data.Offset and + /// Data.Offset + Data.Count, and must be equal to or less than End. + /// + public int Start { get; set; } + + /// + /// The End represents the offset into Array where the range of "active" bytes ends. At the point when the block is leased + /// the End is guaranteed to be equal to Array.Offset. The value of Start may be assigned anywhere between Data.Offset and + /// Data.Offset + Data.Count, and must be equal to or less than End. + /// + public int End { get; set; } + + + /// + /// Reference to the next block of data when the overall "active" bytes spans multiple blocks. At the point when the block is + /// leased Next is guaranteed to be null. Start, End, and Next are used together in order to create a linked-list of discontiguous + /// working memory. The "active" memory is grown when bytes are copied in, End is increased, and Next is assigned. The "active" + /// memory is shrunk when bytes are consumed, Start is increased, and blocks are returned to the pool. + /// + public MemoryPoolBlock2 Next { get; set; } + + ~MemoryPoolBlock2() + { + if (_pinHandle.IsAllocated) + { + // if this is a one-time-use block, ensure that the GCHandle does not leak + _pinHandle.Free(); + } + + if (Slab != null && Slab.IsActive) + { + Pool.Return(new MemoryPoolBlock2 + { + _dataArrayPtr = _dataArrayPtr, + Data = Data, + Pool = Pool, + Slab = Slab, + }); + } + } + + /// + /// Called to ensure that a block is pinned, and return the pointer to native memory just after + /// the range of "active" bytes. This is where arriving data is read into. + /// + /// + public IntPtr Pin() + { + Debug.Assert(!_pinHandle.IsAllocated); + + if (_dataArrayPtr != IntPtr.Zero) + { + // this is a slab managed block - use the native address of the slab which is always locked + return _dataArrayPtr + End; + } + else + { + // this is one-time-use memory - lock the managed memory until Unpin is called + _pinHandle = GCHandle.Alloc(Data.Array, GCHandleType.Pinned); + return _pinHandle.AddrOfPinnedObject() + End; + } + } + + public void Unpin() + { + if (_dataArrayPtr == IntPtr.Zero) + { + // this is one-time-use memory - unlock the managed memory + Debug.Assert(_pinHandle.IsAllocated); + _pinHandle.Free(); + } + } + + public static MemoryPoolBlock2 Create(int size, MemoryPool2 pool) + { + return new MemoryPoolBlock2 + { + Data = new ArraySegment(new byte[size]), + Pool = pool + }; + } + + public static MemoryPoolBlock2 Create( + ArraySegment data, + IntPtr dataPtr, + MemoryPool2 pool, + MemoryPoolSlab2 slab) + { + return new MemoryPoolBlock2 + { + Data = data, + _dataArrayPtr = dataPtr, + Pool = pool, + Slab = slab, + Start = data.Offset, + End = data.Offset, + }; + } + + /// + /// called when the block is returned to the pool. mutable values are re-assigned to their guaranteed initialized state. + /// + public void Reset() + { + Next = null; + Start = Data.Offset; + End = Data.Offset; + } + + /// + /// ToString overridden for debugger convenience. This displays the "active" byte information in this block as ASCII characters. + /// + /// + public override string ToString() + { + return Encoding.ASCII.GetString(Array, Start, End - Start); + } + + /// + /// acquires a cursor pointing into this block at the Start of "active" byte information + /// + /// + public MemoryPoolIterator2 GetIterator() + { + return new MemoryPoolIterator2(this); + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs new file mode 100644 index 0000000000..7e409153c4 --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs @@ -0,0 +1,452 @@ +using System; +using System.Linq; +using System.Numerics; +using System.Text; + +namespace Microsoft.AspNet.Server.Kestrel.Infrastructure +{ + public struct MemoryPoolIterator2 + { + /// + /// Array of "minus one" bytes of the length of SIMD operations on the current hardware. Used as an argument in the + /// vector dot product that counts matching character occurence. + /// + private static Vector _dotCount = new Vector(Byte.MaxValue); + + /// + /// Array of negative numbers starting at 0 and continuing for the length of SIMD operations on the current hardware. + /// Used as an argument in the vector dot product that determines matching character index. + /// + private static Vector _dotIndex = new Vector(Enumerable.Range(0, Vector.Count).Select(x => (byte)-x).ToArray()); + + private static Encoding _utf8 = Encoding.UTF8; + + private MemoryPoolBlock2 _block; + private int _index; + + public MemoryPoolIterator2(MemoryPoolBlock2 block) + { + _block = block; + _index = _block?.Start ?? 0; + } + public MemoryPoolIterator2(MemoryPoolBlock2 block, int index) + { + _block = block; + _index = index; + } + + public bool IsDefault => _block == null; + + public bool IsEnd + { + get + { + if (_block == null) + { + return true; + } + else if (_index < _block.End) + { + return false; + } + else + { + for (var block = _block.Next; block != null; block = block.Next) + { + if (block.Start < block.End) + { + return true; + } + } + return true; + } + } + } + + public MemoryPoolBlock2 Block => _block; + + public int Index => _index; + + public int Take() + { + if (_block == null) + { + return -1; + } + else if (_index < _block.End) + { + return _block.Array[_index++]; + } + + var block = _block; + var index = _index; + while (true) + { + if (index < block.End) + { + _block = block; + _index = index + 1; + return block.Array[index]; + } + else if (block.Next == null) + { + return -1; + } + else + { + block = block.Next; + index = block.Start; + } + } + } + + public int Peek() + { + if (_block == null) + { + return -1; + } + else if (_index < _block.End) + { + return _block.Array[_index]; + } + else if (_block.Next == null) + { + return -1; + } + + var block = _block.Next; + var index = block.Start; + while (true) + { + if (index < block.End) + { + return block.Array[index]; + } + else if (block.Next == null) + { + return -1; + } + else + { + block = block.Next; + index = block.Start; + } + } + } + + public int Seek(int char0) + { + if (IsDefault) + { + return -1; + } + + var byte0 = (byte)char0; + var vectorStride = Vector.Count; + var ch0Vector = new Vector(byte0); + + var block = _block; + var index = _index; + var array = block.Array; + while (true) + { + while (block.End == index) + { + if (block.Next == null) + { + _block = block; + _index = index; + return -1; + } + block = block.Next; + index = block.Start; + array = block.Array; + } + while (block.End != index) + { + var following = block.End - index; + if (following >= vectorStride) + { + var data = new Vector(array, index); + var ch0Equals = Vector.Equals(data, ch0Vector); + var ch0Count = Vector.Dot(ch0Equals, _dotCount); + + if (ch0Count == 0) + { + index += vectorStride; + continue; + } + else if (ch0Count == 1) + { + _block = block; + _index = index + Vector.Dot(ch0Equals, _dotIndex); + return char0; + } + else + { + following = vectorStride; + } + } + for (; following != 0; following--, index++) + { + if (block.Array[index] == byte0) + { + _block = block; + _index = index; + return char0; + } + } + } + } + } + + public int Seek(int char0, int char1) + { + if (IsDefault) + { + return -1; + } + + var byte0 = (byte)char0; + var byte1 = (byte)char1; + var vectorStride = Vector.Count; + var ch0Vector = new Vector(byte0); + var ch1Vector = new Vector(byte1); + + var block = _block; + var index = _index; + var array = block.Array; + while (true) + { + while (block.End == index) + { + if (block.Next == null) + { + _block = block; + _index = index; + return -1; + } + block = block.Next; + index = block.Start; + array = block.Array; + } + while (block.End != index) + { + var following = block.End - index; + if (following >= vectorStride) + { + var data = new Vector(array, index); + var ch0Equals = Vector.Equals(data, ch0Vector); + var ch0Count = Vector.Dot(ch0Equals, _dotCount); + var ch1Equals = Vector.Equals(data, ch1Vector); + var ch1Count = Vector.Dot(ch1Equals, _dotCount); + + if (ch0Count == 0 && ch1Count == 0) + { + index += vectorStride; + continue; + } + else if (ch0Count < 2 && ch1Count < 2) + { + var ch0Index = ch0Count == 1 ? Vector.Dot(ch0Equals, _dotIndex) : byte.MaxValue; + var ch1Index = ch1Count == 1 ? Vector.Dot(ch1Equals, _dotIndex) : byte.MaxValue; + if (ch0Index < ch1Index) + { + _block = block; + _index = index + ch0Index; + return char0; + } + else + { + _block = block; + _index = index + ch1Index; + return char1; + } + } + else + { + following = vectorStride; + } + } + for (; following != 0; following--, index++) + { + var byteIndex = block.Array[index]; + if (byteIndex == byte0) + { + _block = block; + _index = index; + return char0; + } + else if (byteIndex == byte1) + { + _block = block; + _index = index; + return char1; + } + } + } + } + } + + public int GetLength(MemoryPoolIterator2 end) + { + if (IsDefault || end.IsDefault) + { + return -1; + } + + var block = _block; + var index = _index; + var length = 0; + while (true) + { + if (block == end._block) + { + return length + end._index - index; + } + else if (block.Next == null) + { + throw new InvalidOperationException("end did not follow iterator"); + } + else + { + length += block.End - index; + block = block.Next; + index = block.Start; + } + } + } + + public string GetString(MemoryPoolIterator2 end) + { + if (IsDefault || end.IsDefault) + { + return default(string); + } + if (end._block == _block) + { + return _utf8.GetString(_block.Array, _index, end._index - _index); + } + + var decoder = _utf8.GetDecoder(); + + var length = GetLength(end); + var charLength = length * 2; + var chars = new char[charLength]; + var charIndex = 0; + + var block = _block; + var index = _index; + var remaining = length; + while (true) + { + int bytesUsed; + int charsUsed; + bool completed; + var following = block.End - index; + if (remaining <= following) + { + decoder.Convert( + block.Array, + index, + remaining, + chars, + charIndex, + charLength - charIndex, + true, + out bytesUsed, + out charsUsed, + out completed); + return new string(chars, 0, charIndex + charsUsed); + } + else if (block.Next == null) + { + decoder.Convert( + block.Array, + index, + following, + chars, + charIndex, + charLength - charIndex, + true, + out bytesUsed, + out charsUsed, + out completed); + return new string(chars, 0, charIndex + charsUsed); + } + else + { + decoder.Convert( + block.Array, + index, + following, + chars, + charIndex, + charLength - charIndex, + false, + out bytesUsed, + out charsUsed, + out completed); + charIndex += charsUsed; + remaining -= following; + block = block.Next; + index = block.Start; + } + } + } + + public ArraySegment GetArraySegment(MemoryPoolIterator2 end) + { + if (IsDefault || end.IsDefault) + { + return default(ArraySegment); + } + if (end._block == _block) + { + return new ArraySegment(_block.Array, _index, end._index - _index); + } + + var length = GetLength(end); + var array = new byte[length]; + CopyTo(array, 0, length, out length); + return new ArraySegment(array, 0, length); + } + + public MemoryPoolIterator2 CopyTo(byte[] array, int offset, int count, out int actual) + { + if (IsDefault) + { + actual = 0; + return this; + } + + var block = _block; + var index = _index; + var remaining = count; + while (true) + { + var following = block.End - index; + if (remaining <= following) + { + actual = count; + Buffer.BlockCopy(block.Array, index, array, offset, remaining); + return new MemoryPoolIterator2(block, index + remaining); + } + else if (block.Next == null) + { + actual = count - remaining + following; + Buffer.BlockCopy(block.Array, index, array, offset, following); + return new MemoryPoolIterator2(block, index + following); + } + else + { + Buffer.BlockCopy(block.Array, index, array, offset, following); + remaining -= following; + block = block.Next; + index = block.Start; + } + } + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolSlab2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolSlab2.cs similarity index 97% rename from src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolSlab2.cs rename to src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolSlab2.cs index bf0221dda7..9e5e3bb2eb 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolSlab2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolSlab2.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Concurrent; -using System.Diagnostics; using System.Runtime.InteropServices; -namespace Microsoft.AspNet.Server.Kestrel.Http +namespace Microsoft.AspNet.Server.Kestrel.Infrastructure { /// /// Slab tracking object used by the byte buffer memory pool. A slab is a large allocation which is divided into smaller blocks. The diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs index a9c6e5a24f..2f43e0239d 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs @@ -1,8 +1,5 @@ -using Microsoft.AspNet.Server.Kestrel.Http; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Linq; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Xunit; namespace Microsoft.AspNet.Server.KestrelTests @@ -120,7 +117,7 @@ namespace Microsoft.AspNet.Server.KestrelTests } } - private void AssertIterator(MemoryPoolBlock2.Iterator iter, MemoryPoolBlock2 block, int index) + private void AssertIterator(MemoryPoolIterator2 iter, MemoryPoolBlock2 block, int index) { Assert.Same(block, iter.Block); Assert.Equal(index, iter.Index); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolExtensions.cs b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolExtensions.cs index 3578dda184..1eb6830889 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolExtensions.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolExtensions.cs @@ -1,10 +1,10 @@ -using Microsoft.AspNet.Server.Kestrel.Http; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; namespace Microsoft.AspNet.Server.KestrelTests { public static class MemoryPoolExtensions { - public static MemoryPoolBlock2.Iterator Add(this MemoryPoolBlock2.Iterator iterator, int count) + public static MemoryPoolIterator2 Add(this MemoryPoolIterator2 iterator, int count) { int actual; return iterator.CopyTo(new byte[count], 0, count, out actual); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs index 63f55cab03..47470bb4fc 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNet.Server.Kestrel.Http; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; namespace Microsoft.AspNet.Server.KestrelTests {