blob: 64f7b2a09b1090ae09252594663a899dd3c67765 [file] [log] [blame]
/*
* 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;
}
}
}