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
{