-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
VersionConverter.cs
137 lines (119 loc) · 5.21 KB
/
VersionConverter.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Text.Json.Nodes;
using System.Text.Json.Schema;
namespace System.Text.Json.Serialization.Converters
{
internal sealed class VersionConverter : JsonPrimitiveConverter<Version?>
{
#if NET
private const int MinimumVersionLength = 3; // 0.0
private const int MaximumVersionLength = 43; // 2147483647.2147483647.2147483647.2147483647
private const int MaximumEscapedVersionLength = JsonConstants.MaxExpansionFactorWhileEscaping * MaximumVersionLength;
#endif
public override Version? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType is JsonTokenType.Null)
{
return null;
}
if (reader.TokenType != JsonTokenType.String)
{
ThrowHelper.ThrowInvalidOperationException_ExpectedString(reader.TokenType);
}
return ReadCore(ref reader);
}
private static Version ReadCore(ref Utf8JsonReader reader)
{
Debug.Assert(reader.TokenType is JsonTokenType.PropertyName or JsonTokenType.String);
#if NET
if (!JsonHelpers.IsInRangeInclusive(reader.ValueLength, MinimumVersionLength, MaximumEscapedVersionLength))
{
ThrowHelper.ThrowFormatException(DataType.Version);
}
Span<char> charBuffer = stackalloc char[MaximumEscapedVersionLength];
int bytesWritten = reader.CopyString(charBuffer);
ReadOnlySpan<char> source = charBuffer.Slice(0, bytesWritten);
if (!char.IsDigit(source[0]) || !char.IsDigit(source[^1]))
{
// Since leading and trailing whitespaces are forbidden throughout System.Text.Json converters
// we need to make sure that our input doesn't have them,
// and if it has - we need to throw, to match behaviour of other converters
// since Version.TryParse allows them and silently parses input to Version
ThrowHelper.ThrowFormatException(DataType.Version);
}
if (Version.TryParse(source, out Version? result))
{
return result;
}
#else
string? versionString = reader.GetString();
if (!string.IsNullOrEmpty(versionString) && (!char.IsDigit(versionString[0]) || !char.IsDigit(versionString[versionString.Length - 1])))
{
// Since leading and trailing whitespaces are forbidden throughout System.Text.Json converters
// we need to make sure that our input doesn't have them,
// and if it has - we need to throw, to match behaviour of other converters
// since Version.TryParse allows them and silently parses input to Version
ThrowHelper.ThrowFormatException(DataType.Version);
}
if (Version.TryParse(versionString, out Version? result))
{
return result;
}
#endif
ThrowHelper.ThrowJsonException();
return null;
}
public override void Write(Utf8JsonWriter writer, Version? value, JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
return;
}
#if NET
#if NET8_0_OR_GREATER
Span<byte> span = stackalloc byte[MaximumVersionLength];
#else
Span<char> span = stackalloc char[MaximumVersionLength];
#endif
bool formattedSuccessfully = value.TryFormat(span, out int charsWritten);
Debug.Assert(formattedSuccessfully && charsWritten >= MinimumVersionLength);
writer.WriteStringValue(span.Slice(0, charsWritten));
#else
writer.WriteStringValue(value.ToString());
#endif
}
internal override Version ReadAsPropertyNameCore(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return ReadCore(ref reader);
}
internal override void WriteAsPropertyNameCore(Utf8JsonWriter writer, Version value, JsonSerializerOptions options, bool isWritingExtensionDataProperty)
{
if (value is null)
{
ThrowHelper.ThrowArgumentNullException(nameof(value));
}
#if NET
#if NET8_0_OR_GREATER
Span<byte> span = stackalloc byte[MaximumVersionLength];
#else
Span<char> span = stackalloc char[MaximumVersionLength];
#endif
bool formattedSuccessfully = value.TryFormat(span, out int charsWritten);
Debug.Assert(formattedSuccessfully && charsWritten >= MinimumVersionLength);
writer.WritePropertyName(span.Slice(0, charsWritten));
#else
writer.WritePropertyName(value.ToString());
#endif
}
internal override JsonSchema? GetSchema(JsonNumberHandling _) =>
new()
{
Type = JsonSchemaType.String,
Comment = "Represents a version string.",
Pattern = @"^\d+(\.\d+){1,3}$",
};
}
}