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 libcore.io;
18 
19 import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
20 
21 import android.annotation.SystemApi;
22 import android.compat.annotation.UnsupportedAppUsage;
23 
24 import java.io.ByteArrayOutputStream;
25 import java.io.EOFException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.io.Reader;
30 import java.io.StringWriter;
31 import java.util.concurrent.atomic.AtomicReference;
32 import libcore.util.ArrayUtils;
33 import libcore.util.NonNull;
34 import libcore.util.Nullable;
35 
36 /**
37  * Convenience class for reading and writing streams of bytes.
38  *
39  * @hide
40  */
41 @SystemApi(client = MODULE_LIBRARIES)
42 @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
43 public final class Streams {
44     private static AtomicReference<byte[]> skipBuffer = new AtomicReference<byte[]>();
45 
Streams()46     private Streams() {}
47 
48     /**
49      * Implements {@link InputStream#read(int)} in terms of {@link InputStream#read(byte[], int, int)}.
50      * {@link InputStream} assumes that you implement {@link InputStream#read(int)} and provides default
51      * implementations of the others, but often the opposite is more efficient.
52      *
53      * @param in {@link InputStream} to read byte from
54      * @return singlge byte read from {@code in}
55      * @throws IOException in case of I/O error
56      *
57      * @hide
58      */
59     @UnsupportedAppUsage
60     @SystemApi(client = MODULE_LIBRARIES)
61     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
readSingleByte(@onNull InputStream in)62     public static int readSingleByte(@NonNull InputStream in) throws IOException {
63         byte[] buffer = new byte[1];
64         int result = in.read(buffer, 0, 1);
65         return (result != -1) ? buffer[0] & 0xff : -1;
66     }
67 
68     /**
69      * Implements {@link OutputStream#write(int)} in terms of {@link OutputStream#write(byte[], int, int)}.
70      * {@link OutputStream} assumes that you implement {@link OutputStream#write(int)} and provides default
71      * implementations of the others, but often the opposite is more efficient.
72      *
73      * @param out {@link OutputStream} to write byte to
74      * @param b byte to write to stream {@code out}
75      * @throws IOException in case of I/O error
76      *
77      * @hide
78      */
79     @UnsupportedAppUsage
80     @SystemApi(client = MODULE_LIBRARIES)
81     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
writeSingleByte(@onNull OutputStream out, int b)82     public static void writeSingleByte(@NonNull OutputStream out, int b) throws IOException {
83         byte[] buffer = new byte[1];
84         buffer[0] = (byte) (b & 0xff);
85         out.write(buffer);
86     }
87 
88     /**
89      * Fills byte buffer {@code dst} with bytes from {@link InputStream} {@code in}, throwing
90      * {@link EOFException} if insufficient bytes are available.
91      *
92      * @param in {@link InputStream} to read data from
93      * @param dst byte buffer to write data to
94      * @throws IOException in case of I/O error
95      *
96      * @hide
97      */
98     @UnsupportedAppUsage
99     @SystemApi(client = MODULE_LIBRARIES)
100     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
readFully(@onNull InputStream in, @NonNull byte[] dst)101     public static void readFully(@NonNull InputStream in, @NonNull byte[] dst) throws IOException {
102         readFully(in, dst, 0, dst.length);
103     }
104 
105     /**
106      * Reads exactly 'byteCount' bytes from 'in' (into 'dst' at offset 'offset'), and throws
107      * EOFException if insufficient bytes are available.
108      *
109      * Used to implement {@link java.io.DataInputStream#readFully(byte[], int, int)}.
110      *
111      * @hide
112      */
readFully(InputStream in, byte[] dst, int offset, int byteCount)113     public static void readFully(InputStream in, byte[] dst, int offset, int byteCount) throws IOException {
114         if (byteCount == 0) {
115             return;
116         }
117         if (in == null) {
118             throw new NullPointerException("in == null");
119         }
120         if (dst == null) {
121             throw new NullPointerException("dst == null");
122         }
123         ArrayUtils.throwsIfOutOfBounds(dst.length, offset, byteCount);
124         while (byteCount > 0) {
125             int bytesRead = in.read(dst, offset, byteCount);
126             if (bytesRead < 0) {
127                 throw new EOFException();
128             }
129             offset += bytesRead;
130             byteCount -= bytesRead;
131         }
132     }
133 
134     /**
135      * Returns a {@code byte[]} containing the remainder of {@code in} stream and
136      * closes it. Also see {@link #readFullyNoClose(InputStream)}.
137      *
138      * @param in {@link InputStream} to read data from
139      * @return remaining bytes in {@code in} stream.
140      * @throws IOException thrown by {@link InputStream#read(byte[])}.
141      *
142      * @hide
143      */
144     @UnsupportedAppUsage
145     @SystemApi(client = MODULE_LIBRARIES)
146     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
readFully(@onNull InputStream in)147     public static @NonNull byte[] readFully(@NonNull InputStream in) throws IOException {
148         try {
149             return readFullyNoClose(in);
150         } finally {
151             in.close();
152         }
153     }
154 
155     /**
156      * Returns a {@code byte[]} containing the remainder of {@code in} stream, without
157      * closing it.
158      *
159      * @param in {@link InputStream} to read data from
160      * @return remaining bytes in {@code in} stream.
161      * @throws IOException thrown by {@link InputStream#read(byte[])}.
162      *
163      * @hide
164      */
165     @SystemApi(client = MODULE_LIBRARIES)
166     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
readFullyNoClose(@onNull InputStream in)167     public static @NonNull byte[] readFullyNoClose(@NonNull InputStream in) throws IOException {
168         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
169         byte[] buffer = new byte[1024];
170         int count;
171         while ((count = in.read(buffer)) != -1) {
172             bytes.write(buffer, 0, count);
173         }
174         return bytes.toByteArray();
175     }
176 
177     /**
178      * Reads the remainder of {@code reader} as a string, closing it when done.
179      *
180      * @param reader {@link Reader} instance.
181      * @return remainder of {@code reader} as {@link String}.
182      * @throws IOException thrown by {@link Reader#read(java.nio.CharBuffer)}.
183      *
184      * @hide
185      */
186     @SystemApi(client = MODULE_LIBRARIES)
187     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
readFully(@onNull Reader reader)188     public static @NonNull String readFully(@NonNull Reader reader) throws IOException {
189         try {
190             StringWriter writer = new StringWriter();
191             char[] buffer = new char[1024];
192             int count;
193             while ((count = reader.read(buffer)) != -1) {
194                 writer.write(buffer, 0, count);
195             }
196             return writer.toString();
197         } finally {
198             reader.close();
199         }
200     }
201 
202     /**
203      * @hide
204      */
205     @UnsupportedAppUsage
skipAll(InputStream in)206     public static void skipAll(InputStream in) throws IOException {
207         do {
208             in.skip(Long.MAX_VALUE);
209         } while (in.read() != -1);
210     }
211 
212     /**
213      * Skip <b>at most</b> {@code byteCount} bytes from {@code in} by calling read
214      * repeatedly until either the stream is exhausted or we read fewer bytes than
215      * we ask for.
216      *
217      * <p>This method reuses the skip buffer but is careful to never use it at
218      * the same time that another stream is using it. Otherwise streams that use
219      * the caller's buffer for consistency checks like CRC could be clobbered by
220      * other threads. A thread-local buffer is also insufficient because some
221      * streams may call other streams in their skip() method, also clobbering the
222      * buffer.
223      *
224      * @param in {@link InputStream} to skip data from
225      * @param byteCount number of bytes to skip from {@code in}
226      * @return number of bytes skipped from {@code in}
227      * @throws IOException in case of I/O error
228      *
229      * @hide
230      */
231     @SystemApi(client = MODULE_LIBRARIES)
232     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
skipByReading(@onNull InputStream in, long byteCount)233     public static long skipByReading(@NonNull InputStream in, long byteCount) throws IOException {
234         // acquire the shared skip buffer.
235         byte[] buffer = skipBuffer.getAndSet(null);
236         if (buffer == null) {
237             buffer = new byte[4096];
238         }
239 
240         long skipped = 0;
241         while (skipped < byteCount) {
242             int toRead = (int) Math.min(byteCount - skipped, buffer.length);
243             int read = in.read(buffer, 0, toRead);
244             if (read == -1) {
245                 break;
246             }
247             skipped += read;
248             if (read < toRead) {
249                 break;
250             }
251         }
252 
253         // release the shared skip buffer.
254         skipBuffer.set(buffer);
255 
256         return skipped;
257     }
258 
259     /**
260      * Copies all of the bytes from {@code in} to {@code out}. Neither stream is
261      * closed.
262      *
263      * @param in {@link InputStream} to copy data from
264      * @param out {@link InputStream} to write copied data to
265      * @return the total number of bytes transferred.
266      * @throws IOException reading from {@link InputStream} or writing to {@link OutputStream}.
267      *
268      * @hide
269      */
270     @UnsupportedAppUsage
271     @SystemApi(client = MODULE_LIBRARIES)
272     @libcore.api.CorePlatformApi(status = libcore.api.CorePlatformApi.Status.STABLE)
copy(@onNull InputStream in, @NonNull OutputStream out)273     public static int copy(@NonNull InputStream in, @NonNull OutputStream out) throws IOException {
274         int total = 0;
275         byte[] buffer = new byte[8192];
276         int c;
277         while ((c = in.read(buffer)) != -1) {
278             total += c;
279             out.write(buffer, 0, c);
280         }
281         return total;
282     }
283 
284     /**
285      * Returns the ASCII characters up to but not including the next "\r\n", or
286      * "\n".
287      *
288      * @throws java.io.EOFException if the stream is exhausted before the next newline
289      *     character.
290      *
291      * @hide
292      */
293     @UnsupportedAppUsage
readAsciiLine(InputStream in)294     public static String readAsciiLine(InputStream in) throws IOException {
295         // TODO: support UTF-8 here instead
296 
297         StringBuilder result = new StringBuilder(80);
298         while (true) {
299             int c = in.read();
300             if (c == -1) {
301                 throw new EOFException();
302             } else if (c == '\n') {
303                 break;
304             }
305 
306             result.append((char) c);
307         }
308         int length = result.length();
309         if (length > 0 && result.charAt(length - 1) == '\r') {
310             result.setLength(length - 1);
311         }
312         return result.toString();
313     }
314 }
315