readline-pager 0.2.7 → 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 +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 +13 -7
- package/prebuilds/linux-x64/readline-pager.node +0 -0
package/dist/main.cjs
CHANGED
|
@@ -1,63 +1,189 @@
|
|
|
1
|
-
Object.defineProperties(exports, {
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
let node_fs = require("node:fs");
|
|
2
6
|
let node_fs_promises = require("node:fs/promises");
|
|
3
7
|
let node_worker_threads = require("node:worker_threads");
|
|
4
|
-
|
|
5
8
|
//#region src/queue.ts
|
|
6
|
-
function
|
|
7
|
-
|
|
8
|
-
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
|
+
}
|
|
9
78
|
return {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
resolver?.();
|
|
18
|
-
resolver = null;
|
|
79
|
+
push,
|
|
80
|
+
shift,
|
|
81
|
+
shiftSync,
|
|
82
|
+
wake,
|
|
83
|
+
clear,
|
|
84
|
+
get count() {
|
|
85
|
+
return count;
|
|
19
86
|
},
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (done()) return null;
|
|
23
|
-
await new Promise((r) => resolver = r);
|
|
24
|
-
if (queue.length) return queue.shift();
|
|
25
|
-
if (done()) return null;
|
|
26
|
-
return null;
|
|
87
|
+
get capacity() {
|
|
88
|
+
return buf.length;
|
|
27
89
|
}
|
|
28
90
|
};
|
|
29
91
|
}
|
|
30
|
-
|
|
31
92
|
//#endregion
|
|
32
93
|
//#region src/reader/backward.reader.ts
|
|
33
94
|
function createBackwardReader(filepath, options) {
|
|
34
95
|
const { chunkSize, pageSize, delimiter, prefetch } = options;
|
|
35
|
-
const pageQueue =
|
|
96
|
+
const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
|
|
97
|
+
const local = [];
|
|
36
98
|
let fd = null;
|
|
99
|
+
let fdSync = null;
|
|
37
100
|
let pos = 0;
|
|
38
101
|
let buffer = "";
|
|
39
102
|
let done = false;
|
|
40
103
|
let closed = false;
|
|
41
|
-
let
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
pos = (await fd.stat()).size;
|
|
49
|
-
if (pos === 0) done = true;
|
|
104
|
+
let startsWithDelimiter = false;
|
|
105
|
+
fdSync = (0, node_fs.openSync)(filepath, "r");
|
|
106
|
+
pos = (0, node_fs.statSync)(filepath).size;
|
|
107
|
+
if (pos === 0) {
|
|
108
|
+
pageQueue.push([buffer]);
|
|
109
|
+
done = true;
|
|
110
|
+
pageQueue.wake();
|
|
50
111
|
}
|
|
51
|
-
async
|
|
112
|
+
(async () => {
|
|
113
|
+
try {
|
|
114
|
+
fd = await (0, node_fs_promises.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() {
|
|
52
178
|
if (done || closed) return;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
while (pageQueue.queue.length < prefetch && pos > 0) {
|
|
179
|
+
if (fdSync === null) return;
|
|
180
|
+
while (pageQueue.count < prefetch && pos > 0 && !closed) {
|
|
56
181
|
const readSize = Math.min(chunkSize, pos);
|
|
57
182
|
pos -= readSize;
|
|
58
183
|
const buf = Buffer.allocUnsafe(readSize);
|
|
59
|
-
|
|
60
|
-
buffer
|
|
184
|
+
(0, node_fs.readSync)(fdSync, buf, 0, readSize, pos);
|
|
185
|
+
buffer = buf.toString("utf8") + buffer;
|
|
186
|
+
if (pos === 0 && buffer.startsWith(delimiter)) startsWithDelimiter = true;
|
|
61
187
|
let idx;
|
|
62
188
|
while ((idx = buffer.lastIndexOf(delimiter)) !== -1) {
|
|
63
189
|
const line = buffer.slice(idx + delimiter.length);
|
|
@@ -69,52 +195,53 @@ function createBackwardReader(filepath, options) {
|
|
|
69
195
|
}
|
|
70
196
|
}
|
|
71
197
|
}
|
|
72
|
-
if (pos === 0) {
|
|
73
|
-
local.push(buffer);
|
|
198
|
+
if (pos === 0 && !done) {
|
|
199
|
+
if (buffer.length > 0) local.push(buffer);
|
|
200
|
+
else if (startsWithDelimiter) local.push("");
|
|
74
201
|
buffer = "";
|
|
75
202
|
while (local.length > 0) {
|
|
76
|
-
const
|
|
77
|
-
|
|
203
|
+
const page = local.slice(local.length - Math.min(pageSize, local.length));
|
|
204
|
+
local.length -= page.length;
|
|
78
205
|
pageQueue.push(page);
|
|
79
206
|
}
|
|
80
207
|
done = true;
|
|
81
|
-
if (
|
|
82
|
-
|
|
83
|
-
|
|
208
|
+
if (fdSync !== null) {
|
|
209
|
+
(0, node_fs.closeSync)(fdSync);
|
|
210
|
+
fdSync = null;
|
|
84
211
|
}
|
|
212
|
+
pageQueue.wake();
|
|
85
213
|
}
|
|
86
214
|
}
|
|
87
215
|
async function next() {
|
|
88
216
|
if (closed) return null;
|
|
89
|
-
await
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return page;
|
|
217
|
+
return await pageQueue.shift(done);
|
|
218
|
+
}
|
|
219
|
+
function nextSync() {
|
|
220
|
+
if (closed) return null;
|
|
221
|
+
fillSync();
|
|
222
|
+
return pageQueue.shiftSync();
|
|
96
223
|
}
|
|
97
224
|
async function close() {
|
|
98
225
|
closed = true;
|
|
99
226
|
done = true;
|
|
100
|
-
pageQueue.
|
|
227
|
+
pageQueue.clear();
|
|
101
228
|
if (fd) {
|
|
102
|
-
|
|
229
|
+
try {
|
|
230
|
+
await fd.close();
|
|
231
|
+
} catch {}
|
|
103
232
|
fd = null;
|
|
104
233
|
}
|
|
234
|
+
if (fdSync !== null) {
|
|
235
|
+
try {
|
|
236
|
+
(0, node_fs.closeSync)(fdSync);
|
|
237
|
+
} catch {}
|
|
238
|
+
fdSync = null;
|
|
239
|
+
}
|
|
105
240
|
}
|
|
106
241
|
return {
|
|
107
242
|
next,
|
|
243
|
+
nextSync,
|
|
108
244
|
close,
|
|
109
|
-
get lineCount() {
|
|
110
|
-
return emittedCount;
|
|
111
|
-
},
|
|
112
|
-
get firstLine() {
|
|
113
|
-
return firstLine;
|
|
114
|
-
},
|
|
115
|
-
get lastLine() {
|
|
116
|
-
return lastLine;
|
|
117
|
-
},
|
|
118
245
|
async *[Symbol.asyncIterator]() {
|
|
119
246
|
try {
|
|
120
247
|
while (true) {
|
|
@@ -123,43 +250,123 @@ function createBackwardReader(filepath, options) {
|
|
|
123
250
|
yield p;
|
|
124
251
|
}
|
|
125
252
|
} finally {
|
|
126
|
-
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) (0, node_fs.closeSync)(fdSync);
|
|
269
|
+
} catch {}
|
|
270
|
+
fdSync = null;
|
|
271
|
+
try {
|
|
272
|
+
if (fd?.fd) (0, node_fs.closeSync)(fd.fd);
|
|
273
|
+
} catch {}
|
|
274
|
+
fd = null;
|
|
127
275
|
}
|
|
128
276
|
}
|
|
129
277
|
};
|
|
130
278
|
}
|
|
131
|
-
|
|
132
279
|
//#endregion
|
|
133
280
|
//#region src/reader/forward.reader.ts
|
|
134
281
|
function createForwardReader(filepath, options) {
|
|
135
282
|
const { chunkSize, pageSize, delimiter, prefetch } = options;
|
|
136
|
-
const pageQueue =
|
|
283
|
+
const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
|
|
284
|
+
const local = [];
|
|
137
285
|
let fd = null;
|
|
286
|
+
let fdSync = null;
|
|
138
287
|
let pos = 0;
|
|
139
288
|
let size = 0;
|
|
140
289
|
let buffer = "";
|
|
141
290
|
let done = false;
|
|
142
291
|
let closed = false;
|
|
143
|
-
let
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
size = (await fd.stat()).size;
|
|
151
|
-
if (size === 0) done = true;
|
|
292
|
+
let flushed = false;
|
|
293
|
+
fdSync = (0, node_fs.openSync)(filepath, "r");
|
|
294
|
+
size = (0, node_fs.statSync)(filepath).size;
|
|
295
|
+
if (size === 0) {
|
|
296
|
+
pageQueue.push([buffer]);
|
|
297
|
+
done = true;
|
|
298
|
+
pageQueue.wake();
|
|
152
299
|
}
|
|
153
|
-
async
|
|
300
|
+
(async () => {
|
|
301
|
+
try {
|
|
302
|
+
fd = await (0, node_fs_promises.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() {
|
|
154
362
|
if (done || closed) return;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
while (pageQueue.queue.length < prefetch && pos < size) {
|
|
363
|
+
if (fdSync === null) return;
|
|
364
|
+
while (pageQueue.count < prefetch && pos < size && !closed) {
|
|
158
365
|
const readSize = Math.min(chunkSize, size - pos);
|
|
159
366
|
const buf = Buffer.allocUnsafe(readSize);
|
|
160
|
-
const
|
|
367
|
+
const bytesRead = (0, node_fs.readSync)(fdSync, buf, 0, readSize, pos);
|
|
161
368
|
pos += bytesRead;
|
|
162
|
-
buffer
|
|
369
|
+
buffer = buffer + buf.toString("utf8", 0, bytesRead);
|
|
163
370
|
let idx;
|
|
164
371
|
while ((idx = buffer.indexOf(delimiter)) !== -1) {
|
|
165
372
|
const line = buffer.slice(0, idx);
|
|
@@ -168,49 +375,53 @@ function createForwardReader(filepath, options) {
|
|
|
168
375
|
while (local.length >= pageSize) pageQueue.push(local.splice(0, pageSize));
|
|
169
376
|
}
|
|
170
377
|
}
|
|
171
|
-
if (pos >= size) {
|
|
172
|
-
|
|
173
|
-
|
|
378
|
+
if (pos >= size && !flushed) {
|
|
379
|
+
flushed = true;
|
|
380
|
+
local.push(buffer.length > 0 ? buffer : "");
|
|
174
381
|
buffer = "";
|
|
175
|
-
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
|
+
}
|
|
176
387
|
done = true;
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
388
|
+
if (fdSync !== null) {
|
|
389
|
+
(0, node_fs.closeSync)(fdSync);
|
|
390
|
+
fdSync = null;
|
|
180
391
|
}
|
|
392
|
+
pageQueue.wake();
|
|
181
393
|
}
|
|
182
394
|
}
|
|
183
395
|
async function next() {
|
|
184
396
|
if (closed) return null;
|
|
185
|
-
await
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
return page;
|
|
397
|
+
return await pageQueue.shift(done);
|
|
398
|
+
}
|
|
399
|
+
function nextSync() {
|
|
400
|
+
if (closed) return null;
|
|
401
|
+
fillSync();
|
|
402
|
+
return pageQueue.shiftSync();
|
|
192
403
|
}
|
|
193
404
|
async function close() {
|
|
194
405
|
closed = true;
|
|
195
406
|
done = true;
|
|
196
|
-
pageQueue.
|
|
407
|
+
pageQueue.clear();
|
|
197
408
|
if (fd) {
|
|
198
|
-
|
|
409
|
+
try {
|
|
410
|
+
await fd.close();
|
|
411
|
+
} catch {}
|
|
199
412
|
fd = null;
|
|
200
413
|
}
|
|
414
|
+
if (fdSync !== null) {
|
|
415
|
+
try {
|
|
416
|
+
(0, node_fs.closeSync)(fdSync);
|
|
417
|
+
} catch {}
|
|
418
|
+
fdSync = null;
|
|
419
|
+
}
|
|
201
420
|
}
|
|
202
421
|
return {
|
|
203
422
|
next,
|
|
423
|
+
nextSync,
|
|
204
424
|
close,
|
|
205
|
-
get lineCount() {
|
|
206
|
-
return emittedCount;
|
|
207
|
-
},
|
|
208
|
-
get firstLine() {
|
|
209
|
-
return firstLine;
|
|
210
|
-
},
|
|
211
|
-
get lastLine() {
|
|
212
|
-
return lastLine;
|
|
213
|
-
},
|
|
214
425
|
async *[Symbol.asyncIterator]() {
|
|
215
426
|
try {
|
|
216
427
|
while (true) {
|
|
@@ -221,27 +432,45 @@ function createForwardReader(filepath, options) {
|
|
|
221
432
|
} finally {
|
|
222
433
|
await close();
|
|
223
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) (0, node_fs.closeSync)(fdSync);
|
|
449
|
+
} catch {}
|
|
450
|
+
fdSync = null;
|
|
451
|
+
try {
|
|
452
|
+
if (fd?.fd) (0, node_fs.closeSync)(fd.fd);
|
|
453
|
+
} catch {}
|
|
454
|
+
fd = null;
|
|
455
|
+
}
|
|
224
456
|
}
|
|
225
457
|
};
|
|
226
458
|
}
|
|
227
|
-
|
|
228
459
|
//#endregion
|
|
229
460
|
//#region src/reader/worker.reader.ts
|
|
230
|
-
const workerFile =
|
|
461
|
+
const workerFile = new URL("./worker.mjs", require("url").pathToFileURL(__filename).href);
|
|
231
462
|
function createWorkerReader(filepath, options) {
|
|
232
463
|
const { chunkSize, pageSize, delimiter, prefetch } = options;
|
|
464
|
+
const pageQueue = createRingBuffer(Math.max(2, prefetch + 1));
|
|
465
|
+
let done = false;
|
|
466
|
+
let closed = false;
|
|
233
467
|
const worker = new node_worker_threads.Worker(new URL(workerFile, require("url").pathToFileURL(__filename).href), { workerData: {
|
|
234
468
|
filepath,
|
|
235
469
|
chunkSize,
|
|
236
470
|
pageSize,
|
|
237
|
-
delimiter
|
|
471
|
+
delimiter,
|
|
472
|
+
prefetch
|
|
238
473
|
} });
|
|
239
|
-
const pageQueue = createPageQueue();
|
|
240
|
-
let done = false;
|
|
241
|
-
let closed = false;
|
|
242
|
-
let emittedCount = 0;
|
|
243
|
-
let firstLine = null;
|
|
244
|
-
let lastLine = null;
|
|
245
474
|
worker.on("message", (msg) => {
|
|
246
475
|
if (msg.type === "page") pageQueue.push(msg.data);
|
|
247
476
|
if (msg.type === "done") {
|
|
@@ -249,32 +478,35 @@ function createWorkerReader(filepath, options) {
|
|
|
249
478
|
pageQueue.wake();
|
|
250
479
|
}
|
|
251
480
|
});
|
|
481
|
+
worker.on("error", () => {
|
|
482
|
+
done = true;
|
|
483
|
+
pageQueue.wake();
|
|
484
|
+
});
|
|
485
|
+
worker.on("exit", () => {
|
|
486
|
+
done = true;
|
|
487
|
+
pageQueue.wake();
|
|
488
|
+
});
|
|
252
489
|
async function next() {
|
|
253
490
|
if (closed) return null;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
return page;
|
|
491
|
+
return await pageQueue.shift(done);
|
|
492
|
+
}
|
|
493
|
+
function nextSync() {
|
|
494
|
+
if (closed) return null;
|
|
495
|
+
return pageQueue.shiftSync();
|
|
260
496
|
}
|
|
261
497
|
async function close() {
|
|
262
498
|
closed = true;
|
|
263
499
|
done = true;
|
|
500
|
+
pageQueue.clear();
|
|
264
501
|
await worker.terminate();
|
|
265
502
|
}
|
|
503
|
+
function tryClose() {
|
|
504
|
+
close().catch(() => {});
|
|
505
|
+
}
|
|
266
506
|
return {
|
|
267
507
|
next,
|
|
508
|
+
nextSync,
|
|
268
509
|
close,
|
|
269
|
-
get lineCount() {
|
|
270
|
-
return emittedCount;
|
|
271
|
-
},
|
|
272
|
-
get firstLine() {
|
|
273
|
-
return firstLine;
|
|
274
|
-
},
|
|
275
|
-
get lastLine() {
|
|
276
|
-
return lastLine;
|
|
277
|
-
},
|
|
278
510
|
async *[Symbol.asyncIterator]() {
|
|
279
511
|
try {
|
|
280
512
|
while (true) {
|
|
@@ -283,19 +515,29 @@ function createWorkerReader(filepath, options) {
|
|
|
283
515
|
yield p;
|
|
284
516
|
}
|
|
285
517
|
} finally {
|
|
286
|
-
|
|
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();
|
|
287
530
|
}
|
|
288
531
|
}
|
|
289
532
|
};
|
|
290
533
|
}
|
|
291
|
-
|
|
292
534
|
//#endregion
|
|
293
535
|
//#region src/main.ts
|
|
294
536
|
function createPager(filepath, options = {}) {
|
|
295
|
-
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;
|
|
296
538
|
if (!filepath) throw new Error("filepath required");
|
|
297
|
-
if (pageSize
|
|
298
|
-
if (prefetch
|
|
539
|
+
if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
|
|
540
|
+
if (prefetch < 1) throw new RangeError("prefetch must be >= 1");
|
|
299
541
|
if (backward && useWorker) throw new Error("backward not supported with useWorker");
|
|
300
542
|
return useWorker ? createWorkerReader(filepath, {
|
|
301
543
|
chunkSize,
|
|
@@ -314,7 +556,6 @@ function createPager(filepath, options = {}) {
|
|
|
314
556
|
delimiter
|
|
315
557
|
});
|
|
316
558
|
}
|
|
317
|
-
|
|
318
559
|
//#endregion
|
|
319
560
|
exports.createPager = createPager;
|
|
320
|
-
exports.default = createPager;
|
|
561
|
+
exports.default = createPager;
|
package/dist/main.d.cts
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
|
+
nextSync(): string[] | null;
|
|
14
15
|
close(): Promise<void>;
|
|
15
|
-
readonly lineCount: number;
|
|
16
|
-
readonly firstLine: string | null;
|
|
17
|
-
readonly lastLine: string | null;
|
|
18
16
|
}
|
|
19
17
|
//#endregion
|
|
20
18
|
//#region src/main.d.ts
|
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
|
+
nextSync(): string[] | null;
|
|
14
15
|
close(): Promise<void>;
|
|
15
|
-
readonly lineCount: number;
|
|
16
|
-
readonly firstLine: string | null;
|
|
17
|
-
readonly lastLine: string | null;
|
|
18
16
|
}
|
|
19
17
|
//#endregion
|
|
20
18
|
//#region src/main.d.ts
|