Last active
November 19, 2018 19:10
-
-
Save cameronism/6060314 to your computer and use it in GitHub Desktop.
High speed TextReader-like object. Almost 3x faster than StreamReader + MemoryStream
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /// <summary> | |
| /// High speed TextReader-like object, wraps a byte array directly rather than a Stream | |
| /// Almost 3x faster than StreamReader + MemoryStream in my tests | |
| /// </summary> | |
| public struct QuickAsciiReader | |
| { | |
| private readonly byte[] _Array; | |
| private int _Index; | |
| private readonly int _End; | |
| public QuickAsciiReader(byte[] bytes) : this(bytes, 0, bytes.Length) {} | |
| public QuickAsciiReader(byte[] bytes, int start, int count) | |
| { | |
| _Array = bytes; | |
| _Index = start; | |
| _End = start + count; | |
| } | |
| public int Remaining | |
| { | |
| get | |
| { | |
| var count = _End - _Index; | |
| return Math.Max(0, count); | |
| } | |
| } | |
| public string ReadLine() | |
| { | |
| return ReadLine(_Array, ref _Index, _End); | |
| } | |
| public int ReadBlock(char[] buffer, int index, int count) | |
| { | |
| return Read(buffer, index, count); | |
| } | |
| public int Read(char[] buffer, int index, int count) | |
| { | |
| if (count < 1 || _Index >= _End) return 0; | |
| if (count + _Index >= _End) count = _End - _Index; | |
| for (int i = 0; i < count; i++) | |
| { | |
| buffer[index + i] = (char)_Array[_Index + i]; | |
| } | |
| _Index += count; | |
| return count; | |
| } | |
| public bool ReadHeaderLine(out string key, out string value) | |
| { | |
| return ReadHeaderLine(':', ' ', out key, out value); | |
| } | |
| public bool ReadHeaderLine(char separator, char padding, out string key, out string value) | |
| { | |
| value = null; | |
| int start = _Index; | |
| int len = ReadLineInternal(_Array, ref _Index, _End); | |
| if (len < 0) | |
| { | |
| key = null; | |
| return false; | |
| } | |
| if (len == 0) | |
| { | |
| key = String.Empty; | |
| return true; | |
| } | |
| int next = Array.IndexOf<byte>(_Array, (byte)separator, start, len); | |
| if (next == -1) | |
| { | |
| key = GetString(_Array, start, len); | |
| return true; | |
| } | |
| key = GetString(_Array, start, next - start); | |
| int lineEnd = start + len; | |
| do { | |
| next++; | |
| } while (next < lineEnd && _Array[next] == (byte)padding); | |
| len = lineEnd - next; | |
| value = len == 0 ? String.Empty : GetString(_Array, next, len); | |
| return true; | |
| } | |
| public int Read() | |
| { | |
| int index = _Index; | |
| if (index >= _End) return -1; | |
| _Index = index + 1; | |
| return _Array[index]; | |
| } | |
| public int Peek() | |
| { | |
| return _Index >= _End ? -1 : _Array[_Index]; | |
| } | |
| public string ReadToEnd() | |
| { | |
| int count = _End - _Index; | |
| if (count < 1) return String.Empty; | |
| var s = GetString(_Array, _Index, count); | |
| _Index += count; | |
| return s; | |
| } | |
| // Encoding.ASCII.GetBytes can be used instead - about 25% slower in my tests | |
| public unsafe static string GetString(byte[] bytes, int offset, int length) | |
| { | |
| if (length == 0) return String.Empty; | |
| string text = new String('\0', length); | |
| fixed (byte* bytesPtr = bytes) | |
| fixed (char* stringPtr = text) | |
| { | |
| char* pDst = stringPtr; | |
| byte* pSrc = bytesPtr + offset; | |
| for (int i = 0; i < length; i++) | |
| { | |
| *pDst = (char)*pSrc; | |
| pSrc++; | |
| pDst++; | |
| } | |
| } | |
| return text; | |
| } | |
| const byte CR = (byte)'\r'; | |
| const byte LF = (byte)'\n'; | |
| public static string ReadLine(byte[] bytes, ref int index, int stop) | |
| { | |
| int start = index; | |
| int len = ReadLineInternal(bytes, ref index, stop); | |
| return len > 0 ? GetString(bytes, start, len) : | |
| len == 0 ? String.Empty : | |
| null; | |
| } | |
| static int ReadLineInternal(byte[] bytes, ref int index, int stop) | |
| { | |
| int len; | |
| int eol; | |
| len = stop - index; | |
| if (len <= 0) return -1; | |
| eol = Array.IndexOf<byte>(bytes, LF, index, len); | |
| if (eol == -1) | |
| { | |
| index = stop; | |
| return len; | |
| } | |
| len = eol - index; | |
| if (eol > index && bytes[eol - 1] == CR) len--; | |
| index = eol + 1; | |
| return len < 1 ? 0 : len; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment