/*
* Copyright 2010 NCHOVY
*
* 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.rrd.io;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class FilePersistentLayer extends PersistentLayer {
private static final int DEFAULT_CAPACITY = 64 * 1024;
private File file;
private RandomAccessFile raf;
private int capacity;
private ByteBuffer bb;
private long fp = -1L;
private long length;
private int maxPos = 0;
private volatile boolean modified = false;
private boolean read;
public FilePersistentLayer(File file) throws IOException {
this(file, DEFAULT_CAPACITY);
}
public FilePersistentLayer(File file, boolean read) throws IOException {
this(file, DEFAULT_CAPACITY, read);
}
public FilePersistentLayer(File file, int capacity) throws IOException {
this(file, capacity, true);
}
public FilePersistentLayer(File file, int capacity, boolean read) throws IOException {
this.file = file;
this.bb = ByteBuffer.allocate(capacity);
this.capacity = capacity;
open(read);
}
@Override
public void open(boolean read) {
this.read = read;
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
}
}
try {
raf = new RandomAccessFile(file, "rw");
if (!read)
raf.setLength(0L);
seek(0);
} catch (IOException e) {
}
}
private synchronized void seek(long pos) throws IOException {
length = Math.max(length, Math.max(pos, fp + bb.position()));
int offset = (int) (pos % capacity);
if (fp != pos - offset) {
if (modified)
flush();
fp = pos - offset;
raf.seek(fp);
int readed = read ? raf.read(bb.array()) : -1;
if (readed != capacity) {
if (readed == -1)
readed = 0;
Arrays.fill(bb.array(), readed, capacity, (byte) 0x00);
}
maxPos = offset;
} else
maxPos = Math.max(maxPos, Math.max(offset, bb.position()));
bb.position(offset);
}
public long length() {
return Math.max(length, fp + Math.max(maxPos, bb.position()));
}
public synchronized void flush() throws IOException {
raf.seek(fp);
raf.write(bb.array(), 0, Math.max(maxPos, bb.position()));
modified = false;
}
@Override
public synchronized void writeByte(int v) throws IOException {
byte b = (byte) (v & 0xFF);
if (!bb.hasRemaining())
seek(fp + capacity);
bb.put(b);
modified = true;
}
public synchronized void write(byte[] b) throws IOException {
write(b, 0, b.length);
}
public synchronized void write(byte[] b, int offset, int length) throws IOException {
int remain = bb.remaining();
if (remain >= length) {
bb.put(b, offset, length);
modified = true;
} else {
bb.put(b, offset, remain);
modified = true;
raf.seek(fp + capacity);
raf.write(b, offset + remain, length - remain);
seek(fp + capacity + (length - remain));
}
}
@Override
public synchronized void writeBoolean(boolean v) throws IOException {
writeByte(v ? 1 : 0);
}
@Override
public synchronized void writeShort(int v) throws IOException {
short s = (short) (v & 0xFF);
int remain = bb.remaining();
if (remain >= 2) {
bb.putShort(s);
modified = true;
} else {
for (int i = 1; i >= 0; i--) {
if (!bb.hasRemaining())
seek(fp + capacity);
bb.put((byte) ((s >>> (i * 8)) & 0xFF));
modified = true;
}
}
}
@Override
public synchronized void writeChar(int v) throws IOException {
writeShort((char) (v & 0xFF));
}
@Override
public synchronized void write(int b) throws IOException {
writeInt(b);
}
public synchronized void writeInt(int i) throws IOException {
int remain = bb.remaining();
if (remain >= 4) {
bb.putInt(i);
modified = true;
} else {
for (int j = 3; j >= 0; j--) {
if (!bb.hasRemaining())
seek(fp + capacity);
bb.put((byte) ((i >>> (j * 8)) & 0xFF));
modified = true;
}
}
}
public synchronized void writeLong(long l) throws IOException {
int remain = bb.remaining();
if (remain >= 8) {
bb.putLong(l);
modified = true;
} else {
for (int i = 7; i >= 0; i--) {
if (!bb.hasRemaining())
seek(fp + capacity);
bb.put((byte) ((l >>> (i * 8)) & 0xFF));
modified = true;
}
}
}
@Override
public synchronized void writeFloat(float v) throws IOException {
writeInt(Float.floatToRawIntBits(v));
}
@Override
public synchronized void writeDouble(double v) throws IOException {
writeLong(Double.doubleToRawLongBits(v));
}
@Override
public synchronized void writeBytes(String s) throws IOException {
write(s.getBytes());
}
@Override
public synchronized void writeChars(String s) throws IOException {
for (char c : s.toCharArray())
writeChar(c);
}
@Override
public synchronized void writeUTF(String s) throws IOException {
byte[] bytes = s.getBytes("utf-8");
writeShort(bytes.length);
write(bytes);
}
@Override
public synchronized int skipBytes(int n) throws IOException {
seek(fp + bb.position() + n);
return n;
}
public synchronized int read(byte[] b) throws IOException {
readFully(b);
return b.length;
}
@Override
public synchronized int read(byte[] b, int off, int len) throws IOException {
readFully(b, off, len);
return len;
}
@Override
public synchronized void readFully(byte[] b) throws IOException {
readFully(b, 0, b.length);
}
@Override
public synchronized void readFully(byte[] b, int off, int len) throws IOException {
skipBytes(off);
if (bb.remaining() >= len)
bb.get(b, 0, len);
else {
int remain = bb.remaining();
bb.get(b, 0, remain);
raf.seek(fp + capacity);
raf.read(b, remain, len - remain);
seek(fp + capacity + (len - remain));
}
}
@Override
public synchronized boolean readBoolean() throws IOException {
return (readByte() != (byte) 0x0);
}
public synchronized byte readByte() throws IOException {
if (!bb.hasRemaining())
seek(fp + capacity);
return bb.get();
}
@Override
public synchronized int readUnsignedByte() throws IOException {
return (readByte() & 0xFFFFFFFF);
}
public synchronized short readShort() throws IOException {
if (bb.remaining() >= 2)
return bb.getShort();
else {
short s = 0;
for (int i = 0; i < 2; i++) {
if (!bb.hasRemaining())
seek(fp + capacity);
s <<= 8;
s |= bb.get() & 0xFF;
}
return s;
}
}
@Override
public synchronized char readChar() throws IOException {
return (char) (readShort() & 0xFFFF);
}
@Override
public synchronized int readUnsignedShort() throws IOException {
return (readShort() & 0xFFFFFFFF);
}
public synchronized int readInt() throws IOException {
if (bb.remaining() >= 4)
return bb.getInt();
else {
int i = 0;
for (int j = 0; j < 4; j++) {
if (!bb.hasRemaining())
seek(fp + capacity);
i <<= 8;
i |= bb.get() & 0xFF;
}
return i;
}
}
public synchronized long readLong() throws IOException {
if (bb.remaining() >= 8)
return bb.getLong();
else {
long l = 0L;
for (int i = 0; i < 8; i++) {
if (!bb.hasRemaining())
seek(fp + capacity);
l <<= 8;
l |= bb.get() & 0xFF;
}
return l;
}
}
@Override
public synchronized float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
@Override
public synchronized double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
@Override
public synchronized String readLine() throws IOException {
throw new UnsupportedOperationException();
}
@Override
public synchronized String readUTF() throws IOException {
byte[] b = new byte[readShort()];
read(b);
return new String(b, "utf-8");
}
@Override
public synchronized void close() {
try {
if (raf != null)
flush();
} catch (IOException e) {
}
try {
if (raf != null)
raf.close();
} catch (IOException e) {
}
raf = null;
}
}