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