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 using System.Runtime.InteropServices; 7 8 namespace System.Globalization 9 { 10 public sealed partial class IdnMapping 11 { GetAsciiCore(char* unicode, int count)12 private unsafe string GetAsciiCore(char* unicode, int count) 13 { 14 Debug.Assert(!GlobalizationMode.Invariant); 15 16 uint flags = Flags; 17 18 // Determine the required length 19 int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, null, 0); 20 if (length == 0) 21 { 22 ThrowForZeroLength(unicode: true); 23 } 24 25 // Do the conversion 26 const int StackAllocThreshold = 512; // arbitrary limit to switch from stack to heap allocation 27 if (length < StackAllocThreshold) 28 { 29 char* output = stackalloc char[length]; 30 return GetAsciiCore(unicode, count, flags, output, length); 31 } 32 else 33 { 34 char[] output = new char[length]; 35 fixed (char* pOutput = &output[0]) 36 { 37 return GetAsciiCore(unicode, count, flags, pOutput, length); 38 } 39 } 40 } 41 GetAsciiCore(char* unicode, int count, uint flags, char* output, int outputLength)42 private unsafe string GetAsciiCore(char* unicode, int count, uint flags, char* output, int outputLength) 43 { 44 Debug.Assert(!GlobalizationMode.Invariant); 45 46 int length = Interop.Normaliz.IdnToAscii(flags, unicode, count, output, outputLength); 47 if (length == 0) 48 { 49 ThrowForZeroLength(unicode: true); 50 } 51 Debug.Assert(length == outputLength); 52 return new string(output, 0, length); 53 } 54 GetUnicodeCore(char* ascii, int count)55 private unsafe string GetUnicodeCore(char* ascii, int count) 56 { 57 Debug.Assert(!GlobalizationMode.Invariant); 58 59 uint flags = Flags; 60 61 // Determine the required length 62 int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, null, 0); 63 if (length == 0) 64 { 65 ThrowForZeroLength(unicode: false); 66 } 67 68 // Do the conversion 69 const int StackAllocThreshold = 512; // arbitrary limit to switch from stack to heap allocation 70 if (length < StackAllocThreshold) 71 { 72 char* output = stackalloc char[length]; 73 return GetUnicodeCore(ascii, count, flags, output, length); 74 } 75 else 76 { 77 char[] output = new char[length]; 78 fixed (char* pOutput = &output[0]) 79 { 80 return GetUnicodeCore(ascii, count, flags, pOutput, length); 81 } 82 } 83 } 84 GetUnicodeCore(char* ascii, int count, uint flags, char* output, int outputLength)85 private unsafe string GetUnicodeCore(char* ascii, int count, uint flags, char* output, int outputLength) 86 { 87 Debug.Assert(!GlobalizationMode.Invariant); 88 89 int length = Interop.Normaliz.IdnToUnicode(flags, ascii, count, output, outputLength); 90 if (length == 0) 91 { 92 ThrowForZeroLength(unicode: false); 93 } 94 Debug.Assert(length == outputLength); 95 return new string(output, 0, length); 96 } 97 98 // ----------------------------- 99 // ---- PAL layer ends here ---- 100 // ----------------------------- 101 102 private uint Flags 103 { 104 get 105 { 106 int flags = 107 (AllowUnassigned ? Interop.Normaliz.IDN_ALLOW_UNASSIGNED : 0) | 108 (UseStd3AsciiRules ? Interop.Normaliz.IDN_USE_STD3_ASCII_RULES : 0); 109 return (uint)flags; 110 } 111 } 112 ThrowForZeroLength(bool unicode)113 private static void ThrowForZeroLength(bool unicode) 114 { 115 int lastError = Marshal.GetLastWin32Error(); 116 117 throw new ArgumentException( 118 lastError == Interop.Errors.ERROR_INVALID_NAME ? SR.Argument_IdnIllegalName : 119 (unicode ? SR.Argument_InvalidCharSequenceNoIndex : SR.Argument_IdnBadPunycode), 120 unicode ? "unicode" : "ascii"); 121 } 122 } 123 } 124 125