Skip to content

Commit

Permalink
Add .NET Standard 2.0 target.
Browse files Browse the repository at this point in the history
  • Loading branch information
RichardD2 committed Dec 7, 2023
1 parent eb97e62 commit bf1f8cc
Show file tree
Hide file tree
Showing 31 changed files with 571 additions and 191 deletions.
2 changes: 1 addition & 1 deletion RecordParser.Benchmark/Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private static T ProcessSequence<T>(ReadOnlySequence<byte> sequence, FuncSpanT<T

if (sequence.IsSingleSegment)
{
return Parse(sequence.FirstSpan, parser);
return Parse(sequence.First.Span, parser);
}

var length = (int)sequence.Length;
Expand Down
10 changes: 5 additions & 5 deletions RecordParser.Benchmark/CursivelyPersonVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public override void VisitEndOfField(ReadOnlySpan<byte> chunk)
if (_dataBufUsed != 0)
{
VisitPartialFieldContents(chunk);
chunk = _dataBuf.AsSpan(.._dataBufUsed);
chunk = _dataBuf.AsSpan(0, _dataBufUsed);
_dataBufUsed = 0;
}

Expand All @@ -55,8 +55,8 @@ public override void VisitEndOfField(ReadOnlySpan<byte> chunk)
case 3:
// M/d/yyyy format is not supported by this for some reason.
////_ = Utf8Parser.TryParse(chunk, out _person.birthday, out _);
Span<char> birthdayChars = _decodeBuf.AsSpan(..Encoding.UTF8.GetChars(chunk, _decodeBuf));
_person.birthday = DateTime.Parse(birthdayChars, DateTimeFormatInfo.InvariantInfo);
Span<char> birthdayChars = _decodeBuf.AsSpan(0, Encoding.UTF8.GetChars(chunk, _decodeBuf));
_person.birthday = Parse.DateTime(birthdayChars, DateTimeFormatInfo.InvariantInfo);
break;

case 4:
Expand All @@ -67,7 +67,7 @@ public override void VisitEndOfField(ReadOnlySpan<byte> chunk)
// N.B.: there are ways to improve the efficiency of this for earlier
// targets, but I think it's fine for performance-sensitive applications to
// have to upgrade to .NET 6.0 or higher...
_person.gender = Enum.Parse<Gender>(Encoding.UTF8.GetString(chunk));
_person.gender = Parse.Enum<Gender>(Encoding.UTF8.GetString(chunk).AsSpan());
#endif
break;

Expand All @@ -91,7 +91,7 @@ public override void VisitEndOfRecord()
public override void VisitPartialFieldContents(ReadOnlySpan<byte> chunk)
{
EnsureCapacity(_dataBufUsed + chunk.Length);
chunk.CopyTo(_dataBuf.AsSpan(_dataBufUsed..));
chunk.CopyTo(_dataBuf.AsSpan(_dataBufUsed));
_dataBufUsed += chunk.Length;
}

Expand Down
14 changes: 7 additions & 7 deletions RecordParser.Benchmark/FixedLengthReaderBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public async Task Read_FixedLength_ManualString()
name = line.Substring(2, 30).Trim(),
age = int.Parse(line.Substring(32, 2)),
birthday = DateTime.Parse(line.Substring(39, 10), CultureInfo.InvariantCulture),
gender = Enum.Parse<Gender>(line.Substring(85, 6)),
gender = Parse.Enum<Gender>(line.Substring(85, 6).AsSpan()),
email = line.Substring(92, 22).Trim(),
children = bool.Parse(line.Substring(121, 5))
};
Expand Down Expand Up @@ -151,12 +151,12 @@ await ProcessFlatFile((ReadOnlySpan<char> line) =>
return new Person
{
alfa = line[0],
name = new string(line.Slice(2, 30).Trim()),
age = int.Parse(line.Slice(32, 2)),
birthday = DateTime.Parse(line.Slice(39, 10), CultureInfo.InvariantCulture),
gender = Enum.Parse<Gender>(line.Slice(85, 6)),
email = new string(line.Slice(92, 22).Trim()),
children = bool.Parse(line.Slice(121, 5))
name = line.Slice(2, 30).Trim().ToString(),
age = Parse.Int32(line.Slice(32, 2)),
birthday = Parse.DateTime(line.Slice(39, 10), CultureInfo.InvariantCulture),
gender = Parse.Enum<Gender>(line.Slice(85, 6)),
email = line.Slice(92, 22).Trim().ToString(),
children = Parse.Boolean(line.Slice(121, 5))
};
});

