1 7 package com.ibm.icu.text; 8 9 import com.ibm.icu.lang.UCharacter; 10 11 15 16 17 final class Punycode { 18 19 20 private static final int BASE = 36; 21 private static final int TMIN = 1; 22 private static final int TMAX = 26; 23 private static final int SKEW = 38; 24 private static final int DAMP = 700; 25 private static final int INITIAL_BIAS = 72; 26 private static final int INITIAL_N = 0x80; 27 28 29 private static final int HYPHEN = 0x2d; 30 private static final int DELIMITER = HYPHEN; 31 32 private static final int ZERO = 0x30; 33 private static final int NINE = 0x39; 34 35 private static final int SMALL_A = 0x61; 36 private static final int SMALL_Z = 0x7a; 37 38 private static final int CAPITAL_A = 0x41; 39 private static final int CAPITAL_Z = 0x5a; 40 private static final int MAX_CP_COUNT = 200; 41 private static final int UINT_MAGIC = 0x80000000; 42 private static final long ULONG_MAGIC = 0x8000000000000000L; 43 44 private static int adaptBias(int delta, int length, boolean firstTime){ 45 if(firstTime){ 46 delta /=DAMP; 47 }else{ 48 delta /= 2; 49 } 50 delta += delta/length; 51 52 int count=0; 53 for(; delta>((BASE-TMIN)*TMAX)/2; count+=BASE) { 54 delta/=(BASE-TMIN); 55 } 56 57 return count+(((BASE-TMIN+1)*delta)/(delta+SKEW)); 58 } 59 60 65 static final int[] basicToDigit= new int[]{ 66 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 67 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 68 69 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 70 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, 71 72 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 73 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 74 75 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 76 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, 77 78 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 79 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 80 81 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 82 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 83 84 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 85 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 86 87 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 88 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 89 }; 90 91 private static char asciiCaseMap(char b, boolean uppercase) { 93 if(uppercase) { 94 if(SMALL_A<=b && b<=SMALL_Z) { 95 b-=(SMALL_A-CAPITAL_A); 96 } 97 } else { 98 if(CAPITAL_A<=b && b<=CAPITAL_Z) { 99 b+=(SMALL_A-CAPITAL_A); 100 } 101 } 102 return b; 103 } 104 111 private static char digitToBasic(int digit, boolean uppercase) { 112 113 114 if(digit<26) { 115 if(uppercase) { 116 return (char)(CAPITAL_A+digit); 117 } else { 118 return (char)(SMALL_A+digit); 119 } 120 } else { 121 return (char)((ZERO-26)+digit); 122 } 123 } 124 134 public static StringBuffer encode(StringBuffer src, boolean[] caseFlags) throws StringPrepParseException{ 135 136 int[] cpBuffer = new int[MAX_CP_COUNT]; 137 int n, delta, handledCPCount, basicLength, destLength, bias, j, m, q, k, t, srcCPCount; 138 char c, c2; 139 int srcLength = src.length(); 140 int destCapacity = MAX_CP_COUNT; 141 char[] dest = new char[destCapacity]; 142 StringBuffer result = new StringBuffer (); 143 147 srcCPCount=destLength=0; 148 149 for(j=0; j<srcLength; ++j) { 150 if(srcCPCount==MAX_CP_COUNT) { 151 152 throw new IndexOutOfBoundsException (); 153 } 154 c=src.charAt(j); 155 if(isBasic(c)) { 156 if(destLength<destCapacity) { 157 cpBuffer[srcCPCount++]=0; 158 dest[destLength]= 159 caseFlags!=null ? 160 asciiCaseMap((char)c, caseFlags[j]) : 161 (char)c; 162 } 163 ++destLength; 164 } else { 165 n=((caseFlags!=null && caseFlags[j])? 1 : 0)<<31L; 166 if(!UTF16.isSurrogate(c)) { 167 n|=c; 168 } else if(UTF16.isLeadSurrogate(c) && (j+1)<srcLength && UTF16.isTrailSurrogate(c2=src.charAt(j+1))) { 169 ++j; 170 171 n|=UCharacter.getCodePoint(c, c2); 172 } else { 173 174 throw new StringPrepParseException("Illegal char found",StringPrepParseException.ILLEGAL_CHAR_FOUND); 175 } 176 cpBuffer[srcCPCount++]=n; 177 } 178 } 179 180 181 basicLength=destLength; 182 if(basicLength>0) { 183 if(destLength<destCapacity) { 184 dest[destLength]=DELIMITER; 185 } 186 ++destLength; 187 } 188 189 194 195 196 n=INITIAL_N; 197 delta=0; 198 bias=INITIAL_BIAS; 199 200 201 for(handledCPCount=basicLength; handledCPCount<srcCPCount; ) { 202 206 for(m=0x7fffffff, j=0; j<srcCPCount; ++j) { 207 q=cpBuffer[j]&0x7fffffff; 208 if(n<=q && q<m) { 209 m=q; 210 } 211 } 212 213 217 if(m-n>(0x7fffffff-MAX_CP_COUNT-delta)/(handledCPCount+1)) { 218 throw new IllegalStateException ("Internal program error"); 219 } 220 delta+=(m-n)*(handledCPCount+1); 221 n=m; 222 223 224 for(j=0; j<srcCPCount; ++j) { 225 q=cpBuffer[j]&0x7fffffff; 226 if(q<n) { 227 ++delta; 228 } else if(q==n) { 229 230 for(q=delta, k=BASE; ; k+=BASE) { 231 232 241 242 t=k-bias; 243 if(t<TMIN) { 244 t=TMIN; 245 } else if(k>=(bias+TMAX)) { 246 t=TMAX; 247 } 248 249 if(q<t) { 250 break; 251 } 252 253 if(destLength<destCapacity) { 254 dest[destLength++]=digitToBasic(t+(q-t)%(BASE-t), false); 255 } 256 q=(q-t)/(BASE-t); 257 } 258 259 if(destLength<destCapacity) { 260 dest[destLength++]=digitToBasic(q, (cpBuffer[j]<0)); 261 } 262 bias=adaptBias(delta, handledCPCount+1,(handledCPCount==basicLength)); 263 delta=0; 264 ++handledCPCount; 265 } 266 } 267 268 ++delta; 269 ++n; 270 } 271 272 return result.append(dest, 0, destLength); 273 } 274 275 private static boolean isBasic(int ch){ 276 return (ch < INITIAL_N); 277 } 278 private static boolean isBasicUpperCase(int ch){ 280 return( CAPITAL_A<=ch && ch >= CAPITAL_Z); 281 } 282 private static boolean isSurrogate(int ch){ 284 return (((ch)&0xfffff800)==0xd800); 285 } 286 295 public static StringBuffer decode(StringBuffer src, boolean[] caseFlags) 296 throws StringPrepParseException{ 297 int srcLength = src.length(); 298 StringBuffer result = new StringBuffer (); 299 int n, destLength, i, bias, basicLength, j, in, oldi, w, k, digit, t, 300 destCPCount, firstSupplementaryIndex, cpLength; 301 char b; 302 int destCapacity = MAX_CP_COUNT; 303 char[] dest = new char[destCapacity]; 304 305 313 for(j=srcLength; j>0;) { 314 if(src.charAt(--j)==DELIMITER) { 315 break; 316 } 317 } 318 destLength=basicLength=destCPCount=j; 319 320 while(j>0) { 321 b=src.charAt(--j); 322 if(!isBasic(b)) { 323 throw new StringPrepParseException("Illegal char found", StringPrepParseException.INVALID_CHAR_FOUND); 324 } 325 326 if(j<destCapacity) { 327 dest[j]= b; 328 329 if(caseFlags!=null) { 330 caseFlags[j]=isBasicUpperCase(b); 331 } 332 } 333 } 334 335 336 n=INITIAL_N; 337 i=0; 338 bias=INITIAL_BIAS; 339 firstSupplementaryIndex=1000000000; 340 341 346 for(in=basicLength>0 ? basicLength+1 : 0; in<srcLength; ) { 347 356 for(oldi=i, w=1, k=BASE; ; k+=BASE) { 357 if(in>=srcLength) { 358 throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND); 359 } 360 361 digit=basicToDigit[src.charAt(in++) & 0xFF]; 362 if(digit<0) { 363 throw new StringPrepParseException("Invalid char found", StringPrepParseException.INVALID_CHAR_FOUND); 364 } 365 if(digit>(0x7fffffff-i)/w) { 366 367 throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND); 368 } 369 370 i+=digit*w; 371 t=k-bias; 372 if(t<TMIN) { 373 t=TMIN; 374 } else if(k>=(bias+TMAX)) { 375 t=TMAX; 376 } 377 if(digit<t) { 378 break; 379 } 380 381 if(w>0x7fffffff/(BASE-t)) { 382 383 throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND); 384 } 385 w*=BASE-t; 386 } 387 388 393 ++destCPCount; 394 bias=adaptBias(i-oldi, destCPCount, (oldi==0)); 395 396 400 if(i/destCPCount>(0x7fffffff-n)) { 401 402 throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND); 403 } 404 405 n+=i/destCPCount; 406 i%=destCPCount; 407 408 409 410 if(n>0x10ffff || isSurrogate(n)) { 411 412 throw new StringPrepParseException("Illegal char found", StringPrepParseException.ILLEGAL_CHAR_FOUND); 413 } 414 415 416 cpLength=UTF16.getCharCount(n); 417 if((destLength+cpLength)<destCapacity) { 418 int codeUnitIndex; 419 420 430 if(i<=firstSupplementaryIndex) { 431 codeUnitIndex=i; 432 if(cpLength>1) { 433 firstSupplementaryIndex=codeUnitIndex; 434 } else { 435 ++firstSupplementaryIndex; 436 } 437 } else { 438 codeUnitIndex=firstSupplementaryIndex; 439 codeUnitIndex=UTF16.moveCodePointOffset(dest, 0, destLength, codeUnitIndex, i-codeUnitIndex); 440 } 441 442 443 if(codeUnitIndex<destLength) { 444 System.arraycopy(dest, codeUnitIndex, 445 dest, codeUnitIndex+cpLength, 446 (destLength-codeUnitIndex)); 447 if(caseFlags!=null) { 448 System.arraycopy(caseFlags, codeUnitIndex, 449 caseFlags, codeUnitIndex+cpLength, 450 destLength-codeUnitIndex); 451 } 452 } 453 if(cpLength==1) { 454 455 dest[codeUnitIndex]=(char)n; 456 } else { 457 458 dest[codeUnitIndex]=UTF16.getLeadSurrogate(n); 459 dest[codeUnitIndex+1]=UTF16.getTrailSurrogate(n); 460 } 461 if(caseFlags!=null) { 462 463 caseFlags[codeUnitIndex]=isBasicUpperCase(src.charAt(in-1)); 464 if(cpLength==2) { 465 caseFlags[codeUnitIndex+1]=false; 466 } 467 } 468 } 469 destLength+=cpLength; 470 ++i; 471 } 472 result.append(dest, 0, destLength); 473 return result; 474 } 475 } 476 477 | Popular Tags |