/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.x.imageio.stream; import java.util.ArrayList; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public final class RandomAccessMemoryCache { private static final int BLOCK_SHIFT = 9; private static final int BLOCK_SIZE = 1 << BLOCK_SHIFT; private static final int BLOCK_MASK = BLOCK_SIZE - 1; private long length; private int firstUndisposed = 0; private ArrayList<byte[]> blocks = new ArrayList<byte[]>(); public RandomAccessMemoryCache() { } public long length() { return length; } public void close() { blocks.clear(); length = 0; } private void grow(long pos) { int blocksNeeded = (int)(pos >> BLOCK_SHIFT) - blocks.size() + 1; for (int i=0; i < blocksNeeded; i++) { blocks.add(new byte[BLOCK_SIZE]); } length = pos + 1; } public void putData(int oneByte, long pos) { if (pos >= length) { grow(pos); } byte[] block = blocks.get((int)(pos >> BLOCK_SHIFT)); block[(int)(pos & BLOCK_MASK)] = (byte) oneByte; } public void putData(byte[] buffer, int offset, int count, long pos) { if (count > buffer.length - offset || count < 0 || offset < 0) { throw new IndexOutOfBoundsException(); } if (count == 0){ return; } long lastPos = pos + count - 1; if (lastPos >= length) { grow(lastPos); } while (count > 0) { byte[] block = blocks.get((int)(pos >> BLOCK_SHIFT)); int blockOffset = (int)(pos & BLOCK_MASK); int toCopy = Math.min(BLOCK_SIZE - blockOffset, count); System.arraycopy(buffer, offset, block, blockOffset, toCopy); pos += toCopy; count -= toCopy; offset += toCopy; } } public int getData(long pos) { if (pos >= length) { return -1; } byte[] block = blocks.get((int)(pos >> BLOCK_SHIFT)); return block[(int)(pos & BLOCK_MASK)] & 0xFF; } public int getData(byte[] buffer, int offset, int count, long pos) { if (count > buffer.length - offset || count < 0 || offset < 0) { throw new IndexOutOfBoundsException(); } if (count == 0) { return 0; } if (pos >= length) { return -1; } if (count + pos > length) { count = (int) (length - pos); } byte[] block = blocks.get((int)(pos >> BLOCK_SHIFT)); int nbytes = Math.min(count, BLOCK_SIZE - (int)(pos & BLOCK_MASK)); System.arraycopy(block, (int)(pos & BLOCK_MASK), buffer, offset, nbytes); return nbytes; } /* public void seek(long pos) throws IOException { if (pos < 0) { throw new IOException("seek position is negative"); } this.pos = pos; } public void readFully(byte[] buffer) throws IOException { readFully(buffer, 0, buffer.length); } public void readFully(byte[] buffer, int offset, int count) throws IOException { if (0 <= offset && offset <= buffer.length && 0 <= count && count <= buffer.length - offset) { while (count > 0) { int result = read(buffer, offset, count); if (result >= 0) { offset += result; count -= result; } else { throw new EOFException(); } } } else { throw new IndexOutOfBoundsException(); } } public long getFilePointer() { return pos; } */ public void freeBefore(long pos) { int blockIdx = (int)(pos >> BLOCK_SHIFT); if (blockIdx <= firstUndisposed) { // Nothing to do return; } for (int i = firstUndisposed; i < blockIdx; i++) { blocks.set(i, null); } firstUndisposed = blockIdx; } public int appendData(InputStream is, int count) throws IOException { if (count <= 0) { return 0; } long startPos = length; long lastPos = length + count - 1; grow(lastPos); // Changes length int blockIdx = (int)(startPos >> BLOCK_SHIFT); int offset = (int) (startPos & BLOCK_MASK); int bytesAppended = 0; while (count > 0) { byte[] block = blocks.get(blockIdx); int toCopy = Math.min(BLOCK_SIZE - offset, count); count -= toCopy; while (toCopy > 0) { int bytesRead = is.read(block, offset, toCopy); if (bytesRead < 0) { length -= (count - bytesAppended); return bytesAppended; } toCopy -= bytesRead; offset += bytesRead; } blockIdx++; offset = 0; } return count; } public void getData(OutputStream os, int count, long pos) throws IOException { if (pos + count > length) { throw new IndexOutOfBoundsException("Argument out of cache"); } int blockIdx = (int)(pos >> BLOCK_SHIFT); int offset = (int) (pos & BLOCK_MASK); if (blockIdx < firstUndisposed) { throw new IndexOutOfBoundsException("The requested data are already disposed"); } while (count > 0) { byte[] block = blocks.get(blockIdx); int toWrite = Math.min(BLOCK_SIZE - offset, count); os.write(block, offset, toWrite); blockIdx++; offset = 0; count -= toWrite; } } }