src/java.desktop/share/classes/javax/imageio/stream/MemoryCache.java

Print this page




  65     /**
  66      * The largest position ever written to the cache.
  67      */
  68     private long length = 0L;
  69 
  70     private byte[] getCacheBlock(long blockNum) throws IOException {
  71         long blockOffset = blockNum - cacheStart;
  72         if (blockOffset > Integer.MAX_VALUE) {
  73             // This can only happen when the cache hits 16 terabytes of
  74             // contiguous data...
  75             throw new IOException("Cache addressing limit exceeded!");
  76         }
  77         return cache.get((int)blockOffset);
  78     }
  79 
  80     /**
  81      * Ensures that at least <code>pos</code> bytes are cached,
  82      * or the end of the source is reached.  The return value
  83      * is equal to the smaller of <code>pos</code> and the
  84      * length of the source.


  85      */
  86     public long loadFromStream(InputStream stream, long pos)
  87         throws IOException {
  88         // We've already got enough data cached
  89         if (pos < length) {
  90             return pos;
  91         }
  92 
  93         int offset = (int)(length % BUFFER_LENGTH);
  94         byte [] buf = null;
  95 
  96         long len = pos - length;
  97         if (offset != 0) {
  98             buf = getCacheBlock(length/BUFFER_LENGTH);
  99         }
 100 
 101         while (len > 0) {
 102             if (buf == null) {
 103                 try {
 104                     buf = new byte[BUFFER_LENGTH];


 126             if (offset >= BUFFER_LENGTH) {
 127                 // we've filled the current buffer, so a new one will be
 128                 // allocated next time around (and offset will be reset to 0)
 129                 buf = null;
 130             }
 131         }
 132 
 133         return pos;
 134     }
 135 
 136     /**
 137      * Writes out a portion of the cache to an <code>OutputStream</code>.
 138      * This method preserves no state about the output stream, and does
 139      * not dispose of any blocks containing bytes written.  To dispose
 140      * blocks, use {@link #disposeBefore <code>disposeBefore()</code>}.
 141      *
 142      * @exception IndexOutOfBoundsException if any portion of
 143      * the requested data is not in the cache (including if <code>pos</code>
 144      * is in a block already disposed), or if either <code>pos</code> or
 145      * <code>len</code> is < 0.


 146      */
 147     public void writeToStream(OutputStream stream, long pos, long len)
 148         throws IOException {
 149         if (pos + len > length) {
 150             throw new IndexOutOfBoundsException("Argument out of cache");
 151         }
 152         if ((pos < 0) || (len < 0)) {
 153             throw new IndexOutOfBoundsException("Negative pos or len");
 154         }
 155         if (len == 0) {
 156             return;
 157         }
 158 
 159         long bufIndex = pos/BUFFER_LENGTH;
 160         if (bufIndex < cacheStart) {
 161             throw new IndexOutOfBoundsException("pos already disposed");
 162         }
 163         int offset = (int)(pos % BUFFER_LENGTH);
 164 
 165         byte[] buf = getCacheBlock(bufIndex++);
 166         while (len > 0) {
 167             if (buf == null) {
 168                 buf = getCacheBlock(bufIndex++);
 169                 offset = 0;
 170             }
 171             int nbytes = (int)Math.min(len, (long)(BUFFER_LENGTH - offset));
 172             stream.write(buf, offset, nbytes);
 173             buf = null;
 174             len -= nbytes;
 175         }
 176     }
 177 
 178     /**
 179      * Ensure that there is space to write a byte at the given position.


 180      */
 181     private void pad(long pos) throws IOException {
 182         long currIndex = cacheStart + cache.size() - 1;
 183         long lastIndex = pos/BUFFER_LENGTH;
 184         long numNewBuffers = lastIndex - currIndex;
 185         for (long i = 0; i < numNewBuffers; i++) {
 186             try {
 187                 cache.add(new byte[BUFFER_LENGTH]);
 188             } catch (OutOfMemoryError e) {
 189                 throw new IOException("No memory left for cache!");
 190             }
 191         }
 192     }
 193 
 194     /**
 195      * Overwrites and/or appends the cache from a byte array.
 196      * The length of the cache will be extended as needed to hold
 197      * the incoming data.
 198      *
 199      * @param b an array of bytes containing data to be written.
 200      * @param off the starting offset withing the data array.
 201      * @param len the number of bytes to be written.
 202      * @param pos the cache position at which to begin writing.
 203      *
 204      * @exception NullPointerException if <code>b</code> is <code>null</code>.
 205      * @exception IndexOutOfBoundsException if <code>off</code>,
 206      * <code>len</code>, or <code>pos</code> are negative,
 207      * or if <code>off+len > b.length</code>.

 208      */
 209     public void write(byte[] b, int off, int len, long pos)
 210         throws IOException {
 211         if (b == null) {
 212             throw new NullPointerException("b == null!");
 213         }
 214         // Fix 4430357 - if off + len < 0, overflow occurred
 215         if ((off < 0) || (len < 0) || (pos < 0) ||
 216             (off + len > b.length) || (off + len < 0)) {
 217             throw new IndexOutOfBoundsException();
 218         }
 219 
 220         // Ensure there is space for the incoming data
 221         long lastPos = pos + len - 1;
 222         if (lastPos >= length) {
 223             pad(lastPos);
 224             length = lastPos + 1;
 225         }
 226 
 227         // Copy the data into the cache, block by block


 231             int nbytes = Math.min(len, BUFFER_LENGTH - offset);
 232             System.arraycopy(b, off, buf, offset, nbytes);
 233 
 234             pos += nbytes;
 235             off += nbytes;
 236             len -= nbytes;
 237             offset = 0; // Always after the first time
 238         }
 239     }
 240 
 241     /**
 242      * Overwrites or appends a single byte to the cache.
 243      * The length of the cache will be extended as needed to hold
 244      * the incoming data.
 245      *
 246      * @param b an <code>int</code> whose 8 least significant bits
 247      * will be written.
 248      * @param pos the cache position at which to begin writing.
 249      *
 250      * @exception IndexOutOfBoundsException if <code>pos</code> is negative.

 251      */
 252     public void write(int b, long pos) throws IOException {
 253         if (pos < 0) {
 254             throw new ArrayIndexOutOfBoundsException("pos < 0");
 255         }
 256 
 257         // Ensure there is space for the incoming data
 258         if (pos >= length) {
 259             pad(pos);
 260             length = pos + 1;
 261         }
 262 
 263         // Insert the data.
 264         byte[] buf = getCacheBlock(pos/BUFFER_LENGTH);
 265         int offset = (int)(pos % BUFFER_LENGTH);
 266         buf[offset] = (byte)b;
 267     }
 268 
 269     /**
 270      * Returns the total length of data that has been cached,
 271      * regardless of whether any early blocks have been disposed.
 272      * This value will only ever increase.
 273      */
 274     public long getLength() {
 275         return length;
 276     }
 277 
 278     /**
 279      * Returns the single byte at the given position, as an
 280      * <code>int</code>.  Returns -1 if this position has
 281      * not been cached or has been disposed.



 282      */
 283     public int read(long pos) throws IOException {
 284         if (pos >= length) {
 285             return -1;
 286         }
 287 
 288         byte[] buf = getCacheBlock(pos/BUFFER_LENGTH);
 289         if (buf == null) {
 290             return -1;
 291         }
 292 
 293         return buf[(int)(pos % BUFFER_LENGTH)] & 0xff;
 294     }
 295 
 296     /**
 297      * Copy <code>len</code> bytes from the cache, starting
 298      * at cache position <code>pos</code>, into the array
 299      * <code>b</code> at offset <code>off</code>.
 300      *
 301      * @exception NullPointerException if b is <code>null</code>
 302      * @exception IndexOutOfBoundsException if <code>off</code>,
 303      * <code>len</code> or <code>pos</code> are negative or if
 304      * <code>off + len > b.length</code> or if any portion of the
 305      * requested data is not in the cache (including if
 306      * <code>pos</code> is in a block that has already been disposed).


 307      */
 308     public void read(byte[] b, int off, int len, long pos)
 309         throws IOException {
 310         if (b == null) {
 311             throw new NullPointerException("b == null!");
 312         }
 313         // Fix 4430357 - if off + len < 0, overflow occurred
 314         if ((off < 0) || (len < 0) || (pos < 0) ||
 315             (off + len > b.length) || (off + len < 0)) {
 316             throw new IndexOutOfBoundsException();
 317         }
 318         if (pos + len > length) {
 319             throw new IndexOutOfBoundsException();
 320         }
 321 
 322         long index = pos/BUFFER_LENGTH;
 323         int offset = (int)pos % BUFFER_LENGTH;
 324         while (len > 0) {
 325             int nbytes = Math.min(len, BUFFER_LENGTH - offset);
 326             byte[] buf = getCacheBlock(index++);




  65     /**
  66      * The largest position ever written to the cache.
  67      */
  68     private long length = 0L;
  69 
  70     private byte[] getCacheBlock(long blockNum) throws IOException {
  71         long blockOffset = blockNum - cacheStart;
  72         if (blockOffset > Integer.MAX_VALUE) {
  73             // This can only happen when the cache hits 16 terabytes of
  74             // contiguous data...
  75             throw new IOException("Cache addressing limit exceeded!");
  76         }
  77         return cache.get((int)blockOffset);
  78     }
  79 
  80     /**
  81      * Ensures that at least <code>pos</code> bytes are cached,
  82      * or the end of the source is reached.  The return value
  83      * is equal to the smaller of <code>pos</code> and the
  84      * length of the source.
  85      * 
  86      * @throws IOException if there is no more memory for cache
  87      */
  88     public long loadFromStream(InputStream stream, long pos)
  89         throws IOException {
  90         // We've already got enough data cached
  91         if (pos < length) {
  92             return pos;
  93         }
  94 
  95         int offset = (int)(length % BUFFER_LENGTH);
  96         byte [] buf = null;
  97 
  98         long len = pos - length;
  99         if (offset != 0) {
 100             buf = getCacheBlock(length/BUFFER_LENGTH);
 101         }
 102 
 103         while (len > 0) {
 104             if (buf == null) {
 105                 try {
 106                     buf = new byte[BUFFER_LENGTH];


 128             if (offset >= BUFFER_LENGTH) {
 129                 // we've filled the current buffer, so a new one will be
 130                 // allocated next time around (and offset will be reset to 0)
 131                 buf = null;
 132             }
 133         }
 134 
 135         return pos;
 136     }
 137 
 138     /**
 139      * Writes out a portion of the cache to an <code>OutputStream</code>.
 140      * This method preserves no state about the output stream, and does
 141      * not dispose of any blocks containing bytes written.  To dispose
 142      * blocks, use {@link #disposeBefore <code>disposeBefore()</code>}.
 143      *
 144      * @exception IndexOutOfBoundsException if any portion of
 145      * the requested data is not in the cache (including if <code>pos</code>
 146      * is in a block already disposed), or if either <code>pos</code> or
 147      * <code>len</code> is < 0.
 148      * @throws IOException if there is an I/O exception while writing to the
 149      * stream
 150      */
 151     public void writeToStream(OutputStream stream, long pos, long len)
 152         throws IOException {
 153         if (pos + len > length) {
 154             throw new IndexOutOfBoundsException("Argument out of cache");
 155         }
 156         if ((pos < 0) || (len < 0)) {
 157             throw new IndexOutOfBoundsException("Negative pos or len");
 158         }
 159         if (len == 0) {
 160             return;
 161         }
 162 
 163         long bufIndex = pos/BUFFER_LENGTH;
 164         if (bufIndex < cacheStart) {
 165             throw new IndexOutOfBoundsException("pos already disposed");
 166         }
 167         int offset = (int)(pos % BUFFER_LENGTH);
 168 
 169         byte[] buf = getCacheBlock(bufIndex++);
 170         while (len > 0) {
 171             if (buf == null) {
 172                 buf = getCacheBlock(bufIndex++);
 173                 offset = 0;
 174             }
 175             int nbytes = (int)Math.min(len, (long)(BUFFER_LENGTH - offset));
 176             stream.write(buf, offset, nbytes);
 177             buf = null;
 178             len -= nbytes;
 179         }
 180     }
 181 
 182     /**
 183      * Ensure that there is space to write a byte at the given position.
 184      * 
 185      * throws IOException if there is no more memory left for cache
 186      */
 187     private void pad(long pos) throws IOException {
 188         long currIndex = cacheStart + cache.size() - 1;
 189         long lastIndex = pos/BUFFER_LENGTH;
 190         long numNewBuffers = lastIndex - currIndex;
 191         for (long i = 0; i < numNewBuffers; i++) {
 192             try {
 193                 cache.add(new byte[BUFFER_LENGTH]);
 194             } catch (OutOfMemoryError e) {
 195                 throw new IOException("No memory left for cache!");
 196             }
 197         }
 198     }
 199 
 200     /**
 201      * Overwrites and/or appends the cache from a byte array.
 202      * The length of the cache will be extended as needed to hold
 203      * the incoming data.
 204      *
 205      * @param b an array of bytes containing data to be written.
 206      * @param off the starting offset within the data array.
 207      * @param len the number of bytes to be written.
 208      * @param pos the cache position at which to begin writing.
 209      *
 210      * @exception NullPointerException if <code>b</code> is <code>null</code>.
 211      * @exception IndexOutOfBoundsException if <code>off</code>,
 212      * <code>len</code>, or <code>pos</code> are negative,
 213      * or if <code>off+len > b.length</code>.
 214      * @throws IOException if there is an I/O error while writing to the cache
 215      */
 216     public void write(byte[] b, int off, int len, long pos)
 217         throws IOException {
 218         if (b == null) {
 219             throw new NullPointerException("b == null!");
 220         }
 221         // Fix 4430357 - if off + len < 0, overflow occurred
 222         if ((off < 0) || (len < 0) || (pos < 0) ||
 223             (off + len > b.length) || (off + len < 0)) {
 224             throw new IndexOutOfBoundsException();
 225         }
 226 
 227         // Ensure there is space for the incoming data
 228         long lastPos = pos + len - 1;
 229         if (lastPos >= length) {
 230             pad(lastPos);
 231             length = lastPos + 1;
 232         }
 233 
 234         // Copy the data into the cache, block by block


 238             int nbytes = Math.min(len, BUFFER_LENGTH - offset);
 239             System.arraycopy(b, off, buf, offset, nbytes);
 240 
 241             pos += nbytes;
 242             off += nbytes;
 243             len -= nbytes;
 244             offset = 0; // Always after the first time
 245         }
 246     }
 247 
 248     /**
 249      * Overwrites or appends a single byte to the cache.
 250      * The length of the cache will be extended as needed to hold
 251      * the incoming data.
 252      *
 253      * @param b an <code>int</code> whose 8 least significant bits
 254      * will be written.
 255      * @param pos the cache position at which to begin writing.
 256      *
 257      * @exception IndexOutOfBoundsException if <code>pos</code> is negative.
 258      * @throws IOException if there is an I/O error while writing to the cache
 259      */
 260     public void write(int b, long pos) throws IOException {
 261         if (pos < 0) {
 262             throw new ArrayIndexOutOfBoundsException("pos < 0");
 263         }
 264 
 265         // Ensure there is space for the incoming data
 266         if (pos >= length) {
 267             pad(pos);
 268             length = pos + 1;
 269         }
 270 
 271         // Insert the data.
 272         byte[] buf = getCacheBlock(pos/BUFFER_LENGTH);
 273         int offset = (int)(pos % BUFFER_LENGTH);
 274         buf[offset] = (byte)b;
 275     }
 276 
 277     /**
 278      * Returns the total length of data that has been cached,
 279      * regardless of whether any early blocks have been disposed.
 280      * This value will only ever increase.
 281      */
 282     public long getLength() {
 283         return length;
 284     }
 285 
 286     /**
 287      * Returns the single byte at the given position, as an
 288      * <code>int</code>.  Returns -1 if this position has
 289      * not been cached or has been disposed.
 290      * 
 291      * @throws IOException if an I/O error occurs while reading from the byte
 292      * array
 293      */
 294     public int read(long pos) throws IOException {
 295         if (pos >= length) {
 296             return -1;
 297         }
 298 
 299         byte[] buf = getCacheBlock(pos/BUFFER_LENGTH);
 300         if (buf == null) {
 301             return -1;
 302         }
 303 
 304         return buf[(int)(pos % BUFFER_LENGTH)] & 0xff;
 305     }
 306 
 307     /**
 308      * Copy <code>len</code> bytes from the cache, starting
 309      * at cache position <code>pos</code>, into the array
 310      * <code>b</code> at offset <code>off</code>.
 311      *
 312      * @exception NullPointerException if b is <code>null</code>
 313      * @exception IndexOutOfBoundsException if <code>off</code>,
 314      * <code>len</code> or <code>pos</code> are negative or if
 315      * <code>off + len > b.length</code> or if any portion of the
 316      * requested data is not in the cache (including if
 317      * <code>pos</code> is in a block that has already been disposed).
 318      * @throws IOException if an I/O exception occurs while reading from the
 319      * byte array
 320      */
 321     public void read(byte[] b, int off, int len, long pos)
 322         throws IOException {
 323         if (b == null) {
 324             throw new NullPointerException("b == null!");
 325         }
 326         // Fix 4430357 - if off + len < 0, overflow occurred
 327         if ((off < 0) || (len < 0) || (pos < 0) ||
 328             (off + len > b.length) || (off + len < 0)) {
 329             throw new IndexOutOfBoundsException();
 330         }
 331         if (pos + len > length) {
 332             throw new IndexOutOfBoundsException();
 333         }
 334 
 335         long index = pos/BUFFER_LENGTH;
 336         int offset = (int)pos % BUFFER_LENGTH;
 337         while (len > 0) {
 338             int nbytes = Math.min(len, BUFFER_LENGTH - offset);
 339             byte[] buf = getCacheBlock(index++);