Home | History | Annotate | Download | only in io
      1 /*
      2  *  Licensed to the Apache Software Foundation (ASF) under one or more
      3  *  contributor license agreements.  See the NOTICE file distributed with
      4  *  this work for additional information regarding copyright ownership.
      5  *  The ASF licenses this file to You under the Apache License, Version 2.0
      6  *  (the "License"); you may not use this file except in compliance with
      7  *  the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  *  Unless required by applicable law or agreed to in writing, software
     12  *  distributed under the License is distributed on an "AS IS" BASIS,
     13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  *  See the License for the specific language governing permissions and
     15  *  limitations under the License.
     16  */
     17 
     18 package java.io;
     19 
     20 import java.util.Arrays;
     21 
     22 /**
     23  * Wraps an existing {@link OutputStream} and <em>buffers</em> the output.
     24  * Expensive interaction with the underlying input stream is minimized, since
     25  * most (smaller) requests can be satisfied by accessing the buffer alone. The
     26  * drawback is that some extra space is required to hold the buffer and that
     27  * copying takes place when flushing that buffer, but this is usually outweighed
     28  * by the performance benefits.
     29  *
     30  * <p/>A typical application pattern for the class looks like this:<p/>
     31  *
     32  * <pre>
     33  * BufferedOutputStream buf = new BufferedOutputStream(new FileOutputStream(&quot;file.java&quot;));
     34  * </pre>
     35  *
     36  * @see BufferedInputStream
     37  */
     38 public class BufferedOutputStream extends FilterOutputStream {
     39     /**
     40      * The buffer containing the bytes to be written to the target stream.
     41      */
     42     protected byte[] buf;
     43 
     44     /**
     45      * The total number of bytes inside the byte array {@code buf}.
     46      */
     47     protected int count;
     48 
     49     /**
     50      * Constructs a new {@code BufferedOutputStream}, providing {@code out} with a buffer
     51      * of 8192 bytes.
     52      *
     53      * @param out the {@code OutputStream} the buffer writes to.
     54      */
     55     public BufferedOutputStream(OutputStream out) {
     56         this(out, 8192);
     57     }
     58 
     59     /**
     60      * Constructs a new {@code BufferedOutputStream}, providing {@code out} with {@code size} bytes
     61      * of buffer.
     62      *
     63      * @param out the {@code OutputStream} the buffer writes to.
     64      * @param size the size of buffer in bytes.
     65      * @throws IllegalArgumentException if {@code size <= 0}.
     66      */
     67     public BufferedOutputStream(OutputStream out, int size) {
     68         super(out);
     69         if (size <= 0) {
     70             throw new IllegalArgumentException("size <= 0");
     71         }
     72         buf = new byte[size];
     73     }
     74 
     75     /**
     76      * Flushes this stream to ensure all pending data is written out to the
     77      * target stream. In addition, the target stream is flushed.
     78      *
     79      * @throws IOException
     80      *             if an error occurs attempting to flush this stream.
     81      */
     82     @Override
     83     public synchronized void flush() throws IOException {
     84         checkNotClosed();
     85         flushInternal();
     86         out.flush();
     87     }
     88 
     89     private void checkNotClosed() throws IOException {
     90         if (buf == null) {
     91             throw new IOException("BufferedOutputStream is closed");
     92         }
     93     }
     94 
     95     /**
     96      * Writes {@code count} bytes from the byte array {@code buffer} starting at
     97      * {@code offset} to this stream. If there is room in the buffer to hold the
     98      * bytes, they are copied in. If not, the buffered bytes plus the bytes in
     99      * {@code buffer} are written to the target stream, the target is flushed,
    100      * and the buffer is cleared.
    101      *
    102      * @param buffer
    103      *            the buffer to be written.
    104      * @param offset
    105      *            the start position in {@code buffer} from where to get bytes.
    106      * @param length
    107      *            the number of bytes from {@code buffer} to write to this
    108      *            stream.
    109      * @throws IndexOutOfBoundsException
    110      *             if {@code offset < 0} or {@code length < 0}, or if
    111      *             {@code offset + length} is greater than the size of
    112      *             {@code buffer}.
    113      * @throws IOException
    114      *             if an error occurs attempting to write to this stream.
    115      * @throws NullPointerException
    116      *             if {@code buffer} is {@code null}.
    117      * @throws ArrayIndexOutOfBoundsException
    118      *             If offset or count is outside of bounds.
    119      */
    120     @Override
    121     public synchronized void write(byte[] buffer, int offset, int length) throws IOException {
    122         checkNotClosed();
    123 
    124         if (buffer == null) {
    125             throw new NullPointerException("buffer == null");
    126         }
    127 
    128         byte[] internalBuffer = buf;
    129         if (length >= internalBuffer.length) {
    130             flushInternal();
    131             out.write(buffer, offset, length);
    132             return;
    133         }
    134 
    135         Arrays.checkOffsetAndCount(buffer.length, offset, length);
    136 
    137         // flush the internal buffer first if we have not enough space left
    138         if (length > (internalBuffer.length - count)) {
    139             flushInternal();
    140         }
    141 
    142         System.arraycopy(buffer, offset, internalBuffer, count, length);
    143         count += length;
    144     }
    145 
    146     @Override public synchronized void close() throws IOException {
    147         if (buf == null) {
    148             return;
    149         }
    150 
    151         try {
    152             super.close();
    153         } finally {
    154             buf = null;
    155         }
    156     }
    157 
    158     /**
    159      * Writes one byte to this stream. Only the low order byte of the integer
    160      * {@code oneByte} is written. If there is room in the buffer, the byte is
    161      * copied into the buffer and the count incremented. Otherwise, the buffer
    162      * plus {@code oneByte} are written to the target stream, the target is
    163      * flushed, and the buffer is reset.
    164      *
    165      * @param oneByte
    166      *            the byte to be written.
    167      * @throws IOException
    168      *             if an error occurs attempting to write to this stream.
    169      */
    170     @Override
    171     public synchronized void write(int oneByte) throws IOException {
    172         checkNotClosed();
    173         if (count == buf.length) {
    174             out.write(buf, 0, count);
    175             count = 0;
    176         }
    177         buf[count++] = (byte) oneByte;
    178     }
    179 
    180     /**
    181      * Flushes only internal buffer.
    182      */
    183     private void flushInternal() throws IOException {
    184         if (count > 0) {
    185             out.write(buf, 0, count);
    186             count = 0;
    187         }
    188     }
    189 }
    190