xitdb 0.1.0 → 0.3.0

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.
Files changed (64) hide show
  1. package/LICENSE +21 -0
  2. package/dist/core-buffered-file.d.ts +41 -0
  3. package/dist/core-file.d.ts +18 -0
  4. package/dist/core-memory.d.ts +36 -0
  5. package/dist/core.d.ts +23 -0
  6. package/dist/database.d.ts +244 -0
  7. package/dist/exceptions.d.ts +51 -0
  8. package/dist/hasher.d.ts +9 -0
  9. package/dist/index.d.ts +26 -0
  10. package/dist/index.js +429 -266
  11. package/dist/read-array-list.d.ts +13 -0
  12. package/dist/read-counted-hash-map.d.ts +7 -0
  13. package/dist/read-counted-hash-set.d.ts +7 -0
  14. package/dist/read-cursor.d.ts +57 -0
  15. package/dist/read-hash-map.d.ts +27 -0
  16. package/dist/read-hash-set.d.ts +18 -0
  17. package/dist/read-linked-array-list.d.ts +13 -0
  18. package/dist/slot-pointer.d.ts +7 -0
  19. package/dist/slot.d.ts +15 -0
  20. package/{src/slotted.ts → dist/slotted.d.ts} +1 -2
  21. package/dist/tag.d.ts +17 -0
  22. package/dist/write-array-list.d.ts +16 -0
  23. package/dist/write-counted-hash-map.d.ts +7 -0
  24. package/dist/write-counted-hash-set.d.ts +7 -0
  25. package/dist/write-cursor.d.ts +36 -0
  26. package/dist/write-hash-map.d.ts +25 -0
  27. package/dist/write-hash-set.d.ts +19 -0
  28. package/dist/write-linked-array-list.d.ts +19 -0
  29. package/dist/writeable-data.d.ts +20 -0
  30. package/package.json +14 -1
  31. package/.claude/settings.local.json +0 -9
  32. package/bun.lock +0 -24
  33. package/bunfig.toml +0 -1
  34. package/example/README.md +0 -46
  35. package/example/dump.ts +0 -201
  36. package/src/core-buffered-file.ts +0 -226
  37. package/src/core-file.ts +0 -137
  38. package/src/core-memory.ts +0 -179
  39. package/src/core.ts +0 -25
  40. package/src/database.ts +0 -2232
  41. package/src/exceptions.ts +0 -31
  42. package/src/hasher.ts +0 -52
  43. package/src/index.ts +0 -110
  44. package/src/read-array-list.ts +0 -45
  45. package/src/read-counted-hash-map.ts +0 -28
  46. package/src/read-counted-hash-set.ts +0 -28
  47. package/src/read-cursor.ts +0 -546
  48. package/src/read-hash-map.ts +0 -117
  49. package/src/read-hash-set.ts +0 -70
  50. package/src/read-linked-array-list.ts +0 -45
  51. package/src/slot-pointer.ts +0 -15
  52. package/src/slot.ts +0 -51
  53. package/src/tag.ts +0 -23
  54. package/src/write-array-list.ts +0 -65
  55. package/src/write-counted-hash-map.ts +0 -31
  56. package/src/write-counted-hash-set.ts +0 -31
  57. package/src/write-cursor.ts +0 -166
  58. package/src/write-hash-map.ts +0 -129
  59. package/src/write-hash-set.ts +0 -86
  60. package/src/write-linked-array-list.ts +0 -80
  61. package/src/writeable-data.ts +0 -67
  62. package/tests/database.test.ts +0 -2519
  63. package/tests/fixtures/test.db +0 -0
  64. package/tsconfig.json +0 -17
