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