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