Skip to content

Add Base64url encoding/decoding - #102364

Merged
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url
Jun 14, 2024
Merged

Add Base64url encoding/decoding#102364
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url

Conversation

@buyaa-n

@buyaa-nbuyaa-n commented May 17, 2024

Copy link
Copy Markdown
Contributor
  • Base64Url encoding doesn't append padding, therefore encoded byte count differs from Base64 encoding:

    Source byte countBase64 encoded byte countBase64Url encoded byte count
    142
    243
    344
    486
    587
    688
  • Base64Url decoding ignore whitespace and padding, therefore decodable byte count differs from Base64 decoding, the exact decoding result also depend on isFinalBlock and if padding involved

    Source byte countBase64 decoded byte countBase64Url decoded byte count
    1max 0max 0
    2max 0max 1
    3max 0max 2
    4max 3max 3
    5max 3max 3
    6max 3max 4
    7max 3max 5
    8max 6max 6

Approved API shape:

namespaceSystem.Buffers.Text;publicstaticclassBase64Url{publicstaticintGetMaxDecodedLength(intbase64Length);publicstaticintGetEncodedLength(intbytesLength);publicstaticOperationStatusEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintcharsWritten);publicstaticbyte[]EncodeToUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintbytesConsumed,outintcharsWritten,boolisFinalBlock=true);publicstaticintEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination);publicstaticboolTryEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintcharsWritten);publicstaticchar[]EncodeToChars(ReadOnlySpan<byte>source);publicstaticstringEncodeToString(ReadOnlySpan<byte>source);publicstaticboolTryEncodeToUtf8InPlace(Span<byte>buffer,intdataLength,outintbytesWritten);publicstaticOperationStatusDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintcharsConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination);publicstaticboolTryDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromChars(ReadOnlySpan<char>source);publicstaticintDecodeFromUtf8InPlace(Span<byte>buffer);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText,outintdecodedLength);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText,outintdecodedLength);}

Draft PR until below list is completed, for now mainly for checking perf, run tests on different CI legs, and get early feedback.

  • Handle '%' in decoding as a valid padding character for Base64Url
  • Doc needs improvement
  • A few APIs missing unit tests
  • Fix failures on ARM CI legs

Fixes#1658

@ghost

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@gfoidlgfoidl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed over it...
I saw your comment, but for checking perf some of my comments may help (a bit).

Comment threadsrc/libraries/System.Memory/ref/System.Memory.Forwards.cs Outdated
buyaa-nand others added 2 commits May 17, 2024 11:00
Co-authored-by: Günther Foidl <gue@korporal.at>
@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

/benchmark

@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

Just skimmed over it... I saw your comment, but for checking perf some of my comments may help (a bit).

Thank you, sure perf related feedbacks appreciated, I updated the description, any feedbacks are welcome.

@pr-benchmarks

Copy link
Copy Markdown

Crank Pull Request Bot

/benchmark <benchmark[,...]> <profile[,...]> <component,[...]> <arguments>

Benchmarks:

  • micro: .NET Performance micro benchmarks (set filter by adding --variable filter=...; by default filter=*LinqBenchmarks*)
  • plaintext: TechEmpower Plaintext Scenario - ASP.NET Platform implementation
  • json: TechEmpower JSON Scenario - ASP.NET Platform implementation
  • fortunes: TechEmpower Fortunes Scenario - ASP.NET Platform implementation
  • fortunes_ef: TechEmpower Fortunes Scenario with EF Core - ASP.NET Platform implementation
  • httpclient: HttpClient Benchmark (change HTTP version by adding e.g. --variable httpVersion=3.0; change response size by adding e.g. --variable responseSize=256; default: HTTP/1.1 GET 8K)

Profiles:

  • aspnet-citrine-lin: Intel/Linux 28 Cores
  • aspnet-perf-lin: Intel/Linux 12 Cores
  • aspnet-citrine-amd: Amd/Linux 48 Cores

Components:

  • runtime
  • libs

Arguments: any additional arguments to pass through to crank, e.g. --variable name=value

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

buyaa-nand others added 2 commits June 10, 2024 21:23
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
padding++;
}

if (TBase64Decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees this is in bounds?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called with a buffer that has size of 4

constintBlockSize=4;
Span<byte>buffer=stackallocbyte[BlockSize];

intpaddingCount=GetPaddingCount<TBase64Decoder>(refbuffer[^1]);

}

end = srcMax - 64;
end = srcMax - 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change from 64 to 32?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 32 originally, but unintentionally changed with this PR

The method comment also mentions it require 32 byte

