    1   /* RecyclingFastBufferedOutputStream
    2   *
    3   * $Id: RecyclingFastBufferedOutputStream.java 3527 2005-06-06 20:56:24Z gojomo $
    4   *
    5   * Created on May 26, 2005
    6   *
    7   * Based on FastBufferedOutputStream in MG4J; see:
    8   *
    9   *   http://mg4j.dsi.unimi.it/
    10  *
    11  * (Sole addition is one new constructor.)
    12  *
    13  * Revisions copyright (C) 2005 Internet Archive.
    14  *
    15  * This file is part of the Heritrix web crawler (crawler.archive.org).
    16  *
    17  * Heritrix is free software; you can redistribute it and/or modify
    18  * it under the terms of the GNU Lesser Public License as published by
    19  * the Free Software Foundation; either version 2.1 of the License, or
    20  * any later version.
    21  *
    22  * Heritrix is distributed in the hope that it will be useful,
    23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    25  * GNU Lesser Public License for more details.
    26  *
    27  * You should have received a copy of the GNU Lesser Public License
    28  * along with Heritrix; if not, write to the Free Software
    29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    30  */
    31  package org.archive.io;
    32
    33  import java.io.IOException;
    34  import java.io.OutputStream;
    35
    36  /*** Lightweight, unsynchronised, aligned output stream buffering class.
    37   *
    38   * <P>This class provides buffering for output streams, but it does so with
    39   * purposes and an internal logic that are radically different from the ones
    40   * adopted in {@link java.io.BufferedOutputStream}.
    41   *
    42   * <P>All methods are unsychronised. Moreover,
    43   * it is guaranteed that <em>all writes performed by this class will be
    44   * multiples of the given buffer size</em>.  If, for instance, you use the
    45   * default buffer size, writes will be performed on the underlying input stream
    46   * in multiples of 16384 bytes. This is very important on operating systems
    47   * that optimise disk reads on disk block boundaries.
    48   */
    49
    50  public class RecyclingFastBufferedOutputStream extends OutputStream {
    51
    52      /*** The default size of the internal buffer in bytes (16Ki). */
    53      public final static int DEFAULT_BUFFER_SIZE = 16 * 1024;
    54
    55      /*** The internal buffer. */
    56      protected byte buffer[];
    57
    58      /*** The current position in the buffer. */
    59      protected int pos;
    60
    61      /*** The number of buffer bytes available starting from {@link #pos}. */
    62      protected int avail;
    63
    64      /*** The underlying output stream. */
    65      protected OutputStream os;
    66
    67      /*** Creates a new fast buffered output stream by wrapping a given output stream, using a given buffer
    68      *
    69      * @param os an output stream to wrap.
    70      * @param buffer buffer to use internally.
    71      */
    72
    73     public RecyclingFastBufferedOutputStream( final OutputStream os, final byte[] buffer ) {
    74         this.os = os;
    75         this.buffer = buffer;
    76         avail = buffer.length;
    77     }
    78
    79      /*** Creates a new fast buffered output stream by wrapping a given output stream with a given buffer size.
    80       *
    81       * @param os an output stream to wrap.
    82       * @param bufSize the size in bytes of the internal buffer.
    83       */
    84
    85      public RecyclingFastBufferedOutputStream( final OutputStream os, final int bufSize ) {
    86          this(os, new byte [ bufSize]);
    87      }
    88
    89      /*** Creates a new fast buffered ouptut stream by wrapping a given output stream with a buffer of {@link #DEFAULT_BUFFER_SIZE} bytes.
    90       *
    91       * @param os an output stream to wrap.
    92       */
    93      public RecyclingFastBufferedOutputStream( final OutputStream os ) {
    94          this( os, DEFAULT_BUFFER_SIZE );
    95      }
    96
    97      private void dumpBufferIfFull() throws IOException {
    98          if ( avail == 0 ) {
    99              os.write( buffer );
    100             pos = 0;
    101             avail = buffer.length;
    102         }
    103     }
    104
    105     public void write( final int b ) throws IOException {
    106         avail--;
    107         buffer[ pos++ ] = (byte)b;
    108         dumpBufferIfFull();
    109     }
    110
    111
    112     public void write( final byte b[], int offset, int length ) throws IOException {
    113         if ( length <= avail ) {
    114             System.arraycopy( b, offset, buffer, pos, length );
    115             pos += length;
    116             avail -= length;
    117             dumpBufferIfFull();
    118             return;
    119         }
    120
    121         System.arraycopy( b, offset, buffer, pos, avail );
    122         os.write( buffer );
    123
    124         offset += avail;
    125         length -= avail;
    126
    127         final int residual = length % buffer.length;
    128
    129         os.write( b, offset, length - residual );
    130         System.arraycopy( b, offset + length - residual, buffer, 0, residual );
    131         pos = residual;
    132         avail = buffer.length - residual;
    133     }
    134
    135     public void close() throws IOException {
    136         if ( os == null ) return;
    137         if ( pos != 0 ) os.write( buffer, 0, pos );
    138         if ( os != System.out ) os.close();
    139         os = null;
    140         buffer = null;
    141     }
    142
    143 }
    144
    145
    146 // Local Variables:
    147 // mode: jde
    148 // tab-width: 4
    149 // End:
    150