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