aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/io')
-rw-r--r--src/main/java/io/trygvis/btree/HeapFile.java58
-rw-r--r--src/main/java/io/trygvis/btree/HeapPage.java123
2 files changed, 181 insertions, 0 deletions
diff --git a/src/main/java/io/trygvis/btree/HeapFile.java b/src/main/java/io/trygvis/btree/HeapFile.java
new file mode 100644
index 0000000..7a960c8
--- /dev/null
+++ b/src/main/java/io/trygvis/btree/HeapFile.java
@@ -0,0 +1,58 @@
+package io.trygvis.btree;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+import static io.trygvis.btree.HeapPage.blankHeapPage;
+import static java.nio.ByteBuffer.allocateDirect;
+
+public class HeapFile implements Closeable {
+ private final int PAGE_SIZE;
+
+ private final RandomAccessFile file;
+ private final FileChannel channel;
+
+ public HeapFile(int PAGE_SIZE, File file) throws IOException {
+ this.PAGE_SIZE = PAGE_SIZE;
+ this.file = new RandomAccessFile(file, "rw");
+
+ channel = this.file.getChannel();
+ }
+
+ @Override
+ public void close() throws IOException {
+ channel.force(true);
+ file.close();
+ }
+
+ public HeapPage loadPage(int page) throws IOException {
+ ByteBuffer buffer = allocateDirect(PAGE_SIZE);
+
+ channel.read(buffer, page * PAGE_SIZE);
+
+ return HeapPage.heapPageFromBytes(page, buffer);
+ }
+
+ public void writePage(HeapPage page) throws IOException {
+ long position;
+ if (page.pageNumber == -1) {
+ position = file.length();
+ } else {
+ position = page.pageNumber * PAGE_SIZE;
+ }
+ channel.write(page.bytes, position);
+ channel.force(true);
+ }
+
+ public long pageCount() throws IOException {
+ return file.length() / PAGE_SIZE;
+ }
+
+ public HeapPage blankPage() {
+ return blankHeapPage(allocateDirect(PAGE_SIZE));
+ }
+}
diff --git a/src/main/java/io/trygvis/btree/HeapPage.java b/src/main/java/io/trygvis/btree/HeapPage.java
new file mode 100644
index 0000000..499a244
--- /dev/null
+++ b/src/main/java/io/trygvis/btree/HeapPage.java
@@ -0,0 +1,123 @@
+package io.trygvis.btree;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class HeapPage {
+ public static final int headerSize = 4;
+ public static final int FREE_POSITION_INDEX = 0;
+
+ public static final int itemSize = 4;
+
+ // The page number in a heap file
+ final long pageNumber;
+ final ByteBuffer bytes;
+ int freePosition;
+
+ private HeapPage(long pageNumber, ByteBuffer bytes, int freePosition) {
+ this.pageNumber = pageNumber;
+ this.bytes = bytes;
+ this.freePosition = freePosition;
+ }
+
+ public static HeapPage heapPageFromBytes(int pageNumber, ByteBuffer bytes) {
+ int freePosition = bytes.getInt();
+ return new HeapPage(pageNumber, bytes, freePosition);
+ }
+
+ public static HeapPage blankHeapPage(ByteBuffer bytes) {
+ return new HeapPage(-1, bytes, bytes.capacity());
+ }
+
+ public void append(byte[] item) {
+// System.out.println("io.trygvis.btree.HeapPage.append");
+
+ int bytesFree = bytesFree();
+ int bytesRequested = item.length;
+
+ if (bytesRequested > bytesFree) {
+ throw new PageOverflowException(bytesFree, bytesRequested);
+ }
+
+ freePosition -= itemSize;
+ bytes.position(freePosition);
+ bytes.putInt(item.length);
+ freePosition -= bytesRequested;
+ bytes.position(freePosition);
+ bytes.put(item);
+
+ bytes.putInt(FREE_POSITION_INDEX, freePosition);
+ }
+
+ public int bytesFree() {
+ return freePosition - headerSize;
+ }
+
+ public Iterable<ByteBuffer> bufferIterator() {
+ return new Iterable<ByteBuffer>() {
+ @Override
+ public Iterator<ByteBuffer> iterator() {
+ return new Iterator<ByteBuffer>() {
+ private int position;
+
+ private ByteBuffer next;
+
+ {
+ position = bytes.capacity();
+ next = findNext();
+ }
+
+ private ByteBuffer findNext() {
+ if (position == headerSize) {
+ return null;
+ }
+
+ int size = bytes.getInt(position - itemSize);
+
+ if (size == 0) {
+ return null;
+ }
+
+ position -= itemSize + size;
+ ByteBuffer copy = bytes.duplicate();
+ copy.position(position);
+ copy.limit(position + size);
+ return copy.slice();
+ }
+
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public ByteBuffer next() {
+ if (next == null) {
+ throw new NoSuchElementException();
+ }
+
+ ByteBuffer n = next;
+ next = findNext();
+ return n;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+ };
+ }
+
+ public static class PageOverflowException extends RuntimeException {
+ public final int bytesFree;
+ public final int bytesRequested;
+
+ public PageOverflowException(int bytesFree, int bytesRequested) {
+ super("Not enough room on page, bytes free=" + bytesFree + ", requested = " + bytesRequested);
+ this.bytesFree = bytesFree;
+ this.bytesRequested = bytesRequested;
+ }
+ }
+}