Skip to content

Commit 7022bf4

Browse files
committed
Add public Encoding.TryGetBytes/Chars
1 parent c7aadc0 commit 7022bf4

File tree

13 files changed

+157
-61
lines changed

13 files changed

+157
-61
lines changed

src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -390,16 +390,19 @@ private static unsafe bool TryReadCharacterStringCore(
390390
Text.Encoding encoding,
391391
out int charsWritten)
392392
{
393-
if (source.Length == 0)
393+
try
394394
{
395-
charsWritten = 0;
396-
return true;
397-
}
395+
#if NET8_0_OR_GREATER
396+
return encoding.TryGetChars(source, destination, out charsWritten);
397+
#else
398+
if (source.Length == 0)
399+
{
400+
charsWritten = 0;
401+
return true;
402+
}
398403

399-
fixed (byte* bytePtr = &MemoryMarshal.GetReference(source))
400-
fixed (char* charPtr = &MemoryMarshal.GetReference(destination))
401-
{
402-
try
404+
fixed (byte* bytePtr = &MemoryMarshal.GetReference(source))
405+
fixed (char* charPtr = &MemoryMarshal.GetReference(destination))
403406
{
404407
int charCount = encoding.GetCharCount(bytePtr, source.Length);
405408

@@ -411,13 +414,14 @@ private static unsafe bool TryReadCharacterStringCore(
411414

412415
charsWritten = encoding.GetChars(bytePtr, source.Length, charPtr, destination.Length);
413416
Debug.Assert(charCount == charsWritten);
414-
}
415-
catch (DecoderFallbackException e)
416-
{
417-
throw new AsnContentException(SR.ContentException_DefaultMessage, e);
418-
}
419417

420-
return true;
418+
return true;
419+
}
420+
#endif
421+
}
422+
catch (DecoderFallbackException e)
423+
{
424+
throw new AsnContentException(SR.ContentException_DefaultMessage, e);
421425
}
422426
}
423427

src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,13 @@ public static void TryCopyUTF8String(
148148

149149
if (output.Length > 0)
150150
{
151-
output[0] = 'a';
152-
153151
copied = reader.TryReadCharacterString(
154152
output.AsSpan(0, expectedValue.Length - 1),
155153
UniversalTagNumber.UTF8String,
156154
out charsWritten);
157155

158156
Assert.False(copied, "reader.TryCopyUTF8String - too short");
159157
Assert.Equal(0, charsWritten);
160-
Assert.Equal('a', output[0]);
161158
}
162159

163160
copied = reader.TryReadCharacterString(

src/libraries/System.Private.CoreLib/src/System/Text/ASCIIEncoding.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -322,13 +322,8 @@ public override unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
322322
}
323323
}
324324

325-
// TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
326-
/// <summary>Encodes into a span of bytes a set of characters from the specified read-only span if the destination is large enough.</summary>
327-
/// <param name="chars">The span containing the set of characters to encode.</param>
328-
/// <param name="bytes">The byte span to hold the encoded bytes.</param>
329-
/// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes encoded into <paramref name="bytes"/>.</param>
330-
/// <returns><see langword="true"/> if all of the characters were encoded into the destination; <see langword="false"/> if the destination was too small to contain all the encoded bytes.</returns>
331-
internal override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
325+
/// <inheritdoc/>
326+
public override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
332327
{
333328
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
334329
fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
@@ -618,8 +613,26 @@ public override unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
618613
}
619614
}
620615

616+
/// <inheritdoc/>
617+
public override unsafe bool TryGetChars(ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
618+
{
619+
fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
620+
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
621+
{
622+
int written = GetCharsCommon(bytesPtr, bytes.Length, charsPtr, chars.Length, throwForDestinationOverflow: false);
623+
if (written >= 0)
624+
{
625+
charsWritten = written;
626+
return true;
627+
}
628+
629+
charsWritten = 0;
630+
return false;
631+
}
632+
}
633+
621634
[MethodImpl(MethodImplOptions.AggressiveInlining)]
622-
private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int charCount)
635+
private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int charCount, bool throwForDestinationOverflow = true)
623636
{
624637
// Common helper method for all non-DecoderNLS entry points to GetChars.
625638
// A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
@@ -643,7 +656,7 @@ private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int
643656
{
644657
// Simple narrowing conversion couldn't operate on entire buffer - invoke fallback.
645658

646-
return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten);
659+
return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten, throwForDestinationOverflow);
647660
}
648661
}
649662

