blob: 9eba5b5891be8225437b5cc89e71c3046d993241 [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
19import java.io.FilterInputStream;
20import java.io.IOException;
21import java.io.InputStream;
22
23/**
Doug Zongker33f7a802010-02-12 17:41:40 -080024 * An InputStream that does Base64 decoding on the data read through
25 * it.
Doug Zongkerd2affae2010-02-12 15:50:01 -080026 */
27public class Base64InputStream extends FilterInputStream {
Doug Zongker9df2ffd2010-02-14 13:48:49 -080028 private final Base64.Coder coder;
Doug Zongkerd2affae2010-02-12 15:50:01 -080029
30 private static byte[] EMPTY = new byte[0];
31
32 private static final int BUFFER_SIZE = 2048;
33 private boolean eof;
34 private byte[] inputBuffer;
Doug Zongkerd2affae2010-02-12 15:50:01 -080035 private int outputStart;
36 private int outputEnd;
37
38 /**
39 * An InputStream that performs Base64 decoding on the data read
40 * from the wrapped stream.
41 *
42 * @param in the InputStream to read the source data from
43 * @param flags bit flags for controlling the decoder; see the
44 * constants in {@link Base64}
45 */
46 public Base64InputStream(InputStream in, int flags) {
47 this(in, flags, false);
48 }
49
50 /**
51 * Performs Base64 encoding or decoding on the data read from the
52 * wrapped InputStream.
53 *
54 * @param in the InputStream to read the source data from
55 * @param flags bit flags for controlling the decoder; 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 */
61 public Base64InputStream(InputStream in, int flags, boolean encode) {
62 super(in);
Doug Zongkerd2affae2010-02-12 15:50:01 -080063 eof = false;
64 inputBuffer = new byte[BUFFER_SIZE];
65 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 }
Doug Zongker9df2ffd2010-02-14 13:48:49 -080070 coder.output = new byte[coder.maxOutputSize(BUFFER_SIZE)];
Doug Zongkerd2affae2010-02-12 15:50:01 -080071 outputStart = 0;
72 outputEnd = 0;
73 }
74
75 public boolean markSupported() {
76 return false;
77 }
78
79 public void mark(int readlimit) {
80 throw new UnsupportedOperationException();
81 }
82
83 public void reset() {
84 throw new UnsupportedOperationException();
85 }
86
87 public void close() throws IOException {
88 in.close();
89 inputBuffer = null;
90 }
91
92 public int available() {
93 return outputEnd - outputStart;
94 }
95
96 public long skip(long n) throws IOException {
97 if (outputStart >= outputEnd) {
98 refill();
99 }
100 if (outputStart >= outputEnd) {
101 return 0;
102 }
103 long bytes = Math.min(n, outputEnd-outputStart);
104 outputStart += bytes;
105 return bytes;
106 }
107
108 public int read() throws IOException {
109 if (outputStart >= outputEnd) {
110 refill();
111 }
112 if (outputStart >= outputEnd) {
113 return -1;
114 } else {
Jesse Wilson64b25cf2010-09-22 10:28:27 -0700115 return coder.output[outputStart++] & 0xff;
Doug Zongkerd2affae2010-02-12 15:50:01 -0800116 }
117 }
118
119 public int read(byte[] b, int off, int len) throws IOException {
120 if (outputStart >= outputEnd) {
121 refill();
122 }
123 if (outputStart >= outputEnd) {
124 return -1;
125 }
126 int bytes = Math.min(len, outputEnd-outputStart);
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800127 System.arraycopy(coder.output, outputStart, b, off, bytes);
Doug Zongkerd2affae2010-02-12 15:50:01 -0800128 outputStart += bytes;
129 return bytes;
130 }
131
132 /**
133 * Read data from the input stream into inputBuffer, then
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800134 * decode/encode it into the empty coder.output, and reset the
Doug Zongkerd2affae2010-02-12 15:50:01 -0800135 * outputStart and outputEnd pointers.
136 */
137 private void refill() throws IOException {
138 if (eof) return;
139 int bytesRead = in.read(inputBuffer);
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800140 boolean success;
141 if (bytesRead == -1) {
142 eof = true;
143 success = coder.process(EMPTY, 0, 0, true);
Doug Zongkerd2affae2010-02-12 15:50:01 -0800144 } else {
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800145 success = coder.process(inputBuffer, 0, bytesRead, false);
Doug Zongkerd2affae2010-02-12 15:50:01 -0800146 }
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800147 if (!success) {
Andy Stadlerc5a0ce22011-01-24 16:47:56 -0800148 throw new Base64DataException("bad base-64");
Doug Zongker9df2ffd2010-02-14 13:48:49 -0800149 }
150 outputEnd = coder.op;
Doug Zongkerd2affae2010-02-12 15:50:01 -0800151 outputStart = 0;
152 }
153}