privatestaticunsafevoidAvx2Encode<TBase64Encoder,T>(refbyte*srcBytes,refT*destBytes,byte*srcEnd,intsourceLength,intdestLength,byte*srcStart,T*destStart)
whereTBase64Encoder:IBase64Encoder<T>
whereT: unmanaged
{
// If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
// But because we read 32 bytes at a time, ensure we have enough room to do a
// full 32-byte read without segfaulting.

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

where TBase64Decoder : IBase64Decoder<byte>
{
const int BlockSize = 4;
int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would anything bad happen if this were left at a const 4?
This not being const would penalize the non-url path as well a bit.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Base64Url when the source is not multiple of 4 we need to adjust the buffer size accordingly, the BlockSize value used to fill the whitespace with padding (row 653) so that it could decoded correctly (in case remaining bytes were decodable or valid)

@MihaZupan

Copy link
Copy Markdown
Member

@MihuBot fuzz Base64

@lewing

lewing commented Jun 19, 2024

Copy link
Copy Markdown
Member

also showing regressions in dotnet/perf-autofiling-issues#36512

and improvements dotnet/perf-autofiling-issues#36643

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Base 64 URL

8 participants

@buyaa-n@MihaZupan@lewing@stephentoub@gfoidl@jkotas@bartonjs@samsosa
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add Base64url encoding/decoding by buyaa-n · Pull Request #102364 · dotnet/runtime · GitHub
Skip to content

Add Base64url encoding/decoding - #102364

Merged
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url
Jun 14, 2024
Merged

Add Base64url encoding/decoding#102364
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url

Conversation

@buyaa-n

@buyaa-nbuyaa-n commented May 17, 2024

Copy link
Copy Markdown
Contributor
  • Base64Url encoding doesn't append padding, therefore encoded byte count differs from Base64 encoding:

    Source byte countBase64 encoded byte countBase64Url encoded byte count
    142
    243
    344
    486
    587
    688
  • Base64Url decoding ignore whitespace and padding, therefore decodable byte count differs from Base64 decoding, the exact decoding result also depend on isFinalBlock and if padding involved

    Source byte countBase64 decoded byte countBase64Url decoded byte count
    1max 0max 0
    2max 0max 1
    3max 0max 2
    4max 3max 3
    5max 3max 3
    6max 3max 4
    7max 3max 5
    8max 6max 6

Approved API shape:

namespaceSystem.Buffers.Text;publicstaticclassBase64Url{publicstaticintGetMaxDecodedLength(intbase64Length);publicstaticintGetEncodedLength(intbytesLength);publicstaticOperationStatusEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintcharsWritten);publicstaticbyte[]EncodeToUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintbytesConsumed,outintcharsWritten,boolisFinalBlock=true);publicstaticintEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination);publicstaticboolTryEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintcharsWritten);publicstaticchar[]EncodeToChars(ReadOnlySpan<byte>source);publicstaticstringEncodeToString(ReadOnlySpan<byte>source);publicstaticboolTryEncodeToUtf8InPlace(Span<byte>buffer,intdataLength,outintbytesWritten);publicstaticOperationStatusDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintcharsConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination);publicstaticboolTryDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromChars(ReadOnlySpan<char>source);publicstaticintDecodeFromUtf8InPlace(Span<byte>buffer);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText,outintdecodedLength);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText,outintdecodedLength);}

Draft PR until below list is completed, for now mainly for checking perf, run tests on different CI legs, and get early feedback.

  • Handle '%' in decoding as a valid padding character for Base64Url
  • Doc needs improvement
  • A few APIs missing unit tests
  • Fix failures on ARM CI legs

Fixes#1658

@ghost

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@gfoidlgfoidl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed over it...
I saw your comment, but for checking perf some of my comments may help (a bit).

Comment threadsrc/libraries/System.Memory/ref/System.Memory.Forwards.cs Outdated
buyaa-nand others added 2 commits May 17, 2024 11:00
Co-authored-by: Günther Foidl <gue@korporal.at>
@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

/benchmark

@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

Just skimmed over it... I saw your comment, but for checking perf some of my comments may help (a bit).

Thank you, sure perf related feedbacks appreciated, I updated the description, any feedbacks are welcome.

@pr-benchmarks

Copy link
Copy Markdown

Crank Pull Request Bot

/benchmark <benchmark[,...]> <profile[,...]> <component,[...]> <arguments>

Benchmarks:

  • micro: .NET Performance micro benchmarks (set filter by adding --variable filter=...; by default filter=*LinqBenchmarks*)
  • plaintext: TechEmpower Plaintext Scenario - ASP.NET Platform implementation
  • json: TechEmpower JSON Scenario - ASP.NET Platform implementation
  • fortunes: TechEmpower Fortunes Scenario - ASP.NET Platform implementation
  • fortunes_ef: TechEmpower Fortunes Scenario with EF Core - ASP.NET Platform implementation
  • httpclient: HttpClient Benchmark (change HTTP version by adding e.g. --variable httpVersion=3.0; change response size by adding e.g. --variable responseSize=256; default: HTTP/1.1 GET 8K)

Profiles:

  • aspnet-citrine-lin: Intel/Linux 28 Cores
  • aspnet-perf-lin: Intel/Linux 12 Cores
  • aspnet-citrine-amd: Amd/Linux 48 Cores

Components:

  • runtime
  • libs

Arguments: any additional arguments to pass through to crank, e.g. --variable name=value

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

buyaa-nand others added 2 commits June 10, 2024 21:23
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
padding++;
}

if (TBase64Decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees this is in bounds?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called with a buffer that has size of 4

constintBlockSize=4;
Span<byte>buffer=stackallocbyte[BlockSize];

intpaddingCount=GetPaddingCount<TBase64Decoder>(refbuffer[^1]);

}

end = srcMax - 64;
end = srcMax - 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change from 64 to 32?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 32 originally, but unintentionally changed with this PR

The method comment also mentions it require 32 byte

privatestaticunsafevoidAvx2Encode<TBase64Encoder,T>(refbyte*srcBytes,refT*destBytes,byte*srcEnd,intsourceLength,intdestLength,byte*srcStart,T*destStart)
whereTBase64Encoder:IBase64Encoder<T>
whereT: unmanaged
{
// If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
// But because we read 32 bytes at a time, ensure we have enough room to do a
// full 32-byte read without segfaulting.

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

where TBase64Decoder : IBase64Decoder<byte>
{
const int BlockSize = 4;
int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would anything bad happen if this were left at a const 4?
This not being const would penalize the non-url path as well a bit.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Base64Url when the source is not multiple of 4 we need to adjust the buffer size accordingly, the BlockSize value used to fill the whitespace with padding (row 653) so that it could decoded correctly (in case remaining bytes were decodable or valid)

@MihaZupan

Copy link
Copy Markdown
Member

@MihuBot fuzz Base64

@lewing

lewing commented Jun 19, 2024

Copy link
Copy Markdown
Member

also showing regressions in dotnet/perf-autofiling-issues#36512

and improvements dotnet/perf-autofiling-issues#36643

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Base 64 URL

8 participants

@buyaa-n@MihaZupan@lewing@stephentoub@gfoidl@jkotas@bartonjs@samsosa
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add Base64url encoding/decoding by buyaa-n · Pull Request #102364 · dotnet/runtime · GitHub
Skip to content

Add Base64url encoding/decoding - #102364

Merged
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url
Jun 14, 2024
Merged

Add Base64url encoding/decoding#102364
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url

Conversation

@buyaa-n

@buyaa-nbuyaa-n commented May 17, 2024

Copy link
Copy Markdown
Contributor
  • Base64Url encoding doesn't append padding, therefore encoded byte count differs from Base64 encoding:

    Source byte countBase64 encoded byte countBase64Url encoded byte count
    142
    243
    344
    486
    587
    688
  • Base64Url decoding ignore whitespace and padding, therefore decodable byte count differs from Base64 decoding, the exact decoding result also depend on isFinalBlock and if padding involved

    Source byte countBase64 decoded byte countBase64Url decoded byte count
    1max 0max 0
    2max 0max 1
    3max 0max 2
    4max 3max 3
    5max 3max 3
    6max 3max 4
    7max 3max 5
    8max 6max 6

Approved API shape:

namespaceSystem.Buffers.Text;publicstaticclassBase64Url{publicstaticintGetMaxDecodedLength(intbase64Length);publicstaticintGetEncodedLength(intbytesLength);publicstaticOperationStatusEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintcharsWritten);publicstaticbyte[]EncodeToUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintbytesConsumed,outintcharsWritten,boolisFinalBlock=true);publicstaticintEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination);publicstaticboolTryEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintcharsWritten);publicstaticchar[]EncodeToChars(ReadOnlySpan<byte>source);publicstaticstringEncodeToString(ReadOnlySpan<byte>source);publicstaticboolTryEncodeToUtf8InPlace(Span<byte>buffer,intdataLength,outintbytesWritten);publicstaticOperationStatusDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintcharsConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination);publicstaticboolTryDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromChars(ReadOnlySpan<char>source);publicstaticintDecodeFromUtf8InPlace(Span<byte>buffer);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText,outintdecodedLength);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText,outintdecodedLength);}

