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); offset += following; remaining -= following; block = block.Next; index = block.Start; } } } } }