public int GetLength(MemoryPoolIterator2 end) { if (IsDefault || end.IsDefault) { return(-1); } var block = _block; var index = _index; var length = 0; checked { 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 void ConsumingComplete( MemoryPoolIterator2 consumed, MemoryPoolIterator2 examined) { MemoryPoolBlock2 returnStart = null; MemoryPoolBlock2 returnEnd = null; lock (_sync) { if (!consumed.IsDefault) { returnStart = _head; returnEnd = consumed.Block; _head = consumed.Block; _head.Start = consumed.Index; } if (!examined.IsDefault && examined.IsEnd && RemoteIntakeFin == false && _awaitableError == null) { _manualResetEvent.Reset(); var awaitableState = Interlocked.CompareExchange( ref _awaitableState, _awaitableIsNotCompleted, _awaitableIsCompleted); } } while (returnStart != returnEnd) { var returnBlock = returnStart; returnStart = returnStart.Next; returnBlock.Pool.Return(returnBlock); } }
/// <summary> /// Checks 9 bytes from <paramref name="begin"/> correspond to a known HTTP version. /// </summary> /// <remarks> /// A "known HTTP version" Is is either HTTP/1.0 or HTTP/1.1. /// Since those fit in 8 bytes, they can be optimally looked up by reading those bytes as a long. Once /// in that format, it can be checked against the known versions. /// The Known versions will be checked with the required '\r'. /// To optimize performance the HTTP/1.1 will be checked first. /// </remarks> /// <param name="begin">The iterator from which to start the known string lookup.</param> /// <param name="scan">If we found a valid method, then scan will be updated to new position</param> /// <param name="knownMethod">A reference to a pre-allocated known string, if the input matches any.</param> /// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns> public static bool GetKnownVersion(this MemoryPoolIterator2 begin, ref MemoryPoolIterator2 scan, out string knownVersion) { knownVersion = null; var value = begin.PeekLong(); if (value == _http11VersionLong) { knownVersion = Http11Version; scan.Skip(8); if (scan.Take() == '\r') { return(true); } } else if (value == _http10VersionLong) { knownVersion = Http10Version; scan.Skip(8); if (scan.Take() == '\r') { return(true); } } knownVersion = null; return(false); }
public unsafe void Write( UvStreamHandle handle, MemoryPoolIterator2 start, MemoryPoolIterator2 end, int nBuffers, Action<UvWriteReq, int, Exception, object> callback, object state) { try { // add GCHandle to keeps this SafeHandle alive while request processing _pins.Add(GCHandle.Alloc(this, GCHandleType.Normal)); var pBuffers = (Libuv.uv_buf_t*)_bufs; if (nBuffers > BUFFER_COUNT) { // create and pin buffer array when it's larger than the pre-allocated one var bufArray = new Libuv.uv_buf_t[nBuffers]; var gcHandle = GCHandle.Alloc(bufArray, GCHandleType.Pinned); _pins.Add(gcHandle); pBuffers = (Libuv.uv_buf_t*)gcHandle.AddrOfPinnedObject(); } var block = start.Block; for (var index = 0; index < nBuffers; index++) { var blockStart = block == start.Block ? start.Index : block.Data.Offset; var blockEnd = block == end.Block ? end.Index : block.Data.Offset + block.Data.Count; // create and pin each segment being written pBuffers[index] = Libuv.buf_init( block.Pin() + blockStart, blockEnd - blockStart); block = block.Next; } _callback = callback; _state = state; _uv.write(this, handle, pBuffers, nBuffers, _uv_write_cb); } catch { _callback = null; _state = null; Unpin(this); var block = start.Block; for (var index = 0; index < nBuffers; index++) { block.Unpin(); block = block.Next; } throw; } }
public void ProducingComplete(MemoryPoolIterator2 end) { var block = _producingBlock; while (block != end.Block) { _outputStream.Write(block.Data.Array, block.Data.Offset, block.Data.Count); var returnBlock = block; block = block.Next; returnBlock.Pool?.Return(returnBlock); } _outputStream.Write(end.Block.Array, end.Block.Data.Offset, end.Index - end.Block.Data.Offset); end.Block.Pool?.Return(end.Block); }
public static ArraySegment <byte> GetArraySegment(this MemoryPoolIterator2 start, MemoryPoolIterator2 end) { if (start.IsDefault || end.IsDefault) { return(default(ArraySegment <byte>)); } if (end.Block == start.Block) { return(new ArraySegment <byte>(start.Block.Array, start.Index, end.Index - start.Index)); } var length = start.GetLength(end); var array = new byte[length]; start.CopyTo(array, 0, length, out length); return(new ArraySegment <byte>(array, 0, length)); }
public ArraySegment <byte> GetArraySegment(MemoryPoolIterator2 end) { if (IsDefault || end.IsDefault) { return(default(ArraySegment <byte>)); } if (end._block == _block) { return(new ArraySegment <byte>(_block.Array, _index, end._index - _index)); } var length = GetLength(end); var array = new byte[length]; CopyTo(array, 0, length, out length); return(new ArraySegment <byte>(array, 0, length)); }
/// <summary> /// Unescapes the string between given memory iterators in place. /// </summary> /// <param name="start">The iterator points to the beginning of the sequence.</param> /// <param name="end">The iterator points to the byte behind the end of the sequence.</param> /// <returns>The iterator points to the byte behind the end of the processed sequence.</returns> public static MemoryPoolIterator2 Unescape(MemoryPoolIterator2 start, MemoryPoolIterator2 end) { // the slot to read the input var reader = start; // the slot to write the unescaped byte var writer = reader; while (true) { if (CompareIterators(ref reader, ref end)) { return writer; } if (reader.Peek() == '%') { var decodeReader = reader; // If decoding process succeeds, the writer iterator will be moved // to the next write-ready location. On the other hand if the scanned // percent-encodings cannot be interpreted as sequence of UTF-8 octets, // these bytes should be copied to output as is. // The decodeReader iterator is always moved to the first byte not yet // be scanned after the process. A failed decoding means the chars // between the reader and decodeReader can be copied to output untouched. if (!DecodeCore(ref decodeReader, ref writer, end)) { Copy(reader, decodeReader, ref writer); } reader = decodeReader; } else { writer.Put((byte)reader.Take()); } } }
/// <summary> /// Checks that up to 8 bytes from <paramref name="begin"/> correspond to a known HTTP method. /// </summary> /// <remarks> /// A "known HTTP method" can be an HTTP method name defined in the HTTP/1.1 RFC. /// Since all of those fit in at most 8 bytes, they can be optimally looked up by reading those bytes as a long. Once /// in that format, it can be checked against the known method. /// The Known Methods (CONNECT, DELETE, GET, HEAD, PATCH, POST, PUT, OPTIONS, TRACE) are all less than 8 bytes /// and will be compared with the required space. A mask is used if the Known method is less than 8 bytes. /// To optimize performance the GET method will be checked first. /// </remarks> /// <param name="begin">The iterator from which to start the known string lookup.</param> /// <param name="scan">If we found a valid method, then scan will be updated to new position</param> /// <param name="knownMethod">A reference to a pre-allocated known string, if the input matches any.</param> /// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns> public static bool GetKnownMethod(this MemoryPoolIterator2 begin, ref MemoryPoolIterator2 scan, out string knownMethod) { knownMethod = null; var value = begin.PeekLong(); if ((value & _mask4Chars) == _httpGetMethodLong) { knownMethod = HttpGetMethod; scan.Skip(4); return(true); } foreach (var x in _knownMethods) { if ((value & x.Item1) == x.Item2) { knownMethod = x.Item3; scan.Skip(knownMethod.Length + 1); return(true); } } return(false); }
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; } } }
/// <summary> /// Unescape the percent-encodings /// </summary> /// <param name="reader">The iterator point to the first % char</param> /// <param name="writer">The place to write to</param> /// <param name="end">The end of the sequence</param> private static bool DecodeCore(ref MemoryPoolIterator2 reader, ref MemoryPoolIterator2 writer, MemoryPoolIterator2 end) { // preserves the original head. if the percent-encodings cannot be interpreted as sequence of UTF-8 octets, // bytes from this till the last scanned one will be copied to the memory pointed by writer. var byte1 = UnescapePercentEncoding(ref reader, end); if (byte1 == -1) { return false; } if (byte1 <= 0x7F) { // first byte < U+007f, it is a single byte ASCII writer.Put((byte)byte1); return true; } int byte2 = 0, byte3 = 0, byte4 = 0; // anticipate more bytes var currentDecodeBits = 0; var byteCount = 1; var expectValueMin = 0; if ((byte1 & 0xE0) == 0xC0) { // 110x xxxx, expect one more byte currentDecodeBits = byte1 & 0x1F; byteCount = 2; expectValueMin = 0x80; } else if ((byte1 & 0xF0) == 0xE0) { // 1110 xxxx, expect two more bytes currentDecodeBits = byte1 & 0x0F; byteCount = 3; expectValueMin = 0x800; } else if ((byte1 & 0xF8) == 0xF0) { // 1111 0xxx, expect three more bytes currentDecodeBits = byte1 & 0x07; byteCount = 4; expectValueMin = 0x10000; } else { // invalid first byte return false; } var remainingBytes = byteCount - 1; while (remainingBytes > 0) { // read following three chars if (CompareIterators(ref reader, ref end)) { return false; } var nextItr = reader; var nextByte = UnescapePercentEncoding(ref nextItr, end); if (nextByte == -1) { return false; } if ((nextByte & 0xC0) != 0x80) { // the follow up byte is not in form of 10xx xxxx return false; } currentDecodeBits = (currentDecodeBits << 6) | (nextByte & 0x3F); remainingBytes--; if (remainingBytes == 1 && currentDecodeBits >= 0x360 && currentDecodeBits <= 0x37F) { // this is going to end up in the range of 0xD800-0xDFFF UTF-16 surrogates that // are not allowed in UTF-8; return false; } if (remainingBytes == 2 && currentDecodeBits >= 0x110) { // this is going to be out of the upper Unicode bound 0x10FFFF. return false; } reader = nextItr; if (byteCount - remainingBytes == 2) { byte2 = nextByte; } else if (byteCount - remainingBytes == 3) { byte3 = nextByte; } else if (byteCount - remainingBytes == 4) { byte4 = nextByte; } } if (currentDecodeBits < expectValueMin) { // overlong encoding (e.g. using 2 bytes to encode something that only needed 1). return false; } // all bytes are verified, write to the output if (byteCount > 0) { writer.Put((byte)byte1); } if (byteCount > 1) { writer.Put((byte)byte2); } if (byteCount > 2) { writer.Put((byte)byte3); } if (byteCount > 3) { writer.Put((byte)byte4); } return true; }
public ArraySegment<byte> GetArraySegment(MemoryPoolIterator2 end) { if (IsDefault || end.IsDefault) { return default(ArraySegment<byte>); } if (end._block == _block) { return new ArraySegment<byte>(_block.Array, _index, end._index - _index); } var length = GetLength(end); var array = new byte[length]; CopyTo(array, 0, length, out length); return new ArraySegment<byte>(array, 0, length); }
private MemoryPoolIterator2 GetIterator(MemoryPoolIterator2 begin, int displacement) { var result = begin; for (int i = 0; i < displacement; ++i) { result.Take(); } return result; }
private static bool CompareIterators(ref MemoryPoolIterator2 lhs, ref MemoryPoolIterator2 rhs) { // uses ref parameter to save cost of copying return (lhs.Block == rhs.Block) && (lhs.Index == rhs.Index); }
private void AssertIterator(MemoryPoolIterator2 iter, MemoryPoolBlock2 block, int index) { Assert.Same(block, iter.Block); Assert.Equal(index, iter.Index); }
private static void BytesBetween(MemoryPoolIterator2 start, MemoryPoolIterator2 end, out int bytes, out int buffers) { if (start.Block == end.Block) { bytes = end.Index - start.Index; buffers = 1; return; } bytes = start.Block.Data.Offset + start.Block.Data.Count - start.Index; buffers = 1; for (var block = start.Block.Next; block != end.Block; block = block.Next) { bytes += block.Data.Count; buffers++; } bytes += end.Index - end.Block.Data.Offset; buffers++; }
private void LockWrite() { var head = Self._head; var tail = Self._tail; if (head == null || tail == null) { // ReturnAllBlocks has already bee called. Nothing to do here. // Write will no-op since _byteCount will remain 0. return; } _lockedStart = new MemoryPoolIterator2(head, head.Start); _lockedEnd = new MemoryPoolIterator2(tail, tail.End); BytesBetween(_lockedStart, _lockedEnd, out ByteCount, out _bufferCount); }
protected void CopyToFast(ref MemoryPoolIterator2 output) { if (((_bits & 1L) != 0)) { foreach (var value in _CacheControl) { if (value != null) { output.CopyFrom(_headerBytes, 0, 17); output.CopyFromAscii(value); } } } if (((_bits & 2L) != 0)) { if (_rawConnection != null) { output.CopyFrom(_rawConnection, 0, _rawConnection.Length); } else foreach (var value in _Connection) { if (value != null) { output.CopyFrom(_headerBytes, 17, 14); output.CopyFromAscii(value); } } } if (((_bits & 4L) != 0)) { if (_rawDate != null) { output.CopyFrom(_rawDate, 0, _rawDate.Length); } else foreach (var value in _Date) { if (value != null) { output.CopyFrom(_headerBytes, 31, 8); output.CopyFromAscii(value); } } } if (((_bits & 8L) != 0)) { foreach (var value in _KeepAlive) { if (value != null) { output.CopyFrom(_headerBytes, 39, 14); output.CopyFromAscii(value); } } } if (((_bits & 16L) != 0)) { foreach (var value in _Pragma) { if (value != null) { output.CopyFrom(_headerBytes, 53, 10); output.CopyFromAscii(value); } } } if (((_bits & 32L) != 0)) { foreach (var value in _Trailer) { if (value != null) { output.CopyFrom(_headerBytes, 63, 11); output.CopyFromAscii(value); } } } if (((_bits & 64L) != 0)) { if (_rawTransferEncoding != null) { output.CopyFrom(_rawTransferEncoding, 0, _rawTransferEncoding.Length); } else foreach (var value in _TransferEncoding) { if (value != null) { output.CopyFrom(_headerBytes, 74, 21); output.CopyFromAscii(value); } } } if (((_bits & 128L) != 0)) { foreach (var value in _Upgrade) { if (value != null) { output.CopyFrom(_headerBytes, 95, 11); output.CopyFromAscii(value); } } } if (((_bits & 256L) != 0)) { foreach (var value in _Via) { if (value != null) { output.CopyFrom(_headerBytes, 106, 7); output.CopyFromAscii(value); } } } if (((_bits & 512L) != 0)) { foreach (var value in _Warning) { if (value != null) { output.CopyFrom(_headerBytes, 113, 11); output.CopyFromAscii(value); } } } if (((_bits & 1024L) != 0)) { foreach (var value in _Allow) { if (value != null) { output.CopyFrom(_headerBytes, 124, 9); output.CopyFromAscii(value); } } } if (((_bits & 2048L) != 0)) { if (_rawContentLength != null) { output.CopyFrom(_rawContentLength, 0, _rawContentLength.Length); } else foreach (var value in _ContentLength) { if (value != null) { output.CopyFrom(_headerBytes, 133, 18); output.CopyFromAscii(value); } } } if (((_bits & 4096L) != 0)) { foreach (var value in _ContentType) { if (value != null) { output.CopyFrom(_headerBytes, 151, 16); output.CopyFromAscii(value); } } } if (((_bits & 8192L) != 0)) { foreach (var value in _ContentEncoding) { if (value != null) { output.CopyFrom(_headerBytes, 167, 20); output.CopyFromAscii(value); } } } if (((_bits & 16384L) != 0)) { foreach (var value in _ContentLanguage) { if (value != null) { output.CopyFrom(_headerBytes, 187, 20); output.CopyFromAscii(value); } } } if (((_bits & 32768L) != 0)) { foreach (var value in _ContentLocation) { if (value != null) { output.CopyFrom(_headerBytes, 207, 20); output.CopyFromAscii(value); } } } if (((_bits & 65536L) != 0)) { foreach (var value in _ContentMD5) { if (value != null) { output.CopyFrom(_headerBytes, 227, 15); output.CopyFromAscii(value); } } } if (((_bits & 131072L) != 0)) { foreach (var value in _ContentRange) { if (value != null) { output.CopyFrom(_headerBytes, 242, 17); output.CopyFromAscii(value); } } } if (((_bits & 262144L) != 0)) { foreach (var value in _Expires) { if (value != null) { output.CopyFrom(_headerBytes, 259, 11); output.CopyFromAscii(value); } } } if (((_bits & 524288L) != 0)) { foreach (var value in _LastModified) { if (value != null) { output.CopyFrom(_headerBytes, 270, 17); output.CopyFromAscii(value); } } } if (((_bits & 1048576L) != 0)) { foreach (var value in _AcceptRanges) { if (value != null) { output.CopyFrom(_headerBytes, 287, 17); output.CopyFromAscii(value); } } } if (((_bits & 2097152L) != 0)) { foreach (var value in _Age) { if (value != null) { output.CopyFrom(_headerBytes, 304, 7); output.CopyFromAscii(value); } } } if (((_bits & 4194304L) != 0)) { foreach (var value in _ETag) { if (value != null) { output.CopyFrom(_headerBytes, 311, 8); output.CopyFromAscii(value); } } } if (((_bits & 8388608L) != 0)) { foreach (var value in _Location) { if (value != null) { output.CopyFrom(_headerBytes, 319, 12); output.CopyFromAscii(value); } } } if (((_bits & 16777216L) != 0)) { foreach (var value in _ProxyAutheticate) { if (value != null) { output.CopyFrom(_headerBytes, 331, 21); output.CopyFromAscii(value); } } } if (((_bits & 33554432L) != 0)) { foreach (var value in _RetryAfter) { if (value != null) { output.CopyFrom(_headerBytes, 352, 15); output.CopyFromAscii(value); } } } if (((_bits & 67108864L) != 0)) { if (_rawServer != null) { output.CopyFrom(_rawServer, 0, _rawServer.Length); } else foreach (var value in _Server) { if (value != null) { output.CopyFrom(_headerBytes, 367, 10); output.CopyFromAscii(value); } } } if (((_bits & 134217728L) != 0)) { foreach (var value in _SetCookie) { if (value != null) { output.CopyFrom(_headerBytes, 377, 14); output.CopyFromAscii(value); } } } if (((_bits & 268435456L) != 0)) { foreach (var value in _Vary) { if (value != null) { output.CopyFrom(_headerBytes, 391, 8); output.CopyFromAscii(value); } } } if (((_bits & 536870912L) != 0)) { foreach (var value in _WWWAuthenticate) { if (value != null) { output.CopyFrom(_headerBytes, 399, 20); output.CopyFromAscii(value); } } } if (((_bits & 1073741824L) != 0)) { foreach (var value in _AccessControlAllowCredentials) { if (value != null) { output.CopyFrom(_headerBytes, 419, 36); output.CopyFromAscii(value); } } } if (((_bits & 2147483648L) != 0)) { foreach (var value in _AccessControlAllowHeaders) { if (value != null) { output.CopyFrom(_headerBytes, 455, 32); output.CopyFromAscii(value); } } } if (((_bits & 4294967296L) != 0)) { foreach (var value in _AccessControlAllowMethods) { if (value != null) { output.CopyFrom(_headerBytes, 487, 32); output.CopyFromAscii(value); } } } if (((_bits & 8589934592L) != 0)) { foreach (var value in _AccessControlAllowOrigin) { if (value != null) { output.CopyFrom(_headerBytes, 519, 31); output.CopyFromAscii(value); } } } if (((_bits & 17179869184L) != 0)) { foreach (var value in _AccessControlExposeHeaders) { if (value != null) { output.CopyFrom(_headerBytes, 550, 33); output.CopyFromAscii(value); } } } if (((_bits & 34359738368L) != 0)) { foreach (var value in _AccessControlMaxAge) { if (value != null) { output.CopyFrom(_headerBytes, 583, 26); output.CopyFromAscii(value); } } } }
/// <summary> /// Checks 9 bytes from <paramref name="begin"/> correspond to a known HTTP version. /// </summary> /// <remarks> /// A "known HTTP version" Is is either HTTP/1.0 or HTTP/1.1. /// Since those fit in 8 bytes, they can be optimally looked up by reading those bytes as a long. Once /// in that format, it can be checked against the known versions. /// The Known versions will be checked with the required '\r'. /// To optimize performance the HTTP/1.1 will be checked first. /// </remarks> /// <param name="begin">The iterator from which to start the known string lookup.</param> /// <param name="scan">If we found a valid method, then scan will be updated to new position</param> /// <param name="knownMethod">A reference to a pre-allocated known string, if the input matches any.</param> /// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns> public static bool GetKnownVersion(this MemoryPoolIterator2 begin, ref MemoryPoolIterator2 scan, out string knownVersion) { knownVersion = null; var value = begin.PeekLong(); if (value == _http11VersionLong) { knownVersion = Http11Version; scan.Skip(8); if (scan.Take() == '\r') { return true; } } else if (value == _http10VersionLong) { knownVersion = Http10Version; scan.Skip(8); if (scan.Take() == '\r') { return true; } } knownVersion = null; return false; }
public unsafe static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIterator2 end) { if (start.IsDefault || end.IsDefault) { return null; } var length = start.GetLength(end); if (length == 0) { return null; } // Bytes out of the range of ascii are treated as "opaque data" // and kept in string as a char value that casts to same input byte value // https://tools.ietf.org/html/rfc7230#section-3.2.4 var inputOffset = start.Index; var block = start.Block; var asciiString = new string('\0', length); fixed (char* outputStart = asciiString) { var output = outputStart; var remaining = length; var endBlock = end.Block; var endIndex = end.Index; while (true) { int following = (block != endBlock ? block.End : endIndex) - inputOffset; if (following > 0) { fixed (byte* blockStart = block.Array) { var input = blockStart + inputOffset; var i = 0; while (i < following - 11) { i += 12; *(output) = (char)*(input); *(output + 1) = (char)*(input + 1); *(output + 2) = (char)*(input + 2); *(output + 3) = (char)*(input + 3); *(output + 4) = (char)*(input + 4); *(output + 5) = (char)*(input + 5); *(output + 6) = (char)*(input + 6); *(output + 7) = (char)*(input + 7); *(output + 8) = (char)*(input + 8); *(output + 9) = (char)*(input + 9); *(output + 10) = (char)*(input + 10); *(output + 11) = (char)*(input + 11); output += 12; input += 12; } if (i < following - 5) { i += 6; *(output) = (char)*(input); *(output + 1) = (char)*(input + 1); *(output + 2) = (char)*(input + 2); *(output + 3) = (char)*(input + 3); *(output + 4) = (char)*(input + 4); *(output + 5) = (char)*(input + 5); output += 6; input += 6; } if (i < following - 3) { i += 4; *(output) = (char)*(input); *(output + 1) = (char)*(input + 1); *(output + 2) = (char)*(input + 2); *(output + 3) = (char)*(input + 3); output += 4; input += 4; } while (i < following) { i++; *output = (char)*input; output++; input++; } remaining -= following; } } if (remaining == 0) { break; } block = block.Next; inputOffset = block.Start; } } return asciiString; }
/// <summary> /// Checks that up to 8 bytes from <paramref name="begin"/> correspond to a known HTTP method. /// </summary> /// <remarks> /// A "known HTTP method" can be an HTTP method name defined in the HTTP/1.1 RFC. /// Since all of those fit in at most 8 bytes, they can be optimally looked up by reading those bytes as a long. Once /// in that format, it can be checked against the known method. /// The Known Methods (CONNECT, DELETE, GET, HEAD, PATCH, POST, PUT, OPTIONS, TRACE) are all less than 8 bytes /// and will be compared with the required space. A mask is used if the Known method is less than 8 bytes. /// To optimize performance the GET method will be checked first. /// </remarks> /// <param name="begin">The iterator from which to start the known string lookup.</param> /// <param name="scan">If we found a valid method, then scan will be updated to new position</param> /// <param name="knownMethod">A reference to a pre-allocated known string, if the input matches any.</param> /// <returns><c>true</c> if the input matches a known string, <c>false</c> otherwise.</returns> public static bool GetKnownMethod(this MemoryPoolIterator2 begin, ref MemoryPoolIterator2 scan, out string knownMethod) { knownMethod = null; var value = begin.PeekLong(); if ((value & _mask4Chars) == _httpGetMethodLong) { knownMethod = HttpGetMethod; scan.Skip(4); return true; } foreach (var x in _knownMethods) { if ((value & x.Item1) == x.Item2) { knownMethod = x.Item3; scan.Skip(knownMethod.Length + 1); return true; } } return false; }
public static ArraySegment<byte> GetArraySegment(this MemoryPoolIterator2 start, MemoryPoolIterator2 end) { if (start.IsDefault || end.IsDefault) { return default(ArraySegment<byte>); } if (end.Block == start.Block) { return new ArraySegment<byte>(start.Block.Array, start.Index, end.Index - start.Index); } var length = start.GetLength(end); var array = new byte[length]; start.CopyTo(array, 0, length, out length); return new ArraySegment<byte>(array, 0, length); }
public void ServerPipeListenForConnections() { var loop = new UvLoopHandle(_logger); var serverListenPipe = new UvPipeHandle(_logger); loop.Init(_uv); serverListenPipe.Init(loop, false); serverListenPipe.Bind(@"\\.\pipe\ServerPipeListenForConnections"); serverListenPipe.Listen(128, (_1, status, error, _2) => { var serverConnectionPipe = new UvPipeHandle(_logger); serverConnectionPipe.Init(loop, true); try { serverListenPipe.Accept(serverConnectionPipe); } catch (Exception) { serverConnectionPipe.Dispose(); return; } var writeRequest = new UvWriteReq(new KestrelTrace(new TestKestrelTrace())); writeRequest.Init(loop); var block = MemoryPoolBlock2.Create( new ArraySegment<byte>(new byte[] { 1, 2, 3, 4 }), dataPtr: IntPtr.Zero, pool: null, slab: null); var start = new MemoryPoolIterator2(block, 0); var end = new MemoryPoolIterator2(block, block.Data.Count); writeRequest.Write( serverConnectionPipe, start, end, 1, (_3, status2, error2, _4) => { writeRequest.Dispose(); serverConnectionPipe.Dispose(); serverListenPipe.Dispose(); block.Unpin(); }, null); }, null); var worker = new Thread(() => { var loop2 = new UvLoopHandle(_logger); var clientConnectionPipe = new UvPipeHandle(_logger); var connect = new UvConnectRequest(new KestrelTrace(new TestKestrelTrace())); loop2.Init(_uv); clientConnectionPipe.Init(loop2, true); connect.Init(loop2); connect.Connect(clientConnectionPipe, @"\\.\pipe\ServerPipeListenForConnections", (_1, status, error, _2) => { var buf = loop2.Libuv.buf_init(Marshal.AllocHGlobal(8192), 8192); connect.Dispose(); clientConnectionPipe.ReadStart( (_3, cb, _4) => buf, (_3, status2, _4) => { if (status2 == 0) { clientConnectionPipe.Dispose(); } }, null); }, null); loop2.Run(); loop2.Dispose(); }); worker.Start(); loop.Run(); loop.Dispose(); worker.Join(); }
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; } } }
private void ProducingCompleteNoPreComplete(MemoryPoolIterator2 end) { MemoryPoolBlock2 blockToReturn = null; lock (_returnLock) { Debug.Assert(!_lastStart.IsDefault); // If the socket has been closed, return the produced blocks // instead of advancing the now non-existent tail. if (_tail != null) { _tail = end.Block; _tail.End = end.Index; } else { blockToReturn = _lastStart.Block; } _lastStart = default(MemoryPoolIterator2); } if (blockToReturn != null) { ThreadPool.QueueUserWorkItem(_returnBlocks, blockToReturn); } }
public async Task SocketCanReadAndWrite() { var loop = new UvLoopHandle(_logger); loop.Init(_uv); var tcp = new UvTcpHandle(_logger); tcp.Init(loop); var address = ServerAddress.FromUrl("http://localhost:54321/"); tcp.Bind(address); tcp.Listen(10, (_, status, error, state) => { Console.WriteLine("Connected"); var tcp2 = new UvTcpHandle(_logger); tcp2.Init(loop); tcp.Accept(tcp2); var data = Marshal.AllocCoTaskMem(500); tcp2.ReadStart( (a, b, c) => tcp2.Libuv.buf_init(data, 500), (__, nread, state2) => { if (nread <= 0) { tcp2.Dispose(); } else { for (var x = 0; x < 2; x++) { var req = new UvWriteReq(new KestrelTrace(new TestKestrelTrace())); req.Init(loop); var block = MemoryPoolBlock2.Create( new ArraySegment<byte>(new byte[] { 65, 66, 67, 68, 69 }), dataPtr: IntPtr.Zero, pool: null, slab: null); var start = new MemoryPoolIterator2(block, 0); var end = new MemoryPoolIterator2(block, block.Data.Count); req.Write( tcp2, start, end, 1, (_1, _2, _3, _4) => { block.Unpin(); }, null); } } }, null); tcp.Dispose(); }, null); Console.WriteLine("Task.Run"); var t = Task.Run(async () => { var socket = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); #if DNX451 await Task.Factory.FromAsync( socket.BeginConnect, socket.EndConnect, new IPEndPoint(IPAddress.Loopback, 54321), null, TaskCreationOptions.None); await Task.Factory.FromAsync( socket.BeginSend, socket.EndSend, new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4, 5 }) }, SocketFlags.None, null, TaskCreationOptions.None); #else await socket.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 54321)); await socket.SendAsync(new[] { new ArraySegment<byte>(new byte[] { 1, 2, 3, 4, 5 }) }, SocketFlags.None); #endif socket.Shutdown(SocketShutdown.Send); var buffer = new ArraySegment<byte>(new byte[2048]); while (true) { #if DNX451 var count = await Task.Factory.FromAsync( socket.BeginReceive, socket.EndReceive, new[] { buffer }, SocketFlags.None, null, TaskCreationOptions.None); #else var count = await socket.ReceiveAsync(new[] { buffer }, SocketFlags.None); #endif Console.WriteLine("count {0} {1}", count, System.Text.Encoding.ASCII.GetString(buffer.Array, 0, count)); if (count <= 0) break; } socket.Dispose(); }); loop.Run(); loop.Dispose(); await t; }
/// <summary> /// Read the percent-encoding and try unescape it. /// /// The operation first peek at the character the <paramref name="scan"/> /// iterator points at. If it is % the <paramref name="scan"/> is then /// moved on to scan the following to characters. If the two following /// characters are hexadecimal literals they will be unescaped and the /// value will be returned. /// /// If the first character is not % the <paramref name="scan"/> iterator /// will be removed beyond the location of % and -1 will be returned. /// /// If the following two characters can't be successfully unescaped the /// <paramref name="scan"/> iterator will be move behind the % and -1 /// will be returned. /// </summary> /// <param name="scan">The value to read</param> /// <param name="end">The end of the sequence</param> /// <returns>The unescaped byte if success. Otherwise return -1.</returns> private static int UnescapePercentEncoding(ref MemoryPoolIterator2 scan, MemoryPoolIterator2 end) { if (scan.Take() != '%') { return -1; } var probe = scan; int value1 = ReadHex(ref probe, end); if (value1 == -1) { return -1; } int value2 = ReadHex(ref probe, end); if (value2 == -1) { return -1; } if (SkipUnescape(value1, value2)) { return -1; } scan = probe; return (value1 << 4) + value2; }
public static string GetUtf8String(this MemoryPoolIterator2 start, MemoryPoolIterator2 end) { if (start.IsDefault || end.IsDefault) { return(default(string)); } if (end.Block == start.Block) { return(_utf8.GetString(start.Block.Array, start.Index, end.Index - start.Index)); } var decoder = _utf8.GetDecoder(); var length = start.GetLength(end); var charLength = length * 2; var chars = new char[charLength]; var charIndex = 0; var block = start.Block; var index = start.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; } } }
/// <summary> /// Read the next char and convert it into hexadecimal value. /// /// The <paramref name="scan"/> iterator will be moved to the next /// byte no matter no matter whether the operation successes. /// </summary> /// <param name="scan">The value to read</param> /// <param name="end">The end of the sequence</param> /// <returns>The hexadecimal value if successes, otherwise -1.</returns> private static int ReadHex(ref MemoryPoolIterator2 scan, MemoryPoolIterator2 end) { if (CompareIterators(ref scan, ref end)) { return -1; } var value = scan.Take(); var isHead = (((value >= '0') && (value <= '9')) || ((value >= 'A') && (value <= 'F')) || ((value >= 'a') && (value <= 'f'))); if (!isHead) { return -1; } if (value <= '9') { return value - '0'; } else if (value <= 'F') { return (value - 'A') + 10; } else // a - f { return (value - 'a') + 10; } }
public void ProducingStartAndProducingCompleteCanBeUsedDirectly() { int nBuffers = 0; var nBufferWh = new ManualResetEventSlim(); var mockLibuv = new MockLibuv { OnWrite = (socket, buffers, triggerCompleted) => { nBuffers = buffers; nBufferWh.Set(); triggerCompleted(0); return 0; } }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); var ltp = new LoggingThreadPool(trace); var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace, ltp, new Queue<UvWriteReq>()); // block 1 var start = socketOutput.ProducingStart(); start.Block.End = start.Block.Data.Offset + start.Block.Data.Count; // block 2 var block2 = memory.Lease(); block2.End = block2.Data.Offset + block2.Data.Count; start.Block.Next = block2; var end = new MemoryPoolIterator2(block2, block2.End); socketOutput.ProducingComplete(end); // A call to Write is required to ensure a write is scheduled socketOutput.WriteAsync(default(ArraySegment<byte>)); Assert.True(nBufferWh.Wait(1000)); Assert.Equal(2, nBuffers); } }
public void Reset() { _lockedStart = default(MemoryPoolIterator2); _lockedEnd = default(MemoryPoolIterator2); _bufferCount = 0; ByteCount = 0; SocketShutdownSend = false; SocketDisconnect = false; WriteStatus = 0; WriteError = null; ShutdownSendStatus = 0; }
public unsafe static string GetAsciiString(this MemoryPoolIterator2 start, MemoryPoolIterator2 end) { if (start.IsDefault || end.IsDefault) { return(null); } var length = start.GetLength(end); if (length == 0) { return(null); } // Bytes out of the range of ascii are treated as "opaque data" // and kept in string as a char value that casts to same input byte value // https://tools.ietf.org/html/rfc7230#section-3.2.4 var inputOffset = start.Index; var block = start.Block; var asciiString = new string('\0', length); fixed(char *outputStart = asciiString) { var output = outputStart; var remaining = length; var endBlock = end.Block; var endIndex = end.Index; while (true) { int following = (block != endBlock ? block.End : endIndex) - inputOffset; if (following > 0) { fixed(byte *blockStart = block.Array) { var input = blockStart + inputOffset; var i = 0; while (i < following - 11) { i += 12; *(output) = (char)*(input); *(output + 1) = (char)*(input + 1); *(output + 2) = (char)*(input + 2); *(output + 3) = (char)*(input + 3); *(output + 4) = (char)*(input + 4); *(output + 5) = (char)*(input + 5); *(output + 6) = (char)*(input + 6); *(output + 7) = (char)*(input + 7); *(output + 8) = (char)*(input + 8); *(output + 9) = (char)*(input + 9); *(output + 10) = (char)*(input + 10); *(output + 11) = (char)*(input + 11); output += 12; input += 12; } if (i < following - 5) { i += 6; *(output) = (char)*(input); *(output + 1) = (char)*(input + 1); *(output + 2) = (char)*(input + 2); *(output + 3) = (char)*(input + 3); *(output + 4) = (char)*(input + 4); *(output + 5) = (char)*(input + 5); output += 6; input += 6; } if (i < following - 3) { i += 4; *(output) = (char)*(input); *(output + 1) = (char)*(input + 1); *(output + 2) = (char)*(input + 2); *(output + 3) = (char)*(input + 3); output += 4; input += 4; } while (i < following) { i++; *output = (char)*input; output++; input++; } remaining -= following; } } if (remaining == 0) { break; } block = block.Next; inputOffset = block.Start; } } return(asciiString); }
public MemoryPoolIterator2 ProducingStart() { lock (_returnLock) { Debug.Assert(_lastStart.IsDefault); if (_tail == null) { throw new IOException("The socket has been closed."); } _lastStart = new MemoryPoolIterator2(_tail, _tail.End); return _lastStart; } }
private static void Copy(MemoryPoolIterator2 head, MemoryPoolIterator2 tail, ref MemoryPoolIterator2 writer) { while (!CompareIterators(ref head, ref tail)) { writer.Put((byte)head.Take()); } }
public void ProducingComplete(MemoryPoolIterator2 end) { Debug.Assert(!_lastStart.IsDefault); int bytesProduced, buffersIncluded; BytesBetween(_lastStart, end, out bytesProduced, out buffersIncluded); lock (_contextLock) { _numBytesPreCompleted += bytesProduced; } ProducingCompleteNoPreComplete(end); }