Skip to content

Commit

Permalink
Refactor btree.ts and node.ts, starting coverage (#105)
Browse files Browse the repository at this point in the history
* feat: refactor btree, node, started coverage

* change data to uint8[], fix slice range

* more work

* add multi test
  • Loading branch information
friendlymatthew authored Feb 15, 2024
1 parent b1b97d7 commit 65ce3c3
Show file tree
Hide file tree
Showing 19 changed files with 479 additions and 406 deletions.
2 changes: 0 additions & 2 deletions pkg/btree/bptree.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ type BPTree struct {
tree ReadWriteSeekPager
meta MetaPage

maxPageSize int

Data []byte
}

Expand Down
16 changes: 16 additions & 0 deletions pkg/btree/multi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ import (
"github.com/kevmo314/appendable/pkg/buftest"
)

/*
func writeBufferToFile(bytes []byte, filename string) error {
if err := os.WriteFile(filename, bytes, 0644); err != nil {
return err
}
return nil
}
*/

func TestMultiBPTree(t *testing.T) {
t.Run("empty tree", func(t *testing.T) {
b := buftest.NewSeekableBuffer()
Expand Down Expand Up @@ -135,6 +145,9 @@ func TestMultiBPTree(t *testing.T) {
t.Fatal(err)
}
metadata, err := tree.Metadata()

// writeBufferToFile(metadata, "empty_metadata.bin")

if err != nil {
t.Fatal(err)
}
Expand All @@ -160,6 +173,9 @@ func TestMultiBPTree(t *testing.T) {
t.Fatal(err)
}
metadata, err := tree.Metadata()

// writeBufferToFile(metadata, "filled_metadata.bin")

if err != nil {
t.Fatal(err)
}
Expand Down
13 changes: 1 addition & 12 deletions pkg/btree/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@ func (n *BPTreeNode) leaf() bool {
return len(n.leafPointers) > 0
}

func (n *BPTreeNode) Pointers() []MemoryPointer {
if n.leaf() {
return n.leafPointers
}
pointers := make([]MemoryPointer, len(n.internalPointers))
for i, p := range n.internalPointers {
pointers[i].Offset = p
}
return pointers
}

func (n *BPTreeNode) Pointer(i int) MemoryPointer {
if n.leaf() {
return n.leafPointers[(len(n.leafPointers)+i)%len(n.leafPointers)]
Expand Down Expand Up @@ -144,7 +133,7 @@ func (n *BPTreeNode) UnmarshalBinary(buf []byte) error {
n.Keys[i].DataPointer.Offset = binary.BigEndian.Uint64(buf[m+4 : m+12])
n.Keys[i].DataPointer.Length = binary.BigEndian.Uint32(buf[m+12 : m+16])
dp := n.Keys[i].DataPointer
n.Keys[i].Value = n.Data[dp.Offset : dp.Offset+uint64(dp.Length)]
n.Keys[i].Value = n.Data[dp.Offset : dp.Offset+uint64(dp.Length)] // resolving the data-file
m += 4 + 12
} else {
n.Keys[i].Value = buf[m+4 : m+4+int(l)]
Expand Down
9 changes: 9 additions & 0 deletions pkg/btree/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ import (
"testing"
)

/*
func writeBufferToFile(buf *bytes.Buffer, filename string) error {
if err := os.WriteFile(filename, buf.Bytes(), 0644); err != nil {
return err
}
return nil
}
*/

func TestBPTreeNode_ReadWriteLeaf(t *testing.T) {
// Create a test BPTreeNode
node1 := &BPTreeNode{
Expand Down
2 changes: 1 addition & 1 deletion pkg/btree/pagefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type PageFile struct {

var _ ReadWriteSeekPager = &PageFile{}

const maxFreePageIndices = 512
// const maxFreePageIndices = 512
const pageSizeBytes = 4096 // 4kB by default.

func NewPageFile(rws io.ReadWriteSeeker) (*PageFile, error) {
Expand Down
154 changes: 97 additions & 57 deletions src/btree/bptree.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
import { BPTreeNode, MemoryPointer, compareBytes } from "./node";
import { LengthIntegrityError, RangeResolver } from "../resolver";
import { BPTreeNode, MemoryPointer } from "./node";
import { RangeResolver } from "../resolver";
import { LinkedMetaPage } from "./multi";


export interface MetaPage {
root(): MemoryPointer;
setRoot(mp: MemoryPointer): void;
}



type RootResponse = {
rootNode: BPTreeNode;
pointer: MemoryPointer;
};

export class BPTree {
private tree: RangeResolver;
private meta: LinkedMetaPage;
private readonly tree: RangeResolver;
private meta: MetaPage;
private readonly data: Uint8Array; // RangeResolver for the data-file

constructor(tree: RangeResolver, meta: LinkedMetaPage) {
constructor(tree: RangeResolver, meta: MetaPage, data: Uint8Array) {
this.tree = tree;
this.meta = meta;
this.data = data;
}

private async root(): Promise<RootResponse | null> {
Expand All @@ -33,68 +43,43 @@ export class BPTree {
};
}

private async readNode(ptr: MemoryPointer): Promise<BPTreeNode | null> {
private async readNode(ptr: MemoryPointer): Promise<BPTreeNode> {
try {
const { node, bytesRead } = await BPTreeNode.fromMemoryPointer(
ptr,
this.tree
this.tree,
this.data
);

if (!bytesRead || bytesRead !== ptr.length) {
return null;
throw new Error("bytes read do not line up");
}

return node;
} catch (error) {
if (error instanceof LengthIntegrityError) {
// handle LengthIntegrityError
}

return null;
throw new Error(`${error}`);
}
}

private async traverse(
key: Uint8Array,
key: ArrayBuffer,
node: BPTreeNode,
pointer: MemoryPointer
): Promise<TraversalRecord[] | null> {
if (node.leaf()) {
return [{ node: node, index: 0, pointer: pointer }];
}
): Promise<TraversalRecord[]> {
const index = binarySearchReferencedValues(node.keys, key);

for (const [i, k] of node.keys.entries()) {
if (compareBytes(key, k.value) < 0) {
const child = await this.readNode(node.pointers[i]);
if (!child) {
return null;
}
const found = index >= 0;
const childIndex = found ? index : ~index;

const path = await this.traverse(key, child, node.pointers[i]);
if (!path) {
return null;
}

return [...path, { node: node, index: i, pointer: pointer }];
}
}

const child = await this.readNode(node.pointers[node.pointers.length - 1]);

if (!child) {
return null;
}
if (node.leaf()) {
return [{ node, index: childIndex, found, pointer }];
} else {
const childPointer = node.pointer(childIndex);
const child = await this.readNode(childPointer);
const path = await this.traverse(key, child, childPointer);

const path = await this.traverse(
key,
child,
node.pointers[node.pointers.length - 1]
);
if (!path) {
return null;
return [...path, { node, index: childIndex, found, pointer }];
}

return [...path, { node: node, index: node.keys.length, pointer: pointer }];
}

public async find(key: Uint8Array): Promise<[MemoryPointer, boolean]> {
Expand All @@ -111,26 +96,81 @@ export class BPTree {
return [{ offset: BigInt(0), length: 0 }, false];
}

const n = path[0].node;

const i = await n.bsearch(key);

if (i >= 0) {
return [n.pointers[i], true];
}

return [{ offset: BigInt(0), length: 0 }, false];
return [path[0].node.pointer(path[0].index), path[0].found];
}
}

class TraversalRecord {
public node: BPTreeNode;
public index: number;
public found: boolean;
public pointer: MemoryPointer;

constructor(node: BPTreeNode, index: number, pointer: MemoryPointer) {
constructor(
node: BPTreeNode,
index: number,
found: boolean,
pointer: MemoryPointer
) {
this.node = node;
this.index = index;
this.found = found;
this.pointer = pointer;
}
}

export class ReferencedValue {
public dataPointer: MemoryPointer;
public value: ArrayBuffer;

constructor(dataPointer: MemoryPointer, value: Uint8Array) {
this.dataPointer = dataPointer;
this.value = value;
}

static compareBytes(aBuffer: ArrayBuffer, bBuffer: ArrayBuffer): number {
const a = new Uint8Array(aBuffer);
const b = new Uint8Array(bBuffer);

const len = Math.min(a.length, b.length);
for (let idx = 0; idx < len; idx++) {
if (a[idx] !== b[idx]) {
return a[idx] < b[idx] ? -1 : 1;
}
}

if (a.length < b.length) {
return -1;
}
if (a.length > b.length) {
return 1;
}
return 0;
}
}

function binarySearchReferencedValues(
values: ReferencedValue[],
target: ArrayBuffer
): number {
let lo = 0;
let hi = values.length;

while (lo < hi) {
const mid = lo + ((hi - lo) >> 1);

const cmp = ReferencedValue.compareBytes(values[mid].value, target);

if (cmp === 0) {
return mid;
}

if (cmp < 0) {
lo = mid + 1;
} else {
hi = mid;
}
}

return ~lo;
}
Loading

0 comments on commit 65ce3c3

Please sign in to comment.