IdnMapping.Unix.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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.Globalization
  6. {
  7. sealed partial class IdnMapping
  8. {
  9. private unsafe string GetAsciiCore(string unicodeString, char* unicode, int count)
  10. {
  11. Debug.Assert(!GlobalizationMode.Invariant);
  12. Debug.Assert(unicodeString != null && unicodeString.Length >= count);
  13. uint flags = Flags;
  14. CheckInvalidIdnCharacters(unicode, count, flags, nameof(unicode));
  15. const int StackallocThreshold = 512;
  16. // Each unicode character is represented by up to 3 ASCII chars
  17. // and the whole string is prefixed by "xn--" (length 4)
  18. int estimatedLength = (int)Math.Min(count * 3L + 4, StackallocThreshold);
  19. int actualLength;
  20. if (estimatedLength < StackallocThreshold)
  21. {
  22. char* outputStack = stackalloc char[estimatedLength];
  23. actualLength = Interop.Globalization.ToAscii(flags, unicode, count, outputStack, estimatedLength);
  24. if (actualLength > 0 && actualLength <= estimatedLength)
  25. {
  26. return GetStringForOutput(unicodeString, unicode, count, outputStack, actualLength);
  27. }
  28. }
  29. else
  30. {
  31. actualLength = Interop.Globalization.ToAscii(flags, unicode, count, null, 0);
  32. }
  33. if (actualLength == 0)
  34. {
  35. throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(unicode));
  36. }
  37. char[] outputHeap = new char[actualLength];
  38. fixed (char* pOutputHeap = &outputHeap[0])
  39. {
  40. actualLength = Interop.Globalization.ToAscii(flags, unicode, count, pOutputHeap, actualLength);
  41. if (actualLength == 0 || actualLength > outputHeap.Length)
  42. {
  43. throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(unicode));
  44. }
  45. return GetStringForOutput(unicodeString, unicode, count, pOutputHeap, actualLength);
  46. }
  47. }
  48. private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count)
  49. {
  50. Debug.Assert(!GlobalizationMode.Invariant);
  51. Debug.Assert(asciiString != null && asciiString.Length >= count);
  52. uint flags = Flags;
  53. CheckInvalidIdnCharacters(ascii, count, flags, nameof(ascii));
  54. const int StackAllocThreshold = 512;
  55. if (count < StackAllocThreshold)
  56. {
  57. char* output = stackalloc char[count];
  58. return GetUnicodeCore(asciiString, ascii, count, flags, output, count, reattempt: true);
  59. }
  60. else
  61. {
  62. char[] output = new char[count];
  63. fixed (char* pOutput = &output[0])
  64. {
  65. return GetUnicodeCore(asciiString, ascii, count, flags, pOutput, count, reattempt: true);
  66. }
  67. }
  68. }
  69. private unsafe string GetUnicodeCore(string asciiString, char* ascii, int count, uint flags, char* output, int outputLength, bool reattempt)
  70. {
  71. Debug.Assert(!GlobalizationMode.Invariant);
  72. Debug.Assert(asciiString != null && asciiString.Length >= count);
  73. int realLen = Interop.Globalization.ToUnicode(flags, ascii, count, output, outputLength);
  74. if (realLen == 0)
  75. {
  76. throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(ascii));
  77. }
  78. else if (realLen <= outputLength)
  79. {
  80. return GetStringForOutput(asciiString, ascii, count, output, realLen);
  81. }
  82. else if (reattempt)
  83. {
  84. char[] newOutput = new char[realLen];
  85. fixed (char* pNewOutput = newOutput)
  86. {
  87. return GetUnicodeCore(asciiString, ascii, count, flags, pNewOutput, realLen, reattempt: false);
  88. }
  89. }
  90. throw new ArgumentException(SR.Argument_IdnIllegalName, nameof(ascii));
  91. }
  92. // -----------------------------
  93. // ---- PAL layer ends here ----
  94. // -----------------------------
  95. private uint Flags
  96. {
  97. get
  98. {
  99. int flags =
  100. (AllowUnassigned ? Interop.Globalization.AllowUnassigned : 0) |
  101. (UseStd3AsciiRules ? Interop.Globalization.UseStd3AsciiRules : 0);
  102. return (uint)flags;
  103. }
  104. }
  105. /// <summary>
  106. /// ICU doesn't check for invalid characters unless the STD3 rules option
  107. /// is enabled.
  108. ///
  109. /// To match Windows behavior, we walk the string ourselves looking for these
  110. /// bad characters so we can continue to throw ArgumentException in these cases.
  111. /// </summary>
  112. private static unsafe void CheckInvalidIdnCharacters(char* s, int count, uint flags, string paramName)
  113. {
  114. if ((flags & Interop.Globalization.UseStd3AsciiRules) == 0)
  115. {
  116. for (int i = 0; i < count; i++)
  117. {
  118. char c = s[i];
  119. // These characters are prohibited regardless of the UseStd3AsciiRules property.
  120. // See https://msdn.microsoft.com/en-us/library/system.globalization.idnmapping.usestd3asciirules(v=vs.110).aspx
  121. if (c <= 0x1F || c == 0x7F)
  122. {
  123. throw new ArgumentException(SR.Argument_IdnIllegalName, paramName);
  124. }
  125. }
  126. }
  127. }
  128. }
  129. }