Expand Down
53 changes: 53 additions & 0 deletions RecordParser.Benchmark/Parse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Globalization;
using System.Runtime.CompilerServices;

namespace RecordParser.Benchmark
{
internal static class Parse
{
#if NETSTANDARD2_0 || NETFRAMEWORK
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string ProcessSpan(ReadOnlySpan<char> span) => span.ToString();
#else
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static ReadOnlySpan<char> ProcessSpan(ReadOnlySpan<char> span) => span;
#endif

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte Byte(ReadOnlySpan<char> utf8Text, IFormatProvider provider = null) => byte.Parse(ProcessSpan(utf8Text), NumberStyles.Integer, provider);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static sbyte SByte(ReadOnlySpan<char> utf8Text, IFormatProvider provider = null) => sbyte.Parse(ProcessSpan(utf8Text), NumberStyles.Integer, provider);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double Double(ReadOnlySpan<char> utf8Text, IFormatProvider provider = null) => double.Parse(ProcessSpan(utf8Text), NumberStyles.AllowThousands | NumberStyles.Float, provider);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float Single(ReadOnlySpan<char> utf8Text, IFormatProvider provider = null) => float.Parse(ProcessSpan(utf8Text), NumberStyles.AllowThousands | NumberStyles.Float, provider);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Int32(ReadOnlySpan<char> utf8Text, IFormatProvider provider = null) => int.Parse(ProcessSpan(utf8Text), NumberStyles.Integer, provider);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Guid Guid(ReadOnlySpan<char> utf8Text) => System.Guid.Parse(ProcessSpan(utf8Text));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static DateTime DateTime(ReadOnlySpan<char> utf8Text, IFormatProvider provider = null) => System.DateTime.Parse(ProcessSpan(utf8Text), provider, DateTimeStyles.AllowWhiteSpaces);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool Boolean(ReadOnlySpan<char> utf8Text) => bool.Parse(ProcessSpan(utf8Text));

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TEnum Enum<TEnum>(ReadOnlySpan<char> utf8Text) where TEnum : struct, Enum
{
#if NETSTANDARD2_0 || NETFRAMEWORK
return (TEnum)System.Enum.Parse(typeof(TEnum), utf8Text.ToString());
#elif NETSTANDARD2_1
return System.Enum.Parse<TEnum>(utf8Text.ToString());
#else
return System.Enum.Parse<TEnum>(utf8Text);
#endif
}
}
}
13 changes: 12 additions & 1 deletion RecordParser.Benchmark/RecordParser.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net8.0</TargetFrameworks>
<TargetFrameworks>net472;net6.0;net7.0;net8.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<!--<DefineConstants>TEST_ALL</DefineConstants>-->
</PropertyGroup>

<PropertyGroup Condition="'$(TargetFramework)'=='net472'">
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Ben.StringIntern" Version="0.1.8" />
<PackageReference Include="Cursively" Version="1.2.0" />
Expand All @@ -19,6 +23,13 @@
<PackageReference Include="ZString" Version="2.5.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net472'">
<PackageReference Include="PolySharp" Version="1.14.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\RecordParser\RecordParser.csproj" />
</ItemGroup>
Expand Down
72 changes: 72 additions & 0 deletions RecordParser.Benchmark/Shims.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#if NETSTANDARD2_0 || NETFRAMEWORK

