Professional Applications Programmers/Consultants برمجة واستشارات تطبيقات الإنترنت
Skip Navigation LinksHome » Code Library » Punycode

Public general use code classes and xml files that we've compiled and used over the years:

Punycode support class.

   1:  using System;
   2:  using System.Web;
   3:  using System.Xml;
   4:  using System.IO;
   5:  using System.Text;
   6:   
   7:  namespace Ia.Cl.Model
   8:  {
   9:      ////////////////////////////////////////////////////////////////////////////
  10:   
  11:      /// <summary publish="true">
  12:      /// Punycode support class.
  13:      /// </summary>
  14:      /// <remarks> 
  15:      /// Copyright © 2001-2015 Jasem Y. Al-Shamlan (info@ia.com.kw), Internet Applications - Kuwait. All Rights Reserved.
  16:      ///
  17:      /// This library is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by
  18:      /// the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
  19:      ///
  20:      /// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  21:      /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  22:      /// 
  23:      /// You should have received a copy of the GNU General Public License along with this library. If not, see http://www.gnu.org/licenses.
  24:      /// 
  25:      /// Copyright notice: This notice may not be removed or altered from any source distribution.
  26:      /// </remarks> 
  27:      public class Punycode
  28:      {
  29:          ////////////////////////////////////////////////////////////////////////////
  30:   
  31:          /*
  32:           * $Id: punycode.cs,v 1.3 2003/03/30 23:28:41 Mayuki Sawatari Exp $
  33:           *
  34:           * Punycode (RFC 3492) encoder/decoder implementation in C# with .NET Framework
  35:           *
  36:           * RFC 3492: IDNA Punycode
  37:           * http://www.ietf.org/rfc/rfc3492.txt
  38:           *
  39:           * Copyright (C) 2003 Mayuki Sawatari <mayuki@misuzilla.org>, All rights reserved.
  40:           *
  41:           * Redistribution and use in source and binary forms, with or without
  42:           * modification, are permitted provided that the following conditions
  43:           * are met:
  44:           * 1. Redistributions of source code must retain the above copyright
  45:           *    notice, this list of conditions and the following disclaimer.
  46:           * 2. Redistributions in binary form must reproduce the above copyright
  47:           *    notice, this list of conditions and the following disclaimer in the
  48:           *    documentation and/or other materials provided with the distribution.
  49:           *
  50:           * THIS LIBRARY IS PROVIDED BY THE MISUZILLA.ORG ``AS IS'' AND
  51:           * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  52:           * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  53:           * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  54:           * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  55:           * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  56:           * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  57:           * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  58:           * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  59:           * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  60:           * SUCH DAMAGE.
  61:           */
  62:   
  63:          /// <summary/>
  64:          public const string Prefix = "xn--";
  65:   
  66:          /// <summary/>
  67:          public static string Decode(string s)
  68:          {
  69:              if (s.StartsWith(Punycode.Prefix))
  70:                  return DecodeString(s.Substring(Punycode.Prefix.Length));
  71:              else
  72:                  return DecodeString(s);
  73:          }
  74:   
  75:          /// <summary/>
  76:          public static string Encode(string s)
  77:          {
  78:              return EncodeString(s).Insert(0, Punycode.Prefix).ToString();
  79:          }
  80:   
  81:          /// <summary/>
  82:          public static string EncodeWithoutPrefix(string s)
  83:          {
  84:              return EncodeString(s).ToString();
  85:          }
  86:   
  87:          /// <summary/>
  88:          private sealed class PunyParams
  89:          {
  90:              public const Int32 Base = 36;
  91:              public const Int32 Tmin = 1;
  92:              public const Int32 Tmax = 26;
  93:              public const Int32 Skew = 38;
  94:              public const Int32 Damp = 700;
  95:              public const Int32 InitialBias = 72;
  96:              public const Int32 InitialN = 0x80;
  97:              public const Int32 Delimiter = 0x2d;
  98:          }
  99:   
 100:          /// <summary/>
 101:          private static char DecodeDigit(Int32 cp)
 102:          {
 103:              return (char)(cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 :
 104:                  cp - 97 < 26 ? cp - 97 : PunyParams.Base);
 105:          }
 106:   
 107:          /// <summary/>
 108:          private static char EncodeDigit(Int32 d, Int32 flag)
 109:          {
 110:              return (char)(d + 22 + 75 * (d < 26 ? 1 : 0) - (flag << 5));
 111:          }
 112:   
 113:          /// <summary/>
 114:          private static StringBuilder EncodeString(string input)
 115:          {
 116:              StringBuilder output = new StringBuilder();
 117:              Int32 delta, bias, maxOut;
 118:              Int32 m, k, t, b, n, h, q, i;
 119:   
 120:   
 121:              maxOut = Int32.MaxValue;
 122:              n = PunyParams.InitialN;
 123:              bias = PunyParams.InitialBias;
 124:              delta = 0;
 125:   
 126:              for (i = 0; i < input.Length; i++)
 127:              {
 128:                  if (input[i] < 0x80)
 129:                  {
 130:                      if (maxOut - input.Length < 2) throw new PunycodeBigOutputException("punycode_big_output");
 131:                      output.Append(input[i]);
 132:                  }
 133:              }
 134:   
 135:              h = b = (Int32)output.Length;
 136:   
 137:              if (output.Length > 0)
 138:                  output.Append((char)PunyParams.Delimiter);
 139:   
 140:              /* Main encoding loop: */
 141:              while (h < input.Length)
 142:              {
 143:                  m = Int32.MaxValue;
 144:                  for (i = 0; i < input.Length; i++)
 145:                  {
 146:                      if (input[i] >= n && input[i] < m)
 147:                          m = input[i];
 148:                  }
 149:   
 150:                  if (m - n > (Int32.MaxValue - delta) / (h + 1))
 151:                      throw new PunycodeOverflowException("punycode_overflow");
 152:   
 153:                  delta += (char)(m - n) * (h + 1);
 154:                  n = m;
 155:   
 156:                  for (i = 0; i < input.Length; i++)
 157:                  {
 158:                      /* Punycode does not need to check whether input[j] is basic: */
 159:                      if (input[i] < n)
 160:                      {
 161:                          if (++delta == 0)
 162:                              throw new PunycodeOverflowException("punycode_overflow");
 163:                      }
 164:   
 165:                      if (input[i] == n)
 166:                      {
 167:                          /* Represent delta as a generalized variable-length integer: */
 168:                          for (q = delta, k = PunyParams.Base; ; k += PunyParams.Base)
 169:                          {
 170:                              t = (k <= bias) ?
 171:                                  PunyParams.Tmin :
 172:                                  (k >= bias + PunyParams.Tmax) ?
 173:                                      PunyParams.Tmax : k - bias;
 174:   
 175:                              if (q < t)
 176:                                  break;
 177:   
 178:                              output.Append(EncodeDigit(t + (q - t) % (PunyParams.Base - t), 0));
 179:                              q = (q - t) / (PunyParams.Base - t);
 180:                          }
 181:                          //output[outlen++] = EncodeDigit(q, case_flags && case_flags[j]);
 182:                          output.Append(EncodeDigit(q, 0)); // ignore case
 183:                          bias = (char)Adapt(delta, h + 1, h == b);
 184:                          delta = 0;
 185:                          ++h;
 186:                      }
 187:                  }
 188:                  ++delta; ++n;
 189:              }
 190:   
 191:              return output;
 192:          }
 193:   
 194:          private static Int32 Adapt(Int32 delta, Int32 numpoints, Boolean firsttime)
 195:          {
 196:              Int32 k;
 197:              delta = firsttime ? delta / PunyParams.Damp : delta >> 1; /* delta >> 1 --> delta / 2 */
 198:              delta += delta / numpoints;
 199:   
 200:              for (k = 0; delta > ((PunyParams.Base - PunyParams.Tmin) * PunyParams.Tmax) / 2; k += PunyParams.Base)
 201:              {
 202:                  delta /= (PunyParams.Base - PunyParams.Tmin);
 203:              }
 204:   
 205:              return k + ((PunyParams.Base - PunyParams.Tmin) + 1) * delta / (delta + PunyParams.Skew);
 206:          }
 207:   
 208:          private static string DecodeString(string input)
 209:          {
 210:              StringBuilder output = new StringBuilder();
 211:              Int32 n, outlen, i, bias, b, j, inl, oldi, w, k, digit, t;
 212:   
 213:              /* Initialize the state: */
 214:   
 215:              n = PunyParams.InitialN;
 216:              outlen = i = 0;
 217:              bias = PunyParams.InitialBias;
 218:   
 219:              /* Handle the basic code points:  Let b be the number of input code */
 220:              /* points before the last delimiter, or 0 if there is none, then    */
 221:              /* copy the first b code points to the output.                      */
 222:   
 223:              for (b = j = 0; j < input.Length; ++j)
 224:                  if (input[j] == PunyParams.Delimiter)
 225:                      b = j;
 226:   
 227:              if (b > Int32.MaxValue)
 228:                  throw new PunycodeBigOutputException("punycode_big_output");
 229:   
 230:              for (j = 0; j < b; ++j)
 231:              {
 232:                  //if (case_flags)
 233:                  //    case_flags[outlen] = flagged(input[j]);
 234:                  if (!(input[j] < 0x80))
 235:                      throw new PunycodeBadInputException("punycode_bad_input");
 236:                  outlen++;
 237:                  output.Append(input[j]);
 238:              }
 239:   
 240:              /* Main decoding loop:  Start just after the last delimiter if any  */
 241:              /* basic code points were copied; start at the beginning otherwise. */
 242:              for (inl = b > 0 ? b + 1 : 0; inl < input.Length; ++outlen)
 243:              {
 244:   
 245:                  for (oldi = i, w = 1, k = PunyParams.Base; ; k += PunyParams.Base)
 246:                  {
 247:                      if (inl >= input.Length)
 248:                          throw new PunycodeBadInputException(string.Format("{0} >= {1}", inl, input.Length));
 249:   
 250:                      digit = DecodeDigit(input[inl++]);
 251:   
 252:                      if (digit >= PunyParams.Base)
 253:                          throw new PunycodeBadInputException(string.Format("{0} >= {1}", digit, PunyParams.Base));
 254:                      if (digit > (Int32.MaxValue - i) / w)
 255:                          throw new PunycodeOverflowException(string.Format("{0} > ({1} - {2}) / {3}", digit, Int32.MaxValue, i, w));
 256:   
 257:                      i += digit * w;
 258:                      t = (k <= bias ? PunyParams.Tmin : (k >= bias + PunyParams.Tmax ? PunyParams.Tmax : k - bias));
 259:                      if (digit < t) break;
 260:   
 261:                      if (w > Int32.MaxValue / (PunyParams.Base - t))
 262:                          throw new PunycodeOverflowException("punycode_overflow");
 263:   
 264:                      w *= (PunyParams.Base - t);
 265:                  }
 266:   
 267:                  bias = Adapt(i - oldi, outlen + 1, oldi == 0);
 268:   
 269:                  /* i was supposed to wrap around from out+1 to 0,   */
 270:                  /* incrementing n each time, so we'll fix that now: */
 271:   
 272:                  if (i / (outlen + 1) > Int32.MaxValue - n)
 273:                      throw new PunycodeOverflowException("punycode_overflow");
 274:   
 275:                  n += i / (outlen + 1);
 276:                  i %= (outlen + 1);
 277:   
 278:                  /* Insert n at position i of the output: */
 279:                  if (outlen >= Int32.MaxValue)
 280:                      throw new PunycodeBigOutputException("punycode_big_output");
 281:                  //if (case_flags) {
 282:                  //memmove(case_flags + i + 1, case_flags + i, out - i);
 283:   
 284:                  /* Case of last character determines uppercase flag: */
 285:                  //case_flags[i] = flagged(input[inl - 1]);
 286:                  //}
 287:                  output.Insert(i, (char)n);
 288:                  i++;
 289:              }
 290:              return output.ToString();
 291:          }
 292:      }
 293:   
 294:      /// <summary/>
 295:      public class PunycodeBigOutputException : ApplicationException
 296:      {
 297:          /// <summary/>
 298:          public PunycodeBigOutputException(string s) : base(s) { }
 299:      }
 300:   
 301:      /// <summary/>
 302:      public class PunycodeBadInputException : ApplicationException
 303:      {
 304:          /// <summary/>
 305:          public PunycodeBadInputException(string s) : base(s) { }
 306:      }
 307:   
 308:      /// <summary/>
 309:      public class PunycodeOverflowException : ApplicationException
 310:      {
 311:          /// <summary/>
 312:          public PunycodeOverflowException(string s) : base(s) { }
 313:      }
 314:   
 315:      ////////////////////////////////////////////////////////////////////////////
 316:      ////////////////////////////////////////////////////////////////////////////
 317:  }
 318: