package com.github.ltsopensource.core.commons.io; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; public class BufferedRandomAccessFile extends RandomAccessFile { private static final int DEFAULT_BUFFER_BIT_LEN = 10; protected byte buf[]; protected int bufBitLen; protected int bufSize; protected long bufMask; protected boolean bufDirty; protected int bufUsedSize; protected long curPos; protected long bufStartPos; protected long bufEndPos; protected long fileEndPos; protected boolean append; protected String filename; protected long initFileLen; public BufferedRandomAccessFile(String name) throws IOException { this(name, "r", DEFAULT_BUFFER_BIT_LEN); } public BufferedRandomAccessFile(File file) throws IOException { this(file.getPath(), "r", DEFAULT_BUFFER_BIT_LEN); } public BufferedRandomAccessFile(String name, int bufBitLen) throws IOException { this(name, "r", bufBitLen); } public BufferedRandomAccessFile(File file, int bufBitLen) throws IOException { this(file.getPath(), "r", bufBitLen); } public BufferedRandomAccessFile(String name, String mode) throws IOException { this(name, mode, DEFAULT_BUFFER_BIT_LEN); } public BufferedRandomAccessFile(File file, String mode) throws IOException { this(file.getPath(), mode, DEFAULT_BUFFER_BIT_LEN); } public BufferedRandomAccessFile(String name, String mode, int bufBitLen) throws IOException { super(name, mode); this.init(name, mode, bufBitLen); } public BufferedRandomAccessFile(File file, String mode, int bufBitLen) throws IOException { this(file.getPath(), mode, bufBitLen); } private void init(String name, String mode, int bufbitlen) throws IOException { this.append = !mode.equals("r"); this.filename = name; this.initFileLen = super.length(); this.fileEndPos = this.initFileLen - 1; this.curPos = super.getFilePointer(); if (bufbitlen < 0) { throw new IllegalArgumentException("bufBitLen size must >= 0"); } this.bufBitLen = bufbitlen; this.bufSize = 1 << bufbitlen; this.buf = new byte[this.bufSize]; this.bufMask = ~((long) this.bufSize - 1L); this.bufDirty = false; this.bufUsedSize = 0; this.bufStartPos = -1; this.bufEndPos = -1; } private void flushBuf() throws IOException { if (this.bufDirty) { if (super.getFilePointer() != this.bufStartPos) { super.seek(this.bufStartPos); } super.write(this.buf, 0, this.bufUsedSize); this.bufDirty = false; } } private int fillBuf() throws IOException { super.seek(this.bufStartPos); this.bufDirty = false; return super.read(this.buf); } public byte read(long pos) throws IOException { if (pos < this.bufStartPos || pos > this.bufEndPos) { this.flushBuf(); this.seek(pos); if ((pos < this.bufStartPos) || (pos > this.bufEndPos)) { throw new IOException(); } } this.curPos = pos; return this.buf[(int) (pos - this.bufStartPos)]; } public boolean write(byte bw) throws IOException { return this.write(bw, this.curPos); } public boolean append(byte bw) throws IOException { return this.write(bw, this.fileEndPos + 1); } public boolean write(byte bw, long pos) throws IOException { if ((pos >= this.bufStartPos) && (pos <= this.bufEndPos)) { // write pos in buf this.buf[(int) (pos - this.bufStartPos)] = bw; this.bufDirty = true; if (pos == this.fileEndPos + 1) { // write pos is append pos this.fileEndPos++; this.bufUsedSize++; } } else { // write pos not in buf this.seek(pos); if ((pos >= 0) && (pos <= this.fileEndPos) && (this.fileEndPos != 0)) { // write pos is modify file this.buf[(int) (pos - this.bufStartPos)] = bw; } else if (((pos == 0) && (this.fileEndPos == 0)) || (pos == this.fileEndPos + 1)) { // write pos is append pos this.buf[0] = bw; this.fileEndPos++; this.bufUsedSize = 1; } else { throw new IndexOutOfBoundsException(); } this.bufDirty = true; } this.curPos = pos; return true; } public void write(byte b[], int off, int len) throws IOException { long writeEndPos = this.curPos + len - 1; if (writeEndPos <= this.bufEndPos) { // b[] in cur buf System.arraycopy(b, off, this.buf, (int) (this.curPos - this.bufStartPos), len); this.bufDirty = true; this.bufUsedSize = (int) (writeEndPos - this.bufStartPos + 1);//(int)(this.curPos - this.bufStartPos + len - 1); } else { // b[] not in cur buf super.seek(this.curPos); super.write(b, off, len); } if (writeEndPos > this.fileEndPos) this.fileEndPos = writeEndPos; this.seek(writeEndPos + 1); } public int read(byte b[], int off, int len) throws IOException { long readEndPos = this.curPos + len - 1; if (readEndPos <= this.bufEndPos && readEndPos <= this.fileEndPos) { // read in buf System.arraycopy(this.buf, (int) (this.curPos - this.bufStartPos), b, off, len); } else { // read b[] size > buf[] if (readEndPos > this.fileEndPos) { // read b[] part in file len = (int) (this.length() - this.curPos + 1); } super.seek(this.curPos); len = super.read(b, off, len); readEndPos = this.curPos + len - 1; } this.seek(readEndPos + 1); return len; } public void write(byte b[]) throws IOException { this.write(b, 0, b.length); } public int read(byte b[]) throws IOException { return this.read(b, 0, b.length); } public void seek(long pos) throws IOException { if ((pos < this.bufStartPos) || (pos > this.bufEndPos)) { // seek pos not in buf this.flushBuf(); if ((pos >= 0) && (pos <= this.fileEndPos) && (this.fileEndPos != 0)) { // seek pos in file (file length > 0) this.bufStartPos = pos & this.bufMask; this.bufUsedSize = this.fillBuf(); } else if (((pos == 0) && (this.fileEndPos == 0)) || (pos == this.fileEndPos + 1)) { // seek pos is append pos this.bufStartPos = pos; this.bufUsedSize = 0; } this.bufEndPos = this.bufStartPos + this.bufSize - 1; } this.curPos = pos; } public long length() throws IOException { return this.max(this.fileEndPos + 1, this.initFileLen); } public void setLength(long newLength) throws IOException { if (newLength > 0) { this.fileEndPos = newLength - 1; } else { this.fileEndPos = 0; } super.setLength(newLength); } public long getFilePointer() throws IOException { return this.curPos; } private long max(long a, long b) { if (a > b) return a; return b; } public void close() throws IOException { this.flushBuf(); super.close(); } }