@@ -654,7 +667,7 @@ private protected sealed override unsafe int GetCharsFast(byte* pBytes, int byte
654667
return bytesConsumed;
655668
}
656669

657-
private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder)
670+
private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder, bool throwForDestinationOverflow = true)
658671
{
659672
// We special-case DecoderReplacementFallback if it's telling us to write a single BMP char,
660673
// since we believe this to be relatively common and we can handle it more efficiently than
@@ -699,7 +712,7 @@ private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<b
699712
}
700713
else
701714
{
702-
return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder);
715+
return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder, throwForDestinationOverflow);
703716
}
704717
}
705718

src/libraries/System.Private.CoreLib/src/System/Text/Encoding.Internal.cs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,7 +1081,7 @@ private protected virtual unsafe int GetCharsFast(byte* pBytes, int bytesLength,
10811081
/// If the destination buffer is not large enough to hold the entirety of the transcoded data.
10821082
/// </exception>
10831083
[MethodImpl(MethodImplOptions.NoInlining)]
1084-
private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int originalByteCount, char* pOriginalChars, int originalCharCount, int bytesConsumedSoFar, int charsWrittenSoFar)
1084+
private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int originalByteCount, char* pOriginalChars, int originalCharCount, int bytesConsumedSoFar, int charsWrittenSoFar, bool throwForDestinationOverflow = true)
10851085
{
10861086
// This is a stub method that's marked "no-inlining" so that it we don't stack-spill spans
10871087
// into our immediate caller. Doing so increases the method prolog in what's supposed to
@@ -1095,7 +1095,8 @@ private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int orig
10951095
originalBytesLength: originalByteCount,
10961096
chars: new Span<char>(pOriginalChars, originalCharCount).Slice(charsWrittenSoFar),
10971097
originalCharsLength: originalCharCount,
1098-
decoder: null);
1098+
decoder: null,
1099+
throwForDestinationOverflow);
10991100
}
11001101

11011102
/// <summary>
@@ -1104,7 +1105,7 @@ private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int orig
11041105
/// and <paramref name="charsWrittenSoFar"/> signal where in the provided buffers the fallback loop
11051106
/// should begin operating. The behavior of this method is to drain any leftover data in the
11061107
/// <see cref="DecoderNLS"/> instance, then to invoke the <see cref="GetCharsFast"/> virtual method
1107-
/// after data has been drained, then to call <see cref="GetCharsWithFallback(ReadOnlySpan{byte}, int, Span{char}, int, DecoderNLS)"/>.
1108+
/// after data has been drained, then to call GetCharsWithFallback(ReadOnlySpan{byte}, int, Span{char}, int, DecoderNLS, out bool)/>.
11081109
/// </summary>
11091110
/// <returns>
11101111
/// The total number of chars written to <paramref name="pOriginalChars"/>, including <paramref name="charsWrittenSoFar"/>.
@@ -1183,7 +1184,7 @@ private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int orig
11831184
/// implementation, deferring to the base implementation if needed. This method calls <see cref="ThrowCharsOverflow"/>
11841185
/// if necessary.
11851186
/// </remarks>
1186-
private protected virtual unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder)
1187+
private protected virtual unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder, bool throwForDestinationOverflow = true)
11871188
{
11881189
Debug.Assert(!bytes.IsEmpty, "Caller shouldn't invoke this method with an empty input buffer.");
11891190
Debug.Assert(originalBytesLength >= 0, "Caller provided invalid parameter.");
@@ -1272,11 +1273,18 @@ private protected virtual unsafe int GetCharsWithFallback(ReadOnlySpan<byte> byt
12721273

12731274
if (!bytes.IsEmpty)
12741275
{
1275-
// The line below will also throw if the decoder couldn't make any progress at all
1276-
// because the output buffer wasn't large enough to contain the result of even
1277-
// a single scalar conversion or fallback.
1278-
1279-
ThrowCharsOverflow(decoder, nothingDecoded: chars.Length == originalCharsLength);
1276+
if (throwForDestinationOverflow)
1277+
{
1278+
// The line below will also throw if the decoder couldn't make any progress at all
1279+
// because the output buffer wasn't large enough to contain the result of even
1280+
// a single scalar conversion or fallback.
1281+
ThrowCharsOverflow(decoder, nothingDecoded: chars.Length == originalCharsLength);
1282+
}
1283+
else
1284+
{
1285+
Debug.Assert(decoder is null);
1286+
return -1;
1287+
}
12801288
}
12811289

12821290
// If a DecoderNLS instance is active, update its "total consumed byte count" value.

src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -733,13 +733,12 @@ public virtual unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
733733
}
734734
}
735735

