Home | History | Annotate | Download | only in io
      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