/* * Copyright 2012 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.eventstorage.engine; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.util.Arrays; public class BufferedRandomAccessFile { private RandomAccessFile raf; private int hdrSize; private int capacity; private ByteBuffer bb; private long fp = -1L; private long length; private int maxPos = 0; private volatile boolean modified = false; public BufferedRandomAccessFile(File file, String mode, int capacity) throws IOException { this(file, mode, capacity, 0); } public BufferedRandomAccessFile(File file, String mode, int capacity, int hdrSize) throws IOException { this(new RandomAccessFile(file, mode), capacity, hdrSize); } public BufferedRandomAccessFile(RandomAccessFile raf, int capacity) throws IOException { this(raf, capacity, 0); } public BufferedRandomAccessFile(RandomAccessFile raf, int capacity, int hdrSize) throws IOException { this.raf = raf; this.hdrSize = hdrSize; this.capacity = capacity; this.bb = ByteBuffer.allocate(capacity); this.length = raf.length() - hdrSize; seek(length); } public synchronized void seek(long pos) throws IOException { length = Math.max(length, Math.max(pos, fp + bb.position() - hdrSize)); pos += hdrSize; int offset = (int) (pos % capacity); if (fp != pos - offset) { if (modified) flush(false); fp = pos - offset; raf.seek(fp); int readed = raf.read(bb.array()); 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()) - hdrSize); } public synchronized void flush(boolean sync) throws IOException { raf.seek(fp); raf.write(bb.array(), 0, Math.max(maxPos, bb.position())); if (sync) raf.getFD().sync(); modified = false; } public synchronized void write(byte b) throws IOException { if (!bb.hasRemaining()) seek(fp + capacity - hdrSize); 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) - hdrSize); } } public synchronized void writeShort(short s) throws IOException { 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 - hdrSize); bb.put((byte) ((s >>> (i * 8)) & 0xFF)); modified = true; } } } 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 - hdrSize); 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 - hdrSize); bb.put((byte) ((l >>> (i * 8)) & 0xFF)); modified = true; } } } public synchronized void read(byte[] b) throws IOException { if (bb.remaining() >= b.length) bb.get(b); else { int remain = bb.remaining(); bb.get(b, 0, remain); raf.seek(fp + capacity); raf.read(b, remain, b.length - remain); seek(fp + capacity + (b.length - remain) - hdrSize); } } public synchronized byte readByte() throws IOException { if (!bb.hasRemaining()) seek(fp + capacity - hdrSize); return bb.get(); } 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 - hdrSize); s <<= 8; s |= bb.get() & 0xFF; } return s; } } 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 - hdrSize); 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 - hdrSize); l <<= 8; l |= bb.get() & 0xFF; } return l; } } public void close() { try { flush(true); } catch (IOException e) { } try { raf.close(); } catch (IOException e) { } } }