Draft PR until below list is completed, for now mainly for checking perf, run tests on different CI legs, and get early feedback.

  • Handle '%' in decoding as a valid padding character for Base64Url
  • Doc needs improvement
  • A few APIs missing unit tests
  • Fix failures on ARM CI legs

Fixes#1658

@ghost

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@gfoidlgfoidl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed over it...
I saw your comment, but for checking perf some of my comments may help (a bit).

Comment threadsrc/libraries/System.Memory/ref/System.Memory.Forwards.cs Outdated
buyaa-nand others added 2 commits May 17, 2024 11:00
Co-authored-by: Günther Foidl <gue@korporal.at>
@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

/benchmark

@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

Just skimmed over it... I saw your comment, but for checking perf some of my comments may help (a bit).

Thank you, sure perf related feedbacks appreciated, I updated the description, any feedbacks are welcome.

@pr-benchmarks

Copy link
Copy Markdown

Crank Pull Request Bot

/benchmark <benchmark[,...]> <profile[,...]> <component,[...]> <arguments>

Benchmarks:

  • micro: .NET Performance micro benchmarks (set filter by adding --variable filter=...; by default filter=*LinqBenchmarks*)
  • plaintext: TechEmpower Plaintext Scenario - ASP.NET Platform implementation
  • json: TechEmpower JSON Scenario - ASP.NET Platform implementation
  • fortunes: TechEmpower Fortunes Scenario - ASP.NET Platform implementation
  • fortunes_ef: TechEmpower Fortunes Scenario with EF Core - ASP.NET Platform implementation
  • httpclient: HttpClient Benchmark (change HTTP version by adding e.g. --variable httpVersion=3.0; change response size by adding e.g. --variable responseSize=256; default: HTTP/1.1 GET 8K)

Profiles:

  • aspnet-citrine-lin: Intel/Linux 28 Cores
  • aspnet-perf-lin: Intel/Linux 12 Cores
  • aspnet-citrine-amd: Amd/Linux 48 Cores

Components:

  • runtime
  • libs

Arguments: any additional arguments to pass through to crank, e.g. --variable name=value

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

buyaa-nand others added 2 commits June 10, 2024 21:23
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
padding++;
}

if (TBase64Decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees this is in bounds?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called with a buffer that has size of 4

constintBlockSize=4;
Span<byte>buffer=stackallocbyte[BlockSize];

intpaddingCount=GetPaddingCount<TBase64Decoder>(refbuffer[^1]);

}

end = srcMax - 64;
end = srcMax - 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change from 64 to 32?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 32 originally, but unintentionally changed with this PR

The method comment also mentions it require 32 byte

privatestaticunsafevoidAvx2Encode<TBase64Encoder,T>(refbyte*srcBytes,refT*destBytes,byte*srcEnd,intsourceLength,intdestLength,byte*srcStart,T*destStart)
whereTBase64Encoder:IBase64Encoder<T>
whereT: unmanaged
{
// If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
// But because we read 32 bytes at a time, ensure we have enough room to do a
// full 32-byte read without segfaulting.

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

where TBase64Decoder : IBase64Decoder<byte>
{
const int BlockSize = 4;
int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would anything bad happen if this were left at a const 4?
This not being const would penalize the non-url path as well a bit.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Base64Url when the source is not multiple of 4 we need to adjust the buffer size accordingly, the BlockSize value used to fill the whitespace with padding (row 653) so that it could decoded correctly (in case remaining bytes were decodable or valid)

@MihaZupan

Copy link
Copy Markdown
Member

@MihuBot fuzz Base64

@lewing

lewing commented Jun 19, 2024

Copy link
Copy Markdown
Member

also showing regressions in dotnet/perf-autofiling-issues#36512

and improvements dotnet/perf-autofiling-issues#36643

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Base 64 URL

8 participants

@buyaa-n@MihaZupan@lewing@stephentoub@gfoidl@jkotas@bartonjs@samsosa
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add Base64url encoding/decoding by buyaa-n · Pull Request #102364 · dotnet/runtime · GitHub
Skip to content

Add Base64url encoding/decoding - #102364

Merged
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url
Jun 14, 2024
Merged

Add Base64url encoding/decoding#102364
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url

Conversation

@buyaa-n

@buyaa-nbuyaa-n commented May 17, 2024

Copy link
Copy Markdown
Contributor
  • Base64Url encoding doesn't append padding, therefore encoded byte count differs from Base64 encoding:

    Source byte countBase64 encoded byte countBase64Url encoded byte count
    142
    243
    344
    486
    587
    688
  • Base64Url decoding ignore whitespace and padding, therefore decodable byte count differs from Base64 decoding, the exact decoding result also depend on isFinalBlock and if padding involved

    Source byte countBase64 decoded byte countBase64Url decoded byte count
    1max 0max 0
    2max 0max 1
    3max 0max 2
    4max 3max 3
    5max 3max 3
    6max 3max 4
    7max 3max 5
    8max 6max 6

Approved API shape:

namespaceSystem.Buffers.Text;publicstaticclassBase64Url{publicstaticintGetMaxDecodedLength(intbase64Length);publicstaticintGetEncodedLength(intbytesLength);publicstaticOperationStatusEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintcharsWritten);publicstaticbyte[]EncodeToUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintbytesConsumed,outintcharsWritten,boolisFinalBlock=true);publicstaticintEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination);publicstaticboolTryEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintcharsWritten);publicstaticchar[]EncodeToChars(ReadOnlySpan<byte>source);publicstaticstringEncodeToString(ReadOnlySpan<byte>source);publicstaticboolTryEncodeToUtf8InPlace(Span<byte>buffer,intdataLength,outintbytesWritten);publicstaticOperationStatusDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintcharsConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination);publicstaticboolTryDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromChars(ReadOnlySpan<char>source);publicstaticintDecodeFromUtf8InPlace(Span<byte>buffer);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText,outintdecodedLength);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText,outintdecodedLength);}

