Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 66e64e5

Browse files
adamsitnikGrabYourPitchforksstephentoubMihaZupan
authored
New ASCII APIs (#75012)
Co-authored-by: Levi Broderick <[email protected]> Co-authored-by: Stephen Toub <[email protected]> Co-authored-by: Miha Zupan <[email protected]>
1 parent 5fdc188 commit 66e64e5

File tree

49 files changed

+2248
-933
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2248
-933
lines changed

src/coreclr/nativeaot/System.Private.CoreLib/src/System/Runtime/InteropServices/PInvokeMarshal.cs

Lines changed: 8 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using Internal.Runtime.Augments;
1111
using Internal.Runtime.CompilerHelpers;
1212
using Internal.Runtime.CompilerServices;
13+
using System.Text;
14+
using System.Buffers;
1315

1416
namespace System.Runtime.InteropServices
1517
{
@@ -502,17 +504,7 @@ public static unsafe char AnsiCharToWideChar(byte nativeValue)
502504
internal static unsafe byte* StringToAnsiString(char* pManaged, int lenUnicode, byte* pNative, bool terminateWithNull,
503505
bool bestFit, bool throwOnUnmappableChar)
504506
{
505-
bool allAscii = true;
506-
507-
for (int i = 0; i < lenUnicode; i++)
508-
{
509-
if (pManaged[i] >= 128)
510-
{
511-
allAscii = false;
512-
break;
513-
}
514-
}
515-
507+
bool allAscii = Ascii.IsValid(new ReadOnlySpan<char>(pManaged, lenUnicode));
516508
int length;
517509

518510
if (allAscii) // If all ASCII, map one UNICODE character to one ANSI char
@@ -530,17 +522,8 @@ public static unsafe char AnsiCharToWideChar(byte nativeValue)
530522
}
531523
if (allAscii) // ASCII conversion
532524
{
533-
byte* pDst = pNative;
534-
char* pSrc = pManaged;
535-
536-
while (lenUnicode > 0)
537-
{
538-
unchecked
539-
{
540-
*pDst++ = (byte)(*pSrc++);
541-
lenUnicode--;
542-
}
543-
}
525+
OperationStatus conversionStatus = Ascii.FromUtf16(new ReadOnlySpan<char>(pManaged, length), new Span<byte>(pNative, length), out _);
526+
Debug.Assert(conversionStatus == OperationStatus.Done);
544527
}
545528
else // Let OS convert
546529
{
@@ -566,26 +549,9 @@ public static unsafe char AnsiCharToWideChar(byte nativeValue)
566549
/// </summary>
567550
private static unsafe bool CalculateStringLength(byte* pchBuffer, out int ansiBufferLen, out int unicodeBufferLen)
568551
{
569-
ansiBufferLen = 0;
570-
571-
bool allAscii = true;
572-
573-
{
574-
byte* p = pchBuffer;
575-
byte b = *p++;
576-
577-
while (b != 0)
578-
{
579-
if (b >= 128)
580-
{
581-
allAscii = false;
582-
}
583-
584-
ansiBufferLen++;
585-
586-
b = *p++;
587-
}
588-
}
552+
ReadOnlySpan<byte> span = MemoryMarshal.CreateReadOnlySpanFromNullTerminated(pchBuffer);
553+
ansiBufferLen = span.Length;
554+
bool allAscii = Ascii.IsValid(span);
589555

590556
if (allAscii)
591557
{

src/libraries/Common/tests/System/Net/Http/Http2LoopbackConnection.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private async Task ReadPrefixAsync()
9191
throw new Exception("Connection stream closed while attempting to read connection preface.");
9292
}
9393

94-
if (Text.Encoding.ASCII.GetString(_prefix).Contains("HTTP/1.1"))
94+
if (_prefix.AsSpan().IndexOf("HTTP/1.1"u8) >= 0)
9595
{
9696
// Tests that use HttpAgnosticLoopbackServer will attempt to send an HTTP/1.1 request to an HTTP/2 server.
9797
// This is invalid and we should terminate the connection.

src/libraries/System.Net.Http/src/System/Net/Http/Headers/ContentDispositionHeaderValue.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,7 +422,7 @@ private static string EncodeAndQuoteMime(string input)
422422
throw new ArgumentException(SR.Format(CultureInfo.InvariantCulture,
423423
SR.net_http_headers_invalid_value, input));
424424
}
425-
else if (HeaderUtilities.ContainsNonAscii(result))
425+
else if (!Ascii.IsValid(result))
426426
{
427427
needsQuotes = true; // Encoded data must always be quoted, the equals signs are invalid in tokens.
428428
result = EncodeMime(result); // =?utf-8?B?asdfasdfaesdf?=

src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderUtilities.cs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,20 +63,6 @@ internal static void SetQuality(UnvalidatedObjectCollection<NameValueHeaderValue
6363
}
6464
}
6565

66-
internal static bool ContainsNonAscii(string input)
67-
{
68-
Debug.Assert(input != null);
69-
70-
foreach (char c in input)
71-
{
72-
if ((int)c > 0x7f)
73-
{
74-
return true;
75-
}
76-
}
77-
return false;
78-
}
79-
8066
// Encode a string using RFC 5987 encoding.
8167
// encoding'lang'PercentEncodedSpecials
8268
internal static string Encode5987(string input)

src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/AuthenticationHelper.Digest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ internal static partial class AuthenticationHelper
8888
}
8989
else
9090
{
91-
if (HeaderUtilities.ContainsNonAscii(credential.UserName))
91+
if (!Ascii.IsValid(credential.UserName))
9292
{
9393
string usernameStar = HeaderUtilities.Encode5987(credential.UserName);
9494
sb.AppendKeyValue(UsernameStar, usernameStar, includeQuotes: false);

src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,12 +1582,10 @@ private Task WriteAsciiStringAsync(string s, bool async)
15821582
int offset = _writeOffset;
15831583
if (s.Length <= _writeBuffer.Length - offset)
15841584
{
1585-
byte[] writeBuffer = _writeBuffer;
1586-
foreach (char c in s)
1587-
{
1588-
writeBuffer[offset++] = (byte)c;
1589-
}
1590-
_writeOffset = offset;
1585+
OperationStatus operationStatus = Ascii.FromUtf16(s, _writeBuffer.AsSpan(offset), out int bytesWritten);
1586+
Debug.Assert(operationStatus == OperationStatus.Done);
1587+
_writeOffset = offset + bytesWritten;
1588+
15911589
return Task.CompletedTask;
15921590
}
15931591

@@ -1598,14 +1596,14 @@ private Task WriteAsciiStringAsync(string s, bool async)
15981596

15991597
private async Task WriteStringAsyncSlow(string s, bool async)
16001598
{
1599+
if (!Ascii.IsValid(s))
1600+
{
1601+
throw new HttpRequestException(SR.net_http_request_invalid_char_encoding);
1602+
}
1603+
16011604
for (int i = 0; i < s.Length; i++)
16021605
{
1603-
char c = s[i];
1604-
if ((c & 0xFF80) != 0)
1605-
{
1606-
throw new HttpRequestException(SR.net_http_request_invalid_char_encoding);
1607-
}
1608-
await WriteByteAsync((byte)c, async).ConfigureAwait(false);
1606+
await WriteByteAsync((byte)s[i], async).ConfigureAwait(false);
16091607
}
16101608
}
16111609

src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,6 @@
7171
Link="Common\System\Net\CookieFields.cs" />
7272
<Compile Include="$(CommonPath)System\Net\CookieParser.cs"
7373
Link="Common\System\Net\CookieParser.cs" />
74-
<Compile Include="$(CommonPath)System\Net\CaseInsensitiveAscii.cs"
75-
Link="Common\System\Net\CaseInsensitiveAscii.cs" />
7674
<Compile Include="$(CommonPath)System\Net\ExceptionCheck.cs"
7775
Link="Common\System\Net\ExceptionCheck.cs" />
7876
<Compile Include="$(CommonPath)System\Net\HttpStatusDescription.cs"

src/libraries/System.Net.HttpListener/src/System/Net/HttpListener.cs

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Buffers;
45
using System.Collections;
6+
using System.Diagnostics;
57
using System.Diagnostics.CodeAnalysis;
68
using System.Security.Authentication.ExtendedProtection;
79
using System.Text;
@@ -150,22 +152,7 @@ internal void AddPrefix(string uriPrefix)
150152
{
151153
throw new ArgumentException(SR.net_listener_slash, nameof(uriPrefix));
152154
}
153-
StringBuilder registeredPrefixBuilder = new StringBuilder();
154-
if (uriPrefix[j] == ':')
155-
{
156-
registeredPrefixBuilder.Append(uriPrefix);
157-
}
158-
else
159-
{
160-
registeredPrefixBuilder.Append(uriPrefix, 0, j);
161-
registeredPrefixBuilder.Append(i == 7 ? ":80" : ":443");
162-
registeredPrefixBuilder.Append(uriPrefix, j, uriPrefix.Length - j);
163-
}
164-
for (i = 0; registeredPrefixBuilder[i] != ':'; i++)
165-
{
166-
registeredPrefixBuilder[i] = (char)CaseInsensitiveAscii.AsciiToLower[(byte)registeredPrefixBuilder[i]];
167-
}
168-
registeredPrefix = registeredPrefixBuilder.ToString();
155+
registeredPrefix = CreateRegisteredPrefix(uriPrefix, j, i);
169156
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"mapped uriPrefix: {uriPrefix} to registeredPrefix: {registeredPrefix}");
170157
if (_state == State.Started)
171158
{
@@ -179,6 +166,50 @@ internal void AddPrefix(string uriPrefix)
179166
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, exception);
180167
throw;
181168
}
169+
170+
static string CreateRegisteredPrefix(string uriPrefix, int j, int i)
171+
{
172+
int length = uriPrefix.Length;
173+
if (uriPrefix[j] != ':')
174+
{
175+
length += i == 7 ? ":80".Length : ":443".Length;
176+
}
177+
178+
return string.Create(length, (uriPrefix, j, i), static (destination, state) =>
179+
{
180+
if (state.uriPrefix[state.j] == ':')
181+
{
182+
state.uriPrefix.CopyTo(destination);
183+
}
184+
else
185+
{
186+
int indexOfNextCopy = state.j;
187+
state.uriPrefix.AsSpan(0, indexOfNextCopy).CopyTo(destination);
188+
189+
if (state.i == 7)
190+
{
191+
":80".CopyTo(destination.Slice(indexOfNextCopy));
192+
indexOfNextCopy += 3;
193+
}
194+
else
195+
{
196+
":443".CopyTo(destination.Slice(indexOfNextCopy));
197+
indexOfNextCopy += 4;
198+
}
199+
200+
state.uriPrefix.AsSpan(state.j).CopyTo(destination.Slice(indexOfNextCopy));
201+
}
202+
203+
int toLowerLength = destination.IndexOf(':');
204+
if (toLowerLength < 0)
205+
{
206+
toLowerLength = destination.Length;
207+
}
208+
209+
OperationStatus operationStatus = Ascii.ToLowerInPlace(destination.Slice(0, toLowerLength), out _);
210+
Debug.Assert(operationStatus == OperationStatus.Done);
211+
});
212+
}
182213
}
183214

184215
internal bool ContainsPrefix(string uriPrefix) => _uriPrefixes.Contains(uriPrefix);

src/libraries/System.Net.Mail/src/System/Net/Mail/DomainLiteralReader.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Text;
45
using System.Diagnostics;
56
using System.Net.Mime;
67

@@ -70,7 +71,7 @@ internal static bool TryReadReverse(string data, int index, out int outIndex, bo
7071
return true;
7172
}
7273
// Check for invalid characters
73-
else if (data[index] > MailBnfHelper.Ascii7bitMaxValue || !MailBnfHelper.Dtext[data[index]])
74+
else if (!Ascii.IsValid(data[index]) || !MailBnfHelper.Dtext[data[index]])
7475
{
7576
if (throwExceptionIfFail)
7677
{

src/libraries/System.Net.Mail/src/System/Net/Mail/DotAtomReader.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Text;
45
using System.Diagnostics;
56
using System.Net.Mime;
67

@@ -43,7 +44,7 @@ internal static bool TryReadReverse(string data, int index, out int outIndex, bo
4344
// Scan for the first invalid chars (including whitespace)
4445
for (; 0 <= index; index--)
4546
{
46-
if (data[index] <= MailBnfHelper.Ascii7bitMaxValue // Any Unicode allowed
47+
if (Ascii.IsValid(data[index]) // Any ASCII allowed
4748
&& (data[index] != MailBnfHelper.Dot && !MailBnfHelper.Atext[data[index]])) // Invalid char
4849
{
4950
break;

0 commit comments

Comments
 (0)