diff options
Diffstat (limited to 'csharp/Punycode.cs')
-rw-r--r-- | csharp/Punycode.cs | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/csharp/Punycode.cs b/csharp/Punycode.cs new file mode 100644 index 0000000..4a9463a --- /dev/null +++ b/csharp/Punycode.cs @@ -0,0 +1,299 @@ +/// <summary> +/// Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. +/// * +/// Author: Alexander Gnauck AG-Software, mailto:gnauck@ag-software.de +/// * +/// This file is part of GNU Libidn. +/// * +/// This library is free software; you can redistribute it and/or +/// modify it under the terms of the GNU Lesser General Public License +/// as published by the Free Software Foundation; either version 2.1 of +/// the License, or (at your option) any later version. +/// * +/// This library is distributed in the hope that it will be useful, but +/// WITHOUT ANY WARRANTY; without even the implied warranty of +/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +/// Lesser General Public License for more details. +/// * +/// You should have received a copy of the GNU Lesser General Public +/// License along with this library; if not, write to the Free Software +/// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 +/// USA +/// </summary> + +using System; +using System.Text; + +namespace Gnu.Inet.Encoding +{ + + public class Punycode + { + /* Punycode parameters */ + internal const int TMIN = 1; + internal const int TMAX = 26; + internal const int BASE = 36; + internal const int INITIAL_N = 128; + internal const int INITIAL_BIAS = 72; + internal const int DAMP = 700; + internal const int SKEW = 38; + internal const char DELIMITER = '-'; + + /// <summary> + /// Punycodes a unicode string. + /// </summary> + /// <param name="input">Unicode string.</param> + /// <returns> Punycoded string.</returns> + public static string Encode(string input) + { + int n = INITIAL_N; + int delta = 0; + int bias = INITIAL_BIAS; + StringBuilder output = new StringBuilder(); + + // Copy all basic code points to the output + int b = 0; + for (int i = 0; i < input.Length; i++) + { + char c = input[i]; + if (IsBasic(c)) + { + output.Append(c); + b++; + } + } + + // Append delimiter + if (b > 0) + { + output.Append(DELIMITER); + } + + int h = b; + while (h < input.Length) + { + int m = System.Int32.MaxValue; + + // Find the minimum code point >= n + for (int i = 0; i < input.Length; i++) + { + int c = input[i]; + if (c >= n && c < m) + { + m = c; + } + } + + if (m - n > (System.Int32.MaxValue - delta) / (h + 1)) + { + throw new PunycodeException(PunycodeException.OVERFLOW); + } + delta = delta + (m - n) * (h + 1); + n = m; + + for (int j = 0; j < input.Length; j++) + { + int c = input[j]; + if (c < n) + { + delta++; + if (0 == delta) + { + throw new PunycodeException(PunycodeException.OVERFLOW); + } + } + if (c == n) + { + int q = delta; + + for (int k = BASE; ; k += BASE) + { + int t; + if (k <= bias) + { + t = TMIN; + } + else if (k >= bias + TMAX) + { + t = TMAX; + } + else + { + t = k - bias; + } + if (q < t) + { + break; + } + output.Append((char) Digit2Codepoint(t + (q - t) % (BASE - t))); + q = (q - t) / (BASE - t); + } + + output.Append((char) Digit2Codepoint(q)); + bias = Adapt(delta, h + 1, h == b); + delta = 0; + h++; + } + } + + delta++; + n++; + } + + return output.ToString(); + } + + /// <summary> + /// Decode a punycoded string. + /// </summary> + /// <param name="input">Punycode string</param> + /// <returns> Unicode string.</returns> + public static string Decode(string input) + { + int n = INITIAL_N; + int i = 0; + int bias = INITIAL_BIAS; + StringBuilder output = new StringBuilder(); + + int d = input.LastIndexOf((System.Char) DELIMITER); + if (d > 0) + { + for (int j = 0; j < d; j++) + { + char c = input[j]; + if (!IsBasic(c)) + { + throw new PunycodeException(PunycodeException.BAD_INPUT); + } + output.Append(c); + } + d++; + } + else + { + d = 0; + } + + while (d < input.Length) + { + int oldi = i; + int w = 1; + + for (int k = BASE; ; k += BASE) + { + if (d == input.Length) + { + throw new PunycodeException(PunycodeException.BAD_INPUT); + } + int c = input[d++]; + int digit = Codepoint2Digit(c); + if (digit > (System.Int32.MaxValue - i) / w) + { + throw new PunycodeException(PunycodeException.OVERFLOW); + } + + i = i + digit * w; + + int t; + if (k <= bias) + { + t = TMIN; + } + else if (k >= bias + TMAX) + { + t = TMAX; + } + else + { + t = k - bias; + } + if (digit < t) + { + break; + } + w = w * (BASE - t); + } + + bias = Adapt(i - oldi, output.Length + 1, oldi == 0); + + if (i / (output.Length + 1) > Int32.MaxValue - n) + { + throw new PunycodeException(PunycodeException.OVERFLOW); + } + + n = n + i / (output.Length + 1); + i = i % (output.Length + 1); + // following overload is not supported on CF + //output.Insert(i,(char) n); + output.Insert(i, new char[1] { (char) n }); + i++; + } + + return output.ToString(); + } + + public static int Adapt(int delta, int numpoints, bool first) + { + if (first) + { + delta = delta / DAMP; + } + else + { + delta = delta / 2; + } + + delta = delta + (delta / numpoints); + + int k = 0; + while (delta > ((BASE - TMIN) * TMAX) / 2) + { + delta = delta / (BASE - TMIN); + k = k + BASE; + } + + return k + ((BASE - TMIN + 1) * delta) / (delta + SKEW); + } + + public static bool IsBasic(char c) + { + return c < 0x80; + } + + public static int Digit2Codepoint(int d) + { + if (d < 26) + { + // 0..25 : 'a'..'z' + return d + 'a'; + } + else if (d < 36) + { + // 26..35 : '0'..'9'; + return d - 26 + '0'; + } + else + { + throw new PunycodeException(PunycodeException.BAD_INPUT); + } + } + + public static int Codepoint2Digit(int c) + { + if (c - '0' < 10) + { + // '0'..'9' : 26..35 + return c - '0' + 26; + } + else if (c - 'a' < 26) + { + // 'a'..'z' : 0..25 + return c - 'a'; + } + else + { + throw new PunycodeException(PunycodeException.BAD_INPUT); + } + } + } +}
\ No newline at end of file |