Draft PR until below list is completed, for now mainly for checking perf, run tests on different CI legs, and get early feedback.

  • Handle '%' in decoding as a valid padding character for Base64Url
  • Doc needs improvement
  • A few APIs missing unit tests
  • Fix failures on ARM CI legs

Fixes#1658

@ghost

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@gfoidlgfoidl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed over it...
I saw your comment, but for checking perf some of my comments may help (a bit).

Comment threadsrc/libraries/System.Memory/ref/System.Memory.Forwards.cs Outdated
buyaa-nand others added 2 commits May 17, 2024 11:00
Co-authored-by: Günther Foidl <gue@korporal.at>
@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

/benchmark

@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

Just skimmed over it... I saw your comment, but for checking perf some of my comments may help (a bit).

Thank you, sure perf related feedbacks appreciated, I updated the description, any feedbacks are welcome.

@pr-benchmarks

Copy link
Copy Markdown

Crank Pull Request Bot

/benchmark <benchmark[,...]> <profile[,...]> <component,[...]> <arguments>

Benchmarks:

  • micro: .NET Performance micro benchmarks (set filter by adding --variable filter=...; by default filter=*LinqBenchmarks*)
  • plaintext: TechEmpower Plaintext Scenario - ASP.NET Platform implementation
  • json: TechEmpower JSON Scenario - ASP.NET Platform implementation
  • fortunes: TechEmpower Fortunes Scenario - ASP.NET Platform implementation
  • fortunes_ef: TechEmpower Fortunes Scenario with EF Core - ASP.NET Platform implementation
  • httpclient: HttpClient Benchmark (change HTTP version by adding e.g. --variable httpVersion=3.0; change response size by adding e.g. --variable responseSize=256; default: HTTP/1.1 GET 8K)

Profiles:

  • aspnet-citrine-lin: Intel/Linux 28 Cores
  • aspnet-perf-lin: Intel/Linux 12 Cores
  • aspnet-citrine-amd: Amd/Linux 48 Cores

Components:

  • runtime
  • libs

Arguments: any additional arguments to pass through to crank, e.g. --variable name=value

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

buyaa-nand others added 2 commits June 10, 2024 21:23
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
padding++;
}

if (TBase64Decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees this is in bounds?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called with a buffer that has size of 4

constintBlockSize=4;
Span<byte>buffer=stackallocbyte[BlockSize];

intpaddingCount=GetPaddingCount<TBase64Decoder>(refbuffer[^1]);

}

end = srcMax - 64;
end = srcMax - 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change from 64 to 32?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 32 originally, but unintentionally changed with this PR

The method comment also mentions it require 32 byte

privatestaticunsafevoidAvx2Encode<TBase64Encoder,T>(refbyte*srcBytes,refT*destBytes,byte*srcEnd,intsourceLength,intdestLength,byte*srcStart,T*destStart)
whereTBase64Encoder:IBase64Encoder<T>
whereT: unmanaged
{
// If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
// But because we read 32 bytes at a time, ensure we have enough room to do a
// full 32-byte read without segfaulting.

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

where TBase64Decoder : IBase64Decoder<byte>
{
const int BlockSize = 4;
int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would anything bad happen if this were left at a const 4?
This not being const would penalize the non-url path as well a bit.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Base64Url when the source is not multiple of 4 we need to adjust the buffer size accordingly, the BlockSize value used to fill the whitespace with padding (row 653) so that it could decoded correctly (in case remaining bytes were decodable or valid)

@MihaZupan

Copy link
Copy Markdown
Member

@MihuBot fuzz Base64

@lewing

lewing commented Jun 19, 2024

Copy link
Copy Markdown
Member

also showing regressions in dotnet/perf-autofiling-issues#36512

and improvements dotnet/perf-autofiling-issues#36643

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Base 64 URL

8 participants

@buyaa-n@MihaZupan@lewing@stephentoub@gfoidl@jkotas@bartonjs@samsosa
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add Base64url encoding/decoding by buyaa-n · Pull Request #102364 · dotnet/runtime · GitHub
Skip to content

Add Base64url encoding/decoding - #102364

Merged
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url
Jun 14, 2024
Merged

Add Base64url encoding/decoding#102364
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url

Conversation

@buyaa-n

@buyaa-nbuyaa-n commented May 17, 2024

Copy link
Copy Markdown
Contributor
  • Base64Url encoding doesn't append padding, therefore encoded byte count differs from Base64 encoding:

    Source byte countBase64 encoded byte countBase64Url encoded byte count
    142
    243
    344
    486
    587
    688
  • Base64Url decoding ignore whitespace and padding, therefore decodable byte count differs from Base64 decoding, the exact decoding result also depend on isFinalBlock and if padding involved

    Source byte countBase64 decoded byte countBase64Url decoded byte count
    1max 0max 0
    2max 0max 1
    3max 0max 2
    4max 3max 3
    5max 3max 3
    6max 3max 4
    7max 3max 5
    8max 6max 6

Approved API shape:

namespaceSystem.Buffers.Text;publicstaticclassBase64Url{publicstaticintGetMaxDecodedLength(intbase64Length);publicstaticintGetEncodedLength(intbytesLength);publicstaticOperationStatusEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintcharsWritten);publicstaticbyte[]EncodeToUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintbytesConsumed,outintcharsWritten,boolisFinalBlock=true);publicstaticintEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination);publicstaticboolTryEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintcharsWritten);publicstaticchar[]EncodeToChars(ReadOnlySpan<byte>source);publicstaticstringEncodeToString(ReadOnlySpan<byte>source);publicstaticboolTryEncodeToUtf8InPlace(Span<byte>buffer,intdataLength,outintbytesWritten);publicstaticOperationStatusDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintcharsConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination);publicstaticboolTryDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromChars(ReadOnlySpan<char>source);publicstaticintDecodeFromUtf8InPlace(Span<byte>buffer);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText,outintdecodedLength);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText,outintdecodedLength);}

