Skip to content

Commit 7b11a14

Browse files
committed
Reorder decode on BytesStringToArrayJsonConverter
The priority should now be: Hex -> Base64Url -> Base64
1 parent 0e30116 commit 7b11a14

1 file changed

Lines changed: 44 additions & 45 deletions

File tree

Utility/Json/Converters/BytesStringToBytesArrayJsonConverter.cs

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,47 @@ public class BytesStringToArrayJsonConverter<TStruct> : JsonConverter<TStruct[]?
4444
Unsafe.SkipInit(out byte[]? utf8DataBuffer);
4545
try
4646
{
47-
// Try decode in Base64Url first.
47+
// Try decode in Hex first.
48+
if (utf8DataLength % 2 == 0)
49+
{
50+
// If the sample data length is odd, then trim the length (to make it even).
51+
if (utf8SampleData.Length % 2 != 0)
52+
{
53+
// Cut to up-to 16 bytes for sample data
54+
utf8SampleData = utf8SampleData[..Math.Min(utf8SampleData.Length - 1, 16)];
55+
}
56+
57+
// Try to decode the sample data.
58+
Span<byte> hexTestDecodeBuffer = stackalloc byte[utf8SampleData.Length / 2];
59+
OperationStatus hexTestDecodeStatus = Convert.FromHexString(utf8SampleData, hexTestDecodeBuffer, out _, out _);
60+
if (hexTestDecodeStatus != OperationStatus.Done)
61+
{
62+
goto ResumeToBase64;
63+
}
64+
65+
// Alloc buffer
66+
utf8DataBuffer = utf8DataLength > 2048 ? ArrayPool<byte>.Shared.Rent(utf8DataLength) : null;
67+
Span<byte> utf8DataSpan = utf8DataBuffer ?? stackalloc byte[utf8DataLength];
68+
69+
GetUtf8Data(ref reader, utf8DataSpan);
70+
utf8DataSpan = utf8DataSpan[..utf8DataLength];
71+
72+
// Decode the hex string
73+
int hexBytesToDecodeLength = utf8DataLength / 2;
74+
TStruct[] hexDecodedStructBuffer = GC.AllocateUninitializedArray<TStruct>(hexBytesToDecodeLength / sizeof(TStruct));
75+
Span<byte> hexDecodedStructSpan = MemoryMarshal.AsBytes(hexDecodedStructBuffer.AsSpan());
76+
77+
hexTestDecodeStatus = Convert.FromHexString(utf8DataSpan, hexDecodedStructSpan, out _, out _);
78+
if (hexTestDecodeStatus != OperationStatus.Done)
79+
{
80+
ThrowFullHexDecodeFailedException(hexTestDecodeStatus);
81+
}
82+
83+
return hexDecodedStructBuffer;
84+
}
85+
86+
ResumeToBase64:
87+
// Then try Base64Url.
4888
if (Base64Url.IsValid(utf8SampleData))
4989
{
5090
// Alloc buffer
@@ -70,7 +110,7 @@ public class BytesStringToArrayJsonConverter<TStruct> : JsonConverter<TStruct[]?
70110
return base64DecodedStructBuffer;
71111
}
72112

73-
// Try decode in Base64 first.
113+
// Lastly, try Base64Url.
74114
if (Base64.IsValid(utf8SampleData))
75115
{
76116
// Alloc buffer
@@ -100,45 +140,6 @@ public class BytesStringToArrayJsonConverter<TStruct> : JsonConverter<TStruct[]?
100140
utf8DataSpan.CopyTo(base64DecodedStructSpan);
101141
return base64DecodedStructBuffer;
102142
}
103-
104-
// Try decode in Hex.
105-
if (utf8DataLength % 2 == 0)
106-
{
107-
// If the sample data length is odd, then trim the length (to make it even).
108-
if (utf8SampleData.Length % 2 != 0)
109-
{
110-
// Cut to up-to 16 bytes for sample data
111-
utf8SampleData = utf8SampleData[..Math.Min(utf8SampleData.Length - 1, 16)];
112-
}
113-
114-
// Try to decode the sample data.
115-
Span<byte> hexTestDecodeBuffer = stackalloc byte[utf8SampleData.Length / 2];
116-
OperationStatus hexTestDecodeStatus = Convert.FromHexString(utf8SampleData, hexTestDecodeBuffer, out _, out _);
117-
if (hexTestDecodeStatus != OperationStatus.Done)
118-
{
119-
ThrowTestingHexDecodeFailedException(hexTestDecodeStatus);
120-
}
121-
122-
// Alloc buffer
123-
utf8DataBuffer = utf8DataLength > 2048 ? ArrayPool<byte>.Shared.Rent(utf8DataLength) : null;
124-
Span<byte> utf8DataSpan = utf8DataBuffer ?? stackalloc byte[utf8DataLength];
125-
126-
GetUtf8Data(ref reader, utf8DataSpan);
127-
utf8DataSpan = utf8DataSpan[..utf8DataLength];
128-
129-
// Decode the hex string
130-
int hexBytesToDecodeLength = utf8DataLength / 2;
131-
TStruct[] hexDecodedStructBuffer = GC.AllocateUninitializedArray<TStruct>(hexBytesToDecodeLength / sizeof(TStruct));
132-
Span<byte> hexDecodedStructSpan = MemoryMarshal.AsBytes(hexDecodedStructBuffer.AsSpan());
133-
134-
hexTestDecodeStatus = Convert.FromHexString(utf8DataSpan, hexDecodedStructSpan, out _, out _);
135-
if (hexTestDecodeStatus != OperationStatus.Done)
136-
{
137-
ThrowFullHexDecodeFailedException(hexTestDecodeStatus);
138-
}
139-
140-
return hexDecodedStructBuffer;
141-
}
142143
}
143144
finally
144145
{
@@ -148,14 +149,12 @@ public class BytesStringToArrayJsonConverter<TStruct> : JsonConverter<TStruct[]?
148149
}
149150
}
150151

151-
throw new InvalidOperationException($"Value must be in Base64 or Hex string format!");
152+
// Gave up :(
153+
throw new InvalidOperationException("Value must be in Base64 or Hex string format!");
152154

153155
static void ThrowInvalidStructSizeToBufferException(int sizeOfUtf8Data, int sizeOfStruct) =>
154156
throw new InvalidOperationException($"The decoded data length is not a multiple of the struct size ({sizeOfUtf8Data} % {sizeOfStruct} = {sizeOfUtf8Data % sizeOfStruct}).");
155157

156-
static void ThrowTestingHexDecodeFailedException(OperationStatus status) =>
157-
throw new InvalidOperationException($"Failed while testing to decode Hex value with status: {status}.");
158-
159158
static void ThrowFullHexDecodeFailedException(OperationStatus status) =>
160159
throw new InvalidOperationException($"Failed while trying to fully decode the Hex value with status: {status}.");
161160

0 commit comments

Comments
 (0)