UTF8Encoding.Sealed.cs 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Diagnostics;
  5. namespace System.Text
  6. {
  7. public partial class UTF8Encoding
  8. {
  9. /// <summary>
  10. /// A special instance of <see cref="UTF8Encoding"/> that is initialized with "don't throw on invalid sequences;
  11. /// perform <see cref="Rune.ReplacementChar"/> substitution instead" semantics. This type allows for devirtualization
  12. /// of calls made directly off of <see cref="Encoding.UTF8"/>. See https://github.com/dotnet/coreclr/pull/9230.
  13. /// </summary>
  14. internal sealed class UTF8EncodingSealed : UTF8Encoding
  15. {
  16. /// <summary>
  17. /// Maximum number of input elements we'll allow for going through the fast one-pass stackalloc code paths.
  18. /// </summary>
  19. private const int MaxSmallInputElementCount = 32;
  20. public UTF8EncodingSealed(bool encoderShouldEmitUTF8Identifier) : base(encoderShouldEmitUTF8Identifier) { }
  21. public override ReadOnlySpan<byte> Preamble => _emitUTF8Identifier ? PreambleSpan : default;
  22. public override object Clone()
  23. {
  24. // The base implementation of Encoding.Clone calls object.MemberwiseClone and marks the new object mutable.
  25. // We don't want to do this because it violates the invariants we have set for the sealed type.
  26. // Instead, we'll create a new instance of the base UTF8Encoding type and mark it mutable.
  27. return new UTF8Encoding(_emitUTF8Identifier)
  28. {
  29. IsReadOnly = false
  30. };
  31. }
  32. public override byte[] GetBytes(string s)
  33. {
  34. // This method is short and can be inlined, meaning that the null check below
  35. // might be elided if the JIT can prove not-null at the call site.
  36. if (s?.Length <= MaxSmallInputElementCount)
  37. {
  38. return GetBytesForSmallInput(s);
  39. }
  40. else
  41. {
  42. return base.GetBytes(s!); // make the base method responsible for the null check
  43. }
  44. }
  45. private unsafe byte[] GetBytesForSmallInput(string s)
  46. {
  47. Debug.Assert(s != null);
  48. Debug.Assert(s.Length <= MaxSmallInputElementCount);
  49. byte* pDestination = stackalloc byte[MaxSmallInputElementCount * MaxUtf8BytesPerChar];
  50. int sourceLength = s.Length; // hoist this to avoid having the JIT auto-insert null checks
  51. int bytesWritten;
  52. fixed (char* pSource = s)
  53. {
  54. bytesWritten = GetBytesCommon(pSource, sourceLength, pDestination, MaxSmallInputElementCount * MaxUtf8BytesPerChar);
  55. Debug.Assert(0 <= bytesWritten && bytesWritten <= s.Length * MaxUtf8BytesPerChar);
  56. }
  57. return new Span<byte>(ref *pDestination, bytesWritten).ToArray(); // this overload of Span ctor doesn't validate length
  58. }
  59. public override string GetString(byte[] bytes)
  60. {
  61. // This method is short and can be inlined, meaning that the null check below
  62. // might be elided if the JIT can prove not-null at the call site.
  63. if (bytes?.Length <= MaxSmallInputElementCount)
  64. {
  65. return GetStringForSmallInput(bytes);
  66. }
  67. else
  68. {
  69. return base.GetString(bytes!); // make the base method responsible for the null check
  70. }
  71. }
  72. private unsafe string GetStringForSmallInput(byte[] bytes)
  73. {
  74. Debug.Assert(bytes != null);
  75. Debug.Assert(bytes.Length <= MaxSmallInputElementCount);
  76. char* pDestination = stackalloc char[MaxSmallInputElementCount]; // each byte produces at most one char
  77. int sourceLength = bytes.Length; // hoist this to avoid having the JIT auto-insert null checks
  78. int charsWritten;
  79. fixed (byte* pSource = bytes)
  80. {
  81. charsWritten = GetCharsCommon(pSource, sourceLength, pDestination, MaxSmallInputElementCount);
  82. Debug.Assert(0 <= charsWritten && charsWritten <= sourceLength); // should never have more output chars than input bytes
  83. }
  84. return new string(new ReadOnlySpan<char>(ref *pDestination, charsWritten)); // this overload of ROS ctor doesn't validate length
  85. }
  86. }
  87. }
  88. }