Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.Runtime.InteropServices;
using System.Security.Cryptography.Asn1;
using System.Security.Cryptography.Pkcs;
using System.Text;
using Internal.Cryptography;

namespace System.Security.Cryptography
Expand Down
82 changes: 82 additions & 0 deletions src/libraries/Common/src/System/Text/EncodingPolyfills.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Text
{
/// <summary>Provides downlevel polyfills for span-based Encoding APIs.</summary>
internal static class EncodingPolyfills
{
public static unsafe int GetByteCount(this Encoding encoding, ReadOnlySpan<char> chars)
{
fixed (char* charsPtr = &GetNonNullPinnableReference(chars))
{
return encoding.GetByteCount(charsPtr, chars.Length);
}
}

public static unsafe int GetCharCount(this Encoding encoding, ReadOnlySpan<byte> bytes)
{
fixed (byte* bytesPtr = &GetNonNullPinnableReference(bytes))
{
return encoding.GetCharCount(bytesPtr, bytes.Length);
}
}

public static int GetBytes(this Encoding encoding, string str, Span<byte> bytes)
{
return GetBytes(encoding, str.AsSpan(), bytes);
}

public static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan<char> chars, Span<byte> bytes)
{
fixed (char* charsPtr = &GetNonNullPinnableReference(chars))
fixed (byte* bytesPtr = &GetNonNullPinnableReference(bytes))
{
return encoding.GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
}
}

public static unsafe int GetChars(this Encoding encoding, ReadOnlySpan<byte> bytes, Span<char> chars)
{
fixed (byte* bytesPtr = &GetNonNullPinnableReference(bytes))
fixed (char* charsPtr = &GetNonNullPinnableReference(chars))
{
return encoding.GetChars(bytesPtr, bytes.Length, charsPtr, chars.Length);
}
}

public static unsafe string GetString(this Encoding encoding, ReadOnlySpan<byte> bytes)
{
fixed (byte* bytesPtr = &GetNonNullPinnableReference(bytes))
{
return encoding.GetString(bytesPtr, bytes.Length);
}
}

public static bool TryGetChars(this Encoding encoding, ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
{
int charCount = encoding.GetCharCount(bytes);

if (charCount > chars.Length)
{
charsWritten = 0;
return false;
}

charsWritten = encoding.GetChars(bytes, chars);
Debug.Assert(charCount == charsWritten);
return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe ref readonly T GetNonNullPinnableReference<T>(ReadOnlySpan<T> buffer)
{
// Based on the internal implementation from MemoryMarshal.
return ref buffer.Length != 0 ? ref MemoryMarshal.GetReference(buffer) : ref Unsafe.AsRef<T>((void*)1);
}
}
}
1 change: 1 addition & 0 deletions src/libraries/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\MemoryExtensionsPolyfills.cs" Link="System\MemoryExtensionsPolyfills.cs" />
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Buffers\SearchValuesPolyfills.cs" Link="System\Buffers\SearchValuesPolyfills.cs" />
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Text\AsciiPolyfills.cs" Link="System\Text\AsciiPolyfills.cs" />
<Compile Include="$(LibrariesProjectRoot)\Common\src\System\Text\EncodingPolyfills.cs" Link="System\Text\EncodingPolyfills.cs" />
</ItemGroup>

<!-- If a tfm doesn't target .NETCoreApp but uses the platform support attributes, then we include the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<PackageDescription>Provides support for some cryptographic primitives for .NET Framework and .NET Standard.</PackageDescription>
<NoWarn>$(NoWarn);SYSLIB5006</NoWarn>
<IncludeIndexRangeTypes>true</IncludeIndexRangeTypes>
<IncludeSpanPolyfills>true</IncludeSpanPolyfills>
</PropertyGroup>

<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,51 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;

namespace System.Security.Cryptography
{
internal static class NetStandardShims
{
internal static unsafe int GetByteCount(this Encoding encoding, ReadOnlySpan<char> str)
{
if (str.IsEmpty)
{
return 0;
}

fixed (char* pStr = str)
{
return encoding.GetByteCount(pStr, str.Length);
}
}

internal static unsafe int GetBytes(this Encoding encoding, string str, Span<byte> destination)
{
return GetBytes(encoding, str.AsSpan(), destination);
}

internal static unsafe int GetBytes(this Encoding encoding, ReadOnlySpan<char> str, Span<byte> destination)
{
if (str.IsEmpty)
{
return 0;
}

fixed (char* pStr = str)
fixed (byte* pDestination = destination)
{
return encoding.GetBytes(pStr, str.Length, pDestination, destination.Length);
}
}

internal static void ReadExactly(this System.IO.Stream stream, Span<byte> buffer) =>
internal static void ReadExactly(this Stream stream, Span<byte> buffer) =>
ReadAtLeast(stream, buffer, buffer.Length, throwOnEndOfStream: true);

internal static int ReadAtLeast(
this System.IO.Stream stream,
this Stream stream,
Span<byte> buffer,
int minimumBytes,
bool throwOnEndOfStream = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<NoWarn>$(NoWarn);SYSLIB5006</NoWarn>
<StringResourcesPath>../src/Resources/Strings.resx</StringResourcesPath>
<IncludeIndexRangeTypes>true</IncludeIndexRangeTypes>
<IncludeSpanPolyfills>true</IncludeSpanPolyfills>
</PropertyGroup>

<Import Project="$(CommonPath)System\Security\Cryptography\Asn1\AsnXml.targets" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
<DefineConstants>$(DefineConstants);CP_NO_ZEROMEMORY</DefineConstants>
<IsPackable>true</IsPackable>
<PackageDescription>Provides classes that can read and write the ASN.1 BER, CER, and DER data formats.</PackageDescription>
<IncludeSpanPolyfills>true</IncludeSpanPolyfills>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Buffers.Binary;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;

namespace System.Formats.Asn1
Expand All @@ -31,47 +29,6 @@ internal static Encoding GetEncoding(UniversalTagNumber encodingType) =>
UniversalTagNumber.T61String => s_t61Encoding,
_ => throw new ArgumentOutOfRangeException(nameof(encodingType), encodingType, null),
};

internal static int GetByteCount(this Encoding encoding, ReadOnlySpan<char> str)
{
if (str.IsEmpty)
{
// Ensure a non-null pointer is obtained, even though the expected answer is 0.
str = string.Empty.AsSpan();
}

unsafe
{
fixed (char* strPtr = &MemoryMarshal.GetReference(str))
{
return encoding.GetByteCount(strPtr, str.Length);
}
}
}

internal static int GetBytes(this Encoding encoding, ReadOnlySpan<char> chars, Span<byte> bytes)
{
if (chars.IsEmpty)
{
// Ensure a non-null pointer is obtained.
chars = string.Empty.AsSpan();
}

if (bytes.IsEmpty)
{
// Ensure a non-null pointer is obtained.
bytes = Array.Empty<byte>();
}

unsafe
{
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
{
return encoding.GetBytes(charsPtr, chars.Length, bytesPtr, bytes.Length);
}
}
}
}

internal abstract class SpanBasedEncoding : Encoding
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

Expand Down Expand Up @@ -248,7 +246,7 @@ public static bool TryReadCharacterString(
out int charsWritten,
Asn1Tag? expectedTag = null)
{
Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType);
Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType);

return TryReadCharacterStringCore(
source,
Expand Down Expand Up @@ -319,7 +317,7 @@ public static string ReadCharacterString(
out int bytesConsumed,
Asn1Tag? expectedTag = null)
{
Text.Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType);
Encoding encoding = AsnCharacterStringEncodings.GetEncoding(encodingType);

return ReadCharacterStringCore(
source,
Expand Down Expand Up @@ -384,53 +382,12 @@ private static bool TryReadCharacterStringBytesCore(
return copied;
}

private static unsafe bool TryReadCharacterStringCore(
ReadOnlySpan<byte> source,
Span<char> destination,
Text.Encoding encoding,
out int charsWritten)
{
try
{
#if NET
return encoding.TryGetChars(source, destination, out charsWritten);
#else
if (source.Length == 0)
{
charsWritten = 0;
return true;
}

fixed (byte* bytePtr = &MemoryMarshal.GetReference(source))
fixed (char* charPtr = &MemoryMarshal.GetReference(destination))
{
int charCount = encoding.GetCharCount(bytePtr, source.Length);

if (charCount > destination.Length)
{
charsWritten = 0;
return false;
}

charsWritten = encoding.GetChars(bytePtr, source.Length, charPtr, destination.Length);
Debug.Assert(charCount == charsWritten);

return true;
}
#endif
}
catch (DecoderFallbackException e)
{
throw new AsnContentException(SR.ContentException_DefaultMessage, e);
}
}

private static string ReadCharacterStringCore(
ReadOnlySpan<byte> source,
AsnEncodingRules ruleSet,
Asn1Tag expectedTag,
UniversalTagNumber universalTagNumber,
Text.Encoding encoding,
Encoding encoding,
out int bytesConsumed)
{
byte[]? rented = null;
Expand All @@ -445,27 +402,13 @@ private static string ReadCharacterStringCore(
ref rented);

string str;

if (contents.Length == 0)
try
{
str = string.Empty;
str = encoding.GetString(contents);
}
else
catch (DecoderFallbackException e)
{
unsafe
{
fixed (byte* bytePtr = &MemoryMarshal.GetReference(contents))
{
try
{
str = encoding.GetString(bytePtr, contents.Length);
}
catch (DecoderFallbackException e)
{
throw new AsnContentException(SR.ContentException_DefaultMessage, e);
}
}
}
throw new AsnContentException(SR.ContentException_DefaultMessage, e);
}

if (rented != null)
Expand All @@ -482,7 +425,7 @@ private static bool TryReadCharacterStringCore(
AsnEncodingRules ruleSet,
Asn1Tag expectedTag,
UniversalTagNumber universalTagNumber,
Text.Encoding encoding,
Encoding encoding,
Span<char> destination,
out int bytesConsumed,
out int charsWritten)
Expand All @@ -498,11 +441,15 @@ private static bool TryReadCharacterStringCore(
out int bytesRead,
ref rented);

bool copied = TryReadCharacterStringCore(
contents,
destination,
encoding,
out charsWritten);
bool copied;
try
{
copied = encoding.TryGetChars(contents, destination, out charsWritten);
}
catch (DecoderFallbackException e)
{
throw new AsnContentException(SR.ContentException_DefaultMessage, e);
}

if (rented != null)
{
Expand Down
Loading
Loading