001: /* Base32
002: *
003: * $Id: Base32.java 1828 2004-04-15 19:04:01Z stack-sf $
004: *
005: * Created on Jan 21, 2004
006: *
007: * Copyright (C) 2004 Internet Archive.
008: *
009: * This file is part of the Heritrix web crawler (crawler.archive.org).
010: *
011: * Heritrix is free software; you can redistribute it and/or modify
012: * it under the terms of the GNU Lesser Public License as published by
013: * the Free Software Foundation; either version 2.1 of the License, or
014: * any later version.
015: *
016: * Heritrix is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
019: * GNU Lesser Public License for more details.
020: *
021: * You should have received a copy of the GNU Lesser Public License
022: * along with Heritrix; if not, write to the Free Software
023: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024: */
025: package org.archive.util;
026:
027: /**
028: * Base32 - encodes and decodes RFC3548 Base32
029: * (see http://www.faqs.org/rfcs/rfc3548.html )
030: *
031: * Imported public-domain code of Bitzi.
032: *
033: * @author Robert Kaye
034: * @author Gordon Mohr
035: */
036: public class Base32 {
037: private static final String base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
038: private static final int[] base32Lookup = { 0xFF, 0xFF, 0x1A, 0x1B,
039: 0x1C, 0x1D, 0x1E, 0x1F, // '0', '1', '2', '3', '4', '5', '6', '7'
040: 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // '8', '9', ':', ';', '<', '=', '>', '?'
041: 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G'
042: 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'
043: 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W'
044: 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 'X', 'Y', 'Z', '[', '\', ']', '^', '_'
045: 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g'
046: 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o'
047: 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 'p', 'q', 'r', 's', 't', 'u', 'v', 'w'
048: 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF // 'x', 'y', 'z', '{', '|', '}', '~', 'DEL'
049: };
050:
051: /**
052: * Encodes byte array to Base32 String.
053: *
054: * @param bytes Bytes to encode.
055: * @return Encoded byte array <code>bytes</code> as a String.
056: *
057: */
058: static public String encode(final byte[] bytes) {
059: int i = 0, index = 0, digit = 0;
060: int currByte, nextByte;
061: StringBuffer base32 = new StringBuffer(
062: (bytes.length + 7) * 8 / 5);
063:
064: while (i < bytes.length) {
065: currByte = (bytes[i] >= 0) ? bytes[i] : (bytes[i] + 256); // unsign
066:
067: /* Is the current digit going to span a byte boundary? */
068: if (index > 3) {
069: if ((i + 1) < bytes.length) {
070: nextByte = (bytes[i + 1] >= 0) ? bytes[i + 1]
071: : (bytes[i + 1] + 256);
072: } else {
073: nextByte = 0;
074: }
075:
076: digit = currByte & (0xFF >> index);
077: index = (index + 5) % 8;
078: digit <<= index;
079: digit |= nextByte >> (8 - index);
080: i++;
081: } else {
082: digit = (currByte >> (8 - (index + 5))) & 0x1F;
083: index = (index + 5) % 8;
084: if (index == 0)
085: i++;
086: }
087: base32.append(base32Chars.charAt(digit));
088: }
089:
090: return base32.toString();
091: }
092:
093: /**
094: * Decodes the given Base32 String to a raw byte array.
095: *
096: * @param base32
097: * @return Decoded <code>base32</code> String as a raw byte array.
098: */
099: static public byte[] decode(final String base32) {
100: int i, index, lookup, offset, digit;
101: byte[] bytes = new byte[base32.length() * 5 / 8];
102:
103: for (i = 0, index = 0, offset = 0; i < base32.length(); i++) {
104: lookup = base32.charAt(i) - '0';
105:
106: /* Skip chars outside the lookup table */
107: if (lookup < 0 || lookup >= base32Lookup.length) {
108: continue;
109: }
110:
111: digit = base32Lookup[lookup];
112:
113: /* If this digit is not in the table, ignore it */
114: if (digit == 0xFF) {
115: continue;
116: }
117:
118: if (index <= 3) {
119: index = (index + 5) % 8;
120: if (index == 0) {
121: bytes[offset] |= digit;
122: offset++;
123: if (offset >= bytes.length)
124: break;
125: } else {
126: bytes[offset] |= digit << (8 - index);
127: }
128: } else {
129: index = (index + 5) % 8;
130: bytes[offset] |= (digit >>> index);
131: offset++;
132:
133: if (offset >= bytes.length) {
134: break;
135: }
136: bytes[offset] |= digit << (8 - index);
137: }
138: }
139: return bytes;
140: }
141:
142: /** For testing, take a command-line argument in Base32, decode, print in hex,
143: * encode, print
144: *
145: * @param args
146: */
147: static public void main(String[] args) {
148: if (args.length == 0) {
149: System.out.println("Supply a Base32-encoded argument.");
150: return;
151: }
152: System.out.println(" Original: " + args[0]);
153: byte[] decoded = Base32.decode(args[0]);
154: System.out.print(" Hex: ");
155: for (int i = 0; i < decoded.length; i++) {
156: int b = decoded[i];
157: if (b < 0) {
158: b += 256;
159: }
160: System.out.print((Integer.toHexString(b + 256))
161: .substring(1));
162: }
163: System.out.println();
164: System.out.println("Reencoded: " + Base32.encode(decoded));
165: }
166: }
|