1/*
2 * Copyright (c) 2000, 2017, 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
26package javax.imageio.stream;
27
28import java.io.DataInputStream;
29import java.io.EOFException;
30import java.io.IOException;
31import java.nio.ByteOrder;
32import java.util.Stack;
33import javax.imageio.IIOException;
34
35/**
36 * An abstract class implementing the {@code ImageInputStream} interface.
37 * This class is designed to reduce the number of methods that must
38 * be implemented by subclasses.
39 *
40 * <p> In particular, this class handles most or all of the details of
41 * byte order interpretation, buffering, mark/reset, discarding,
42 * closing, and disposing.
43 */
44public abstract class ImageInputStreamImpl implements ImageInputStream {
45
46    private Stack<Long> markByteStack = new Stack<>();
47
48    private Stack<Integer> markBitStack = new Stack<>();
49
50    private boolean isClosed = false;
51
52    // Length of the buffer used for readFully(type[], int, int)
53    private static final int BYTE_BUF_LENGTH = 8192;
54
55    /**
56     * Byte buffer used for readFully(type[], int, int).  Note that this
57     * array is also used for bulk reads in readShort(), readInt(), etc, so
58     * it should be large enough to hold a primitive value (i.e. >= 8 bytes).
59     * Also note that this array is package protected, so that it can be
60     * used by ImageOutputStreamImpl in a similar manner.
61     */
62    byte[] byteBuf = new byte[BYTE_BUF_LENGTH];
63
64    /**
65     * The byte order of the stream as an instance of the enumeration
66     * class {@code java.nio.ByteOrder}, where
67     * {@code ByteOrder.BIG_ENDIAN} indicates network byte order
68     * and {@code ByteOrder.LITTLE_ENDIAN} indicates the reverse
69     * order.  By default, the value is
70     * {@code ByteOrder.BIG_ENDIAN}.
71     */
72    protected ByteOrder byteOrder = ByteOrder.BIG_ENDIAN;
73
74    /**
75     * The current read position within the stream.  Subclasses are
76     * responsible for keeping this value current from any method they
77     * override that alters the read position.
78     */
79    protected long streamPos;
80
81    /**
82     * The current bit offset within the stream.  Subclasses are
83     * responsible for keeping this value current from any method they
84     * override that alters the bit offset.
85     */
86    protected int bitOffset;
87
88    /**
89     * The position prior to which data may be discarded.  Seeking
90     * to a smaller position is not allowed.  {@code flushedPos}
91     * will always be {@literal >= 0}.
92     */
93    protected long flushedPos = 0;
94
95    /**
96     * Constructs an {@code ImageInputStreamImpl}.
97     */
98    public ImageInputStreamImpl() {
99    }
100
101    /**
102     * Throws an {@code IOException} if the stream has been closed.
103     * Subclasses may call this method from any of their methods that
104     * require the stream not to be closed.
105     *
106     * @exception IOException if the stream is closed.
107     */
108    protected final void checkClosed() throws IOException {
109        if (isClosed) {
110            throw new IOException("closed");
111        }
112    }
113
114    public void setByteOrder(ByteOrder byteOrder) {
115        this.byteOrder = byteOrder;
116    }
117
118    public ByteOrder getByteOrder() {
119        return byteOrder;
120    }
121
122    /**
123     * Reads a single byte from the stream and returns it as an
124     * {@code int} between 0 and 255.  If EOF is reached,
125     * {@code -1} is returned.
126     *
127     * <p> Subclasses must provide an implementation for this method.
128     * The subclass implementation should update the stream position
129     * before exiting.
130     *
131     * <p> The bit offset within the stream must be reset to zero before
132     * the read occurs.
133     *
134     * @return the value of the next byte in the stream, or {@code -1}
135     * if EOF is reached.
136     *
137     * @exception IOException if the stream has been closed.
138     */
139    public abstract int read() throws IOException;
140
141    /**
142     * A convenience method that calls {@code read(b, 0, b.length)}.
143     *
144     * <p> The bit offset within the stream is reset to zero before
145     * the read occurs.
146     *
147     * @return the number of bytes actually read, or {@code -1}
148     * to indicate EOF.
149     *
150     * @exception NullPointerException if {@code b} is
151     * {@code null}.
152     * @exception IOException if an I/O error occurs.
153     */
154    public int read(byte[] b) throws IOException {
155        return read(b, 0, b.length);
156    }
157
158    /**
159     * Reads up to {@code len} bytes from the stream, and stores
160     * them into {@code b} starting at index {@code off}.
161     * If no bytes can be read because the end of the stream has been
162     * reached, {@code -1} is returned.
163     *
164     * <p> The bit offset within the stream must be reset to zero before
165     * the read occurs.
166     *
167     * <p> Subclasses must provide an implementation for this method.
168     * The subclass implementation should update the stream position
169     * before exiting.
170     *
171     * @param b an array of bytes to be written to.
172     * @param off the starting position within {@code b} to write to.
173     * @param len the maximum number of bytes to read.
174     *
175     * @return the number of bytes actually read, or {@code -1}
176     * to indicate EOF.
177     *
178     * @exception IndexOutOfBoundsException if {@code off} is
179     * negative, {@code len} is negative, or {@code off + len}
180     * is greater than {@code b.length}.
181     * @exception NullPointerException if {@code b} is
182     * {@code null}.
183     * @exception IOException if an I/O error occurs.
184     */
185    public abstract int read(byte[] b, int off, int len) throws IOException;
186
187    public void readBytes(IIOByteBuffer buf, int len) throws IOException {
188        if (len < 0) {
189            throw new IndexOutOfBoundsException("len < 0!");
190        }
191        if (buf == null) {
192            throw new NullPointerException("buf == null!");
193        }
194
195        byte[] data = new byte[len];
196        len = read(data, 0, len);
197
198        buf.setData(data);
199        buf.setOffset(0);
200        buf.setLength(len);
201    }
202
203    public boolean readBoolean() throws IOException {
204        int ch = this.read();
205        if (ch < 0) {
206            throw new EOFException();
207        }
208        return (ch != 0);
209    }
210
211    public byte readByte() throws IOException {
212        int ch = this.read();
213        if (ch < 0) {
214            throw new EOFException();
215        }
216        return (byte)ch;
217    }
218
219    public int readUnsignedByte() throws IOException {
220        int ch = this.read();
221        if (ch < 0) {
222            throw new EOFException();
223        }
224        return ch;
225    }
226
227    public short readShort() throws IOException {
228        if (read(byteBuf, 0, 2) != 2) {
229            throw new EOFException();
230        }
231
232        if (byteOrder == ByteOrder.BIG_ENDIAN) {
233            return (short)
234                (((byteBuf[0] & 0xff) << 8) | ((byteBuf[1] & 0xff) << 0));
235        } else {
236            return (short)
237                (((byteBuf[1] & 0xff) << 8) | ((byteBuf[0] & 0xff) << 0));
238        }
239    }
240
241    public int readUnsignedShort() throws IOException {
242        return ((int)readShort()) & 0xffff;
243    }
244
245    public char readChar() throws IOException {
246        return (char)readShort();
247    }
248
249    public int readInt() throws IOException {
250        if (read(byteBuf, 0, 4) !=  4) {
251            throw new EOFException();
252        }
253
254        if (byteOrder == ByteOrder.BIG_ENDIAN) {
255            return
256                (((byteBuf[0] & 0xff) << 24) | ((byteBuf[1] & 0xff) << 16) |
257                 ((byteBuf[2] & 0xff) <<  8) | ((byteBuf[3] & 0xff) <<  0));
258        } else {
259            return
260                (((byteBuf[3] & 0xff) << 24) | ((byteBuf[2] & 0xff) << 16) |
261                 ((byteBuf[1] & 0xff) <<  8) | ((byteBuf[0] & 0xff) <<  0));
262        }
263    }
264
265    public long readUnsignedInt() throws IOException {
266        return ((long)readInt()) & 0xffffffffL;
267    }
268
269    public long readLong() throws IOException {
270        // REMIND: Once 6277756 is fixed, we should do a bulk read of all 8
271        // bytes here as we do in readShort() and readInt() for even better
272        // performance (see 6347575 for details).
273        int i1 = readInt();
274        int i2 = readInt();
275
276        if (byteOrder == ByteOrder.BIG_ENDIAN) {
277            return ((long)i1 << 32) + (i2 & 0xFFFFFFFFL);
278        } else {
279            return ((long)i2 << 32) + (i1 & 0xFFFFFFFFL);
280        }
281    }
282
283    public float readFloat() throws IOException {
284        return Float.intBitsToFloat(readInt());
285    }
286
287    public double readDouble() throws IOException {
288        return Double.longBitsToDouble(readLong());
289    }
290
291    public String readLine() throws IOException {
292        StringBuilder input = new StringBuilder();
293        int c = -1;
294        boolean eol = false;
295
296        while (!eol) {
297            switch (c = read()) {
298            case -1:
299            case '\n':
300                eol = true;
301                break;
302            case '\r':
303                eol = true;
304                long cur = getStreamPosition();
305                if ((read()) != '\n') {
306                    seek(cur);
307                }
308                break;
309            default:
310                input.append((char)c);
311                break;
312            }
313        }
314
315        if ((c == -1) && (input.length() == 0)) {
316            return null;
317        }
318        return input.toString();
319    }
320
321    public String readUTF() throws IOException {
322        this.bitOffset = 0;
323
324        // Fix 4494369: method ImageInputStreamImpl.readUTF()
325        // does not work as specified (it should always assume
326        // network byte order).
327        ByteOrder oldByteOrder = getByteOrder();
328        setByteOrder(ByteOrder.BIG_ENDIAN);
329
330        String ret;
331        try {
332            ret = DataInputStream.readUTF(this);
333        } catch (IOException e) {
334            // Restore the old byte order even if an exception occurs
335            setByteOrder(oldByteOrder);
336            throw e;
337        }
338
339        setByteOrder(oldByteOrder);
340        return ret;
341    }
342
343    public void readFully(byte[] b, int off, int len) throws IOException {
344        // Fix 4430357 - if off + len < 0, overflow occurred
345        if (off < 0 || len < 0 || off + len > b.length || off + len < 0) {
346            throw new IndexOutOfBoundsException
347                ("off < 0 || len < 0 || off + len > b.length!");
348        }
349
350        while (len > 0) {
351            int nbytes = read(b, off, len);
352            if (nbytes == -1) {
353                throw new EOFException();
354            }
355            off += nbytes;
356            len -= nbytes;
357        }
358    }
359
360    public void readFully(byte[] b) throws IOException {
361        readFully(b, 0, b.length);
362    }
363
364    public void readFully(short[] s, int off, int len) throws IOException {
365        // Fix 4430357 - if off + len < 0, overflow occurred
366        if (off < 0 || len < 0 || off + len > s.length || off + len < 0) {
367            throw new IndexOutOfBoundsException
368                ("off < 0 || len < 0 || off + len > s.length!");
369        }
370
371        while (len > 0) {
372            int nelts = Math.min(len, byteBuf.length/2);
373            readFully(byteBuf, 0, nelts*2);
374            toShorts(byteBuf, s, off, nelts);
375            off += nelts;
376            len -= nelts;
377        }
378    }
379
380    public void readFully(char[] c, int off, int len) throws IOException {
381        // Fix 4430357 - if off + len < 0, overflow occurred
382        if (off < 0 || len < 0 || off + len > c.length || off + len < 0) {
383            throw new IndexOutOfBoundsException
384                ("off < 0 || len < 0 || off + len > c.length!");
385        }
386
387        while (len > 0) {
388            int nelts = Math.min(len, byteBuf.length/2);
389            readFully(byteBuf, 0, nelts*2);
390            toChars(byteBuf, c, off, nelts);
391            off += nelts;
392            len -= nelts;
393        }
394    }
395
396    public void readFully(int[] i, int off, int len) throws IOException {
397        // Fix 4430357 - if off + len < 0, overflow occurred
398        if (off < 0 || len < 0 || off + len > i.length || off + len < 0) {
399            throw new IndexOutOfBoundsException
400                ("off < 0 || len < 0 || off + len > i.length!");
401        }
402
403        while (len > 0) {
404            int nelts = Math.min(len, byteBuf.length/4);
405            readFully(byteBuf, 0, nelts*4);
406            toInts(byteBuf, i, off, nelts);
407            off += nelts;
408            len -= nelts;
409        }
410    }
411
412    public void readFully(long[] l, int off, int len) throws IOException {
413        // Fix 4430357 - if off + len < 0, overflow occurred
414        if (off < 0 || len < 0 || off + len > l.length || off + len < 0) {
415            throw new IndexOutOfBoundsException
416                ("off < 0 || len < 0 || off + len > l.length!");
417        }
418
419        while (len > 0) {
420            int nelts = Math.min(len, byteBuf.length/8);
421            readFully(byteBuf, 0, nelts*8);
422            toLongs(byteBuf, l, off, nelts);
423            off += nelts;
424            len -= nelts;
425        }
426    }
427
428    public void readFully(float[] f, int off, int len) throws IOException {
429        // Fix 4430357 - if off + len < 0, overflow occurred
430        if (off < 0 || len < 0 || off + len > f.length || off + len < 0) {
431            throw new IndexOutOfBoundsException
432                ("off < 0 || len < 0 || off + len > f.length!");
433        }
434
435        while (len > 0) {
436            int nelts = Math.min(len, byteBuf.length/4);
437            readFully(byteBuf, 0, nelts*4);
438            toFloats(byteBuf, f, off, nelts);
439            off += nelts;
440            len -= nelts;
441        }
442    }
443
444    public void readFully(double[] d, int off, int len) throws IOException {
445        // Fix 4430357 - if off + len < 0, overflow occurred
446        if (off < 0 || len < 0 || off + len > d.length || off + len < 0) {
447            throw new IndexOutOfBoundsException
448                ("off < 0 || len < 0 || off + len > d.length!");
449        }
450
451        while (len > 0) {
452            int nelts = Math.min(len, byteBuf.length/8);
453            readFully(byteBuf, 0, nelts*8);
454            toDoubles(byteBuf, d, off, nelts);
455            off += nelts;
456            len -= nelts;
457        }
458    }
459
460    private void toShorts(byte[] b, short[] s, int off, int len) {
461        int boff = 0;
462        if (byteOrder == ByteOrder.BIG_ENDIAN) {
463            for (int j = 0; j < len; j++) {
464                int b0 = b[boff];
465                int b1 = b[boff + 1] & 0xff;
466                s[off + j] = (short)((b0 << 8) | b1);
467                boff += 2;
468            }
469        } else {
470            for (int j = 0; j < len; j++) {
471                int b0 = b[boff + 1];
472                int b1 = b[boff] & 0xff;
473                s[off + j] = (short)((b0 << 8) | b1);
474                boff += 2;
475            }
476        }
477    }
478
479    private void toChars(byte[] b, char[] c, int off, int len) {
480        int boff = 0;
481        if (byteOrder == ByteOrder.BIG_ENDIAN) {
482            for (int j = 0; j < len; j++) {
483                int b0 = b[boff];
484                int b1 = b[boff + 1] & 0xff;
485                c[off + j] = (char)((b0 << 8) | b1);
486                boff += 2;
487            }
488        } else {
489            for (int j = 0; j < len; j++) {
490                int b0 = b[boff + 1];
491                int b1 = b[boff] & 0xff;
492                c[off + j] = (char)((b0 << 8) | b1);
493                boff += 2;
494            }
495        }
496    }
497
498    private void toInts(byte[] b, int[] i, int off, int len) {
499        int boff = 0;
500        if (byteOrder == ByteOrder.BIG_ENDIAN) {
501            for (int j = 0; j < len; j++) {
502                int b0 = b[boff];
503                int b1 = b[boff + 1] & 0xff;
504                int b2 = b[boff + 2] & 0xff;
505                int b3 = b[boff + 3] & 0xff;
506                i[off + j] = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
507                boff += 4;
508            }
509        } else {
510            for (int j = 0; j < len; j++) {
511                int b0 = b[boff + 3];
512                int b1 = b[boff + 2] & 0xff;
513                int b2 = b[boff + 1] & 0xff;
514                int b3 = b[boff] & 0xff;
515                i[off + j] = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
516                boff += 4;
517            }
518        }
519    }
520
521    private void toLongs(byte[] b, long[] l, int off, int len) {
522        int boff = 0;
523        if (byteOrder == ByteOrder.BIG_ENDIAN) {
524            for (int j = 0; j < len; j++) {
525                int b0 = b[boff];
526                int b1 = b[boff + 1] & 0xff;
527                int b2 = b[boff + 2] & 0xff;
528                int b3 = b[boff + 3] & 0xff;
529                int b4 = b[boff + 4];
530                int b5 = b[boff + 5] & 0xff;
531                int b6 = b[boff + 6] & 0xff;
532                int b7 = b[boff + 7] & 0xff;
533
534                int i0 = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
535                int i1 = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
536
537                l[off + j] = ((long)i0 << 32) | (i1 & 0xffffffffL);
538                boff += 8;
539            }
540        } else {
541            for (int j = 0; j < len; j++) {
542                int b0 = b[boff + 7];
543                int b1 = b[boff + 6] & 0xff;
544                int b2 = b[boff + 5] & 0xff;
545                int b3 = b[boff + 4] & 0xff;
546                int b4 = b[boff + 3];
547                int b5 = b[boff + 2] & 0xff;
548                int b6 = b[boff + 1] & 0xff;
549                int b7 = b[boff]     & 0xff;
550
551                int i0 = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
552                int i1 = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
553
554                l[off + j] = ((long)i0 << 32) | (i1 & 0xffffffffL);
555                boff += 8;
556            }
557        }
558    }
559
560    private void toFloats(byte[] b, float[] f, int off, int len) {
561        int boff = 0;
562        if (byteOrder == ByteOrder.BIG_ENDIAN) {
563            for (int j = 0; j < len; j++) {
564                int b0 = b[boff];
565                int b1 = b[boff + 1] & 0xff;
566                int b2 = b[boff + 2] & 0xff;
567                int b3 = b[boff + 3] & 0xff;
568                int i = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
569                f[off + j] = Float.intBitsToFloat(i);
570                boff += 4;
571            }
572        } else {
573            for (int j = 0; j < len; j++) {
574                int b0 = b[boff + 3];
575                int b1 = b[boff + 2] & 0xff;
576                int b2 = b[boff + 1] & 0xff;
577                int b3 = b[boff + 0] & 0xff;
578                int i = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
579                f[off + j] = Float.intBitsToFloat(i);
580                boff += 4;
581            }
582        }
583    }
584
585    private void toDoubles(byte[] b, double[] d, int off, int len) {
586        int boff = 0;
587        if (byteOrder == ByteOrder.BIG_ENDIAN) {
588            for (int j = 0; j < len; j++) {
589                int b0 = b[boff];
590                int b1 = b[boff + 1] & 0xff;
591                int b2 = b[boff + 2] & 0xff;
592                int b3 = b[boff + 3] & 0xff;
593                int b4 = b[boff + 4];
594                int b5 = b[boff + 5] & 0xff;
595                int b6 = b[boff + 6] & 0xff;
596                int b7 = b[boff + 7] & 0xff;
597
598                int i0 = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
599                int i1 = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
600                long l = ((long)i0 << 32) | (i1 & 0xffffffffL);
601
602                d[off + j] = Double.longBitsToDouble(l);
603                boff += 8;
604            }
605        } else {
606            for (int j = 0; j < len; j++) {
607                int b0 = b[boff + 7];
608                int b1 = b[boff + 6] & 0xff;
609                int b2 = b[boff + 5] & 0xff;
610                int b3 = b[boff + 4] & 0xff;
611                int b4 = b[boff + 3];
612                int b5 = b[boff + 2] & 0xff;
613                int b6 = b[boff + 1] & 0xff;
614                int b7 = b[boff] & 0xff;
615
616                int i0 = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
617                int i1 = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
618                long l = ((long)i0 << 32) | (i1 & 0xffffffffL);
619
620                d[off + j] = Double.longBitsToDouble(l);
621                boff += 8;
622            }
623        }
624    }
625
626    public long getStreamPosition() throws IOException {
627        checkClosed();
628        return streamPos;
629    }
630
631    public int getBitOffset() throws IOException {
632        checkClosed();
633        return bitOffset;
634    }
635
636    public void setBitOffset(int bitOffset) throws IOException {
637        checkClosed();
638        if (bitOffset < 0 || bitOffset > 7) {
639            throw new IllegalArgumentException("bitOffset must be betwwen 0 and 7!");
640        }
641        this.bitOffset = bitOffset;
642    }
643
644    public int readBit() throws IOException {
645        checkClosed();
646
647        // Compute final bit offset before we call read() and seek()
648        int newBitOffset = (this.bitOffset + 1) & 0x7;
649
650        int val = read();
651        if (val == -1) {
652            throw new EOFException();
653        }
654
655        if (newBitOffset != 0) {
656            // Move byte position back if in the middle of a byte
657            seek(getStreamPosition() - 1);
658            // Shift the bit to be read to the rightmost position
659            val >>= 8 - newBitOffset;
660        }
661        this.bitOffset = newBitOffset;
662
663        return val & 0x1;
664    }
665
666    public long readBits(int numBits) throws IOException {
667        checkClosed();
668
669        if (numBits < 0 || numBits > 64) {
670            throw new IllegalArgumentException();
671        }
672        if (numBits == 0) {
673            return 0L;
674        }
675
676        // Have to read additional bits on the left equal to the bit offset
677        int bitsToRead = numBits + bitOffset;
678
679        // Compute final bit offset before we call read() and seek()
680        int newBitOffset = (this.bitOffset + numBits) & 0x7;
681
682        // Read a byte at a time, accumulate
683        long accum = 0L;
684        while (bitsToRead > 0) {
685            int val = read();
686            if (val == -1) {
687                throw new EOFException();
688            }
689
690            accum <<= 8;
691            accum |= val;
692            bitsToRead -= 8;
693        }
694
695        // Move byte position back if in the middle of a byte
696        if (newBitOffset != 0) {
697            seek(getStreamPosition() - 1);
698        }
699        this.bitOffset = newBitOffset;
700
701        // Shift away unwanted bits on the right.
702        accum >>>= (-bitsToRead); // Negative of bitsToRead == extra bits read
703
704        // Mask out unwanted bits on the left
705        accum &= (-1L >>> (64 - numBits));
706
707        return accum;
708    }
709
710    /**
711     * Returns {@code -1L} to indicate that the stream has unknown
712     * length.  Subclasses must override this method to provide actual
713     * length information.
714     *
715     * @return -1L to indicate unknown length.
716     */
717    public long length() {
718        return -1L;
719    }
720
721    /**
722     * Advances the current stream position by calling
723     * {@code seek(getStreamPosition() + n)}.
724     *
725     * <p> The bit offset is reset to zero.
726     *
727     * @param n the number of bytes to seek forward.
728     *
729     * @return an {@code int} representing the number of bytes
730     * skipped.
731     *
732     * @exception IOException if {@code getStreamPosition}
733     * throws an {@code IOException} when computing either
734     * the starting or ending position.
735     */
736    public int skipBytes(int n) throws IOException {
737        long pos = getStreamPosition();
738        seek(pos + n);
739        return (int)(getStreamPosition() - pos);
740    }
741
742    /**
743     * Advances the current stream position by calling
744     * {@code seek(getStreamPosition() + n)}.
745     *
746     * <p> The bit offset is reset to zero.
747     *
748     * @param n the number of bytes to seek forward.
749     *
750     * @return a {@code long} representing the number of bytes
751     * skipped.
752     *
753     * @exception IOException if {@code getStreamPosition}
754     * throws an {@code IOException} when computing either
755     * the starting or ending position.
756     */
757    public long skipBytes(long n) throws IOException {
758        long pos = getStreamPosition();
759        seek(pos + n);
760        return getStreamPosition() - pos;
761    }
762
763    public void seek(long pos) throws IOException {
764        checkClosed();
765
766        // This test also covers pos < 0
767        if (pos < flushedPos) {
768            throw new IndexOutOfBoundsException("pos < flushedPos!");
769        }
770
771        this.streamPos = pos;
772        this.bitOffset = 0;
773    }
774
775    /**
776     * Pushes the current stream position onto a stack of marked
777     * positions.
778     */
779    public void mark() {
780        try {
781            markByteStack.push(Long.valueOf(getStreamPosition()));
782            markBitStack.push(Integer.valueOf(getBitOffset()));
783        } catch (IOException e) {
784        }
785    }
786
787    /**
788     * Resets the current stream byte and bit positions from the stack
789     * of marked positions.
790     *
791     * <p> An {@code IOException} will be thrown if the previous
792     * marked position lies in the discarded portion of the stream.
793     *
794     * @exception IOException if an I/O error occurs.
795     */
796    public void reset() throws IOException {
797        if (markByteStack.empty()) {
798            return;
799        }
800
801        long pos = markByteStack.pop().longValue();
802        if (pos < flushedPos) {
803            throw new IIOException
804                ("Previous marked position has been discarded!");
805        }
806        seek(pos);
807
808        int offset = markBitStack.pop().intValue();
809        setBitOffset(offset);
810    }
811
812    public void flushBefore(long pos) throws IOException {
813        checkClosed();
814        if (pos < flushedPos) {
815            throw new IndexOutOfBoundsException("pos < flushedPos!");
816        }
817        if (pos > getStreamPosition()) {
818            throw new IndexOutOfBoundsException("pos > getStreamPosition()!");
819        }
820        // Invariant: flushedPos >= 0
821        flushedPos = pos;
822    }
823
824    public void flush() throws IOException {
825        flushBefore(getStreamPosition());
826    }
827
828    public long getFlushedPosition() {
829        return flushedPos;
830    }
831
832    /**
833     * Default implementation returns false.  Subclasses should
834     * override this if they cache data.
835     */
836    public boolean isCached() {
837        return false;
838    }
839
840    /**
841     * Default implementation returns false.  Subclasses should
842     * override this if they cache data in main memory.
843     */
844    public boolean isCachedMemory() {
845        return false;
846    }
847
848    /**
849     * Default implementation returns false.  Subclasses should
850     * override this if they cache data in a temporary file.
851     */
852    public boolean isCachedFile() {
853        return false;
854    }
855
856    public void close() throws IOException {
857        checkClosed();
858
859        isClosed = true;
860    }
861
862    /**
863     * Finalizes this object prior to garbage collection.  The
864     * {@code close} method is called to close any open input
865     * source.  This method should not be called from application
866     * code.
867     *
868     * @exception Throwable if an error occurs during superclass
869     * finalization.
870     *
871     * @deprecated The {@code finalize} method has been deprecated.
872     *     Subclasses that override {@code finalize} in order to perform cleanup
873     *     should be modified to use alternative cleanup mechanisms and
874     *     to remove the overriding {@code finalize} method.
875     *     When overriding the {@code finalize} method, its implementation must explicitly
876     *     ensure that {@code super.finalize()} is invoked as described in {@link Object#finalize}.
877     *     See the specification for {@link Object#finalize()} for further
878     *     information about migration options.
879     */
880    @Deprecated(since="9")
881    protected void finalize() throws Throwable {
882        if (!isClosed) {
883            try {
884                close();
885            } catch (IOException e) {
886            }
887        }
888        super.finalize();
889    }
890}
891