blob: 230a3a58d5b5f537a88bcb3bb039970d7371c4f9 [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
Mathew Inwood4eb56ab2018-08-14 17:24:32 +010019import android.annotation.UnsupportedAppUsage;
Doug Zongkerd2affae2010-02-12 15:50:01 -080020import java.io.FilterOutputStream;
21import java.io.IOException;
22import java.io.OutputStream;
23
24/**
Doug Zongker33f7a802010-02-12 17:41:40 -080025 * An OutputStream that does Base64 encoding on the data written to
26 * it, writing the resulting data to another OutputStream.
Doug Zongkerd2affae2010-02-12 15:50:01 -080027 */
28public class Base64OutputStream extends FilterOutputStream {
Doug Zongker9df2ffd2010-02-14 13:48:49 -080029 private final Base64.Coder coder;
Doug Zongkerd2affae2010-02-12 15:50:01 -080030 private final int flags;
31
32 private byte[] buffer = null;
33 private int bpos = 0;
34
35 private static byte[] EMPTY = new byte[0];
36
37 /**
38 * Performs Base64 encoding on the data written to the stream,
39 * writing the encoded data to another OutputStream.
40 *
41 * @param out the OutputStream to write the encoded data to
42 * @param flags bit flags for controlling the encoder; see the
43 * constants in {@link Base64}
44 */
45 public Base64OutputStream(OutputStream out, int flags) {
46 this(out, flags, true);
47 }
48
49 /**
50 * Performs Base64 encoding or decoding on the data written to the
51 * stream, writing the encoded/decoded data to another
52 * OutputStream.
53 *
54 * @param out the OutputStream to write the encoded data to
55 * @param flags bit flags for controlling the encoder; see the
56 * constants in {@link Base64}
57 * @param encode true to encode, false to decode
Doug Zongker33f7a802010-02-12 17:41:40 -080058 *
59 * @hide
Doug Zongkerd2affae2010-02-12 15:50:01 -080060 */
Mathew Inwood4eb56ab2018-08-14 17:24:32 +010061 @UnsupportedAppUsage
Doug Zongkerd2affae2010-02-12 15:50:01 -080062 public Base64OutputStream(OutputStream out, int flags, boolean encode) {
63 super(out);
64 this.flags = flags;
Doug Zongkerd2affae2010-02-12 15:50:01 -080065 if (encode) {
Doug Zongker9df2ffd2010-02-14 13:48:49 -080066 coder = new Base64.Encoder(flags, null);
Doug Zongkerd2affae2010-02-12 15:50:01 -080067 } else {
Doug Zongker9df2ffd2010-02-14 13:48:49 -080068 coder = new Base64.Decoder(flags, null);
Doug Zongkerd2affae2010-02-12 15:50:01 -080069 }
70 }
71
72 public void write(int b) throws IOException {
73 // To avoid invoking the encoder/decoder routines for single
74 // bytes, we buffer up calls to write(int) in an internal
75 // byte array to transform them into writes of decently-sized
76 // arrays.
77
78 if (buffer == null) {
79 buffer = new byte[1024];
80 }
81 if (bpos >= buffer.length) {
82 // internal buffer full; write it out.
83 internalWrite(buffer, 0, bpos, false);
84 bpos = 0;
85 }
86 buffer[bpos++] = (byte) b;
87 }
88
89 /**
90 * Flush any buffered data from calls to write(int). Needed
91 * before doing a write(byte[], int, int) or a close().
92 */
93 private void flushBuffer() throws IOException {
94 if (bpos > 0) {
95 internalWrite(buffer, 0, bpos, false);
96 bpos = 0;
97 }
98 }
99
100 public void write(byte[] b, int off, int len) throws IOException {
101 if (len <= 0) return;
102 flushBuffer();
103 internalWrite(b, off, len, false);
104 }
105
106 public void close() throws IOException {
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800107 IOException thrown = null;
108 try {
109 flushBuffer();
110 internalWrite(EMPTY, 0, 0, true);
111 } catch (IOException e) {
112 thrown = e;
113 }
114
115 try {
116 if ((flags & Base64.NO_CLOSE) == 0) {
117 out.close();
118 } else {
119 out.flush();
120 }
121 } catch (IOException e) {
Tobias Thierer97c4d132018-07-23 12:53:00 +0100122 if (thrown == null) {
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800123 thrown = e;
Tobias Thierer97c4d132018-07-23 12:53:00 +0100124 } else {
125 thrown.addSuppressed(e);
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800126 }
127 }
128
129 if (thrown != null) {
130 throw thrown;
Doug Zongkerd2affae2010-02-12 15:50:01 -0800131 }
132 }
133
134 /**
135 * Write the given bytes to the encoder/decoder.
136 *
137 * @param finish true if this is the last batch of input, to cause
138 * encoder/decoder state to be finalized.
139 */
140 private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800141 coder.output = embiggen(coder.output, coder.maxOutputSize(len));
142 if (!coder.process(b, off, len, finish)) {
Andy Stadlerc5a0ce22011-01-24 16:47:56 -0800143 throw new Base64DataException("bad base-64");
Doug Zongkerd2affae2010-02-12 15:50:01 -0800144 }
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800145 out.write(coder.output, 0, coder.op);
Doug Zongkerd2affae2010-02-12 15:50:01 -0800146 }
147
148 /**
149 * If b.length is at least len, return b. Otherwise return a new
150 * byte array of length len.
151 */
152 private byte[] embiggen(byte[] b, int len) {
153 if (b == null || b.length < len) {
154 return new byte[len];
155 } else {
156 return b;
157 }
158 }
159}