readline-pager 0.6.2 → 0.6.5
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 +9 -3
- package/dist/main.cjs +7 -7
- package/dist/main.mjs +7 -7
- package/dist/native.cjs +24 -22
- package/dist/native.mjs +24 -22
- package/dist/worker.cjs +2 -2
- package/dist/worker.mjs +2 -2
- package/package.json +6 -7
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ let page;
|
|
|
68
68
|
const pager = createPager("./bigfile.txt");
|
|
69
69
|
while ((page = pager.nextSync()) !== null) {}
|
|
70
70
|
|
|
71
|
-
// Native C++
|
|
71
|
+
// Native C++
|
|
72
72
|
for await (const page of createNativePager("./bigfile.txt")) {
|
|
73
73
|
}
|
|
74
74
|
```
|
|
@@ -87,6 +87,12 @@ createPager(filepath, {
|
|
|
87
87
|
useWorker?: boolean, // default: false
|
|
88
88
|
tryNative?: boolean, // default: true
|
|
89
89
|
});
|
|
90
|
+
|
|
91
|
+
createNativePager(filepath, {
|
|
92
|
+
pageSize?: number, // default: 1_000
|
|
93
|
+
delimiter?: string, // default: "\n"
|
|
94
|
+
backward?: boolean, // default: false
|
|
95
|
+
});
|
|
90
96
|
```
|
|
91
97
|
|
|
92
98
|
- `chunkSize` — number of bytes read per I/O operation.
|
|
@@ -98,7 +104,7 @@ createPager(filepath, {
|
|
|
98
104
|
- `tryNative` — attempts to use the native reader, falls back to the non-native version if it fails.
|
|
99
105
|
|
|
100
106
|
> **Note:**
|
|
101
|
-
> `createNativePager`
|
|
107
|
+
> `createNativePager` requires x86 AVX2 or ARM NEON CPU instruction set extensions and will throw if they are not available. It also does **not** support multi-character delimiters due to fast SIMD-based scanning.
|
|
102
108
|
|
|
103
109
|
---
|
|
104
110
|
|
|
@@ -138,7 +144,7 @@ Run the benchmark locally:
|
|
|
138
144
|
npm run benchmark:node
|
|
139
145
|
|
|
140
146
|
# or customize with args
|
|
141
|
-
node test/
|
|
147
|
+
node test/benchmark.ts --lines=20000 --page-size=500 --backward
|
|
142
148
|
```
|
|
143
149
|
|
|
144
150
|
> Test setup: generated text files (UUID lines), NVMe SSD, Node.js runtime.
|
package/dist/main.cjs
CHANGED
|
@@ -529,7 +529,7 @@ function createPager(filepath, options = {}) {
|
|
|
529
529
|
if (tryNative) {
|
|
530
530
|
if (delimiter.length !== 1) throw new RangeError("native reader only supports single-character delimiters");
|
|
531
531
|
}
|
|
532
|
-
const
|
|
532
|
+
const readerOptions = {
|
|
533
533
|
chunkSize,
|
|
534
534
|
pageSize,
|
|
535
535
|
prefetch,
|
|
@@ -537,19 +537,19 @@ function createPager(filepath, options = {}) {
|
|
|
537
537
|
};
|
|
538
538
|
let nativeReader;
|
|
539
539
|
if (tryNative) {
|
|
540
|
-
const
|
|
540
|
+
const nativeOptions = {
|
|
541
541
|
pageSize,
|
|
542
542
|
delimiter,
|
|
543
543
|
backward
|
|
544
544
|
};
|
|
545
545
|
try {
|
|
546
|
-
nativeReader = require_native.createNativePager(filepath,
|
|
546
|
+
nativeReader = require_native.createNativePager(filepath, nativeOptions);
|
|
547
547
|
} catch {}
|
|
548
548
|
}
|
|
549
|
-
const reader = tryNative && nativeReader ? nativeReader : useWorker ? createWorkerReader(filepath,
|
|
550
|
-
if (process.env.
|
|
551
|
-
globalThis.
|
|
552
|
-
globalThis.
|
|
549
|
+
const reader = tryNative && nativeReader ? nativeReader : useWorker ? createWorkerReader(filepath, readerOptions) : backward ? createBackwardReader(filepath, readerOptions) : createForwardReader(filepath, readerOptions);
|
|
550
|
+
if (process.env.PAGER_TEST_CLEANUPS) {
|
|
551
|
+
globalThis.__pager_test_cleanups__ ??= [];
|
|
552
|
+
globalThis.__pager_test_cleanups__.push(reader.close);
|
|
553
553
|
}
|
|
554
554
|
return reader;
|
|
555
555
|
}
|
package/dist/main.mjs
CHANGED
|
@@ -529,7 +529,7 @@ function createPager(filepath, options = {}) {
|
|
|
529
529
|
if (tryNative) {
|
|
530
530
|
if (delimiter.length !== 1) throw new RangeError("native reader only supports single-character delimiters");
|
|
531
531
|
}
|
|
532
|
-
const
|
|
532
|
+
const readerOptions = {
|
|
533
533
|
chunkSize,
|
|
534
534
|
pageSize,
|
|
535
535
|
prefetch,
|
|
@@ -537,19 +537,19 @@ function createPager(filepath, options = {}) {
|
|
|
537
537
|
};
|
|
538
538
|
let nativeReader;
|
|
539
539
|
if (tryNative) {
|
|
540
|
-
const
|
|
540
|
+
const nativeOptions = {
|
|
541
541
|
pageSize,
|
|
542
542
|
delimiter,
|
|
543
543
|
backward
|
|
544
544
|
};
|
|
545
545
|
try {
|
|
546
|
-
nativeReader = createNativePager(filepath,
|
|
546
|
+
nativeReader = createNativePager(filepath, nativeOptions);
|
|
547
547
|
} catch {}
|
|
548
548
|
}
|
|
549
|
-
const reader = tryNative && nativeReader ? nativeReader : useWorker ? createWorkerReader(filepath,
|
|
550
|
-
if (process.env.
|
|
551
|
-
globalThis.
|
|
552
|
-
globalThis.
|
|
549
|
+
const reader = tryNative && nativeReader ? nativeReader : useWorker ? createWorkerReader(filepath, readerOptions) : backward ? createBackwardReader(filepath, readerOptions) : createForwardReader(filepath, readerOptions);
|
|
550
|
+
if (process.env.PAGER_TEST_CLEANUPS) {
|
|
551
|
+
globalThis.__pager_test_cleanups__ ??= [];
|
|
552
|
+
globalThis.__pager_test_cleanups__.push(reader.close);
|
|
553
553
|
}
|
|
554
554
|
return reader;
|
|
555
555
|
}
|
package/dist/native.cjs
CHANGED
|
@@ -7,16 +7,13 @@ function isMusl() {
|
|
|
7
7
|
if ((0, node_os.platform)() !== "linux") return false;
|
|
8
8
|
try {
|
|
9
9
|
return !(process.report?.getReport?.())?.header?.glibcVersionRuntime;
|
|
10
|
-
} catch {
|
|
11
|
-
|
|
12
|
-
}
|
|
10
|
+
} catch {}
|
|
11
|
+
return false;
|
|
13
12
|
}
|
|
14
13
|
function getPackageName() {
|
|
15
14
|
const p = (0, node_os.platform)();
|
|
16
15
|
const a = (0, node_os.arch)();
|
|
17
16
|
switch (p) {
|
|
18
|
-
case "darwin":
|
|
19
|
-
case "win32": return `@devmor-j/readline-pager-${p}-${a}`;
|
|
20
17
|
case "linux": return `@devmor-j/readline-pager-${p}-${isMusl() ? "musl-" : ""}${a}`;
|
|
21
18
|
default: throw new Error(`Unsupported platform: ${p}/${a}`);
|
|
22
19
|
}
|
|
@@ -25,40 +22,45 @@ function loadNativeAddon() {
|
|
|
25
22
|
try {
|
|
26
23
|
return require$1(getPackageName());
|
|
27
24
|
} catch {
|
|
28
|
-
const
|
|
29
|
-
|
|
25
|
+
const p = (0, node_os.platform)();
|
|
26
|
+
const a = (0, node_os.arch)();
|
|
27
|
+
throw new Error(`Native addon not available for ${p}/${a}.`);
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
|
-
function createNativePager(filepath, options) {
|
|
33
|
-
const { pageSize = 1e3, delimiter = "\n", backward = false } = options
|
|
30
|
+
function createNativePager(filepath, options = {}) {
|
|
31
|
+
const { pageSize = 1e3, delimiter = "\n", backward = false } = options;
|
|
34
32
|
if (!filepath) throw new Error("filepath required");
|
|
35
33
|
if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
|
|
36
|
-
if (delimiter
|
|
37
|
-
const
|
|
38
|
-
|
|
34
|
+
if (delimiter?.length > 1) throw new RangeError("native reader only supports single-character delimiters");
|
|
35
|
+
const nativeReader = loadNativeAddon();
|
|
36
|
+
if (process.env.PAGER_TEST_CLEANUPS) {
|
|
37
|
+
globalThis.__pager_test_cleanups__ ??= [];
|
|
38
|
+
globalThis.__pager_test_cleanups__.push(nativeReader.close);
|
|
39
|
+
}
|
|
40
|
+
let fd = nativeReader.open(filepath, pageSize, delimiter, backward);
|
|
39
41
|
let closed = false;
|
|
40
|
-
|
|
42
|
+
async function next() {
|
|
41
43
|
if (closed || !fd) return null;
|
|
42
|
-
const data = await
|
|
44
|
+
const data = await nativeReader.next(fd);
|
|
43
45
|
if (!data) return null;
|
|
44
46
|
return data.toString("utf8").split(delimiter);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
+
}
|
|
48
|
+
function nextSync() {
|
|
47
49
|
if (closed || !fd) return null;
|
|
48
|
-
const data =
|
|
50
|
+
const data = nativeReader.nextSync(fd);
|
|
49
51
|
if (!data) return null;
|
|
50
52
|
return data.toString("utf8").split(delimiter);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
if (closed) return;
|
|
53
|
+
}
|
|
54
|
+
async function close() {
|
|
55
|
+
if (closed || !fd) return;
|
|
54
56
|
closed = true;
|
|
55
57
|
if (fd) {
|
|
56
58
|
try {
|
|
57
|
-
await
|
|
59
|
+
await nativeReader.close(fd);
|
|
58
60
|
} catch {}
|
|
59
61
|
fd = null;
|
|
60
62
|
}
|
|
61
|
-
}
|
|
63
|
+
}
|
|
62
64
|
function tryClose() {
|
|
63
65
|
close().catch(() => {});
|
|
64
66
|
}
|
package/dist/native.mjs
CHANGED
|
@@ -6,16 +6,13 @@ function isMusl() {
|
|
|
6
6
|
if (platform() !== "linux") return false;
|
|
7
7
|
try {
|
|
8
8
|
return !(process.report?.getReport?.())?.header?.glibcVersionRuntime;
|
|
9
|
-
} catch {
|
|
10
|
-
|
|
11
|
-
}
|
|
9
|
+
} catch {}
|
|
10
|
+
return false;
|
|
12
11
|
}
|
|
13
12
|
function getPackageName() {
|
|
14
13
|
const p = platform();
|
|
15
14
|
const a = arch();
|
|
16
15
|
switch (p) {
|
|
17
|
-
case "darwin":
|
|
18
|
-
case "win32": return `@devmor-j/readline-pager-${p}-${a}`;
|
|
19
16
|
case "linux": return `@devmor-j/readline-pager-${p}-${isMusl() ? "musl-" : ""}${a}`;
|
|
20
17
|
default: throw new Error(`Unsupported platform: ${p}/${a}`);
|
|
21
18
|
}
|
|
@@ -24,40 +21,45 @@ function loadNativeAddon() {
|
|
|
24
21
|
try {
|
|
25
22
|
return require(getPackageName());
|
|
26
23
|
} catch {
|
|
27
|
-
const
|
|
28
|
-
|
|
24
|
+
const p = platform();
|
|
25
|
+
const a = arch();
|
|
26
|
+
throw new Error(`Native addon not available for ${p}/${a}.`);
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
|
-
function createNativePager(filepath, options) {
|
|
32
|
-
const { pageSize = 1e3, delimiter = "\n", backward = false } = options
|
|
29
|
+
function createNativePager(filepath, options = {}) {
|
|
30
|
+
const { pageSize = 1e3, delimiter = "\n", backward = false } = options;
|
|
33
31
|
if (!filepath) throw new Error("filepath required");
|
|
34
32
|
if (pageSize < 1) throw new RangeError("pageSize must be >= 1");
|
|
35
|
-
if (delimiter
|
|
36
|
-
const
|
|
37
|
-
|
|
33
|
+
if (delimiter?.length > 1) throw new RangeError("native reader only supports single-character delimiters");
|
|
34
|
+
const nativeReader = loadNativeAddon();
|
|
35
|
+
if (process.env.PAGER_TEST_CLEANUPS) {
|
|
36
|
+
globalThis.__pager_test_cleanups__ ??= [];
|
|
37
|
+
globalThis.__pager_test_cleanups__.push(nativeReader.close);
|
|
38
|
+
}
|
|
39
|
+
let fd = nativeReader.open(filepath, pageSize, delimiter, backward);
|
|
38
40
|
let closed = false;
|
|
39
|
-
|
|
41
|
+
async function next() {
|
|
40
42
|
if (closed || !fd) return null;
|
|
41
|
-
const data = await
|
|
43
|
+
const data = await nativeReader.next(fd);
|
|
42
44
|
if (!data) return null;
|
|
43
45
|
return data.toString("utf8").split(delimiter);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
+
}
|
|
47
|
+
function nextSync() {
|
|
46
48
|
if (closed || !fd) return null;
|
|
47
|
-
const data =
|
|
49
|
+
const data = nativeReader.nextSync(fd);
|
|
48
50
|
if (!data) return null;
|
|
49
51
|
return data.toString("utf8").split(delimiter);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (closed) return;
|
|
52
|
+
}
|
|
53
|
+
async function close() {
|
|
54
|
+
if (closed || !fd) return;
|
|
53
55
|
closed = true;
|
|
54
56
|
if (fd) {
|
|
55
57
|
try {
|
|
56
|
-
await
|
|
58
|
+
await nativeReader.close(fd);
|
|
57
59
|
} catch {}
|
|
58
60
|
fd = null;
|
|
59
61
|
}
|
|
60
|
-
}
|
|
62
|
+
}
|
|
61
63
|
function tryClose() {
|
|
62
64
|
close().catch(() => {});
|
|
63
65
|
}
|
package/dist/worker.cjs
CHANGED
|
@@ -4,10 +4,10 @@ let node_worker_threads = require("node:worker_threads");
|
|
|
4
4
|
const { filepath, options } = node_worker_threads.workerData;
|
|
5
5
|
const { chunkSize, pageSize, delimiter } = options;
|
|
6
6
|
const backpressure = pageSize * 8;
|
|
7
|
-
|
|
7
|
+
function post(msg) {
|
|
8
8
|
if (!node_worker_threads.parentPort) process.exit(1);
|
|
9
9
|
node_worker_threads.parentPort.postMessage(msg);
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
(async () => {
|
|
12
12
|
try {
|
|
13
13
|
const fd = await (0, node_fs_promises.open)(filepath, "r");
|
package/dist/worker.mjs
CHANGED
|
@@ -4,10 +4,10 @@ import { parentPort, workerData } from "node:worker_threads";
|
|
|
4
4
|
const { filepath, options } = workerData;
|
|
5
5
|
const { chunkSize, pageSize, delimiter } = options;
|
|
6
6
|
const backpressure = pageSize * 8;
|
|
7
|
-
|
|
7
|
+
function post(msg) {
|
|
8
8
|
if (!parentPort) process.exit(1);
|
|
9
9
|
parentPort.postMessage(msg);
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
(async () => {
|
|
12
12
|
try {
|
|
13
13
|
const fd = await open(filepath, "r");
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "readline-pager",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.5",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"build:js": "tsdown",
|
|
6
6
|
"build:native": "node-gyp rebuild",
|
|
7
7
|
"build": "npm run build:native && npm run build:js",
|
|
8
|
-
"pkg:native": "node scripts/package-native.ts",
|
|
9
8
|
"pretest": "npm run build",
|
|
10
|
-
"test": "
|
|
9
|
+
"test": "PAGER_TEST_CLEANUPS=1 node --test --experimental-test-coverage test/**/*.test.ts",
|
|
11
10
|
"prepublishOnly": "npm run build && npm run test",
|
|
12
11
|
"benchmark:node": "node test/benchmark.ts",
|
|
13
12
|
"benchmark:deno": "deno --allow-write --allow-read --allow-env test/benchmark.ts",
|
|
@@ -24,10 +23,10 @@
|
|
|
24
23
|
"typescript": "~6.0.2"
|
|
25
24
|
},
|
|
26
25
|
"optionalDependencies": {
|
|
27
|
-
"@devmor-j/readline-pager-linux-arm64": "0.6.
|
|
28
|
-
"@devmor-j/readline-pager-linux-musl-arm64": "0.6.
|
|
29
|
-
"@devmor-j/readline-pager-linux-musl-x64": "0.6.
|
|
30
|
-
"@devmor-j/readline-pager-linux-x64": "0.6.
|
|
26
|
+
"@devmor-j/readline-pager-linux-arm64": "0.6.5",
|
|
27
|
+
"@devmor-j/readline-pager-linux-musl-arm64": "0.6.5",
|
|
28
|
+
"@devmor-j/readline-pager-linux-musl-x64": "0.6.5",
|
|
29
|
+
"@devmor-j/readline-pager-linux-x64": "0.6.5"
|
|
31
30
|
},
|
|
32
31
|
"gypfile": false,
|
|
33
32
|
"type": "module",
|