Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2633,6 +2633,10 @@ namespace nietras.SeparatedValues
where T : System.ISpanFormattable { }
public void Format<T>(T value, System.ReadOnlySpan<char> format)
where T : System.ISpanFormattable { }
public void FormatEnum<T>(T value)
where T : struct, System.Enum { }
public void FormatEnum<T>(T value, System.ReadOnlySpan<char> format)
where T : struct, System.Enum { }
public void Set(System.ReadOnlySpan<byte> utf8Span) { }
public void Set(System.ReadOnlySpan<char> span) { }
public void Set([System.Runtime.CompilerServices.InterpolatedStringHandlerArgument("")] ref nietras.SeparatedValues.SepWriter.Col.FormatInterpolatedStringHandler handler) { }
Expand Down
80 changes: 73 additions & 7 deletions src/Sep.Test/SepWriterColTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ public class SepWriterColTest
[TestMethod]
public async ValueTask SepWriterColTest_ColIndex()
{
await Run(col => Assert.AreEqual(0, col.ColIndex), null);
await Run(static col => Assert.AreEqual(0, col.ColIndex),
expectedColValue: null);
}

[TestMethod]
public async ValueTask SepWriterColTest_ColName()
{
await Run(col => Assert.AreEqual(ColName, col.ColName), null);
await Run(static col => Assert.AreEqual(ColName, col.ColName),
expectedColValue: null);
}

[TestMethod]
Expand All @@ -46,13 +48,15 @@ public async ValueTask SepWriterColTest_Set_String_Long()
[TestMethod]
public async ValueTask SepWriterColTest_Set_Utf8Span()
{
await Run(col => col.Set(Encoding.UTF8.GetBytes(ColText)));
var bytes = Encoding.UTF8.GetBytes(ColText);
await Run(col => col.Set(bytes));
}

[TestMethod]
public async ValueTask SepWriterColTest_Set_Utf8Span_Long()
{
await Run(col => col.Set(Encoding.UTF8.GetBytes(ColTextLong)), ColTextLong);
var bytes = Encoding.UTF8.GetBytes(ColTextLong);
await Run(col => col.Set(bytes), ColTextLong);
}

[TestMethod]
Expand Down Expand Up @@ -92,13 +96,15 @@ public async ValueTask SepWriterColTest_Set_InterpolatedString_F2()
[TestMethod]
public async ValueTask SepWriterColTest_Set_InterpolatedString_F2_CultureInfoAsConfig()
{
await Run(col => col.Set($"{ColValue:F2}"), ColText + ",00", CultureInfo.GetCultureInfo("da-DK"));
var cultureInfo = CultureInfo.GetCultureInfo("da-DK");
await Run(col => col.Set($"{ColValue:F2}"), ColText + ",00", cultureInfo);
}

[TestMethod]
public async ValueTask SepWriterColTest_Set_InterpolatedString_F2_CultureInfoAsParam()
{
await Run(col => col.Set(CultureInfo.GetCultureInfo("da-DK"), $"{ColValue:F2}"), ColText + ",00");
var cultureInfo = CultureInfo.GetCultureInfo("da-DK");
await Run(col => col.Set(cultureInfo, $"{ColValue:F2}"), ColText + ",00");
}

[TestMethod]
Expand Down Expand Up @@ -176,6 +182,52 @@ public async ValueTask SepWriterColTest_Format_Long()
await Run(col => col.Format(f), f.Text);
}

enum TestEnum { Aaaa = 42 }
[TestMethod]
public async ValueTask SepWriterColTest_Set_Enum()
{
await Run(col => col.Set($"{TestEnum.Aaaa}"), TestEnum.Aaaa.ToString(), expectedAllocatedBytes: 0);
}
[TestMethod]
public async ValueTask SepWriterColTest_Format_Enum()
{
// Unfortunately, formatting enum via T : ISpanFormatable allocates on
// each format, due to boxing enum. Prefer FormatEnum/Set(..) instead.
var expectedAllocatedBytes = System.Runtime.CompilerServices.Unsafe.SizeOf<nint>() * 3;
await Run(col => col.Format(TestEnum.Aaaa), TestEnum.Aaaa.ToString(),
expectedAllocatedBytes: expectedAllocatedBytes);
await Run(col => col.Format(TestEnum.Aaaa, "D"), ((int)TestEnum.Aaaa).ToString(),
expectedAllocatedBytes: expectedAllocatedBytes);
}
[TestMethod]
public async ValueTask SepWriterColTest_FormatEnum_Enum()
{
await Run(col => col.FormatEnum(TestEnum.Aaaa), TestEnum.Aaaa.ToString(), expectedAllocatedBytes: 0);
await Run(col => col.FormatEnum(TestEnum.Aaaa, "D"), ((int)TestEnum.Aaaa).ToString(), expectedAllocatedBytes: 0);
}

