Sync with TP.
[libidn.git] / csharp / Punycode.cs
blob9058c27e8d9dfe6889bd4c8078a5eebae2f9341a
1 /// <summary>
2 /// *
3 /// Author: Alexander Gnauck AG-Software, mailto:gnauck@ag-software.de
4 /// *
5 /// This file is part of GNU Libidn.
6 /// *
7 /// This library is free software; you can redistribute it and/or
8 /// modify it under the terms of the GNU Lesser General Public License
9 /// as published by the Free Software Foundation; either version 2.1 of
10 /// the License, or (at your option) any later version.
11 /// *
12 /// This library is distributed in the hope that it will be useful, but
13 /// WITHOUT ANY WARRANTY; without even the implied warranty of
14 /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 /// Lesser General Public License for more details.
16 /// *
17 /// You should have received a copy of the GNU Lesser General Public
18 /// License along with this library; if not, write to the Free Software
19 /// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 /// USA
21 /// </summary>
23 using System;
24 using System.Text;
26 namespace Gnu.Inet.Encoding
29 public class Punycode
31 /* Punycode parameters */
32 internal const int TMIN = 1;
33 internal const int TMAX = 26;
34 internal const int BASE = 36;
35 internal const int INITIAL_N = 128;
36 internal const int INITIAL_BIAS = 72;
37 internal const int DAMP = 700;
38 internal const int SKEW = 38;
39 internal const char DELIMITER = '-';
41 /// <summary>
42 /// Punycodes a unicode string.
43 /// </summary>
44 /// <param name="input">Unicode string.</param>
45 /// <returns> Punycoded string.</returns>
46 public static string Encode(string input)
48 int n = INITIAL_N;
49 int delta = 0;
50 int bias = INITIAL_BIAS;
51 StringBuilder output = new StringBuilder();
53 // Copy all basic code points to the output
54 int b = 0;
55 for (int i = 0; i < input.Length; i++)
57 char c = input[i];
58 if (IsBasic(c))
60 output.Append(c);
61 b++;
65 // Append delimiter
66 if (b > 0)
68 output.Append(DELIMITER);
71 int h = b;
72 while (h < input.Length)
74 int m = System.Int32.MaxValue;
76 // Find the minimum code point >= n
77 for (int i = 0; i < input.Length; i++)
79 int c = input[i];
80 if (c >= n && c < m)
82 m = c;
86 if (m - n > (System.Int32.MaxValue - delta) / (h + 1))
88 throw new PunycodeException(PunycodeException.OVERFLOW);
90 delta = delta + (m - n) * (h + 1);
91 n = m;
93 for (int j = 0; j < input.Length; j++)
95 int c = input[j];
96 if (c < n)
98 delta++;
99 if (0 == delta)
101 throw new PunycodeException(PunycodeException.OVERFLOW);
104 if (c == n)
106 int q = delta;
108 for (int k = BASE; ; k += BASE)
110 int t;
111 if (k <= bias)
113 t = TMIN;
115 else if (k >= bias + TMAX)
117 t = TMAX;
119 else
121 t = k - bias;
123 if (q < t)
125 break;
127 output.Append((char) Digit2Codepoint(t + (q - t) % (BASE - t)));
128 q = (q - t) / (BASE - t);
131 output.Append((char) Digit2Codepoint(q));
132 bias = Adapt(delta, h + 1, h == b);
133 delta = 0;
134 h++;
138 delta++;
139 n++;
142 return output.ToString();
145 /// <summary>
146 /// Decode a punycoded string.
147 /// </summary>
148 /// <param name="input">Punycode string</param>
149 /// <returns> Unicode string.</returns>
150 public static string Decode(string input)
152 int n = INITIAL_N;
153 int i = 0;
154 int bias = INITIAL_BIAS;
155 StringBuilder output = new StringBuilder();
157 int d = input.LastIndexOf((System.Char) DELIMITER);
158 if (d > 0)
160 for (int j = 0; j < d; j++)
162 char c = input[j];
163 if (!IsBasic(c))
165 throw new PunycodeException(PunycodeException.BAD_INPUT);
167 output.Append(c);
169 d++;
171 else
173 d = 0;
176 while (d < input.Length)
178 int oldi = i;
179 int w = 1;
181 for (int k = BASE; ; k += BASE)
183 if (d == input.Length)
185 throw new PunycodeException(PunycodeException.BAD_INPUT);
187 int c = input[d++];
188 int digit = Codepoint2Digit(c);
189 if (digit > (System.Int32.MaxValue - i) / w)
191 throw new PunycodeException(PunycodeException.OVERFLOW);
194 i = i + digit * w;
196 int t;
197 if (k <= bias)
199 t = TMIN;
201 else if (k >= bias + TMAX)
203 t = TMAX;
205 else
207 t = k - bias;
209 if (digit < t)
211 break;
213 w = w * (BASE - t);
216 bias = Adapt(i - oldi, output.Length + 1, oldi == 0);
218 if (i / (output.Length + 1) > Int32.MaxValue - n)
220 throw new PunycodeException(PunycodeException.OVERFLOW);
223 n = n + i / (output.Length + 1);
224 i = i % (output.Length + 1);
225 // following overload is not supported on CF
226 //output.Insert(i,(char) n);
227 output.Insert(i, new char[1] { (char) n });
228 i++;
231 return output.ToString();
234 public static int Adapt(int delta, int numpoints, bool first)
236 if (first)
238 delta = delta / DAMP;
240 else
242 delta = delta / 2;
245 delta = delta + (delta / numpoints);
247 int k = 0;
248 while (delta > ((BASE - TMIN) * TMAX) / 2)
250 delta = delta / (BASE - TMIN);
251 k = k + BASE;
254 return k + ((BASE - TMIN + 1) * delta) / (delta + SKEW);
257 public static bool IsBasic(char c)
259 return c < 0x80;
262 public static int Digit2Codepoint(int d)
264 if (d < 26)
266 // 0..25 : 'a'..'z'
267 return d + 'a';
269 else if (d < 36)
271 // 26..35 : '0'..'9';
272 return d - 26 + '0';
274 else
276 throw new PunycodeException(PunycodeException.BAD_INPUT);
280 public static int Codepoint2Digit(int c)
282 if (c - '0' < 10)
284 // '0'..'9' : 26..35
285 return c - '0' + 26;
287 else if (c - 'a' < 26)
289 // 'a'..'z' : 0..25
290 return c - 'a';
292 else
294 throw new PunycodeException(PunycodeException.BAD_INPUT);