001: /*
002: *******************************************************************************
003: * Copyright (C) 2003-2004, International Business Machines Corporation and *
004: * others. All Rights Reserved. *
005: *******************************************************************************
006: */
007:
008: /*
009: *
010: Disclaimer and license
011:
012: Regarding this entire document or any portion of it (including
013: the pseudocode and C code), the author makes no guarantees and
014: is not responsible for any damage resulting from its use. The
015: author grants irrevocable permission to anyone to use, modify,
016: and distribute it in any way that does not diminish the rights
017: of anyone else to use, modify, and distribute it, provided that
018: redistributed derivative works do not contain misleading author or
019: version information. Derivative works need not be licensed under
020: similar terms.
021:
022: punycode.c 0.4.0 (2001-Nov-17-Sat)
023: http://www.cs.berkeley.edu/~amc/idn/
024: Adam M. Costello
025: http://www.nicemice.net/amc/
026: */
027:
028: package com.ibm.icu.dev.test.stringprep;
029:
030: import com.ibm.icu.text.StringPrepParseException;
031: import com.ibm.icu.text.UCharacterIterator;
032: import com.ibm.icu.text.UTF16;
033:
034: /**
035: * The implementation is direct port of C code in the RFC
036: */
037:
038: public final class PunycodeReference {
039: /*** punycode status codes */
040: public static final int punycode_success = 0;
041: public static final int punycode_bad_input = 1; /* Input is invalid. */
042: public static final int punycode_big_output = 2; /* Output would exceed the space provided. */
043: public static final int punycode_overflow = 3; /* Input needs wider integers to process. */
044:
045: /*** Bootstring parameters for Punycode ***/
046: private static final int base = 36;
047: private static final int tmin = 1;
048: private static final int tmax = 26;
049: private static final int skew = 38;
050: private static final int damp = 700;
051: private static final int initial_bias = 72;
052: private static final int initial_n = 0x80;
053: private static final int delimiter = 0x2D;
054:
055: private static final long UNSIGNED_INT_MASK = 0xffffffffL;
056:
057: /* basic(cp) tests whether cp is a basic code point: */
058: private static boolean basic(int cp) {
059: return (char) (cp) < 0x80;
060: }
061:
062: /* delim(cp) tests whether cp is a delimiter: */
063: private static boolean delim(int cp) {
064: return ((cp) == delimiter);
065: }
066:
067: /* decode_digit(cp) returns the numeric value of a basic code */
068: /* point (for use in representing integers) in the range 0 to */
069: /* base-1, or base if cp is does not represent a value. */
070:
071: private static int decode_digit(int cp) {
072: return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65
073: : cp - 97 < 26 ? cp - 97 : base;
074: }
075:
076: /* encode_digit(d,flag) returns the basic code point whose value */
077: /* (when used for representing integers) is d, which needs to be in */
078: /* the range 0 to base-1. The lowercase form is used unless flag is */
079: /* nonzero, in which case the uppercase form is used. The behavior */
080: /* is undefined if flag is nonzero and digit d has no uppercase form. */
081:
082: private static char encode_digit(int d, int flag) {
083: return (char) (d + 22 + (75 * ((d < 26) ? 1 : 0) - (((flag != 0) ? 1
084: : 0) << 5)));
085: /* 0..25 map to ASCII a..z or A..Z */
086: /* 26..35 map to ASCII 0..9 */
087: }
088:
089: /* flagged(bcp) tests whether a basic code point is flagged */
090: /* (uppercase). The behavior is undefined if bcp is not a */
091: /* basic code point. */
092:
093: private static boolean flagged(int bcp) {
094: return ((bcp) - 65 < 26);
095: }
096:
097: /* encode_basic(bcp,flag) forces a basic code point to lowercase */
098: /* if flag is zero, uppercase if flag is nonzero, and returns */
099: /* the resulting code point. The code point is unchanged if it */
100: /* is caseless. The behavior is undefined if bcp is not a basic */
101: /* code point. */
102:
103: private static char encode_basic(int bcp, int flag) {
104: bcp -= (((bcp - 97) < 26) ? 1 : 0) << 5;
105: boolean mybcp = (bcp - 65 < 26);
106: return (char) (bcp + (((flag == 0) && mybcp) ? 1 : 0) << 5);
107: }
108:
109: /*** Platform-specific constants ***/
110:
111: /* maxint is the maximum value of a punycode_uint variable: */
112: private static long maxint = 0xFFFFFFFFL;
113:
114: /* Because maxint is unsigned, -1 becomes the maximum value. */
115:
116: /*** Bias adaptation function ***/
117:
118: private static int adapt(int delta, int numpoints, boolean firsttime) {
119: int k;
120:
121: delta = (firsttime == true) ? delta / damp : delta >> 1;
122: /* delta >> 1 is a faster way of doing delta / 2 */
123: delta += delta / numpoints;
124:
125: for (k = 0; delta > ((base - tmin) * tmax) / 2; k += base) {
126: delta /= base - tmin;
127: }
128:
129: return k + (base - tmin + 1) * delta / (delta + skew);
130: }
131:
132: /*** Main encode function ***/
133:
134: public static final int encode(int input_length, int input[],
135: char[] case_flags, int[] output_length, char output[]) {
136: int delta, h, b, out, max_out, bias, j, q, k, t;
137: long m, n;
138: /* Initialize the state: */
139:
140: n = initial_n;
141: delta = out = 0;
142: max_out = output_length[0];
143: bias = initial_bias;
144:
145: /* Handle the basic code points: */
146:
147: for (j = 0; j < input_length; ++j) {
148: if (basic(input[j])) {
149: if (max_out - out < 2)
150: return punycode_big_output;
151: output[out++] = (char) (case_flags != null ? encode_basic(
152: input[j], case_flags[j])
153: : input[j]);
154: }
155: /* else if (input[j] < n) return punycode_bad_input; */
156: /* (not needed for Punycode with unsigned code points) */
157: }
158:
159: h = b = out;
160:
161: /* h is the number of code points that have been handled, b is the */
162: /* number of basic code points, and out is the number of characters */
163: /* that have been output. */
164:
165: if (b > 0)
166: output[out++] = delimiter;
167:
168: /* Main encoding loop: */
169:
170: while (h < input_length) {
171: /* All non-basic code points < n have been */
172: /* handled already. Find the next larger one: */
173:
174: for (m = maxint, j = 0; j < input_length; ++j) {
175: /* if (basic(input[j])) continue; */
176: /* (not needed for Punycode) */
177: if (input[j] >= n && input[j] < m)
178: m = input[j];
179: }
180:
181: /* Increase delta enough to advance the decoder's */
182: /* <n,i> state to <m,0>, but guard against overflow: */
183:
184: if (m - n > (maxint - delta) / (h + 1))
185: return punycode_overflow;
186: delta += (m - n) * (h + 1);
187: n = m;
188:
189: for (j = 0; j < input_length; ++j) {
190: /* Punycode does not need to check whether input[j] is basic: */
191: if (input[j] < n /* || basic(input[j]) */) {
192: if (++delta == 0)
193: return punycode_overflow;
194: }
195:
196: if (input[j] == n) {
197: /* Represent delta as a generalized variable-length integer: */
198:
199: for (q = delta, k = base;; k += base) {
200: if (out >= max_out)
201: return punycode_big_output;
202: t = k <= bias /* + tmin */? tmin : /* +tmin not needed */
203: k >= bias + tmax ? tmax : k - bias;
204: if (q < t)
205: break;
206: output[out++] = encode_digit(t + (q - t)
207: % (base - t), 0);
208: q = (q - t) / (base - t);
209: }
210:
211: output[out++] = encode_digit(q,
212: (case_flags != null) ? case_flags[j] : 0);
213: bias = adapt(delta, h + 1, (h == b));
214: delta = 0;
215: ++h;
216: }
217: }
218:
219: ++delta;
220: ++n;
221: }
222:
223: output_length[0] = out;
224: return punycode_success;
225: }
226:
227: public static final StringBuffer encode(StringBuffer input,
228: char[] case_flags) throws StringPrepParseException {
229: int[] in = new int[input.length()];
230: int inLen = 0;
231: int ch;
232: StringBuffer result = new StringBuffer();
233: UCharacterIterator iter = UCharacterIterator.getInstance(input);
234: while ((ch = iter.nextCodePoint()) != UCharacterIterator.DONE) {
235: in[inLen++] = ch;
236: }
237:
238: int[] outLen = new int[1];
239: outLen[0] = input.length() * 4;
240: char[] output = new char[outLen[0]];
241: int rc = punycode_success;
242: for (;;) {
243: rc = encode(inLen, in, case_flags, outLen, output);
244: if (rc == punycode_big_output) {
245: outLen[0] = outLen[0] * 4;
246: output = new char[outLen[0]];
247: // continue to convert
248: continue;
249: }
250: break;
251: }
252: if (rc == punycode_success) {
253: return result.append(output, 0, outLen[0]);
254: }
255: getException(rc);
256: return result;
257: }
258:
259: private static void getException(int rc)
260: throws StringPrepParseException {
261: switch (rc) {
262: case punycode_big_output:
263: throw new StringPrepParseException(
264: "The output capacity was not sufficient.",
265: StringPrepParseException.BUFFER_OVERFLOW_ERROR);
266: case punycode_bad_input:
267: throw new StringPrepParseException(
268: "Illegal char found in the input",
269: StringPrepParseException.ILLEGAL_CHAR_FOUND);
270: case punycode_overflow:
271: throw new StringPrepParseException(
272: "Invalid char found in the input",
273: StringPrepParseException.INVALID_CHAR_FOUND);
274: }
275:
276: }
277:
278: private static final int MAX_BUFFER_SIZE = 100;
279:
280: public static final StringBuffer decode(StringBuffer input,
281: char[] case_flags) throws StringPrepParseException {
282: char[] in = input.toString().toCharArray();
283: int[] outLen = new int[1];
284: outLen[0] = MAX_BUFFER_SIZE;
285: int[] output = new int[outLen[0]];
286: int rc = punycode_success;
287: StringBuffer result = new StringBuffer();
288: for (;;) {
289: rc = decode(input.length(), in, outLen, output, case_flags);
290: if (rc == punycode_big_output) {
291: outLen[0] = output.length * 4;
292: output = new int[outLen[0]];
293: continue;
294: }
295: break;
296: }
297: if (rc == punycode_success) {
298: for (int i = 0; i < outLen[0]; i++) {
299: UTF16.append(result, output[i]);
300: }
301: } else {
302: getException(rc);
303: }
304: return result;
305: }
306:
307: /*** Main decode function ***/
308: public static final int decode(int input_length, char[] input,
309: int[] output_length, int[] output, char[] case_flags) {
310: int n, out, i, max_out, bias, b, j, in, oldi, w, k, digit, t;
311:
312: /* Initialize the state: */
313:
314: n = initial_n;
315: out = i = 0;
316: max_out = output_length[0];
317: bias = initial_bias;
318:
319: /* Handle the basic code points: Let b be the number of input code */
320: /* points before the last delimiter, or 0 if there is none, then */
321: /* copy the first b code points to the output. */
322:
323: for (b = j = 0; j < input_length; ++j) {
324: if (delim(input[j]) == true) {
325: b = j;
326: }
327: }
328: if (b > max_out)
329: return punycode_big_output;
330:
331: for (j = 0; j < b; ++j) {
332: if (case_flags != null)
333: case_flags[out] = (char) (flagged(input[j]) ? 1 : 0);
334: if (!basic(input[j]))
335: return punycode_bad_input;
336: output[out++] = input[j];
337: }
338:
339: /* Main decoding loop: Start just after the last delimiter if any */
340: /* basic code points were copied; start at the beginning otherwise. */
341:
342: for (in = b > 0 ? b + 1 : 0; in < input_length; ++out) {
343:
344: /* in is the index of the next character to be consumed, and */
345: /* out is the number of code points in the output array. */
346:
347: /* Decode a generalized variable-length integer into delta, */
348: /* which gets added to i. The overflow checking is easier */
349: /* if we increase i as we go, then subtract off its starting */
350: /* value at the end to obtain delta. */
351:
352: for (oldi = i, w = 1, k = base;; k += base) {
353: if (in >= input_length)
354: return punycode_bad_input;
355: digit = decode_digit(input[in++]);
356: if (digit >= base)
357: return punycode_bad_input;
358: if (digit > (maxint - i) / w)
359: return punycode_overflow;
360: i += digit * w;
361: t = (k <= bias) /* + tmin */? tmin : /* +tmin not needed */
362: (k >= (bias + tmax)) ? tmax : k - bias;
363: if (digit < t)
364: break;
365: if (w > maxint / (base - t))
366: return punycode_overflow;
367: w *= (base - t);
368: }
369:
370: bias = adapt(i - oldi, out + 1, (oldi == 0));
371:
372: /* i was supposed to wrap around from out+1 to 0, */
373: /* incrementing n each time, so we'll fix that now: */
374:
375: if (i / (out + 1) > maxint - n)
376: return punycode_overflow;
377: n += i / (out + 1);
378: i %= (out + 1);
379:
380: /* Insert n at position i of the output: */
381:
382: /* not needed for Punycode: */
383: /* if (decode_digit(n) <= base) return punycode_invalid_input; */
384: if (out >= max_out)
385: return punycode_big_output;
386:
387: if (case_flags != null) {
388: System.arraycopy(case_flags, i, case_flags, i + 1, out
389: - i);
390: /* Case of last character determines uppercase flag: */
391: case_flags[i] = (char) (flagged(input[in - 1]) ? 0 : 1);
392: }
393:
394: System.arraycopy(output, i, output, i + 1, (out - i));
395: output[i++] = n;
396: }
397:
398: output_length[0] = out;
399: return punycode_success;
400: }
401:
402: }
|