/*
* Copyright 2011 Future Systems
*
* 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.krakenapps.logstorage.file;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class BufferedRandomAccessFileReader implements DataInput {
private static final int BUFFER_SIZE = 8192;
private final RandomAccessFile file;
private ByteBuffer buf;
private DataInputStream dataInputStream;
private boolean isInvalidated = true;
private long bufStartPos = 0;
private boolean isClosed = false;
public BufferedRandomAccessFileReader(File path) throws FileNotFoundException {
file = new RandomAccessFile(path, "r");
buf = ByteBuffer.allocate(BUFFER_SIZE);
dataInputStream = new DataInputStream(new InputStream() {
@Override
public synchronized int read() throws IOException {
if (!buf.hasRemaining()) {
if (file.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()) {
bufStartPos += buf.position();
isInvalidated = true;
return file.read(bytes, off, len);
} else if (len > buf.remaining()) {
bufStartPos += buf.position();
syncBuffer();
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 - BUFFER_SIZE;
if (seekPos < 0)
seekPos = 0;
bufStartPos = seekPos;
file.seek(bufStartPos);
buf.clear();
if (buf.capacity() < BUFFER_SIZE * 2) {
buf = ByteBuffer.allocate(BUFFER_SIZE * 2);
}
int read = file.read(buf.array());
if (read != -1) {
buf.limit(read);
if (seekPos == 0) {
buf.position((int) pos);
} else {
buf.position(BUFFER_SIZE);
}
}
isInvalidated = false;
} else {
buf.position((int) (pos - bufStartPos));
}
}
public long length() throws IOException {
return file.length();
}
public long getFilePointer() throws IOException {
invalidateBuffer();
return file.getFilePointer();
}
public FileDescriptor getFD() throws IOException {
invalidateBuffer();
return file.getFD();
}
public FileChannel getChannel() {
invalidateBuffer();
return file.getChannel();
}
private void invalidateBuffer() {
isInvalidated = true;
}
private void syncBuffer() throws IOException {
syncBuffer((int) buf.capacity());
}
private void syncBuffer(int bufSize) throws IOException {
long nextSeekPos = bufStartPos + buf.position();
file.seek(nextSeekPos);
bufStartPos = nextSeekPos;
buf.clear();
if (buf.capacity() < bufSize) {
if (bufSize != buf.capacity()) {
buf = ByteBuffer.allocate(bufSize);
}
}
int read = file.read(buf.array());
if (read != -1) {
buf.limit(read);
}
isInvalidated = false;
}
public void close() throws IOException {
if (isClosed)
return;
file.close();
isClosed = true;
}
}