-
Notifications
You must be signed in to change notification settings - Fork 5.2k
JSON: Add support for Int128, UInt128 and Half #88962
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
b4b569e
JSON: Add support for Int128, UInt128 and Half and add Number support…
jozkee d42cf66
Remove parsing limits on Read and move Number support of CopyString t…
jozkee 8b71bf3
Fix AllowNamedFloatingPointLiterals on Write for Half
jozkee 38483e5
Specify InvariantCulture on TryParse and TryFormat
jozkee 2c5cb22
Add test for invalid number input format
jozkee 22bc1f7
Fix net6.0 build error about missing Half.TryParse overload
jozkee 43fe8a8
Move rentedCharBuffer logic to TryParse helper
jozkee 02e83b3
Address feedback
jozkee 8c3e143
Disable test for OSX
jozkee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
217 changes: 217 additions & 0 deletions
217
...ies/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/HalfConverter.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Buffers; | ||
using System.Diagnostics; | ||
using System.Globalization; | ||
|
||
namespace System.Text.Json.Serialization.Converters | ||
{ | ||
internal sealed class HalfConverter : JsonPrimitiveConverter<Half> | ||
{ | ||
private const int MaxFormatLength = 20; | ||
|
||
public HalfConverter() | ||
{ | ||
IsInternalConverterForNumberType = true; | ||
} | ||
|
||
public override Half Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||
{ | ||
if (reader.TokenType != JsonTokenType.Number) | ||
{ | ||
ThrowHelper.ThrowInvalidOperationException_ExpectedNumber(reader.TokenType); | ||
} | ||
|
||
return ReadCore(ref reader); | ||
} | ||
|
||
public override void Write(Utf8JsonWriter writer, Half value, JsonSerializerOptions options) | ||
{ | ||
WriteCore(writer, value); | ||
} | ||
|
||
private static Half ReadCore(ref Utf8JsonReader reader) | ||
{ | ||
Half result; | ||
|
||
byte[]? rentedByteBuffer = null; | ||
int bufferLength = reader.ValueLength; | ||
|
||
Span<byte> byteBuffer = bufferLength <= JsonConstants.StackallocByteThreshold | ||
? stackalloc byte[JsonConstants.StackallocByteThreshold] | ||
: (rentedByteBuffer = ArrayPool<byte>.Shared.Rent(bufferLength)); | ||
|
||
int written = reader.CopyValue(byteBuffer); | ||
byteBuffer = byteBuffer.Slice(0, written); | ||
|
||
bool success = TryParse(byteBuffer, out result); | ||
if (rentedByteBuffer != null) | ||
{ | ||
ArrayPool<byte>.Shared.Return(rentedByteBuffer); | ||
} | ||
|
||
if (!success) | ||
{ | ||
ThrowHelper.ThrowFormatException(NumericType.Half); | ||
} | ||
|
||
Debug.Assert(!Half.IsNaN(result) && !Half.IsInfinity(result)); | ||
return result; | ||
} | ||
|
||
private static void WriteCore(Utf8JsonWriter writer, Half value) | ||
{ | ||
#if NET8_0_OR_GREATER | ||
Span<byte> buffer = stackalloc byte[MaxFormatLength]; | ||
#else | ||
Span<char> buffer = stackalloc char[MaxFormatLength]; | ||
#endif | ||
Format(buffer, value, out int written); | ||
writer.WriteRawValue(buffer.Slice(0, written)); | ||
} | ||
|
||
internal override Half ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) | ||
{ | ||
Debug.Assert(reader.TokenType == JsonTokenType.PropertyName); | ||
return ReadCore(ref reader); | ||
} | ||
|
||
internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, Half value, JsonSerializerOptions options, bool isWritingExtensionDataProperty) | ||
{ | ||
#if NET8_0_OR_GREATER | ||
Span<byte> buffer = stackalloc byte[MaxFormatLength]; | ||
#else | ||
Span<char> buffer = stackalloc char[MaxFormatLength]; | ||
#endif | ||
Format(buffer, value, out int written); | ||
writer.WritePropertyName(buffer.Slice(0, written)); | ||
} | ||
|
||
internal override Half ReadNumberWithCustomHandling(ref Utf8JsonReader reader, JsonNumberHandling handling, JsonSerializerOptions options) | ||
{ | ||
if (reader.TokenType == JsonTokenType.String) | ||
{ | ||
if ((JsonNumberHandling.AllowReadingFromString & handling) != 0) | ||
{ | ||
if (TryGetFloatingPointConstant(ref reader, out Half value)) | ||
{ | ||
return value; | ||
} | ||
|
||
return ReadCore(ref reader); | ||
} | ||
else if ((JsonNumberHandling.AllowNamedFloatingPointLiterals & handling) != 0) | ||
{ | ||
if (!TryGetFloatingPointConstant(ref reader, out Half value)) | ||
{ | ||
ThrowHelper.ThrowFormatException(NumericType.Half); | ||
} | ||
|
||
return value; | ||
} | ||
} | ||
|
||
return Read(ref reader, Type, options); | ||
} | ||
|
||
internal override void WriteNumberWithCustomHandling(Utf8JsonWriter writer, Half value, JsonNumberHandling handling) | ||
{ | ||
if ((JsonNumberHandling.WriteAsString & handling) != 0) | ||
{ | ||
#if NET8_0_OR_GREATER | ||
const byte Quote = JsonConstants.Quote; | ||
Span<byte> buffer = stackalloc byte[MaxFormatLength + 2]; | ||
#else | ||
const char Quote = (char)JsonConstants.Quote; | ||
Span<char> buffer = stackalloc char[MaxFormatLength + 2]; | ||
#endif | ||
buffer[0] = Quote; | ||
Format(buffer.Slice(1), value, out int written); | ||
|
||
int length = written + 2; | ||
buffer[length - 1] = Quote; | ||
writer.WriteRawValue(buffer.Slice(0, length)); | ||
} | ||
else if ((JsonNumberHandling.AllowNamedFloatingPointLiterals & handling) != 0) | ||
{ | ||
WriteFloatingPointConstant(writer, value); | ||
} | ||
else | ||
{ | ||
WriteCore(writer, value); | ||
} | ||
} | ||
|
||
private static bool TryGetFloatingPointConstant(ref Utf8JsonReader reader, out Half value) | ||
{ | ||
Span<byte> buffer = stackalloc byte[MaxFormatLength]; | ||
int written = reader.CopyValue(buffer); | ||
|
||
return JsonReaderHelper.TryGetFloatingPointConstant(buffer.Slice(0, written), out value); | ||
} | ||
|
||
private static void WriteFloatingPointConstant(Utf8JsonWriter writer, Half value) | ||
{ | ||
if (Half.IsNaN(value)) | ||
{ | ||
writer.WriteNumberValueAsStringUnescaped(JsonConstants.NaNValue); | ||
} | ||
else if (Half.IsPositiveInfinity(value)) | ||
{ | ||
writer.WriteNumberValueAsStringUnescaped(JsonConstants.PositiveInfinityValue); | ||
} | ||
else if (Half.IsNegativeInfinity(value)) | ||
{ | ||
writer.WriteNumberValueAsStringUnescaped(JsonConstants.NegativeInfinityValue); | ||
} | ||
else | ||
{ | ||
WriteCore(writer, value); | ||
} | ||
} | ||
|
||
private static bool TryParse(ReadOnlySpan<byte> buffer, out Half result) | ||
{ | ||
#if NET8_0_OR_GREATER | ||
eiriktsarpalis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
bool success = Half.TryParse(buffer, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out result); | ||
#else | ||
// Half.TryFormat/TryParse(ROS<byte>) are not available on .NET 7 | ||
// we need to use Half.TryFormat/TryParse(ROS<char>) in that case. | ||
char[]? rentedCharBuffer = null; | ||
|
||
Span<char> charBuffer = buffer.Length <= JsonConstants.StackallocCharThreshold | ||
? stackalloc char[JsonConstants.StackallocCharThreshold] | ||
: (rentedCharBuffer = ArrayPool<char>.Shared.Rent(buffer.Length)); | ||
|
||
int written = JsonReaderHelper.TranscodeHelper(buffer, charBuffer); | ||
|
||
bool success = Half.TryParse(charBuffer, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out result); | ||
|
||
if (rentedCharBuffer != null) | ||
{ | ||
ArrayPool<char>.Shared.Return(rentedCharBuffer); | ||
} | ||
#endif | ||
|
||
// Half.TryParse is more lax with floating-point literals than other S.T.Json floating-point types | ||
// e.g: it parses "naN" successfully. Only succeed with the exact match. | ||
eiriktsarpalis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return success && | ||
(!Half.IsNaN(result) || buffer.SequenceEqual(JsonConstants.NaNValue)) && | ||
(!Half.IsPositiveInfinity(result) || buffer.SequenceEqual(JsonConstants.PositiveInfinityValue)) && | ||
(!Half.IsNegativeInfinity(result) || buffer.SequenceEqual(JsonConstants.NegativeInfinityValue)); | ||
} | ||
|
||
private static void Format( | ||
#if NET8_0_OR_GREATER | ||
Span<byte> destination, | ||
#else | ||
Span<char> destination, | ||
#endif | ||
Half value, out int written) | ||
{ | ||
bool formattedSuccessfully = value.TryFormat(destination, out written, provider: CultureInfo.InvariantCulture); | ||
Debug.Assert(formattedSuccessfully); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.