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
1,315 changes: 656 additions & 659 deletions src/CsToml/Error/ExceptionHelper.cs

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions src/CsToml/Extension/CollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;


Expand All @@ -9,21 +10,21 @@ internal static class CollectionExtensions
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T At<T>(this Span<T> span, int index)
{
// Avoid range check.
Debug.Assert((uint)index < (uint)span.Length, $"Index {index} out of range [0, {span.Length})");
return ref Unsafe.Add<T>(ref MemoryMarshal.GetReference(span), index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T At<T>(this ReadOnlySpan<T> span, int index)
{
// Avoid range check.
Debug.Assert((uint)index < (uint)span.Length, $"Index {index} out of range [0, {span.Length})");
return ref Unsafe.Add<T>(ref MemoryMarshal.GetReference(span), index);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ref T At<T>(this T[] array, int index)
{
// Avoid range check.
Debug.Assert((uint)index < (uint)array.Length, $"Index {index} out of range [0, {array.Length})");
return ref Unsafe.Add<T>(ref MemoryMarshal.GetArrayDataReference(array), index);
}
}
Expand Down
78 changes: 55 additions & 23 deletions src/CsToml/TomlCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using CsToml.Error;
using CsToml.Extension;
using CsToml.Utility;
using System.Buffers;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Text;
Expand Down Expand Up @@ -58,28 +59,55 @@ internal static int ParseHex(byte hexNumber)
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xe0 - 0xef
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0xf0 - 0xff
];
return hexTable[hexNumber];
return hexTable.At(hexNumber);
}

internal static int DigitsDecimalUnroll4(long value)
private static readonly ulong[] zeroOrPowersOf10 =
[
0,
0,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
10000000000,
100000000000,
1000000000000,
10000000000000,
100000000000000,
1000000000000000,
10000000000000000,
100000000000000000,
1000000000000000000,
10000000000000000000
];

// https://github.com/fmtlib/fmt/blob/662adf4f33346ba9aba8b072194e319869ede54a/include/fmt/format.h#L1124
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static int CountDigitsForInt64(long value)
{
// long.MinValue raises an OverflowException, so fixed support
// https://learn.microsoft.com/en-us/dotnet/api/system.math.abs?view=net-8.0#system-math-abs(system-int64)
if (value == long.MinValue)
return 19;

var number = 1;
value = Math.Abs(value);
ulong absValue = (ulong)(value < 0 ? -value : value);

for (; ; )
{
if (value < 10) return number;
if (value < 100) return number + 1;
if (value < 1000) return number + 2;
if (value < 10000) return number + 3;
value /= 10000;
number += 4;
}
ReadOnlySpan<byte> bsr2log10 =
[
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5,
6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 15, 15,
15, 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, 19, 19, 19, 19, 20
];

int t = bsr2log10.At((int)ulong.Log2(absValue));
return (int)(t - (absValue < zeroOrPowersOf10.At(t) ? 1 : 0));
Comment thread
prozolic marked this conversation as resolved.
}
}

