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