org.h2.store.FileStoreInputStream Maven / Gradle / Ivy
/*
* Copyright 2004-2022 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.store;
import java.io.IOException;
import java.io.InputStream;
import org.h2.engine.Constants;
import org.h2.message.DbException;
import org.h2.tools.CompressTool;
import org.h2.util.Utils;
/**
* An input stream that is backed by a file store.
*/
public class FileStoreInputStream extends InputStream {
private FileStore store;
private final Data page;
private int remainingInBuffer;
private final CompressTool compress;
private boolean endOfFile;
private final boolean alwaysClose;
public FileStoreInputStream(FileStore store, boolean compression, boolean alwaysClose) {
this.store = store;
this.alwaysClose = alwaysClose;
if (compression) {
compress = CompressTool.getInstance();
} else {
compress = null;
}
page = Data.create(Constants.FILE_BLOCK_SIZE);
try {
if (store.length() <= FileStore.HEADER_LENGTH) {
close();
} else {
fillBuffer();
}
} catch (IOException e) {
throw DbException.convertIOException(e, store.name);
}
}
@Override
public int available() {
return remainingInBuffer <= 0 ? 0 : remainingInBuffer;
}
@Override
public int read(byte[] buff) throws IOException {
return read(buff, 0, buff.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (len == 0) {
return 0;
}
int read = 0;
while (len > 0) {
int r = readBlock(b, off, len);
if (r < 0) {
break;
}
read += r;
off += r;
len -= r;
}
return read == 0 ? -1 : read;
}
private int readBlock(byte[] buff, int off, int len) throws IOException {
fillBuffer();
if (endOfFile) {
return -1;
}
int l = Math.min(remainingInBuffer, len);
page.read(buff, off, l);
remainingInBuffer -= l;
return l;
}
private void fillBuffer() throws IOException {
if (remainingInBuffer > 0 || endOfFile) {
return;
}
page.reset();
store.openFile();
if (store.length() == store.getFilePointer()) {
close();
return;
}
store.readFully(page.getBytes(), 0, Constants.FILE_BLOCK_SIZE);
page.reset();
remainingInBuffer = page.readInt();
if (remainingInBuffer < 0) {
close();
return;
}
page.checkCapacity(remainingInBuffer);
// get the length to read
if (compress != null) {
page.checkCapacity(Integer.BYTES);
page.readInt();
}
page.setPos(page.length() + remainingInBuffer);
page.fillAligned();
int len = page.length() - Constants.FILE_BLOCK_SIZE;
page.reset();
page.readInt();
store.readFully(page.getBytes(), Constants.FILE_BLOCK_SIZE, len);
page.reset();
page.readInt();
if (compress != null) {
int uncompressed = page.readInt();
byte[] buff = Utils.newBytes(remainingInBuffer);
page.read(buff, 0, remainingInBuffer);
page.reset();
page.checkCapacity(uncompressed);
CompressTool.expand(buff, page.getBytes(), 0);
remainingInBuffer = uncompressed;
}
if (alwaysClose) {
store.closeFile();
}
}
@Override
public void close() {
if (store != null) {
try {
store.close();
endOfFile = true;
} finally {
store = null;
}
}
}
@Override
protected void finalize() {
close();
}
@Override
public int read() throws IOException {
fillBuffer();
if (endOfFile) {
return -1;
}
int i = page.readByte() & 0xff;
remainingInBuffer--;
return i;
}
}