Text.Encoding encoding,
out int charsWritten)
{
- if (source.Length == 0)
+ try
{
- charsWritten = 0;
- return true;
- }
+#if NET8_0_OR_GREATER
+ 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))
- {
- try
+ fixed (byte* bytePtr = &MemoryMarshal.GetReference(source))
+ fixed (char* charPtr = &MemoryMarshal.GetReference(destination))
{
int charCount = encoding.GetCharCount(bytePtr, source.Length);
charsWritten = encoding.GetChars(bytePtr, source.Length, charPtr, destination.Length);
Debug.Assert(charCount == charsWritten);
- }
- catch (DecoderFallbackException e)
- {
- throw new AsnContentException(SR.ContentException_DefaultMessage, e);
- }
- return true;
+ return true;
+ }
+#endif
+ }
+ catch (DecoderFallbackException e)
+ {
+ throw new AsnContentException(SR.ContentException_DefaultMessage, e);
}
}
if (output.Length > 0)
{
- output[0] = 'a';
-
copied = reader.TryReadCharacterString(
output.AsSpan(0, expectedValue.Length - 1),
UniversalTagNumber.UTF8String,
Assert.False(copied, "reader.TryCopyUTF8String - too short");
Assert.Equal(0, charsWritten);
- Assert.Equal('a', output[0]);
}
copied = reader.TryReadCharacterString(
}
}
- // TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
- /// <summary>Encodes into a span of bytes a set of characters from the specified read-only span if the destination is large enough.</summary>
- /// <param name="chars">The span containing the set of characters to encode.</param>
- /// <param name="bytes">The byte span to hold the encoded bytes.</param>
- /// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes encoded into <paramref name="bytes"/>.</param>
- /// <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>
- internal override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
+ /// <inheritdoc/>
+ public override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
{
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
}
}
+ /// <inheritdoc/>
+ public override unsafe bool TryGetChars(ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
+ {
+ fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+ fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+ {
+ int written = GetCharsCommon(bytesPtr, bytes.Length, charsPtr, chars.Length, throwForDestinationOverflow: false);
+ if (written >= 0)
+ {
+ charsWritten = written;
+ return true;
+ }
+
+ charsWritten = 0;
+ return false;
+ }
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int charCount)
+ private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int charCount, bool throwForDestinationOverflow = true)
{
// Common helper method for all non-DecoderNLS entry points to GetChars.
// A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
{
// Simple narrowing conversion couldn't operate on entire buffer - invoke fallback.
- return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten);
+ return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten, throwForDestinationOverflow);
}
}
return bytesConsumed;
}
- private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder)
+ private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder, bool throwForDestinationOverflow = true)
{
// We special-case DecoderReplacementFallback if it's telling us to write a single BMP char,
// since we believe this to be relatively common and we can handle it more efficiently than
}
else
{
- return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder);
+ return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder, throwForDestinationOverflow);
}
}
/// If the destination buffer is not large enough to hold the entirety of the transcoded data.
/// </exception>
[MethodImpl(MethodImplOptions.NoInlining)]
- private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int originalByteCount, char* pOriginalChars, int originalCharCount, int bytesConsumedSoFar, int charsWrittenSoFar)
+ private protected unsafe int GetCharsWithFallback(byte* pOriginalBytes, int originalByteCount, char* pOriginalChars, int originalCharCount, int bytesConsumedSoFar, int charsWrittenSoFar, bool throwForDestinationOverflow = true)
{
// This is a stub method that's marked "no-inlining" so that it we don't stack-spill spans
// into our immediate caller. Doing so increases the method prolog in what's supposed to
originalBytesLength: originalByteCount,
chars: new Span<char>(pOriginalChars, originalCharCount).Slice(charsWrittenSoFar),
originalCharsLength: originalCharCount,
- decoder: null);
+ decoder: null,
+ throwForDestinationOverflow);
}
/// <summary>
/// and <paramref name="charsWrittenSoFar"/> signal where in the provided buffers the fallback loop
/// should begin operating. The behavior of this method is to drain any leftover data in the
/// <see cref="DecoderNLS"/> instance, then to invoke the <see cref="GetCharsFast"/> virtual method
- /// after data has been drained, then to call <see cref="GetCharsWithFallback(ReadOnlySpan{byte}, int, Span{char}, int, DecoderNLS)"/>.
+ /// after data has been drained, then to call GetCharsWithFallback(ReadOnlySpan{byte}, int, Span{char}, int, DecoderNLS, out bool)/>.
/// </summary>
/// <returns>
/// The total number of chars written to <paramref name="pOriginalChars"/>, including <paramref name="charsWrittenSoFar"/>.
/// implementation, deferring to the base implementation if needed. This method calls <see cref="ThrowCharsOverflow"/>
/// if necessary.
/// </remarks>
- private protected virtual unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder)
+ private protected virtual unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder, bool throwForDestinationOverflow = true)
{
Debug.Assert(!bytes.IsEmpty, "Caller shouldn't invoke this method with an empty input buffer.");
Debug.Assert(originalBytesLength >= 0, "Caller provided invalid parameter.");
if (!bytes.IsEmpty)
{
- // The line below will also throw if the decoder couldn't make any progress at all
- // because the output buffer wasn't large enough to contain the result of even
- // a single scalar conversion or fallback.
-
- ThrowCharsOverflow(decoder, nothingDecoded: chars.Length == originalCharsLength);
+ if (throwForDestinationOverflow)
+ {
+ // The line below will also throw if the decoder couldn't make any progress at all
+ // because the output buffer wasn't large enough to contain the result of even
+ // a single scalar conversion or fallback.
+ ThrowCharsOverflow(decoder, nothingDecoded: chars.Length == originalCharsLength);
+ }
+ else
+ {
+ Debug.Assert(decoder is null);
+ return -1;
+ }
}
// If a DecoderNLS instance is active, update its "total consumed byte count" value.
}
}
- // TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
/// <summary>Encodes into a span of bytes a set of characters from the specified read-only span if the destination is large enough.</summary>
/// <param name="chars">The span containing the set of characters to encode.</param>
/// <param name="bytes">The byte span to hold the encoded bytes.</param>
/// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes encoded into <paramref name="bytes"/>.</param>
/// <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>
- internal virtual bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
+ public virtual bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
{
int required = GetByteCount(chars);
if (required <= bytes.Length)
}
}
+ /// <summary>Decodes into a span of chars a set of bytes from the specified read-only span if the destination is large enough.</summary>
+ /// <param name="bytes">A read-only span containing the sequence of bytes to decode.</param>
+ /// <param name="chars">The character span receiving the decoded bytes.</param>
+ /// <param name="charsWritten">Upon successful completion of the operation, the number of chars decoded into <paramref name="chars"/>.</param>
+ /// <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>
+ public virtual bool TryGetChars(ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
+ {
+ int required = GetCharCount(bytes);
+ if (required <= chars.Length)
+ {
+ charsWritten = GetChars(bytes, chars);
+ return true;
+ }
+
+ charsWritten = 0;
+ return false;
+ }
+
[CLSCompliant(false)]
public unsafe string GetString(byte* bytes, int byteCount)
{
}
}
- // TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
- /// <summary>Encodes into a span of bytes a set of characters from the specified read-only span if the destination is large enough.</summary>
- /// <param name="chars">The span containing the set of characters to encode.</param>
- /// <param name="bytes">The byte span to hold the encoded bytes.</param>
- /// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes encoded into <paramref name="bytes"/>.</param>
- /// <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>
- internal override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
+ /// <inheritdoc/>
+ public override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
{
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
}
}
+ /// <inheritdoc/>
+ public override unsafe bool TryGetChars(ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
+ {
+ if (bytes.Length <= chars.Length)
+ {
+ fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+ fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+ {
+ charsWritten = GetCharsCommon(bytesPtr, bytes.Length, charsPtr, chars.Length);
+ return true;
+ }
+ }
+
+ charsWritten = 0;
+ return false;
+ }
+
public override unsafe string GetString(byte[] bytes)
{
if (bytes is null)
return new string(new ReadOnlySpan<char>(ref *pDestination, charsWritten)); // this overload of ROS ctor doesn't validate length
}
- // TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
// TODO: Make this [Intrinsic] and handle JIT-time UTF8 encoding of literal `chars`.
- internal override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
+ /// <inheritdoc/>
+ public override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
{
return base.TryGetBytes(chars, bytes, out bytesWritten);
}
}
}
- // TODO https://github.com/dotnet/runtime/issues/84425: Make this public.
- /// <summary>Encodes into a span of bytes a set of characters from the specified read-only span if the destination is large enough.</summary>
- /// <param name="chars">The span containing the set of characters to encode.</param>
- /// <param name="bytes">The byte span to hold the encoded bytes.</param>
- /// <param name="bytesWritten">Upon successful completion of the operation, the number of bytes encoded into <paramref name="bytes"/>.</param>
- /// <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>
- internal override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
+ /// <inheritdoc/>
+ public override unsafe bool TryGetBytes(ReadOnlySpan<char> chars, Span<byte> bytes, out int bytesWritten)
{
fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
}
}
+ /// <inheritdoc/>
+ public override unsafe bool TryGetChars(ReadOnlySpan<byte> bytes, Span<char> chars, out int charsWritten)
+ {
+ fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes))
+ fixed (char* charsPtr = &MemoryMarshal.GetReference(chars))
+ {
+ int written = GetCharsCommon(bytesPtr, bytes.Length, charsPtr, chars.Length, throwForDestinationOverflow: false);
+ if (written >= 0)
+ {
+ charsWritten = written;
+ return true;
+ }
+
+ charsWritten = 0;
+ return false;
+ }
+ }
+
// WARNING: If we throw an error, then System.Resources.ResourceReader calls this method.
// So if we're really broken, then that could also throw an error... recursively.
// So try to make sure GetChars can at least process all uses by
// Note: We throw exceptions on individually encoded surrogates and other non-shortest forms.
// If exceptions aren't turned on, then we drop all non-shortest &individual surrogates.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int charCount)
+ private unsafe int GetCharsCommon(byte* pBytes, int byteCount, char* pChars, int charCount, bool throwForDestinationOverflow = true)
{
// Common helper method for all non-DecoderNLS entry points to GetChars.
// A modification of this method should be copied in to each of the supported encodings: ASCII, UTF8, UTF16, UTF32.
{
// Simple narrowing conversion couldn't operate on entire buffer - invoke fallback.
- return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten);
+ return GetCharsWithFallback(pBytes, byteCount, pChars, charCount, bytesConsumed, charsWritten, throwForDestinationOverflow);
}
}
return (int)(pOutputBufferRemaining - pChars);
}
- private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder)
+ private protected sealed override unsafe int GetCharsWithFallback(ReadOnlySpan<byte> bytes, int originalBytesLength, Span<char> chars, int originalCharsLength, DecoderNLS? decoder, bool throwForDestinationOverflow = true)
{
// We special-case DecoderReplacementFallback if it's telling us to write a single U+FFFD char,
// since we believe this to be relatively common and we can handle it more efficiently than
}
else
{
- return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder);
+ return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder, throwForDestinationOverflow);
}
}
try
{
// If we're asking for more than are possibly available, or more than are truly available then we can return the entire thing
- if (charCount >= encoding.GetMaxCharCount(byteCount) || charCount >= encoding.GetCharCount(bytes, byteOffset, byteCount))
+ if (encoding.TryGetChars(bytes.AsSpan(byteOffset, byteCount), chars.AsSpan(charOffset, charCount), out actualCharCount))
{
- actualCharCount = encoding.GetChars(bytes, byteOffset, byteCount, chars, charOffset);
actualByteCount = byteCount;
}
else
public bool IsAlwaysNormalized() { throw null; }
public virtual bool IsAlwaysNormalized(System.Text.NormalizationForm form) { throw null; }
public static void RegisterProvider(System.Text.EncodingProvider provider) { }
+ public virtual bool TryGetBytes(System.ReadOnlySpan<char> chars, System.Span<byte> bytes, out int bytesWritten) { throw null; }
+ public virtual bool TryGetChars(System.ReadOnlySpan<byte> bytes, System.Span<char> chars, out int charsWritten) { throw null; }
}
public sealed partial class EncodingInfo
{
public override int GetMaxByteCount(int charCount) { throw null; }
public override int GetMaxCharCount(int byteCount) { throw null; }
public override string GetString(byte[] bytes, int byteIndex, int byteCount) { throw null; }
+ public override bool TryGetBytes(System.ReadOnlySpan<char> chars, System.Span<byte> bytes, out int bytesWritten) { throw null; }
+ public override bool TryGetChars(System.ReadOnlySpan<byte> bytes, System.Span<char> chars, out int charsWritten) { throw null; }
}
public partial class UnicodeEncoding : System.Text.Encoding
{
public override int GetMaxCharCount(int byteCount) { throw null; }
public override byte[] GetPreamble() { throw null; }
public override string GetString(byte[] bytes, int index, int count) { throw null; }
+ public override bool TryGetBytes(System.ReadOnlySpan<char> chars, System.Span<byte> bytes, out int bytesWritten) { throw null; }
+ public override bool TryGetChars(System.ReadOnlySpan<byte> bytes, System.Span<char> chars, out int charsWritten) { throw null; }
}
}
Assert.Equal(expected.Length, encoding.GetBytes(chars.AsSpan(index, count), (Span<byte>)stringResultAdvanced));
VerifyGetBytes(stringResultAdvanced, 0, stringResultAdvanced.Length, new byte[expected.Length], expected);
+ // Use TryGetBytes(ReadOnlySpan<char>, Span<byte>, out int bytesWritten)
+ Array.Clear(stringResultAdvanced);
+ Assert.True(encoding.TryGetBytes(chars.AsSpan(index, count), (Span<byte>)stringResultAdvanced, out int bytesWritten));
+ Assert.Equal(expected.Length, bytesWritten);
+ VerifyGetBytes(stringResultAdvanced, 0, stringResultAdvanced.Length, new byte[expected.Length], expected);
+
if (count == 0)
Assert.Equal(expected.Length, encoding.GetBytes(ReadOnlySpan<char>.Empty, (Span<byte>)stringResultAdvanced));
}
VerifyGetChars(byteChars, charIndex, charCount, (char[])chars.Clone(), expectedChars);
Assert.Equal(expectedChars.Length, charCount);
+ // Use TryGetChars(ReadOnlySpan<byte>, Span<char>, out int charsWritten)
+ byteChars = (char[])chars.Clone();
+ Assert.True(encoding.TryGetChars(new ReadOnlySpan<byte>(bytes, byteIndex, byteCount), new Span<char>(byteChars).Slice(charIndex), out charCount));
+ VerifyGetChars(byteChars, charIndex, charCount, (char[])chars.Clone(), expectedChars);
+ Assert.Equal(expectedChars.Length, charCount);
+
if (byteCount == 0)
{
charCount = encoding.GetChars(ReadOnlySpan<byte>.Empty, new Span<char>(byteChars).Slice(charIndex));
AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes("a", 0, 1, new byte[0], 0));
AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes("abc", 0, 3, new byte[1], 0));
AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes("\uD800\uDC00", 0, 2, new byte[1], 0));
+
AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes(new char[1], 0, 1, new byte[0], 0));
AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes(new char[3], 0, 3, new byte[1], 0));
AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes("\uD800\uDC00".ToCharArray(), 0, 2, new byte[1], 0));
+ AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes((ReadOnlySpan<char>)new char[1], (Span<byte>)new byte[0]));
+ AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes((ReadOnlySpan<char>)new char[3], (Span<byte>)new byte[1]));
+ AssertExtensions.Throws<ArgumentException>("bytes", () => encoding.GetBytes((ReadOnlySpan<char>)"\uD800\uDC00".ToCharArray(), (Span<byte>)new byte[1]));
+
+ Assert.False(encoding.TryGetBytes((ReadOnlySpan<char>)new char[1], (Span<byte>)new byte[0], out int bytesWritten));
+ Assert.Equal(0, bytesWritten);
+ Assert.False(encoding.TryGetBytes((ReadOnlySpan<char>)new char[3], (Span<byte>)new byte[1], out bytesWritten));
+ Assert.Equal(0, bytesWritten);
+ Assert.False(encoding.TryGetBytes((ReadOnlySpan<char>)"\uD800\uDC00".ToCharArray(), (Span<byte>)new byte[1], out bytesWritten));
+ Assert.Equal(0, bytesWritten);
+
char[] chars = new char[3];
byte[] bytes = new byte[3];
byte[] smallBytes = new byte[1];
// Chars does not have enough capacity to accommodate result
AssertExtensions.Throws<ArgumentException>("chars", () => encoding.GetChars(new byte[4], 0, 4, new char[1], 1));
+ AssertExtensions.Throws<ArgumentException>("chars", () => encoding.GetChars((ReadOnlySpan<byte>)new byte[4], (new char[1]).AsSpan(1)));
+ Assert.False(encoding.TryGetChars((ReadOnlySpan<byte>)new byte[4], (new char[1]).AsSpan(1), out int charsWritten));
+ Assert.Equal(0, charsWritten);
byte[] bytes = new byte[encoding.GetMaxByteCount(2)];
char[] chars = new char[4];