Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
using System;
// include prerelease nuget package 'System.Memory'
public static class CharSpanExtensions
{
public static DateTime ConsumeDateTime(this ReadOnlySpan<char> source, out ReadOnlySpan<char> remaining)
{
// date time parsing is a nightmare, so take the heap allocation hit and have DateTime.Parse do it
int i = 0;
while (i < source.Length)
{
char character = source[i];
if (character == ',')
break;
i++;
}
remaining = source.ConsumeAt(i);
unsafe
{
fixed (char* data = &source[0])
{
return DateTime.Parse(new string(data, 0, i));
}
}
}
public static int ConsumeInt(this ReadOnlySpan<char> source, out ReadOnlySpan<char> remaining)
{
int result = 0;
int place = 1;
int i = 0;
while (i < source.Length)
{
char character = source[i];
if (character == ',')
break;
result += (source[i] - 48) * place;
place *= 10;
i++;
}
remaining = source.ConsumeAt(i);
return result;
}
public static float ConsumeFloat(this ReadOnlySpan<char> source, out ReadOnlySpan<char> remaining)
{
double result = 0; // keeping double percision while doing a bunch of math to reduce rounding errors
bool integer_component = true;
double divisor = 10.0;
int i = 0;
while (i < source.Length)
{
char character = source[i];
if (character == ',')
break;
int digit = source[i] - 48;
if (integer_component)
{
result *= 10;
result += digit;
}
else
{
result += digit / divisor;
divisor *= 10;
}
i++;
}
remaining = source.ConsumeAt(i);
return (float)result; // cast down to float
}
public static char ConsumeChar(this ReadOnlySpan<char> source, out ReadOnlySpan<char> remaining)
{
remaining = source.ConsumeAt(1);
return source[0];
}
public static string ConsumeString(this ReadOnlySpan<char> source, out ReadOnlySpan<char> remaining)
{
int i = 0;
while (i < source.Length)
{
char character = source[i];
if (character == ',')
break;
i++;
}
remaining = source.ConsumeAt(i);
unsafe
{
fixed (char* data = &source[0])
{
return new string(data, 0, i);
}
}
}
private static ReadOnlySpan<char> ConsumeAt(this ReadOnlySpan<char> source, int index)
{
if (index >= source.Length)
return new ReadOnlySpan<char>(); // empty
if (source[index] == ',')
{
if (source.Length >= index + 2)
{
if (source[index + 1] == '\\' && source[index + 2] == 'r')
return source.Slice(index + 5);
else if (source[index + 1] == '\r')
return source.Slice(index + 3);
}
return source.Slice(index + 1);
}
return source.Slice(index);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.