Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Span support to Webencoders with direct encoding #338

Closed
wants to merge 8 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Cleanup
  • Loading branch information
gfoidl committed Jun 18, 2018
commit a08b7a5d47658f48b2a4d0b7cb2bb2d95d5988a5
74 changes: 35 additions & 39 deletions shared/Microsoft.Extensions.WebEncoders.Sources/WebEncoders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@

using System;
using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Microsoft.Extensions.WebEncoders.Sources;

#if !NET461
using System.Numerics;
#endif

#if WebEncoders_In_WebUtilities
namespace Microsoft.AspNetCore.WebUtilities
#else
Expand All @@ -29,7 +24,9 @@ namespace Microsoft.Extensions.Internal
#endif
static class WebEncoders
{
#if !NETCOREAPP2_1
private const int MaxStackallocBytes = 256;
#endif
private const int MaxEncodedLength = (int.MaxValue / 4) * 3; // encode inflates the data by 4/3
private static readonly byte[] EmptyBytes = new byte[0];

Expand Down Expand Up @@ -94,7 +91,8 @@ public static byte[] Base64UrlDecode(ReadOnlySpan<char> base64Url)

var base64Len = GetBufferSizeRequiredToUrlDecode(base64Url.Length, out int dataLength);
var data = new byte[dataLength];
var written = Base64UrlDecodeCore(base64Url, data);
var status = Base64UrlDecodeCore(base64Url, data, out int consumed, out int written);
Debug.Assert(base64Url.Length == consumed);
Debug.Assert(data.Length == written);

return data;
Expand All @@ -118,7 +116,11 @@ public static int Base64UrlDecode(ReadOnlySpan<char> base64Url, Span<byte> data)
return 0;
}

return Base64UrlDecodeCore(base64Url, data);
var status = Base64UrlDecodeCore(base64Url, data, out int consumed, out int written);
Debug.Assert(base64Url.Length == consumed);
Debug.Assert(data.Length >= written);

return written;
}

/// <summary>
Expand Down Expand Up @@ -193,7 +195,8 @@ public static byte[] Base64UrlDecode(string input, int offset, char[] buffer, in
}

var data = new byte[dataLength];
var written = Base64UrlDecodeCore(input.AsSpan(offset, count), data);
var status = Base64UrlDecodeCore(input.AsSpan(offset, count), data, out int consumed, out int written);
Debug.Assert(count == consumed);
Debug.Assert(dataLength == written);

return data;
Expand Down Expand Up @@ -254,8 +257,9 @@ public static unsafe string Base64UrlEncode(ReadOnlySpan<byte> data)
return string.Create(base64UrlLen, (Ptr: (IntPtr)ptr, data.Length), (base64Url, state) =>
{
var bytes = new ReadOnlySpan<byte>(state.Ptr.ToPointer(), state.Length);
var urlEncodedLen = Base64UrlEncodeCore(bytes, base64Url);
Debug.Assert(base64Url.Length == urlEncodedLen);
var status = Base64UrlEncodeCore(bytes, base64Url, out int consumed, out int written);
Debug.Assert(bytes.Length == consumed);
Debug.Assert(base64Url.Length == written);
});
}
#else
Expand All @@ -272,12 +276,12 @@ public static unsafe string Base64UrlEncode(ReadOnlySpan<byte> data)
#else
: arrayToReturnToPool = ArrayPool<char>.Shared.Rent(base64UrlLen);
#endif
var urlEncodedLen = Base64UrlEncodeCore(data, base64Url);
Debug.Assert(base64UrlLen == urlEncodedLen);
var status = Base64UrlEncodeCore(data, base64Url, out int consumed, out int written);
Debug.Assert(base64UrlLen == written);

fixed (char* ptr = &MemoryMarshal.GetReference(base64Url))
{
return new string(ptr, 0, urlEncodedLen);
return new string(ptr, 0, written);
}
#if !NET461
}
Expand Down Expand Up @@ -308,7 +312,11 @@ public static int Base64UrlEncode(ReadOnlySpan<byte> data, Span<char> base64Url)
return 0;
}

return Base64UrlEncodeCore(data, base64Url);
var status = Base64UrlEncodeCore(data, base64Url, out int consumed, out int written);
Debug.Assert(data.Length == consumed);
Debug.Assert(base64Url.Length >= written);

return written;
}

/// <summary>
Expand Down Expand Up @@ -338,9 +346,7 @@ public static OperationStatus Base64UrlEncode(ReadOnlySpan<byte> data, Span<byte
return OperationStatus.Done;
}

// TODO check if status exception should be thrown

return UrlEncoder<byte>.Encode(data, base64Url, out bytesConsumed, out bytesWritten, isFinalBlock);
return Base64UrlEncodeCore(data, base64Url, out bytesConsumed, out bytesWritten, isFinalBlock);
}

/// <summary>
Expand Down Expand Up @@ -384,7 +390,11 @@ public static int Base64UrlEncode(byte[] input, int offset, char[] output, int o
return 0;
}

return Base64UrlEncodeCore(input.AsSpan(offset, count), output.AsSpan(outputOffset));
var status = Base64UrlEncodeCore(input.AsSpan(offset, count), output.AsSpan(outputOffset), out int consumed, out int written);
Debug.Assert(count == consumed);
Debug.Assert(base64Len >= written);

return written;
}

/// <summary>
Expand Down Expand Up @@ -426,23 +436,9 @@ public static int GetArraySizeRequiredToEncode(int count)
return count == 0 ? 0 : GetBufferSizeRequiredToBase64Encode(count);
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Base64UrlDecodeCore(ReadOnlySpan<char> base64Url, Span<byte> data)
private static OperationStatus Base64UrlDecodeCore<T>(ReadOnlySpan<T> base64Url, Span<byte> data, out int consumed, out int written, bool isFinalBlock = true)
{
var status = UrlEncoder<char>.Decode(base64Url, data, out int consumed, out int written);

if (status != OperationStatus.Done)
{
ThrowHelper.ThrowOperationNotDone(status);
}

return written;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static OperationStatus Base64UrlDecodeCore(ReadOnlySpan<byte> base64Url, Span<byte> data, out int consumed, out int written, bool isFinalBlock)
{
var status = UrlEncoder<byte>.Decode(base64Url, data, out consumed, out written, isFinalBlock);
var status = UrlEncoder<T>.Decode(base64Url, data, out consumed, out written, isFinalBlock);

if (status != OperationStatus.Done && status != OperationStatus.NeedMoreData)
{
Expand All @@ -452,17 +448,16 @@ private static OperationStatus Base64UrlDecodeCore(ReadOnlySpan<byte> base64Url,
return status;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Base64UrlEncodeCore(ReadOnlySpan<byte> data, Span<char> base64Url)
private static OperationStatus Base64UrlEncodeCore<T>(ReadOnlySpan<byte> data, Span<T> base64Url, out int consumed, out int written, bool isFinalBlock = true)
{
var status = UrlEncoder<char>.Encode(data, base64Url, out int consumed, out int written);
var status = UrlEncoder<T>.Encode(data, base64Url, out consumed, out written, isFinalBlock);

if (status != OperationStatus.Done)
if (status != OperationStatus.Done && status != OperationStatus.NeedMoreData)
{
ThrowHelper.ThrowOperationNotDone(status);
}

return written;
return status;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -511,6 +506,7 @@ private static int GetNumBase64PaddingCharsToAddForDecode(int urlEncodedLen)
return result;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetBufferSizeRequiredToBase64Encode(int count)
{
if ((uint)count > MaxEncodedLength)
Expand Down