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