1 /*
  2  * Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 
 26 package java.io;
 27 
 28 /**
 29  * A {@code PushbackInputStream} adds
 30  * functionality to another input stream, namely
 31  * the  ability to "push back" or "unread" bytes,
 32  * by storing pushed-back bytes in an internal buffer.
 33  * This is useful in situations where
 34  * it is convenient for a fragment of code
 35  * to read an indefinite number of data bytes
 36  * that  are delimited by a particular byte
 37  * value; after reading the terminating byte,
 38  * the  code fragment can "unread" it, so that
 39  * the next read operation on the input stream
 40  * will reread the byte that was pushed back.
 41  * For example, bytes representing the  characters
 42  * constituting an identifier might be terminated
 43  * by a byte representing an  operator character;
 44  * a method whose job is to read just an identifier
 45  * can read until it  sees the operator and
 46  * then push the operator back to be re-read.
 47  *
 48  * @author  David Connelly
 49  * @author  Jonathan Payne
 50  * @since   1.0
 51  */
 52 public class PushbackInputStream extends FilterInputStream {
 53     /**
 54      * The pushback buffer.
 55      * @since   1.1
 56      */
 57     protected byte[] buf;
 58 
 59     /**
 60      * The position within the pushback buffer from which the next byte will
 61      * be read.  When the buffer is empty, {@code pos} is equal to
 62      * {@code buf.length}; when the buffer is full, {@code pos} is
 63      * equal to zero.
 64      *
 65      * @since   1.1
 66      */
 67     protected int pos;
 68 
 69     /**
 70      * Check to make sure that this stream has not been closed
 71      */
 72     private void ensureOpen() throws IOException {
 73         if (in == null)
 74             throw new IOException("Stream closed");
 75     }
 76 
 77     /**
 78      * Creates a {@code PushbackInputStream}
 79      * with a pushback buffer of the specified {@code size},
 80      * and saves its argument, the input stream
 81      * {@code in}, for later use. Initially,
 82      * the pushback buffer is empty.
 83      *
 84      * @param  in    the input stream from which bytes will be read.
 85      * @param  size  the size of the pushback buffer.
 86      * @throws IllegalArgumentException if {@code size <= 0}
 87      * @since  1.1
 88      */
 89     public PushbackInputStream(InputStream in, int size) {
 90         super(in);
 91         if (size <= 0) {
 92             throw new IllegalArgumentException("size <= 0");
 93         }
 94         this.buf = new byte[size];
 95         this.pos = size;
 96     }
 97 
 98     /**
 99      * Creates a {@code PushbackInputStream}
100      * with a 1-byte pushback buffer, and saves its argument, the input stream
101      * {@code in}, for later use. Initially,
102      * the pushback buffer is empty.
103      *
104      * @param   in   the input stream from which bytes will be read.
105      */
106     public PushbackInputStream(InputStream in) {
107         this(in, 1);
108     }
109 
110     /**
111      * Reads the next byte of data from this input stream. The value
112      * byte is returned as an {@code int} in the range
113      * {@code 0} to {@code 255}. If no byte is available
114      * because the end of the stream has been reached, the value
115      * {@code -1} is returned. This method blocks until input data
116      * is available, the end of the stream is detected, or an exception
117      * is thrown.
118      *
119      * <p> This method returns the most recently pushed-back byte, if there is
120      * one, and otherwise calls the {@code read} method of its underlying
121      * input stream and returns whatever value that method returns.
122      *
123      * @return     the next byte of data, or {@code -1} if the end of the
124      *             stream has been reached.
125      * @throws     IOException  if this input stream has been closed by
126      *             invoking its {@link #close()} method,
127      *             or an I/O error occurs.
128      * @see        java.io.InputStream#read()
129      */
130     public int read() throws IOException {
131         ensureOpen();
132         if (pos < buf.length) {
133             return buf[pos++] & 0xff;
134         }
135         return super.read();
136     }
137 
138     /**
139      * Reads up to {@code len} bytes of data from this input stream into
140      * an array of bytes.  This method first reads any pushed-back bytes; after
141      * that, if fewer than {@code len} bytes have been read then it
142      * reads from the underlying input stream. If {@code len} is not zero, the method
143      * blocks until at least 1 byte of input is available; otherwise, no
144      * bytes are read and {@code 0} is returned.
145      *
146      * @param      b     the buffer into which the data is read.
147      * @param      off   the start offset in the destination array {@code b}
148      * @param      len   the maximum number of bytes read.
149      * @return     the total number of bytes read into the buffer, or
150      *             {@code -1} if there is no more data because the end of
151      *             the stream has been reached.
152      * @throws     NullPointerException If {@code b} is {@code null}.
153      * @throws     IndexOutOfBoundsException If {@code off} is negative,
154      *             {@code len} is negative, or {@code len} is greater than
155      *             {@code b.length - off}
156      * @throws     IOException  if this input stream has been closed by
157      *             invoking its {@link #close()} method,
158      *             or an I/O error occurs.
159      * @see        java.io.InputStream#read(byte[], int, int)
160      */
161     public int read(byte[] b, int off, int len) throws IOException {
162         ensureOpen();
163         if (b == null) {
164             throw new NullPointerException();
165         } else if (off < 0 || len < 0 || len > b.length - off) {
166             throw new IndexOutOfBoundsException();
167         } else if (len == 0) {
168             return 0;
169         }
170 
171         int avail = buf.length - pos;
172         if (avail > 0) {
173             if (len < avail) {
174                 avail = len;
175             }
176             System.arraycopy(buf, pos, b, off, avail);
177             pos += avail;
178             off += avail;
179             len -= avail;
180         }
181         if (len > 0) {
182             len = super.read(b, off, len);
183             if (len == -1) {
184                 return avail == 0 ? -1 : avail;
185             }
186             return avail + len;
187         }
188         return avail;
189     }
190 
191     /**
192      * Pushes back a byte by copying it to the front of the pushback buffer.
193      * After this method returns, the next byte to be read will have the value
194      * {@code (byte)b}.
195      *
196      * @param      b   the {@code int} value whose low-order
197      *                  byte is to be pushed back.
198      * @throws    IOException If there is not enough room in the pushback
199      *            buffer for the byte, or this input stream has been closed by
200      *            invoking its {@link #close()} method.
201      */
202     public void unread(int b) throws IOException {
203         ensureOpen();
204         if (pos == 0) {
205             throw new IOException("Push back buffer is full");
206         }
207         buf[--pos] = (byte)b;
208     }
209 
210     /**
211      * Pushes back a portion of an array of bytes by copying it to the front
212      * of the pushback buffer.  After this method returns, the next byte to be
213      * read will have the value {@code b[off]}, the byte after that will
214      * have the value {@code b[off+1]}, and so forth.
215      *
216      * @param     b the byte array to push back.
217      * @param     off the start offset of the data.
218      * @param     len the number of bytes to push back.
219      * @throws    NullPointerException If {@code b} is {@code null}.
220      * @throws    IOException If there is not enough room in the pushback
221      *            buffer for the specified number of bytes,
222      *            or this input stream has been closed by
223      *            invoking its {@link #close()} method.
224      * @since     1.1
225      */
226     public void unread(byte[] b, int off, int len) throws IOException {
227         ensureOpen();
228         if (len > pos) {
229             throw new IOException("Push back buffer is full");
230         }
231         pos -= len;
232         System.arraycopy(b, off, buf, pos, len);
233     }
234 
235     /**
236      * Pushes back an array of bytes by copying it to the front of the
237      * pushback buffer.  After this method returns, the next byte to be read
238      * will have the value {@code b[0]}, the byte after that will have the
239      * value {@code b[1]}, and so forth.
240      *
241      * @param     b the byte array to push back
242      * @throws    NullPointerException If {@code b} is {@code null}.
243      * @throws    IOException If there is not enough room in the pushback
244      *            buffer for the specified number of bytes,
245      *            or this input stream has been closed by
246      *            invoking its {@link #close()} method.
247      * @since     1.1
248      */
249     public void unread(byte[] b) throws IOException {
250         unread(b, 0, b.length);
251     }
252 
253     /**
254      * Returns an estimate of the number of bytes that can be read (or
255      * skipped over) from this input stream without blocking by the next
256      * invocation of a method for this input stream. The next invocation might be
257      * the same thread or another thread.  A single read or skip of this
258      * many bytes will not block, but may read or skip fewer bytes.
259      *
260      * <p> The method returns the sum of the number of bytes that have been
261      * pushed back and the value returned by {@link
262      * java.io.FilterInputStream#available available}.
263      *
264      * @return     the number of bytes that can be read (or skipped over) from
265      *             the input stream without blocking.
266      * @throws     IOException  if this input stream has been closed by
267      *             invoking its {@link #close()} method,
268      *             or an I/O error occurs.
269      * @see        java.io.FilterInputStream#in
270      * @see        java.io.InputStream#available()
271      */
272     public int available() throws IOException {
273         ensureOpen();
274         int n = buf.length - pos;
275         int avail = super.available();
276         return n > (Integer.MAX_VALUE - avail)
277                     ? Integer.MAX_VALUE
278                     : n + avail;
279     }
280 
281     /**
282      * Skips over and discards {@code n} bytes of data from this
283      * input stream. The {@code skip} method may, for a variety of
284      * reasons, end up skipping over some smaller number of bytes,
285      * possibly zero.  If {@code n} is negative, no bytes are skipped.
286      *
287      * <p> The {@code skip} method of {@code PushbackInputStream}
288      * first skips over the bytes in the pushback buffer, if any.  It then
289      * calls the {@code skip} method of the underlying input stream if
290      * more bytes need to be skipped.  The actual number of bytes skipped
291      * is returned.
292      *
293      * @param      n  {@inheritDoc}
294      * @return     {@inheritDoc}
295      * @throws     IOException  if the stream has been closed by
296      *             invoking its {@link #close()} method,
297      *             {@code in.skip(n)} throws an IOException,
298      *             or an I/O error occurs.
299      * @see        java.io.FilterInputStream#in
300      * @see        java.io.InputStream#skip(long n)
301      * @since      1.2
302      */
303     public long skip(long n) throws IOException {
304         ensureOpen();
305         if (n <= 0) {
306             return 0;
307         }
308 
309         long pskip = buf.length - pos;
310         if (pskip > 0) {
311             if (n < pskip) {
312                 pskip = n;
313             }
314             pos += pskip;
315             n -= pskip;
316         }
317         if (n > 0) {
318             pskip += super.skip(n);
319         }
320         return pskip;
321     }
322 
323     /**
324      * Tests if this input stream supports the {@code mark} and
325      * {@code reset} methods, which it does not.
326      *
327      * @return   {@code false}, since this class does not support the
328      *           {@code mark} and {@code reset} methods.
329      * @see      java.io.InputStream#mark(int)
330      * @see      java.io.InputStream#reset()
331      */
332     public boolean markSupported() {
333         return false;
334     }
335 
336     /**
337      * Marks the current position in this input stream.
338      *
339      * <p> The {@code mark} method of {@code PushbackInputStream}
340      * does nothing.
341      *
342      * @param   readlimit   the maximum limit of bytes that can be read before
343      *                      the mark position becomes invalid.
344      * @see     java.io.InputStream#reset()
345      */
346     public synchronized void mark(int readlimit) {
347     }
348 
349     /**
350      * Repositions this stream to the position at the time the
351      * {@code mark} method was last called on this input stream.
352      *
353      * <p> The method {@code reset} for class
354      * {@code PushbackInputStream} does nothing except throw an
355      * {@code IOException}.
356      *
357      * @throws  IOException  if this method is invoked.
358      * @see     java.io.InputStream#mark(int)
359      * @see     java.io.IOException
360      */
361     public synchronized void reset() throws IOException {
362         throw new IOException("mark/reset not supported");
363     }
364 
365     /**
366      * Closes this input stream and releases any system resources
367      * associated with the stream.
368      * Once the stream has been closed, further read(), unread(),
369      * available(), reset(), or skip() invocations will throw an IOException.
370      * Closing a previously closed stream has no effect.
371      *
372      * @throws     IOException  if an I/O error occurs.
373      */
374     public synchronized void close() throws IOException {
375         if (in == null)
376             return;
377         in.close();
378         in = null;
379         buf = null;
380     }
381 }