Draft PR until below list is completed, for now mainly for checking perf, run tests on different CI legs, and get early feedback.

  • Handle '%' in decoding as a valid padding character for Base64Url
  • Doc needs improvement
  • A few APIs missing unit tests
  • Fix failures on ARM CI legs

Fixes#1658

@ghost

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@gfoidlgfoidl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed over it...
I saw your comment, but for checking perf some of my comments may help (a bit).

Comment threadsrc/libraries/System.Memory/ref/System.Memory.Forwards.cs Outdated
buyaa-nand others added 2 commits May 17, 2024 11:00
Co-authored-by: Günther Foidl <gue@korporal.at>
@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

/benchmark

@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

Just skimmed over it... I saw your comment, but for checking perf some of my comments may help (a bit).

Thank you, sure perf related feedbacks appreciated, I updated the description, any feedbacks are welcome.

@pr-benchmarks

Copy link
Copy Markdown

Crank Pull Request Bot

/benchmark <benchmark[,...]> <profile[,...]> <component,[...]> <arguments>

Benchmarks:

  • micro: .NET Performance micro benchmarks (set filter by adding --variable filter=...; by default filter=*LinqBenchmarks*)
  • plaintext: TechEmpower Plaintext Scenario - ASP.NET Platform implementation
  • json: TechEmpower JSON Scenario - ASP.NET Platform implementation
  • fortunes: TechEmpower Fortunes Scenario - ASP.NET Platform implementation
  • fortunes_ef: TechEmpower Fortunes Scenario with EF Core - ASP.NET Platform implementation
  • httpclient: HttpClient Benchmark (change HTTP version by adding e.g. --variable httpVersion=3.0; change response size by adding e.g. --variable responseSize=256; default: HTTP/1.1 GET 8K)

Profiles:

  • aspnet-citrine-lin: Intel/Linux 28 Cores
  • aspnet-perf-lin: Intel/Linux 12 Cores
  • aspnet-citrine-amd: Amd/Linux 48 Cores

Components:

  • runtime
  • libs

Arguments: any additional arguments to pass through to crank, e.g. --variable name=value

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

buyaa-nand others added 2 commits June 10, 2024 21:23
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
padding++;
}

if (TBase64Decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees this is in bounds?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called with a buffer that has size of 4

constintBlockSize=4;
Span<byte>buffer=stackallocbyte[BlockSize];

intpaddingCount=GetPaddingCount<TBase64Decoder>(refbuffer[^1]);

}

end = srcMax - 64;
end = srcMax - 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change from 64 to 32?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 32 originally, but unintentionally changed with this PR

The method comment also mentions it require 32 byte

privatestaticunsafevoidAvx2Encode<TBase64Encoder,T>(refbyte*srcBytes,refT*destBytes,byte*srcEnd,intsourceLength,intdestLength,byte*srcStart,T*destStart)
whereTBase64Encoder:IBase64Encoder<T>
whereT: unmanaged
{
// If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
// But because we read 32 bytes at a time, ensure we have enough room to do a
// full 32-byte read without segfaulting.

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

where TBase64Decoder : IBase64Decoder<byte>
{
const int BlockSize = 4;
int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would anything bad happen if this were left at a const 4?
This not being const would penalize the non-url path as well a bit.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Base64Url when the source is not multiple of 4 we need to adjust the buffer size accordingly, the BlockSize value used to fill the whitespace with padding (row 653) so that it could decoded correctly (in case remaining bytes were decodable or valid)

@MihaZupan

Copy link
Copy Markdown
Member

@MihuBot fuzz Base64

@lewing

lewing commented Jun 19, 2024

Copy link
Copy Markdown
Member

also showing regressions in dotnet/perf-autofiling-issues#36512

and improvements dotnet/perf-autofiling-issues#36643

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Base 64 URL

8 participants

@buyaa-n@MihaZupan@lewing@stephentoub@gfoidl@jkotas@bartonjs@samsosa
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add Base64url encoding/decoding by buyaa-n · Pull Request #102364 · dotnet/runtime · GitHub
Skip to content

Add Base64url encoding/decoding - #102364

Merged
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url
Jun 14, 2024
Merged

Add Base64url encoding/decoding#102364
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url

Conversation

@buyaa-n

@buyaa-nbuyaa-n commented May 17, 2024

Copy link
Copy Markdown
Contributor
  • Base64Url encoding doesn't append padding, therefore encoded byte count differs from Base64 encoding:

    Source byte countBase64 encoded byte countBase64Url encoded byte count
    142
    243
    344
    486
    587
    688
  • Base64Url decoding ignore whitespace and padding, therefore decodable byte count differs from Base64 decoding, the exact decoding result also depend on isFinalBlock and if padding involved

    Source byte countBase64 decoded byte countBase64Url decoded byte count
    1max 0max 0
    2max 0max 1
    3max 0max 2
    4max 3max 3
    5max 3max 3
    6max 3max 4
    7max 3max 5
    8max 6max 6

Approved API shape:

namespaceSystem.Buffers.Text;publicstaticclassBase64Url{publicstaticintGetMaxDecodedLength(intbase64Length);publicstaticintGetEncodedLength(intbytesLength);publicstaticOperationStatusEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintcharsWritten);publicstaticbyte[]EncodeToUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintbytesConsumed,outintcharsWritten,boolisFinalBlock=true);publicstaticintEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination);publicstaticboolTryEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintcharsWritten);publicstaticchar[]EncodeToChars(ReadOnlySpan<byte>source);publicstaticstringEncodeToString(ReadOnlySpan<byte>source);publicstaticboolTryEncodeToUtf8InPlace(Span<byte>buffer,intdataLength,outintbytesWritten);publicstaticOperationStatusDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintcharsConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination);publicstaticboolTryDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromChars(ReadOnlySpan<char>source);publicstaticintDecodeFromUtf8InPlace(Span<byte>buffer);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText,outintdecodedLength);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText,outintdecodedLength);}

