@minnowdb/core 0.7.0 → 0.7.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/dist/block-format/block.js +254 -325
- package/dist/block-format/checksum.js +30 -52
- package/dist/block-format/codecs.js +112 -125
- package/dist/block-format/column.js +187 -205
- package/dist/block-format/index.js +5 -1
- package/dist/block-format/physical.js +371 -387
- package/dist/block-format/types.js +4 -1
- package/dist/block-format/unicode.js +30 -39
- package/dist/date-value.js +67 -78
- package/dist/engine/artifact-cache.js +106 -116
- package/dist/engine/batch.js +36 -44
- package/dist/engine/buffered-writer.js +156 -158
- package/dist/engine/byte-estimates.js +20 -22
- package/dist/engine/cache-limits.js +24 -25
- package/dist/engine/cancellation.js +5 -3
- package/dist/engine/catalog.js +64 -73
- package/dist/engine/client.js +715 -780
- package/dist/engine/database.js +15941 -19313
- package/dist/engine/defaults.js +104 -111
- package/dist/engine/errors.js +113 -128
- package/dist/engine/fts.js +233 -280
- package/dist/engine/group-index.js +287 -321
- package/dist/engine/index.js +23 -12
- package/dist/engine/join-index.js +177 -186
- package/dist/engine/keyed-live.js +168 -173
- package/dist/engine/live-api.js +0 -1
- package/dist/engine/live-equal.js +32 -37
- package/dist/engine/live.js +603 -647
- package/dist/engine/memory.js +109 -111
- package/dist/engine/optimizer.d.ts +19 -1
- package/dist/engine/optimizer.js +2633 -2934
- package/dist/engine/point-read.js +122 -182
- package/dist/engine/query-api.js +12 -3
- package/dist/engine/query-cache.js +61 -69
- package/dist/engine/query.d.ts +6 -0
- package/dist/engine/query.js +8503 -9980
- package/dist/engine/result-wire.js +221 -252
- package/dist/engine/schema-wire.js +97 -112
- package/dist/engine/schema.js +1099 -1254
- package/dist/engine/sort-keys.js +315 -389
- package/dist/engine/sql-domains.js +646 -769
- package/dist/engine/sql-driver.js +0 -1
- package/dist/engine/sql-functions.js +1031 -1125
- package/dist/engine/sql-json.js +199 -218
- package/dist/engine/sql-semantics.js +572 -639
- package/dist/engine/typed-live.js +271 -293
- package/dist/engine/vector.js +4484 -5145
- package/dist/engine/worker-host.d.ts +8 -63
- package/dist/engine/worker-host.js +28 -1130
- package/dist/engine/worker-indexeddb.d.ts +1 -0
- package/dist/engine/worker-indexeddb.js +3 -0
- package/dist/engine/worker-memory.d.ts +1 -0
- package/dist/engine/worker-memory.js +3 -0
- package/dist/engine/worker-opfs.d.ts +1 -0
- package/dist/engine/worker-opfs.js +3 -0
- package/dist/engine/worker-server.d.ts +93 -0
- package/dist/engine/worker-server.js +999 -0
- package/dist/engine/worker-store-indexeddb.d.ts +2 -0
- package/dist/engine/worker-store-indexeddb.js +11 -0
- package/dist/engine/worker-store-memory.d.ts +2 -0
- package/dist/engine/worker-store-memory.js +6 -0
- package/dist/engine/worker-store-opfs.d.ts +2 -0
- package/dist/engine/worker-store-opfs.js +9 -0
- package/dist/engine/worker.js +0 -12
- package/dist/engine/write-block-planner.js +93 -100
- package/dist/plan/index.js +15 -16
- package/dist/plan/model.d.ts +6 -0
- package/dist/plan/model.js +20 -27
- package/dist/storage/index.js +0 -12
- package/dist/storage/indexeddb.js +11776 -13171
- package/dist/storage/memory.js +1198 -1241
- package/dist/storage/opfs/files.js +238 -295
- package/dist/storage/opfs/index.js +9 -3
- package/dist/storage/opfs/leader.js +4386 -4856
- package/dist/storage/opfs/rpc.js +223 -259
- package/dist/storage/opfs/snapshot-ledger.js +219 -260
- package/dist/storage/opfs/store.js +884 -1003
- package/dist/storage/persistence.js +59 -74
- package/dist/storage/snapshot-stream.js +788 -826
- package/dist/storage/snapshot.js +36 -37
- package/dist/storage/toolkit/extents.js +444 -520
- package/dist/storage/toolkit/index.js +28 -29
- package/dist/storage/toolkit/record-core.js +5792 -6377
- package/dist/storage/toolkit/sync-file.js +34 -42
- package/dist/storage/toolkit/wal.js +92 -127
- package/dist/storage/toolkit/wire.js +232 -256
- package/dist/storage/types.js +2948 -3250
- package/dist/testing/block-store-conformance.js +1556 -1629
- package/dist/testing/index.js +310 -291
- package/dist/testing/opfs-shim.js +280 -312
- package/dist/testing/simulator.js +496 -534
- package/dist/testing/sqllogictest.js +425 -365
- package/dist/transactions/index.js +1464 -1712
- package/dist/worker-protocol/index.js +70 -69
- package/package.json +20 -2
|
@@ -1,289 +1,265 @@
|
|
|
1
1
|
import { crc32 } from "../../block-format/index.js";
|
|
2
2
|
import { MAX_ROW_ID_EXCLUSIVE_END, StorageFormatVersionError } from "../types.js";
|
|
3
|
-
|
|
4
|
-
* Payload encodings for a log-structured adapter's control data: checkpoint files, immutable
|
|
5
|
-
* artifact chunks, and the JSON codec write-ahead-log frames carry. Every envelope is
|
|
6
|
-
* checksummed so a torn write — the only artifact a crash can leave — is detectable and
|
|
7
|
-
* indistinguishable from "not written".
|
|
8
|
-
*
|
|
9
|
-
* Record payloads are JSON with one extension: bigints (segment row ids, counters, full-text
|
|
10
|
-
* posting row ids, compaction rewrite plans) encode as `{"$n":"<decimal>"}`. No record shape
|
|
11
|
-
* can produce that object naturally — keys are ids and column names, values are JSON scalars —
|
|
12
|
-
* and the reviver only converts an object whose sole key is `$n`.
|
|
13
|
-
*/
|
|
14
|
-
export const LOG_FORMAT_VERSION = 5;
|
|
3
|
+
const LOG_FORMAT_VERSION = 5;
|
|
15
4
|
const textEncoder = new TextEncoder();
|
|
16
5
|
const textDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
catch {
|
|
25
|
-
json = JSON.stringify(value, (_key, entry) => {
|
|
26
|
-
if (typeof entry !== "bigint")
|
|
27
|
-
return entry;
|
|
28
|
-
if (entry < 0n || entry > MAX_ROW_ID_EXCLUSIVE_END) {
|
|
29
|
-
throw new RangeError("Record bigint exceeds the unsigned 64-bit persisted range");
|
|
30
|
-
}
|
|
31
|
-
return { $n: entry.toString() };
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
if (typeof json !== "string")
|
|
35
|
-
throw new TypeError("Record value is not JSON-serializable");
|
|
36
|
-
return textEncoder.encode(json);
|
|
37
|
-
}
|
|
38
|
-
export function decodeRecordJson(bytes) {
|
|
39
|
-
const json = textDecoder.decode(bytes);
|
|
40
|
-
return JSON.parse(json, (_key, entry) => {
|
|
41
|
-
if (typeof entry === "object" &&
|
|
42
|
-
entry !== null &&
|
|
43
|
-
!Array.isArray(entry) &&
|
|
44
|
-
Object.keys(entry).length === 1 &&
|
|
45
|
-
typeof entry.$n === "string") {
|
|
46
|
-
const decimal = entry.$n;
|
|
47
|
-
if (!/^(?:0|[1-9][0-9]{0,19})$/.test(decimal)) {
|
|
48
|
-
throw new TypeError("Record bigint is not a canonical bounded unsigned decimal");
|
|
49
|
-
}
|
|
50
|
-
const value = BigInt(decimal);
|
|
51
|
-
if (value > MAX_ROW_ID_EXCLUSIVE_END) {
|
|
52
|
-
throw new RangeError("Record bigint exceeds the unsigned 64-bit persisted range");
|
|
53
|
-
}
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
6
|
+
function encodeRecordJson(value) {
|
|
7
|
+
let json;
|
|
8
|
+
try {
|
|
9
|
+
json = JSON.stringify(value);
|
|
10
|
+
} catch {
|
|
11
|
+
json = JSON.stringify(value, (_key, entry) => {
|
|
12
|
+
if (typeof entry !== "bigint")
|
|
56
13
|
return entry;
|
|
14
|
+
if (entry < 0n || entry > MAX_ROW_ID_EXCLUSIVE_END) {
|
|
15
|
+
throw new RangeError("Record bigint exceeds the unsigned 64-bit persisted range");
|
|
16
|
+
}
|
|
17
|
+
return { $n: entry.toString() };
|
|
57
18
|
});
|
|
19
|
+
}
|
|
20
|
+
if (typeof json !== "string")
|
|
21
|
+
throw new TypeError("Record value is not JSON-serializable");
|
|
22
|
+
return textEncoder.encode(json);
|
|
23
|
+
}
|
|
24
|
+
function decodeRecordJson(bytes) {
|
|
25
|
+
const json = textDecoder.decode(bytes);
|
|
26
|
+
return JSON.parse(json, (_key, entry) => {
|
|
27
|
+
if (typeof entry === "object" && entry !== null && !Array.isArray(entry) && Object.keys(entry).length === 1 && typeof entry.$n === "string") {
|
|
28
|
+
const decimal = entry.$n;
|
|
29
|
+
if (!/^(?:0|[1-9][0-9]{0,19})$/.test(decimal)) {
|
|
30
|
+
throw new TypeError("Record bigint is not a canonical bounded unsigned decimal");
|
|
31
|
+
}
|
|
32
|
+
const value = BigInt(decimal);
|
|
33
|
+
if (value > MAX_ROW_ID_EXCLUSIVE_END) {
|
|
34
|
+
throw new RangeError("Record bigint exceeds the unsigned 64-bit persisted range");
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
return entry;
|
|
39
|
+
});
|
|
58
40
|
}
|
|
59
41
|
const ENVELOPE_HEADER_BYTES = 8 + 4 + 4 + 4;
|
|
60
42
|
function encodeEnvelope(magic, payload) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
43
|
+
const bytes = new Uint8Array(ENVELOPE_HEADER_BYTES + payload.byteLength);
|
|
44
|
+
const view = new DataView(bytes.buffer);
|
|
45
|
+
textEncoder.encodeInto(magic, bytes);
|
|
46
|
+
view.setUint32(8, LOG_FORMAT_VERSION, true);
|
|
47
|
+
view.setUint32(12, payload.byteLength, true);
|
|
48
|
+
view.setUint32(16, crc32(payload), true);
|
|
49
|
+
bytes.set(payload, ENVELOPE_HEADER_BYTES);
|
|
50
|
+
return bytes;
|
|
69
51
|
}
|
|
70
|
-
/**
|
|
71
|
-
* `undefined` means the bytes are torn or foreign — treat as "not written". A recognized magic
|
|
72
|
-
* with an unknown format version throws instead: reading a different layout as the current one
|
|
73
|
-
* would silently corrupt or roll back the database.
|
|
74
|
-
*/
|
|
75
52
|
function decodeEnvelope(magic, bytes) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
53
|
+
if (bytes.byteLength < ENVELOPE_HEADER_BYTES)
|
|
54
|
+
return void 0;
|
|
55
|
+
for (let index = 0; index < 8; index += 1) {
|
|
56
|
+
if (bytes[index] !== magic.charCodeAt(index))
|
|
57
|
+
return void 0;
|
|
58
|
+
}
|
|
59
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
60
|
+
const version = view.getUint32(8, true);
|
|
61
|
+
if (version !== LOG_FORMAT_VERSION) {
|
|
62
|
+
throw new StorageFormatVersionError("opfs", `envelope/${magic}`, version, LOG_FORMAT_VERSION, version < LOG_FORMAT_VERSION ? "older" : "newer");
|
|
63
|
+
}
|
|
64
|
+
const payloadLength = view.getUint32(12, true);
|
|
65
|
+
if (bytes.byteLength !== ENVELOPE_HEADER_BYTES + payloadLength)
|
|
66
|
+
return void 0;
|
|
67
|
+
const payload = bytes.subarray(ENVELOPE_HEADER_BYTES, ENVELOPE_HEADER_BYTES + payloadLength);
|
|
68
|
+
if (crc32(payload) !== view.getUint32(16, true))
|
|
69
|
+
return void 0;
|
|
70
|
+
return payload;
|
|
94
71
|
}
|
|
95
72
|
const CHUNK_MAGIC = "MNWCHNK1";
|
|
96
73
|
const POSTING_CHUNK_MAGIC = "MNWPOST1";
|
|
97
74
|
const SYNC_CHECKPOINT_MAGIC = "MNWCKPS1";
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return encodeEnvelope(CHUNK_MAGIC, encodeRecordJson(value));
|
|
75
|
+
function encodeChunk(value) {
|
|
76
|
+
return encodeEnvelope(CHUNK_MAGIC, encodeRecordJson(value));
|
|
101
77
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
78
|
+
function decodeChunk(bytes) {
|
|
79
|
+
const payload = decodeEnvelope(CHUNK_MAGIC, bytes);
|
|
80
|
+
return payload === void 0 ? void 0 : decodeRecordJson(payload);
|
|
105
81
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const writer = new BinaryWriter();
|
|
113
|
-
writer.varuint(BigInt(entries.length));
|
|
114
|
-
for (const entry of entries) {
|
|
115
|
-
if (entry.rowIds.length !== entry.tf.length) {
|
|
116
|
-
throw new TypeError("Posting row IDs and term frequencies must have the same length");
|
|
117
|
-
}
|
|
118
|
-
const term = textEncoder.encode(entry.term);
|
|
119
|
-
if (term.byteLength === 0)
|
|
120
|
-
throw new TypeError("Posting terms cannot be empty");
|
|
121
|
-
if (entry.rowIds.length === 0)
|
|
122
|
-
throw new TypeError("Postings cannot have no row IDs");
|
|
123
|
-
writer.varuint(BigInt(term.byteLength));
|
|
124
|
-
writer.bytes(term);
|
|
125
|
-
writer.varuint(BigInt(entry.rowIds.length));
|
|
126
|
-
let previous = 0n;
|
|
127
|
-
for (const rowId of entry.rowIds) {
|
|
128
|
-
if (rowId <= previous || rowId > MAX_ROW_ID_EXCLUSIVE_END - 1n) {
|
|
129
|
-
throw new TypeError("Posting row IDs must be positive, uint64, and strictly sorted");
|
|
130
|
-
}
|
|
131
|
-
writer.varuint(rowId - previous);
|
|
132
|
-
previous = rowId;
|
|
133
|
-
}
|
|
134
|
-
for (const frequency of entry.tf) {
|
|
135
|
-
if (!Number.isSafeInteger(frequency) || frequency <= 0) {
|
|
136
|
-
throw new TypeError("Posting term frequencies must be positive whole numbers");
|
|
137
|
-
}
|
|
138
|
-
writer.varuint(BigInt(frequency));
|
|
139
|
-
}
|
|
82
|
+
function encodePostingChunk(entries) {
|
|
83
|
+
const writer = new BinaryWriter();
|
|
84
|
+
writer.varuint(BigInt(entries.length));
|
|
85
|
+
for (const entry of entries) {
|
|
86
|
+
if (entry.rowIds.length !== entry.tf.length) {
|
|
87
|
+
throw new TypeError("Posting row IDs and term frequencies must have the same length");
|
|
140
88
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
89
|
+
const term = textEncoder.encode(entry.term);
|
|
90
|
+
if (term.byteLength === 0)
|
|
91
|
+
throw new TypeError("Posting terms cannot be empty");
|
|
92
|
+
if (entry.rowIds.length === 0)
|
|
93
|
+
throw new TypeError("Postings cannot have no row IDs");
|
|
94
|
+
writer.varuint(BigInt(term.byteLength));
|
|
95
|
+
writer.bytes(term);
|
|
96
|
+
writer.varuint(BigInt(entry.rowIds.length));
|
|
97
|
+
let previous = 0n;
|
|
98
|
+
for (const rowId of entry.rowIds) {
|
|
99
|
+
if (rowId <= previous || rowId > MAX_ROW_ID_EXCLUSIVE_END - 1n) {
|
|
100
|
+
throw new TypeError("Posting row IDs must be positive, uint64, and strictly sorted");
|
|
101
|
+
}
|
|
102
|
+
writer.varuint(rowId - previous);
|
|
103
|
+
previous = rowId;
|
|
152
104
|
}
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
const term = textDecoder.decode(reader.bytes(termLength));
|
|
160
|
-
const rowCount = reader.safeInteger("posting row count");
|
|
161
|
-
if (rowCount < 1 || rowCount > Math.floor(reader.remaining / 2)) {
|
|
162
|
-
throw new Error("Posting row count exceeds the remaining payload");
|
|
163
|
-
}
|
|
164
|
-
const rowIds = new Array(rowCount);
|
|
165
|
-
let previous = 0n;
|
|
166
|
-
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
|
167
|
-
previous += reader.varuint();
|
|
168
|
-
if (previous < 1n || previous > MAX_ROW_ID_EXCLUSIVE_END - 1n) {
|
|
169
|
-
throw new RangeError("Posting row ID exceeds the uint64 range");
|
|
170
|
-
}
|
|
171
|
-
if (rowIndex > 0 && previous <= (rowIds[rowIndex - 1] ?? 0n)) {
|
|
172
|
-
throw new TypeError("Posting row IDs are not strictly sorted");
|
|
173
|
-
}
|
|
174
|
-
rowIds[rowIndex] = previous;
|
|
175
|
-
}
|
|
176
|
-
const tf = new Array(rowCount);
|
|
177
|
-
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
|
178
|
-
const frequency = reader.safeInteger("posting term frequency");
|
|
179
|
-
if (frequency <= 0)
|
|
180
|
-
throw new TypeError("Posting term frequency must be positive");
|
|
181
|
-
tf[rowIndex] = frequency;
|
|
182
|
-
}
|
|
183
|
-
entries[entryIndex] = { term, rowIds, tf };
|
|
105
|
+
for (const frequency of entry.tf) {
|
|
106
|
+
if (!Number.isSafeInteger(frequency) || frequency <= 0) {
|
|
107
|
+
throw new TypeError("Posting term frequencies must be positive whole numbers");
|
|
108
|
+
}
|
|
109
|
+
writer.varuint(BigInt(frequency));
|
|
184
110
|
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return entries;
|
|
111
|
+
}
|
|
112
|
+
return encodeEnvelope(POSTING_CHUNK_MAGIC, writer.finish());
|
|
188
113
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
114
|
+
function decodePostingChunk(bytes) {
|
|
115
|
+
const payload = decodeEnvelope(POSTING_CHUNK_MAGIC, bytes);
|
|
116
|
+
if (payload === void 0)
|
|
117
|
+
return void 0;
|
|
118
|
+
const reader = new BinaryReader(payload);
|
|
119
|
+
const count = reader.safeInteger("posting count");
|
|
120
|
+
if (count > Math.floor(reader.remaining / 5)) {
|
|
121
|
+
throw new Error("Posting count exceeds the remaining payload");
|
|
122
|
+
}
|
|
123
|
+
const entries = new Array(count);
|
|
124
|
+
for (let entryIndex = 0; entryIndex < count; entryIndex += 1) {
|
|
125
|
+
const termLength = reader.safeInteger("posting term length");
|
|
126
|
+
if (termLength < 1 || termLength > reader.remaining) {
|
|
127
|
+
throw new Error("Posting term length exceeds the remaining payload");
|
|
196
128
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
do {
|
|
202
|
-
this.#reserve(1);
|
|
203
|
-
const byte = Number(value & 0x7fn);
|
|
204
|
-
value >>= 7n;
|
|
205
|
-
this.#buffer[this.#length] = value === 0n ? byte : byte | 0x80;
|
|
206
|
-
this.#length += 1;
|
|
207
|
-
} while (value !== 0n);
|
|
129
|
+
const term = textDecoder.decode(reader.bytes(termLength));
|
|
130
|
+
const rowCount = reader.safeInteger("posting row count");
|
|
131
|
+
if (rowCount < 1 || rowCount > Math.floor(reader.remaining / 2)) {
|
|
132
|
+
throw new Error("Posting row count exceeds the remaining payload");
|
|
208
133
|
}
|
|
209
|
-
|
|
210
|
-
|
|
134
|
+
const rowIds = new Array(rowCount);
|
|
135
|
+
let previous = 0n;
|
|
136
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
|
137
|
+
previous += reader.varuint();
|
|
138
|
+
if (previous < 1n || previous > MAX_ROW_ID_EXCLUSIVE_END - 1n) {
|
|
139
|
+
throw new RangeError("Posting row ID exceeds the uint64 range");
|
|
140
|
+
}
|
|
141
|
+
if (rowIndex > 0 && previous <= (rowIds[rowIndex - 1] ?? 0n)) {
|
|
142
|
+
throw new TypeError("Posting row IDs are not strictly sorted");
|
|
143
|
+
}
|
|
144
|
+
rowIds[rowIndex] = previous;
|
|
211
145
|
}
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
if (needed <= this.#buffer.byteLength)
|
|
219
|
-
return;
|
|
220
|
-
let capacity = this.#buffer.byteLength;
|
|
221
|
-
while (capacity < needed) {
|
|
222
|
-
const doubled = capacity * 2;
|
|
223
|
-
capacity = Number.isSafeInteger(doubled) ? doubled : needed;
|
|
224
|
-
}
|
|
225
|
-
const grown = new Uint8Array(capacity);
|
|
226
|
-
grown.set(this.#buffer);
|
|
227
|
-
this.#buffer = grown;
|
|
146
|
+
const tf = new Array(rowCount);
|
|
147
|
+
for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
|
|
148
|
+
const frequency = reader.safeInteger("posting term frequency");
|
|
149
|
+
if (frequency <= 0)
|
|
150
|
+
throw new TypeError("Posting term frequency must be positive");
|
|
151
|
+
tf[rowIndex] = frequency;
|
|
228
152
|
}
|
|
153
|
+
entries[entryIndex] = { term, rowIds, tf };
|
|
154
|
+
}
|
|
155
|
+
if (!reader.done)
|
|
156
|
+
throw new Error("Posting chunk has trailing bytes");
|
|
157
|
+
return entries;
|
|
229
158
|
}
|
|
230
|
-
class
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
159
|
+
class BinaryWriter {
|
|
160
|
+
#buffer = new Uint8Array(1024);
|
|
161
|
+
#length = 0;
|
|
162
|
+
bytes(bytes) {
|
|
163
|
+
this.#reserve(bytes.byteLength);
|
|
164
|
+
this.#buffer.set(bytes, this.#length);
|
|
165
|
+
this.#length += bytes.byteLength;
|
|
166
|
+
}
|
|
167
|
+
varuint(value) {
|
|
168
|
+
if (value < 0n || value >= MAX_ROW_ID_EXCLUSIVE_END) {
|
|
169
|
+
throw new RangeError("A varuint must fit canonical uint64");
|
|
241
170
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
171
|
+
do {
|
|
172
|
+
this.#reserve(1);
|
|
173
|
+
const byte = Number(value & 0x7fn);
|
|
174
|
+
value >>= 7n;
|
|
175
|
+
this.#buffer[this.#length] = value === 0n ? byte : byte | 128;
|
|
176
|
+
this.#length += 1;
|
|
177
|
+
} while (value !== 0n);
|
|
178
|
+
}
|
|
179
|
+
finish() {
|
|
180
|
+
return this.#buffer.slice(0, this.#length);
|
|
181
|
+
}
|
|
182
|
+
#reserve(extra) {
|
|
183
|
+
if (!Number.isSafeInteger(extra) || extra < 0)
|
|
184
|
+
throw new RangeError("Invalid binary growth");
|
|
185
|
+
const needed = this.#length + extra;
|
|
186
|
+
if (!Number.isSafeInteger(needed))
|
|
187
|
+
throw new RangeError("Binary payload is too large");
|
|
188
|
+
if (needed <= this.#buffer.byteLength)
|
|
189
|
+
return;
|
|
190
|
+
let capacity = this.#buffer.byteLength;
|
|
191
|
+
while (capacity < needed) {
|
|
192
|
+
const doubled = capacity * 2;
|
|
193
|
+
capacity = Number.isSafeInteger(doubled) ? doubled : needed;
|
|
248
194
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
195
|
+
const grown = new Uint8Array(capacity);
|
|
196
|
+
grown.set(this.#buffer);
|
|
197
|
+
this.#buffer = grown;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
class BinaryReader {
|
|
201
|
+
source;
|
|
202
|
+
#offset = 0;
|
|
203
|
+
constructor(source) {
|
|
204
|
+
this.source = source;
|
|
205
|
+
}
|
|
206
|
+
get done() {
|
|
207
|
+
return this.#offset === this.source.byteLength;
|
|
208
|
+
}
|
|
209
|
+
get remaining() {
|
|
210
|
+
return this.source.byteLength - this.#offset;
|
|
211
|
+
}
|
|
212
|
+
bytes(length) {
|
|
213
|
+
if (!Number.isSafeInteger(length) || length < 0 || length > this.remaining)
|
|
214
|
+
throw new Error("Posting chunk is truncated");
|
|
215
|
+
const bytes = this.source.subarray(this.#offset, this.#offset + length);
|
|
216
|
+
this.#offset += length;
|
|
217
|
+
return bytes;
|
|
218
|
+
}
|
|
219
|
+
varuint() {
|
|
220
|
+
let value = 0n;
|
|
221
|
+
let shift = 0n;
|
|
222
|
+
for (let byteIndex = 0; byteIndex < 10; byteIndex += 1) {
|
|
223
|
+
const byte = this.source[this.#offset];
|
|
224
|
+
if (byte === void 0)
|
|
225
|
+
throw new Error("Posting chunk is truncated");
|
|
226
|
+
this.#offset += 1;
|
|
227
|
+
if (byteIndex === 9 && (byte & 254) !== 0) {
|
|
228
|
+
throw new Error("Posting chunk varuint exceeds uint64");
|
|
229
|
+
}
|
|
230
|
+
value |= BigInt(byte & 127) << shift;
|
|
231
|
+
if ((byte & 128) === 0) {
|
|
232
|
+
if (byteIndex > 0 && (byte & 127) === 0) {
|
|
233
|
+
throw new Error("Posting chunk varuint is overlong");
|
|
268
234
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const value = this.varuint();
|
|
273
|
-
if (value > BigInt(Number.MAX_SAFE_INTEGER))
|
|
274
|
-
throw new RangeError(`${label} is too large`);
|
|
275
|
-
return Number(value);
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
shift += 7n;
|
|
276
238
|
}
|
|
239
|
+
throw new Error("Posting chunk varuint is too wide");
|
|
240
|
+
}
|
|
241
|
+
safeInteger(label) {
|
|
242
|
+
const value = this.varuint();
|
|
243
|
+
if (value > BigInt(Number.MAX_SAFE_INTEGER))
|
|
244
|
+
throw new RangeError(`${label} is too large`);
|
|
245
|
+
return Number(value);
|
|
246
|
+
}
|
|
277
247
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
* await, and the checkpoint must land in the same synchronous run that resets the WAL, or
|
|
281
|
-
* frames appended in between would be lost by the reset).
|
|
282
|
-
*/
|
|
283
|
-
export function encodeSyncCheckpoint(state) {
|
|
284
|
-
return encodeEnvelope(SYNC_CHECKPOINT_MAGIC, encodeRecordJson(state));
|
|
248
|
+
function encodeSyncCheckpoint(state) {
|
|
249
|
+
return encodeEnvelope(SYNC_CHECKPOINT_MAGIC, encodeRecordJson(state));
|
|
285
250
|
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
251
|
+
function decodeSyncCheckpoint(bytes) {
|
|
252
|
+
const payload = decodeEnvelope(SYNC_CHECKPOINT_MAGIC, bytes);
|
|
253
|
+
return payload === void 0 ? void 0 : decodeRecordJson(payload);
|
|
289
254
|
}
|
|
255
|
+
export {
|
|
256
|
+
LOG_FORMAT_VERSION,
|
|
257
|
+
decodeChunk,
|
|
258
|
+
decodePostingChunk,
|
|
259
|
+
decodeRecordJson,
|
|
260
|
+
decodeSyncCheckpoint,
|
|
261
|
+
encodeChunk,
|
|
262
|
+
encodePostingChunk,
|
|
263
|
+
encodeRecordJson,
|
|
264
|
+
encodeSyncCheckpoint
|
|
265
|
+
};
|