• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.UnsupportedAppUsage;
20 import java.io.FilterOutputStream;
21 import java.io.IOException;
22 import java.io.OutputStream;
23 
24 /**
25  * An OutputStream that does Base64 encoding on the data written to
26  * it, writing the resulting data to another OutputStream.
27  */
28 public class Base64OutputStream extends FilterOutputStream {
29     private final Base64.Coder coder;
30     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      */
Base64OutputStream(OutputStream out, int flags)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
58      *
59      * @hide
60      */
61     @UnsupportedAppUsage
Base64OutputStream(OutputStream out, int flags, boolean encode)62     public Base64OutputStream(OutputStream out, int flags, boolean encode) {
63         super(out);
64         this.flags = flags;
65         if (encode) {
66             coder = new Base64.Encoder(flags, null);
67         } else {
68             coder = new Base64.Decoder(flags, null);
69         }
70     }
71 
write(int b)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      */
flushBuffer()93     private void flushBuffer() throws IOException {
94         if (bpos > 0) {
95             internalWrite(buffer, 0, bpos, false);
96             bpos = 0;
97         }
98     }
99 
write(byte[] b, int off, int len)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 
close()106     public void close() throws IOException {
107         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) {
122             if (thrown == null) {
123                 thrown = e;
124             } else {
125                 thrown.addSuppressed(e);
126             }
127         }
128 
129         if (thrown != null) {
130             throw thrown;
131         }
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      */
internalWrite(byte[] b, int off, int len, boolean finish)140     private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
141         coder.output = embiggen(coder.output, coder.maxOutputSize(len));
142         if (!coder.process(b, off, len, finish)) {
143             throw new Base64DataException("bad base-64");
144         }
145         out.write(coder.output, 0, coder.op);
146     }
147 
148     /**
149      * If b.length is at least len, return b.  Otherwise return a new
150      * byte array of length len.
151      */
embiggen(byte[] b, int len)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 }
160