From d739ce4748863e944a83702fac7f5174b8ba6ffb Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 6 Oct 2017 08:21:47 -0400 Subject: Add Span-based Convert Base64 methods --- src/mscorlib/shared/System/Convert.cs | 125 ++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/mscorlib/shared/System/Convert.cs b/src/mscorlib/shared/System/Convert.cs index 4a60164f94..b6637b13fc 100644 --- a/src/mscorlib/shared/System/Convert.cs +++ b/src/mscorlib/shared/System/Convert.cs @@ -2386,7 +2386,7 @@ namespace System { throw new ArgumentNullException(nameof(inArray)); } - return ToBase64String(inArray, 0, inArray.Length, Base64FormattingOptions.None); + return ToBase64String(new ReadOnlySpan(inArray), Base64FormattingOptions.None); } public static String ToBase64String(byte[] inArray, Base64FormattingOptions options) @@ -2395,7 +2395,7 @@ namespace System { throw new ArgumentNullException(nameof(inArray)); } - return ToBase64String(inArray, 0, inArray.Length, options); + return ToBase64String(new ReadOnlySpan(inArray), options); } public static String ToBase64String(byte[] inArray, int offset, int length) @@ -2403,42 +2403,46 @@ namespace System return ToBase64String(inArray, offset, length, Base64FormattingOptions.None); } - public static unsafe String ToBase64String(byte[] inArray, int offset, int length, Base64FormattingOptions options) + public static String ToBase64String(byte[] inArray, int offset, int length, Base64FormattingOptions options) { - //Do data verfication if (inArray == null) throw new ArgumentNullException(nameof(inArray)); if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), SR.ArgumentOutOfRange_Index); if (offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_GenericPositive); - if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks) - throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options)); + if (offset > (inArray.Length - length)) + throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_OffsetLength); - int inArrayLength; - int stringLength; + return ToBase64String(new ReadOnlySpan(inArray, offset, length), options); + } - inArrayLength = inArray.Length; - if (offset > (inArrayLength - length)) - throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_OffsetLength); + public static string ToBase64String(ReadOnlySpan bytes, Base64FormattingOptions options = Base64FormattingOptions.None) + { + if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks) + { + throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options)); + } - if (inArrayLength == 0) - return String.Empty; + if (bytes.Length == 0) + { + return string.Empty; + } bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); - //Create the new string. This is the maximally required length. - stringLength = ToBase64_CalculateAndValidateOutputLength(length, insertLineBreaks); + string result = string.FastAllocateString(ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks)); - string returnString = string.FastAllocateString(stringLength); - fixed (char* outChars = returnString) + unsafe { - fixed (byte* inData = &inArray[0]) + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + fixed (char* charsPtr = result) { - int j = ConvertToBase64Array(outChars, inData, offset, length, insertLineBreaks); - Debug.Assert(returnString.Length == j, "returnString.Length == j"); - return returnString; + int charsWritten = ConvertToBase64Array(charsPtr, bytesPtr, 0, bytes.Length, insertLineBreaks); + Debug.Assert(result.Length == charsWritten, $"Expected {result.Length} == {charsWritten}"); } } + + return result; } public static int ToBase64CharArray(byte[] inArray, int offsetIn, int length, char[] outArray, int offsetOut) @@ -2462,7 +2466,7 @@ namespace System if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks) { - throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options)); + throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options)); } @@ -2501,6 +2505,36 @@ namespace System return retVal; } + public static unsafe bool TryToBase64Chars(ReadOnlySpan bytes, Span chars, out int charsWritten, Base64FormattingOptions options = Base64FormattingOptions.None) + { + if (options < Base64FormattingOptions.None || options > Base64FormattingOptions.InsertLineBreaks) + { + throw new ArgumentException(string.Format(SR.Arg_EnumIllegalVal, (int)options), nameof(options)); + } + + if (bytes.Length == 0) + { + charsWritten = 0; + return true; + } + + bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); + + int charLengthRequired = ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks); + if (charLengthRequired > chars.Length) + { + charsWritten = 0; + return false; + } + + fixed (char* outChars = &chars.DangerousGetPinnableReference()) + fixed (byte* inData = &bytes.DangerousGetPinnableReference()) + { + charsWritten = ConvertToBase64Array(outChars, inData, 0, bytes.Length, insertLineBreaks); + return true; + } + } + private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int offset, int length, bool insertLineBreaks) { int lengthmod3 = length % 3; @@ -2612,6 +2646,53 @@ namespace System } } + public static bool TryFromBase64String(string s, Span bytes, out int bytesWritten) + { + if (s == null) + { + throw new ArgumentNullException(nameof(s)); + } + + return TryFromBase64Chars(s.AsReadOnlySpan(), bytes, out bytesWritten); + } + + public static unsafe bool TryFromBase64Chars(ReadOnlySpan chars, Span bytes, out int bytesWritten) + { + if (chars.Length == 0) + { + bytesWritten = 0; + return true; + } + + // We need to get rid of any trailing white spaces. + // Otherwise we would be rejecting input such as "abc= ": + while (chars.Length > 0) + { + char lastChar = chars[chars.Length - 1]; + if (lastChar != ' ' && lastChar != '\n' && lastChar != '\r' && lastChar != '\t') + { + break; + } + chars = chars.Slice(0, chars.Length - 1); + } + + fixed (char* charsPtr = &chars.DangerousGetPinnableReference()) + { + int resultLength = FromBase64_ComputeResultLength(charsPtr, chars.Length); + Debug.Assert(resultLength >= 0); + if (resultLength > bytes.Length) + { + bytesWritten = 0; + return false; + } + + fixed (byte* bytesPtr = &bytes.DangerousGetPinnableReference()) + { + bytesWritten = FromBase64_Decode(charsPtr, chars.Length, bytesPtr, bytes.Length); + return true; + } + } + } /// /// Converts the specified range of a Char array, which encodes binary data as Base64 digits, to the equivalent byte array. -- cgit v1.2.3