package apes.models; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.io.Serializable; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; /** * This is not a class. * * @author Daniel Kvick (kvick@student.chalmers.se) */ public class MemoryHandler implements Serializable { private final int PAGE_SIZE = 100000; // Magic number. private final int FRAME_NUM = 100; private long usedMemory = 0; private Frame[] frameTable; private List<Page> pageTable; public MemoryHandler() { frameTable = new Frame[FRAME_NUM]; pageTable = new LinkedList<Page>(); for(int i = 0; i < FRAME_NUM; i++) frameTable[i] = new Frame(); } public long getUsedMemory() { return usedMemory; } public boolean free(long index, long bytes) throws IOException { if(index < 0 || index + bytes > usedMemory || bytes <= 0) return false; int frameF = find(index); int frameL = find(index + bytes - 1); // Find new size Frame fstFrame = (frameF == -1 ? swap(index) : frameTable[frameF]); Frame lstFrame = (frameL == -1 ? swap(index + bytes - 1) : frameTable[frameL]); long firstIndex = fstFrame.page.index; int fstSize = (int)(index - firstIndex); int lstSize = (int)((lstFrame.data.length + lstFrame.page.index) - (index + bytes)); int size = fstSize + lstSize; // Destroy pages List<Page> doomed = new ArrayList<Page>(); for(Page p : pageTable) if(p.index >= firstIndex && p.index < index + bytes) doomed.add(p); for(Page p : doomed) { destroyPage(p); } doomed.clear(); if(size != 0) { // Create pages createPages(firstIndex, size); byte[] data = new byte[size]; // Copy arrays System.arraycopy(fstFrame.data, 0, data, 0, fstSize); System.arraycopy(lstFrame.data, lstFrame.data.length - lstSize, data, fstSize, lstSize); // Copy data write(firstIndex, data); } return true; } /** * Allocates new memory and effectively inserts it at the given index. * Inserted data is to be considered uninitialized. * * @param index Position in memory where to add new memory. * @param bytes Amount of data to allocate, in INTEGER_BYTES. * @return Returns true if allocation succeeded, otherwise false. * @throws IOException IOException */ // TODO: Fix small files. public boolean malloc(long index, long bytes) throws IOException { if(index < 0 || index > usedMemory || bytes <= 0) return false; int frameI = find(index); Frame frame; // Not in memory if(frameI == -1) { // Doesn't exist if((frame = swap(index)) == null) { createPages(index, bytes); return true; } } else frame = frameTable[frameI]; long firstIndex = frame.page.index; long index1 = index + bytes; if(frame.page.index == index) { createPages(index, bytes); return true; } // Destroy old page destroyPage(frame.page); // Create pages createPages(firstIndex, bytes + frame.data.length); byte[] fst = new byte[(int)(index - firstIndex)]; byte[] lst = new byte[frame.data.length - fst.length]; System.arraycopy(frame.data, 0, fst, 0, fst.length); System.arraycopy(frame.data, fst.length, lst, 0, lst.length); write(firstIndex, fst); write(index1, lst); return true; } private Page[] createPages(long index, long amount) throws IOException { if(amount < 1 || index < 0) return null; // Update indexes of pages after insertion point. for(Page page : pageTable) { if(page.index >= index) { page.index += amount; } } if(amount <= PAGE_SIZE) { Page[] pages = new Page[1]; Page page = new Page(amount); page.index = index; pages[0] = page; pageTable.add(page); usedMemory += amount; return pages; } // Create and add pages int numPages = (int)(amount / PAGE_SIZE); Page[] pages = new Page[numPages]; for(int i = 0; i < pages.length - 1; i++) { Page page = new Page(PAGE_SIZE); // Set index page.index = index + i * PAGE_SIZE; // Add pages pages[i] = page; pageTable.add(page); } Page page = new Page(PAGE_SIZE + (amount % PAGE_SIZE)); page.index = index + (pages.length - 1) * PAGE_SIZE; pageTable.add(page); pages[pages.length - 1] = page; usedMemory += amount; return pages; } /** * Removes a page from the system. * * @param page Page to remove. * @return The frame which contained the specified page. If no such frame * exists, returns null. * @throws IOException IOException */ private Frame destroyPage(Page page) throws IOException { // Remove from page table if(!pageTable.remove(page)) return null; usedMemory -= page.tempFile.length(); // Update indexes that comes after the one that was removed. for(Page pageX : pageTable) { if(pageX.index > page.index) { pageX.index -= page.tempFile.length(); } } // Close file. page.file.close(); page.tempFile.delete(); for(Frame f : frameTable) { if(f.page == page) { f.page = null; f.timeStamp = Long.MIN_VALUE; return f; } } return null; } public byte[] read(long index, int amount) throws IOException { Frame frame = null; int frameI; byte[] buf = new byte[amount]; int prevEndPos = 0; while(amount > 0) { frameI = find(index); frame = (frameI == -1 ? swap(index) : frameTable[frameI]); int offset = (int)(index - frame.page.index); int length = frame.data.length - offset; if(length > amount) length = amount; System.arraycopy(frame.data, offset, buf, prevEndPos, length); index += length; prevEndPos += length; amount -= length; } frame.timeStamp = System.currentTimeMillis(); return buf; } public void write(long index, byte[] data) throws IOException { if(index < 0 || index + data.length > usedMemory || data.length < 1) return; Frame frame = null; int frameI; int offset = 0; int targetPos; while(offset < data.length) { frameI = find(index); frame = (frameI == -1 ? swap(index) : frameTable[frameI]); targetPos = (int)(index - frame.page.index); int length = Math.min(frame.data.length - targetPos, data.length - offset); System.arraycopy(data, offset, frame.data, targetPos, length); index += length; offset += length; } frame.timeStamp = System.currentTimeMillis(); } public void dispose() { try { free(0, usedMemory); } catch(Exception e) { e.printStackTrace(); } } /** * @param source * @param start * @param stop * @param putAt */ public void transfer(MemoryHandler source, long start, long amount, long putAt) { if(start < 0 || source.usedMemory < start + amount || putAt > usedMemory) return; // Allocate memory try { malloc(putAt, amount); // Copy memory int rest = (int)amount; for(int i = 0; i < amount; i += PAGE_SIZE) { int chunkSize = (int)Math.min(rest, PAGE_SIZE); byte[] chunk = source.read(start + i, chunkSize); write(putAt + i, chunk); rest -= chunkSize; } } catch(Exception e) { e.printStackTrace(); } } private Frame swap(long index) throws IOException { long timeStamp = Long.MAX_VALUE; Frame evicted = null; for(Frame frame : frameTable) { if(frame.page == null) { evicted = frame; timeStamp = frame.timeStamp; break; } if(frame.timeStamp < timeStamp) { evicted = frame; timeStamp = frame.timeStamp; } } for(Page page : pageTable) { if(page.index <= index && (page.index + page.tempFile.length()) > index) { evicted.writeAll(); evicted.load(page); evicted.timeStamp = System.currentTimeMillis(); return evicted; } } return null; } private int find(long index) throws IOException { for(int i = 0; i < frameTable.length; i++) { Frame f = frameTable[i]; if(f.page != null && (f.page.index <= index) && (f.page.index + f.page.tempFile.length() > index)) { return i; } } return -1; } private class Frame { private Page page; byte[] data; private long timeStamp; private void writeAll() throws IOException { if(page != null) page.write(data); } public void load(Page page) throws IOException { if(this.page != null) { this.page.file.close(); } this.page = page; page.file = new RandomAccessFile(page.tempFile, "rw"); data = new byte[(int)page.tempFile.length()]; page.read(data); } } private class Page { private RandomAccessFile file; private File tempFile; private long index; public Page(long l) throws IOException { tempFile = File.createTempFile("apes", "page"); tempFile.deleteOnExit(); file = new RandomAccessFile(tempFile, "rw"); file.setLength(l); setIndex(pageTable.size()); } public void read(byte[] data) throws IOException { file.seek(0); file.read(data); } public void write(byte[] data) throws IOException { file.seek(0); file.write(data); } public void setIndex(long index) { this.index = index; } public long getIndex() { return index; } } }