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