using System;
using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace RecordParser.Benchmark
{
internal static class Shims
{
public static int GetChars(this Encoding encoding, ReadOnlySpan<byte> bytes, Span<char> chars)
{
unsafe
{
fixed (byte* b = &MemoryMarshal.GetReference(bytes))
{
int charCount = encoding.GetCharCount(b, bytes.Length);
if (charCount > chars.Length) return 0;

fixed (char* c = &MemoryMarshal.GetReference(chars))
{
return encoding.GetChars(b, bytes.Length, c, chars.Length);
}
}
}
}

public static string GetString(this Encoding encoding, scoped ReadOnlySpan<byte> bytes)
{
if (bytes.IsEmpty) return string.Empty;

unsafe
{
fixed (byte* pB = &MemoryMarshal.GetReference(bytes))
{
return encoding.GetString(pB, bytes.Length);
}
}
}

public static Task WriteLineAsync(this TextWriter writer, ReadOnlyMemory<char> value, CancellationToken cancellationToken = default)
{
if (MemoryMarshal.TryGetArray(value, out var arraySegment))
{
return arraySegment.Array is null ? Task.CompletedTask : writer.WriteLineAsync(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
}

return Impl(writer, value);

static async Task Impl(TextWriter writer, ReadOnlyMemory<char> value)
{
var pool = ArrayPool<char>.Shared;
var array = pool.Rent(value.Length);
try
{
value.CopyTo(array.AsMemory());
await writer.WriteLineAsync(array, 0, value.Length);
}
finally
{
pool.Return(array);
}
}
}
}
}

#endif
16 changes: 8 additions & 8 deletions RecordParser.Benchmark/VariableLengthReaderBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ public async Task Read_VariableLength_ManualString()
{
if (i++ == LimitRecord) return;

var coluns = line.Split(",");
var coluns = line.Split(',');
var person = new Person()
{
id = Guid.Parse(coluns[0]),
name = coluns[1].Trim(),
age = int.Parse(coluns[2]),
birthday = DateTime.Parse(coluns[3], CultureInfo.InvariantCulture),
gender = Enum.Parse<Gender>(coluns[4]),
gender = Parse.Enum<Gender>(coluns[4].AsSpan()),
email = coluns[5].Trim(),
children = bool.Parse(coluns[7])
};
Expand Down Expand Up @@ -203,7 +203,7 @@ Person PersonFactory(Func<int, string> getColumnValue)
name = getColumnValue(1).Trim(),
age = int.Parse(getColumnValue(2)),
birthday = DateTime.Parse(getColumnValue(3), CultureInfo.InvariantCulture),
gender = Enum.Parse<Gender>(getColumnValue(4)),
gender = Parse.Enum<Gender>(getColumnValue(4).AsSpan()),
email = getColumnValue(5).Trim(),
children = bool.Parse(getColumnValue(7))
};
Expand Down Expand Up @@ -262,13 +262,13 @@ await ProcessCSVFile((ReadOnlySpan<char> line) =>

return new Person
{
id = Guid.Parse(id),
id = Parse.Guid(id),
name = name.ToString(),
age = int.Parse(age),
birthday = DateTime.Parse(birthday, DateTimeFormatInfo.InvariantInfo),
gender = Enum.Parse<Gender>(gender),
age = Parse.Int32(age),
birthday = Parse.DateTime(birthday, DateTimeFormatInfo.InvariantInfo),
gender = Parse.Enum<Gender>(gender),
email = email.ToString(),
children = bool.Parse(children)
children = Parse.Boolean(children)
};
});
}
Expand Down
2 changes: 1 addition & 1 deletion RecordParser.Benchmark/VariableLengthWriterBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public async Task Write_VariableLength_ManualString()
sb.Append(";");
sb.Append(person.children);

await streamWriter.WriteLineAsync(sb);
await streamWriter.WriteLineAsync(sb.ToString());
sb.Clear();
}
}
Expand Down
4 changes: 2 additions & 2 deletions RecordParser.Test/FileWriterTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void Write_csv_file(int repeat, bool parallel, bool ordered)

var reader = new VariableLengthReaderBuilder<(string Name, DateTime Birthday, decimal Money, Color Color, int Index)>()
.Map(x => x.Name, 0)
.Map(x => x.Birthday, 1, value => new DateTime(long.Parse(value)))
.Map(x => x.Birthday, 1, value => new DateTime(Parse.Int64(value)))
.Map(x => x.Money, 2)
.Map(x => x.Color, 3)
.Map(x => x.Index, 4)
Expand Down Expand Up @@ -97,4 +97,4 @@ public void Write_csv_file(int repeat, bool parallel, bool ordered)
items.Should().BeEquivalentTo(expectedItems);
}
}
}
}
Loading

0 comments on commit bf1f8cc

Please sign in to comment.