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