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