Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/cybertivity/powerjournal/Base64.java


1   package com.cybertivity.powerjournal;
2   
3   /**
4    * Freeware from:
5    * Roedy Green
6    * Canadian Mind Products
7    * #208 - 525 Ninth Street
8    * New Westminster, BC Canada V3M 5T9
9    * tel: (604) 777-1804
10   * mailto:roedy@mindprod.com
11   */
12  
13  /**
14   * Encode arbitrary binary into printable ASCII using BASE64 encoding.
15   * very loosely based on the Base64 Reader by
16   * Dr. Mark Thornton
17   * Optrak Distribution Software Ltd.
18   * http://www.optrak.co.uk
19   * and Kevin Kelley's  http://www.ruralnet.net/~kelley/java/Base64.java
20   *
21   * Base64 is a way of encoding 8-bit characters using only ASCII printable
22   * characters.  The spec is described in RFC 2045.  Base64 is a scheme where
23   * 3 bytes are concatenated, then split to form 4 groups of 6-bits each; and
24   * each 6-bits gets translated to an encoded printable ASCII character, via a
25   * table lookup.  An encoded string is therefore longer than the original by
26   * about 1/3.  The "=" character is used to pad the end.  Base64 is used,
27   * among other things, to encode the user:password string in an
28   * Authorization: header for HTTP.  Don't confuse Base64 with
29   * x-www-form-urlencoded which is handled by
30   * Java.net.URLEncoder.encode/decode
31   * If you wanted to encode a giant file, you could do it in large chunks that
32   * are even multiples of 3 bytes, except for the last chunk, and append the outputs.
33   *
34   * version 1.1 1999 December 04 -- more symmetrical encoding algorithm.
35   *                                 more accurate StringBuffer allocation size.
36   * version 1.0 1999 December 03 -- posted in comp.lang.java.programmer.
37   */
38  
39  public class Base64
40  {
41  
42    /**
43     * Encode an arbitrary array of bytes as Base64 printable ASCII.
44     * It will be broken into lines of 72 chars each.  The last line is not
45     * terminated with a line separator.
46     */
47    public static String encode(byte[] b)
48    {
49      // Each group or partial group of 3 bytes becomes four chars
50      int outputLength = ((b.length + 2) / 3) * 4;
51  
52      // account for embedded newlines
53      outputLength += (outputLength / lineLength) * lineSeparator.length();
54  
55      // must be local for recursion to work.
56      StringBuffer sb = new StringBuffer( outputLength );
57  
58      // must be local for recursion to work.
59      int linePos = 0;
60  
61      // first deal with even multiples of 3 bytes.
62      int len = (b.length / 3) * 3;
63      int leftover = b.length - len;
64      for ( int i=0; i<len; i+=3 )
65      {
66  
67  
68        // Start a new line if next 4 chars won't fit on the current line
69        // We don't encapsulate so that linePos and sb will work recursively
70        {
71  
72          linePos += 4;
73          if ( linePos > lineLength )
74          {
75            linePos = 0;
76            if ( lineLength != 0) {sb.append(lineSeparator);}
77          }
78        }
79  
80        // get next three bytes in unsigned form lined up,
81        // in big-endian order
82        int combined = b[i+0] & 0xff;
83        combined <<= 8;
84        combined |= b[i+1] & 0xff;
85        combined <<= 8;
86        combined |= b[i+2] & 0xff;
87  
88        // break those 24 bits into a 4 groups of 6 bits,
89        // working LSB to MSB.
90        int c3 = combined & 0x3f;
91        combined >>>= 6;
92        int c2 = combined & 0x3f;
93        combined >>>= 6;
94        int c1 = combined & 0x3f;
95        combined >>>= 6;
96        int c0 = combined & 0x3f;
97  
98        // Translate into the equivalent alpha character
99        // emitting them in big-endian order.
100       sb.append( valueToChar[c0]);
101       sb.append( valueToChar[c1]);
102       sb.append( valueToChar[c2]);
103       sb.append( valueToChar[c3]);
104     }
105 
106     // deal with leftover bytes
107     switch ( leftover )
108     {
109       case 0:
110       default:
111         // nothing to do
112         break;
113 
114       case 1:
115         // One leftover byte generates xx==
116         // Start a new line if next 4 chars won't fit on the current line
117         // We don't encapsulate so that linePos and sb will work recursively
118         {
119 
120           linePos += 4;
121           if ( linePos > lineLength )
122           {
123             linePos = 0;
124             if ( lineLength != 0) {sb.append(lineSeparator);}
125           }
126         }
127         // Handle this recursively with a faked complete triple.
128         // Throw away last two chars and replace with ==
129         sb.append(encode(new byte[] {b[len], 0, 0}
130                 ).substring(0,2));
131         sb.append("==");
132         break;
133 
134       case 2:
135         // Two leftover bytes generates xxx=
136         // Start a new line if next 4 chars won't fit on the current line
137         // We don't encapsulate so that linePos and sb will work recursively
138         {
139           linePos += 4;
140           if ( linePos > lineLength )
141           {
142             linePos = 0;
143              if ( lineLength != 0) {sb.append(lineSeparator);}
144           }
145         }
146         // Handle this recursively with a faked complete triple.
147         // Throw away last char and replace with =
148         sb.append(encode(new byte[] {b[len], b[len+1], 0}
149                 ).substring(0,3));
150         sb.append("=");
151         break;
152 
153     } // end switch;
154 
155     if ( outputLength != sb.length() ) System.out.println("oops");
156 
157     return sb.toString();
158   }// end encode
159 
160 
161   /**
162    * how we separate lines, e.g. \n, \r\n, \r etc.
163    */
164   static String lineSeparator = System.getProperty( "line.separator" );
165 
166 
167   /**
168    * decode a well-formed complete Base64 string back into an array of bytes.
169    */
170   public static byte[] decode( String s)
171   {
172 
173     // estimate worst case size of output array, no embedded newlines.
174     byte[] b = new byte[(s.length() / 4) * 3];
175 
176     // tracks where we are in a cycle of 4 input chars.
177     int cycle = 0;
178 
179     // where we combine 4 groups of 6 bits and take apart as 3 groups of 8.
180     int combined = 0;
181 
182     // how many bytes we have prepared.
183     int j = 0;
184     int len = s.length();
185     for ( int i=0; i<len; i++ )
186     {
187 
188       int c = s.charAt(i);
189       int value  = (c <= 255) ? charToValue[c] : IGNORE;
190       // there are two magic values PAD (=) and IGNORE.
191       switch ( value )
192       {
193         case IGNORE:
194           // e.g. \n, just ignore it.
195           break;
196 
197         case PAD:
198           break;
199 
200         default:
201           /* regular value character */
202           switch ( cycle )
203           {
204             case 0:
205               combined = value;
206               cycle = 1;
207               break;
208 
209             case 1:
210               combined <<= 6;
211               combined |= value;
212               cycle = 2;
213               break;
214 
215             case 2:
216               combined <<= 6;
217               combined |= value;
218               cycle = 3;
219               break;
220 
221             case 3:
222               combined <<= 6;
223               combined |= value;
224               // we have just completed a cycle of 4 chars.
225               // the four 6-bit values are in combined in big-endian order
226               // peel them off 8 bits at a time working lsb to msb
227               // to get our original 3 8-bit bytes back
228 
229               b[j+2] = (byte)combined;
230               combined >>>= 8;
231               b[j+1] = (byte)combined;
232               combined >>>= 8;
233               b[j+0] = (byte)combined;
234               j += 3;
235               cycle = 0;
236               break;
237           }
238           break;
239       }
240     }
241 
242     // to come.
243     return b;
244 
245   }// end decode
246 
247   /**
248    * max chars per line. A multiple of 4.
249    */
250   private static int lineLength = 72;
251 
252 
253   /**
254    * determines how long the lines are that are generated by encode.
255    * Ignored by decode.
256    * @param length 0 means no newlines inserted.
257    */
258   public static void setLineLength(int length)
259   {
260     lineLength = length;
261   }
262 
263   /**
264    * letter of the alphabet used to encode binary values 0..63
265    */
266   static final char[] valueToChar = new char[64];
267 
268 
269   /**
270    * binary value encoded by a given letter of the alphabet 0..63
271    */
272   static final int[] charToValue = new int[256];
273 
274 
275 
276   /**
277    * Marker value for chars we just ignore, e.g. \n \r high ascii
278    */
279   static final int IGNORE = -1;
280 
281   /**
282    * Marker for = trailing pad
283    */
284   static final int PAD = -2;
285 
286 
287   static
288   {
289     // build translate valueToChar table only once.
290     // 0..25 -> 'A'..'Z'
291     for ( int i=0; i<=25; i++ )
292       valueToChar[i] = (char)('A'+i);
293     // 26..51 -> 'a'..'z'
294     for ( int i=0; i<=25; i++ )
295       valueToChar[i+26] = (char)('a'+i);
296     // 52..61 -> '0'..'9'
297     for ( int i=0; i<=9; i++ )
298       valueToChar[i+52] = (char)('0'+i);
299     valueToChar[62] = '+';
300     valueToChar[63] = '/';
301 
302     // build translate charToValue table only once.
303     for ( int i=0; i<256; i++ )
304     {
305       charToValue[i] = IGNORE;  // default is to ignore
306     }
307 
308     for ( int i=0; i<64; i++ )
309     {
310       charToValue[valueToChar[i]] = i;
311     }
312 
313     charToValue['='] = PAD;
314   }
315 
316   /**
317    * used to disable test driver
318    */
319   private static final boolean debug = true;
320 
321   /**
322    * test driver
323    */
324   public static void main(String[] args)
325   {
326     if ( debug )
327     {
328       byte[] a = { (byte)0xfc, (byte)0x0f, (byte)0xc0};
329       byte[] b = { (byte)0x03, (byte)0xf0, (byte)0x3f};
330       byte[] c = { (byte)0x00, (byte)0x00, (byte)0x00};
331       byte[] d = { (byte)0xff, (byte)0xff, (byte)0xff};
332       byte[] e = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1};
333       byte[] f = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1, (byte)2};
334       byte[] g = { (byte)0xfc, (byte)0x0f, (byte)0xc0, (byte)1, (byte)2, (byte)3};
335 
336       System.out.println(Base64.encode(a));
337       System.out.println(Base64.encode(b));
338       System.out.println(Base64.encode(c));
339       System.out.println(Base64.encode(d));
340       System.out.println(Base64.encode(e));
341       System.out.println(Base64.encode(f));
342       System.out.println(Base64.encode(g));
343 
344       for ( int i=0; i<64; i++ )
345       {
346         c[2] = (byte)i;
347         System.out.println(Base64.encode(c));
348       }
349     }
350 
351   }// end main
352 
353 } // end Base64
354