Draft PR until below list is completed, for now mainly for checking perf, run tests on different CI legs, and get early feedback.

  • Handle '%' in decoding as a valid padding character for Base64Url
  • Doc needs improvement
  • A few APIs missing unit tests
  • Fix failures on ARM CI legs

Fixes#1658

@ghost

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@gfoidlgfoidl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed over it...
I saw your comment, but for checking perf some of my comments may help (a bit).

Comment threadsrc/libraries/System.Memory/ref/System.Memory.Forwards.cs Outdated
buyaa-nand others added 2 commits May 17, 2024 11:00
Co-authored-by: Günther Foidl <gue@korporal.at>
@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

/benchmark

@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

Just skimmed over it... I saw your comment, but for checking perf some of my comments may help (a bit).

Thank you, sure perf related feedbacks appreciated, I updated the description, any feedbacks are welcome.

@pr-benchmarks

Copy link
Copy Markdown

Crank Pull Request Bot

/benchmark <benchmark[,...]> <profile[,...]> <component,[...]> <arguments>

Benchmarks:

  • micro: .NET Performance micro benchmarks (set filter by adding --variable filter=...; by default filter=*LinqBenchmarks*)
  • plaintext: TechEmpower Plaintext Scenario - ASP.NET Platform implementation
  • json: TechEmpower JSON Scenario - ASP.NET Platform implementation
  • fortunes: TechEmpower Fortunes Scenario - ASP.NET Platform implementation
  • fortunes_ef: TechEmpower Fortunes Scenario with EF Core - ASP.NET Platform implementation
  • httpclient: HttpClient Benchmark (change HTTP version by adding e.g. --variable httpVersion=3.0; change response size by adding e.g. --variable responseSize=256; default: HTTP/1.1 GET 8K)

Profiles:

  • aspnet-citrine-lin: Intel/Linux 28 Cores
  • aspnet-perf-lin: Intel/Linux 12 Cores
  • aspnet-citrine-amd: Amd/Linux 48 Cores

Components:

  • runtime
  • libs

Arguments: any additional arguments to pass through to crank, e.g. --variable name=value

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

buyaa-nand others added 2 commits June 10, 2024 21:23
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
padding++;
}

if (TBase64Decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees this is in bounds?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called with a buffer that has size of 4

constintBlockSize=4;
Span<byte>buffer=stackallocbyte[BlockSize];

intpaddingCount=GetPaddingCount<TBase64Decoder>(refbuffer[^1]);

}

end = srcMax - 64;
end = srcMax - 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change from 64 to 32?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 32 originally, but unintentionally changed with this PR

The method comment also mentions it require 32 byte

privatestaticunsafevoidAvx2Encode<TBase64Encoder,T>(refbyte*srcBytes,refT*destBytes,byte*srcEnd,intsourceLength,intdestLength,byte*srcStart,T*destStart)
whereTBase64Encoder:IBase64Encoder<T>
whereT: unmanaged
{
// If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
// But because we read 32 bytes at a time, ensure we have enough room to do a
// full 32-byte read without segfaulting.

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

where TBase64Decoder : IBase64Decoder<byte>
{
const int BlockSize = 4;
int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would anything bad happen if this were left at a const 4?
This not being const would penalize the non-url path as well a bit.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Base64Url when the source is not multiple of 4 we need to adjust the buffer size accordingly, the BlockSize value used to fill the whitespace with padding (row 653) so that it could decoded correctly (in case remaining bytes were decodable or valid)

@MihaZupan

Copy link
Copy Markdown
Member

@MihuBot fuzz Base64

@lewing

lewing commented Jun 19, 2024

Copy link
Copy Markdown
Member

also showing regressions in dotnet/perf-autofiling-issues#36512

and improvements dotnet/perf-autofiling-issues#36643

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Base 64 URL

8 participants

@buyaa-n@MihaZupan@lewing@stephentoub@gfoidl@jkotas@bartonjs@samsosa
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add Base64url encoding/decoding by buyaa-n · Pull Request #102364 · dotnet/runtime · GitHub
Skip to content

Add Base64url encoding/decoding - #102364

Merged
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url
Jun 14, 2024
Merged

Add Base64url encoding/decoding#102364
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url

Conversation

@buyaa-n

@buyaa-nbuyaa-n commented May 17, 2024

Copy link
Copy Markdown
Contributor
  • Base64Url encoding doesn't append padding, therefore encoded byte count differs from Base64 encoding:

    Source byte countBase64 encoded byte countBase64Url encoded byte count
    142
    243
    344
    486
    587
    688
  • Base64Url decoding ignore whitespace and padding, therefore decodable byte count differs from Base64 decoding, the exact decoding result also depend on isFinalBlock and if padding involved

    Source byte countBase64 decoded byte countBase64Url decoded byte count
    1max 0max 0
    2max 0max 1
    3max 0max 2
    4max 3max 3
    5max 3max 3
    6max 3max 4
    7max 3max 5
    8max 6max 6

Approved API shape:

namespaceSystem.Buffers.Text;publicstaticclassBase64Url{publicstaticintGetMaxDecodedLength(intbase64Length);publicstaticintGetEncodedLength(intbytesLength);publicstaticOperationStatusEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintcharsWritten);publicstaticbyte[]EncodeToUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintbytesConsumed,outintcharsWritten,boolisFinalBlock=true);publicstaticintEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination);publicstaticboolTryEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintcharsWritten);publicstaticchar[]EncodeToChars(ReadOnlySpan<byte>source);publicstaticstringEncodeToString(ReadOnlySpan<byte>source);publicstaticboolTryEncodeToUtf8InPlace(Span<byte>buffer,intdataLength,outintbytesWritten);publicstaticOperationStatusDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintcharsConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination);publicstaticboolTryDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromChars(ReadOnlySpan<char>source);publicstaticintDecodeFromUtf8InPlace(Span<byte>buffer);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText,outintdecodedLength);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText,outintdecodedLength);}

