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