Subversion Repositories Code-Repo

Rev

Rev 95 | Blame | Compare with Previous | 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();
                }
        }
}