Draft PR until below list is completed, for now mainly for checking perf, run tests on different CI legs, and get early feedback.

  • Handle '%' in decoding as a valid padding character for Base64Url
  • Doc needs improvement
  • A few APIs missing unit tests
  • Fix failures on ARM CI legs

Fixes#1658

@ghost

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@gfoidlgfoidl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed over it...
I saw your comment, but for checking perf some of my comments may help (a bit).

Comment threadsrc/libraries/System.Memory/ref/System.Memory.Forwards.cs Outdated
buyaa-nand others added 2 commits May 17, 2024 11:00
Co-authored-by: Günther Foidl <gue@korporal.at>
@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

/benchmark

@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

Just skimmed over it... I saw your comment, but for checking perf some of my comments may help (a bit).

Thank you, sure perf related feedbacks appreciated, I updated the description, any feedbacks are welcome.

@pr-benchmarks

Copy link
Copy Markdown

Crank Pull Request Bot

/benchmark <benchmark[,...]> <profile[,...]> <component,[...]> <arguments>

Benchmarks:

  • micro: .NET Performance micro benchmarks (set filter by adding --variable filter=...; by default filter=*LinqBenchmarks*)
  • plaintext: TechEmpower Plaintext Scenario - ASP.NET Platform implementation
  • json: TechEmpower JSON Scenario - ASP.NET Platform implementation
  • fortunes: TechEmpower Fortunes Scenario - ASP.NET Platform implementation
  • fortunes_ef: TechEmpower Fortunes Scenario with EF Core - ASP.NET Platform implementation
  • httpclient: HttpClient Benchmark (change HTTP version by adding e.g. --variable httpVersion=3.0; change response size by adding e.g. --variable responseSize=256; default: HTTP/1.1 GET 8K)

Profiles:

  • aspnet-citrine-lin: Intel/Linux 28 Cores
  • aspnet-perf-lin: Intel/Linux 12 Cores
  • aspnet-citrine-amd: Amd/Linux 48 Cores

Components:

  • runtime
  • libs

Arguments: any additional arguments to pass through to crank, e.g. --variable name=value

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

buyaa-nand others added 2 commits June 10, 2024 21:23
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
padding++;
}

if (TBase64Decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees this is in bounds?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called with a buffer that has size of 4

constintBlockSize=4;
Span<byte>buffer=stackallocbyte[BlockSize];

intpaddingCount=GetPaddingCount<TBase64Decoder>(refbuffer[^1]);

}

end = srcMax - 64;
end = srcMax - 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change from 64 to 32?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 32 originally, but unintentionally changed with this PR

The method comment also mentions it require 32 byte

privatestaticunsafevoidAvx2Encode<TBase64Encoder,T>(refbyte*srcBytes,refT*destBytes,byte*srcEnd,intsourceLength,intdestLength,byte*srcStart,T*destStart)
whereTBase64Encoder:IBase64Encoder<T>
whereT: unmanaged
{
// If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
// But because we read 32 bytes at a time, ensure we have enough room to do a
// full 32-byte read without segfaulting.

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

where TBase64Decoder : IBase64Decoder<byte>
{
const int BlockSize = 4;
int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would anything bad happen if this were left at a const 4?
This not being const would penalize the non-url path as well a bit.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Base64Url when the source is not multiple of 4 we need to adjust the buffer size accordingly, the BlockSize value used to fill the whitespace with padding (row 653) so that it could decoded correctly (in case remaining bytes were decodable or valid)

@MihaZupan

Copy link
Copy Markdown
Member

@MihuBot fuzz Base64

@lewing

lewing commented Jun 19, 2024

Copy link
Copy Markdown
Member

also showing regressions in dotnet/perf-autofiling-issues#36512

and improvements dotnet/perf-autofiling-issues#36643

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Base 64 URL

8 participants

@buyaa-n@MihaZupan@lewing@stephentoub@gfoidl@jkotas@bartonjs@samsosa
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Add Base64url encoding/decoding by buyaa-n · Pull Request #102364 · dotnet/runtime · GitHub
Skip to content

Add Base64url encoding/decoding - #102364

Merged
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url
Jun 14, 2024
Merged

Add Base64url encoding/decoding#102364
buyaa-n merged 40 commits into
dotnet:mainfrom
buyaa-n:base64url

Conversation

@buyaa-n

@buyaa-nbuyaa-n commented May 17, 2024

Copy link
Copy Markdown
Contributor
  • Base64Url encoding doesn't append padding, therefore encoded byte count differs from Base64 encoding:

    Source byte countBase64 encoded byte countBase64Url encoded byte count
    142
    243
    344
    486
    587
    688
  • Base64Url decoding ignore whitespace and padding, therefore decodable byte count differs from Base64 decoding, the exact decoding result also depend on isFinalBlock and if padding involved

    Source byte countBase64 decoded byte countBase64Url decoded byte count
    1max 0max 0
    2max 0max 1
    3max 0max 2
    4max 3max 3
    5max 3max 3
    6max 3max 4
    7max 3max 5
    8max 6max 6

Approved API shape:

namespaceSystem.Buffers.Text;publicstaticclassBase64Url{publicstaticintGetMaxDecodedLength(intbase64Length);publicstaticintGetEncodedLength(intbytesLength);publicstaticOperationStatusEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryEncodeToUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintcharsWritten);publicstaticbyte[]EncodeToUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintbytesConsumed,outintcharsWritten,boolisFinalBlock=true);publicstaticintEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination);publicstaticboolTryEncodeToChars(ReadOnlySpan<byte>source,Span<char>destination,outintcharsWritten);publicstaticchar[]EncodeToChars(ReadOnlySpan<byte>source);publicstaticstringEncodeToString(ReadOnlySpan<byte>source);publicstaticboolTryEncodeToUtf8InPlace(Span<byte>buffer,intdataLength,outintbytesWritten);publicstaticOperationStatusDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination);publicstaticboolTryDecodeFromUtf8(ReadOnlySpan<byte>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromUtf8(ReadOnlySpan<byte>source);publicstaticOperationStatusDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintcharsConsumed,outintbytesWritten,boolisFinalBlock=true);publicstaticintDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination);publicstaticboolTryDecodeFromChars(ReadOnlySpan<char>source,Span<byte>destination,outintbytesWritten);publicstaticbyte[]DecodeFromChars(ReadOnlySpan<char>source);publicstaticintDecodeFromUtf8InPlace(Span<byte>buffer);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText);publicstaticboolIsValid(ReadOnlySpan<char>base64UrlText,outintdecodedLength);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText);publicstaticboolIsValid(ReadOnlySpan<byte>utf8Base64UrlText,outintdecodedLength);}

