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; } } } }
/// <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="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, out string knownVersion) { knownVersion = null; var value = begin.PeekLong(); if (value == _http11VersionLong) { knownVersion = Http11Version; } else if (value == _http10VersionLong) { knownVersion = Http10Version; } if (knownVersion != null) { begin.Skip(knownVersion.Length); if (begin.Peek() != '\r') { knownVersion = null; } } return(knownVersion != null); }
/// <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="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, out string knownMethod) { knownMethod = null; ulong value; if (!begin.TryPeekLong(out value)) { return(false); } if ((value & _mask4Chars) == _httpGetMethodLong) { knownMethod = HttpMethods.Get; return(true); } foreach (var x in _knownMethods) { if ((value & x.Item1) == x.Item2) { knownMethod = x.Item3; return(true); } } return(false); }
public static ArraySegment <byte> PeekArraySegment(this MemoryPoolIterator iter) { if (iter.IsDefault || iter.IsEnd) { return(default(ArraySegment <byte>)); } if (iter.Index < iter.Block.End) { return(new ArraySegment <byte>(iter.Block.Array, iter.Index, iter.Block.End - iter.Index)); } var block = iter.Block.Next; while (block != null) { if (block.Start < block.End) { return(new ArraySegment <byte>(block.Array, block.Start, block.End - block.Start)); } block = block.Next; } // The following should be unreachable due to the IsEnd check above. throw new InvalidOperationException("This should be unreachable!"); }
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); } 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; var outputOffset = 0; while (true) { int following = (block != endBlock ? block.End : endIndex) - inputOffset; if (following > 0) { if (!AsciiUtilities.TryGetAsciiString(block.DataFixedPtr + inputOffset, output + outputOffset, following)) { throw new BadHttpRequestException("The input string contains non-ASCII or null characters."); } outputOffset += following; remaining -= following; } if (remaining == 0) { break; } block = block.Next; inputOffset = block.Start; } } return(asciiString); }
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)); }
public static string GetAsciiStringEscaped(this MemoryPoolIterator start, MemoryPoolIterator end, int maxChars) { var sb = new StringBuilder(); var scan = start; while (maxChars > 0 && (scan.Block != end.Block || scan.Index != end.Index)) { var ch = scan.Take(); sb.Append(ch < 0x20 || ch >= 0x7F ? $"<0x{ch.ToString("X2")}>" : ((char)ch).ToString()); maxChars--; } if (scan.Block != end.Block || scan.Index != end.Index) { sb.Append("..."); } return(sb.ToString()); }
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; } } }
public unsafe int Seek( ref Vector <byte> byte0Vector, ref Vector <byte> byte1Vector, ref Vector <byte> byte2Vector, ref MemoryPoolIterator limit) { if (IsDefault) { return(-1); } var block = _block; var index = _index; var wasLastBlock = block.Next == null; var following = block.End - index; byte[] array; int byte0Index = int.MaxValue; int byte1Index = int.MaxValue; int byte2Index = int.MaxValue; var byte0 = byte0Vector[0]; var byte1 = byte1Vector[0]; var byte2 = byte2Vector[0]; while (true) { while (following == 0) { if ((block == limit.Block && index > limit.Index) || wasLastBlock) { _block = block; // Ensure iterator is left at limit position _index = limit.Index; return(-1); } block = block.Next; index = block.Start; wasLastBlock = block.Next == null; following = block.End - index; } array = block.Array; while (following > 0) { // Need unit tests to test Vector path #if !DEBUG // Check will be Jitted away https://github.com/dotnet/coreclr/issues/1079 if (Vector.IsHardwareAccelerated) { #endif if (following >= _vectorSpan) { var data = new Vector <byte>(array, index); var byte0Equals = Vector.Equals(data, byte0Vector); var byte1Equals = Vector.Equals(data, byte1Vector); var byte2Equals = Vector.Equals(data, byte2Vector); if (!byte0Equals.Equals(Vector <byte> .Zero)) { byte0Index = FindFirstEqualByte(ref byte0Equals); } if (!byte1Equals.Equals(Vector <byte> .Zero)) { byte1Index = FindFirstEqualByte(ref byte1Equals); } if (!byte2Equals.Equals(Vector <byte> .Zero)) { byte2Index = FindFirstEqualByte(ref byte2Equals); } if (byte0Index == int.MaxValue && byte1Index == int.MaxValue && byte2Index == int.MaxValue) { following -= _vectorSpan; index += _vectorSpan; if (block == limit.Block && index > limit.Index) { _block = block; // Ensure iterator is left at limit position _index = limit.Index; return(-1); } continue; } _block = block; int toReturn, toMove; if (byte0Index < byte1Index) { if (byte0Index < byte2Index) { toReturn = byte0; toMove = byte0Index; } else { toReturn = byte2; toMove = byte2Index; } } else { if (byte1Index < byte2Index) { toReturn = byte1; toMove = byte1Index; } else { toReturn = byte2; toMove = byte2Index; } } _index = index + toMove; if (block == limit.Block && _index > limit.Index) { // Ensure iterator is left at limit position _index = limit.Index; return(-1); } return(toReturn); } // Need unit tests to test Vector path #if !DEBUG } #endif var pCurrent = (block.DataFixedPtr + index); var pEnd = block == limit.Block ? block.DataFixedPtr + limit.Index + 1 : pCurrent + following; do { if (*pCurrent == byte0) { _block = block; _index = index; return(byte0); } if (*pCurrent == byte1) { _block = block; _index = index; return(byte1); } if (*pCurrent == byte2) { _block = block; _index = index; return(byte2); } pCurrent++; index++; } while (pCurrent != pEnd); following = 0; break; } } }
public int Seek(ref Vector <byte> byte0Vector, ref Vector <byte> byte1Vector, ref Vector <byte> byte2Vector) { var limit = new MemoryPoolIterator(); return(Seek(ref byte0Vector, ref byte1Vector, ref byte2Vector, ref limit)); }