Skip to content

Instantly share code, notes, and snippets.

@cameronism
Last active November 19, 2018 19:10
Show Gist options
  • Select an option

  • Save cameronism/6060314 to your computer and use it in GitHub Desktop.

Select an option

Save cameronism/6060314 to your computer and use it in GitHub Desktop.
High speed TextReader-like object. Almost 3x faster than StreamReader + MemoryStream
/// <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