// See the LICENSE file in the project root for more information.
using System.Globalization;
+using System.Buffers;
+using System.Diagnostics;
namespace System.Text.Json
{
/// Returns <see cref="JsonValueKind.String"/>
/// </summary>
public override JsonValueKind ValueKind { get => JsonValueKind.String; }
+
+ /// <summary>
+ /// Converts the text value of this instance, which should encode binary data as base-64 digits, to an equivalent 8-bit unsigned <see cref="byte"/> array.
+ /// The return value indicates wether the conversion succeeded.
+ /// </summary>
+ /// <param name="value">
+ /// When this method returns, contains the <see cref="byte"/> array equivalent of the text contained in this instance,
+ /// if the conversion succeeded.
+ /// </param>
+ /// <returns>
+ /// <see langword="true"/> if text was converted successfully; othwerwise returns <see langword="false"/>.
+ /// </returns>
+ internal bool TryGetBytesFromBase64(out byte[] value)
+ {
+ Debug.Assert(_value != null);
+
+ // Shortest length of a base-64 string is 4 characters.
+ if (_value.Length < 4)
+ {
+ value = default;
+ return false;
+ }
+
+#if BUILDING_INBOX_LIBRARY
+ // we decode string -> byte, so the resulting length will
+ // be /4 * 3 - padding. To be on the safe side, keep padding and slice later
+ int bufferSize = _value.Length / 4 * 3;
+
+ byte[] arrayToReturnToPool = null;
+ Span<byte> buffer = bufferSize <= JsonConstants.StackallocThreshold
+ ? stackalloc byte[JsonConstants.StackallocThreshold]
+ : arrayToReturnToPool = ArrayPool<byte>.Shared.Rent(bufferSize);
+ try
+ {
+ if (Convert.TryFromBase64String(_value, buffer, out int bytesWritten))
+ {
+ buffer = buffer.Slice(0, bytesWritten);
+ value = buffer.ToArray();
+ return true;
+ }
+ else
+ {
+ value = default;
+ return false;
+ }
+ }
+ finally
+ {
+ if (arrayToReturnToPool != null)
+ {
+ buffer.Clear();
+ ArrayPool<byte>.Shared.Return(arrayToReturnToPool);
+ }
+ }
+
+#else
+ try
+ {
+ value = Convert.FromBase64String(_value);
+ return true;
+ }
+ catch (FormatException)
+ {
+ value = null;
+ return false;
+ }
+#endif
+ }
}
}
[Fact]
public static void TestBytesFromBase64()
{
- Assert.Throws<NotSupportedException>(() => new JsonString().AsJsonElement().GetBytesFromBase64());
+ string valueString = "value";
+ string valueBase64String = "dmFsdWU=";
+
+ Assert.Equal(Encoding.UTF8.GetBytes(valueString), new JsonString(valueBase64String).AsJsonElement().GetBytesFromBase64());
+ Assert.Equal(Encoding.UTF8.GetBytes(SR.LoremIpsum40Words), new JsonString(SR.LoremIpsum40WordsBase64).AsJsonElement().GetBytesFromBase64());
+
+ Assert.Throws<FormatException>(() => new JsonString("Not base-64").AsJsonElement().GetBytesFromBase64());
+ Assert.Throws<FormatException>(() => new JsonString("abc").AsJsonElement().GetBytesFromBase64());
+ Assert.Throws<FormatException>(() => new JsonString("").AsJsonElement().GetBytesFromBase64());
+ Assert.Throws<FormatException>(() => new JsonString().AsJsonElement().GetBytesFromBase64());
+
Assert.Throws<InvalidOperationException>(() => new JsonBoolean().AsJsonElement().GetBytesFromBase64());
+
+ Assert.True(new JsonString(valueBase64String).AsJsonElement().TryGetBytesFromBase64(out byte[] buffer));
+ Assert.Equal(Encoding.UTF8.GetBytes(valueString), buffer);
+ Assert.False(new JsonString().AsJsonElement().TryGetBytesFromBase64(out _));
}
[Fact]
-<root>
+<?xml version="1.0" encoding="utf-8"?>
+<root>
<!--
Microsoft ResX Schema
<data name="BufferWriterAdvancedTooFar" xml:space="preserve">
<value>Cannot advance past the end of the buffer, which has a size of {0}.</value>
</data>
-</root>
\ No newline at end of file
+ <data name="LoremIpsum40Words" xml:space="preserve">
+ <value>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur viverra neque at erat laoreet, at sagittis arcu efficitur. Ut pulvinar eros nec odio cursus eleifend. Maecenas viverra elementum porttitor. Nullam mi velit, malesuada commodo tristique eu, mollis a velit. Morbi.</value>
+ </data>
+ <data name="LoremIpsum40WordsBase64" xml:space="preserve">
+ <value>TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gQ3VyYWJpdHVyIHZpdmVycmEgbmVxdWUgYXQgZXJhdCBsYW9yZWV0LCBhdCBzYWdpdHRpcyBhcmN1IGVmZmljaXR1ci4gVXQgcHVsdmluYXIgZXJvcyBuZWMgb2RpbyBjdXJzdXMgZWxlaWZlbmQuIE1hZWNlbmFzIHZpdmVycmEgZWxlbWVudHVtIHBvcnR0aXRvci4gTnVsbGFtIG1pIHZlbGl0LCBtYWxlc3VhZGEgY29tbW9kbyB0cmlzdGlxdWUgZXUsIG1vbGxpcyBhIHZlbGl0LiBNb3JiaS4=</value>
+ </data>
+</root>