• 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 java.io.FilterOutputStream;
20 import java.io.IOException;
21 import java.io.OutputStream;
22 
23 /**
24  * An OutputStream that does Base64 encoding on the data written to
25  * it, writing the resulting data to another OutputStream.
26  */
27 public class Base64OutputStream extends FilterOutputStream {
28     private final Base64.Coder coder;
29     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      */
Base64OutputStream(OutputStream out, int flags)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
57      *
58      * @hide
59      */
Base64OutputStream(OutputStream out, int flags, boolean encode)60     public Base64OutputStream(OutputStream out, int flags, boolean encode) {
61         super(out);
62         this.flags = flags;
63         if (encode) {
64             coder = new Base64.Encoder(flags, null);
65         } else {
66             coder = new Base64.Decoder(flags, null);
67         }
68     }
69 
write(int b)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      */
flushBuffer()91     private void flushBuffer() throws IOException {
92         if (bpos > 0) {
93             internalWrite(buffer, 0, bpos, false);
94             bpos = 0;
95         }
96     }
97 
write(byte[] b, int off, int len)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 
close()104     public void close() throws IOException {
105         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) {
120             if (thrown != null) {
121                 thrown = e;
122             }
123         }
124 
125         if (thrown != null) {
126             throw thrown;
127         }
128     }
129 
130     /**
131      * Write the given bytes to the encoder/decoder.
132      *
133      * @param finish true if this is the last batch of input, to cause
134      *        encoder/decoder state to be finalized.
135      */
internalWrite(byte[] b, int off, int len, boolean finish)136     private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
137         coder.output = embiggen(coder.output, coder.maxOutputSize(len));
138         if (!coder.process(b, off, len, finish)) {
139             throw new Base64DataException("bad base-64");
140         }
141         out.write(coder.output, 0, coder.op);
142     }
143 
144     /**
145      * If b.length is at least len, return b.  Otherwise return a new
146      * byte array of length len.
147      */
embiggen(byte[] b, int len)148     private byte[] embiggen(byte[] b, int len) {
149         if (b == null || b.length < len) {
150             return new byte[len];
151         } else {
152             return b;
153         }
154     }
155 }
156