xitdb 0.1.0 → 0.2.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.
- package/dist/core-buffered-file.d.ts +41 -0
- package/dist/core-file.d.ts +18 -0
- package/dist/core-memory.d.ts +36 -0
- package/dist/core.d.ts +23 -0
- package/dist/database.d.ts +244 -0
- package/dist/exceptions.d.ts +51 -0
- package/dist/hasher.d.ts +9 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +429 -266
- package/dist/read-array-list.d.ts +13 -0
- package/dist/read-counted-hash-map.d.ts +7 -0
- package/dist/read-counted-hash-set.d.ts +7 -0
- package/dist/read-cursor.d.ts +57 -0
- package/dist/read-hash-map.d.ts +27 -0
- package/dist/read-hash-set.d.ts +18 -0
- package/dist/read-linked-array-list.d.ts +13 -0
- package/dist/slot-pointer.d.ts +7 -0
- package/dist/slot.d.ts +15 -0
- package/{src/slotted.ts → dist/slotted.d.ts} +1 -2
- package/dist/tag.d.ts +17 -0
- package/dist/write-array-list.d.ts +16 -0
- package/dist/write-counted-hash-map.d.ts +7 -0
- package/dist/write-counted-hash-set.d.ts +7 -0
- package/dist/write-cursor.d.ts +36 -0
- package/dist/write-hash-map.d.ts +25 -0
- package/dist/write-hash-set.d.ts +19 -0
- package/dist/write-linked-array-list.d.ts +19 -0
- package/dist/writeable-data.d.ts +20 -0
- package/package.json +12 -1
- package/.claude/settings.local.json +0 -9
- package/bun.lock +0 -24
- package/bunfig.toml +0 -1
- package/example/README.md +0 -46
- package/example/dump.ts +0 -201
- package/src/core-buffered-file.ts +0 -226
- package/src/core-file.ts +0 -137
- package/src/core-memory.ts +0 -179
- package/src/core.ts +0 -25
- package/src/database.ts +0 -2232
- package/src/exceptions.ts +0 -31
- package/src/hasher.ts +0 -52
- package/src/index.ts +0 -110
- package/src/read-array-list.ts +0 -45
- package/src/read-counted-hash-map.ts +0 -28
- package/src/read-counted-hash-set.ts +0 -28
- package/src/read-cursor.ts +0 -546
- package/src/read-hash-map.ts +0 -117
- package/src/read-hash-set.ts +0 -70
- package/src/read-linked-array-list.ts +0 -45
- package/src/slot-pointer.ts +0 -15
- package/src/slot.ts +0 -51
- package/src/tag.ts +0 -23
- package/src/write-array-list.ts +0 -65
- package/src/write-counted-hash-map.ts +0 -31
- package/src/write-counted-hash-set.ts +0 -31
- package/src/write-cursor.ts +0 -166
- package/src/write-hash-map.ts +0 -129
- package/src/write-hash-set.ts +0 -86
- package/src/write-linked-array-list.ts +0 -80
- package/src/writeable-data.ts +0 -67
- package/tests/database.test.ts +0 -2519
- package/tests/fixtures/test.db +0 -0
- 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
|
-
}
|