001/*
002 * $RCSfile: ISRandomAccessIO.java,v $
003 * $Revision: 1.1 $
004 * $Date: 2005/02/11 05:02:25 $
005 * $State: Exp $
006 *
007 * Class:                   ISRandomAccessIO
008 *
009 * Description:             Turns an InsputStream into a read-only
010 *                          RandomAccessIO, using buffering.
011 *
012 *
013 *
014 * COPYRIGHT:
015 *
016 * This software module was originally developed by Raphaël Grosbois and
017 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel
018 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David
019 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research
020 * Centre France S.A) in the course of development of the JPEG2000
021 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This
022 * software module is an implementation of a part of the JPEG 2000
023 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio
024 * Systems AB and Canon Research Centre France S.A (collectively JJ2000
025 * Partners) agree not to assert against ISO/IEC and users of the JPEG
026 * 2000 Standard (Users) any of their rights under the copyright, not
027 * including other intellectual property rights, for this software module
028 * with respect to the usage by ISO/IEC and Users of this software module
029 * or modifications thereof for use in hardware or software products
030 * claiming conformance to the JPEG 2000 Standard. Those intending to use
031 * this software module in hardware or software products are advised that
032 * their use may infringe existing patents. The original developers of
033 * this software module, JJ2000 Partners and ISO/IEC assume no liability
034 * for use of this software module or modifications thereof. No license
035 * or right to this software module is granted for non JPEG 2000 Standard
036 * conforming products. JJ2000 Partners have full right to use this
037 * software module for his/her own purpose, assign or donate this
038 * software module to any third party and to inhibit third parties from
039 * using this software module for non JPEG 2000 Standard conforming
040 * products. This copyright notice must be included in all copies or
041 * derivative works of this software module.
042 *
043 * Copyright (c) 1999/2000 JJ2000 Partners.
044 *
045 *
046 *
047 */
048package jj2000.j2k.util;
049
050import java.io.*;
051import jj2000.j2k.io.*;
052
053/**
054 * This class implements a wrapper to turn an InputStream into a
055 * RandomAccessIO. To provide random access the input data from the
056 * InputStream is cached in an in-memory buffer. The in-memory buffer size can
057 * be limited to a specified size. The data is read into the cache on a as
058 * needed basis, blocking only when necessary.
059 *
060 * <P>The cache grows automatically as necessary. However, if the data length
061 * is known prior to the creation of a ISRandomAccessIO object, it is best to
062 * specify that as the initial in-memory buffer size. That will minimize data
063 * copying and multiple allocation.
064 *
065 * <P>Multi-byte data is read in big-endian order. The in-memory buffer
066 * storage is released when 'close()' is called. This class can only be used
067 * for data input, not output. The wrapped InputStream is closed when all the
068 * input data is cached or when 'close()' is called.
069 *
070 * <P>If an out of memory condition is encountered when growing the
071 * in-memory buffer an IOException is thrown instead of an
072 * OutOfMemoryError. The exception message is "Out of memory to cache input
073 * data".
074 *
075 * <P>This class is intended for use as a "quick and dirty" way to give
076 * network connectivity to RandomAccessIO based classes. It is not intended as
077 * an efficient means of implementing network connectivity. Doing such
078 * requires reimplementing the RandomAccessIO based classes to directly use
079 * network connections.
080 *
081 * <P>This class does not use temporary files as buffers, because that would
082 * preclude the use in unsigned applets.
083 * */
084public class ISRandomAccessIO implements RandomAccessIO {
085
086    /** The InputStream that is wrapped */
087    private InputStream is;
088
089    /* Tha maximum size, in bytes, of the in memory buffer. The maximum size
090     * includes the EOF. */
091    private int maxsize;
092
093    /* The increment, in bytes, for the in-memory buffer size */
094    private int inc;
095
096    /* The in-memory buffer to cache received data */
097    private byte buf[];
098
099    /* The length of the already received data */
100    private int len;
101
102    /* The position of the next byte to be read from the in-memory buffer */
103    private int pos;
104
105    /* Flag to indicate if all the data has been received. That is, if the EOF
106     * has been reached. */
107    private boolean complete;
108
109    /**
110     * Creates a new RandomAccessIO wrapper for the given InputStream
111     * 'is'. The internal cache buffer will have size 'size' and will
112     * increment by 'inc' each time it is needed. The maximum buffer size is
113     * limited to 'maxsize'.
114     *
115     * @param is The input from where to get the data.
116     *
117     * @param size The initial size for the cache buffer, in bytes.
118     *
119     * @param inc The size increment for the cache buffer, in bytes.
120     *
121     * @param maxsize The maximum size for the cache buffer, in bytes.
122     */
123    public ISRandomAccessIO(InputStream is, int size, int inc, int maxsize) {
124        if (size < 0 || inc <= 0 || maxsize <= 0 || is == null) {
125            throw new IllegalArgumentException();
126        }
127        this.is = is;
128        // Increase size by one to count in EOF
129        if (size < Integer.MAX_VALUE) size++;
130        buf = new byte[size];
131        this.inc = inc;
132        // The maximum size is one byte more, to allow reading the EOF.
133        if (maxsize < Integer.MAX_VALUE) maxsize++;
134        this.maxsize = maxsize;
135        pos = 0;
136        len = 0;
137        complete = false;
138    }
139
140    /**
141     * Creates a new RandomAccessIO wrapper for the given InputStream
142     * 'is'. The internal cache buffer size and increment is to to 256 kB. The
143     * maximum buffer size is set to Integer.MAX_VALUE (2 GB).
144     *
145     * @param is The input from where to get the data.
146     *
147     */
148    public ISRandomAccessIO(InputStream is) {
149        this(is,1<<18,1<<18,Integer.MAX_VALUE);
150    }
151
152    /**
153     * Grows the cache buffer by 'inc', upto a maximum of 'maxsize'. The
154     * buffer size will be increased by at least one byte, if no exception is
155     * thrown.
156     *
157     * @exception IOException If the maximum cache size is reached or if not
158     * enough memory is available to grow the buffer.
159     */
160    private void growBuffer() throws IOException {
161        byte newbuf[];
162        int effinc;           // effective increment
163
164        effinc = inc;
165        if (buf.length+effinc > maxsize) effinc = maxsize-buf.length;
166        if (effinc <= 0) {
167            throw new IOException("Reached maximum cache size ("+maxsize+")");
168        }
169        try {
170            newbuf = new byte[buf.length+inc];
171        } catch (OutOfMemoryError e) {
172            throw new IOException("Out of memory to cache input data");
173        }
174        System.arraycopy(buf,0,newbuf,0,len);
175        buf = newbuf;
176    }
177
178    /**
179     * Reads data from the wrapped InputStream and places it in the cache
180     * buffer. Reads all input data that will not cause it to block, but at
181     * least on byte is read (even if it blocks), unless EOF is reached. This
182     * method can not be called if EOF has been already reached
183     * (i.e. 'complete' is true).
184     *
185     * @exception IOException An I/O error occurred, out of meory to grow
186     * cache or maximum cache size reached.
187     * */
188    private void readInput() throws IOException {
189        int n;
190        int b;
191        int k;
192
193        if (complete) {
194            throw new IllegalArgumentException("Already reached EOF");
195        }
196//        may need reflection to call this method
197//        n = is.available(); /* how much can we read without blocking? */
198        n = 0; /* how much can we read without blocking? */
199        if (n == 0) n = 1;  /* read at least one byte (even if it blocks) */
200        while (len+n > buf.length) { /* Ensure buffer size */
201            growBuffer();
202        }
203        /* Read the data. Loop to be sure that we do read 'n' bytes */
204        do {
205            k = is.read(buf,len,n);
206            if (k > 0) { /* Some data was read */
207                len += k;
208                n -= k;
209            }
210        } while (n > 0 && k > 0);
211        if (k <= 0) { /* we reached EOF */
212            complete = true;
213            is = null;
214        }
215    }
216
217    /**
218     * Closes this object for reading as well as the wrapped InputStream, if
219     * not already closed. The memory used by the cache is released.
220     *
221     * @exception IOException If an I/O error occurs while closing the
222     * underlying InputStream.  */
223    public void close() throws IOException {
224        buf = null;
225        if (!complete) {
226            is.close();
227            is = null;
228        }
229    }
230
231    /**
232     * Returns the current position in the stream, which is the
233     * position from where the next byte of data would be read. The first
234     * byte in the stream is in position 0.
235     *
236     * @exception IOException If an I/O error occurred.
237     * */
238    public int getPos() throws IOException {
239        return pos;
240    }
241
242    /**
243     * Moves the current position for the next read operation to
244     * offset. The offset is measured from the beginning of the stream. If the
245     * offset is set beyond the currently cached data, the missing data will
246     * be read only when a read operation is performed. Setting
247     * the offset beyond the end of the data will cause an EOFException only
248     * if the data length is currently known, otherwise an IOException will
249     * occur when a read operation is attempted at that position.
250     *
251     * @param off The offset where to move to.
252     *
253     * @exception EOFException If seeking beyond EOF and the data length is
254     * known.
255     *
256     * @exception IOException If an I/O error ocurred.
257     * */
258    public void seek(int off) throws IOException {
259        if (complete) { /* we know the length, check seek is within length */
260            if (off > len) {
261                throw new EOFException();
262            }
263        }
264        pos = off;
265    }
266
267    /**
268     * Returns the length of the stream. This will cause all the data to be
269     * read. This method will block until all the data is read, which can be
270     * lengthy across the network.
271     *
272     * @return The length of the stream, in bytes.
273     *
274     * @exception IOException If an I/O error ocurred.
275     */
276    public int length() throws IOException {
277        if (Integer.MAX_VALUE != maxsize)
278            return maxsize - 1;
279        while (!complete) { // read until we reach EOF
280            readInput();
281        }
282        return len;
283    }
284
285    /**
286     * Reads a byte of data from the stream.
287     *
288     * @return The byte read, as an int in the range [0-255].
289     *
290     * @exception EOFException If the end-of file was reached.
291     *
292     * @exception IOException If an I/O error ocurred.
293     *
294     */
295    public int read() throws IOException {
296        if (pos < len) { // common, fast case
297            return 0xFF & (int)buf[pos++];
298        }
299        // general case
300        while (!complete && pos >= len) {
301            readInput();
302        }
303        if (pos == len) {
304            throw new EOFException();
305        } else if (pos > len) {
306            throw new IOException("Position beyond EOF");
307        }
308        return 0xFF & (int)buf[pos++];
309    }
310
311    /**
312     * Reads 'len' bytes of data from this file into an array of bytes. This
313     * method reads repeatedly from the stream until all the bytes are
314     * read. This method blocks until all the bytes are read, the end of the
315     * stream is detected, or an exception is thrown.
316     *
317     * @param b The buffer into which the data is to be read. It must be long
318     * enough.
319     *
320     * @param off The index in 'b' where to place the first byte read.
321     *
322     * @param len The number of bytes to read.
323     *
324     * @exception EOFException If the end-of file was reached before
325     * getting all the necessary data.
326     *
327     * @exception IOException If an I/O error ocurred.
328     *
329     * */
330    public void readFully(byte b[], int off, int n) throws IOException {
331        if (pos+n <= len) { // common, fast case
332            System.arraycopy(buf,pos,b,off,n);
333            pos += n;
334            return;
335        }
336        // general case
337        while (!complete && pos+n > len) {
338            readInput();
339        }
340        if (pos+n > len) {
341            throw new EOFException();
342        }
343        System.arraycopy(buf,pos,b,off,n);
344        pos += n;
345    }
346
347    /**
348     * Returns the endianess (i.e., byte ordering) of multi-byte I/O
349     * operations. Always EndianType.BIG_ENDIAN since this class implements
350     * only big-endian.
351     *
352     * @return Always EndianType.BIG_ENDIAN.
353     *
354     * @see EndianType
355     *
356     */
357    public int getByteOrdering() {
358        return EndianType.BIG_ENDIAN;
359    }
360
361    /**
362     * Reads a signed byte (8 bit) from the input.
363     *
364     * @return The next byte-aligned signed byte (8 bit) from the
365     * input.
366     *
367     * @exception EOFException If the end-of file was reached before
368     * getting all the necessary data.
369     *
370     * @exception IOException If an I/O error ocurred.
371     * */
372    public byte readByte() throws IOException {
373        if (pos < len) { // common, fast case
374            return buf[pos++];
375        }
376        // general case
377        return (byte) read();
378    }
379
380    /**
381     * Reads an unsigned byte (8 bit) from the input.
382     *
383     * @return The next byte-aligned unsigned byte (8 bit) from the
384     * input.
385     *
386     * @exception EOFException If the end-of file was reached before
387     * getting all the necessary data.
388     *
389     * @exception IOException If an I/O error ocurred.
390     * */
391    public int readUnsignedByte() throws IOException {
392        if (pos < len) { // common, fast case
393            return 0xFF & buf[pos++];
394        }
395        // general case
396        return read();
397    }
398
399    /**
400     * Reads a signed short (16 bit) from the input.
401     *
402     * @return The next byte-aligned signed short (16 bit) from the
403     * input.
404     *
405     * @exception EOFException If the end-of file was reached before
406     * getting all the necessary data.
407     *
408     * @exception IOException If an I/O error ocurred.
409     * */
410    public short readShort() throws IOException {
411        if (pos+1 < len) { // common, fast case
412            return (short) ((buf[pos++]<<8) | (0xFF & buf[pos++]));
413        }
414        // general case
415        return (short) ((read()<<8) | read());
416    }
417
418    /**
419     * Reads an unsigned short (16 bit) from the input.
420     *
421     * @return The next byte-aligned unsigned short (16 bit) from the
422     * input.
423     *
424     * @exception EOFException If the end-of file was reached before
425     * getting all the necessary data.
426     *
427     * @exception IOException If an I/O error ocurred.
428     * */
429    public int readUnsignedShort() throws IOException {
430        if (pos+1 < len) { // common, fast case
431            return ((0xFF & buf[pos++])<<8) | (0xFF & buf[pos++]);
432        }
433        // general case
434        return (read()<<8) | read();
435    }
436
437    /**
438     * Reads a signed int (32 bit) from the input.
439     *
440     * @return The next byte-aligned signed int (32 bit) from the
441     * input.
442     *
443     * @exception EOFException If the end-of file was reached before
444     * getting all the necessary data.
445     *
446     * @exception IOException If an I/O error ocurred.
447     * */
448    public int readInt() throws IOException {
449        if (pos+3 < len) { // common, fast case
450            return ((buf[pos++]<<24) | ((0xFF & buf[pos++])<<16)
451                    | ((0xFF & buf[pos++])<<8) | (0xFF & buf[pos++]));
452        }
453        // general case
454        return (read()<<24) | (read()<<16) | (read()<<8) | read();
455    }
456
457    /**
458     * Reads a unsigned int (32 bit) from the input.
459     *
460     * @return The next byte-aligned unsigned int (32 bit) from the
461     * input.
462     *
463     * @exception EOFException If the end-of file was reached before
464     * getting all the necessary data.
465     *
466     * @exception IOException If an I/O error ocurred.
467     * */
468    public long readUnsignedInt() throws IOException {
469        if (pos+3 < len) { // common, fast case
470            return (0xFFFFFFFFL
471                    & (long)((buf[pos++]<<24) | ((0xFF & buf[pos++])<<16)
472                             | ((0xFF & buf[pos++])<<8) | (0xFF & buf[pos++])));
473        }
474        // general case
475        return (0xFFFFFFFFL
476                & (long)((read()<<24) | (read()<<16) | (read()<<8) | read()));
477    }
478
479    /**
480     * Reads a signed long (64 bit) from the input.
481     *
482     * @return The next byte-aligned signed long (64 bit) from the
483     * input.
484     *
485     * @exception EOFException If the end-of file was reached before
486     * getting all the necessary data.
487     *
488     * @exception IOException If an I/O error ocurred.
489     * */
490    public long readLong() throws IOException {
491        if (pos+7 < len) { // common, fast case
492            return (((long)buf[pos++]<<56)
493                    | ((long)(0xFF&buf[pos++])<<48)
494                    | ((long)(0xFF&buf[pos++])<<40)
495                    | ((long)(0xFF&buf[pos++])<<32)
496                    | ((long)(0xFF&buf[pos++])<<24)
497                    | ((long)(0xFF&buf[pos++])<<16)
498                    | ((long)(0xFF&buf[pos++])<<8)
499                    | (long)(0xFF&buf[pos++]));
500        }
501        // general case
502        return (((long)read()<<56)
503                | ((long)read()<<48)
504                | ((long)read()<<40)
505                | ((long)read()<<32)
506                | ((long)read()<<24)
507                | ((long)read()<<16)
508                | ((long)read()<<8)
509                | (long)read());
510    }
511
512    /**
513     * Reads an IEEE single precision (i.e., 32 bit) floating-point number
514     * from the input.
515     *
516     * @return The next byte-aligned IEEE float (32 bit) from the input.
517     *
518     * @exception EOFException If the end-of file was reached before
519     * getting all the necessary data.
520     *
521     * @exception IOException If an I/O error ocurred.
522     * */
523    public float readFloat() throws IOException {
524        if (pos+3 < len) { // common, fast case
525            return Float.intBitsToFloat((buf[pos++]<<24)
526                                        | ((0xFF & buf[pos++])<<16)
527                                        | ((0xFF & buf[pos++])<<8)
528                                        | (0xFF & buf[pos++]));
529        }
530        // general case
531        return Float.intBitsToFloat((read()<<24) | (read()<<16)
532                                    | (read()<<8) | read());
533    }
534
535    /**
536     * Reads an IEEE double precision (i.e., 64 bit) floating-point number
537     * from the input.
538     *
539     * @return The next byte-aligned IEEE double (64 bit) from the input.
540     *
541     * @exception EOFException If the end-of file was reached before
542     * getting all the necessary data.
543     *
544     * @exception IOException If an I/O error ocurred.
545     * */
546    public double readDouble() throws IOException {
547        if (pos+7 < len) { // common, fast case
548            return Double.longBitsToDouble(((long)buf[pos++]<<56)
549                                           | ((long)(0xFF&buf[pos++])<<48)
550                                           | ((long)(0xFF&buf[pos++])<<40)
551                                           | ((long)(0xFF&buf[pos++])<<32)
552                                           | ((long)(0xFF&buf[pos++])<<24)
553                                           | ((long)(0xFF&buf[pos++])<<16)
554                                           | ((long)(0xFF&buf[pos++])<<8)
555                                           | (long)(0xFF&buf[pos++]));
556        }
557        // general case
558        return Double.longBitsToDouble(((long)read()<<56)
559                                       | ((long)read()<<48)
560                                       | ((long)read()<<40)
561                                       | ((long)read()<<32)
562                                       | ((long)read()<<24)
563                                       | ((long)read()<<16)
564                                       | ((long)read()<<8)
565                                       | (long)read());
566    }
567
568    /**
569     * Skips 'n' bytes from the input.
570     *
571     * @param n The number of bytes to skip
572     *
573     * @return Always n.
574     *
575     * @exception EOFException If the end-of file was reached before
576     * all the bytes could be skipped.
577     *
578     * @exception IOException If an I/O error ocurred.
579     * */
580    public int skipBytes(int n) throws IOException {
581        if (complete) { /* we know the length, check skip is within length */
582            if (pos+n > len) {
583                throw new EOFException();
584            }
585        }
586        pos += n;
587        return n;
588    }
589
590    /**
591     * Does nothing since this class does not implement data output.
592     */
593    public void flush() { /* no-op */
594    }
595
596    /**
597     * Throws an IOException since this class does not implement data output.
598     */
599    public void write(int b) throws IOException {
600        throw new IOException("read-only");
601    }
602
603    /**
604     * Throws an IOException since this class does not implement data output.
605     */
606    public void writeByte(int v) throws IOException {
607        throw new IOException("read-only");
608    }
609
610    /**
611     * Throws an IOException since this class does not implement data output.
612     */
613    public void writeShort(int v) throws IOException {
614        throw new IOException("read-only");
615    }
616
617    /**
618     * Throws an IOException since this class does not implement data output.
619     */
620    public void writeInt(int v) throws IOException {
621        throw new IOException("read-only");
622    }
623
624    /**
625     * Throws an IOException since this class does not implement data output.
626     */
627    public void writeLong(long v) throws IOException {
628        throw new IOException("read-only");
629    }
630
631    /**
632     * Throws an IOException since this class does not implement data output.
633     */
634    public void writeFloat(float v) throws IOException {
635        throw new IOException("read-only");
636    }
637
638    /**
639     * Throws an IOException since this class does not implement data output.
640     */
641    public void writeDouble(double v) throws IOException {
642        throw new IOException("read-only");
643    }
644}