diff --git a/package.json b/package.json index c5a4d5a..06e43f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serializable-bptree", - "version": "1.0.2", + "version": "1.0.3", "description": "Store the B+tree flexibly, not only in-memory.", "main": "dist/cjs/index.js", "module": "dist/esm/index.js", diff --git a/src/BPTree.ts b/src/BPTree.ts index d7c9196..6283ed7 100644 --- a/src/BPTree.ts +++ b/src/BPTree.ts @@ -2,28 +2,39 @@ import { BinarySearch } from './utils/BinarySearch' import { ValueComparator } from './ValueComparator' import { SerializeStrategy, SerializeStrategyHead } from './SerializeStrategy' -type BPTreeNodeKey = K[]|BPTreeNode +type BPTreeNodeKey = number|K type BPTreeCondition = { gt?: V, lt?: V }|{ equal: V }|{ notEqual: V} type BPTreePair = { key: K, value: V } +export type BPTreeUnknownNode = BPTreeInternalNode|BPTreeLeafNode export interface BPTreeNode { id: number - keys: BPTreeNodeKey[], + keys: number[]|K[][], values: V[], leaf: boolean parent: number next: number } +export interface BPTreeInternalNode extends BPTreeNode { + leaf: false + keys: number[] +} + +export interface BPTreeLeafNode extends BPTreeNode { + leaf: true + keys: K[][] +} + export class BPTree { protected readonly strategy: SerializeStrategy protected readonly comparator: ValueComparator protected readonly search: BinarySearch protected readonly order: number - protected readonly nodes: Map> - protected root: BPTreeNode - private readonly _creates: Map> - private readonly _updates: Map> + protected readonly nodes: Map> + protected root: BPTreeUnknownNode + private readonly _creates: Map> + private readonly _updates: Map> private _updatedHead: SerializeStrategyHead|null private _createNodeId(): number { @@ -34,16 +45,16 @@ export class BPTree { return id } - private _createNode(keys: BPTreeNodeKey[], values: V[], leaf = false, parent = 0, next = 0): BPTreeNode { + private _createNode(keys: number[]|K[][], values: V[], leaf = false, parent = 0, next = 0): BPTreeUnknownNode { const id = this._createNodeId() - const node: BPTreeNode = { + const node = { id, keys, values, leaf, parent, next - } + } as BPTreeUnknownNode this.nodes.set(id, node) return node } @@ -94,11 +105,11 @@ export class BPTree { this._updatedHead = head } - private _setCreates(node: BPTreeNode): void { + private _setCreates(node: BPTreeUnknownNode): void { this._creates.set(node.id, node) } - private _setUpdates(node: BPTreeNode): void { + private _setUpdates(node: BPTreeUnknownNode): void { this._updates.set(node.id, node) } @@ -123,36 +134,38 @@ export class BPTree { this._updates.clear() } - protected getNode(id: number): BPTreeNode { + protected getNode(id: number): BPTreeUnknownNode { if (!this.nodes.has(id)) { - this.nodes.set(id, this.strategy.read(id)) + this.nodes.set(id, this.strategy.read(id) as BPTreeUnknownNode) } return this.nodes.get(id)! } - protected leftestNode(): BPTreeNode { + protected leftestNode(): BPTreeLeafNode { let node = this.root while (!node.leaf) { - node = node.keys[0] as BPTreeNode + const keys = node.keys + node = this.getNode(keys[0]) } return node } - private _insertableNode(value: V): BPTreeNode { + private _insertableNode(value: V): BPTreeLeafNode { let node = this.root while (!node.leaf) { for (let i = 0, len = node.values.length; i < len; i++) { const nValue = node.values[i] + const k = node.keys if (this.comparator.isSame(value, nValue)) { - node = node.keys[i+1] as BPTreeNode + node = this.getNode(k[i+1]) break } else if (this.comparator.isLower(value, nValue)) { - node = node.keys[i] as BPTreeNode + node = this.getNode(k[i]) break } else if (i+1 === node.values.length) { - node = node.keys[i+1] as BPTreeNode + node = this.getNode(k[i+1]) break } } @@ -170,19 +183,19 @@ export class BPTree { for (let i = 0, len = node.values.length; i < len; i++) { const nValue = node.values[i] if (this.comparator.isSame(value, nValue)) { - const keys = node.keys[i] as K[] + const keys = node.keys[i] return keys.includes(key) } } return false } - private _insertAtLeaf(node: BPTreeNode, key: K, value: V): void { + private _insertAtLeaf(node: BPTreeLeafNode, key: K, value: V): void { if (node.values.length) { for (let i = 0, len = node.values.length; i < len; i++) { const nValue = node.values[i] if (this.comparator.isSame(value, nValue)) { - const keys = node.keys[i] as K[] + const keys = node.keys[i] keys.push(key) this._setUpdates(node) break @@ -208,9 +221,9 @@ export class BPTree { } } - private _insertInParent(node: BPTreeNode, value: V, pointer: BPTreeNode): void { + private _insertInParent(node: BPTreeUnknownNode, value: V, pointer: BPTreeUnknownNode): void { if (this.root === node) { - const root = this._createNode([node, pointer], [value]) + const root = this._createNode([node.id, pointer.id], [value]) this.root = root node.parent = root.id pointer.parent = root.id @@ -220,16 +233,16 @@ export class BPTree { this._setUpdates(pointer) return } - const parentNode = this.getNode(node.parent) + const parentNode = this.getNode(node.parent) as BPTreeInternalNode for (let i = 0, len = parentNode.keys.length; i < len; i++) { const nKeys = parentNode.keys[i] - if (nKeys === node) { + if (nKeys === node.id) { parentNode.values.splice(i, 0, value) - parentNode.keys.splice(i+1, 0, pointer) + parentNode.keys.splice(i+1, 0, pointer.id) this._setUpdates(parentNode) if (parentNode.keys.length > this.order) { - const parentPointer = this._createNode([], []) + const parentPointer = this._createNode([], []) as BPTreeInternalNode parentPointer.parent = parentNode.parent const mid = Math.ceil(this.order/2)-1 parentPointer.values = parentNode.values.slice(mid+1) @@ -243,12 +256,14 @@ export class BPTree { } parentNode.keys = parentNode.keys.slice(0, mid+1) for (const k of parentNode.keys) { - const key = k as BPTreeNode - key.parent = parentNode.id + const node = this.getNode(k) + node.parent = parentNode.id + this._setUpdates(node) } for (const k of parentPointer.keys) { - const key = k as BPTreeNode - key.parent = parentPointer.id + const node = this.getNode(k) + node.parent = parentPointer.id + this._setUpdates(node) } this._insertInParent(parentNode, midValue, parentPointer) @@ -296,7 +311,7 @@ export class BPTree { } const pairs = [] for (let i = start; i < end; i++) { - const keys = node.keys[i] as K[] + const keys = node.keys[i] for (const key of keys) { pairs.push({ key, value }) } @@ -311,7 +326,7 @@ export class BPTree { while (!done) { for (let i = 0, len = node.values.length; i < len; i++) { const nValue = node.values[i] - const keys = node.keys[i] as K[] + const keys = node.keys[i] if (this.comparator.isSame(nValue, value) === false) { for (const key of keys) { pairs.push({ key, value: nValue }) @@ -322,7 +337,7 @@ export class BPTree { done = true break } - node = this.getNode(node.next) + node = this.getNode(node.next) as BPTreeLeafNode } return pairs } @@ -335,7 +350,7 @@ export class BPTree { while (!done) { for (let i = 0, len = node.values.length; i < len; i++) { const nValue = node.values[i] - const keys = node.keys[i] as K[] + const keys = node.keys[i] if ( this.comparator.isHigher(nValue, gt) && this.comparator.isLower(nValue, lt) @@ -354,7 +369,7 @@ export class BPTree { done = true break } - node = this.getNode(node.next) + node = this.getNode(node.next) as BPTreeLeafNode } return pairs } @@ -367,7 +382,7 @@ export class BPTree { while (!done) { for (let i = 0, len = node.values.length; i < len; i++) { const nValue = node.values[i] - const keys = node.keys[i] as K[] + const keys = node.keys[i] if (this.comparator.isHigher(nValue, gt)) { found = true for (const key of keys) { @@ -383,7 +398,7 @@ export class BPTree { done = true break } - node = this.getNode(node.next) + node = this.getNode(node.next) as BPTreeLeafNode } return pairs } @@ -396,7 +411,7 @@ export class BPTree { while (!done) { for (let i = 0, len = node.values.length; i < len; i++) { const nValue = node.values[i] - const keys = node.keys[i] as K[] + const keys = node.keys[i] if (this.comparator.isLower(nValue, lt)) { found = true for (const key of keys) { @@ -412,7 +427,7 @@ export class BPTree { done = true break } - node = this.getNode(node.next) + node = this.getNode(node.next) as BPTreeLeafNode } return pairs } @@ -461,7 +476,7 @@ export class BPTree { true, before.parent, before.next - ) + ) as BPTreeLeafNode const mid = Math.ceil(this.order/2)-1 after.values = before.values.slice(mid+1) after.keys = before.keys.slice(mid+1) @@ -489,10 +504,10 @@ export class BPTree { while (i--) { const nValue = node.values[i] if (this.comparator.isSame(value, nValue)) { - const keys = node.keys[i] as (K|BPTreeNode)[] - if (keys.includes(key as K)) { + const keys = node.keys[i] + if (keys.includes(key)) { if (keys.length > 1) { - keys.splice(keys.indexOf(key as K), 1) + keys.splice(keys.indexOf(key), 1) this._setUpdates(node) } else if (node === this.root) { @@ -501,10 +516,10 @@ export class BPTree { this._setUpdates(node) } else { - keys.splice(keys.indexOf(key as K), 1) + keys.splice(keys.indexOf(key), 1) node.keys.splice(i, 1) node.values.splice(node.values.indexOf(value), 1) - this._deleteEntry(node, key as BPTreeNodeKey, value) + this._deleteEntry(node, key, value) this._setUpdates(node) } } @@ -515,7 +530,7 @@ export class BPTree { this._emitUpdates() } - private _deleteEntry(node: BPTreeNode, key: BPTreeNodeKey, value: V): void { + private _deleteEntry(node: BPTreeUnknownNode, key: BPTreeNodeKey, value: V): void { if (!node.leaf) { for (let i = 0, len = node.keys.length; i < len; i++) { const nKey = node.keys[i] @@ -536,8 +551,8 @@ export class BPTree { } if (this.root === node && node.keys.length === 1) { - const keys = node.keys as BPTreeNode[] - this.root = keys[0] + const keys = node.keys as number[] + this.root = this.getNode(keys[0]) this.root.parent = 0 this._setHeadUpdate(this._headState) this._setUpdates(this.root) @@ -551,27 +566,27 @@ export class BPTree { (node.values.length < Math.ceil((this.order-1)/2) && node.leaf) ) { let isPredecessor = false - let parentNode = this.getNode(node.parent) - let prevNode: BPTreeNode|null = null - let nextNode: BPTreeNode|null = null + let parentNode = this.getNode(node.parent) as BPTreeInternalNode + let prevNode: BPTreeInternalNode|null = null + let nextNode: BPTreeInternalNode|null = null let prevK: V|null = null let postK: V|null = null for (let i = 0, len = parentNode.keys.length; i < len; i++) { const nKey = parentNode.keys[i] - if (nKey === node) { + if (nKey === node.id) { if (i > 0) { - prevNode = parentNode.keys[i-1] as BPTreeNode + prevNode = this.getNode(parentNode.keys[i-1]) as BPTreeInternalNode prevK = parentNode.values[i-1] } if (i < parentNode.keys.length-1) { - nextNode = parentNode.keys[i+1] as BPTreeNode + nextNode = this.getNode(parentNode.keys[i+1]) as BPTreeInternalNode postK = parentNode.values[i] } } } - let pointer: BPTreeNode + let pointer: BPTreeUnknownNode let guess: V|null if (prevNode === null) { pointer = nextNode! @@ -595,11 +610,11 @@ export class BPTree { } if (node.values.length + pointer!.values.length < this.order) { if (!isPredecessor) { - const pTemp = pointer as BPTreeNode - pointer = node + const pTemp = pointer + pointer = node as BPTreeInternalNode node = pTemp } - pointer.keys.push(...node.keys) + pointer.keys.push(...node.keys as any) if (!node.leaf) { pointer.values.push(guess!) } @@ -609,13 +624,15 @@ export class BPTree { pointer.values.push(...node.values) if (!pointer.leaf) { - const keys = pointer.keys as BPTreeNode[] + const keys = pointer.keys for (const key of keys) { - key.parent = pointer.id + const node = this.getNode(key) + node.parent = pointer.id + this._setUpdates(node) } } - this._deleteEntry(this.getNode(node.parent), node, guess!) + this._deleteEntry(this.getNode(node.parent), node.id, guess!) this._setUpdates(pointer) } else { @@ -627,7 +644,7 @@ export class BPTree { pointerKm = pointer.values.splice(-1)[0] node.keys = [pointerPm, ...node.keys] node.values = [guess!, ...node.values] - parentNode = this.getNode(node.parent) + parentNode = this.getNode(node.parent) as BPTreeInternalNode for (let i = 0, len = parentNode.values.length; i < len; i++) { const nValue = parentNode.values[i] if (this.comparator.isSame(guess!, nValue)) { @@ -638,11 +655,11 @@ export class BPTree { } } else { - pointerPm = pointer.keys.splice(-1)[0] + pointerPm = pointer.keys.splice(-1)[0] as unknown as K[] pointerKm = pointer.values.splice(-1)[0] node.keys = [pointerPm, ...node.keys] node.values = [pointerKm, ...node.values] - parentNode = this.getNode(node.parent) + parentNode = this.getNode(node.parent) as BPTreeInternalNode for (let i = 0, len = parentNode.values.length; i < len; i++) { const nValue = parentNode.values[i] if (this.comparator.isSame(guess!, nValue)) { @@ -663,7 +680,7 @@ export class BPTree { pointerK0 = pointer.values.splice(0, 1)[0] node.keys = [...node.keys, pointerP0] node.values = [...node.values, guess!] - parentNode = this.getNode(node.parent) + parentNode = this.getNode(node.parent) as BPTreeInternalNode for (let i = 0, len = parentNode.values.length; i < len; i++) { const nValue = parentNode.values[i] if (this.comparator.isSame(guess!, nValue)) { @@ -674,11 +691,11 @@ export class BPTree { } } else { - pointerP0 = pointer.keys.splice(0, 1)[0] + pointerP0 = pointer.keys.splice(0, 1)[0] as unknown as K[] pointerK0 = pointer.values.splice(0, 1)[0] node.keys = [...node.keys, pointerP0] node.values = [...node.values, pointerK0] - parentNode = this.getNode(node.parent) + parentNode = this.getNode(node.parent) as BPTreeInternalNode for (let i = 0, len = parentNode.values.length; i < len; i++) { const nValue = parentNode.values[i] if (this.comparator.isSame(guess!, nValue)) { @@ -692,24 +709,24 @@ export class BPTree { this._setUpdates(pointer) } if (!pointer.leaf) { - const keys = pointer.keys as BPTreeNode[] - for (const key of keys) { - key.parent = pointer.id - this._setUpdates(pointer) + for (const key of pointer.keys) { + const n = this.getNode(key) + n.parent = pointer.id + this._setUpdates(n) } } if (!node.leaf) { - const keys = node.keys as BPTreeNode[] - for (const key of keys) { - key.parent = node.id - this._setUpdates(node) + for (const key of node.keys) { + const n = this.getNode(key) + n.parent = node.id + this._setUpdates(n) } } if (!parentNode.leaf) { - const keys = parentNode.keys as BPTreeNode[] - for (const key of keys) { - key.parent = parentNode.id - this._setUpdates(parentNode) + for (const key of parentNode.keys) { + const n = this.getNode(key) + n.parent = parentNode.id + this._setUpdates(n) } } }