1 /*
   2  * Copyright (c) 2000, 2008, 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 javax.imageio.stream;
  27 
  28 import java.io.DataInputStream;
  29 import java.io.EOFException;
  30 import java.io.IOException;
  31 import java.nio.ByteOrder;
  32 import java.util.Stack;
  33 import javax.imageio.IIOException;
  34 
  35 /**
  36  * An abstract class implementing the <code>ImageInputStream</code> 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  */
  44 public abstract class ImageInputStreamImpl implements ImageInputStream {
  45 
  46     private Stack markByteStack = new Stack();
  47 
  48     private Stack 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</code>, where
  67      * <code>ByteOrder.BIG_ENDIAN</code> indicates network byte order
  68      * and <code>ByteOrder.LITTLE_ENDIAN</code> indicates the reverse
  69      * order.  By default, the value is
  70      * <code>ByteOrder.BIG_ENDIAN</code>.
  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</code>
  91      * will always be {@literal >= 0}.
  92      */
  93     protected long flushedPos = 0;
  94 
  95     /**
  96      * Constructs an <code>ImageInputStreamImpl</code>.
  97      */
  98     public ImageInputStreamImpl() {
  99     }
 100 
 101     /**
 102      * Throws an <code>IOException</code> 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</code> between 0 and 255.  If EOF is reached,
 125      * <code>-1</code> 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</code>
 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)</code>.
 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</code>
 148      * to indicate EOF.
 149      *
 150      * @exception NullPointerException if <code>b</code> is
 151      * <code>null</code>.
 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</code> bytes from the stream, and stores
 160      * them into <code>b</code> starting at index <code>off</code>.
 161      * If no bytes can be read because the end of the stream has been
 162      * reached, <code>-1</code> 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</code> 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</code>
 176      * to indicate EOF.
 177      *
 178      * @exception IndexOutOfBoundsException if <code>off</code> is
 179      * negative, <code>len</code> is negative, or <code>off +
 180      * len</code> is greater than <code>b.length</code>.
 181      * @exception NullPointerException if <code>b</code> is
 182      * <code>null</code>.
 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) < 0) {
 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) < 0) {
 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         StringBuffer input = new StringBuffer();
 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</code> 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)</code>.
 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</code> representing the number of bytes
 730      * skipped.
 731      *
 732      * @exception IOException if <code>getStreamPosition</code>
 733      * throws an <code>IOException</code> 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)</code>.
 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</code> representing the number of bytes
 751      * skipped.
 752      *
 753      * @exception IOException if <code>getStreamPosition</code>
 754      * throws an <code>IOException</code> 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</code> 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 = ((Long)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 = ((Integer)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</code> 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     protected void finalize() throws Throwable {
 872         if (!isClosed) {
 873             try {
 874                 close();
 875             } catch (IOException e) {
 876             }
 877         }
 878         super.finalize();
 879     }
 880 }