/// <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="knownVersion">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 MemoryPoolIterator begin, ref MemoryPoolIterator 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 int GetLength(MemoryPoolIterator 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 static ArraySegment <byte> GetArraySegment(this MemoryPoolIterator start, MemoryPoolIterator 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)); }
/// <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 MemoryPoolIterator begin, ref MemoryPoolIterator 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 unsafe static string GetAsciiString(this MemoryPoolIterator start, MemoryPoolIterator 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 static string GetUtf8String(this MemoryPoolIterator start, MemoryPoolIterator 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; } } }