Skip to content

Commit

Permalink
feat: Add setHeadData, getHeadData, and autoIncrement methods due to …
Browse files Browse the repository at this point in the history
…issue #1 (comment)
  • Loading branch information
izure1 committed Mar 15, 2024
1 parent 5e86dbe commit 27fae71
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 137 deletions.
22 changes: 17 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ import {

class FileStoreStrategySync extends SerializeStrategySync<K, V> {
id(): number {
const random = Math.ceil(Math.random()*1000000)
return random
const index = this.getHeadData('index', 1) as number
this.setHeadData('index', index+1)
return index
}

read(id: number): BPTreeNode<K, V> {
Expand Down Expand Up @@ -202,7 +203,17 @@ id(): number {

Or, you could use file input/output to save and load the value of the **before** variable.

This method is called before a node is created in the tree. Therefore, it can also be used to allocate space for storing the node.
Typically, such an **id** value increases sequentially, and it would be beneficial to store such a value separately within the tree. For that purpose, the **setHeadData** and **getHeadData** methods are available. These methods are responsible for storing arbitrary data in the tree's header or retrieving stored data. Below is an example of usage:

```typescript
id(): number {
const current = this.getHeadData('index', 1) as number
this.setHeadData('index', current+1)
return current
}
```

The **id** method is called before a node is created in the tree. Therefore, it can also be used to allocate space for storing the node.

#### read(id: `number`): `BPTreeNode<K, V>`

Expand Down Expand Up @@ -350,8 +361,9 @@ import {

class FileStoreStrategyAsync extends SerializeStrategyAsync<K, V> {
async id(): Promise<number> {
const random = Math.ceil(Math.random()*1000000)
return random
const index = await this.getHeadData('index', 1) as number
await this.setHeadData('index', index+1)
return index
}

async read(id: number): Promise<BPTreeNode<K, V>> {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serializable-bptree",
"version": "3.1.0",
"version": "3.2.0",
"description": "Store the B+tree flexibly, not only in-memory.",
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
10 changes: 4 additions & 6 deletions src/BPTreeAsync.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { Json } from './utils/types'
import {
BPTree,
BPTreeCondition,
Expand All @@ -10,6 +9,7 @@ import {
} from './base/BPTree'
import { SerializeStrategyAsync } from './SerializeStrategyAsync'
import { ValueComparator } from './base/ValueComparator'
import { SerializableData } from './base/SerializeStrategy'

export class BPTreeAsync<K, V> extends BPTree<K, V> {
declare protected readonly strategy: SerializeStrategyAsync<K, V>
Expand Down Expand Up @@ -415,7 +415,6 @@ export class BPTreeAsync<K, V> extends BPTree<K, V> {
// first created
if (head === null) {
this.order = this.strategy.order
this.data = {}
this.root = await this._createNode([], [], true)
this.bufferForHeadUpdate(this.headState)
this.bufferForNodeCreate(this.root)
Expand All @@ -424,9 +423,8 @@ export class BPTreeAsync<K, V> extends BPTree<K, V> {
}
// loaded
else {
const { root, order, data } = head
const { root, order } = head
this.order = order
this.data = data ?? {}
this.root = await this.getNode(root)
}
if (this.order < 3) {
Expand Down Expand Up @@ -613,8 +611,8 @@ export class BPTreeAsync<K, V> extends BPTree<K, V> {
return false
}

public async setHeadData(data: Record<string, Json>): Promise<void> {
this.data = data
public async setHeadData(data: SerializableData): Promise<void> {
this.strategy.head.data = data
this.bufferForHeadUpdate(this.headState)
await this.commitHeadBuffer()
}
Expand Down
10 changes: 4 additions & 6 deletions src/BPTreeSync.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import type { Json } from './utils/types'
import {
BPTree,
BPTreeCondition,
Expand All @@ -10,6 +9,7 @@ import {
} from './base/BPTree'
import { SerializeStrategySync } from './SerializeStrategySync'
import { ValueComparator } from './base/ValueComparator'
import { SerializableData } from './base/SerializeStrategy'

export class BPTreeSync<K, V> extends BPTree<K, V> {
declare protected readonly strategy: SerializeStrategySync<K, V>
Expand Down Expand Up @@ -446,7 +446,6 @@ export class BPTreeSync<K, V> extends BPTree<K, V> {
// first created
if (head === null) {
this.order = this.strategy.order
this.data = {}
this.root = this._createNode([], [], true)
this.bufferForHeadUpdate(this.headState)
this.bufferForNodeCreate(this.root)
Expand All @@ -455,9 +454,8 @@ export class BPTreeSync<K, V> extends BPTree<K, V> {
}
// loaded
else {
const { root, order, data } = head
const { root, order } = head
this.order = order
this.data = data ?? {}
this.root = this.getNode(root)
}
if (this.order < 3) {
Expand Down Expand Up @@ -644,8 +642,8 @@ export class BPTreeSync<K, V> extends BPTree<K, V> {
return false
}

public setHeadData(data: Record<string, Json>): void {
this.data = data
public setHeadData(data: SerializableData): void {
this.strategy.head.data = data
this.bufferForHeadUpdate(this.headState)
this.commitHeadBuffer()
}
Expand Down
45 changes: 31 additions & 14 deletions src/SerializeStrategyAsync.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,65 @@
import { BPTreeNode } from './base/BPTree'
import { SerializeStrategy, SerializeStrategyHead } from './base/SerializeStrategy'
import { Json } from './utils/types'

export abstract class SerializeStrategyAsync<K, V> extends SerializeStrategy<K, V> {
abstract id(): Promise<number>
abstract read(id: number): Promise<BPTreeNode<K, V>>
abstract write(id: number, node: BPTreeNode<K, V>): Promise<void>
abstract readHead(): Promise<SerializeStrategyHead|null>
abstract writeHead(head: SerializeStrategyHead): Promise<void>

async getHeadData(key: string, defaultValue: Json): Promise<Json> {
if (!Object.hasOwn(this.head.data, key)) {
return defaultValue
}
return this.head.data[key]
}

async setHeadData(key: string, data: Json): Promise<void> {
this.head.data[key] = data
await this.writeHead(this.head)
}

async autoIncrement(key: string, defaultValue: number): Promise<number> {
const current = await this.getHeadData(key, defaultValue) as number
const next = current+1
await this.setHeadData(key, next)
return current
}
}

export class InMemoryStoreStrategyAsync<K, V> extends SerializeStrategyAsync<K, V> {
protected readonly data: {
head: SerializeStrategyHead|null,
node: Record<number, BPTreeNode<K, V>>
}
protected readonly node: Record<number, BPTreeNode<K, V>>

constructor(order: number) {
super(order)
this.data = {
head: null,
node: {}
}
this.node = {}
}

async id(): Promise<number> {
return Math.ceil(Math.random()*Number.MAX_SAFE_INTEGER-1)
return await this.autoIncrement('index', 1)
}

async read(id: number): Promise<BPTreeNode<K, V>> {
if (!Object.prototype.hasOwnProperty.call(this.data.node, id)) {
if (!Object.hasOwn(this.node, id)) {
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`)
}
return this.data.node[id]
return this.node[id] as BPTreeNode<K, V>
}

async write(id: number, node: BPTreeNode<K, V>): Promise<void> {
this.data.node[id] = node
this.node[id] = node
}

async readHead(): Promise<SerializeStrategyHead|null> {
return this.data.head
if (this.head.root === 0) {
return null
}
return this.head
}

async writeHead(head: SerializeStrategyHead): Promise<void> {
this.data.head = head
(this as any).head = head
}
}
45 changes: 31 additions & 14 deletions src/SerializeStrategySync.ts
Original file line number Diff line number Diff line change
@@ -1,48 +1,65 @@
import { BPTreeNode } from './base/BPTree'
import { SerializeStrategy, SerializeStrategyHead } from './base/SerializeStrategy'
import { Json } from './utils/types'

export abstract class SerializeStrategySync<K, V> extends SerializeStrategy<K, V> {
abstract id(): number
abstract read(id: number): BPTreeNode<K, V>
abstract write(id: number, node: BPTreeNode<K, V>): void
abstract readHead(): SerializeStrategyHead|null
abstract writeHead(head: SerializeStrategyHead): void

getHeadData(key: string, defaultValue: Json): Json {
if (!Object.hasOwn(this.head.data, key)) {
return defaultValue
}
return this.head.data[key]
}

setHeadData(key: string, data: Json): void {
this.head.data[key] = data
this.writeHead(this.head)
}

autoIncrement(key: string, defaultValue: number): number {
const current = this.getHeadData(key, defaultValue) as number
const next = current+1
this.setHeadData(key, next)
return current
}
}

export class InMemoryStoreStrategySync<K, V> extends SerializeStrategySync<K, V> {
protected readonly data: {
head: SerializeStrategyHead|null,
node: Record<number, BPTreeNode<K, V>>
}
protected readonly node: Record<number, BPTreeNode<K, V>>

constructor(order: number) {
super(order)
this.data = {
head: null,
node: {}
}
this.node = {}
}

id(): number {
return Math.ceil(Math.random()*Number.MAX_SAFE_INTEGER-1)
return this.autoIncrement('index', 1)
}

read(id: number): BPTreeNode<K, V> {
if (!Object.prototype.hasOwnProperty.call(this.data.node, id)) {
if (!Object.hasOwn(this.node, id)) {
throw new Error(`The tree attempted to reference node '${id}', but couldn't find the corresponding node.`)
}
return this.data.node[id]
return this.node[id] as BPTreeNode<K, V>
}

write(id: number, node: BPTreeNode<K, V>): void {
this.data.node[id] = node
this.node[id] = node
}

readHead(): SerializeStrategyHead|null {
return this.data.head
if (this.head.root === 0) {
return null
}
return this.head
}

writeHead(head: SerializeStrategyHead): void {
this.data.head = head
(this as any).head = head
}
}
12 changes: 5 additions & 7 deletions src/base/BPTree.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { CacheStorage } from '../utils/CacheStorage'
import type { Json } from '../utils/types'
import { ValueComparator } from './ValueComparator'
import { SerializeStrategy, SerializeStrategyHead } from './SerializeStrategy'
import { SerializableData, SerializeStrategy, SerializeStrategyHead } from './SerializeStrategy'

type Sync<T> = T
type Async<T> = Promise<T>
Expand Down Expand Up @@ -55,7 +54,6 @@ export abstract class BPTree<K, V> {
protected readonly comparator: ValueComparator<V>
protected readonly nodes: Map<number, BPTreeUnknownNode<K, V>>
protected order!: number
protected data!: Record<string, Json>
protected root!: BPTreeUnknownNode<K, V>

protected readonly _nodeCreateBuffer: Map<number, BPTreeUnknownNode<K, V>>
Expand Down Expand Up @@ -119,7 +117,7 @@ export abstract class BPTree<K, V> {
protected get headState(): SerializeStrategyHead {
const root = this.root.id
const order = this.order
const data = this.data
const data = this.strategy.head.data
return {
root,
order,
Expand Down Expand Up @@ -218,7 +216,7 @@ export abstract class BPTree<K, V> {
* For example, you can store information such as the last update time and the number of insertions.
* @param data User-defined data to be stored in the head of the tree.
*/
public abstract setHeadData(data: Record<string, Json>): Deferred<void>
public abstract setHeadData(data: SerializableData): Deferred<void>
/**
* This method deletes nodes cached in-memory and caches new nodes from the stored nodes.
* Typically, there's no need to use this method, but it can be used to synchronize data in scenarios where the remote storage and the client are in a 1:n relationship.
Expand Down Expand Up @@ -274,7 +272,7 @@ export abstract class BPTree<K, V> {
* This value can be set using the `setHeadData` method. If no data has been previously inserted, the default value is returned, and the default value is `{}`.
* @returns User-defined data stored in the head of the tree.
*/
getHeadData(): Record<string, Json> {
return this.data
getHeadData(): SerializableData {
return this.strategy.head.data
}
}
Loading

0 comments on commit 27fae71

Please sign in to comment.