1b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallampackage org.bouncycastle.util.encoders;
2b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
3b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.IOException;
4b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallamimport java.io.OutputStream;
5b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
6b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallampublic class Base64Encoder
7b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    implements Encoder
8b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam{
9b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected final byte[] encodingTable =
10b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
11b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
12b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
13b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
14b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
15b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
16b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
17b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
18b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'v',
19b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'w', (byte)'x', (byte)'y', (byte)'z',
20b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6',
21b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'7', (byte)'8', (byte)'9',
22b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            (byte)'+', (byte)'/'
23b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        };
24b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
25b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected byte    padding = (byte)'=';
26b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
27b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    /*
28b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * set up the decoding table.
29b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     */
30b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected final byte[] decodingTable = new byte[128];
31b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
32b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    protected void initialiseDecodingTable()
33b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
3470c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom        for (int i = 0; i < decodingTable.length; i++)
3570c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom        {
3670c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            decodingTable[i] = (byte)0xff;
3770c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom        }
3870c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom
39b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        for (int i = 0; i < encodingTable.length; i++)
40b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
41b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            decodingTable[encodingTable[i]] = (byte)i;
42b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
43b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
44b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
45b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public Base64Encoder()
46b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
47b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        initialiseDecodingTable();
48b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
49b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
50b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    /**
51b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * encode the input data producing a base 64 output stream.
52b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     *
53b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * @return the number of bytes produced.
54b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     */
55b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public int encode(
56b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]                data,
57b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int                    off,
58b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int                    length,
59b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        OutputStream    out)
60b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
61b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
62b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int modulus = length % 3;
63b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int dataLength = (length - modulus);
64b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int a1, a2, a3;
65b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
66b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        for (int i = off; i < off + dataLength; i += 3)
67b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
68b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            a1 = data[i] & 0xff;
69b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            a2 = data[i + 1] & 0xff;
70b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            a3 = data[i + 2] & 0xff;
71b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
72b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[(a1 >>> 2) & 0x3f]);
73b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[((a1 << 4) | (a2 >>> 4)) & 0x3f]);
74b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[((a2 << 2) | (a3 >>> 6)) & 0x3f]);
75b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[a3 & 0x3f]);
76b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
77b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
78b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        /*
79b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam         * process the tail end.
80b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam         */
81b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int    b1, b2, b3;
82b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int    d1, d2;
83b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
84b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        switch (modulus)
85b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
86b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        case 0:        /* nothing left to do */
87b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            break;
88b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        case 1:
89b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            d1 = data[off + dataLength] & 0xff;
90b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b1 = (d1 >>> 2) & 0x3f;
91b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b2 = (d1 << 4) & 0x3f;
92b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
93b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[b1]);
94b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[b2]);
95b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(padding);
96b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(padding);
97b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            break;
98b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        case 2:
99b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            d1 = data[off + dataLength] & 0xff;
100b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            d2 = data[off + dataLength + 1] & 0xff;
101b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
102b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b1 = (d1 >>> 2) & 0x3f;
103b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b2 = ((d1 << 4) | (d2 >>> 4)) & 0x3f;
104b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b3 = (d2 << 2) & 0x3f;
105b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
106b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[b1]);
107b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[b2]);
108b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(encodingTable[b3]);
109b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write(padding);
110b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            break;
111b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
112b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
113b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return (dataLength / 3) * 4 + ((modulus == 0) ? 0 : 4);
114b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
115b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
116b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private boolean ignore(
117b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        char    c)
118b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
119b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return (c == '\n' || c =='\r' || c == '\t' || c == ' ');
120b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
121b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
122b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    /**
123b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * decode the base 64 encoded byte data writing it to the given output stream,
124b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * whitespace characters will be ignored.
125b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     *
126b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * @return the number of bytes produced.
127b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     */
128b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public int decode(
129b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte[]          data,
130b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int             off,
131b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int             length,
132b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        OutputStream    out)
133b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
134b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
135b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte    b1, b2, b3, b4;
136b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int     outLen = 0;
137b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
138b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int     end = off + length;
139b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
140b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while (end > off)
141b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
142b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (!ignore((char)data[end - 1]))
143b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
144b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                break;
145b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
146b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
147b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            end--;
148b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
149b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
150b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int  i = off;
151b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int  finish = end - 4;
152b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
153b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        i = nextI(data, i, finish);
154b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
155b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while (i < finish)
156b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
157b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b1 = decodingTable[data[i++]];
158b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
159b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i = nextI(data, i, finish);
160b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
161b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b2 = decodingTable[data[i++]];
162b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
163b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i = nextI(data, i, finish);
164b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
165b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b3 = decodingTable[data[i++]];
166b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
167b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i = nextI(data, i, finish);
168b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
169b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b4 = decodingTable[data[i++]];
170b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
17170c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            if ((b1 | b2 | b3 | b4) < 0)
17270c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            {
17370c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom                throw new IOException("invalid characters encountered in base64 data");
17470c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            }
17570c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom
176b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b1 << 2) | (b2 >> 4));
177b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b2 << 4) | (b3 >> 2));
178b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b3 << 6) | b4);
179b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
180b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            outLen += 3;
181b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
182b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i = nextI(data, i, finish);
183b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
184b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
185b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        outLen += decodeLastBlock(out, (char)data[end - 4], (char)data[end - 3], (char)data[end - 2], (char)data[end - 1]);
186b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
187b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return outLen;
188b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
189b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
190b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private int nextI(byte[] data, int i, int finish)
191b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
192b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while ((i < finish) && ignore((char)data[i]))
193b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
194b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i++;
195b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
196b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return i;
197b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
198b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
199b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    /**
200b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * decode the base 64 encoded String data writing it to the given output stream,
201b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * whitespace characters will be ignored.
202b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     *
203b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     * @return the number of bytes produced.
204b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam     */
205b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    public int decode(
206b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        String          data,
207b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        OutputStream    out)
208b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
209b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
210b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte    b1, b2, b3, b4;
211b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int     length = 0;
212b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
213b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int     end = data.length();
214b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
215b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while (end > 0)
216b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
217b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            if (!ignore(data.charAt(end - 1)))
218b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            {
219b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam                break;
220b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            }
221b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
222b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            end--;
223b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
224b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
225b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int  i = 0;
226b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        int  finish = end - 4;
227b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
228b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        i = nextI(data, i, finish);
229b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
230b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while (i < finish)
231b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
232b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b1 = decodingTable[data.charAt(i++)];
233b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
234b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i = nextI(data, i, finish);
235b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
236b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b2 = decodingTable[data.charAt(i++)];
237b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
238b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i = nextI(data, i, finish);
239b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
240b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b3 = decodingTable[data.charAt(i++)];
241b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
242b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i = nextI(data, i, finish);
243b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
244b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b4 = decodingTable[data.charAt(i++)];
245b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
24670c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            if ((b1 | b2 | b3 | b4) < 0)
24770c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            {
24870c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom                throw new IOException("invalid characters encountered in base64 data");
24970c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            }
25070c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom
251b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b1 << 2) | (b2 >> 4));
252b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b2 << 4) | (b3 >> 2));
253b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b3 << 6) | b4);
254b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
255b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            length += 3;
256b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
257b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i = nextI(data, i, finish);
258b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
259b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
260b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        length += decodeLastBlock(out, data.charAt(end - 4), data.charAt(end - 3), data.charAt(end - 2), data.charAt(end - 1));
261b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
262b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return length;
263b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
264b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
265b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private int decodeLastBlock(OutputStream out, char c1, char c2, char c3, char c4)
266b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        throws IOException
267b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
268b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        byte    b1, b2, b3, b4;
269b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
270b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        if (c3 == padding)
271b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
272b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b1 = decodingTable[c1];
273b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b2 = decodingTable[c2];
274b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
27570c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            if ((b1 | b2) < 0)
27670c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            {
27770c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom                throw new IOException("invalid characters encountered at end of base64 data");
27870c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            }
27970c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom
280b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b1 << 2) | (b2 >> 4));
281b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
282b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return 1;
283b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
284b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        else if (c4 == padding)
285b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
286b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b1 = decodingTable[c1];
287b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b2 = decodingTable[c2];
288b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b3 = decodingTable[c3];
289b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
29070c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            if ((b1 | b2 | b3) < 0)
29170c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            {
29270c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom                throw new IOException("invalid characters encountered at end of base64 data");
29370c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            }
29470c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom
295b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b1 << 2) | (b2 >> 4));
296b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b2 << 4) | (b3 >> 2));
297b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
298b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return 2;
299b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
300b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        else
301b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
302b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b1 = decodingTable[c1];
303b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b2 = decodingTable[c2];
304b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b3 = decodingTable[c3];
305b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            b4 = decodingTable[c4];
306b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
30770c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            if ((b1 | b2 | b3 | b4) < 0)
30870c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            {
30970c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom                throw new IOException("invalid characters encountered at end of base64 data");
31070c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom            }
31170c8287138e69a98c2f950036f9f703ee37228c8Brian Carlstrom
312b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b1 << 2) | (b2 >> 4));
313b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b2 << 4) | (b3 >> 2));
314b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            out.write((b3 << 6) | b4);
315b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
316b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            return 3;
317b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
318b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
319b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam
320b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    private int nextI(String data, int i, int finish)
321b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    {
322b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        while ((i < finish) && ignore(data.charAt(i)))
323b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        {
324b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam            i++;
325b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        }
326b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam        return i;
327b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam    }
328b61a96e7ef1a78acf013bbf08fe537e5b5f129caPeter Hallam}
329