Base64InputStream.java revision 629e18ad866917523599ed059898f4d986e49e26
1/****************************************************************
2 * Licensed to the Apache Software Foundation (ASF) under one   *
3 * or more contributor license agreements.  See the NOTICE file *
4 * distributed with this work for additional information        *
5 * regarding copyright ownership.  The ASF licenses this file   *
6 * to you under the Apache License, Version 2.0 (the            *
7 * "License"); you may not use this file except in compliance   *
8 * with the License.  You may obtain a copy of the License at   *
9 *                                                              *
10 *   http://www.apache.org/licenses/LICENSE-2.0                 *
11 *                                                              *
12 * Unless required by applicable law or agreed to in writing,   *
13 * software distributed under the License is distributed on an  *
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15 * KIND, either express or implied.  See the License for the    *
16 * specific language governing permissions and limitations      *
17 * under the License.                                           *
18 ****************************************************************/
19
20/**
21 * Modified to improve efficiency by Android   21-Aug-2009
22 */
23
24package org.apache.james.mime4j.decoder;
25
26import java.io.IOException;
27import java.io.InputStream;
28
29/**
30 * Performs Base-64 decoding on an underlying stream.
31 *
32 *
33 * @version $Id: Base64InputStream.java,v 1.3 2004/11/29 13:15:47 ntherning Exp $
34 */
35public class Base64InputStream extends InputStream {
36    private final InputStream s;
37    private int outCount = 0;
38    private int outIndex = 0;
39    private final int[] outputBuffer = new int[3];
40    private final byte[] inputBuffer = new byte[4];
41    private boolean done = false;
42
43    public Base64InputStream(InputStream s) {
44        this.s = s;
45    }
46
47    /**
48     * Closes the underlying stream.
49     *
50     * @throws IOException on I/O errors.
51     */
52    @Override
53    public void close() throws IOException {
54        s.close();
55    }
56
57    @Override
58    public int read() throws IOException {
59        if (outIndex == outCount) {
60            fillBuffer();
61            if (outIndex == outCount) {
62                return -1;
63            }
64        }
65
66        return outputBuffer[outIndex++];
67    }
68
69    /**
70     * Retrieve data from the underlying stream, decode it,
71     * and put the results in the byteq.
72     * @throws IOException
73     */
74    private void fillBuffer() throws IOException {
75        outCount = 0;
76        outIndex = 0;
77        int inCount = 0;
78
79        int i;
80        // "done" is needed for the two successive '=' at the end
81        while (!done) {
82            switch (i = s.read()) {
83                case -1:
84                    // No more input - just return, let outputBuffer drain out, and be done
85                    return;
86                case '=':
87                    // once we meet the first '=', avoid reading the second '='
88                    done = true;
89                    decodeAndEnqueue(inCount);
90                    return;
91                default:
92                    byte sX = TRANSLATION[i];
93                    if (sX < 0) continue;
94                    inputBuffer[inCount++] = sX;
95                    if (inCount == 4) {
96                        decodeAndEnqueue(inCount);
97                        return;
98                    }
99                    break;
100            }
101        }
102    }
103
104    private void decodeAndEnqueue(int len) {
105        int accum = 0;
106        accum |= inputBuffer[0] << 18;
107        accum |= inputBuffer[1] << 12;
108        accum |= inputBuffer[2] << 6;
109        accum |= inputBuffer[3];
110
111        // There's a bit of duplicated code here because we want to have straight-through operation
112        // for the most common case of len==4
113        if (len == 4) {
114            outputBuffer[0] = (accum >> 16) & 0xFF;
115            outputBuffer[1] = (accum >> 8) & 0xFF;
116            outputBuffer[2] = (accum) & 0xFF;
117            outCount = 3;
118            return;
119        } else if (len == 3) {
120            outputBuffer[0] = (accum >> 16) & 0xFF;
121            outputBuffer[1] = (accum >> 8) & 0xFF;
122            outCount = 2;
123            return;
124        } else {    // len == 2
125            outputBuffer[0] = (accum >> 16) & 0xFF;
126            outCount = 1;
127            return;
128        }
129    }
130
131    private static byte[] TRANSLATION = {
132        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x00 */
133        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x10 */
134        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, /* 0x20 */
135        52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, /* 0x30 */
136        -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 0x40 */
137        15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 0x50 */
138        -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 0x60 */
139        41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, /* 0x70 */
140        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x80 */
141        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0x90 */
142        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xA0 */
143        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xB0 */
144        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xC0 */
145        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xD0 */
146        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 0xE0 */
147        -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1     /* 0xF0 */
148    };
149
150
151}
152