From 2f331d66104eaf7e0806d9e6bdbfc4ba4fff8e31 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 02:31:37 +0200 Subject: [PATCH 01/17] Use utf8 version --- .../src/System/Buffers/Text/Base64.cs | 0 .../src/System/Buffers/Text/Base64Decoder.cs | 16 ---------------- .../src/System/Buffers/Text/Base64Encoder.cs | 17 +++++++++++++++++ .../System.Memory/src/System.Memory.csproj | 6 +++--- .../src/System.Private.CoreLib.Shared.projitems | 2 ++ .../src/System/Convert.cs | 13 ++++++++++++- 6 files changed, 34 insertions(+), 20 deletions(-) rename src/libraries/{System.Memory => Common}/src/System/Buffers/Text/Base64.cs (100%) rename src/libraries/{System.Memory => Common}/src/System/Buffers/Text/Base64Decoder.cs (98%) rename src/libraries/{System.Memory => Common}/src/System/Buffers/Text/Base64Encoder.cs (97%) diff --git a/src/libraries/System.Memory/src/System/Buffers/Text/Base64.cs b/src/libraries/Common/src/System/Buffers/Text/Base64.cs similarity index 100% rename from src/libraries/System.Memory/src/System/Buffers/Text/Base64.cs rename to src/libraries/Common/src/System/Buffers/Text/Base64.cs diff --git a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs similarity index 98% rename from src/libraries/System.Memory/src/System/Buffers/Text/Base64Decoder.cs rename to src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs index b0c491a5beaba..143fb6f76a2d0 100644 --- a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs @@ -477,22 +477,6 @@ private static unsafe void Avx2Decode(ref byte* srcBytes, ref byte* destBytes, b destBytes = dest; } - // This can be replaced once https://github.com/dotnet/runtime/issues/63331 is implemented. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 SimdShuffle(Vector128 left, Vector128 right, Vector128 mask8F) - { - Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); - - if (Ssse3.IsSupported) - { - return Ssse3.Shuffle(left, right); - } - else - { - return AdvSimd.Arm64.VectorTableLookup(left, Vector128.BitwiseAnd(right, mask8F)); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe void Vector128Decode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { diff --git a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs similarity index 97% rename from src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs rename to src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs index 9eb60032b65e4..8538db086ca62 100644 --- a/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs @@ -1,6 +1,7 @@ // 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.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -17,6 +18,22 @@ namespace System.Buffers.Text /// public static partial class Base64 { + // This can be replaced once https://github.com/dotnet/runtime/issues/63331 is implemented. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 SimdShuffle(Vector128 left, Vector128 right, Vector128 mask8F) + { + Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); + + if (Ssse3.IsSupported) + { + return Ssse3.Shuffle(left, right); + } + else + { + return AdvSimd.Arm64.VectorTableLookup(left, Vector128.BitwiseAnd(right, mask8F)); + } + } + /// /// Encode the span of binary data into UTF-8 encoded text represented as base64. /// diff --git a/src/libraries/System.Memory/src/System.Memory.csproj b/src/libraries/System.Memory/src/System.Memory.csproj index 7294197f7a962..1f2e8675a5e2f 100644 --- a/src/libraries/System.Memory/src/System.Memory.csproj +++ b/src/libraries/System.Memory/src/System.Memory.csproj @@ -25,9 +25,9 @@ - - - + + + diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index d978ef0b8c488..27588a75dc15c 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -243,6 +243,8 @@ + + diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 5b212556c684e..398cb5ddf74b2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -6,6 +6,8 @@ using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Buffers.Text; +using System.Text; namespace System { @@ -2336,7 +2338,16 @@ public static string ToBase64String(ReadOnlySpan bytes, Base64FormattingOp } bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); - string result = string.FastAllocateString(ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks)); + int outputLength = ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks); + + if (!insertLineBreaks && bytes.Length >= 64) + { + Span utf8Result = outputLength <= 256 ? stackalloc byte[256] : new byte[outputLength]; + Base64.EncodeToUtf8(bytes, utf8Result, out int _, out int _); + return Encoding.Latin1.GetString(utf8Result); + } + + string result = string.FastAllocateString(outputLength); unsafe { From 80dd0fcdc3554f95da762c26e34997408b7c6d83 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 02:54:29 +0200 Subject: [PATCH 02/17] Clean up --- .../System.Private.CoreLib/src/System/Convert.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 398cb5ddf74b2..5e55308536c37 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2342,9 +2342,16 @@ public static string ToBase64String(ReadOnlySpan bytes, Base64FormattingOp if (!insertLineBreaks && bytes.Length >= 64) { - Span utf8Result = outputLength <= 256 ? stackalloc byte[256] : new byte[outputLength]; - Base64.EncodeToUtf8(bytes, utf8Result, out int _, out int _); - return Encoding.Latin1.GetString(utf8Result); + // For large inputs it's faster to allocate a temp buffer and call UTF8 version + // which is then extended to UTF8 via Latin1.GetString (base64 is always ASCI) + [MethodImpl(MethodImplOptions.NoInlining)] + static string ToBase64StringLargeInputs(ReadOnlySpan data, int outputLen) + { + Span utf8buffer = outputLen <= 256 ? stackalloc byte[256] : new byte[outputLen]; + Base64.EncodeToUtf8(data, utf8buffer, out int _, out int _); + return Encoding.Latin1.GetString(utf8buffer); + } + return ToBase64StringLargeInputs(bytes, outputLength); } string result = string.FastAllocateString(outputLength); From 4e2681100493142ebafa862471529a5ad080fe4f Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 02:57:05 +0200 Subject: [PATCH 03/17] Clean up --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 5e55308536c37..6beecad951d31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2340,7 +2340,7 @@ public static string ToBase64String(ReadOnlySpan bytes, Base64FormattingOp bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); int outputLength = ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks); - if (!insertLineBreaks && bytes.Length >= 64) + if (!insertLineBreaks && bytes.Length >= 64 && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported)) { // For large inputs it's faster to allocate a temp buffer and call UTF8 version // which is then extended to UTF8 via Latin1.GetString (base64 is always ASCI) From 7ef13110444600d58428e3a474aa7df432364e0c Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 03:02:22 +0200 Subject: [PATCH 04/17] add an assert --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 6beecad951d31..a9ca98ae6cc7c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2348,7 +2348,8 @@ public static string ToBase64String(ReadOnlySpan bytes, Base64FormattingOp static string ToBase64StringLargeInputs(ReadOnlySpan data, int outputLen) { Span utf8buffer = outputLen <= 256 ? stackalloc byte[256] : new byte[outputLen]; - Base64.EncodeToUtf8(data, utf8buffer, out int _, out int _); + OperationStatus status = Base64.EncodeToUtf8(data, utf8buffer, out int _, out int _); + Debug.Assert(status == OperationStatus.Done); return Encoding.Latin1.GetString(utf8buffer); } return ToBase64StringLargeInputs(bytes, outputLength); From 00dc7c613305280086a91ada493cc0eae329ead8 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 8 Jul 2022 03:33:22 +0200 Subject: [PATCH 05/17] Update Convert.cs --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index a9ca98ae6cc7c..a9fa9476a8dd3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2340,7 +2340,7 @@ public static string ToBase64String(ReadOnlySpan bytes, Base64FormattingOp bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); int outputLength = ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks); - if (!insertLineBreaks && bytes.Length >= 64 && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported)) + if (!insertLineBreaks && bytes.Length >= 64) { // For large inputs it's faster to allocate a temp buffer and call UTF8 version // which is then extended to UTF8 via Latin1.GetString (base64 is always ASCI) From e7cdaf06966d4d34a9ce9acffc7e13265adbdc38 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 8 Jul 2022 10:07:51 +0200 Subject: [PATCH 06/17] Update Convert.cs --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index a9fa9476a8dd3..6abceb2c197a4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -6,6 +6,7 @@ using System.Globalization; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Buffers; using System.Buffers.Text; using System.Text; From 29a6b3c49a697b2216210a6649b9fc3de44f9984 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 11:31:34 +0200 Subject: [PATCH 07/17] change access --- src/libraries/Common/src/System/Buffers/Text/Base64.cs | 7 ++++++- .../Common/src/System/Buffers/Text/Base64Decoder.cs | 7 ++++++- .../Common/src/System/Buffers/Text/Base64Encoder.cs | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64.cs b/src/libraries/Common/src/System/Buffers/Text/Base64.cs index 4b1f597e5e8b5..dc2959dd62861 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64.cs +++ b/src/libraries/Common/src/System/Buffers/Text/Base64.cs @@ -6,7 +6,12 @@ namespace System.Buffers.Text { - public static partial class Base64 +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + static partial class Base64 { [Conditional("DEBUG")] private static unsafe void AssertRead(byte* src, byte* srcStart, int srcLength) diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs index 143fb6f76a2d0..edbf000b7cf12 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs @@ -13,7 +13,12 @@ namespace System.Buffers.Text // AVX2 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/avx2 // Vector128 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/ssse3 - public static partial class Base64 +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + static partial class Base64 { /// /// Decode the span of UTF-8 encoded text represented as base64 into binary data. diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs index 8538db086ca62..84dfe7c0a322e 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs @@ -16,7 +16,12 @@ namespace System.Buffers.Text /// /// Convert between binary data and UTF-8 encoded text that is represented in base 64. /// - public static partial class Base64 +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + static partial class Base64 { // This can be replaced once https://github.com/dotnet/runtime/issues/63331 is implemented. [MethodImpl(MethodImplOptions.AggressiveInlining)] From a0085e7c40dba92960dfdb0b8d28ef5a56a1829d Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 11:54:00 +0200 Subject: [PATCH 08/17] fix build --- src/libraries/Common/src/System/Buffers/Text/Base64.cs | 4 ++-- src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs | 4 ++-- src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64.cs b/src/libraries/Common/src/System/Buffers/Text/Base64.cs index dc2959dd62861..d34d6725e1436 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64.cs +++ b/src/libraries/Common/src/System/Buffers/Text/Base64.cs @@ -7,9 +7,9 @@ namespace System.Buffers.Text { #if SYSTEM_PRIVATE_CORELIB - public -#else internal +#else + public #endif static partial class Base64 { diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs index edbf000b7cf12..59ba8aefd69a0 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs @@ -14,9 +14,9 @@ namespace System.Buffers.Text // Vector128 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/ssse3 #if SYSTEM_PRIVATE_CORELIB - public -#else internal +#else + public #endif static partial class Base64 { diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs index 84dfe7c0a322e..96ed6342a0489 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs @@ -17,9 +17,9 @@ namespace System.Buffers.Text /// Convert between binary data and UTF-8 encoded text that is represented in base 64. /// #if SYSTEM_PRIVATE_CORELIB - public -#else internal +#else + public #endif static partial class Base64 { From af875831737b257a7d5ed3ed956d0329e6734702 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 16:24:35 +0200 Subject: [PATCH 09/17] Address feedback --- .../ref/System.Memory.Forwards.cs | 1 + .../System.Memory/src/System.Memory.csproj | 3 --- .../System.Private.CoreLib.Shared.projitems | 5 ++-- .../src/System/Buffers/Text/Base64.cs | 7 +----- .../src/System/Buffers/Text/Base64Decoder.cs | 23 +++++++++++++----- .../src/System/Buffers/Text/Base64Encoder.cs | 24 +------------------ .../src/System/Convert.cs | 10 ++++++-- 7 files changed, 31 insertions(+), 42 deletions(-) rename src/libraries/{Common => System.Private.CoreLib}/src/System/Buffers/Text/Base64.cs (93%) rename src/libraries/{Common => System.Private.CoreLib}/src/System/Buffers/Text/Base64Decoder.cs (97%) rename src/libraries/{Common => System.Private.CoreLib}/src/System/Buffers/Text/Base64Encoder.cs (97%) diff --git a/src/libraries/System.Memory/ref/System.Memory.Forwards.cs b/src/libraries/System.Memory/ref/System.Memory.Forwards.cs index 07916d433d445..e79102cfcd350 100644 --- a/src/libraries/System.Memory/ref/System.Memory.Forwards.cs +++ b/src/libraries/System.Memory/ref/System.Memory.Forwards.cs @@ -10,3 +10,4 @@ [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Buffers.MemoryHandle))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Buffers.MemoryManager<>))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Buffers.OperationStatus))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Buffers.Text.Base64))] diff --git a/src/libraries/System.Memory/src/System.Memory.csproj b/src/libraries/System.Memory/src/System.Memory.csproj index 1f2e8675a5e2f..a250bb20793b8 100644 --- a/src/libraries/System.Memory/src/System.Memory.csproj +++ b/src/libraries/System.Memory/src/System.Memory.csproj @@ -25,9 +25,6 @@ - - - diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index 27588a75dc15c..e6842b9549125 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -114,6 +114,9 @@ + + + @@ -243,8 +246,6 @@ - - diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64.cs similarity index 93% rename from src/libraries/Common/src/System/Buffers/Text/Base64.cs rename to src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64.cs index d34d6725e1436..4b1f597e5e8b5 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64.cs @@ -6,12 +6,7 @@ namespace System.Buffers.Text { -#if SYSTEM_PRIVATE_CORELIB - internal -#else - public -#endif - static partial class Base64 + public static partial class Base64 { [Conditional("DEBUG")] private static unsafe void AssertRead(byte* src, byte* srcStart, int srcLength) diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs similarity index 97% rename from src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs rename to src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs index 59ba8aefd69a0..b0c491a5beaba 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64Decoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Decoder.cs @@ -13,12 +13,7 @@ namespace System.Buffers.Text // AVX2 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/avx2 // Vector128 version based on https://github.com/aklomp/base64/tree/e516d769a2a432c08404f1981e73b431566057be/lib/arch/ssse3 -#if SYSTEM_PRIVATE_CORELIB - internal -#else - public -#endif - static partial class Base64 + public static partial class Base64 { /// /// Decode the span of UTF-8 encoded text represented as base64 into binary data. @@ -482,6 +477,22 @@ private static unsafe void Avx2Decode(ref byte* srcBytes, ref byte* destBytes, b destBytes = dest; } + // This can be replaced once https://github.com/dotnet/runtime/issues/63331 is implemented. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 SimdShuffle(Vector128 left, Vector128 right, Vector128 mask8F) + { + Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); + + if (Ssse3.IsSupported) + { + return Ssse3.Shuffle(left, right); + } + else + { + return AdvSimd.Arm64.VectorTableLookup(left, Vector128.BitwiseAnd(right, mask8F)); + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static unsafe void Vector128Decode(ref byte* srcBytes, ref byte* destBytes, byte* srcEnd, int sourceLength, int destLength, byte* srcStart, byte* destStart) { diff --git a/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs similarity index 97% rename from src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs rename to src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs index 96ed6342a0489..9eb60032b65e4 100644 --- a/src/libraries/Common/src/System/Buffers/Text/Base64Encoder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Base64Encoder.cs @@ -1,7 +1,6 @@ // 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.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -16,29 +15,8 @@ namespace System.Buffers.Text /// /// Convert between binary data and UTF-8 encoded text that is represented in base 64. /// -#if SYSTEM_PRIVATE_CORELIB - internal -#else - public -#endif - static partial class Base64 + public static partial class Base64 { - // This can be replaced once https://github.com/dotnet/runtime/issues/63331 is implemented. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static Vector128 SimdShuffle(Vector128 left, Vector128 right, Vector128 mask8F) - { - Debug.Assert((Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) && BitConverter.IsLittleEndian); - - if (Ssse3.IsSupported) - { - return Ssse3.Shuffle(left, right); - } - else - { - return AdvSimd.Arm64.VectorTableLookup(left, Vector128.BitwiseAnd(right, mask8F)); - } - } - /// /// Encode the span of binary data into UTF-8 encoded text represented as base64. /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 6abceb2c197a4..537b7b4b8232d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2348,10 +2348,16 @@ public static string ToBase64String(ReadOnlySpan bytes, Base64FormattingOp [MethodImpl(MethodImplOptions.NoInlining)] static string ToBase64StringLargeInputs(ReadOnlySpan data, int outputLen) { - Span utf8buffer = outputLen <= 256 ? stackalloc byte[256] : new byte[outputLen]; + byte[]? rentedBytes = null; + Span utf8buffer = outputLen <= 256 ? stackalloc byte[256] : (rentedBytes = ArrayPool.Shared.Rent(outputLen)); OperationStatus status = Base64.EncodeToUtf8(data, utf8buffer, out int _, out int _); Debug.Assert(status == OperationStatus.Done); - return Encoding.Latin1.GetString(utf8buffer); + string result = Encoding.Latin1.GetString(utf8buffer); + if (rentedBytes != null) + { + ArrayPool.Shared.Return(rentedBytes); + } + return result; } return ToBase64StringLargeInputs(bytes, outputLength); } From 0617533faf587a265d4adf80f9894847fbe7bf0b Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 16:43:24 +0200 Subject: [PATCH 10/17] Address feedback --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 537b7b4b8232d..e5a8b57aa1a6d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2350,7 +2350,7 @@ static string ToBase64StringLargeInputs(ReadOnlySpan data, int outputLen) { byte[]? rentedBytes = null; Span utf8buffer = outputLen <= 256 ? stackalloc byte[256] : (rentedBytes = ArrayPool.Shared.Rent(outputLen)); - OperationStatus status = Base64.EncodeToUtf8(data, utf8buffer, out int _, out int _); + OperationStatus status = Base64.EncodeToUtf8(data, utf8buffer.Slice(outputLen), out int _, out int _); Debug.Assert(status == OperationStatus.Done); string result = Encoding.Latin1.GetString(utf8buffer); if (rentedBytes != null) From b36c0d2273986e54c382beee0495024e6a7477c1 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 16:59:01 +0200 Subject: [PATCH 11/17] Address feedback --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index e5a8b57aa1a6d..a9c646e78469c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2350,9 +2350,9 @@ static string ToBase64StringLargeInputs(ReadOnlySpan data, int outputLen) { byte[]? rentedBytes = null; Span utf8buffer = outputLen <= 256 ? stackalloc byte[256] : (rentedBytes = ArrayPool.Shared.Rent(outputLen)); - OperationStatus status = Base64.EncodeToUtf8(data, utf8buffer.Slice(outputLen), out int _, out int _); - Debug.Assert(status == OperationStatus.Done); - string result = Encoding.Latin1.GetString(utf8buffer); + OperationStatus status = Base64.EncodeToUtf8(data, utf8buffer, out int _, out int bytesWritten); + Debug.Assert(status == OperationStatus.Done && bytesWritten == outputLen); + string result = Encoding.Latin1.GetString(utf8buffer.Slice(outputLen)); if (rentedBytes != null) { ArrayPool.Shared.Return(rentedBytes); From f1d0b925bb3fcdda0e870c9112956c5711f5f7b0 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 17:02:59 +0200 Subject: [PATCH 12/17] fix compilation issue --- src/libraries/System.Memory/ref/System.Memory.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 648959e958d70..82076983e6062 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -474,15 +474,6 @@ public static void WriteUInt64LittleEndian(System.Span destination, ulong } namespace System.Buffers.Text { - public static partial class Base64 - { - public static System.Buffers.OperationStatus DecodeFromUtf8(System.ReadOnlySpan utf8, System.Span bytes, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) { throw null; } - public static System.Buffers.OperationStatus DecodeFromUtf8InPlace(System.Span buffer, out int bytesWritten) { throw null; } - public static System.Buffers.OperationStatus EncodeToUtf8(System.ReadOnlySpan bytes, System.Span utf8, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) { throw null; } - public static System.Buffers.OperationStatus EncodeToUtf8InPlace(System.Span buffer, int dataLength, out int bytesWritten) { throw null; } - public static int GetMaxDecodedFromUtf8Length(int length) { throw null; } - public static int GetMaxEncodedToUtf8Length(int length) { throw null; } - } public static partial class Utf8Formatter { public static bool TryFormat(bool value, System.Span destination, out int bytesWritten, System.Buffers.StandardFormat format = default(System.Buffers.StandardFormat)) { throw null; } From e951b07b975d0013323f75cfe20007f7110b3930 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 20:31:41 +0200 Subject: [PATCH 13/17] update System.Runtime.cs --- src/libraries/System.Runtime/ref/System.Runtime.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 07abb8a23199f..9c0b990aa5f00 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -7315,6 +7315,18 @@ public enum OperationStatus public delegate void ReadOnlySpanAction(System.ReadOnlySpan span, TArg arg); public delegate void SpanAction(System.Span span, TArg arg); } +namespace System.Buffers.Text +{ + public static partial class Base64 + { + public static System.Buffers.OperationStatus DecodeFromUtf8(System.ReadOnlySpan utf8, System.Span bytes, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) { throw null; } + public static System.Buffers.OperationStatus DecodeFromUtf8InPlace(System.Span buffer, out int bytesWritten) { throw null; } + public static System.Buffers.OperationStatus EncodeToUtf8(System.ReadOnlySpan bytes, System.Span utf8, out int bytesConsumed, out int bytesWritten, bool isFinalBlock = true) { throw null; } + public static System.Buffers.OperationStatus EncodeToUtf8InPlace(System.Span buffer, int dataLength, out int bytesWritten) { throw null; } + public static int GetMaxDecodedFromUtf8Length(int length) { throw null; } + public static int GetMaxEncodedToUtf8Length(int length) { throw null; } + } +} namespace System.CodeDom.Compiler { [System.AttributeUsageAttribute(System.AttributeTargets.All, Inherited=false, AllowMultiple=false)] From 682ef07ba9208776240ce9ff7d7481be932c6b91 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Fri, 8 Jul 2022 21:14:21 +0200 Subject: [PATCH 14/17] Add more tests --- .../tests/System/Convert.ToBase64String.cs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs index c3c5b55693562..91c79750996db 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs @@ -56,5 +56,91 @@ public static void InvalidLength() Assert.Throws(() => Convert.ToBase64String(inputBytes, 0, inputBytes.Length + 1)); Assert.Throws(() => Convert.ToBase64String(inputBytes, 1, inputBytes.Length)); } + + public static IEnumerable ConvertToBase64StringTests_TestData() + { + yield return new object[] { Enumerable.Range(0, 0).Select(i => (byte)i).ToArray(), "" }; + yield return new object[] { Enumerable.Range(0, 1).Select(i => (byte)i).ToArray(), "AA==" }; + yield return new object[] { Enumerable.Range(0, 2).Select(i => (byte)i).ToArray(), "AAE=" }; + yield return new object[] { Enumerable.Range(0, 3).Select(i => (byte)i).ToArray(), "AAEC" }; + yield return new object[] { Enumerable.Range(0, 4).Select(i => (byte)i).ToArray(), "AAECAw==" }; + yield return new object[] { Enumerable.Range(0, 5).Select(i => (byte)i).ToArray(), "AAECAwQ=" }; + yield return new object[] { Enumerable.Range(0, 6).Select(i => (byte)i).ToArray(), "AAECAwQF" }; + yield return new object[] { Enumerable.Range(0, 7).Select(i => (byte)i).ToArray(), "AAECAwQFBg==" }; + yield return new object[] { Enumerable.Range(0, 8).Select(i => (byte)i).ToArray(), "AAECAwQFBgc=" }; + yield return new object[] { Enumerable.Range(0, 9).Select(i => (byte)i).ToArray(), "AAECAwQFBgcI" }; + yield return new object[] { Enumerable.Range(0, 10).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQ==" }; + yield return new object[] { Enumerable.Range(0, 11).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQo=" }; + yield return new object[] { Enumerable.Range(0, 12).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoL" }; + yield return new object[] { Enumerable.Range(0, 13).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA==" }; + yield return new object[] { Enumerable.Range(0, 14).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0=" }; + yield return new object[] { Enumerable.Range(0, 15).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0O" }; + yield return new object[] { Enumerable.Range(0, 16).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODw==" }; + yield return new object[] { Enumerable.Range(0, 17).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxA=" }; + yield return new object[] { Enumerable.Range(0, 18).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAR" }; + yield return new object[] { Enumerable.Range(0, 19).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREg==" }; + yield return new object[] { Enumerable.Range(0, 20).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhM=" }; + yield return new object[] { Enumerable.Range(0, 21).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMU" }; + yield return new object[] { Enumerable.Range(0, 22).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFQ==" }; + yield return new object[] { Enumerable.Range(0, 23).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRY=" }; + yield return new object[] { Enumerable.Range(0, 24).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYX" }; + yield return new object[] { Enumerable.Range(0, 25).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGA==" }; + yield return new object[] { Enumerable.Range(0, 26).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBk=" }; + yield return new object[] { Enumerable.Range(0, 27).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBka" }; + yield return new object[] { Enumerable.Range(0, 28).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGw==" }; + yield return new object[] { Enumerable.Range(0, 29).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxw=" }; + yield return new object[] { Enumerable.Range(0, 30).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwd" }; + yield return new object[] { Enumerable.Range(0, 31).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHg==" }; + yield return new object[] { Enumerable.Range(0, 32).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=" }; + yield return new object[] { Enumerable.Range(0, 33).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8g" }; + yield return new object[] { Enumerable.Range(0, 34).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gIQ==" }; + yield return new object[] { Enumerable.Range(0, 35).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISI=" }; + yield return new object[] { Enumerable.Range(0, 36).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIj" }; + yield return new object[] { Enumerable.Range(0, 37).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJA==" }; + yield return new object[] { Enumerable.Range(0, 38).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCU=" }; + yield return new object[] { Enumerable.Range(0, 39).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUm" }; + yield return new object[] { Enumerable.Range(0, 40).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJw==" }; + yield return new object[] { Enumerable.Range(0, 41).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJyg=" }; + yield return new object[] { Enumerable.Range(0, 42).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygp" }; + yield return new object[] { Enumerable.Range(0, 43).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKg==" }; + yield return new object[] { Enumerable.Range(0, 44).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKis=" }; + yield return new object[] { Enumerable.Range(0, 45).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKiss" }; + yield return new object[] { Enumerable.Range(0, 46).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLQ==" }; + yield return new object[] { Enumerable.Range(0, 47).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4=" }; + yield return new object[] { Enumerable.Range(0, 48).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4v" }; + yield return new object[] { Enumerable.Range(0, 49).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMA==" }; + yield return new object[] { Enumerable.Range(0, 50).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDE=" }; + yield return new object[] { Enumerable.Range(0, 51).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEy" }; + yield return new object[] { Enumerable.Range(0, 52).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMw==" }; + yield return new object[] { Enumerable.Range(0, 53).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ=" }; + yield return new object[] { Enumerable.Range(0, 54).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1" }; + yield return new object[] { Enumerable.Range(0, 55).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Ng==" }; + yield return new object[] { Enumerable.Range(0, 56).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc=" }; + yield return new object[] { Enumerable.Range(0, 57).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4" }; + yield return new object[] { Enumerable.Range(0, 58).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OQ==" }; + yield return new object[] { Enumerable.Range(0, 59).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo=" }; + yield return new object[] { Enumerable.Range(0, 60).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7" }; + yield return new object[] { Enumerable.Range(0, 61).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PA==" }; + yield return new object[] { Enumerable.Range(0, 62).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0=" }; + yield return new object[] { Enumerable.Range(0, 63).Select(i => (byte)i).ToArray(), "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+" }; + yield return new object[] { Encoding.Unicode.GetBytes("aaaabbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccdd"), "YQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAA==" }; + yield return new object[] { Encoding.Unicode.GetBytes("vbnmbbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccddx"), "dgBiAG4AbQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAHgA" }; + yield return new object[] { Encoding.Unicode.GetBytes("rrrrbbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccdd\0"), "cgByAHIAcgBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAAAA" }; + yield return new object[] { Encoding.Unicode.GetBytes("uuuubbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccdd\0feffe"), "dQB1AHUAdQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAAAAZgBlAGYAZgBlAA==" }; + yield return new object[] { Encoding.Unicode.GetBytes("kkkkkbbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccddxприветмир你好世界"), "awBrAGsAawBrAGIAYgBiAGIAYwBjAGMAYwBkAGQAZABkAGQAZABkAGUAZQBlAGUAZQBhAGEAYQBhAGIAYgBiAGIAYwBjAGMAYwBkAGQAZABkAGQAZABkAGUAZQBlAGUAZQBhAGEAYQBhAGIAYgBiAGIAYwBjAGMAYwBkAGQAeAA/BEAEOAQyBDUEQgQ8BDgEQARgT31ZFk5MdQ==" }; + yield return new object[] { Encoding.Unicode.GetBytes(",,,,bbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccddxприветмир你好世界ddddeeeeea"), "LAAsACwALABiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAHgAPwRABDgEMgQ1BEIEPAQ4BEAEYE99WRZOTHVkAGQAZABkAGUAZQBlAGUAZQBhAA==" }; + yield return new object[] { Encoding.Unicode.GetBytes("____bbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccddaaaabbbbccccdddddddeeeeeaaaabbbbccccdcccd"), "XwBfAF8AXwBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGEAYQBhAGEAYgBiAGIAYgBjAGMAYwBjAGQAZABkAGQAZABkAGQAZQBlAGUAZQBlAGEAYQBhAGEAYgBiAGIAYgBjAGMAYwBjAGQAYwBjAGMAZAA=" }; + yield return new object[] { Encoding.Unicode.GetBytes(" bbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccddaaaabbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccd"), "IAAgACAAIABiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGEAYQBhAGEAYgBiAGIAYgBjAGMAYwBjAGQAZABkAGQAZABkAGQAZQBlAGUAZQBlAGEAYQBhAGEAYgBiAGIAYgBjAGMAYwBjAGQAZABkAGQAZABkAGQAZQBlAGUAZQBlAGEAYQBhAGEAYgBiAGIAYgBjAGMAYwBjAGQA" }; + yield return new object[] { Encoding.Unicode.GetBytes("\0\0bbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccddaaaabbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccddx"), "AAAAAGIAYgBiAGIAYwBjAGMAYwBkAGQAZABkAGQAZABkAGUAZQBlAGUAZQBhAGEAYQBhAGIAYgBiAGIAYwBjAGMAYwBkAGQAZABkAGQAZABkAGUAZQBlAGUAZQBhAGEAYQBhAGIAYgBiAGIAYwBjAGMAYwBkAGQAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAHgA" }; + yield return new object[] { Encoding.Unicode.GetBytes("eeeebbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccdgggdaaaabbbbccccdddddddeeeeeaaaabbbbccccdddddddeeeeeaaaabbbbccccddx"), "ZQBlAGUAZQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABkAGQAZABkAGQAZABlAGUAZQBlAGUAYQBhAGEAYQBiAGIAYgBiAGMAYwBjAGMAZABnAGcAZwBkAGEAYQBhAGEAYgBiAGIAYgBjAGMAYwBjAGQAZABkAGQAZABkAGQAZQBlAGUAZQBlAGEAYQBhAGEAYgBiAGIAYgBjAGMAYwBjAGQAZABkAGQAZABkAGQAZQBlAGUAZQBlAGEAYQBhAGEAYgBiAGIAYgBjAGMAYwBjAGQAZAB4AA==" }; + } + + + [Theory] + [MemberData(nameof(ConvertToBase64StringTests_TestData))] + public static void ConvertToBase64String(byte[] inputBytes, string expectedBase64) + { + Assert.Equal(expectedBase64, Convert.ToBase64String(inputBytes)); + } } } From 847152113ae592811f29ab564a689ac43ec51b32 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 8 Jul 2022 22:35:58 +0200 Subject: [PATCH 15/17] Update Convert.ToBase64String.cs --- .../tests/System/Convert.ToBase64String.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs index 91c79750996db..1cd6c944ec96c 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. using Xunit; +using System.Collections.Generic; +using System.Text; namespace System.Tests { From 70cb0dfc32eb33fb3795f36b32140b44f0b8bda0 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Fri, 8 Jul 2022 23:34:25 +0200 Subject: [PATCH 16/17] Update Convert.ToBase64String.cs --- .../tests/System/Convert.ToBase64String.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs b/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs index 1cd6c944ec96c..d8a34d7816db6 100644 --- a/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs +++ b/src/libraries/System.Runtime.Extensions/tests/System/Convert.ToBase64String.cs @@ -4,6 +4,7 @@ using Xunit; using System.Collections.Generic; using System.Text; +using System.Linq; namespace System.Tests { From 19c57952638b16276a09b19336b2e351248c80c8 Mon Sep 17 00:00:00 2001 From: Egor Bogatov Date: Sun, 10 Jul 2022 00:41:03 +0200 Subject: [PATCH 17/17] oops... --- src/libraries/System.Private.CoreLib/src/System/Convert.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index a9c646e78469c..34a4dd112bc72 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2352,7 +2352,7 @@ static string ToBase64StringLargeInputs(ReadOnlySpan data, int outputLen) Span utf8buffer = outputLen <= 256 ? stackalloc byte[256] : (rentedBytes = ArrayPool.Shared.Rent(outputLen)); OperationStatus status = Base64.EncodeToUtf8(data, utf8buffer, out int _, out int bytesWritten); Debug.Assert(status == OperationStatus.Done && bytesWritten == outputLen); - string result = Encoding.Latin1.GetString(utf8buffer.Slice(outputLen)); + string result = Encoding.Latin1.GetString(utf8buffer.Slice(0, outputLen)); if (rentedBytes != null) { ArrayPool.Shared.Return(rentedBytes);