Expand Down Expand Up @@ -181,7 +209,7 @@ internal static bool IsEscape(byte rawByte) // U+0000-U+0008,U+000A-U+001F,U+007
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return escapeTable[rawByte];
return escapeTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -206,7 +234,7 @@ internal static bool IsEscapeSequence(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return escapeSequenceTable[rawByte];
return escapeSequenceTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -231,7 +259,7 @@ internal static bool IsBareKey(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return barekeyTable[rawByte];
return barekeyTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -278,6 +306,10 @@ internal static bool IsRightSquareBrackets(byte rawByte)
internal static bool IsRightBraces(byte rawByte)
=> rawByte == Symbol.RIGHTBRACES;

internal static ReadOnlySpan<byte> TabOrWhiteSpaceOrNewlineBytes => [Symbol.TAB, Symbol.SPACE, Symbol.LINEFEED, Symbol.CARRIAGE];

internal static ReadOnlySpan<byte> TabOrWhiteSpaceBytes => [Symbol.TAB, Symbol.SPACE];

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static bool IsTabOrWhiteSpace(byte rawByte)
=> IsTab(rawByte) || IsWhiteSpace(rawByte);
Expand Down Expand Up @@ -308,7 +340,7 @@ internal static bool IsNumber(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return numberTable[rawByte];
return numberTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -333,7 +365,7 @@ internal static bool IsBinary(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return binaryTable[rawByte];
return binaryTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -358,7 +390,7 @@ internal static bool IsOctal(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return octalTable[rawByte];
return octalTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -383,7 +415,7 @@ internal static bool IsHex(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return hexTable[rawByte];
return hexTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -409,7 +441,7 @@ internal static bool IsUpperHexAlphabet(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return upperHexAlphabetTable[rawByte];
return upperHexAlphabetTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -435,7 +467,7 @@ internal static bool IsLowerHexAlphabet(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return lowerHexAlphabetTable[rawByte];
return lowerHexAlphabetTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -461,7 +493,7 @@ internal static bool IsAlphabet(byte rawByte)
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xe0 - 0xef
false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, // 0xf0 - 0xff
];
return alphabetTable[rawByte];
return alphabetTable.At(rawByte);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down
26 changes: 13 additions & 13 deletions src/CsToml/Utf8TomlDocumentWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public ref struct Utf8TomlDocumentWriter<TBufferWriter>
public readonly bool IsRoot => valueStates.Count == 1 && !valueOnly;

internal Utf8TomlDocumentWriter(ref TBufferWriter bufferWriter, bool valueOnly = false) : this(ref bufferWriter, valueOnly, null)
{}
{ }

internal Utf8TomlDocumentWriter(ref TBufferWriter bufferWriter, bool valueOnly, CsTomlSerializerOptions? options)
{
Expand Down Expand Up @@ -140,7 +140,7 @@ public void WriteBoolean(bool value)

public void WriteInt64(long value)
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value);
var length = TomlCodes.Number.CountDigitsForInt64(value);
if (value < 0) length++;

value.TryFormat(writer.GetWrittenSpan(length), out int bytesWritten, null, CultureInfo.InvariantCulture);
Expand Down Expand Up @@ -183,7 +183,7 @@ internal void WriteInt64InOctalFormat(long value)
var writtenSpan = writer.GetWrittenSpan(digits);
var index = digits - 1;

while (value > 0)
while (value > 0)
{
var v = value & 7;
writtenSpan[index--] = (byte)(TomlCodes.Number.Zero + (byte)v);
Expand Down Expand Up @@ -240,7 +240,7 @@ internal void WriteInt64InHexFormat(long value)

public void WriteDouble(double value)
{
switch(value)
switch (value)
{
case double.NegativeInfinity:
WriteBytes("-inf"u8);
Expand Down Expand Up @@ -370,7 +370,7 @@ public void WriteDateTimeOffset(DateTimeOffset value)
{
if (value.Microsecond == 0)
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value.Millisecond);
var length = TomlCodes.Number.CountDigitsForInt64(value.Millisecond);
switch (length)
{
case 1:
Expand All @@ -397,7 +397,7 @@ public void WriteDateTimeOffset(DateTimeOffset value)
}
else
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value.Microsecond);
var length = TomlCodes.Number.CountDigitsForInt64(value.Microsecond);
switch (length)
{
case 1:
Expand Down Expand Up @@ -434,7 +434,7 @@ public void WriteDateTimeOffset(DateTimeOffset value)
{
if (value.Microsecond == 0)
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value.Millisecond);
var length = TomlCodes.Number.CountDigitsForInt64(value.Millisecond);
switch (length)
{
case 1:
Expand All @@ -461,7 +461,7 @@ public void WriteDateTimeOffset(DateTimeOffset value)
}
else
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value.Microsecond);
var length = TomlCodes.Number.CountDigitsForInt64(value.Microsecond);
switch (length)
{
case 1:
Expand Down Expand Up @@ -501,7 +501,7 @@ public void WriteDateTime(DateTime value)
{
if (value.Microsecond == 0)
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value.Millisecond);
var length = TomlCodes.Number.CountDigitsForInt64(value.Millisecond);
switch (length)
{
case 1:
Expand All @@ -528,7 +528,7 @@ public void WriteDateTime(DateTime value)
}
else
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value.Microsecond);
var length = TomlCodes.Number.CountDigitsForInt64(value.Microsecond);
switch (length)
{
case 1:
Expand Down Expand Up @@ -572,7 +572,7 @@ public void WriteTimeOnly(TimeOnly value)
{
if (value.Microsecond == 0)
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value.Millisecond);
var length = TomlCodes.Number.CountDigitsForInt64(value.Millisecond);
switch (length)
{
case 1:
Expand All @@ -599,7 +599,7 @@ public void WriteTimeOnly(TimeOnly value)
}
else
{
var length = TomlCodes.Number.DigitsDecimalUnroll4(value.Microsecond);
var length = TomlCodes.Number.CountDigitsForInt64(value.Microsecond);
switch (length)
{
case 1:
Expand Down Expand Up @@ -713,7 +713,7 @@ internal void WriteKeyInternal(string key)
fixed (byte* ptr = &destReference)
{
var writtenSpan = MemoryMarshal.CreateSpan(ref Unsafe.AsRef<byte>(ptr), bytesWritten);
WriteKeyInternal(writtenSpan, TomlDottedKeyHelper.GetTomlKeyType( writtenSpan, options.Spec.SupportsEscapeSequenceE, options.Spec.SupportsEscapeSequenceX));
WriteKeyInternal(writtenSpan, TomlDottedKeyHelper.GetTomlKeyType(writtenSpan, options.Spec.SupportsEscapeSequenceE, options.Spec.SupportsEscapeSequenceX));
}
}
}
Expand Down
11 changes: 4 additions & 7 deletions src/CsToml/Utility/SpanWriter.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using CsToml.Error;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace CsToml.Utility;

internal ref struct SpanWriter(Span<byte> buffer)
internal ref struct SpanWriter(Span<byte> source)
{
private readonly Span<byte> source = buffer;
private readonly ref byte refSource = ref MemoryMarshal.GetReference(buffer);
private readonly Span<byte> source = source;
private int written = 0;
Comment thread
prozolic marked this conversation as resolved.

public readonly int Written => written;
Expand All @@ -17,14 +15,13 @@ internal ref struct SpanWriter(Span<byte> buffer)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Write(byte ch)
{
if (written >= source.Length)
if ((uint)written >= (uint)source.Length)
{
ExceptionHelper.ThrowOverflowDuringParsingOfNumericTypes();
return;
}

ref var v = ref Unsafe.Add(ref refSource, written++);
v = ch;
source[written++] = ch;
}

}
7 changes: 4 additions & 3 deletions src/CsToml/Utility/Utf8Helper.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using CsToml.Error;
using System.Buffers;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Unicode;

namespace CsToml.Utility;
Expand Down Expand Up @@ -281,7 +281,8 @@ public static bool ContainsEscapeChar(ReadOnlySpan<byte> bytes, bool newline)
public static void FromUtf16(IBufferWriter<byte> writer, ReadOnlySpan<char> value)
{
// buffer size to 3 times worst-case (UTF16 -> UTF8)
var maxBufferSize = (value.Length + 1) * 3;
// var maxBufferSize = (value.Length + 1) * 3;
var maxBufferSize = Encoding.UTF8.GetMaxByteCount(value.Length);

var status = Utf8.FromUtf16(value, writer.GetSpan(maxBufferSize),
out int charsRead, out int bytesWritten, replaceInvalidSequences: false);
Expand Down Expand Up @@ -315,7 +316,7 @@ public static string ToUtf16(ReadOnlySpan<byte> utf8Bytes)
return string.Empty;
}

var maxBufferSize = utf8Bytes.Length * 2;
var maxBufferSize = utf8Bytes.Length;
if (maxBufferSize <= 1024)
{
Span<char> bufferBytesSpan = stackalloc char[maxBufferSize];
Expand Down
Loading