/*
* Copyright 2013 Eediom Inc.
*
* Licensed 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.araqne.logstorage.file;
import java.io.Closeable;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import org.araqne.storage.api.FilePath;
import org.araqne.storage.api.StorageInputStream;
public class BufferedStorageInputStream implements DataInput, Closeable {
private static final int BUFFER_SIZE = 8192 * 2;
private final StorageInputStream storageInputStream;
private ByteBuffer buf;
private DataInputStream dataInputStream;
private boolean isInvalidated = true;
private long bufStartPos = 0;
private boolean isClosed = false;
private int bufSize;
public BufferedStorageInputStream(FilePath path) throws IOException {
this(path, BUFFER_SIZE);
}
public BufferedStorageInputStream(StorageInputStream sis) throws IOException {
this(sis, BUFFER_SIZE);
}
public BufferedStorageInputStream(FilePath path, int bufSize) throws IOException {
this(path.newInputStream(), bufSize);
}
public BufferedStorageInputStream(StorageInputStream sis, int bufSize) throws IOException {
this.storageInputStream = sis;
this.bufSize = bufSize;
buf = ByteBuffer.allocate(bufSize);
dataInputStream = new DataInputStream(new InputStream() {
@Override
public synchronized int read() throws IOException {
if (!buf.hasRemaining()) {
if (storageInputStream.length() - (bufStartPos + buf.position()) < 1)
return -1;
else
syncBuffer();
}
return (int) buf.get() & 0xff;
}
@Override
public synchronized int read(byte[] bytes, int off, int len) throws IOException {
if (len > buf.capacity()) {
long seekpos = bufStartPos + buf.position();
bufStartPos += buf.position() + len;
buf.position(0);
isInvalidated = true;
storageInputStream.seek(seekpos);
return storageInputStream.read(bytes, off, len);
} else if (len > buf.remaining()) {
bufStartPos += buf.position();
buf.position(0);
if (bufStartPos >= storageInputStream.length())
return -1;
syncBuffer();
len = Math.min(len, buf.remaining());
buf.get(bytes, off, len);
return len;
} else {
len = Math.min(len, buf.remaining());
buf.get(bytes, off, len);
return len;
}
}
});
}
@Override
public void readFully(byte[] b) throws IOException {
if (isInvalidated) {
syncBuffer();
}
dataInputStream.readFully(b);
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
if (isInvalidated) {
syncBuffer();
}
dataInputStream.readFully(b, off, len);
}
@Override
public int skipBytes(int n) throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.skipBytes(n);
}
@Override
public boolean readBoolean() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readBoolean();
}
@Override
public byte readByte() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readByte();
}
@Override
public int readUnsignedByte() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readUnsignedByte();
}
@Override
public short readShort() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readShort();
}
@Override
public int readUnsignedShort() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readUnsignedShort();
}
@Override
public char readChar() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readChar();
}
@Override
public int readInt() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readInt();
}
@Override
public long readLong() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readLong();
}
@Override
public float readFloat() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readFloat();
}
@Override
public double readDouble() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readDouble();
}
@SuppressWarnings("deprecation")
@Override
public String readLine() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readLine();
}
@Override
public String readUTF() throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.readUTF();
}
public void seek(long pos) throws IOException {
if (pos > bufStartPos + buf.remaining()) {
buf.clear();
bufStartPos = pos;
syncBuffer(buf.capacity());
} else if (pos - bufStartPos < 0) {
buf.clear();
long seekPos = pos - bufSize;
if (seekPos < 0)
seekPos = 0;
bufStartPos = seekPos;
storageInputStream.seek(bufStartPos);
buf.clear();
if (buf.capacity() < bufSize * 2) {
buf = ByteBuffer.allocate(bufSize * 2);
}
int read = storageInputStream.read(buf.array());
if (read != -1) {
buf.limit(read);
if (seekPos == 0) {
buf.position((int) pos);
} else {
buf.position(bufSize);
}
}
isInvalidated = false;
} else {
buf.position((int) (pos - bufStartPos));
}
}
public long length() throws IOException {
return storageInputStream.length();
}
private void syncBuffer() throws IOException {
syncBuffer((int) buf.capacity());
}
private void syncBuffer(int bufSize) throws IOException {
long nextSeekPos = bufStartPos + buf.position();
long currLength = storageInputStream.length();
if (nextSeekPos >= currLength)
throw new EOFException("expected pos " + nextSeekPos + " of " + getPath() + " but actual length is " + currLength);
storageInputStream.seek(nextSeekPos);
bufStartPos = nextSeekPos;
buf.clear();
if (buf.capacity() < bufSize) {
if (bufSize != buf.capacity()) {
buf = ByteBuffer.allocate(bufSize);
}
}
int read = storageInputStream.read(buf.array());
if (read != -1) {
buf.limit(read);
}
isInvalidated = false;
}
public void close() throws IOException {
if (isClosed)
return;
try {
storageInputStream.close();
} catch (IOException e) {
}
isClosed = true;
}
public FilePath getPath() {
return storageInputStream.getPath();
}
public int read(byte[] b) throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.read(b);
}
public int read(byte[] b, int off, int len) throws IOException {
if (isInvalidated) {
syncBuffer();
}
return dataInputStream.read(b, off, len);
}
}