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