blob: 837870546c197d549699d7cc3ebb0a8144b8b66f [file] [log] [blame]
Doug Zongkerd2affae2010-02-12 15:50:01 -08001/*
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
Doug Zongkerab69e292010-03-29 13:23:15 -070017package android.util;
Doug Zongkerd2affae2010-02-12 15:50:01 -080018
19import java.io.FilterOutputStream;
20import java.io.IOException;
21import java.io.OutputStream;
22
23/**
Doug Zongker33f7a802010-02-12 17:41:40 -080024 * An OutputStream that does Base64 encoding on the data written to
25 * it, writing the resulting data to another OutputStream.
Doug Zongkerd2affae2010-02-12 15:50:01 -080026 */
27public class Base64OutputStream extends FilterOutputStream {
Doug Zongker9df2ffd2010-02-14 13:48:49 -080028 private final Base64.Coder coder;
Doug Zongkerd2affae2010-02-12 15:50:01 -080029 private final int flags;
30
31 private byte[] buffer = null;
32 private int bpos = 0;
33
34 private static byte[] EMPTY = new byte[0];
35
36 /**
37 * Performs Base64 encoding on the data written to the stream,
38 * writing the encoded data to another OutputStream.
39 *
40 * @param out the OutputStream to write the encoded data to
41 * @param flags bit flags for controlling the encoder; see the
42 * constants in {@link Base64}
43 */
44 public Base64OutputStream(OutputStream out, int flags) {
45 this(out, flags, true);
46 }
47
48 /**
49 * Performs Base64 encoding or decoding on the data written to the
50 * stream, writing the encoded/decoded data to another
51 * OutputStream.
52 *
53 * @param out the OutputStream to write the encoded data to
54 * @param flags bit flags for controlling the encoder; see the
55 * constants in {@link Base64}
56 * @param encode true to encode, false to decode
Doug Zongker33f7a802010-02-12 17:41:40 -080057 *
58 * @hide
Doug Zongkerd2affae2010-02-12 15:50:01 -080059 */
60 public Base64OutputStream(OutputStream out, int flags, boolean encode) {
61 super(out);
62 this.flags = flags;
Doug Zongkerd2affae2010-02-12 15:50:01 -080063 if (encode) {
Doug Zongker9df2ffd2010-02-14 13:48:49 -080064 coder = new Base64.Encoder(flags, null);
Doug Zongkerd2affae2010-02-12 15:50:01 -080065 } else {
Doug Zongker9df2ffd2010-02-14 13:48:49 -080066 coder = new Base64.Decoder(flags, null);
Doug Zongkerd2affae2010-02-12 15:50:01 -080067 }
68 }
69
70 public void write(int b) throws IOException {
71 // To avoid invoking the encoder/decoder routines for single
72 // bytes, we buffer up calls to write(int) in an internal
73 // byte array to transform them into writes of decently-sized
74 // arrays.
75
76 if (buffer == null) {
77 buffer = new byte[1024];
78 }
79 if (bpos >= buffer.length) {
80 // internal buffer full; write it out.
81 internalWrite(buffer, 0, bpos, false);
82 bpos = 0;
83 }
84 buffer[bpos++] = (byte) b;
85 }
86
87 /**
88 * Flush any buffered data from calls to write(int). Needed
89 * before doing a write(byte[], int, int) or a close().
90 */
91 private void flushBuffer() throws IOException {
92 if (bpos > 0) {
93 internalWrite(buffer, 0, bpos, false);
94 bpos = 0;
95 }
96 }
97
98 public void write(byte[] b, int off, int len) throws IOException {
99 if (len <= 0) return;
100 flushBuffer();
101 internalWrite(b, off, len, false);
102 }
103
104 public void close() throws IOException {
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800105 IOException thrown = null;
106 try {
107 flushBuffer();
108 internalWrite(EMPTY, 0, 0, true);
109 } catch (IOException e) {
110 thrown = e;
111 }
112
113 try {
114 if ((flags & Base64.NO_CLOSE) == 0) {
115 out.close();
116 } else {
117 out.flush();
118 }
119 } catch (IOException e) {
Tobias Thierer97c4d132018-07-23 12:53:00 +0100120 if (thrown == null) {
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800121 thrown = e;
Tobias Thierer97c4d132018-07-23 12:53:00 +0100122 } else {
123 thrown.addSuppressed(e);
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800124 }
125 }
126
127 if (thrown != null) {
128 throw thrown;
Doug Zongkerd2affae2010-02-12 15:50:01 -0800129 }
130 }
131
132 /**
133 * Write the given bytes to the encoder/decoder.
134 *
135 * @param finish true if this is the last batch of input, to cause
136 * encoder/decoder state to be finalized.
137 */
138 private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800139 coder.output = embiggen(coder.output, coder.maxOutputSize(len));
140 if (!coder.process(b, off, len, finish)) {
Andy Stadlerc5a0ce22011-01-24 16:47:56 -0800141 throw new Base64DataException("bad base-64");
Doug Zongkerd2affae2010-02-12 15:50:01 -0800142 }
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800143 out.write(coder.output, 0, coder.op);
Doug Zongkerd2affae2010-02-12 15:50:01 -0800144 }
145
146 /**
147 * If b.length is at least len, return b. Otherwise return a new
148 * byte array of length len.
149 */
150 private byte[] embiggen(byte[] b, int len) {
151 if (b == null || b.length < len) {
152 return new byte[len];
153 } else {
154 return b;
155 }
156 }
157}