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
2 changes: 1 addition & 1 deletion sandbox/Benchmark/Benchmark/ClassSerializationBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public void GlobalSetup()
new () { Value = "Hammer3" }
]
};
testTomlSerializedObjectInSnakeCase = new Benchmark.Model.TestTomlSerializedObjectInSnakeCase()
testTomlSerializedObjectInSnakeCase = new TestTomlSerializedObjectInSnakeCase()
{
Str = testTomlSerializedObject.Str,
Long = testTomlSerializedObject.Long,
Expand Down
38 changes: 38 additions & 0 deletions sandbox/Benchmark/Benchmark/Utf8TomlDocumentWriterBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using BenchmarkDotNet.Attributes;
using CsToml;
using System.Buffers;
using System.Text;

namespace Benchmark;

public class Utf8TomlDocumentWriterBenchmark
{
#pragma warning disable CS8618
private static readonly Encoding utf8 = Encoding.UTF8;
private StringObject stringObject;
#pragma warning restore CS8618

[Params(1, 10, 100, 1000, 10000)]
public int Size = 10;

[GlobalSetup]
public void GlobalSetup()
{
stringObject = new StringObject() { Value = string.Join("", Enumerable.Repeat(1, Size).Select(i => i.ToString())) };
}

[Benchmark]
public string WriteString()
{
var bufferWriter = new ArrayBufferWriter<byte>();
CsTomlSerializer.Serialize(ref bufferWriter, stringObject);
return utf8.GetString(bufferWriter.WrittenSpan);
}
}

[TomlSerializedObject]
public partial class StringObject
{
[TomlValueOnSerialized]
public string? Value { get; set; }
}
2 changes: 1 addition & 1 deletion src/CsToml.Generator/CsToml.Generator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>12</LangVersion>
<LangVersion>14</LangVersion>
<IsRoslynComponent>true</IsRoslynComponent>
<AnalyzerLanguage>cs</AnalyzerLanguage>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>
Expand Down
6 changes: 6 additions & 0 deletions src/CsToml/CsTomlReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ private void ReadKeyToAllowUnicodeInBareKeys(ref ExtendableArray<TomlDottedKey>
{
case TomlCodes.Symbol.TAB:
case TomlCodes.Symbol.SPACE:
Advance(1);
SkipWhiteSpace();
continue;
case TomlCodes.Symbol.EQUAL:
Expand Down Expand Up @@ -150,6 +151,7 @@ private void ReadKeyToNotAllowUnicodeInBareKeys(ref ExtendableArray<TomlDottedKe
{
case TomlCodes.Symbol.TAB:
case TomlCodes.Symbol.SPACE:
Advance(1);
SkipWhiteSpace();
continue;
case TomlCodes.Symbol.EQUAL:
Expand Down Expand Up @@ -223,6 +225,7 @@ public void ReadTableHeaderToAllowUnicodeInBareKeys(ref ExtendableArray<TomlDott
{
case TomlCodes.Symbol.TAB:
case TomlCodes.Symbol.SPACE:
Advance(1);
SkipWhiteSpace();
continue;
case TomlCodes.Symbol.DOT:
Expand Down Expand Up @@ -284,6 +287,7 @@ public void ReadTableHeaderToNotAllowUnicodeInBareKeys(ref ExtendableArray<TomlD
{
case TomlCodes.Symbol.TAB:
case TomlCodes.Symbol.SPACE:
Advance(1);
SkipWhiteSpace();
continue;
case TomlCodes.Symbol.DOT:
Expand Down Expand Up @@ -354,6 +358,7 @@ public void ReadArrayOfTablesHeaderToAllowUnicodeInBareKeys(ref ExtendableArray<
{
case TomlCodes.Symbol.TAB:
case TomlCodes.Symbol.SPACE:
Advance(1);
SkipWhiteSpace();
continue;
case TomlCodes.Symbol.DOT:
Expand Down Expand Up @@ -423,6 +428,7 @@ public void ReadArrayOfTablesHeaderToNotAllowUnicodeInBareKeys(ref ExtendableArr
{
case TomlCodes.Symbol.TAB:
case TomlCodes.Symbol.SPACE:
Advance(1);
SkipWhiteSpace();
continue;
case TomlCodes.Symbol.DOT:
Expand Down
9 changes: 6 additions & 3 deletions src/CsToml/Utf8TomlDocumentWriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ internal Utf8TomlDocumentWriter(ref TBufferWriter bufferWriter, bool valueOnly =
internal Utf8TomlDocumentWriter(ref TBufferWriter bufferWriter, bool valueOnly, CsTomlSerializerOptions? options)
{
writer = new Utf8Writer<TBufferWriter>(ref bufferWriter);
dottedKeys = valueOnly ? [] : new List<TomlDottedKey>();
dottedKeys = [];
valueStates = [(TomlValueState.Default, -1)];
this.valueOnly = valueOnly;
this.options = options ?? CsTomlSerializerOptions.Default;
Expand Down Expand Up @@ -236,6 +236,8 @@ internal void WriteInt64InHexFormat(long value)
}
}

private static readonly SearchValues<byte> ExponentialBytes = SearchValues.Create(".eE"u8);

public void WriteDouble(double value)
{
switch(value)
Expand All @@ -261,8 +263,9 @@ public void WriteDouble(double value)
}
writer.Advance(bytesWritten);

// integer check
if (!writtenSpan.Slice(0, bytesWritten).ContainsAny(".eE"u8))
// If the formatted value has no decimal point or exponent (i.e., looks like an integer),
// append .0 so that it is represented as a valid TOML float rather than an integer.
if (!writtenSpan.Slice(0, bytesWritten).ContainsAny(ExponentialBytes))
{
var writtenSpanEx = writer.GetWrittenSpan(2);
writtenSpanEx[0] = TomlCodes.Symbol.DOT;
Expand Down
167 changes: 99 additions & 68 deletions src/CsToml/Values/TomlString.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,44 +138,58 @@ internal static void ToTomlBasicString<TBufferWriter>(ref Utf8TomlDocumentWriter
{
writer.WriteBytes("\""u8);

for (int i = 0; i < byteSpan.Length; i++)
var index = byteSpan.IndexOfAny(EscapedChars);
if (index < 0)
{
var ch = byteSpan[i];
switch (ch)
writer.WriteBytes(byteSpan);
}
else
{
if (index > 0)
{
case TomlCodes.Symbol.DOUBLEQUOTED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Symbol.DOUBLEQUOTED);
continue;
case TomlCodes.Symbol.BACKSLASH:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Symbol.BACKSLASH);
continue;
case TomlCodes.Symbol.BACKSPACE:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.b);
continue;
case TomlCodes.Symbol.TAB:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.t);
continue;
case TomlCodes.Symbol.LINEFEED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.n);
continue;
case TomlCodes.Symbol.FORMFEED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.f);
continue;
case TomlCodes.Symbol.CARRIAGE:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.r);
continue;
default:
writer.Write(ch);
continue;
writer.WriteBytes(byteSpan.Slice(0, index));
byteSpan = byteSpan.Slice(index);
}

for (index = 0; index < byteSpan.Length; index++)
{
var ch = byteSpan[index];
switch (ch)
{
case TomlCodes.Symbol.DOUBLEQUOTED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Symbol.DOUBLEQUOTED);
continue;
case TomlCodes.Symbol.BACKSLASH:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Symbol.BACKSLASH);
continue;
case TomlCodes.Symbol.BACKSPACE:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.b);
continue;
case TomlCodes.Symbol.TAB:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.t);
continue;
case TomlCodes.Symbol.LINEFEED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.n);
continue;
case TomlCodes.Symbol.FORMFEED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.f);
continue;
case TomlCodes.Symbol.CARRIAGE:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.r);
continue;
default:
writer.Write(ch);
continue;
}

}
}

writer.WriteBytes("\""u8);
Expand Down Expand Up @@ -247,43 +261,58 @@ internal static void ToTomlMultiLineBasicString<TBufferWriter>(ref Utf8TomlDocum
{
writer.WriteBytes("\"\"\""u8);

for (int i = 0; i < byteSpan.Length; i++)
var index = byteSpan.IndexOfAny(EscapedChars);
if (index < 0)
{
writer.WriteBytes(byteSpan);
}
else
{
var ch = byteSpan[i];
switch (ch)
if (index > 0)
{
case TomlCodes.Symbol.DOUBLEQUOTED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Symbol.DOUBLEQUOTED);
continue;
case TomlCodes.Symbol.BACKSLASH:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Symbol.BACKSLASH);
continue;
case TomlCodes.Symbol.BACKSPACE:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.b);
continue;
case TomlCodes.Symbol.TAB:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.t);
continue;
case TomlCodes.Symbol.LINEFEED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.n);
continue;
case TomlCodes.Symbol.FORMFEED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.f);
continue;
case TomlCodes.Symbol.CARRIAGE:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.r);
continue;
default:
writer.Write(ch);
continue;
writer.WriteBytes(byteSpan.Slice(0, index));
byteSpan = byteSpan.Slice(index);
}

