1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.util; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 21 import java.io.FilterOutputStream; 22 import java.io.IOException; 23 import java.io.OutputStream; 24 25 /** 26 * An OutputStream that does Base64 encoding on the data written to 27 * it, writing the resulting data to another OutputStream. 28 */ 29 public class Base64OutputStream extends FilterOutputStream { 30 private final Base64.Coder coder; 31 private final int flags; 32 33 private byte[] buffer = null; 34 private int bpos = 0; 35 36 private static byte[] EMPTY = new byte[0]; 37 38 /** 39 * Performs Base64 encoding on the data written to the stream, 40 * writing the encoded data to another OutputStream. 41 * 42 * @param out the OutputStream to write the encoded data to 43 * @param flags bit flags for controlling the encoder; see the 44 * constants in {@link Base64} 45 */ Base64OutputStream(OutputStream out, int flags)46 public Base64OutputStream(OutputStream out, int flags) { 47 this(out, flags, true); 48 } 49 50 /** 51 * Performs Base64 encoding or decoding on the data written to the 52 * stream, writing the encoded/decoded data to another 53 * OutputStream. 54 * 55 * @param out the OutputStream to write the encoded data to 56 * @param flags bit flags for controlling the encoder; see the 57 * constants in {@link Base64} 58 * @param encode true to encode, false to decode 59 * 60 * @hide 61 */ 62 @UnsupportedAppUsage Base64OutputStream(OutputStream out, int flags, boolean encode)63 public Base64OutputStream(OutputStream out, int flags, boolean encode) { 64 super(out); 65 this.flags = flags; 66 if (encode) { 67 coder = new Base64.Encoder(flags, null); 68 } else { 69 coder = new Base64.Decoder(flags, null); 70 } 71 } 72 write(int b)73 public void write(int b) throws IOException { 74 // To avoid invoking the encoder/decoder routines for single 75 // bytes, we buffer up calls to write(int) in an internal 76 // byte array to transform them into writes of decently-sized 77 // arrays. 78 79 if (buffer == null) { 80 buffer = new byte[1024]; 81 } 82 if (bpos >= buffer.length) { 83 // internal buffer full; write it out. 84 internalWrite(buffer, 0, bpos, false); 85 bpos = 0; 86 } 87 buffer[bpos++] = (byte) b; 88 } 89 90 /** 91 * Flush any buffered data from calls to write(int). Needed 92 * before doing a write(byte[], int, int) or a close(). 93 */ flushBuffer()94 private void flushBuffer() throws IOException { 95 if (bpos > 0) { 96 internalWrite(buffer, 0, bpos, false); 97 bpos = 0; 98 } 99 } 100 write(byte[] b, int off, int len)101 public void write(byte[] b, int off, int len) throws IOException { 102 if (len <= 0) return; 103 flushBuffer(); 104 internalWrite(b, off, len, false); 105 } 106 close()107 public void close() throws IOException { 108 IOException thrown = null; 109 try { 110 flushBuffer(); 111 internalWrite(EMPTY, 0, 0, true); 112 } catch (IOException e) { 113 thrown = e; 114 } 115 116 try { 117 if ((flags & Base64.NO_CLOSE) == 0) { 118 out.close(); 119 } else { 120 out.flush(); 121 } 122 } catch (IOException e) { 123 if (thrown == null) { 124 thrown = e; 125 } else { 126 thrown.addSuppressed(e); 127 } 128 } 129 130 if (thrown != null) { 131 throw thrown; 132 } 133 } 134 135 /** 136 * Write the given bytes to the encoder/decoder. 137 * 138 * @param finish true if this is the last batch of input, to cause 139 * encoder/decoder state to be finalized. 140 */ internalWrite(byte[] b, int off, int len, boolean finish)141 private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException { 142 coder.output = embiggen(coder.output, coder.maxOutputSize(len)); 143 if (!coder.process(b, off, len, finish)) { 144 throw new Base64DataException("bad base-64"); 145 } 146 out.write(coder.output, 0, coder.op); 147 } 148 149 /** 150 * If b.length is at least len, return b. Otherwise return a new 151 * byte array of length len. 152 */ embiggen(byte[] b, int len)153 private byte[] embiggen(byte[] b, int len) { 154 if (b == null || b.length < len) { 155 return new byte[len]; 156 } else { 157 return b; 158 } 159 } 160 } 161