///
/// 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
///
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 = '-';
///
/// Punycodes a unicode string.
///
/// Unicode string.
/// Punycoded string.
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();
}
///
/// Decode a punycoded string.
///
/// Punycode string
/// Unicode string.
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);
}
}
}
}