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