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.FilterInputStream;
20d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongkerimport java.io.IOException;
21d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongkerimport java.io.InputStream;
22d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
23d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker/**
2433f7a80f767d72393c2d38b0dcbaeecdf7956c89Doug Zongker * An InputStream that does Base64 decoding on the data read through
2533f7a80f767d72393c2d38b0dcbaeecdf7956c89Doug Zongker * it.
26d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker */
27d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongkerpublic class Base64InputStream extends FilterInputStream {
289df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker    private final Base64.Coder coder;
29d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
30d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    private static byte[] EMPTY = new byte[0];
31d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
32d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    private static final int BUFFER_SIZE = 2048;
33d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    private boolean eof;
34d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    private byte[] inputBuffer;
35d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    private int outputStart;
36d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    private int outputEnd;
37d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
38d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    /**
39d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * An InputStream that performs Base64 decoding on the data read
40d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * from the wrapped stream.
41d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     *
42d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * @param in the InputStream to read the source data from
43d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * @param flags bit flags for controlling the decoder; see the
44d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     *        constants in {@link Base64}
45d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     */
46d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public Base64InputStream(InputStream in, int flags) {
47d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        this(in, flags, false);
48d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
49d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
50d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    /**
51d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * Performs Base64 encoding or decoding on the data read from the
52d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * wrapped InputStream.
53d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     *
54d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * @param in the InputStream to read the source data from
55d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * @param flags bit flags for controlling the decoder; see the
56d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     *        constants in {@link Base64}
57d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * @param encode true to encode, false to decode
5833f7a80f767d72393c2d38b0dcbaeecdf7956c89Doug Zongker     *
5933f7a80f767d72393c2d38b0dcbaeecdf7956c89Doug Zongker     * @hide
60d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     */
61d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public Base64InputStream(InputStream in, int flags, boolean encode) {
62d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        super(in);
63d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        eof = false;
64d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        inputBuffer = new byte[BUFFER_SIZE];
65d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        if (encode) {
669df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker            coder = new Base64.Encoder(flags, null);
67d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        } else {
689df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker            coder = new Base64.Decoder(flags, null);
69d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        }
709df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker        coder.output = new byte[coder.maxOutputSize(BUFFER_SIZE)];
71d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        outputStart = 0;
72d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        outputEnd = 0;
73d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
74d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
75d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public boolean markSupported() {
76d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        return false;
77d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
78d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
79d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public void mark(int readlimit) {
80d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        throw new UnsupportedOperationException();
81d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
82d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
83d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public void reset() {
84d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        throw new UnsupportedOperationException();
85d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
86d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
87d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public void close() throws IOException {
88d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        in.close();
89d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        inputBuffer = null;
90d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
91d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
92d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public int available() {
93d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        return outputEnd - outputStart;
94d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
95d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
96d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public long skip(long n) throws IOException {
97d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        if (outputStart >= outputEnd) {
98d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker            refill();
99d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        }
100d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        if (outputStart >= outputEnd) {
101d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker            return 0;
102d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        }
103d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        long bytes = Math.min(n, outputEnd-outputStart);
104d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        outputStart += bytes;
105d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        return bytes;
106d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
107d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
108d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public int read() throws IOException {
109d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        if (outputStart >= outputEnd) {
110d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker            refill();
111d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        }
112d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        if (outputStart >= outputEnd) {
113d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker            return -1;
114d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        } else {
11564b25cf736bc5f9b9f6d548f0694fb76fe805d0cJesse Wilson            return coder.output[outputStart++] & 0xff;
116d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        }
117d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
118d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
119d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    public int read(byte[] b, int off, int len) throws IOException {
120d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        if (outputStart >= outputEnd) {
121d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker            refill();
122d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        }
123d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        if (outputStart >= outputEnd) {
124d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker            return -1;
125d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        }
126d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        int bytes = Math.min(len, outputEnd-outputStart);
1279df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker        System.arraycopy(coder.output, outputStart, b, off, bytes);
128d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        outputStart += bytes;
129d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        return bytes;
130d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
131d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker
132d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    /**
133d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * Read data from the input stream into inputBuffer, then
1349df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker     * decode/encode it into the empty coder.output, and reset the
135d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     * outputStart and outputEnd pointers.
136d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker     */
137d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    private void refill() throws IOException {
138d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        if (eof) return;
139d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        int bytesRead = in.read(inputBuffer);
1409df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker        boolean success;
1419df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker        if (bytesRead == -1) {
1429df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker            eof = true;
1439df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker            success = coder.process(EMPTY, 0, 0, true);
144d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        } else {
1459df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker            success = coder.process(inputBuffer, 0, bytesRead, false);
1469df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker        }
1479df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker        if (!success) {
148c5a0ce242f3794972599163927b58bebf8c354b5Andy Stadler            throw new Base64DataException("bad base-64");
149d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        }
1509df2ffd4205115a68c4fe0651b2072e3e4573dd2Doug Zongker        outputEnd = coder.op;
151d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker        outputStart = 0;
152d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker    }
153d2affae13dfdb116adaee1bb10aaaac80a885481Doug Zongker}
154