blob: ed6cbd34c16f9d38dfbbcb2b78f211baea1e92a1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* OBEO - fork for stand alone use in Acceleo
*******************************************************************************/
package org.eclipse.acceleo.model.mtl.resource;
// CHECKSTYLE:OFF
import java.io.IOException;
import java.io.InputStream;
/**
* Forked from org.eclipse.core
*
* @since 3.1
*/
public class LazyInputStream extends InputStream {
private int blockCapacity;
byte[][] blocks = {};
private int bufferSize;
private InputStream in;
private int mark;
private int offset;
public LazyInputStream(InputStream in, int blockCapacity) {
this.in = in;
this.blockCapacity = blockCapacity;
}
@Override
public int available() throws IOException {
try {
return bufferSize - offset + in.available();
} catch (IOException ioe) {
throw new LowLevelIOException(ioe);
}
}
private int computeBlockSize(int blockIndex) {
if (blockIndex < blocks.length - 1) {
return blockCapacity;
}
int blockSize = bufferSize % blockCapacity;
return blockSize == 0 ? blockCapacity : blockSize;
}
private int copyFromBuffer(byte[] userBuffer, int userOffset, int needed) {
int copied = 0;
int current = offset / blockCapacity;
while ((needed - copied) > 0 && current < blocks.length) {
int blockSize = computeBlockSize(current);
int offsetInBlock = offset % blockCapacity;
int availableInBlock = blockSize - offsetInBlock;
int toCopy = Math.min(availableInBlock, needed - copied);
System.arraycopy(blocks[current], offsetInBlock, userBuffer, userOffset + copied, toCopy);
copied += toCopy;
current++;
offset += toCopy;
}
return copied;
}
private void ensureAvailable(long bytesToRead) throws IOException {
int loadedBlockSize = blockCapacity;
while (bufferSize < offset + bytesToRead && loadedBlockSize == blockCapacity) {
try {
loadedBlockSize = loadBlock();
} catch (IOException e) {
throw new LowLevelIOException(e);
}
bufferSize += loadedBlockSize;
}
}
// for testing purposes
protected int getBlockCount() {
return blocks.length;
}
// for testing purposes
protected int getBufferSize() {
return bufferSize;
}
// for testing purposes
protected int getMark() {
return mark;
}
// for testing purposes
protected int getOffset() {
return offset;
}
public boolean isText() {
return false;
}
private int loadBlock() throws IOException {
// read a block from the underlying stream
byte[] newBlock = new byte[blockCapacity];
int readCount = in.read(newBlock);
if (readCount == -1) {
return 0;
}
// expand blocks array
byte[][] tmpBlocks = new byte[blocks.length + 1][];
System.arraycopy(blocks, 0, tmpBlocks, 0, blocks.length);
blocks = tmpBlocks;
blocks[blocks.length - 1] = newBlock;
return readCount;
}
@Override
public synchronized void mark(int readlimit) {
mark = offset;
}
@Override
public boolean markSupported() {
return true;
}
@Override
public int read() throws IOException {
ensureAvailable(1);
if (bufferSize <= offset) {
return -1;
}
int nextByte = 0xFF & blocks[offset / blockCapacity][offset % blockCapacity];
offset++;
return nextByte;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
ensureAvailable(len);
int copied = copyFromBuffer(b, off, len);
return copied == 0 ? -1 : copied;
}
@Override
public synchronized void reset() {
offset = mark;
}
public void rewind() {
mark = 0;
offset = 0;
}
@Override
public long skip(long toSkip) throws IOException {
if (toSkip <= 0) {
return 0;
}
ensureAvailable(toSkip);
long skipped = Math.min(toSkip, bufferSize - offset);
offset += skipped;
return skipped;
}
}