Skip to content

Instantly share code, notes, and snippets.

@leandromoh
Last active September 18, 2021 03:13
Show Gist options
  • Save leandromoh/8397fa43bb489c9c10a954e63a70d8cb to your computer and use it in GitHub Desktop.
Save leandromoh/8397fa43bb489c9c10a954e63a70d8cb to your computer and use it in GitHub Desktop.
public static IEnumerable<Memory<char>> TryReadLine(TextReader reader)
{
var bufferLength = (int)Math.Pow(2, 16);
var buffer = ArrayPool<char>.Shared.Rent(bufferLength);
var i = 0;
var j = 0;
var state = State.BeforeField;
int c;
FillBufferAsync(true);
int FillBufferAsync(bool initial)
{
var len = i - j;
if (initial == false)
{
Array.Copy(buffer, j, buffer, 0, len);
}
var totalRead = reader.ReadAsync(buffer, len, bufferLength - len).GetAwaiter().GetResult();
bufferLength = len + totalRead;
i = len;
j = 0;
return totalRead;
}
var tried = false;
int Peek()
{
if (i < bufferLength)
return buffer[i];
tried = true;
return -1;
}
preloop:
j = i;
state = State.BeforeField;
reloop:
void Loop()
{
while (Peek() >= 0)
{
c = buffer[i++];
switch (state)
{
case State.BeforeField:
switch (c)
{
case '"':
state = State.InQuotedField;
break;
case ',':
// fields.Add(string.Empty);
break;
case '\r':
// fields.Add(string.Empty);
if (Peek() == '\n')
{
i++;
}
state = State.LineEnd;
return;
case '\n':
// fields.Add(string.Empty);
state = State.LineEnd;
return;
default:
// builder.Append((char)c);
state = State.InField;
break;
}
break;
case State.InField:
switch (c)
{
case ',':
// AddField(fields, builder);
state = State.BeforeField;
break;
case '\r':
// AddField(fields, builder);
if (Peek() == '\n')
{
i++;
}
state = State.LineEnd;
return;
case '\n':
// AddField(fields, builder);
state = State.LineEnd;
return;
default:
// builder.Append((char)c);
break;
}
break;
case State.InQuotedField:
switch (c)
{
case '"':
var nc = Peek();
switch (nc)
{
case '"':
// builder.Append('"');
i++;
break;
case ',':
i++;
// AddField(fields, builder);
state = State.BeforeField;
break;
case '\r':
i++;
// AddField(fields, builder);
if (Peek() == '\n')
{
i++;
}
state = State.LineEnd;
return;
case '\n':
i++;
// AddField(fields, builder);
state = State.LineEnd;
return;
default:
throw new InvalidDataException("Corrupt field found. A double quote is not escaped or there is extra data after a quoted field.");
}
break;
default:
// builder.Append((char)c);
break;
}
break;
// case State.LineEnd:
case State.LineEnd:
state = State.BeforeField;
break;
default:
throw new NotImplementedException();
}
}
}
Loop();
afterLoop:
if (i < bufferLength == false)
{
FillBufferAsync(false);
if (i == 0)
{
ArrayPool<char>.Shared.Return(buffer);
yield break;
}
if (state != State.LineEnd)
goto reloop;
}
switch (state)
{
case State.BeforeField:
case State.InField:
case State.LineEnd:
var ff = buffer.AsMemory(j, i - j);
j = i;
state = State.BeforeField;
var t1 = Task.Run(Loop);
yield return ff;
t1.Wait();
if (tried)
{
tried = false;
FillBufferAsync(false);
if (state == State.LineEnd)
state = State.InField;
goto reloop;
}
goto afterLoop;
case State.InQuotedField:
throw new InvalidDataException("When the line ends with a quoted field, the last character should be an unescaped double quote.");
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment