/Users/lyon/j4p/src/net/proxy/BASE64Encoder.java

1    package net.proxy; 
2     
3    import java.util.Arrays; 
4     
5    /** 
6     * Utility class to do Base64 encoding, as defined by RFC 2045, 
7     * section 6.8 (http://www.ietf.org/rfc/rfc2045.txt) 
8     * Uses the same class and function names as Sun's implementation from 
9     * sun.misc 
10    */ 
11   public class BASE64Encoder { 
12    
13     /** 
14      * Convert a byte to an integer.  Needed because in Java bytes 
15      * are signed, and for Base64 purposes they are not.  If not done 
16      * this way, when converted to an int, 0xFF will become -127 
17      * @param b Byte value to be converted 
18      * @return Value as an integer, as if byte was unsigned 
19      */ 
20     private int uByteToInt(byte b) { 
21       if (b >= 0) { 
22         return (int) b; 
23       } 
24    
25       return 256 + b; 
26     } 
27    
28     /** 
29      * Encode an array of bytes using Base64 
30      * @param data[] The bytes to be encoded 
31      * @return A valid Base64 representation of the input 
32      */ 
33     public String encode(byte data[]) { 
34       // Base64 encoding yields a String that is 33% longer than the byte array 
35       int charCount = ((data.length * 4) / 3) + 4; 
36    
37       // New lines will also be needed for every 76 charactesr, so allocate a 
38       // StringBuffer that is long enough to hold the full result without 
39       // having to expand later 
40       StringBuffer result = new StringBuffer( 
41           (charCount * 77) / 76); 
42    
43       int byteArrayLength = data.length; 
44       int byteArrayIndex = 0; 
45       int byteTriplet = 0; 
46       while (byteArrayIndex < byteArrayLength - 2) { 
47         byteArrayIndex = processData( 
48             data, byteArrayIndex, result); 
49    
50       } 
51    
52       byteArrayIndex = checkIfWeHave1ByteLeftOver( 
53           byteArrayIndex, byteArrayLength, data, result); 
54       checkIfWeHaveTwoBytesLeftOver( 
55           byteArrayIndex, byteArrayLength, data, result); 
56    
57       return result.toString(); 
58     } 
59    
60     private int processData( 
61         byte[] data, int index, 
62         StringBuffer result) { 
63       int byteTriplet; 
64       // Build the 24 bit byte triplet from the input data 
65       byteTriplet = uByteToInt(data[index++]); 
66       // Each input byte contributes 8 bits to the triplet 
67       byteTriplet <<= 8; 
68       byteTriplet |= uByteToInt(data[index++]); 
69       byteTriplet <<= 8; 
70       byteTriplet |= uByteToInt(data[index++]); 
71    
72       // Look at the lowest order six bits and remember them 
73       byte b4 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
74       // Move the byte triplet to get the next 6 bit value 
75       byteTriplet >>= 6; 
76       byte b3 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
77       byteTriplet >>= 6; 
78       byte b2 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
79       byteTriplet >>= 6; 
80       byte b1 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
81    
82       // Add the Base64 encoded character to the result String 
83       result.append(byteToChar(b1)); 
84       result.append(byteToChar(b2)); 
85       result.append(byteToChar(b3)); 
86       result.append(byteToChar(b4)); 
87    
88       // There are 57 bytes for every 76 characters, so wrap the line when needed 
89       if (index % 57 == 0) { 
90         result.append("\n"); 
91       } 
92       return index; 
93     } 
94    
95     private void checkIfWeHaveTwoBytesLeftOver( 
96         int index, 
97         int length, 
98         byte[] byteArray, 
99         StringBuffer sb) { 
100      int byteTriplet; 
101      if (index == length - 2) { 
102        // Convert our two bytes to an int 
103        byteTriplet = twoBytesToInt(byteArray, index); 
104        // Right pad the third 6 bit value with zeros 
105        byteTriplet <<= 2; 
106   
107        byte b3 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
108        byteTriplet >>= 6; 
109        byte b2 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
110        byteTriplet >>= 6; 
111        byte b1 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
112   
113        sb.append(byteToChar(b1)); 
114        sb.append(byteToChar(b2)); 
115        sb.append(byteToChar(b3)); 
116   
117        // Add "==" to the output to make it a multiple of 4 Base64 characters 
118        sb.append("="); 
119      } 
120    } 
121   
122    private int checkIfWeHave1ByteLeftOver( 
123        int index, 
124        int length, 
125        byte[] byteArray, 
126        StringBuffer sb) { 
127      int byteTriplet; 
128      if (index == length - 1) { 
129        // Convert our one byte to an int 
130        byteTriplet = uByteToInt(byteArray[index++]); 
131        // Right pad the second 6 bit value with zeros 
132        byteTriplet <<= 4; 
133   
134        byte b2 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
135        byteTriplet >>= 6; 
136        byte b1 = (byte) (Constants.SIX_BIT_MASK & byteTriplet); 
137   
138        sb.append(byteToChar(b1)); 
139        sb.append(byteToChar(b2)); 
140   
141        // Add "==" to the output to make it a multiple of 4 Base64 characters 
142        sb.append("=="); 
143      } 
144      return index; 
145    } 
146   
147    private int twoBytesToInt(byte[] data, int byteArrayIndex) { 
148      int byteTriplet; 
149      byteTriplet = 
150          uByteToInt(data[byteArrayIndex++]); 
151      byteTriplet <<= 8; 
152      byteTriplet |= uByteToInt(data[byteArrayIndex++]); 
153      return byteTriplet; 
154    } 
155   
156    /** 
157     * Convert a byte between 0 and 63 to its Base64 character equivalent 
158     * @param b Byte value to be converted 
159     * @return Base64 char value 
160     */ 
161    private char byteToChar(byte b) { 
162      if (b < Constants.LOWER_CASE_A_VALUE) { 
163        return (char) ('A' + b); 
164      } 
165   
166      if (b < Constants.ZERO_VALUE) { 
167        return (char) ('a' + (b - Constants.LOWER_CASE_A_VALUE)); 
168      } 
169   
170      if (b < Constants.PLUS_VALUE) { 
171        return (char) ('0' + (b - Constants.ZERO_VALUE)); 
172      } 
173   
174      if (b == Constants.PLUS_VALUE) { 
175        return '+'; 
176      } 
177   
178      if (b == Constants.SLASH_VALUE) { 
179        return '/'; 
180      } 
181   
182      throw 
183          new IllegalArgumentException( 
184              "Byte " + 
185              new Integer(b) + 
186              " is not a valid Base64 value"); 
187    } 
188   
189    /** 
190     * Simple test method to make sure everything works correctly 
191     * Creates 100 randomly sized arrays of random bytes, encodes them, 
192     * decodes them, and checks to make sure the result matches the input 
193     */ 
194    public static void main(String args[]) throws Exception { 
195      testCodec(); 
196   
197    } 
198   
199    private static void testCodec() { 
200  //      sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder(); 
201  //      sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder(); 
202   
203      for (int j = 0; j < 100; j++) 
204        test(new BASE64Encoder(), new BASE64Decoder()); 
205   
206    } 
207   
208    private static void test(BASE64Encoder encoder, BASE64Decoder decoder) { 
209      byte test[] = new byte[(int) (100000 * Math.random())]; 
210      for (int i = 0; i < test.length; i++) { 
211        test[i] = (byte) (256 * Math.random()); 
212      } 
213   
214      String string = encoder.encode(test); 
215      byte result[] = decoder.decodeBuffer(string); 
216   
217      if (!Arrays.equals(test, result) || test.length != result.length) { 
218        System.out.println("ARRAYS DO NOT MATCH!"); 
219      } 
220    } 
221  } 
222