// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System.Diagnostics; namespace System.Text { public partial class UTF8Encoding { /// /// A special instance of that is initialized with "don't throw on invalid sequences; /// perform substitution instead" semantics. This type allows for devirtualization /// of calls made directly off of . See https://github.com/dotnet/coreclr/pull/9230. /// internal sealed class UTF8EncodingSealed : UTF8Encoding { /// /// Maximum number of input elements we'll allow for going through the fast one-pass stackalloc code paths. /// private const int MaxSmallInputElementCount = 32; public UTF8EncodingSealed(bool encoderShouldEmitUTF8Identifier) : base(encoderShouldEmitUTF8Identifier) { } public override ReadOnlySpan Preamble => _emitUTF8Identifier ? PreambleSpan : default; public override object Clone() { // The base implementation of Encoding.Clone calls object.MemberwiseClone and marks the new object mutable. // We don't want to do this because it violates the invariants we have set for the sealed type. // Instead, we'll create a new instance of the base UTF8Encoding type and mark it mutable. return new UTF8Encoding(_emitUTF8Identifier) { IsReadOnly = false }; } public override byte[] GetBytes(string s) { // This method is short and can be inlined, meaning that the null check below // might be elided if the JIT can prove not-null at the call site. if (s?.Length <= MaxSmallInputElementCount) { return GetBytesForSmallInput(s); } else { return base.GetBytes(s!); // make the base method responsible for the null check } } private unsafe byte[] GetBytesForSmallInput(string s) { Debug.Assert(s != null); Debug.Assert(s.Length <= MaxSmallInputElementCount); byte* pDestination = stackalloc byte[MaxSmallInputElementCount * MaxUtf8BytesPerChar]; int sourceLength = s.Length; // hoist this to avoid having the JIT auto-insert null checks int bytesWritten; fixed (char* pSource = s) { bytesWritten = GetBytesCommon(pSource, sourceLength, pDestination, MaxSmallInputElementCount * MaxUtf8BytesPerChar); Debug.Assert(0 <= bytesWritten && bytesWritten <= s.Length * MaxUtf8BytesPerChar); } return new Span(ref *pDestination, bytesWritten).ToArray(); // this overload of Span ctor doesn't validate length } public override string GetString(byte[] bytes) { // This method is short and can be inlined, meaning that the null check below // might be elided if the JIT can prove not-null at the call site. if (bytes?.Length <= MaxSmallInputElementCount) { return GetStringForSmallInput(bytes); } else { return base.GetString(bytes!); // make the base method responsible for the null check } } private unsafe string GetStringForSmallInput(byte[] bytes) { Debug.Assert(bytes != null); Debug.Assert(bytes.Length <= MaxSmallInputElementCount); char* pDestination = stackalloc char[MaxSmallInputElementCount]; // each byte produces at most one char int sourceLength = bytes.Length; // hoist this to avoid having the JIT auto-insert null checks int charsWritten; fixed (byte* pSource = bytes) { charsWritten = GetCharsCommon(pSource, sourceLength, pDestination, MaxSmallInputElementCount); Debug.Assert(0 <= charsWritten && charsWritten <= sourceLength); // should never have more output chars than input bytes } return new string(new ReadOnlySpan(ref *pDestination, charsWritten)); // this overload of ROS ctor doesn't validate length } } } }