Save This Page
Home » apache-harmony-6.0-src-r917296-snapshot » java » io » [javadoc | source]
    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 org.apache.harmony.luni.internal.nls.Messages;
   21   
   22   /**
   23    * Wraps an existing {@link InputStream} and <em>buffers</em> the input.
   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 filling 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    * BufferedInputStream buf = new BufferedInputStream(new FileInputStream(&quot;file.java&quot;));
   34    * </pre>
   35    *
   36    * @see BufferedOutputStream
   37    */
   38   public class BufferedInputStream extends FilterInputStream {
   39       /**
   40        * The buffer containing the current bytes read from the target InputStream.
   41        */
   42       protected volatile byte[] buf;
   43   
   44       /**
   45        * The total number of bytes inside the byte array {@code buf}.
   46        */
   47       protected int count;
   48   
   49       /**
   50        * The current limit, which when passed, invalidates the current mark.
   51        */
   52       protected int marklimit;
   53   
   54       /**
   55        * The currently marked position. -1 indicates no mark has been set or the
   56        * mark has been invalidated.
   57        */
   58       protected int markpos = -1;
   59   
   60       /**
   61        * The current position within the byte array {@code buf}.
   62        */
   63       protected int pos;
   64   
   65       /**
   66        * Constructs a new {@code BufferedInputStream} on the {@link InputStream}
   67        * {@code in}. The default buffer size (8 KB) is allocated and all reads
   68        * can now be filtered through this stream.
   69        * 
   70        * @param in
   71        *            the InputStream the buffer reads from.
   72        */
   73       public BufferedInputStream(InputStream in) {
   74           super(in);
   75           buf = new byte[8192];
   76       }
   77   
   78       /**
   79        * Constructs a new {@code BufferedInputStream} on the {@link InputStream}
   80        * {@code in}. The buffer size is specified by the parameter {@code size}
   81        * and all reads are now filtered through this stream.
   82        * 
   83        * @param in
   84        *            the input stream the buffer reads from.
   85        * @param size
   86        *            the size of buffer to allocate.
   87        * @throws IllegalArgumentException
   88        *             if {@code size < 0}.
   89        */
   90       public BufferedInputStream(InputStream in, int size) {
   91           super(in);
   92           if (size <= 0) {
   93               // luni.A3=size must be > 0
   94               throw new IllegalArgumentException(Messages.getString("luni.A3")); //$NON-NLS-1$
   95           }
   96           buf = new byte[size];
   97       }
   98   
   99       /**
  100        * Returns the number of bytes that are available before this stream will
  101        * block. This method returns the number of bytes available in the buffer
  102        * plus those available in the source stream.
  103        * 
  104        * @return the number of bytes available before blocking.
  105        * @throws IOException
  106        *             if this stream is closed.
  107        */
  108       @Override
  109       public synchronized int available() throws IOException {
  110           InputStream localIn = in; // 'in' could be invalidated by close()
  111           if (buf == null || localIn == null) {
  112               // luni.24=Stream is closed
  113               throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
  114           }
  115           return count - pos + localIn.available();
  116       }
  117   
  118       /**
  119        * Closes this stream. The source stream is closed and any resources
  120        * associated with it are released.
  121        * 
  122        * @throws IOException
  123        *             if an error occurs while closing this stream.
  124        */
  125       @Override
  126       public void close() throws IOException {
  127           buf = null;
  128           InputStream localIn = in;
  129           in = null;
  130           if (localIn != null) {
  131               localIn.close();
  132           }
  133       }
  134   
  135       private int fillbuf(InputStream localIn, byte[] localBuf)
  136               throws IOException {
  137           if (markpos == -1 || (pos - markpos >= marklimit)) {
  138               /* Mark position not set or exceeded readlimit */
  139               int result = localIn.read(localBuf);
  140               if (result > 0) {
  141                   markpos = -1;
  142                   pos = 0;
  143                   count = result == -1 ? 0 : result;
  144               }
  145               return result;
  146           }
  147           if (markpos == 0 && marklimit > localBuf.length) {
  148               /* Increase buffer size to accommodate the readlimit */
  149               int newLength = localBuf.length * 2;
  150               if (newLength > marklimit) {
  151                   newLength = marklimit;
  152               }
  153               byte[] newbuf = new byte[newLength];
  154               System.arraycopy(localBuf, 0, newbuf, 0, localBuf.length);
  155               // Reassign buf, which will invalidate any local references
  156               // FIXME: what if buf was null?
  157               localBuf = buf = newbuf;
  158           } else if (markpos > 0) {
  159               System.arraycopy(localBuf, markpos, localBuf, 0, localBuf.length
  160                       - markpos);
  161           }
  162           /* Set the new position and mark position */
  163           pos -= markpos;
  164           count = markpos = 0;
  165           int bytesread = localIn.read(localBuf, pos, localBuf.length - pos);
  166           count = bytesread <= 0 ? pos : pos + bytesread;
  167           return bytesread;
  168       }
  169   
  170       /**
  171        * Sets a mark position in this stream. The parameter {@code readlimit}
  172        * indicates how many bytes can be read before a mark is invalidated.
  173        * Calling {@code reset()} will reposition the stream back to the marked
  174        * position if {@code readlimit} has not been surpassed. The underlying
  175        * buffer may be increased in size to allow {@code readlimit} number of
  176        * bytes to be supported.
  177        * 
  178        * @param readlimit
  179        *            the number of bytes that can be read before the mark is
  180        *            invalidated.
  181        * @see #reset()
  182        */
  183       @Override
  184       public synchronized void mark(int readlimit) {
  185           marklimit = readlimit;
  186           markpos = pos;
  187       }
  188   
  189       /**
  190        * Indicates whether {@code BufferedInputStream} supports the {@code mark()}
  191        * and {@code reset()} methods.
  192        * 
  193        * @return {@code true} for BufferedInputStreams.
  194        * @see #mark(int)
  195        * @see #reset()
  196        */
  197       @Override
  198       public boolean markSupported() {
  199           return true;
  200       }
  201   
  202       /**
  203        * Reads a single byte from this stream and returns it as an integer in the
  204        * range from 0 to 255. Returns -1 if the end of the source string has been
  205        * reached. If the internal buffer does not contain any available bytes then
  206        * it is filled from the source stream and the first byte is returned.
  207        * 
  208        * @return the byte read or -1 if the end of the source stream has been
  209        *         reached.
  210        * @throws IOException
  211        *             if this stream is closed or another IOException occurs.
  212        */
  213       @Override
  214       public synchronized int read() throws IOException {
  215           // Use local refs since buf and in may be invalidated by an
  216           // unsynchronized close()
  217           byte[] localBuf = buf;
  218           InputStream localIn = in;
  219           if (localBuf == null || localIn == null) {
  220               // luni.24=Stream is closed
  221               throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
  222           }
  223   
  224           /* Are there buffered bytes available? */
  225           if (pos >= count && fillbuf(localIn, localBuf) == -1) {
  226               return -1; /* no, fill buffer */
  227           }
  228           // localBuf may have been invalidated by fillbuf
  229           if (localBuf != buf) {
  230               localBuf = buf;
  231               if (localBuf == null) {
  232                   // luni.24=Stream is closed
  233                   throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
  234               }
  235           }
  236   
  237           /* Did filling the buffer fail with -1 (EOF)? */
  238           if (count - pos > 0) {
  239               return localBuf[pos++] & 0xFF;
  240           }
  241           return -1;
  242       }
  243   
  244       /**
  245        * Reads at most {@code length} bytes from this stream and stores them in
  246        * byte array {@code buffer} starting at offset {@code offset}. Returns the
  247        * number of bytes actually read or -1 if no bytes were read and the end of
  248        * the stream was encountered. If all the buffered bytes have been used, a
  249        * mark has not been set and the requested number of bytes is larger than
  250        * the receiver's buffer size, this implementation bypasses the buffer and
  251        * simply places the results directly into {@code buffer}.
  252        * 
  253        * @param buffer
  254        *            the byte array in which to store the bytes read.
  255        * @param offset
  256        *            the initial position in {@code buffer} to store the bytes read
  257        *            from this stream.
  258        * @param length
  259        *            the maximum number of bytes to store in {@code buffer}.
  260        * @return the number of bytes actually read or -1 if end of stream.
  261        * @throws IndexOutOfBoundsException
  262        *             if {@code offset < 0} or {@code length < 0}, or if
  263        *             {@code offset + length} is greater than the size of
  264        *             {@code buffer}.
  265        * @throws IOException
  266        *             if the stream is already closed or another IOException
  267        *             occurs.
  268        */
  269       @Override
  270       public synchronized int read(byte[] buffer, int offset, int length)
  271               throws IOException {
  272           // Use local ref since buf may be invalidated by an unsynchronized
  273           // close()
  274           byte[] localBuf = buf;
  275           if (localBuf == null) {
  276               // luni.24=Stream is closed
  277               throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
  278           }
  279           // avoid int overflow
  280           if (offset > buffer.length - length || offset < 0 || length < 0) {
  281               throw new IndexOutOfBoundsException();
  282           }
  283           if (length == 0) {
  284               return 0;
  285           }
  286           InputStream localIn = in;
  287           if (localIn == null) {
  288               // luni.24=Stream is closed
  289               throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
  290           }
  291   
  292           int required;
  293           if (pos < count) {
  294               /* There are bytes available in the buffer. */
  295               int copylength = count - pos >= length ? length : count - pos;
  296               System.arraycopy(localBuf, pos, buffer, offset, copylength);
  297               pos += copylength;
  298               if (copylength == length || localIn.available() == 0) {
  299                   return copylength;
  300               }
  301               offset += copylength;
  302               required = length - copylength;
  303           } else {
  304               required = length;
  305           }
  306   
  307           while (true) {
  308               int read;
  309               /*
  310                * If we're not marked and the required size is greater than the
  311                * buffer, simply read the bytes directly bypassing the buffer.
  312                */
  313               if (markpos == -1 && required >= localBuf.length) {
  314                   read = localIn.read(buffer, offset, required);
  315                   if (read == -1) {
  316                       return required == length ? -1 : length - required;
  317                   }
  318               } else {
  319                   if (fillbuf(localIn, localBuf) == -1) {
  320                       return required == length ? -1 : length - required;
  321                   }
  322                   // localBuf may have been invalidated by fillbuf
  323                   if (localBuf != buf) {
  324                       localBuf = buf;
  325                       if (localBuf == null) {
  326                           // luni.24=Stream is closed
  327                           throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
  328                       }
  329                   }
  330   
  331                   read = count - pos >= required ? required : count - pos;
  332                   System.arraycopy(localBuf, pos, buffer, offset, read);
  333                   pos += read;
  334               }
  335               required -= read;
  336               if (required == 0) {
  337                   return length;
  338               }
  339               if (localIn.available() == 0) {
  340                   return length - required;
  341               }
  342               offset += read;
  343           }
  344       }
  345   
  346       /**
  347        * Resets this stream to the last marked location.
  348        * 
  349        * @throws IOException
  350        *             if this stream is closed, no mark has been set or the mark is
  351        *             no longer valid because more than {@code readlimit} bytes
  352        *             have been read since setting the mark.
  353        * @see #mark(int)
  354        */
  355       @Override
  356       public synchronized void reset() throws IOException {
  357           if (buf == null) {
  358               // luni.24=Stream is closed
  359               throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$	
  360           }
  361           if (-1 == markpos) {
  362               // luni.A4=Mark has been invalidated.
  363               throw new IOException(Messages.getString("luni.A4")); //$NON-NLS-1$
  364           }
  365           pos = markpos;
  366       }
  367   
  368       /**
  369        * Skips {@code amount} number of bytes in this stream. Subsequent
  370        * {@code read()}'s will not return these bytes unless {@code reset()} is
  371        * used.
  372        * 
  373        * @param amount
  374        *            the number of bytes to skip. {@code skip} does nothing and
  375        *            returns 0 if {@code amount} is less than zero.
  376        * @return the number of bytes actually skipped.
  377        * @throws IOException
  378        *             if this stream is closed or another IOException occurs.
  379        */
  380       @Override
  381       public synchronized long skip(long amount) throws IOException {
  382           // Use local refs since buf and in may be invalidated by an
  383           // unsynchronized close()
  384           byte[] localBuf = buf;
  385           InputStream localIn = in;
  386           if (localBuf == null) {
  387               // luni.24=Stream is closed
  388               throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
  389           }
  390           if (amount < 1) {
  391               return 0;
  392           }
  393           if (localIn == null) {
  394               // luni.24=Stream is closed
  395               throw new IOException(Messages.getString("luni.24")); //$NON-NLS-1$
  396           }
  397   
  398           if (count - pos >= amount) {
  399               pos += amount;
  400               return amount;
  401           }
  402           long read = count - pos;
  403           pos = count;
  404   
  405           if (markpos != -1) {
  406               if (amount <= marklimit) {
  407                   if (fillbuf(localIn, localBuf) == -1) {
  408                       return read;
  409                   }
  410                   if (count - pos >= amount - read) {
  411                       pos += amount - read;
  412                       return amount;
  413                   }
  414                   // Couldn't get all the bytes, skip what we read
  415                   read += (count - pos);
  416                   pos = count;
  417                   return read;
  418               }
  419           }
  420           return read + localIn.skip(amount - read);
  421       }
  422   }

Save This Page
Home » apache-harmony-6.0-src-r917296-snapshot » java » io » [javadoc | source]