readline-pager 0.2.7 → 0.3.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/README.md +38 -31
- package/dist/main.cjs +378 -137
- package/dist/main.d.cts +2 -4
- package/dist/main.d.mts +2 -4
- package/dist/main.mjs +373 -136
- package/dist/worker.cjs +15 -13
- package/dist/worker.mjs +15 -13
- package/package.json +11 -6
package/dist/main.mjs
CHANGED
|
@@ -1,67 +1,189 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
+
import { closeSync, openSync, readSync, statSync } from "node:fs";
|
|
2
3
|
import { open } from "node:fs/promises";
|
|
3
4
|
import { Worker } from "node:worker_threads";
|
|
4
|
-
|
|
5
5
|
//#region \0rolldown/runtime.js
|
|
6
6
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
7
|
-
|
|
8
7
|
//#endregion
|
|
9
8
|
//#region src/queue.ts
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
let
|
|
9
|
+
function createRingBuffer(capacity) {
|
|
10
|
+
if (!Number.isFinite(capacity) || capacity <= 0) throw new RangeError("capacity must be a positive number");
|
|
11
|
+
let buf = new Array(capacity);
|
|
12
|
+
let head = 0;
|
|
13
|
+
let tail = 0;
|
|
14
|
+
let count = 0;
|
|
15
|
+
let consumerWaiter = null;
|
|
16
|
+
let producerWaiter = null;
|
|
17
|
+
function push(item) {
|
|
18
|
+
if (count === buf.length) {
|
|
19
|
+
const newCap = buf.length * 2;
|
|
20
|
+
const newBuf = new Array(newCap);
|
|
21
|
+
for (let i = 0; i < count; i++) newBuf[i] = buf[(head + i) % buf.length];
|
|
22
|
+
buf = newBuf;
|
|
23
|
+
head = 0;
|
|
24
|
+
tail = count;
|
|
25
|
+
}
|
|
26
|
+
buf[tail] = item;
|
|
27
|
+
tail++;
|
|
28
|
+
if (tail === buf.length) tail = 0;
|
|
29
|
+
count++;
|
|
30
|
+
if (consumerWaiter) {
|
|
31
|
+
const w = consumerWaiter;
|
|
32
|
+
consumerWaiter = null;
|
|
33
|
+
w();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function shiftSync() {
|
|
37
|
+
if (count === 0) return null;
|
|
38
|
+
const v = buf[head];
|
|
39
|
+
buf[head] = void 0;
|
|
40
|
+
head++;
|
|
41
|
+
if (head === buf.length) head = 0;
|
|
42
|
+
count--;
|
|
43
|
+
if (producerWaiter) {
|
|
44
|
+
const w = producerWaiter;
|
|
45
|
+
producerWaiter = null;
|
|
46
|
+
w();
|
|
47
|
+
}
|
|
48
|
+
return v;
|
|
49
|
+
}
|
|
50
|
+
async function shift(done = false) {
|
|
51
|
+
if (count) return shiftSync();
|
|
52
|
+
if (done) return null;
|
|
53
|
+
await new Promise((r) => {
|
|
54
|
+
consumerWaiter = r;
|
|
55
|
+
});
|
|
56
|
+
if (count) return shiftSync();
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function wake() {
|
|
60
|
+
if (consumerWaiter) {
|
|
61
|
+
const w = consumerWaiter;
|
|
62
|
+
consumerWaiter = null;
|
|
63
|
+
w();
|
|
64
|
+
}
|
|
65
|
+
if (producerWaiter) {
|
|
66
|
+
const w = producerWaiter;
|
|
67
|
+
producerWaiter = null;
|
|
68
|
+
w();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function clear() {
|
|
72
|
+
for (let i = 0; i < buf.length; i++) buf[i] = void 0;
|
|
73
|
+
head = 0;
|
|
74
|
+
tail = 0;
|
|
75
|
+
count = 0;
|
|
76
|
+
wake();
|
|
77
|
+
}
|
|
13
78
|
return {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
resolver?.();
|
|
22
|
-
resolver = null;
|
|
79
|
+
push,
|
|
80
|
+
shift,
|
|
81
|
+
shiftSync,
|
|
82
|
+
wake,
|
|
83
|
+
clear,
|
|
84
|
+
get count() {
|
|
85
|
+
return count;
|
|
23
86
|
},
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (done()) return null;
|
|
27
|
-
await new Promise((r) => resolver = r);
|
|
28
|
-
if (queue.length) return queue.shift();
|
|
29
|
-
if (done()) return null;
|
|
30
|
-
return null;
|
|
87
|
+
get capacity() {
|
|
88
|
+
return buf.length;
|
|
31
89
|
}
|
|
32
90
|
};
|
|
33
91
|
}
|
|
34
|
-
|
|
35
92
|
//#endregion
|
|
36
93
|
//#region src/reader/backward.reader.ts
|
|
37
94
|
function createBackwardReader(filepath, options) {
|
|
38
95
|
const { chunkSize, pageSize, delimiter, prefetch } = options;
|
|
39
|
-
const pageQueue =
|
|
96
|
+
const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
|
|
97
|
+
const local = [];
|
|
40
98
|
let fd = null;
|
|
99
|
+
let fdSync = null;
|
|
41
100
|
let pos = 0;
|
|
42
101
|
let buffer = "";
|
|
43
102
|
let done = false;
|
|
44
103
|
let closed = false;
|
|
45
|
-
let
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
pos = (await fd.stat()).size;
|
|
53
|
-
if (pos === 0) done = true;
|
|
104
|
+
let startsWithDelimiter = false;
|
|
105
|
+
fdSync = openSync(filepath, "r");
|
|
106
|
+
pos = statSync(filepath).size;
|
|
107
|
+
if (pos === 0) {
|
|
108
|
+
pageQueue.push([buffer]);
|
|
109
|
+
done = true;
|
|
110
|
+
pageQueue.wake();
|
|
54
111
|
}
|
|
55
|
-
async
|
|
112
|
+
(async () => {
|
|
113
|
+
try {
|
|
114
|
+
fd = await open(filepath, "r");
|
|
115
|
+
pos = (await fd.stat()).size;
|
|
116
|
+
if (pos === 0) {
|
|
117
|
+
if (!done) {
|
|
118
|
+
pageQueue.push([buffer]);
|
|
119
|
+
done = true;
|
|
120
|
+
}
|
|
121
|
+
if (fd) {
|
|
122
|
+
await fd.close();
|
|
123
|
+
fd = null;
|
|
124
|
+
}
|
|
125
|
+
pageQueue.wake();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
while (!done && !closed) {
|
|
129
|
+
while (pageQueue.count < prefetch && pos > 0 && !closed) {
|
|
130
|
+
const readSize = Math.min(chunkSize, pos);
|
|
131
|
+
pos -= readSize;
|
|
132
|
+
const buf = Buffer.allocUnsafe(readSize);
|
|
133
|
+
await fd.read(buf, 0, readSize, pos);
|
|
134
|
+
buffer = buf.toString("utf8") + buffer;
|
|
135
|
+
if (pos === 0 && buffer.startsWith(delimiter)) startsWithDelimiter = true;
|
|
136
|
+
let idx;
|
|
137
|
+
while ((idx = buffer.lastIndexOf(delimiter)) !== -1) {
|
|
138
|
+
const line = buffer.slice(idx + delimiter.length);
|
|
139
|
+
buffer = buffer.slice(0, idx);
|
|
140
|
+
local.push(line);
|
|
141
|
+
while (local.length >= pageSize) {
|
|
142
|
+
const page = local.splice(0, pageSize);
|
|
143
|
+
pageQueue.push(page);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (pos === 0 && !done) {
|
|
148
|
+
if (buffer.length > 0) local.push(buffer);
|
|
149
|
+
else if (startsWithDelimiter) local.push("");
|
|
150
|
+
buffer = "";
|
|
151
|
+
while (local.length > 0 && !closed) {
|
|
152
|
+
const page = local.slice(local.length - Math.min(pageSize, local.length));
|
|
153
|
+
local.length -= page.length;
|
|
154
|
+
pageQueue.push(page);
|
|
155
|
+
}
|
|
156
|
+
done = true;
|
|
157
|
+
if (fd) {
|
|
158
|
+
await fd.close();
|
|
159
|
+
fd = null;
|
|
160
|
+
}
|
|
161
|
+
pageQueue.wake();
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
if (!done && !closed) await new Promise((r) => setImmediate(r));
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
done = true;
|
|
168
|
+
pageQueue.wake();
|
|
169
|
+
try {
|
|
170
|
+
if (fd) {
|
|
171
|
+
await fd.close();
|
|
172
|
+
fd = null;
|
|
173
|
+
}
|
|
174
|
+
} catch {}
|
|
175
|
+
}
|
|
176
|
+
})();
|
|
177
|
+
function fillSync() {
|
|
56
178
|
if (done || closed) return;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
while (pageQueue.queue.length < prefetch && pos > 0) {
|
|
179
|
+
if (fdSync === null) return;
|
|
180
|
+
while (pageQueue.count < prefetch && pos > 0 && !closed) {
|
|
60
181
|
const readSize = Math.min(chunkSize, pos);
|
|
61
182
|
pos -= readSize;
|
|
62
183
|
const buf = Buffer.allocUnsafe(readSize);
|
|
63
|
-
|
|
64
|
-
buffer
|
|
184
|
+
readSync(fdSync, buf, 0, readSize, pos);
|
|
185
|
+
buffer = buf.toString("utf8") + buffer;
|
|
186
|
+
if (pos === 0 && buffer.startsWith(delimiter)) startsWithDelimiter = true;
|
|
65
187
|
let idx;
|
|
66
188
|
while ((idx = buffer.lastIndexOf(delimiter)) !== -1) {
|
|
67
189
|
const line = buffer.slice(idx + delimiter.length);
|
|
@@ -73,52 +195,53 @@ function createBackwardReader(filepath, options) {
|
|
|
73
195
|
}
|
|
74
196
|
}
|
|
75
197
|
}
|
|
76
|
-
if (pos === 0) {
|
|
77
|
-
local.push(buffer);
|
|
198
|
+
if (pos === 0 && !done) {
|
|
199
|
+
if (buffer.length > 0) local.push(buffer);
|
|
200
|
+
else if (startsWithDelimiter) local.push("");
|
|
78
201
|
buffer = "";
|
|
79
202
|
while (local.length > 0) {
|
|
80
|
-
const
|
|
81
|
-
|
|
203
|
+
const page = local.slice(local.length - Math.min(pageSize, local.length));
|
|
204
|
+
local.length -= page.length;
|
|
82
205
|
pageQueue.push(page);
|
|
83
206
|
}
|
|
84
207
|
done = true;
|
|
85
|
-
if (
|
|
86
|
-
|
|
87
|
-
|
|
208
|
+
if (fdSync !== null) {
|
|
209
|
+
closeSync(fdSync);
|
|
210
|
+
fdSync = null;
|
|
88
211
|
}
|
|
212
|
+
pageQueue.wake();
|
|
89
213
|
}
|
|
90
214
|
}
|
|
91
215
|
async function next() {
|
|
92
216
|
if (closed) return null;
|
|
93
|
-
await
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
return page;
|
|
217
|
+
return await pageQueue.shift(done);
|
|
218
|
+
}
|
|
219
|
+
function nextSync() {
|
|
220
|
+
if (closed) return null;
|
|
221
|
+
fillSync();
|
|
222
|
+
return pageQueue.shiftSync();
|
|
100
223
|
}
|
|
101
224
|
async function close() {
|
|
102
225
|
closed = true;
|
|
103
226
|
done = true;
|
|
104
|
-
pageQueue.
|
|
227
|
+
pageQueue.clear();
|
|
105
228
|
if (fd) {
|
|
106
|
-
|
|
229
|
+
try {
|
|
230
|
+
await fd.close();
|
|
231
|
+
} catch {}
|
|
107
232
|
fd = null;
|
|
108
233
|
}
|
|
234
|
+
if (fdSync !== null) {
|
|
235
|
+
try {
|
|
236
|
+
closeSync(fdSync);
|
|
237
|
+
} catch {}
|
|
238
|
+
fdSync = null;
|
|
239
|
+
}
|
|
109
240
|
}
|
|
110
241
|
return {
|
|
111
242
|
next,
|
|
243
|
+
nextSync,
|
|
112
244
|
close,
|
|
113
|
-
get lineCount() {
|
|
114
|
-
return emittedCount;
|
|
115
|
-
},
|
|
116
|
-
get firstLine() {
|
|
117
|
-
return firstLine;
|
|
118
|
-
},
|
|
119
|
-
get lastLine() {
|
|
120
|
-
return lastLine;
|
|
121
|
-
},
|
|
122
245
|
async *[Symbol.asyncIterator]() {
|
|
123
246
|
try {
|
|
124
247
|
while (true) {
|
|
@@ -127,43 +250,123 @@ function createBackwardReader(filepath, options) {
|
|
|
127
250
|
yield p;
|
|
128
251
|
}
|
|
129
252
|
} finally {
|
|
130
|
-
await close()
|
|
253
|
+
await close();
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
*[Symbol.iterator]() {
|
|
257
|
+
try {
|
|
258
|
+
while (true) {
|
|
259
|
+
const p = nextSync();
|
|
260
|
+
if (!p) break;
|
|
261
|
+
yield p;
|
|
262
|
+
}
|
|
263
|
+
} finally {
|
|
264
|
+
closed = true;
|
|
265
|
+
done = true;
|
|
266
|
+
pageQueue.clear();
|
|
267
|
+
try {
|
|
268
|
+
if (fdSync) closeSync(fdSync);
|
|
269
|
+
} catch {}
|
|
270
|
+
fdSync = null;
|
|
271
|
+
try {
|
|
272
|
+
if (fd?.fd) closeSync(fd.fd);
|
|
273
|
+
} catch {}
|
|
274
|
+
fd = null;
|
|
131
275
|
}
|
|
132
276
|
}
|
|
133
277
|
};
|
|
134
278
|
}
|
|
135
|
-
|
|
136
279
|
//#endregion
|
|
137
280
|
//#region src/reader/forward.reader.ts
|
|
138
281
|
function createForwardReader(filepath, options) {
|
|
139
282
|
const { chunkSize, pageSize, delimiter, prefetch } = options;
|
|
140
|
-
const pageQueue =
|
|
283
|
+
const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
|
|
284
|
+
const local = [];
|
|
141
285
|
let fd = null;
|
|
286
|
+
let fdSync = null;
|
|
142
287
|
let pos = 0;
|
|
143
288
|
let size = 0;
|
|
144
289
|
let buffer = "";
|
|
145
290
|
let done = false;
|
|
146
291
|
let closed = false;
|
|
147
|
-
let
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
size = (await fd.stat()).size;
|
|
155
|
-
if (size === 0) done = true;
|
|
292
|
+
let flushed = false;
|
|
293
|
+
fdSync = openSync(filepath, "r");
|
|
294
|
+
size = statSync(filepath).size;
|
|
295
|
+
if (size === 0) {
|
|
296
|
+
pageQueue.push([buffer]);
|
|
297
|
+
done = true;
|
|
298
|
+
pageQueue.wake();
|
|
156
299
|
}
|
|
157
|
-
async
|
|
300
|
+
(async () => {
|
|
301
|
+
try {
|
|
302
|
+
fd = await open(filepath, "r");
|
|
303
|
+
size = (await fd.stat()).size;
|
|
304
|
+
if (size === 0) {
|
|
305
|
+
if (!done) {
|
|
306
|
+
pageQueue.push([buffer]);
|
|
307
|
+
done = true;
|
|
308
|
+
}
|
|
309
|
+
if (fd) {
|
|
310
|
+
await fd.close();
|
|
311
|
+
fd = null;
|
|
312
|
+
}
|
|
313
|
+
pageQueue.wake();
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
while (!done && !closed) {
|
|
317
|
+
while (pageQueue.count < prefetch && pos < size && !closed) {
|
|
318
|
+
const readSize = Math.min(chunkSize, size - pos);
|
|
319
|
+
const buf = Buffer.allocUnsafe(readSize);
|
|
320
|
+
const { bytesRead } = await fd.read(buf, 0, readSize, pos);
|
|
321
|
+
pos += bytesRead;
|
|
322
|
+
buffer = buffer + buf.toString("utf8", 0, bytesRead);
|
|
323
|
+
let idx;
|
|
324
|
+
while ((idx = buffer.indexOf(delimiter)) !== -1) {
|
|
325
|
+
const line = buffer.slice(0, idx);
|
|
326
|
+
buffer = buffer.slice(idx + delimiter.length);
|
|
327
|
+
local.push(line);
|
|
328
|
+
while (local.length >= pageSize) pageQueue.push(local.splice(0, pageSize));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
if (pos >= size && !flushed) {
|
|
332
|
+
flushed = true;
|
|
333
|
+
local.push(buffer.length > 0 ? buffer : "");
|
|
334
|
+
buffer = "";
|
|
335
|
+
while (local.length > 0 && !closed) {
|
|
336
|
+
const page = local.slice(0, pageSize);
|
|
337
|
+
local.length -= page.length;
|
|
338
|
+
pageQueue.push(page);
|
|
339
|
+
}
|
|
340
|
+
done = true;
|
|
341
|
+
if (fd) {
|
|
342
|
+
await fd.close();
|
|
343
|
+
fd = null;
|
|
344
|
+
}
|
|
345
|
+
pageQueue.wake();
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
if (!done && !closed) await new Promise((r) => setImmediate(r));
|
|
349
|
+
}
|
|
350
|
+
} catch {
|
|
351
|
+
done = true;
|
|
352
|
+
pageQueue.wake();
|
|
353
|
+
try {
|
|
354
|
+
if (fd) {
|
|
355
|
+
await fd.close();
|
|
356
|
+
fd = null;
|
|
357
|
+
}
|
|
358
|
+
} catch {}
|
|
359
|
+
}
|
|
360
|
+
})();
|
|
361
|
+
function fillSync() {
|
|
158
362
|
if (done || closed) return;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
while (pageQueue.queue.length < prefetch && pos < size) {
|
|
363
|
+
if (fdSync === null) return;
|
|
364
|
+
while (pageQueue.count < prefetch && pos < size && !closed) {
|
|
162
365
|
const readSize = Math.min(chunkSize, size - pos);
|
|
163
366
|
const buf = Buffer.allocUnsafe(readSize);
|
|
164
|
-
const
|
|
367
|
+
const bytesRead = readSync(fdSync, buf, 0, readSize, pos);
|
|
165
368
|
pos += bytesRead;
|
|
166
|
-
buffer
|
|
369
|
+
buffer = buffer + buf.toString("utf8", 0, bytesRead);
|
|
167
370
|
let idx;
|
|
168
371
|
while ((idx = buffer.indexOf(delimiter)) !== -1) {
|
|
169
372
|
const line = buffer.slice(0, idx);
|
|
@@ -172,49 +375,53 @@ function createForwardReader(filepath, options) {
|
|
|
172
375
|
while (local.length >= pageSize) pageQueue.push(local.splice(0, pageSize));
|
|
173
376
|
}
|
|
174
377
|
}
|
|
175
|
-
if (pos >= size) {
|
|
176
|
-
|
|
177
|
-
|
|
378
|
+
if (pos >= size && !flushed) {
|
|
379
|
+
flushed = true;
|
|
380
|
+
local.push(buffer.length > 0 ? buffer : "");
|
|
178
381
|
buffer = "";
|
|
179
|
-
while (local.length > 0)
|
|
382
|
+
while (local.length > 0) {
|
|
383
|
+
const page = local.slice(0, pageSize);
|
|
384
|
+
local.length -= page.length;
|
|
385
|
+
pageQueue.push(page);
|
|
386
|
+
}
|
|
180
387
|
done = true;
|
|
181
|
-
if (
|
|
182
|
-
|
|
183
|
-
|
|
388
|
+
if (fdSync !== null) {
|
|
389
|
+
closeSync(fdSync);
|
|
390
|
+
fdSync = null;
|
|
184
391
|
}
|
|
392
|
+
pageQueue.wake();
|
|
185
393
|
}
|
|
186
394
|
}
|
|
187
395
|
async function next() {
|
|
188
396
|
if (closed) return null;
|
|
189
|
-
await
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return page;
|
|
397
|
+
return await pageQueue.shift(done);
|
|
398
|
+
}
|
|
399
|
+
function nextSync() {
|
|
400
|
+
if (closed) return null;
|
|
401
|
+
fillSync();
|
|
402
|
+
return pageQueue.shiftSync();
|
|
196
403
|
}
|
|
197
404
|
async function close() {
|
|
198
405
|
closed = true;
|
|
199
406
|
done = true;
|
|
200
|
-
pageQueue.
|
|
407
|
+
pageQueue.clear();
|
|
201
408
|
if (fd) {
|
|
202
|
-
|
|
409
|
+
try {
|
|
410
|
+
await fd.close();
|
|
411
|
+
} catch {}
|
|
203
412
|
fd = null;
|
|
204
413
|
}
|
|
414
|
+
if (fdSync !== null) {
|
|
415
|
+
try {
|
|
416
|
+
closeSync(fdSync);
|
|
417
|
+
} catch {}
|
|
418
|
+
fdSync = null;
|
|
419
|
+
}
|
|
205
420
|
}
|
|
206
421
|
return {
|
|
207
422
|
next,
|
|
423
|
+
nextSync,
|
|
208
424
|
close,
|
|
209
|
-
get lineCount() {
|
|
210
|
-
return emittedCount;
|
|
211
|
-
},
|
|
212
|
-
get firstLine() {
|
|
213
|
-
return firstLine;
|
|
214
|
-
},
|
|
215
|
-
get lastLine() {
|
|
216
|
-
return lastLine;
|
|
217
|
-
},
|
|
218
425
|
async *[Symbol.asyncIterator]() {
|
|
219
426
|
try {
|
|
220
427
|
while (true) {
|
|
@@ -225,27 +432,45 @@ function createForwardReader(filepath, options) {
|
|
|
225
432
|
} finally {
|
|
226
433
|
await close();
|
|
227
434
|
}
|
|
435
|
+
},
|
|
436
|
+
*[Symbol.iterator]() {
|
|
437
|
+
try {
|
|
438
|
+
while (true) {
|
|
439
|
+
const p = nextSync();
|
|
440
|
+
if (!p) break;
|
|
441
|
+
yield p;
|
|
442
|
+
}
|
|
443
|
+
} finally {
|
|
444
|
+
closed = true;
|
|
445
|
+
done = true;
|
|
446
|
+
pageQueue.clear();
|
|
447
|
+
try {
|
|
448
|
+
if (fdSync !== null) closeSync(fdSync);
|
|
449
|
+
} catch {}
|
|
450
|
+
fdSync = null;
|
|
451
|
+
try {
|
|
452
|
+
if (fd?.fd) closeSync(fd.fd);
|
|
453
|
+
} catch {}
|
|
454
|
+
fd = null;
|
|
455
|
+
}
|
|
228
456
|
}
|
|
229
457
|
};
|
|
230
458
|
}
|
|
231
|
-
|
|
232
459
|
//#endregion
|
|
233
460
|
//#region src/reader/worker.reader.ts
|
|
234
461
|
const workerFile = typeof import.meta !== "undefined" ? new URL("./worker.mjs", import.meta.url) : __require.resolve("./worker.cjs");
|
|
235
462
|
function createWorkerReader(filepath, options) {
|
|
236
463
|
const { chunkSize, pageSize, delimiter, prefetch } = options;
|
|
464
|
+
const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
|
|
465
|
+
let done = false;
|
|
466
|
+
let closed = false;
|
|
237
467
|
const worker = new Worker(new URL(workerFile, import.meta.url), { workerData: {
|
|
238
468
|
filepath,
|
|
239
469
|
chunkSize,
|
|
240
470
|
pageSize,
|
|
241
|
-
delimiter
|
|
471
|
+
delimiter,
|
|
472
|
+
prefetch
|
|
242
473
|
} });
|
|
243
|
-
const pageQueue = createPageQueue();
|
|
244
|
-
let done = false;
|
|
245
|
-
let closed = false;
|
|
246
|
-
let emittedCount = 0;
|
|
247
|
-
let firstLine = null;
|
|
248
|
-
let lastLine = null;
|
|
249
474
|
worker.on("message", (msg) => {
|
|
250
475
|
if (msg.type === "page") pageQueue.push(msg.data);
|
|
251
476
|
if (msg.type === "done") {
|
|
@@ -253,32 +478,35 @@ function createWorkerReader(filepath, options) {
|
|
|
253
478
|
pageQueue.wake();
|
|
254
479
|
}
|
|
255
480
|
});
|
|
481
|
+
worker.on("error", () => {
|
|
482
|
+
done = true;
|
|
483
|
+
pageQueue.wake();
|
|
484
|
+
});
|
|
485
|
+
worker.on("exit", () => {
|
|
486
|
+
done = true;
|
|
487
|
+
pageQueue.wake();
|
|
488
|
+
});
|
|
256
489
|
async function next() {
|
|
257
490
|
if (closed) return null;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
return page;
|
|
491
|
+
return await pageQueue.shift(done);
|
|
492
|
+
}
|
|
493
|
+
function nextSync() {
|
|
494
|
+
if (closed) return null;
|
|
495
|
+
return pageQueue.shiftSync();
|
|
264
496
|
}
|
|
265
497
|
async function close() {
|
|
266
498
|
closed = true;
|
|
267
499
|
done = true;
|
|
500
|
+
pageQueue.clear();
|
|
268
501
|
await worker.terminate();
|
|
269
502
|
}
|
|
503
|
+
function tryClose() {
|
|
504
|
+
close().catch(() => {});
|
|
505
|
+
}
|
|
270
506
|
return {
|
|
271
507
|
next,
|
|
508
|
+
nextSync,
|
|
272
509
|
close,
|
|
273
|
-
get lineCount() {
|
|
274
|
-
return emittedCount;
|
|
275
|
-
},
|
|
276
|
-
get firstLine() {
|
|
277
|
-
return firstLine;
|
|
278
|
-
},
|
|
279
|
-
get lastLine() {
|
|
280
|
-
return lastLine;
|
|
281
|
-
},
|
|
282
510
|
async *[Symbol.asyncIterator]() {
|
|
283
511
|
try {
|
|
284
512
|
while (true) {
|
|
@@ -287,19 +515,29 @@ function createWorkerReader(filepath, options) {
|
|
|
287
515
|
yield p;
|
|
288
516
|
}
|
|
289
517
|
} finally {
|
|
290
|
-
|
|
518
|
+
tryClose();
|
|
519
|
+
}
|
|
520
|
+
},
|
|
521
|
+
*[Symbol.iterator]() {
|
|
522
|
+
try {
|
|
523
|
+
while (true) {
|
|
524
|
+
const p = nextSync();
|
|
525
|
+
if (!p) break;
|
|
526
|
+
yield p;
|
|
527
|
+
}
|
|
528
|
+
} finally {
|
|
529
|
+
tryClose();
|
|
291
530
|
}
|
|
292
531
|
}
|
|
293
532
|
};
|
|
294
533
|
}
|
|
295
|
-
|
|
296
534
|
//#endregion
|
|
297
535
|
//#region src/main.ts
|
|
298
536
|
function createPager(filepath, options = {}) {
|
|
299
|
-
const { chunkSize = 64 * 1024, pageSize = 1e3, delimiter = "\n", prefetch =
|
|
537
|
+
const { chunkSize = 64 * 1024, pageSize = 1e3, delimiter = "\n", prefetch = 8, backward = false, useWorker = false } = options;
|
|
300
538
|
if (!filepath) throw new Error("filepath required");
|
|
301
|
-
if (pageSize
|
|
302
|
-
if (prefetch
|
|
539
|
+
if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
|
|
540
|
+
if (prefetch < 1) throw new RangeError("prefetch must be >= 1");
|
|
303
541
|
if (backward && useWorker) throw new Error("backward not supported with useWorker");
|
|
304
542
|
return useWorker ? createWorkerReader(filepath, {
|
|
305
543
|
chunkSize,
|
|
@@ -318,6 +556,5 @@ function createPager(filepath, options = {}) {
|
|
|
318
556
|
delimiter
|
|
319
557
|
});
|
|
320
558
|
}
|
|
321
|
-
|
|
322
559
|
//#endregion
|
|
323
|
-
export { createPager, createPager as default };
|
|
560
|
+
export { createPager, createPager as default };
|