Blame | Last modification | View Log | RSS feed
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
public class LRUBuffer {
private int blockSize = 0;
private int recordSize = 0;
private int cacheHit = 0;
private int cacheMiss = 0;
private int diskReads = 0;
private int diskWrites = 0;
private String dataFile;
private String statFile;
private int bufferSize;
private BufferBlock bufferHead;
private RandomAccessFile file;
public LRUBuffer(String datafile, String statfile, int size, int blocksize, int recordsize) {
this.dataFile = datafile;
this.statFile = statfile;
this.bufferSize = size;
this.blockSize = blocksize;
this.recordSize = recordsize;
// Allocates a new doubly linked list of BufferBlocks
// The buffer will have at least one block
bufferHead = new BufferBlock(blockSize);
BufferBlock cur = bufferHead;
for (int i = 0; i < size-1; i++) {
BufferBlock newBlock = new BufferBlock(blockSize);
cur.setNext(newBlock);
newBlock.setPrev(cur);
cur = newBlock;
}
// Open access to the datafile
try {
file = new RandomAccessFile(dataFile, "rw");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
// Returns recordSize number of bytes to out from record # pos
public boolean getBytes(byte[] out, long pos) {
BufferBlock cur = bufferHead;
// Calculate the address of record
long address = pos * recordSize;
// Check if address is already in a buffer block
while (cur != null && cur.getStartAddress() != -1) {
if (address >= cur.getStartAddress() && address < cur.getStartAddress() + blockSize) {
// Address is in block, get data
cacheHit++;
cur.getBytes(out, (int)address % blockSize, recordSize);
// Bring current block to front of list
bringToFront(cur);
return true;
}
cur = cur.getNext();
}
// Address is not in buffer, read new block from disk
cacheMiss++;
BufferBlock newBlock = readFromDisk(address);
// Get requested bytes from block
newBlock.getBytes(out, (int)address % blockSize, recordSize);
insertToFront(newBlock);
return false;
}
// Puts recordSize number of bytes from in to record # pos
public boolean putBytes(byte[] in, long pos) {
BufferBlock cur = bufferHead;
// Calculate the address of record
long address = pos * recordSize;
// Check if address is already in a buffer block
while (cur != null && cur.getStartAddress() != -1) {
if (address >= cur.getStartAddress() && address < cur.getStartAddress() + blockSize) {
// Address is in block, put data
cacheHit++;
cur.setBytes(in, (int)address % blockSize, recordSize);
cur.setDirtyBit(true);
// Bring current block to front of list
bringToFront(cur);
return true;
}
cur = cur.getNext();
}
// Address is not in buffer, read new block from disk
cacheMiss++;
BufferBlock newBlock = readFromDisk(address);
// Put passed bytes into block
newBlock.setBytes(in, (int)address % blockSize, recordSize);
newBlock.setDirtyBit(true);
insertToFront(newBlock);
return false;
}
// Brings a block currently in the list to the front of the list
private void bringToFront(BufferBlock block) {
// If block is already in front, return
if (block == bufferHead)
return;
if (block.getPrev() != null)
block.getPrev().setNext(block.getNext());
if (block.getNext() != null)
block.getNext().setPrev(block.getPrev());
block.setPrev(null);
bufferHead.setPrev(block);
block.setNext(bufferHead);
bufferHead = block;
}
// Inserts a new block into the front of the list
private void insertToFront(BufferBlock block) {
// Set head as current block
block.setPrev(null);
bufferHead.setPrev(block);
block.setNext(bufferHead);
bufferHead = block;
// Write last block in list to disk if dirty bit is set
// Last block is also removed
BufferBlock cur = bufferHead;
while (cur.getNext() != null) {
cur = cur.getNext();
}
cur.getPrev().setNext(null);
if (cur.isDirtyBit()) {
writeToDisk(cur);
}
}
// Reads a new block from disk given an address
private BufferBlock readFromDisk(long address) {
diskReads++;
byte[] data = new byte[blockSize];
long offset = address / blockSize;
offset = offset * blockSize;
try {
file.seek(offset);
file.read(data);
} catch (IOException e) {
e.printStackTrace();
}
// Pass read block into a new block
BufferBlock newBlock = new BufferBlock(blockSize);
newBlock.setBytes(data, 0, blockSize);
newBlock.setStartAddress(offset);
return newBlock;
}
// Writes the specified block to disk
private void writeToDisk(BufferBlock block) {
diskWrites++;
byte[] data = new byte[blockSize];
block.getBytes(data, 0, blockSize);
try {
file.seek(block.getStartAddress());
file.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
// Flush the buffer by writing all blocks in buffer to disk
public void flushBuffer() {
BufferBlock cur = bufferHead;
while (cur != null) {
if (cur.isDirtyBit()) {
writeToDisk(cur);
}
cur = cur.getNext();
}
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// Write stats to stat file
public void writeStats(long time) {
try {
BufferedWriter out = new BufferedWriter(new FileWriter(statFile, true));
StringBuilder str = new StringBuilder();
str.append("Datafile: " + dataFile + " | ");
str.append("Cache Hits: " + cacheHit + " | ");
str.append("Cache Misses: " + cacheMiss + " | ");
str.append("Disk Reads: " + diskReads + " | ");
str.append("Disk Writes: " + diskWrites + " | ");
str.append("Time to Sort: " + time + "\n");
out.write(str.toString());
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}