001: /*
002: * @(#)Base64Decoder.java 1.1 05/01/20
003: *
004: * Copyright (c) 2004,2005 Sun Microsystems, Inc. All Rights Reserved.
005: *
006: * See the file "LICENSE.txt" for information on usage and redistribution
007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
008: */
009: package org.pnuts.io;
010:
011: import java.io.ByteArrayInputStream;
012: import java.io.ByteArrayOutputStream;
013: import java.io.IOException;
014: import java.io.InputStream;
015: import java.io.OutputStream;
016:
017: public class Base64Decoder {
018: private static final int BUFFER_SIZE = 4096;
019: private static byte[] table = new byte[255];
020:
021: static {
022: for (int i = 0; i < 255; i++) {
023: table[i] = (byte) -1;
024: }
025: for (int i = 'Z'; i >= 'A'; i--) {
026: table[i] = (byte) (i - 'A');
027: }
028: for (int i = 'z'; i >= 'a'; i--) {
029: table[i] = (byte) (i - 'a' + 26);
030: }
031: for (int i = '9'; i >= '0'; i--) {
032: table[i] = (byte) (i - '0' + 52);
033: }
034: table['='] = 65;
035: table['+'] = 62;
036: table['/'] = 63;
037: }
038:
039: public Base64Decoder() {
040: }
041:
042: private final int get1(byte buf[], int offset) {
043: return ((buf[offset] & 0x3f) << 2)
044: | ((buf[offset + 1] & 0x30) >>> 4);
045: }
046:
047: private final int get2(byte buf[], int offset) {
048: return ((buf[offset + 1] & 0x0f) << 4)
049: | ((buf[offset + 2] & 0x3c) >>> 2);
050: }
051:
052: private final int get3(byte buf[], int offset) {
053: return ((buf[offset + 2] & 0x03) << 6)
054: | (buf[offset + 3] & 0x3f);
055: }
056:
057: public void decode(InputStream in, OutputStream out)
058: throws IOException {
059: byte buffer[] = new byte[BUFFER_SIZE];
060: byte chunk[] = new byte[4];
061: int nread = -1;
062: int ready = 0;
063:
064: fill: while ((nread = in.read(buffer)) > 0) {
065: int skiped = 0;
066: while (skiped < nread) {
067: while (ready < 4) {
068: if (skiped >= nread) {
069: continue fill;
070: }
071: int ch = table[buffer[skiped++]];
072: if (ch >= 0) {
073: chunk[ready++] = (byte) ch;
074: }
075: }
076: if (chunk[2] == 65) {
077: out.write(get1(chunk, 0));
078: return;
079: } else if (chunk[3] == 65) {
080: out.write(get1(chunk, 0));
081: out.write(get2(chunk, 0));
082: return;
083: } else {
084: out.write(get1(chunk, 0));
085: out.write(get2(chunk, 0));
086: out.write(get3(chunk, 0));
087: }
088: ready = 0;
089: }
090: }
091: if (ready != 0) {
092: throw new IOException("Invalid length.");
093: }
094: out.flush();
095: }
096:
097: public String decode(String input) throws IOException {
098: int len = input.length();
099: byte bytes[] = new byte[len];
100: for (int i = 0; i < len; i++) {
101: bytes[i] = (byte) input.charAt(i);
102: }
103: InputStream in = new ByteArrayInputStream(bytes);
104: ByteArrayOutputStream bout = new ByteArrayOutputStream();
105: decode(in, bout);
106: return bout.toString();
107: }
108:
109: public byte[] decode(byte[] bytes) throws IOException {
110: InputStream in = new ByteArrayInputStream(bytes);
111: ByteArrayOutputStream bout = new ByteArrayOutputStream();
112: decode(in, bout);
113: return bout.toByteArray();
114: }
115: }
|