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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 */ 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 */ 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 */ 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