[Flags]
enum LongFlags
{
Flag000 = 1 << 0, Flag001 = 1 << 1, Flag002 = 1 << 2, Flag003 = 1 << 3,
Flag004 = 1 << 4, Flag005 = 1 << 5, Flag006 = 1 << 6, Flag007 = 1 << 7,
Flag008 = 1 << 8, Flag009 = 1 << 9, Flag010 = 1 << 10, Flag011 = 1 << 11,
Flag012 = 1 << 12, Flag013 = 1 << 13, Flag014 = 1 << 14, Flag015 = 1 << 15,
Flag016 = 1 << 16, Flag017 = 1 << 17, Flag018 = 1 << 18, Flag019 = 1 << 19,
Flag020 = 1 << 20, Flag021 = 1 << 21, Flag022 = 1 << 22, Flag023 = 1 << 23,
Flag024 = 1 << 24, Flag025 = 1 << 25, Flag026 = 1 << 26, Flag027 = 1 << 27,
Flag028 = 1 << 28, Flag029 = 1 << 29, Flag030 = 1 << 30,
}
[TestMethod]
public async ValueTask SepWriterColTest_FormatEnum_Enum_Long()
{
var value = (LongFlags)0x7FFFFFFF;
var expected = value.ToString();
Assert.IsGreaterThan(256, expected.Length);

await Run(col => col.FormatEnum(value), expected);
}

public class LongSpanFormattable : ISpanFormattable
{
public string Text { get; } = ColTextLong;
Expand Down Expand Up @@ -230,7 +282,8 @@ static void Assert(string expectedCol, SepWriter writer)
}
}

static async ValueTask Run(SepWriter.ColAction action, string? expectedColValue = ColText, CultureInfo? cultureInfo = null)
static async ValueTask Run(SepWriter.ColAction action, string? expectedColValue = ColText,
CultureInfo? cultureInfo = null, long? expectedAllocatedBytes = null)
{
Func<SepWriter>[] createWriters =
[
Expand All @@ -245,7 +298,20 @@ static async ValueTask Run(SepWriter.ColAction action, string? expectedColValue
using var writer = createWriter();
{
using var row = writer.NewRow();
// First row, first access will allocate ColImpl and supports, so warming up
action(row[ColName]);

var a0 = GC.GetAllocatedBytesForCurrentThread();
var col = row[ColName];
var a1 = GC.GetAllocatedBytesForCurrentThread();
action(col);
var a2 = GC.GetAllocatedBytesForCurrentThread();
// Getting col should always allocate zero bytes
Assert.AreEqual(0, a1 - a0);
if (expectedAllocatedBytes.HasValue)
{
Assert.AreEqual(expectedAllocatedBytes.Value, a2 - a1);
}
}
AssertCol(expectedColValue, writer);
}
Expand Down
21 changes: 21 additions & 0 deletions src/Sep/SepWriter.Col.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,27 @@ public void Format<T>(T value, ReadOnlySpan<char> format) where T : ISpanFormatt
MarkSet();
}

public void FormatEnum<T>(T value) where T : struct, Enum
{
FormatEnum(value, null);
}
public void FormatEnum<T>(T value, ReadOnlySpan<char> format) where T : struct, Enum
{
var impl = _impl;
impl.Clear();
if (Enum.TryFormat(value, impl._buffer, out var charsWritten, format))
{
impl._position = charsWritten;
}
else
{
var handler = new FormatInterpolatedStringHandler(0, 1, this);
handler.AppendFormatted(value);
handler.Finish();
}
MarkSet();
}

/// <summary>
/// Provides a handler used by the language compiler to append
/// interpolated strings into <see cref="Col"/> instances.
Expand Down