/**
* Copyright 2011-2017 Asakusa Framework Team.
*
* 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 com.asakusafw.runtime.io.util;
import java.io.Closeable;
import java.io.DataInput;
import java.io.EOFException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.RandomAccess;
/**
* An implementation of {@link DataInput} for {@link RandomAccessFile}.
* @since 0.7.0
*/
public class BufferedFileInput implements RandomAccess, DataInput, Closeable {
private final byte[] buffuer;
private final RandomAccessFile file;
private long physicalPointer = -1L;
private long physicalSize = -1L;
private int offset = 0;
private int limit = 0;
/**
* Creates a new instance.
* @param bufferSize the buffer size
* @param file the target file
*/
public BufferedFileInput(RandomAccessFile file, int bufferSize) {
this(file, new byte[bufferSize]);
}
/**
* Creates a new instance.
* @param buffer internal buffer
* @param file the target file
*/
public BufferedFileInput(RandomAccessFile file, byte[] buffer) {
this.buffuer = buffer;
this.file = file;
}
/**
* Resets internal buffer.
* @throws IOException if failed by I/O error
*/
public void sync() throws IOException {
physicalPointer = -1L;
offset = 0;
limit = 0;
}
/**
* Sets cursor in the target file.
* @param position the new position
* @throws IOException if failed by I/O error
*/
public void seek(long position) throws IOException {
long begin = getPosition() - offset;
long end = begin + limit;
if (0 <= begin && begin <= position && position <= end) {
offset = (int) (position - begin);
} else {
file.seek(position);
physicalPointer = Math.min(position, getSize());
offset = 0;
limit = 0;
}
}
/**
* Returns the current file position in bytes.
* @return the current position
* @throws IOException if failed by I/O error
*/
public long getPosition() throws IOException {
if (physicalPointer < 0L) {
physicalPointer = file.getFilePointer();
}
return physicalPointer - (limit - offset);
}
/**
* Returns the file size in bytes.
* @return the file size
* @throws IOException if failed by I/O error
*/
public long getSize() throws IOException {
if (physicalSize < 0L) {
physicalSize = file.length();
}
return physicalSize;
}
@Override
public void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
@Override
public void readFully(byte[] b, int off, int len) throws IOException {
int last = off + len;
int rest = len;
while (rest > 0) {
int available = prepare();
if (available == 0) {
throw new EOFException();
}
int size = Math.min(rest, available);
System.arraycopy(buffuer, offset, b, last - rest, size);
offset += size;
rest -= size;
}
}
@Override
public int skipBytes(int n) throws IOException {
int result = 0;
if (offset < limit) {
result = Math.min(n, limit - offset);
offset += result;
}
if (result < n) {
int skip = file.skipBytes(n - result);
if (skip > 0 && physicalPointer >= 0) {
physicalPointer += skip;
}
return result + skip;
} else {
return result;
}
}
@Override
public boolean readBoolean() throws IOException {
return read() != 0;
}
@Override
public short readShort() throws IOException {
byte d0 = read();
byte d1 = read();
return (short) (0
| (d0 << Byte.SIZE * 1) & (0xff << Byte.SIZE * 1)
| (d1 << Byte.SIZE * 0) & (0xff << Byte.SIZE * 0)
);
}
@Override
public byte readByte() throws IOException {
return read();
}
@Override
public int readInt() throws IOException {
byte d3 = read();
byte d2 = read();
byte d1 = read();
byte d0 = read();
return 0
| (d3 << Byte.SIZE * 3) & (0xff << Byte.SIZE * 3)
| (d2 << Byte.SIZE * 2) & (0xff << Byte.SIZE * 2)
| (d1 << Byte.SIZE * 1) & (0xff << Byte.SIZE * 1)
| (d0 << Byte.SIZE * 0) & (0xff << Byte.SIZE * 0)
;
}
@Override
public long readLong() throws IOException {
int i1 = readInt();
int i0 = readInt();
return ((long) i1 << Integer.SIZE) | (i0 & 0xffffffffL);
}
@Override
public float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
@Override
public double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
@Override
public int readUnsignedByte() throws IOException {
return read() & 0xff;
}
@Override
public int readUnsignedShort() throws IOException {
return readShort() & 0xffff;
}
@Override
public char readChar() throws IOException {
return (char) (readShort() & 0xffff);
}
@Override
public String readLine() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public String readUTF() throws IOException {
return DataIoUtils.readUTF(this);
}
private byte read() throws IOException {
int rest = prepare();
if (rest < 1) {
throw new EOFException();
}
return buffuer[offset++];
}
private int prepare() throws IOException {
if (offset >= limit) {
offset = 0;
limit = 0;
if (physicalPointer < 0) {
physicalPointer = file.getFilePointer();
}
int read = file.read(buffuer);
if (read > 0) {
limit = read;
physicalPointer += read;
}
}
return limit - offset;
}
@Override
public void close() throws IOException {
file.close();
}
}