Add public Encoding.TryGetBytes/Chars (#85120)
authorStephen Toub <stoub@microsoft.com>
Sun, 23 Apr 2023 19:12:31 +0000 (15:12 -0400)
committerGitHub <noreply@github.com>
Sun, 23 Apr 2023 19:12:31 +0000 (15:12 -0400)
13 files changed:
src/libraries/System.Formats.Asn1/src/System/Formats/Asn1/AsnDecoder.Text.cs
src/libraries/System.Formats.Asn1/tests/Reader/ReadUTF8String.cs
src/libraries/System.Private.CoreLib/src/System/Text/ASCIIEncoding.cs
src/libraries/System.Private.CoreLib/src/System/Text/Encoding.Internal.cs
src/libraries/System.Private.CoreLib/src/System/Text/Encoding.cs
src/libraries/System.Private.CoreLib/src/System/Text/Latin1Encoding.cs
src/libraries/System.Private.CoreLib/src/System/Text/UTF8Encoding.Sealed.cs
src/libraries/System.Private.CoreLib/src/System/Text/UTF8Encoding.cs
src/libraries/System.Private.DataContractSerialization/src/System/Xml/ValueHandle.cs
src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.Text.Encoding.Extensions/ref/System.Text.Encoding.Extensions.cs
src/libraries/System.Text.Encoding/tests/EncodingTestHelpers.cs
src/libraries/System.Text.Encoding/tests/NegativeEncodingTests.cs

index fb39a10499084da7bbaa38a7e9119fa18db8560c..b9556fdd2850498a4fe0ccda24aa9180d13b876d 100644 (file)
@@ -390,16 +390,19 @@ namespace System.Formats.Asn1
             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);
 
@@ -411,13 +414,14 @@ namespace System.Formats.Asn1
 
                     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);
             }
         }
 
index db38b0b2d4f3569ff6b6ce474f4c1f6c1f7acb16..23ebf28c4028781e6516d883494f4914c49dc6b7 100644 (file)
@@ -148,8 +148,6 @@ namespace System.Formats.Asn1.Tests.Reader
 
             if (output.Length > 0)
             {
-                output[0] = 'a';
-
                 copied = reader.TryReadCharacterString(
                     output.AsSpan(0, expectedValue.Length - 1),
                     UniversalTagNumber.UTF8String,
@@ -157,7 +155,6 @@ namespace System.Formats.Asn1.Tests.Reader
 
                 Assert.False(copied, "reader.TryCopyUTF8String - too short");
                 Assert.Equal(0, charsWritten);
-                Assert.Equal('a', output[0]);
             }
 
             copied = reader.TryReadCharacterString(
index 57b345043e92e05778ea99636fdbc023d7edd489..4d02033b642bfb574bc448837505d6c02c0313f7 100644 (file)
@@ -322,13 +322,8 @@ namespace System.Text
             }
         }
 
-        // 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))
@@ -618,8 +613,26 @@ namespace System.Text
             }
         }
 
+        /// <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.
@@ -643,7 +656,7 @@ namespace System.Text
             {
                 // 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);
             }
         }
 
@@ -654,7 +667,7 @@ namespace System.Text
             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
@@ -699,7 +712,7 @@ namespace System.Text
             }
             else
             {
-                return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder);
+                return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder, throwForDestinationOverflow);
             }
         }
 
index 874513163002a7e5dfe5a86e76b771f315df1631..5ccf6b9039b8e04492ae8e8bf011091321ce4062 100644 (file)
@@ -1081,7 +1081,7 @@ namespace System.Text
         /// 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
@@ -1095,7 +1095,8 @@ namespace System.Text
                 originalBytesLength: originalByteCount,
                 chars: new Span<char>(pOriginalChars, originalCharCount).Slice(charsWrittenSoFar),
                 originalCharsLength: originalCharCount,
-                decoder: null);
+                decoder: null,
+                throwForDestinationOverflow);
         }
 
         /// <summary>
@@ -1104,7 +1105,7 @@ namespace System.Text
         /// 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"/>.
@@ -1183,7 +1184,7 @@ namespace System.Text
         /// 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.");
@@ -1272,11 +1273,18 @@ namespace System.Text
 
                 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.
index 5ebbeeafcfadd47898bdc87e81aabd9ccbea2002..9935024ae8fc333a9863b98cc1e5f1d1d1836a8b 100644 (file)
@@ -733,13 +733,12 @@ namespace System.Text
             }
         }
 
-        // 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)
@@ -883,6 +882,24 @@ namespace System.Text
             }
         }
 
+        /// <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)
         {
index 7167d4de6d6dbf4590f89bc78bddc707cc915908..04d0e4f93e09a78d52a76f555cb59acbc584b695 100644 (file)
@@ -234,13 +234,8 @@ namespace System.Text
             }
         }
 
-        // 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))
@@ -534,6 +529,23 @@ namespace System.Text
             }
         }
 
+        /// <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)
index 3679e90e34d593a0ffe0dd1cfb565579b1251fab..dc5f36deca8de2c8a8b7b07aebe3a0eece394eb8 100644 (file)
@@ -147,9 +147,9 @@ namespace System.Text
                 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);
             }
index 9bb500f5ddf074813407f4a0f8cf3dc09071f7f6..f4eb8a5358a20a02abaf344fd6072e512a824874 100644 (file)
@@ -369,13 +369,8 @@ namespace System.Text
             }
         }
 
-        // 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))
@@ -569,6 +564,24 @@ namespace System.Text
             }
         }
 
+        /// <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
@@ -577,7 +590,7 @@ namespace System.Text
         // 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.
@@ -601,7 +614,7 @@ namespace System.Text
             {
                 // 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);
             }
         }
 
@@ -618,7 +631,7 @@ namespace System.Text
             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
@@ -649,7 +662,7 @@ namespace System.Text
             }
             else
             {
-                return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder);
+                return base.GetCharsWithFallback(bytes, originalBytesLength, chars, originalCharsLength, decoder, throwForDestinationOverflow);
             }
         }
 
index 98f7229c0759af69e0512a377f50351fea58c00a..2b875e1f6b160142315656a71b6516ac0ea8bc93 100644 (file)
@@ -730,9 +730,8 @@ namespace System.Xml
                 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
index 4148bbebe759c130f0552ca9b22362c0901359ba..07ccd203fdf34be0458f45486a73acc236145585 100644 (file)
@@ -14441,6 +14441,8 @@ namespace System.Text
         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
     {
index a13aedd4f5733396841a1dba94fdeaf341792dd4..0c9b8230911fe9d3bc7c0e6aac00a3e43d89b2ab 100644 (file)
@@ -33,6 +33,8 @@ namespace System.Text
         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
     {
@@ -153,5 +155,7 @@ namespace System.Text
         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; }
     }
 }
index f049afdf196f313f3851ba3d19f2ea100c3a1a41..9b9170cf5826bca98b71415e134ddb38a1d29bea 100644 (file)
@@ -268,6 +268,12 @@ namespace System.Text.Tests
             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));
         }
@@ -289,6 +295,12 @@ namespace System.Text.Tests
             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));
index 1754d8771ab44bc3252d50c4399a8dfe56b3113d..d0e1e5a51cfa6f878e7504861a06d154a5c993da 100644 (file)
@@ -128,10 +128,22 @@ namespace System.Text.Tests
             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];
@@ -223,6 +235,9 @@ namespace System.Text.Tests
 
             // 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];