1 package net.sf.openrocket.util;
3 import java.util.Arrays;
4 import java.util.HashMap;
6 import java.util.Random;
10 public static final int DEFAULT_CHARS_PER_LINE = 72;
12 private static final char[] ALPHABET = new char[] {
13 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
14 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
15 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
16 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
18 private static final char PAD = '=';
20 private static final Map<Character,Integer> REVERSE = new HashMap<Character,Integer>();
22 for (int i=0; i<64; i++) {
23 REVERSE.put(ALPHABET[i], i);
31 public static String encode(byte[] data) {
32 return encode(data, DEFAULT_CHARS_PER_LINE);
35 public static String encode(byte[] data, int maxColumn) {
36 StringBuilder builder = new StringBuilder();
39 for (int position=0; position < data.length; position+=3) {
40 if (column+4 > maxColumn) {
44 builder.append(encodeGroup(data, position));
48 return builder.toString();
54 public static byte[] decode(String data) {
55 byte[] array = new byte[data.length()*3/4];
56 char[] block = new char[4];
59 for (int position=0; position < data.length(); ) {
61 for (p=0; p<4 && position < data.length(); position++) {
62 char c = data.charAt(position);
63 if (!Character.isWhitespace(c)) {
72 throw new IllegalArgumentException("Data ended when decoding Base64, p="+p);
75 int l = decodeGroup(block, array, length);
80 return Arrays.copyOf(array, length);
88 * Encode three bytes of data into four characters.
90 private static char[] encodeGroup(byte[] data, int position) {
91 char[] c = new char[] { '=','=','=','=' };
93 int length = data.length - position;
99 b1 = ((int)data[position])&0xFF;
102 b2 = ((int)data[position+1])&0xFF;
105 b3 = ((int)data[position+2])&0xFF;
108 c[0] = ALPHABET[b1>>2];
109 c[1] = ALPHABET[(b1 & 3)<<4 | (b2>>4)];
112 c[2] = ALPHABET[(b2 & 15)<<2 | (b3>>6)];
115 c[3] = ALPHABET[b3 & 0x3f];
121 * Decode four chars from data into 0-3 bytes of data starting at position in array.
122 * @return the number of bytes decoded.
124 private static int decodeGroup(char[] data, byte[] array, int position) {
128 b1 = REVERSE.get(data[0]);
129 b2 = REVERSE.get(data[1]);
130 b3 = REVERSE.get(data[2]);
131 b4 = REVERSE.get(data[3]);
132 } catch (NullPointerException e) {
133 // If auto-boxing fails
134 throw new IllegalArgumentException("Illegal characters in the sequence to be "+
135 "decoded: "+Arrays.toString(data));
138 array[position] = (byte)((b1 << 2) | (b2 >> 4));
139 array[position+1] = (byte)((b2 << 4) | (b3 >> 2));
140 array[position+2] = (byte)((b3 << 6) | (b4));
142 // Check the amount of data decoded
145 if (data[1] == PAD) {
146 throw new IllegalArgumentException("Illegal character padding in sequence to be "+
147 "decoded: "+Arrays.toString(data));
159 public static void main(String[] arg) {
160 Random rnd = new Random();
162 for (int round=0; round < 1000; round++) {
163 int n = rnd.nextInt(1000);
166 byte[] array = new byte[n];
167 rnd.nextBytes(array);
169 String encoded = encode(array);
171 System.out.println(encoded);
173 // for (int i=0; i<1000; i++) {
174 // int pos = rnd.nextInt(encoded.length());
175 // String s1 = encoded.substring(0, pos);
176 // String s2 = encoded.substring(pos);
177 // switch (rnd.nextInt(15)) {
179 // encoded = s1 + " " + s2;
182 // encoded = s1 + "\u0009" + s2;
185 // encoded = s1 + "\n" + s2;
188 // encoded = s1 + "\u000B" + s2;
191 // encoded = s1 + "\r" + s2;
194 // encoded = s1 + "\u000C" + s2;
197 // encoded = s1 + "\u001C" + s2;
202 byte[] decoded = null;
204 decoded = decode(encoded);
205 } catch (IllegalArgumentException e) {
207 System.err.println("Bad data:\n"+encoded);
211 if (!Arrays.equals(array, decoded)) {
212 System.err.println("Data differs! n="+n);
215 System.out.println("n="+n+" ok!");