1417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughespackage SQLite; 2417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 3417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes/** 4417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * String encoder/decoder for SQLite. 5417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * 6417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * This module was kindly donated by Eric van der Maarel of Nedap N.V. 7417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * 8417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * This encoder was implemented based on an original idea from an anonymous 9417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * author in the source code of the SQLite distribution. 10417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * I feel obliged to provide a quote from the original C-source code: 11417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * 12417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * "The author disclaims copyright to this source code. In place of 13417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * a legal notice, here is a blessing: 14417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * 15417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * May you do good and not evil. 16417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * May you find forgiveness for yourself and forgive others. 17417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * May you share freely, never taking more than you give." 18417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * 19417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes */ 20417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 21417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughespublic class StringEncoder { 22417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 23417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes /** 24417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * Encodes the given byte array into a string that can be used by 25417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * the SQLite database. The database cannot handle null (0x00) and 26417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * the character '\'' (0x27). The encoding consists of escaping 27417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * these characters with a reserved character (0x01). The escaping 28417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * is applied after determining and applying a shift that minimizes 29417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * the number of escapes required. 30417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * With this encoding the data of original size n is increased to a 31417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * maximum of 1+(n*257)/254. 32417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * For sufficiently large n the overhead is thus less than 1.2%. 33417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @param a the byte array to be encoded. A null reference is handled as 34417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * an empty array. 35417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @return the encoded bytes as a string. When an empty array is 36417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * provided a string of length 1 is returned, the value of 37417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * which is bogus. 38417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * When decoded with this class' <code>decode</code> method 39417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * a string of size 1 will return an empty byte array. 40417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes */ 41417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 42417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes public static String encode(byte[] a) { 437a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // check input 447a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if (a == null || a.length == 0) { 457a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // bogus shift, no data 467a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes return "x"; 477a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 487a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // determine count 497a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int[] cnt = new int[256]; 507a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes for (int i = 0 ; i < a.length; i++) { 517a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes cnt[a[i] & 0xff]++; 527a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 537a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // determine shift for minimum number of escapes 547a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int shift = 1; 557a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int nEscapes = a.length; 567a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes for (int i = 1; i < 256; i++) { 577a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if (i == '\'') { 587a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes continue; 597a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 607a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int sum = cnt[i] + cnt[(i + 1) & 0xff] + cnt[(i + '\'') & 0xff]; 617a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if (sum < nEscapes) { 627a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes nEscapes = sum; 637a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes shift = i; 647a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if (nEscapes == 0) { 657a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // cannot become smaller 667a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes break; 677a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 687a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 697a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 707a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // construct encoded output 717a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int outLen = a.length + nEscapes + 1; 727a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes StringBuffer out = new StringBuffer(outLen); 737a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append((char)shift); 747a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes for (int i = 0; i < a.length; i++) { 757a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // apply shift 767a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes char c = (char)((a[i] - shift)&0xff); 777a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // insert escapes 787a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if (c == 0) { // forbidden 797a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append((char)1); 807a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append((char)1); 817a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } else if (c == 1) { // escape character 827a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append((char)1); 837a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append((char)2); 847a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } else if (c == '\'') { // forbidden 857a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append((char)1); 867a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append((char)3); 877a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } else { 887a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append(c); 897a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 907a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 917a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes return out.toString(); 92417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes } 93417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 94417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes /** 95417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * Decodes the given string that is assumed to be a valid encoding 96417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * of a byte array. Typically the given string is generated by 97417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * this class' <code>encode</code> method. 98417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @param s the given string encoding. 99417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @return the byte array obtained from the decoding. 100417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @throws IllegalArgumentException when the string given is not 101417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * a valid encoded string for this encoder. 102417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes */ 103417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 104417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes public static byte[] decode(String s) { 1057a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes char[] a = s.toCharArray(); 1067a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if (a.length > 2 && a[0] == 'X' && 1077a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes a[1] == '\'' && a[a.length-1] == '\'') { 1087a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // SQLite3 BLOB syntax 1097a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes byte[] result = new byte[(a.length-3)/2]; 1107a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes for (int i = 2, k = 0; i < a.length - 1; i += 2, k++) { 1117a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes byte tmp; 1127a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes switch (a[i]) { 1137a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '0': tmp = 0; break; 1147a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '1': tmp = 1; break; 1157a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '2': tmp = 2; break; 1167a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '3': tmp = 3; break; 1177a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '4': tmp = 4; break; 1187a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '5': tmp = 5; break; 1197a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '6': tmp = 6; break; 1207a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '7': tmp = 7; break; 1217a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '8': tmp = 8; break; 1227a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '9': tmp = 9; break; 1237a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'A': 1247a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'a': tmp = 10; break; 1257a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'B': 1267a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'b': tmp = 11; break; 1277a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'C': 1287a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'c': tmp = 12; break; 1297a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'D': 1307a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'd': tmp = 13; break; 1317a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'E': 1327a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'e': tmp = 14; break; 1337a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'F': 1347a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'f': tmp = 15; break; 1357a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes default: tmp = 0; break; 1367a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 1377a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes result[k] = (byte) (tmp << 4); 1387a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes switch (a[i+1]) { 1397a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '0': tmp = 0; break; 1407a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '1': tmp = 1; break; 1417a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '2': tmp = 2; break; 1427a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '3': tmp = 3; break; 1437a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '4': tmp = 4; break; 1447a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '5': tmp = 5; break; 1457a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '6': tmp = 6; break; 1467a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '7': tmp = 7; break; 1477a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '8': tmp = 8; break; 1487a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case '9': tmp = 9; break; 1497a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'A': 1507a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'a': tmp = 10; break; 1517a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'B': 1527a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'b': tmp = 11; break; 1537a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'C': 1547a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'c': tmp = 12; break; 1557a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'D': 1567a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'd': tmp = 13; break; 1577a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'E': 1587a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'e': tmp = 14; break; 1597a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'F': 1607a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes case 'f': tmp = 15; break; 1617a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes default: tmp = 0; break; 1627a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 1637a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes result[k] |= tmp; 1647a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 1657a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes return result; 1667a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 1677a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // first element is the shift 1687a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes byte[] result = new byte[a.length-1]; 1697a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int i = 0; 1707a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int shift = s.charAt(i++); 1717a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int j = 0; 1727a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes while (i < s.length()) { 1737a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int c; 1747a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if ((c = s.charAt(i++)) == 1) { // escape character found 1757a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if ((c = s.charAt(i++)) == 1) { 1767a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes c = 0; 1777a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } else if (c == 2) { 1787a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes c = 1; 1797a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } else if (c == 3) { 1807a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes c = '\''; 1817a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } else { 1827a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes throw new IllegalArgumentException( 1837a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes "invalid string passed to decoder: " + j); 1847a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 1857a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 1867a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // do shift 1877a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes result[j++] = (byte)((c + shift) & 0xff); 1887a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 1897a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int outLen = j; 1907a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // provide array of correct length 1917a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if (result.length != outLen) { 1927a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes result = byteCopy(result, 0, outLen, new byte[outLen]); 1937a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 1947a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes return result; 195417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes } 196417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 197417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes /** 198417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * Copies count elements from source, starting at element with 199417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * index offset, to the given target. 200417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @param source the source. 201417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @param offset the offset. 202417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @param count the number of elements to be copied. 203417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @param target the target to be returned. 204417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @return the target being copied to. 205417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes */ 206417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 207417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes private static byte[] byteCopy(byte[] source, int offset, 2087a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int count, byte[] target) { 2097a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes for (int i = offset, j = 0; i < offset + count; i++, j++) { 2107a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes target[j] = source[i]; 2117a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 2127a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes return target; 213417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes } 214417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 215417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 216417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes static final char[] xdigits = { 2177a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes '0', '1', '2', '3', '4', '5', '6', '7', 2187a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 219417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes }; 220417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 221417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes /** 222417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * Encodes the given byte array into SQLite3 blob notation, ie X'..' 223417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @param a the byte array to be encoded. A null reference is handled as 224417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * an empty array. 225417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes * @return the encoded bytes as a string. 226417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes */ 227417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes 228417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes public static String encodeX(byte[] a) { 2297a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes // check input 2307a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes if (a == null || a.length == 0) { 2317a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes return "X''"; 2327a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 2337a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes int outLen = a.length * 2 + 3; 2347a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes StringBuffer out = new StringBuffer(outLen); 2357a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append('X'); 2367a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append('\''); 2377a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes for (int i = 0; i < a.length; i++) { 2387a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append(xdigits[(a[i] >> 4) & 0x0F]); 2397a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append(xdigits[a[i] & 0x0F]); 2407a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes } 2417a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes out.append('\''); 2427a647e8547e57ca573541be55b3728ef7ce376feElliott Hughes return out.toString(); 243417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes } 244417deb1db112103aff04231b6ca79772ff7d3a21Elliott Hughes} 245