Draft PR until below list is completed, for now mainly for checking perf, run tests on different CI legs, and get early feedback.

  • Handle '%' in decoding as a valid padding character for Base64Url
  • Doc needs improvement
  • A few APIs missing unit tests
  • Fix failures on ARM CI legs

Fixes#1658

@ghost

Copy link
Copy Markdown

Note regarding the new-api-needs-documentation label:

This serves as a reminder for when your PR is modifying a ref *.cs file and adding/modifying public APIs, please make sure the API implementation in the src *.cs file is documented with triple slash comments, so the PR reviewers can sign off that change.

@gfoidlgfoidl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skimmed over it...
I saw your comment, but for checking perf some of my comments may help (a bit).

Comment threadsrc/libraries/System.Memory/ref/System.Memory.Forwards.cs Outdated
buyaa-nand others added 2 commits May 17, 2024 11:00
Co-authored-by: Günther Foidl <gue@korporal.at>
@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

/benchmark

@buyaa-n

Copy link
Copy Markdown
ContributorAuthor

Just skimmed over it... I saw your comment, but for checking perf some of my comments may help (a bit).

Thank you, sure perf related feedbacks appreciated, I updated the description, any feedbacks are welcome.

@pr-benchmarks

Copy link
Copy Markdown

Crank Pull Request Bot

/benchmark <benchmark[,...]> <profile[,...]> <component,[...]> <arguments>

Benchmarks:

  • micro: .NET Performance micro benchmarks (set filter by adding --variable filter=...; by default filter=*LinqBenchmarks*)
  • plaintext: TechEmpower Plaintext Scenario - ASP.NET Platform implementation
  • json: TechEmpower JSON Scenario - ASP.NET Platform implementation
  • fortunes: TechEmpower Fortunes Scenario - ASP.NET Platform implementation
  • fortunes_ef: TechEmpower Fortunes Scenario with EF Core - ASP.NET Platform implementation
  • httpclient: HttpClient Benchmark (change HTTP version by adding e.g. --variable httpVersion=3.0; change response size by adding e.g. --variable responseSize=256; default: HTTP/1.1 GET 8K)

Profiles:

  • aspnet-citrine-lin: Intel/Linux 28 Cores
  • aspnet-perf-lin: Intel/Linux 12 Cores
  • aspnet-citrine-amd: Amd/Linux 48 Cores

Components:

  • runtime
  • libs

Arguments: any additional arguments to pass through to crank, e.g. --variable name=value

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

@pr-benchmarks

This comment was marked as duplicate.

buyaa-nand others added 2 commits June 10, 2024 21:23
Co-authored-by: Jeremy Barton <jbarton@microsoft.com>
padding++;
}

if (TBase64Decoder.IsValidPadding(Unsafe.Subtract(ref ptrToLastElement, 1)))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What guarantees this is in bounds?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only called with a buffer that has size of 4

constintBlockSize=4;
Span<byte>buffer=stackallocbyte[BlockSize];

intpaddingCount=GetPaddingCount<TBase64Decoder>(refbuffer[^1]);

}

end = srcMax - 64;
end = srcMax - 32;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did this change from 64 to 32?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was 32 originally, but unintentionally changed with this PR

The method comment also mentions it require 32 byte

privatestaticunsafevoidAvx2Encode<TBase64Encoder,T>(refbyte*srcBytes,refT*destBytes,byte*srcEnd,intsourceLength,intdestLength,byte*srcStart,T*destStart)
whereTBase64Encoder:IBase64Encoder<T>
whereT: unmanaged
{
// If we have AVX2 support, pick off 24 bytes at a time for as long as we can.
// But because we read 32 bytes at a time, ensure we have enough room to do a
// full 32-byte read without segfaulting.

@stephentoubstephentoub left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

where TBase64Decoder : IBase64Decoder<byte>
{
const int BlockSize = 4;
int BlockSize = Math.Min(source.Length - (int)sourceIndex, 4);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would anything bad happen if this were left at a const 4?
This not being const would penalize the non-url path as well a bit.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Base64Url when the source is not multiple of 4 we need to adjust the buffer size accordingly, the BlockSize value used to fill the whitespace with padding (row 653) so that it could decoded correctly (in case remaining bytes were decodable or valid)

@MihaZupan

Copy link
Copy Markdown
Member

@MihuBot fuzz Base64

@lewing

lewing commented Jun 19, 2024

Copy link
Copy Markdown
Member

also showing regressions in dotnet/perf-autofiling-issues#36512

and improvements dotnet/perf-autofiling-issues#36643

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support Base 64 URL

8 participants

@buyaa-n@MihaZupan@lewing@stephentoub@gfoidl@jkotas@bartonjs@samsosa