736-
// TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
737736
/// <summary>Encodes into a span of bytes a set of characters from the specified read-only span if the destination is large enough.</summary>
738737
/// <param name="chars">The span containing the set of characters to encode.</param>
739738
/// <param name="bytes">The byte span to hold the encoded bytes.</param>
740739
/// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes encoded into <paramref name="bytes"/>.</param>
741740
/// <returns><see langword="true"/> if all of the characters were encoded into the destination; <see langword="false"/> if the destination was too small to contain all the encoded bytes.</returns>
742-
internal virtual bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
741+
public virtual bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
743742
{
744743
int required = GetByteCount(chars);
745744
if (required <= bytes.Length)
@@ -883,6 +882,24 @@ public virtual unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
883882
}
884883
}
885884

885+
/// <summary>Decodes into a span of chars a set of bytes from the specified read-only span if the destination is large enough.</summary>
886+
/// <param name="bytes">A read-only span containing the sequence of bytes to decode.</param>
887+
/// <param name="chars">The character span receiving the decoded bytes.</param>
888+
/// <param name="charsWritten">Upon successful completion of the operation, the number of chars decoded into <paramref name="chars"/>.</param>
889+
/// <returns><see langword="true"/> if all of the characters were decoded into the destination; <see langword="false"/> if the destination was too small to contain all the decoded chars.</returns>
890+
public virtual bool TryGetChars(ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
891+
{
892+
int required = GetCharCount(bytes);
893+
if (required <= chars.Length)
894+
{
895+
charsWritten = GetChars(bytes, chars);
896+
return true;
897+
}
898+
899+
charsWritten = 0;
900+
return false;
901+
}
902+
886903
[CLSCompliant(false)]
887904
public unsafe string GetString(byte* bytes, int byteCount)
888905
{

src/libraries/System.Private.CoreLib/src/System/Text/Latin1Encoding.cs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,13 +234,8 @@ public override unsafe int GetBytes(ReadOnlySpan<char> chars, Span<byte> bytes)
234234
}
235235
}
236236

237-
// TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
238-
/// <summary>Encodes into a span of bytes a set of characters from the specified read-only span if the destination is large enough.</summary>
239-
/// <param name="chars">The span containing the set of characters to encode.</param>
240-
/// <param name="bytes">The byte span to hold the encoded bytes.</param>
241-
/// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes encoded into <paramref name="bytes"/>.</param>
242-
/// <returns><see langword="true"/> if all of the characters were encoded into the destination; <see langword="false"/> if the destination was too small to contain all the encoded bytes.</returns>
243-
internal override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
237+
/// <inheritdoc/>
238+
public override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
244239
{
245240
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
246241
fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
@@ -534,6 +529,23 @@ public override unsafe int GetChars(ReadOnlySpan<byte> bytes, Span<char> chars)
534529
}
535530
}
536531

532+
/// <inheritdoc/>
533+
public override unsafe bool TryGetChars(ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
534+
{
535+
if (bytes.Length <= chars.Length)
536+
{
537+
fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
538+
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
539+
{
540+
charsWritten = GetCharsCommon(bytesPtr, bytes.Length, charsPtr, chars.Length);
541+
return true;
542+
}
543+
}
544+
545+
charsWritten = 0;
546+
return false;
547+
}
548+
537549
public override unsafe string GetString(byte[] bytes)
538550
{
539551
if (bytes is null)

src/libraries/System.Private.CoreLib/src/System/Text/UTF8Encoding.Sealed.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ private unsafe string GetStringForSmallInput(byte[] bytes)
147147
return new string(new ReadOnlySpan<char>(ref *pDestination, charsWritten)); // this overload of ROS ctor doesn't validate length
148148
}
149149

150-
// TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
151150
// TODO: Make this [Intrinsic] and handle JIT-time UTF8 encoding of literal `chars`.
152-
internal override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
151+
/// <inheritdoc/>
152+
public override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
153153
{
154154
return base.TryGetBytes(chars, bytes, out bytesWritten);
155155
}

0 commit comments

Comments
 (0)