for (index = 0; index < byteSpan.Length; index++)
{
var ch = byteSpan[index];
switch (ch)
{
case TomlCodes.Symbol.DOUBLEQUOTED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Symbol.DOUBLEQUOTED);
continue;
case TomlCodes.Symbol.BACKSLASH:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Symbol.BACKSLASH);
continue;
case TomlCodes.Symbol.BACKSPACE:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.b);
continue;
case TomlCodes.Symbol.TAB:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.t);
continue;
case TomlCodes.Symbol.LINEFEED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.n);
continue;
case TomlCodes.Symbol.FORMFEED:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.f);
continue;
case TomlCodes.Symbol.CARRIAGE:
writer.Write(TomlCodes.Symbol.BACKSLASH);
writer.Write(TomlCodes.Alphabet.r);
continue;
default:
writer.Write(ch);
continue;
}
}

}

writer.WriteBytes("\"\"\""u8);
Expand Down Expand Up @@ -430,6 +459,8 @@ internal static void ToTomlMultiLineLiteralString<TBufferWriter>(ref Utf8TomlDoc
[DebuggerDisplay("{Utf16String}")]
internal abstract partial class TomlString(string value) : TomlValue()
{
protected static readonly SearchValues<byte> EscapedChars = SearchValues.Create("\"\\\b\t\n\f\r"u8);

protected readonly string value = value;

public override bool HasValue => true;
Expand Down
18 changes: 10 additions & 8 deletions src/CsToml/Values/TomlTableNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,24 +198,26 @@ internal NodeStatus TryGetOrAddChildNode(TomlDottedKey key, out TomlTableNode ge
getOrAddChildNode = Empty;
return NodeStatus.Empty;
}
if (nodes.TryGetValueOrAdd(key, out var existingNode, out var newNode))

var result = nodes.GetOrAddIfNotFound(key);
if (result.IsExistingValueFound)
{
getOrAddChildNode = existingNode!;
getOrAddChildNode = result.ExistingValue!;
return NodeStatus.Existed;
}

getOrAddChildNode = newNode!;

getOrAddChildNode = result.AddedValue!;
return NodeStatus.NewAdd;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal bool TryGetChildNode(ReadOnlySpan<byte> key, out TomlTableNode? childNode)
{
var nodes = this.nodes;
if (Value is TomlInlineTable t)
{
nodes = t.RootNode.nodes;
}
TomlTableNodeDictionary? nodes =
Value is TomlInlineTable inlineTable ?
inlineTable.RootNode.nodes :
this.nodes;

if (nodes == null)
{
Expand Down
Loading