readline-pager 0.2.6 → 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.
- package/README.md +65 -40
- package/dist/main.cjs +407 -148
- package/dist/main.d.cts +3 -5
- package/dist/main.d.mts +3 -5
- package/dist/main.mjs +402 -147
- package/dist/worker.cjs +15 -13
- package/dist/worker.mjs +15 -13
- package/package.json +15 -7
- package/prebuilds/linux-x64/readline-pager.node +0 -0
package/dist/main.d.mts
CHANGED
|
@@ -9,12 +9,10 @@ interface PagerOptions extends Partial<ReaderOptions> {
|
|
|
9
9
|
backward?: boolean;
|
|
10
10
|
useWorker?: boolean;
|
|
11
11
|
}
|
|
12
|
-
interface Pager extends AsyncIterable<string[]> {
|
|
12
|
+
interface Pager extends AsyncIterable<string[]>, Iterable<string[]> {
|
|
13
13
|
next(): Promise<string[] | null>;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
readonly firstLine: string | null;
|
|
17
|
-
readonly lastLine: string | null;
|
|
14
|
+
nextSync(): string[] | null;
|
|
15
|
+
close(): Promise<void>;
|
|
18
16
|
}
|
|
19
17
|
//#endregion
|
|
20
18
|
//#region src/main.d.ts
|
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,90 +195,178 @@ 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.
|
|
105
|
-
if (fd)
|
|
227
|
+
pageQueue.clear();
|
|
228
|
+
if (fd) {
|
|
229
|
+
try {
|
|
230
|
+
await fd.close();
|
|
231
|
+
} catch {}
|
|
232
|
+
fd = null;
|
|
233
|
+
}
|
|
234
|
+
if (fdSync !== null) {
|
|
235
|
+
try {
|
|
236
|
+
closeSync(fdSync);
|
|
237
|
+
} catch {}
|
|
238
|
+
fdSync = null;
|
|
239
|
+
}
|
|
106
240
|
}
|
|
107
241
|
return {
|
|
108
242
|
next,
|
|
243
|
+
nextSync,
|
|
109
244
|
close,
|
|
110
|
-
get lineCount() {
|
|
111
|
-
return emittedCount;
|
|
112
|
-
},
|
|
113
|
-
get firstLine() {
|
|
114
|
-
return firstLine;
|
|
115
|
-
},
|
|
116
|
-
get lastLine() {
|
|
117
|
-
return lastLine;
|
|
118
|
-
},
|
|
119
245
|
async *[Symbol.asyncIterator]() {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
246
|
+
try {
|
|
247
|
+
while (true) {
|
|
248
|
+
const p = await next();
|
|
249
|
+
if (!p) break;
|
|
250
|
+
yield p;
|
|
251
|
+
}
|
|
252
|
+
} finally {
|
|
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;
|
|
124
275
|
}
|
|
125
276
|
}
|
|
126
277
|
};
|
|
127
278
|
}
|
|
128
|
-
|
|
129
279
|
//#endregion
|
|
130
280
|
//#region src/reader/forward.reader.ts
|
|
131
281
|
function createForwardReader(filepath, options) {
|
|
132
282
|
const { chunkSize, pageSize, delimiter, prefetch } = options;
|
|
133
|
-
const pageQueue =
|
|
283
|
+
const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
|
|
284
|
+
const local = [];
|
|
134
285
|
let fd = null;
|
|
286
|
+
let fdSync = null;
|
|
135
287
|
let pos = 0;
|
|
136
288
|
let size = 0;
|
|
137
289
|
let buffer = "";
|
|
138
290
|
let done = false;
|
|
139
291
|
let closed = false;
|
|
140
|
-
let
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
size = (await fd.stat()).size;
|
|
148
|
-
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();
|
|
149
299
|
}
|
|
150
|
-
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() {
|
|
151
362
|
if (done || closed) return;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
while (pageQueue.queue.length < prefetch && pos < size) {
|
|
363
|
+
if (fdSync === null) return;
|
|
364
|
+
while (pageQueue.count < prefetch && pos < size && !closed) {
|
|
155
365
|
const readSize = Math.min(chunkSize, size - pos);
|
|
156
366
|
const buf = Buffer.allocUnsafe(readSize);
|
|
157
|
-
const
|
|
367
|
+
const bytesRead = readSync(fdSync, buf, 0, readSize, pos);
|
|
158
368
|
pos += bytesRead;
|
|
159
|
-
buffer
|
|
369
|
+
buffer = buffer + buf.toString("utf8", 0, bytesRead);
|
|
160
370
|
let idx;
|
|
161
371
|
while ((idx = buffer.indexOf(delimiter)) !== -1) {
|
|
162
372
|
const line = buffer.slice(0, idx);
|
|
@@ -165,73 +375,102 @@ function createForwardReader(filepath, options) {
|
|
|
165
375
|
while (local.length >= pageSize) pageQueue.push(local.splice(0, pageSize));
|
|
166
376
|
}
|
|
167
377
|
}
|
|
168
|
-
if (pos >= size) {
|
|
169
|
-
|
|
170
|
-
|
|
378
|
+
if (pos >= size && !flushed) {
|
|
379
|
+
flushed = true;
|
|
380
|
+
local.push(buffer.length > 0 ? buffer : "");
|
|
171
381
|
buffer = "";
|
|
172
|
-
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
|
+
}
|
|
173
387
|
done = true;
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
388
|
+
if (fdSync !== null) {
|
|
389
|
+
closeSync(fdSync);
|
|
390
|
+
fdSync = null;
|
|
177
391
|
}
|
|
392
|
+
pageQueue.wake();
|
|
178
393
|
}
|
|
179
394
|
}
|
|
180
395
|
async function next() {
|
|
181
396
|
if (closed) return null;
|
|
182
|
-
await
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return page;
|
|
397
|
+
return await pageQueue.shift(done);
|
|
398
|
+
}
|
|
399
|
+
function nextSync() {
|
|
400
|
+
if (closed) return null;
|
|
401
|
+
fillSync();
|
|
402
|
+
return pageQueue.shiftSync();
|
|
189
403
|
}
|
|
190
404
|
async function close() {
|
|
191
405
|
closed = true;
|
|
192
406
|
done = true;
|
|
193
|
-
pageQueue.
|
|
194
|
-
if (fd)
|
|
407
|
+
pageQueue.clear();
|
|
408
|
+
if (fd) {
|
|
409
|
+
try {
|
|
410
|
+
await fd.close();
|
|
411
|
+
} catch {}
|
|
412
|
+
fd = null;
|
|
413
|
+
}
|
|
414
|
+
if (fdSync !== null) {
|
|
415
|
+
try {
|
|
416
|
+
closeSync(fdSync);
|
|
417
|
+
} catch {}
|
|
418
|
+
fdSync = null;
|
|
419
|
+
}
|
|
195
420
|
}
|
|
196
421
|
return {
|
|
197
422
|
next,
|
|
423
|
+
nextSync,
|
|
198
424
|
close,
|
|
199
|
-
get lineCount() {
|
|
200
|
-
return emittedCount;
|
|
201
|
-
},
|
|
202
|
-
get firstLine() {
|
|
203
|
-
return firstLine;
|
|
204
|
-
},
|
|
205
|
-
get lastLine() {
|
|
206
|
-
return lastLine;
|
|
207
|
-
},
|
|
208
425
|
async *[Symbol.asyncIterator]() {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
426
|
+
try {
|
|
427
|
+
while (true) {
|
|
428
|
+
const p = await next();
|
|
429
|
+
if (!p) break;
|
|
430
|
+
yield p;
|
|
431
|
+
}
|
|
432
|
+
} finally {
|
|
433
|
+
await close();
|
|
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;
|
|
213
455
|
}
|
|
214
456
|
}
|
|
215
457
|
};
|
|
216
458
|
}
|
|
217
|
-
|
|
218
459
|
//#endregion
|
|
219
460
|
//#region src/reader/worker.reader.ts
|
|
220
461
|
const workerFile = typeof import.meta !== "undefined" ? new URL("./worker.mjs", import.meta.url) : __require.resolve("./worker.cjs");
|
|
221
462
|
function createWorkerReader(filepath, options) {
|
|
222
463
|
const { chunkSize, pageSize, delimiter, prefetch } = options;
|
|
464
|
+
const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
|
|
465
|
+
let done = false;
|
|
466
|
+
let closed = false;
|
|
223
467
|
const worker = new Worker(new URL(workerFile, import.meta.url), { workerData: {
|
|
224
468
|
filepath,
|
|
225
469
|
chunkSize,
|
|
226
470
|
pageSize,
|
|
227
|
-
delimiter
|
|
471
|
+
delimiter,
|
|
472
|
+
prefetch
|
|
228
473
|
} });
|
|
229
|
-
const pageQueue = createPageQueue();
|
|
230
|
-
let done = false;
|
|
231
|
-
let closed = false;
|
|
232
|
-
let emittedCount = 0;
|
|
233
|
-
let firstLine = null;
|
|
234
|
-
let lastLine = null;
|
|
235
474
|
worker.on("message", (msg) => {
|
|
236
475
|
if (msg.type === "page") pageQueue.push(msg.data);
|
|
237
476
|
if (msg.type === "done") {
|
|
@@ -239,49 +478,66 @@ function createWorkerReader(filepath, options) {
|
|
|
239
478
|
pageQueue.wake();
|
|
240
479
|
}
|
|
241
480
|
});
|
|
481
|
+
worker.on("error", () => {
|
|
482
|
+
done = true;
|
|
483
|
+
pageQueue.wake();
|
|
484
|
+
});
|
|
485
|
+
worker.on("exit", () => {
|
|
486
|
+
done = true;
|
|
487
|
+
pageQueue.wake();
|
|
488
|
+
});
|
|
242
489
|
async function next() {
|
|
243
490
|
if (closed) return null;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
return page;
|
|
491
|
+
return await pageQueue.shift(done);
|
|
492
|
+
}
|
|
493
|
+
function nextSync() {
|
|
494
|
+
if (closed) return null;
|
|
495
|
+
return pageQueue.shiftSync();
|
|
250
496
|
}
|
|
251
497
|
async function close() {
|
|
252
498
|
closed = true;
|
|
253
499
|
done = true;
|
|
254
|
-
|
|
500
|
+
pageQueue.clear();
|
|
501
|
+
await worker.terminate();
|
|
502
|
+
}
|
|
503
|
+
function tryClose() {
|
|
504
|
+
close().catch(() => {});
|
|
255
505
|
}
|
|
256
506
|
return {
|
|
257
507
|
next,
|
|
508
|
+
nextSync,
|
|
258
509
|
close,
|
|
259
|
-
get lineCount() {
|
|
260
|
-
return emittedCount;
|
|
261
|
-
},
|
|
262
|
-
get firstLine() {
|
|
263
|
-
return firstLine;
|
|
264
|
-
},
|
|
265
|
-
get lastLine() {
|
|
266
|
-
return lastLine;
|
|
267
|
-
},
|
|
268
510
|
async *[Symbol.asyncIterator]() {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
511
|
+
try {
|
|
512
|
+
while (true) {
|
|
513
|
+
const p = await next();
|
|
514
|
+
if (!p) break;
|
|
515
|
+
yield p;
|
|
516
|
+
}
|
|
517
|
+
} finally {
|
|
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();
|
|
273
530
|
}
|
|
274
531
|
}
|
|
275
532
|
};
|
|
276
533
|
}
|
|
277
|
-
|
|
278
534
|
//#endregion
|
|
279
535
|
//#region src/main.ts
|
|
280
536
|
function createPager(filepath, options = {}) {
|
|
281
|
-
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;
|
|
282
538
|
if (!filepath) throw new Error("filepath required");
|
|
283
|
-
if (pageSize
|
|
284
|
-
if (prefetch
|
|
539
|
+
if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
|
|
540
|
+
if (prefetch < 1) throw new RangeError("prefetch must be >= 1");
|
|
285
541
|
if (backward && useWorker) throw new Error("backward not supported with useWorker");
|
|
286
542
|
return useWorker ? createWorkerReader(filepath, {
|
|
287
543
|
chunkSize,
|
|
@@ -300,6 +556,5 @@ function createPager(filepath, options = {}) {
|
|
|
300
556
|
delimiter
|
|
301
557
|
});
|
|
302
558
|
}
|
|
303
|
-
|
|
304
559
|
//#endregion
|
|
305
|
-
export { createPager, createPager as default };
|
|
560
|
+
export { createPager, createPager as default };
|