0,0 → 1,306 |
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 cacheHit = 0; |
private int cacheMiss = 0; |
private int diskReads = 0; |
private int diskWrites = 0; |
|
private String dataFile; |
private String statFile; |
|
private BufferBlock bufferHead; |
|
private RandomAccessFile file; |
|
public LRUBuffer(String datafile, String statfile, int size, int blocksize) { |
this.dataFile = datafile; |
this.statFile = statfile; |
this.blockSize = blocksize; |
|
// 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"); |
file.setLength(0); |
} catch (FileNotFoundException e) { |
e.printStackTrace(); |
} catch (IOException e) { |
e.printStackTrace(); |
} |
} |
|
/** Recursive function to copy length number of bytes from file at startAddress |
* to offset position in out |
* |
* @param out Destination array to copy bytes to |
* @param offset Offset of position to write to in the destination array |
* @param startAddress Starting address on disk to copy bytes from |
* @param length Number of bytes to copy to the destination array |
* @return |
*/ |
public boolean getBytes(byte[] out, int offset, long startAddress, int length) { |
BufferBlock cur = bufferHead; |
|
// If the requested length doesnt extends to the next block |
if (startAddress % blockSize + length < blockSize) { |
// Check if address is already in a buffer block |
while (cur != null && cur.getStartAddress() != -1) { |
if (startAddress >= cur.getStartAddress() && startAddress < cur.getStartAddress() + blockSize) { |
// Address is in block |
cacheHit++; |
// Bring current block to front of list |
bringToFront(cur); |
// Get data |
cur.getBytes(out, offset, (int)startAddress % blockSize, length); |
return true; |
} |
cur = cur.getNext(); |
} |
|
// Address is not in buffer, read new block from disk |
cacheMiss++; |
cur = readFromDisk(startAddress); |
insertToFront(cur); |
// Get data from block |
cur.getBytes(out, offset, (int)startAddress % blockSize, length); |
return true; |
// If the requested length extends to the next block |
} else { |
int bytesToRead = 0; |
// Check if address is already in a buffer block |
while (cur != null && cur.getStartAddress() != -1) { |
if (startAddress >= cur.getStartAddress() && startAddress < cur.getStartAddress() + blockSize) { |
// Address is in block |
cacheHit++; |
// Bring current block to front of list |
bringToFront(cur); |
// Read in from address to end of block |
bytesToRead = blockSize - (int)startAddress % blockSize; |
cur.getBytes(out, offset, (int)startAddress % blockSize, bytesToRead); |
offset += bytesToRead; |
// Get data from the next consecutive block |
getBytes(out, offset, startAddress + bytesToRead, length - bytesToRead); |
return true; |
} |
cur = cur.getNext(); |
} |
|
// Address is not in buffer, read new block from disk |
cacheMiss++; |
cur = readFromDisk(startAddress); |
insertToFront(cur); |
// Read in from address to end of block |
bytesToRead = blockSize - (int)startAddress % blockSize; |
cur.getBytes(out, offset, (int)startAddress % blockSize, bytesToRead); |
offset += bytesToRead; |
// Get data from the next consecutive block |
getBytes(out, offset, startAddress + bytesToRead, length - bytesToRead); |
return true; |
} |
} |
|
/** Recursive function to copy length number of bytes from in to file at startAddress |
* |
* @param in Input array to copy bytes from |
* @param startAddress Starting address on disk to copy bytes to |
* @param length Number of bytes to copy from in to disk |
* @return |
*/ |
public boolean putBytes(byte[] in, int offset, long startAddress, int length) { |
BufferBlock cur = bufferHead; |
|
// If the requested length doesnt extends to the next block |
if (startAddress % blockSize + length <= blockSize) { |
// Check if address is already in a buffer block |
while (cur != null && cur.getStartAddress() != -1) { |
if (startAddress >= cur.getStartAddress() && startAddress < cur.getStartAddress() + blockSize) { |
// Address is in block |
cacheHit++; |
// Bring current block to front of list |
bringToFront(cur); |
// Put data into block |
cur.setBytes(in, offset, (int)startAddress % blockSize, length); |
cur.setDirtyBit(true); |
return true; |
} |
cur = cur.getNext(); |
} |
|
// Address is not in buffer, read new block from disk |
cacheMiss++; |
cur = readFromDisk(startAddress); |
insertToFront(cur); |
// Put data into block |
cur.setBytes(in, offset, (int)startAddress % blockSize, length); |
cur.setDirtyBit(true); |
return true; |
// If the requested length extends to the next block |
} else { |
int bytesToWrite = 0; |
// Check if address is already in a buffer block |
while (cur != null && cur.getStartAddress() != -1) { |
if (startAddress >= cur.getStartAddress() && startAddress < cur.getStartAddress() + blockSize) { |
// Address is in block |
cacheHit++; |
// Bring current block to front of list |
bringToFront(cur); |
// Put in data until end of block |
bytesToWrite = blockSize - (int)startAddress % blockSize; |
cur.setBytes(in, offset, (int)startAddress % blockSize, bytesToWrite); |
cur.setDirtyBit(true); |
startAddress += bytesToWrite; |
// Put remaining data into the next consecutive block |
putBytes(in, offset + bytesToWrite, startAddress, length - bytesToWrite); |
return true; |
} |
cur = cur.getNext(); |
} |
|
// Address is not in buffer, read new block from disk |
cacheMiss++; |
cur = readFromDisk(startAddress); |
insertToFront(cur); |
// Put in data until end of block |
bytesToWrite = blockSize - (int)startAddress % blockSize; |
cur.setBytes(in, offset, (int)startAddress % blockSize, bytesToWrite); |
cur.setDirtyBit(true); |
startAddress += bytesToWrite; |
// Put remaining data into the next consecutive block |
putBytes(in, offset + bytesToWrite, startAddress, length - bytesToWrite); |
return true; |
} |
} |
|
/** 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, 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, 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(); |
} |
} |
|
public void printBlockIDs() { |
BufferBlock cur = bufferHead; |
System.out.print("Buffer Pool Blocks: "); |
while (cur != null && cur.getStartAddress() != -1) { |
// System.out.print(cur.getStartAddress() + " ; "); |
System.out.print(cur.getStartAddress() / blockSize + " ; "); |
cur = cur.getNext(); |
} |
System.out.print("\n"); |
} |
} |