Last active
September 18, 2021 03:13
-
-
Save leandromoh/8397fa43bb489c9c10a954e63a70d8cb to your computer and use it in GitHub Desktop.
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
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