extra-disk-store 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "extra-disk-store",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "files": [
7
- "lib"
7
+ "lib",
8
+ "src"
8
9
  ],
9
10
  "type": "module",
10
11
  "main": "lib/index.js",
@@ -0,0 +1,13 @@
1
+ import { IKeyConverter } from '@src/types.js'
2
+
3
+ export class IndexKeyConverter implements IKeyConverter<number> {
4
+ constructor(private radix: number = 10) {}
5
+
6
+ toString(value: number): string {
7
+ return value.toString(this.radix)
8
+ }
9
+
10
+ fromString(value: string): number {
11
+ return Number.parseInt(value, this.radix)
12
+ }
13
+ }
@@ -0,0 +1,11 @@
1
+ export * from './json-key-converter.js'
2
+ export * from './json-value-converter.js'
3
+
4
+ export * from './passthrough-key-converter.js'
5
+ export * from './passthrough-value-converter.js'
6
+
7
+ export * from './index-key-converter.js'
8
+ export * from './prefix-key-converter.js'
9
+
10
+ export * from './lz4-value-converter.js'
11
+ export * from './zstandard-value-converter.js'
@@ -0,0 +1,11 @@
1
+ import { IKeyConverter } from '@src/types.js'
2
+
3
+ export class JSONKeyConverter<T> implements IKeyConverter<T> {
4
+ fromString(value: string): T {
5
+ return JSON.parse(value)
6
+ }
7
+
8
+ toString(value: T): string {
9
+ return JSON.stringify(value)
10
+ }
11
+ }
@@ -0,0 +1,13 @@
1
+ import { IValueConverter } from '@src/types.js'
2
+
3
+ export class JSONValueConverter<T> implements IValueConverter<T> {
4
+ constructor(private encoding: BufferEncoding = 'utf-8') {}
5
+
6
+ fromBuffer(buffer: Buffer): T {
7
+ return JSON.parse(buffer.toString(this.encoding))
8
+ }
9
+
10
+ toBuffer(value: T): Buffer {
11
+ return Buffer.from(JSON.stringify(value), this.encoding)
12
+ }
13
+ }
@@ -0,0 +1,18 @@
1
+ import * as lz4 from 'lz4-wasm-nodejs'
2
+ import { IValueConverter } from '@src/types.js'
3
+
4
+ export class LZ4ValueConverter<T> implements IValueConverter<T> {
5
+ constructor(
6
+ private valueConverter: IValueConverter<T>
7
+ ) {}
8
+
9
+ async toBuffer(value: T): Promise<Buffer> {
10
+ const buffer = await this.valueConverter.toBuffer(value)
11
+ return Buffer.from(lz4.compress(buffer))
12
+ }
13
+
14
+ async fromBuffer(value: Buffer): Promise<T> {
15
+ const buffer = Buffer.from(lz4.decompress(value))
16
+ return await this.valueConverter.fromBuffer(buffer)
17
+ }
18
+ }
@@ -0,0 +1,11 @@
1
+ import { IKeyConverter } from '@src/types.js'
2
+
3
+ export class PassthroughKeyConverter implements IKeyConverter<string> {
4
+ toString(value: string): string {
5
+ return value
6
+ }
7
+
8
+ fromString(value: string): string {
9
+ return value
10
+ }
11
+ }
@@ -0,0 +1,11 @@
1
+ import { IValueConverter } from '@src/types.js'
2
+
3
+ export class PassthroughValueConverter implements IValueConverter<Buffer> {
4
+ toBuffer(value: Buffer): Buffer {
5
+ return value
6
+ }
7
+
8
+ fromBuffer(value: Buffer): Buffer {
9
+ return value
10
+ }
11
+ }
@@ -0,0 +1,22 @@
1
+ import { IKeyConverter } from '@src/types.js'
2
+
3
+ export class PrefixKeyConverter<T> implements IKeyConverter<T> {
4
+ constructor(
5
+ private keyConverter: IKeyConverter<T>
6
+ , private prefix: string
7
+ ) {}
8
+
9
+ async toString(value: T): Promise<string> {
10
+ const key = await this.keyConverter.toString(value)
11
+ return this.prefix + key
12
+ }
13
+
14
+ async fromString(value: string): Promise<T | undefined> {
15
+ if (value.startsWith(this.prefix)) {
16
+ const key = await this.keyConverter.fromString(value.slice(this.prefix.length))
17
+ return key
18
+ } else {
19
+ return undefined
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,19 @@
1
+ import * as zstd from '@mongodb-js/zstd'
2
+ import { IValueConverter } from '@src/types.js'
3
+
4
+ export class ZstandardValueConverter<T> implements IValueConverter<T> {
5
+ constructor(
6
+ private valueConverter: IValueConverter<T>
7
+ , private level: number
8
+ ) {}
9
+
10
+ async toBuffer(value: T): Promise<Buffer> {
11
+ const buffer = await this.valueConverter.toBuffer(value)
12
+ return Buffer.from(await zstd.compress(buffer, this.level))
13
+ }
14
+
15
+ async fromBuffer(value: Buffer): Promise<T> {
16
+ const buffer = Buffer.from(await zstd.decompress(value))
17
+ return await this.valueConverter.fromBuffer(buffer)
18
+ }
19
+ }
@@ -0,0 +1,62 @@
1
+ import { DiskStore } from '@src/disk-store.js'
2
+ import { DiskStoreWithCache } from '@src/disk-store-with-cache.js'
3
+ import { pipe } from 'extra-utils'
4
+ import { mapAsync, filterAsync } from 'iterable-operator'
5
+ import { Awaitable, isntUndefined } from '@blackglory/prelude'
6
+
7
+ export interface IKeyConverter<T> {
8
+ toString: (value: T) => Awaitable<string>
9
+ fromString: (value: string) => Awaitable<T | undefined>
10
+ }
11
+
12
+ export interface IValueConverter<T> {
13
+ toBuffer: (value: T) => Awaitable<Buffer>
14
+ fromBuffer: (value: Buffer) => Awaitable<T>
15
+ }
16
+
17
+ export class DiskStoreView<K, V> {
18
+ constructor(
19
+ private store: DiskStore | DiskStoreWithCache
20
+ , private keyConverter: IKeyConverter<K>
21
+ , private valueConverter: IValueConverter<V>
22
+ ) {}
23
+
24
+ async has(key: K): Promise<boolean> {
25
+ return this.store.has(await this.keyConverter.toString(key))
26
+ }
27
+
28
+ async get(key: K): Promise<V | undefined> {
29
+ const buffer = this.store.get(await this.keyConverter.toString(key))
30
+
31
+ if (buffer) {
32
+ return this.valueConverter.fromBuffer(buffer)
33
+ } else {
34
+ return undefined
35
+ }
36
+ }
37
+
38
+ async set(key: K, value: V): Promise<void> {
39
+ await this.store.set(
40
+ ...await Promise.all([
41
+ await this.keyConverter.toString(key)
42
+ , await this.valueConverter.toBuffer(value)
43
+ ])
44
+ )
45
+ }
46
+
47
+ async delete(key: K): Promise<void> {
48
+ await this.store.delete(await this.keyConverter.toString(key))
49
+ }
50
+
51
+ async clear(): Promise<void> {
52
+ await this.store.clear()
53
+ }
54
+
55
+ keys(): AsyncIterableIterator<K> {
56
+ return pipe(
57
+ this.store.keys()
58
+ , iter => mapAsync(iter, async key => await this.keyConverter.fromString(key))
59
+ , iter => filterAsync(iter, isntUndefined)
60
+ )
61
+ }
62
+ }
@@ -0,0 +1,78 @@
1
+ import { isntUndefined } from '@blackglory/prelude'
2
+ import { DiskStore } from './disk-store.js'
3
+
4
+ export interface ICache {
5
+ set(key: string, value: Buffer | false): void
6
+ get(key: string): Buffer | false | undefined
7
+ delete(key: string): void
8
+ clear(): void
9
+ }
10
+
11
+ export class DiskStoreWithCache {
12
+ constructor(
13
+ private store: DiskStore
14
+ , private cache: ICache
15
+ ) {}
16
+
17
+ async close(): Promise<void> {
18
+ await this.store.close()
19
+ }
20
+
21
+ has(key: string): boolean {
22
+ const result = this.cache.get(key)
23
+ if (result === false) {
24
+ return result
25
+ } else if (isntUndefined(result)) {
26
+ return true
27
+ } else {
28
+ const result = this.store.get(key)
29
+ if (result) {
30
+ this.cache.set(key, result)
31
+ return true
32
+ } else {
33
+ this.cache.set(key, false)
34
+ return false
35
+ }
36
+ }
37
+ }
38
+
39
+ get(key: string): Buffer | undefined {
40
+ const result = this.cache.get(key)
41
+ if (result === false) {
42
+ return undefined
43
+ } else if (isntUndefined(result)) {
44
+ return result
45
+ } else {
46
+ const result = this.store.get(key)
47
+ if (result) {
48
+ this.cache.set(key, result)
49
+ return result
50
+ } else {
51
+ this.cache.set(key, false)
52
+ return result
53
+ }
54
+ }
55
+ }
56
+
57
+ async set(key: string, value: Buffer): Promise<void> {
58
+ await this.store.set(key, value)
59
+
60
+ this.cache.delete(key)
61
+ }
62
+
63
+ async delete(key: string): Promise<void> {
64
+ await this.store.delete(key)
65
+
66
+ this.cache.delete(key)
67
+ }
68
+
69
+ async clear(): Promise<void> {
70
+ await this.store.clear()
71
+
72
+ this.cache.clear()
73
+ }
74
+
75
+ keys(): IterableIterator<string> {
76
+ return this.store.keys()
77
+ }
78
+ }
@@ -0,0 +1,59 @@
1
+ import * as Iter from 'iterable-operator'
2
+ import * as LMDB from 'lmdb'
3
+ import { createTempNameSync, remove } from 'extra-filesystem'
4
+ import { isUndefined } from '@blackglory/prelude'
5
+
6
+ export class DiskStore {
7
+ public _db: LMDB.RootDatabase
8
+ public _dirname: string
9
+ private isTempPathname: boolean
10
+
11
+ constructor(dirname?: string) {
12
+ if (isUndefined(dirname)) {
13
+ this._dirname = createTempNameSync()
14
+ this.isTempPathname = true
15
+ } else {
16
+ this._dirname = dirname
17
+ this.isTempPathname = false
18
+ }
19
+
20
+ this._db = LMDB.open<Buffer, string>(this._dirname, {
21
+ // 采用其他编码方式可能遇到错误.
22
+ // 尤其是采用与lmdb-js同作者的msgpackr一定会出现错误, 因为它是一个有问题的实现.
23
+ encoding: 'binary'
24
+ , compression: false
25
+ })
26
+ }
27
+
28
+ async close(): Promise<void> {
29
+ await this._db.close()
30
+
31
+ if (this.isTempPathname) {
32
+ await remove(this._dirname)
33
+ }
34
+ }
35
+
36
+ has(key: string): boolean {
37
+ return this._db.doesExist(key)
38
+ }
39
+
40
+ get(key: string): Buffer | undefined {
41
+ return this._db.getBinary(key)
42
+ }
43
+
44
+ async set(key: string, value: Buffer): Promise<void> {
45
+ await this._db.put(key, value)
46
+ }
47
+
48
+ async delete(key: string): Promise<void> {
49
+ await this._db.remove(key)
50
+ }
51
+
52
+ async clear(): Promise<void> {
53
+ await this._db.clearAsync()
54
+ }
55
+
56
+ keys(): IterableIterator<string> {
57
+ return Iter.map(this._db.getKeys(), key => key as string)
58
+ }
59
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './disk-store.js'
2
+ export { DiskStoreWithCache, ICache } from './disk-store-with-cache.js'
3
+ export { DiskStoreView, IKeyConverter, IValueConverter } from './disk-store-view.js'
4
+ export * from './converters/index.js'
package/src/types.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { Awaitable } from '@blackglory/prelude'
2
+
3
+ export interface ICache {
4
+ set(key: string, value: Buffer | boolean | undefined): void
5
+ get(key: string): Buffer | boolean | undefined
6
+ delete(key: string): void
7
+ clear(): void
8
+ }
9
+
10
+ export interface IKeyConverter<T> {
11
+ toString: (value: T) => Awaitable<string>
12
+ fromString: (value: string) => Awaitable<T | undefined>
13
+ }
14
+
15
+ export interface IValueConverter<T> {
16
+ toBuffer: (value: T) => Awaitable<Buffer>
17
+ fromBuffer: (value: Buffer) => Awaitable<T>
18
+ }