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 
24 package org.apache.james.mime4j.decoder;
25 
26 import java.io.IOException;
27 import 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  */
35 public 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 
Base64InputStream(InputStream s)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
close()53     public void close() throws IOException {
54         s.close();
55     }
56 
57     @Override
read()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      */
fillBuffer()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 
decodeAndEnqueue(int len)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