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 * Skip <b>at most</b> {@code byteCount} bytes from {@code in} by calling read 139 * repeatedly until either the stream is exhausted or we read fewer bytes than 140 * we ask for. 141 * 142 * <p>This method reuses the skip buffer but is careful to never use it at 143 * the same time that another stream is using it. Otherwise streams that use 144 * the caller's buffer for consistency checks like CRC could be clobbered by 145 * other threads. A thread-local buffer is also insufficient because some 146 * streams may call other streams in their skip() method, also clobbering the 147 * buffer. 148 */ skipByReading(InputStream in, long byteCount)149 public static long skipByReading(InputStream in, long byteCount) throws IOException { 150 // acquire the shared skip buffer. 151 byte[] buffer = skipBuffer.getAndSet(null); 152 if (buffer == null) { 153 buffer = new byte[4096]; 154 } 155 156 long skipped = 0; 157 while (skipped < byteCount) { 158 int toRead = (int) Math.min(byteCount - skipped, buffer.length); 159 int read = in.read(buffer, 0, toRead); 160 if (read == -1) { 161 break; 162 } 163 skipped += read; 164 if (read < toRead) { 165 break; 166 } 167 } 168 169 // release the shared skip buffer. 170 skipBuffer.set(buffer); 171 172 return skipped; 173 } 174 175 /** 176 * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed. 177 * Returns the total number of bytes transferred. 178 */ copy(InputStream in, OutputStream out)179 public static int copy(InputStream in, OutputStream out) throws IOException { 180 int total = 0; 181 byte[] buffer = new byte[8192]; 182 int c; 183 while ((c = in.read(buffer)) != -1) { 184 total += c; 185 out.write(buffer, 0, c); 186 } 187 return total; 188 } 189 190 /** 191 * Returns the ASCII characters up to but not including the next "\r\n", or 192 * "\n". 193 * 194 * @throws java.io.EOFException if the stream is exhausted before the next newline 195 * character. 196 */ readAsciiLine(InputStream in)197 public static String readAsciiLine(InputStream in) throws IOException { 198 // TODO: support UTF-8 here instead 199 200 StringBuilder result = new StringBuilder(80); 201 while (true) { 202 int c = in.read(); 203 if (c == -1) { 204 throw new EOFException(); 205 } else if (c == '\n') { 206 break; 207 } 208 209 result.append((char) c); 210 } 211 int length = result.length(); 212 if (length > 0 && result.charAt(length - 1) == '\r') { 213 result.setLength(length - 1); 214 } 215 return result.toString(); 216 } 217 } 218