diff --git a/src/FSharpTests/FSharpTests.fsproj b/src/FSharpTests/FSharpTests.fsproj index 185db866c..eee29e823 100644 --- a/src/FSharpTests/FSharpTests.fsproj +++ b/src/FSharpTests/FSharpTests.fsproj @@ -6,7 +6,7 @@ - + diff --git a/src/Verify/Serialization/Converters/DateConverter.cs b/src/Verify/Serialization/Converters/DateConverter.cs index 046719833..0dc3ea2f9 100644 --- a/src/Verify/Serialization/Converters/DateConverter.cs +++ b/src/Verify/Serialization/Converters/DateConverter.cs @@ -10,7 +10,9 @@ public override void Write(VerifyJsonWriter writer, Date value) return; } - writer.WriteRawValueWithScrubbers(value.ToString("yyyy-MM-dd", Culture.InvariantCulture)); + Span buffer = stackalloc char[10]; + value.TryFormat(buffer, out _, "yyyy-MM-dd".AsSpan(), Culture.InvariantCulture); + writer.WriteRawValueWithScrubbers(buffer); } } #endif \ No newline at end of file diff --git a/src/Verify/Serialization/Converters/TimeConverter.cs b/src/Verify/Serialization/Converters/TimeConverter.cs index f7d01be84..654f10b5f 100644 --- a/src/Verify/Serialization/Converters/TimeConverter.cs +++ b/src/Verify/Serialization/Converters/TimeConverter.cs @@ -10,7 +10,9 @@ public override void Write(VerifyJsonWriter writer, Time value) return; } - writer.WriteRawValueWithScrubbers(value.ToString("h:mm tt", Culture.InvariantCulture)); + Span buffer = stackalloc char[8]; + value.TryFormat(buffer, out var charsWritten, "h:mm tt".AsSpan(), Culture.InvariantCulture); + writer.WriteRawValueWithScrubbers(buffer[..charsWritten]); } } #endif \ No newline at end of file diff --git a/src/Verify/Serialization/CustomContractResolver.cs b/src/Verify/Serialization/CustomContractResolver.cs index 82c7e38e0..de2e3d005 100644 --- a/src/Verify/Serialization/CustomContractResolver.cs +++ b/src/Verify/Serialization/CustomContractResolver.cs @@ -75,7 +75,7 @@ string ResolveDictionaryKey(JsonDictionaryContract contract, string name, object if (original is string stringValue) { - if (settings.TryParseConvert(counter, stringValue, out var result)) + if (settings.TryParseConvert(counter, stringValue.AsSpan(), out var result)) { return result; } diff --git a/src/Verify/Serialization/Scrubbers/ApplyScrubbers.cs b/src/Verify/Serialization/Scrubbers/ApplyScrubbers.cs index b4c58b8e8..cbe69d91b 100644 --- a/src/Verify/Serialization/Scrubbers/ApplyScrubbers.cs +++ b/src/Verify/Serialization/Scrubbers/ApplyScrubbers.cs @@ -142,9 +142,10 @@ public static void ApplyForExtension(string extension, StringBuilder target, Ver target.FixNewlines(); } - public static string ApplyForPropertyValue(string value, VerifySettings settings, Counter counter) + public static CharSpan ApplyForPropertyValue(CharSpan value, VerifySettings settings, Counter counter) { - var builder = new StringBuilder(value); + var builder = new StringBuilder(value.Length); + builder.Append(value); foreach (var scrubber in settings.InstanceScrubbers) { scrubber(builder, counter); @@ -161,7 +162,7 @@ public static string ApplyForPropertyValue(string value, VerifySettings settings } builder.FixNewlines(); - return builder.ToString(); + return builder.AsSpan(); } static string CleanPath(string directory) => diff --git a/src/Verify/Serialization/Scrubbers/SharedScrubber.cs b/src/Verify/Serialization/Scrubbers/SharedScrubber.cs index e03bb2d5a..5280cd923 100644 --- a/src/Verify/Serialization/Scrubbers/SharedScrubber.cs +++ b/src/Verify/Serialization/Scrubbers/SharedScrubber.cs @@ -1,6 +1,6 @@ partial class SerializationSettings { - internal bool TryConvertString(Counter counter, string value, [NotNullWhen(true)] out string? result) + internal bool TryConvertString(Counter counter, CharSpan value, [NotNullWhen(true)] out string? result) { if (TryParseConvertGuid(counter, value, out result)) { diff --git a/src/Verify/Serialization/Scrubbers/SharedScrubber_Dates.cs b/src/Verify/Serialization/Scrubbers/SharedScrubber_Dates.cs index d45fb2fdc..116d19d68 100644 --- a/src/Verify/Serialization/Scrubbers/SharedScrubber_Dates.cs +++ b/src/Verify/Serialization/Scrubbers/SharedScrubber_Dates.cs @@ -18,7 +18,7 @@ internal bool TryConvert(Counter counter, DateTime value, [NotNullWhen(true)] ou } #if NET6_0_OR_GREATER - internal bool TryParseConvertDate(Counter counter, string value, [NotNullWhen(true)] out string? result) + internal bool TryParseConvertDate(Counter counter, CharSpan value, [NotNullWhen(true)] out string? result) { if (scrubDateTimes) { @@ -63,7 +63,7 @@ internal static string Convert(Counter counter, Date date) return counter.NextString(date); } - internal bool TryParseConvertTime(Counter counter, string value, [NotNullWhen(true)] out string? result) + internal bool TryParseConvertTime(Counter counter, CharSpan value, [NotNullWhen(true)] out string? result) { if (scrubDateTimes) { @@ -83,14 +83,14 @@ internal bool TryParseConvertTime(Counter counter, string value, [NotNullWhen(tr internal bool TryConvert(Counter counter, Time value, [NotNullWhen(true)] out string? result) { - if (!scrubDateTimes) + if (scrubDateTimes) { - result = null; - return false; + result = Convert(counter, value); + return true; } - result = Convert(counter, value); - return true; + result = null; + return false; } static string Convert(Counter counter, Time time) @@ -152,11 +152,11 @@ internal static string Convert(Counter counter, DateTimeOffset date) return counter.NextString(date); } - internal bool TryParseConvertDateTime(Counter counter, string value, [NotNullWhen(true)] out string? result) + internal bool TryParseConvertDateTime(Counter counter, CharSpan value, [NotNullWhen(true)] out string? result) { if (scrubDateTimes) { - if (TryParse("yyyy-MM-ddTHH:mm:ss.FFFFFFFK", out var dateTime)) + if (TryParseDateTime(value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFK", out var dateTime)) { result = Convert(counter, dateTime); return true; @@ -164,7 +164,7 @@ internal bool TryParseConvertDateTime(Counter counter, string value, [NotNullWhe foreach (var format in datetimeFormats) { - if (TryParse(format, out dateTime)) + if (TryParseDateTime(value, format, out dateTime)) { result = Convert(counter, dateTime); return true; @@ -174,16 +174,20 @@ internal bool TryParseConvertDateTime(Counter counter, string value, [NotNullWhe result = null; return false; - - bool TryParse(string format, out DateTime dateTime) => - DateTime.TryParseExact(value, format, null, DateTimeStyles.None, out dateTime); } - internal bool TryParseConvertDateTimeOffset(Counter counter, string value, [NotNullWhen(true)] out string? result) + static bool TryParseDateTime(CharSpan value, string format, out DateTime dateTime) => +#if NET47_OR_GREATER + DateTime.TryParseExact(value.ToString(), format, null, DateTimeStyles.None, out dateTime); +#else + DateTime.TryParseExact(value, format.AsSpan(), null, DateTimeStyles.None, out dateTime); +#endif + + internal bool TryParseConvertDateTimeOffset(Counter counter, CharSpan value, [NotNullWhen(true)] out string? result) { if (scrubDateTimes) { - if (TryParse("yyyy-MM-ddTHH:mm:ss.FFFFFFFK", out var dateTimeOffset)) + if (TryParseDateTimeOffset(value, "yyyy-MM-ddTHH:mm:ss.FFFFFFFK", out var dateTimeOffset)) { result = Convert(counter, dateTimeOffset); return true; @@ -191,7 +195,7 @@ internal bool TryParseConvertDateTimeOffset(Counter counter, string value, [NotN foreach (var format in datetimeOffsetFormats) { - if (TryParse(format, out dateTimeOffset)) + if (TryParseDateTimeOffset(value, format, out dateTimeOffset)) { result = Convert(counter, dateTimeOffset); return true; @@ -202,7 +206,12 @@ internal bool TryParseConvertDateTimeOffset(Counter counter, string value, [NotN result = null; return false; - bool TryParse(string format, out DateTimeOffset dateTimeOffset) => - DateTimeOffset.TryParseExact(value, format, null, DateTimeStyles.None, out dateTimeOffset); } + + static bool TryParseDateTimeOffset(CharSpan value, string format, out DateTimeOffset dateTimeOffset) => +#if NET47_OR_GREATER + DateTimeOffset.TryParseExact(value.ToString(), format, null, DateTimeStyles.None, out dateTimeOffset); +#else + DateTimeOffset.TryParseExact(value, format, null, DateTimeStyles.None, out dateTimeOffset); +#endif } \ No newline at end of file diff --git a/src/Verify/Serialization/Scrubbers/SharedScrubber_Guids.cs b/src/Verify/Serialization/Scrubbers/SharedScrubber_Guids.cs index 2ba8e5e4d..1901501e6 100644 --- a/src/Verify/Serialization/Scrubbers/SharedScrubber_Guids.cs +++ b/src/Verify/Serialization/Scrubbers/SharedScrubber_Guids.cs @@ -22,11 +22,15 @@ internal static string Convert(Counter counter, Guid guid) return counter.NextString(guid); } - internal bool TryParseConvertGuid(Counter counter, string value, [NotNullWhen(true)] out string? result) + internal bool TryParseConvertGuid(Counter counter, CharSpan value, [NotNullWhen(true)] out string? result) { if (scrubGuids) { +#if NET47_OR_GREATER + if (Guid.TryParse(value.ToString(), out var guid)) +#else if (Guid.TryParse(value, out var guid)) +#endif { result = Convert(counter, guid); return true; @@ -37,7 +41,7 @@ internal bool TryParseConvertGuid(Counter counter, string value, [NotNullWhen(tr return false; } - internal bool TryParseConvert(Counter counter, string value, [NotNullWhen(true)] out string? result) + internal bool TryParseConvert(Counter counter, CharSpan value, [NotNullWhen(true)] out string? result) { if (TryParseConvertGuid(counter, value, out result)) { diff --git a/src/Verify/Serialization/VerifyJsonWriter.cs b/src/Verify/Serialization/VerifyJsonWriter.cs index d2616a5dc..74035b696 100644 --- a/src/Verify/Serialization/VerifyJsonWriter.cs +++ b/src/Verify/Serialization/VerifyJsonWriter.cs @@ -29,7 +29,10 @@ public class VerifyJsonWriter : } } - public void WriteRawValueIfNoStrict(string value) + public void WriteRawValueIfNoStrict(string value) => + WriteRawValueIfNoStrict(value.AsSpan()); + + public void WriteRawValueIfNoStrict(CharSpan value) { if (VerifierSettings.StrictJson) { @@ -40,9 +43,12 @@ public void WriteRawValueIfNoStrict(string value) base.WriteRawValue(value); } - public void WriteRawValueWithScrubbers(string value) + public void WriteRawValueWithScrubbers(string value) => + WriteRawValueWithScrubbers(value.AsSpan()); + + public void WriteRawValueWithScrubbers(CharSpan value) { - if (value is "") + if (value.Length == 0) { WriteRawValueIfNoStrict(value); return; @@ -52,6 +58,16 @@ public void WriteRawValueWithScrubbers(string value) WriteRawValueIfNoStrict(value); } + public override void WritePropertyName(CharSpan name, bool escape) + { + if (VerifierSettings.StrictJson) + { + escape = false; + } + + base.WritePropertyName(name, escape); + } + public override void WritePropertyName(string name, bool escape) { if (VerifierSettings.StrictJson) @@ -70,6 +86,15 @@ public override void WriteValue(string? value) return; } + WriteValue(value.AsSpan()); + } + + public override void WriteValue(StringBuilder? value) => + // TODO: + WriteValue(value?.ToString()); + + public override void WriteValue(CharSpan value) + { if (value is "") { WriteRawValueIfNoStrict(value); @@ -93,12 +118,15 @@ public override void WriteValue(string? value) { base.Flush(); var builderLength = builder.Length; - if (!value.StartsWith('\n')) + if (value[0] != '\n') { - value = $"\n{value}"; + //todo: avoid alloc + WriteRawValue($"\n{value.ToString()}"); + } + else + { + WriteRawValue(value); } - - WriteRawValue(value); base.Flush(); builder.Remove(builderLength, 1); return; @@ -151,7 +179,9 @@ public override void WriteValue(Guid value) return; } - WriteRawValueWithScrubbers(value.ToString("D", Culture.InvariantCulture)); + Span buffer = stackalloc char[36]; + value.TryFormat(buffer, out _); + WriteRawValueWithScrubbers(buffer); } /// @@ -224,8 +254,7 @@ void WriteOrSerialize(object converted) { WriteValue(convertedString); } - else if (converted.GetType() - .IsPrimitive) + else if (converted.GetType().IsPrimitive) { WriteValue(converted); } diff --git a/src/Verify/Verifier/InnerVerifier_Json.cs b/src/Verify/Verifier/InnerVerifier_Json.cs index 5ee2780af..39cf7f8de 100644 --- a/src/Verify/Verifier/InnerVerifier_Json.cs +++ b/src/Verify/Verifier/InnerVerifier_Json.cs @@ -33,7 +33,7 @@ public async Task VerifyJson(Stream? target) using var reader = new StreamReader(target); using var textReader = new JsonTextReader(reader); - var token = await JToken.LoadAsync(textReader); + var token = JToken.Load(textReader); return await VerifyJson(token); } diff --git a/src/Verify/Verifier/InnerVerifier_Xml.cs b/src/Verify/Verifier/InnerVerifier_Xml.cs index 103b720e5..e13b5e4d5 100644 --- a/src/Verify/Verifier/InnerVerifier_Xml.cs +++ b/src/Verify/Verifier/InnerVerifier_Xml.cs @@ -114,11 +114,12 @@ Task VerifyXml(XContainer? target) string ConvertValue(SerializationSettings serialization, string value) { - if (serialization.TryConvertString(counter, value, out var result)) + var span = value.AsSpan(); + if (serialization.TryConvertString(counter, span, out var result)) { return result; } - return ApplyScrubbers.ApplyForPropertyValue(value, settings, counter); + return ApplyScrubbers.ApplyForPropertyValue(span, settings, counter).ToString(); } } \ No newline at end of file diff --git a/src/Verify/Verify.csproj b/src/Verify/Verify.csproj index 4cb2bec00..b3a6a7b67 100644 --- a/src/Verify/Verify.csproj +++ b/src/Verify/Verify.csproj @@ -15,7 +15,7 @@ - +