package/example/dump.ts DELETED
@@ -1,201 +0,0 @@
1
- #!/usr/bin/env bun
2
- import {
3
- Database,
4
- Hasher,
5
- CoreBufferedFile,
6
- ReadArrayList,
7
- ReadCursor,
8
- Tag,
9
- } from '../src';
10
-
11
- async function formatKey(cursor: ReadCursor): Promise<string> {
12
- const tag = cursor.slotPtr.slot.tag;
13
-
14
- switch (tag) {
15
- case Tag.NONE:
16
- return '(none)';
17
- case Tag.BYTES:
18
- case Tag.SHORT_BYTES: {
19
- const bytes = await cursor.readBytes(null);
20
- const text = new TextDecoder().decode(bytes);
21
- return `"${text}"`;
22
- }
23
- case Tag.UINT:
24
- return `${cursor.readUint()}`;
25
- case Tag.INT:
26
- return `${cursor.readInt()}`;
27
- case Tag.FLOAT:
28
- return `${cursor.readFloat()}`;
29
- default:
30
- return `<key tag: ${tag}>`;
31
- }
32
- }
33
-
34
- async function printValue(cursor: ReadCursor, indent: string): Promise<void> {
35
- const tag = cursor.slotPtr.slot.tag;
36
-
37
- switch (tag) {
38
- case Tag.NONE:
39
- console.log(`${indent}(none)`);
40
- break;
41
-
42
- case Tag.ARRAY_LIST: {
43
- const list = new ReadArrayList(cursor);
44
- const count = await list.count();
45
- console.log(`${indent}ArrayList[${count}]:`);
46
- if (indent == '') {
47
- const itemCursor = await list.getCursor(count - 1);
48
- if (itemCursor) {
49
- await printValue(itemCursor, indent + ' ');
50
- }
51
- } else {
52
- for (let i = 0; i < count; i++) {
53
- const itemCursor = await list.getCursor(i);
54
- if (itemCursor) {
55
- console.log(`${indent} [${i}]:`);
56
- await printValue(itemCursor, indent + ' ');
57
- }
58
- }
59
- }
60
- break;
61
- }
62
-
63
- case Tag.HASH_MAP:
64
- case Tag.COUNTED_HASH_MAP: {
65
- const iter = cursor.iterator();
66
- await iter.init();
67
- const entries: Array<{ key: string; valueCursor: ReadCursor }> = [];
68
-
69
- while (await iter.hasNext()) {
70
- const kvPairCursor = await iter.next();
71
- if (kvPairCursor) {
72
- const kvPair = await kvPairCursor.readKeyValuePair();
73
- const key = await formatKey(kvPair.keyCursor);
74
- entries.push({ key, valueCursor: kvPair.valueCursor });
75
- }
76
- }
77
-
78
- const prefix = tag === Tag.COUNTED_HASH_MAP ? 'CountedHashMap' : 'HashMap';
79
- console.log(`${indent}${prefix}{${entries.length}}:`);
80
-
81
- for (const entry of entries) {
82
- console.log(`${indent} ${entry.key}:`);
83
- await printValue(entry.valueCursor, indent + ' ');
84
- }
85
- break;
86
- }
87
-
88
- case Tag.HASH_SET:
89
- case Tag.COUNTED_HASH_SET: {
90
- const iter = cursor.iterator();
91
- await iter.init();
92
- const keys: string[] = [];
93
-
94
- while (await iter.hasNext()) {
95
- const kvPairCursor = await iter.next();
96
- if (kvPairCursor) {
97
- const kvPair = await kvPairCursor.readKeyValuePair();
98
- const key = await formatKey(kvPair.keyCursor);
99
- keys.push(key);
100
- }
101
- }
102
-
103
- const prefix = tag === Tag.COUNTED_HASH_SET ? 'CountedHashSet' : 'HashSet';
104
- console.log(`${indent}${prefix}{${keys.length}}: [${keys.join(', ')}]`);
105
- break;
106
- }
107
-
108
- case Tag.LINKED_ARRAY_LIST: {
109
- const count = await cursor.count();
110
- console.log(`${indent}LinkedArrayList[${count}]:`);
111
- const iter = cursor.iterator();
112
- await iter.init();
113
- let i = 0;
114
- while (await iter.hasNext()) {
115
- const itemCursor = await iter.next();
116
- if (itemCursor) {
117
- console.log(`${indent} [${i}]:`);
118
- await printValue(itemCursor, indent + ' ');
119
- }
120
- i++;
121
- }
122
- break;
123
- }
124
-
125
- case Tag.BYTES:
126
- case Tag.SHORT_BYTES: {
127
- const bytesObj = await cursor.readBytesObject(null);
128
- const text = new TextDecoder().decode(bytesObj.value);
129
- const isPrintable = /^[\x20-\x7E\n\r\t]*$/.test(text);
130
-
131
- if (isPrintable && text.length <= 100) {
132
- if (bytesObj.formatTag) {
133
- const formatTag = new TextDecoder().decode(bytesObj.formatTag);
134
- console.log(`${indent}"${text}" (format: ${formatTag})`);
135
- } else {
136
- console.log(`${indent}"${text}"`);
137
- }
138
- } else {
139
- const preview = bytesObj.value.slice(0, 16);
140
- const hex = Array.from(preview).map(b => b.toString(16).padStart(2, '0')).join(' ');
141
- if (bytesObj.formatTag) {
142
- const formatTag = new TextDecoder().decode(bytesObj.formatTag);
143
- console.log(`${indent}<${bytesObj.value.length} bytes: ${hex}...> (format: ${formatTag})`);
144
- } else {
145
- console.log(`${indent}<${bytesObj.value.length} bytes: ${hex}...>`);
146
- }
147
- }
148
- break;
149
- }
150
-
151
- case Tag.UINT: {
152
- const value = cursor.readUint();
153
- console.log(`${indent}${value} (uint)`);
154
- break;
155
- }
156
-
157
- case Tag.INT: {
158
- const value = cursor.readInt();
159
- console.log(`${indent}${value} (int)`);
160
- break;
161
- }
162
-
163
- case Tag.FLOAT: {
164
- const value = cursor.readFloat();
165
- console.log(`${indent}${value} (float)`);
166
- break;
167
- }
168
-
169
- default:
170
- console.log(`${indent}<unknown tag: ${tag}>`);
171
- }
172
- }
173
-
174
- async function main() {
175
- const args = process.argv.slice(2);
176
-
177
- if (args.length < 1) {
178
- console.error('Usage: bun run dump.ts <database-file>');
179
- process.exit(1);
180
- }
181
-
182
- const filePath = args[0];
183
-
184
- try {
185
- const core = await CoreBufferedFile.create(filePath);
186
- const hasher = new Hasher('SHA-1');
187
- const db = await Database.create(core, hasher);
188
-
189
- console.log(`Database: ${filePath}`);
190
- console.log('---');
191
-
192
- const rootCursor = await db.rootCursor();
193
- await printValue(rootCursor, '');
194
-
195
- } catch (error) {
196
- console.error(`Error reading database: ${error}`);
197
- process.exit(1);
198
- }
199
- }
200
-
201
- main();
@@ -1,226 +0,0 @@
1
- import type { Core, DataReader, DataWriter } from './core';
2
- import { CoreFile } from './core-file';
3
- import { CoreMemory } from './core-memory';
4
-
5
- export class CoreBufferedFile implements Core {
6
- public file: RandomAccessBufferedFile;
7
-
8
- constructor(file: RandomAccessBufferedFile) {
9
- this.file = file;
10
- }
11
-
12
- static async create(filePath: string, bufferSize?: number): Promise<CoreBufferedFile> {
13
- const file = await RandomAccessBufferedFile.create(filePath, bufferSize);
14
- return new CoreBufferedFile(file);
15
- }
16
-
17
- reader(): DataReader {
18
- return this.file;
19
- }
20
-
21
- writer(): DataWriter {
22
- return this.file;
23
- }
24
-
25
- async length(): Promise<number> {
26
- return await this.file.length();
27
- }
28
-
29
- async seek(pos: number): Promise<void> {
30
- await this.file.seek(pos);
31
- }
32
-
33
- position(): number {
34
- return this.file.position();
35
- }
36
-
37
- async setLength(len: number): Promise<void> {
38
- await this.file.setLength(len);
39
- }
40
-
41
- async flush(): Promise<void> {
42
- await this.file.flush();
43
- }
44
-
45
- async sync(): Promise<void> {
46
- await this.file.sync();
47
- }
48
-
49
- [Symbol.dispose]() {
50
- this.file.file[Symbol.dispose]();
51
- }
52
- }
53
-
54
- const DEFAULT_BUFFER_SIZE = 8 * 1024 * 1024; // 8MB
55
-
56
- class RandomAccessBufferedFile implements DataReader, DataWriter {
57
- public file: CoreFile;
58
- private memory: CoreMemory;
59
- private bufferSize: number; // flushes when the memory is >= this size
60
- private filePos: number;
61
- private memoryPos: number;
62
-
63
- private constructor(file: CoreFile, bufferSize: number) {
64
- this.file = file;
65
- this.memory = new CoreMemory();
66
- this.bufferSize = bufferSize;
67
- this.filePos = 0;
68
- this.memoryPos = 0;
69
- }
70
-
71
- static async create(filePath: string, bufferSize: number = DEFAULT_BUFFER_SIZE): Promise<RandomAccessBufferedFile> {
72
- const file = await CoreFile.create(filePath);
73
- return new RandomAccessBufferedFile(file, bufferSize);
74
- }
75
-
76
- async seek(pos: number): Promise<void> {
77
- // flush if we are going past the end of the in-memory buffer
78
- if (pos > this.memoryPos + await this.memory.length()) {
79
- await this.flush();
80
- }
81
-
82
- this.filePos = pos;
83
-
84
- // if the buffer is empty, set its position to this offset as well
85
- if (await this.memory.length() === 0) {
86
- this.memoryPos = pos;
87
- }
88
- }
89
-
90
- async length(): Promise<number> {
91
- return Math.max(this.memoryPos + await this.memory.length(), await this.file.length());
92
- }
93
-
94
- position(): number {
95
- return this.filePos;
96
- }
97
-
98
- async setLength(len: number): Promise<void> {
99
- await this.flush();
100
- await this.file.setLength(len);
101
- this.filePos = Math.min(len, this.filePos);
102
- }
103
-
104
- async flush(): Promise<void> {
105
- if (await this.memory.length() > 0) {
106
- await this.file.seek(this.memoryPos);
107
- await this.file.writer().write(this.memory.memory.toByteArray());
108
-
109
- this.memoryPos = 0;
110
- this.memory.memory.reset();
111
- }
112
- }
113
-
114
- async sync(): Promise<void> {
115
- await this.flush();
116
- await this.file.sync();
117
- }
118
-
119
- // DataWriter interface
120
-
121
- async write(buffer: Uint8Array): Promise<void> {
122
- if (await this.memory.length() + buffer.length > this.bufferSize) {
123
- await this.flush();
124
- }
125
-
126
- if (this.filePos >= this.memoryPos && this.filePos <= this.memoryPos + await this.memory.length()) {
127
- this.memory.seek(this.filePos - this.memoryPos);
128
- await this.memory.memory.write(buffer);
129
- } else {
130
- // Write directly to file
131
- await this.file.seek(this.filePos);
132
- await this.file.writer().write(buffer);
133
- }
134
-
135
- this.filePos += buffer.length;
136
- }
137
-
138
- async writeByte(v: number): Promise<void> {
139
- await this.write(new Uint8Array([v & 0xff]));
140
- }
141
-
142
- async writeShort(v: number): Promise<void> {
143
- const buffer = new ArrayBuffer(2);
144
- const view = new DataView(buffer);
145
- view.setInt16(0, v & 0xffff, false); // big-endian
146
- await this.write(new Uint8Array(buffer));
147
- }
148
-
149
- async writeLong(v: number): Promise<void> {
150
- const buffer = new ArrayBuffer(8);
151
- const view = new DataView(buffer);
152
- view.setBigInt64(0, BigInt(v), false);
153
- await this.write(new Uint8Array(buffer));
154
- }
155
-
156
- // DataReader interface
157
-
158
- async readFully(buffer: Uint8Array): Promise<void> {
159
- let pos = 0;
160
-
161
- // read from the disk -- before the in-memory buffer
162
- if (this.filePos < this.memoryPos) {
163
- const sizeBeforeMem = Math.min(this.memoryPos - this.filePos, buffer.length);
164
- const tempBuffer = new Uint8Array(sizeBeforeMem);
165
- await this.file.seek(this.filePos);
166
- await this.file.reader().readFully(tempBuffer);
167
- buffer.set(tempBuffer, pos);
168
- pos += sizeBeforeMem;
169
- this.filePos += sizeBeforeMem;
170
- }
171
-
172
- if (pos === buffer.length) return;
173
-
174
- // read from the in-memory buffer
175
- if (this.filePos >= this.memoryPos && this.filePos < this.memoryPos + await this.memory.length()) {
176
- const memPos = this.filePos - this.memoryPos;
177
- const sizeInMem = Math.min(await this.memory.length() - memPos, buffer.length - pos);
178
- this.memory.seek(memPos);
179
- const memBuffer = new Uint8Array(sizeInMem);
180
- await this.memory.memory.readFully(memBuffer);
181
- buffer.set(memBuffer, pos);
182
- pos += sizeInMem;
183
- this.filePos += sizeInMem;
184
- }
185
-
186
- if (pos === buffer.length) return;
187
-
188
- // read from the disk -- after the in-memory buffer
189
- if (this.filePos >= this.memoryPos + await this.memory.length()) {
190
- const sizeAfterMem = buffer.length - pos;
191
- const tempBuffer = new Uint8Array(sizeAfterMem);
192
- await this.file.seek(this.filePos);
193
- await this.file.reader().readFully(tempBuffer);
194
- buffer.set(tempBuffer, pos);
195
- pos += sizeAfterMem;
196
- this.filePos += sizeAfterMem;
197
- }
198
- }
199
-
200
- async readByte(): Promise<number> {
201
- const bytes = new Uint8Array(1);
202
- await this.readFully(bytes);
203
- return bytes[0];
204
- }
205
-
206
- async readShort(): Promise<number> {
207
- const bytes = new Uint8Array(2);
208
- await this.readFully(bytes);
209
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
210
- return view.getInt16(0, false); // big-endian
211
- }
212
-
213
- async readInt(): Promise<number> {
214
- const bytes = new Uint8Array(4);
215
- await this.readFully(bytes);
216
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
217
- return view.getInt32(0, false); // big-endian
218
- }
219
-
220
- async readLong(): Promise<number> {
221
- const bytes = new Uint8Array(8);
222
- await this.readFully(bytes);
223
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
224
- return Number(view.getBigInt64(0, false));
225
- }
226
- }
package/src/core-file.ts DELETED
@@ -1,137 +0,0 @@
1
- import type { Core, DataReader, DataWriter } from './core';
2
- import * as fs from 'fs/promises';
3
- import type { FileHandle } from 'fs/promises';
4
-
5
- export class CoreFile implements Core {
6
- public filePath: string;
7
- private _position: number = 0;
8
- public fileHandle: FileHandle;
9
-
10
- private constructor(filePath: string, fileHandle: FileHandle) {
11
- this.filePath = filePath;
12
- this.fileHandle = fileHandle;
13
- }
14
-
15
- static async create(filePath: string): Promise<CoreFile> {
16
- // Create file if it doesn't exist
17
- try {
18
- await fs.access(filePath);
19
- } catch {
20
- await fs.writeFile(filePath, new Uint8Array(0));
21
- }
22
- // Open file handle for reading and writing
23
- const fileHandle = await fs.open(filePath, 'r+');
24
- return new CoreFile(filePath, fileHandle);
25
- }
26
-
27
- reader(): DataReader {
28
- return new FileDataReader(this);
29
- }
30
-
31
- writer(): DataWriter {
32
- return new FileDataWriter(this);
33
- }
34
-
35
- async length(): Promise<number> {
36
- const stats = await this.fileHandle.stat();
37
- return stats.size;
38
- }
39
-
40
- async seek(pos: number): Promise<void> {
41
- this._position = pos;
42
- }
43
-
44
- position(): number {
45
- return this._position;
46
- }
47
-
48
- async setLength(len: number): Promise<void> {
49
- await this.fileHandle.truncate(len);
50
- }
51
-
52
- async flush(): Promise<void> {
53
- }
54
-
55
- async sync(): Promise<void> {
56
- await this.fileHandle.sync();
57
- }
58
-
59
- [Symbol.dispose]() {
60
- import("fs").then(fs => {
61
- fs.closeSync(this.fileHandle.fd);
62
- });
63
- }
64
- }
65
-
66
- class FileDataReader implements DataReader {
67
- private core: CoreFile;
68
-
69
- constructor(core: CoreFile) {
70
- this.core = core;
71
- }
72
-
73
- async readFully(b: Uint8Array): Promise<void> {
74
- const position = this.core.position();
75
- await this.core.fileHandle.readv([b], position);
76
- this.core.seek(position + b.length);
77
- }
78
-
79
- async readByte(): Promise<number> {
80
- const bytes = new Uint8Array(1);
81
- await this.readFully(bytes);
82
- return bytes[0];
83
- }
84
-
85
- async readShort(): Promise<number> {
86
- const bytes = new Uint8Array(2);
87
- await this.readFully(bytes);
88
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
89
- return view.getInt16(0, false);
90
- }
91
-
92
- async readInt(): Promise<number> {
93
- const bytes = new Uint8Array(4);
94
- await this.readFully(bytes);
95
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
96
- return view.getInt32(0, false);
97
- }
98
-
99
- async readLong(): Promise<number> {
100
- const bytes = new Uint8Array(8);
101
- await this.readFully(bytes);
102
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
103
- return Number(view.getBigInt64(0, false));
104
- }
105
- }
106
-
107
- class FileDataWriter implements DataWriter {
108
- private core: CoreFile;
109
-
110
- constructor(core: CoreFile) {
111
- this.core = core;
112
- }
113
-
114
- async write(buffer: Uint8Array): Promise<void> {
115
- const position = this.core.position();
116
- await this.core.fileHandle.writev([buffer], position);
117
- this.core.seek(position + buffer.length);
118
- }
119
-
120
- async writeByte(v: number): Promise<void> {
121
- await this.write(new Uint8Array([v & 0xff]));
122
- }
123
-
124
- async writeShort(v: number): Promise<void> {
125
- const buffer = new ArrayBuffer(2);
126
- const view = new DataView(buffer);
127
- view.setInt16(0, v, false);
128
- await this.write(new Uint8Array(buffer));
129
- }
130
-
131
- async writeLong(v: number): Promise<void> {
132
- const buffer = new ArrayBuffer(8);
133
- const view = new DataView(buffer);
134
- view.setBigInt64(0, BigInt(v), false);
135
- await this.write(new Uint8Array(buffer));
136
- }
137
- }