@pid7/ashwa 0.2.0 → 0.2.2
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 +154 -58
- package/browser.js +27 -6
- package/browser.mjs +30 -2
- package/index.d.ts +27 -1
- package/index.js +17 -0
- package/index.mjs +17 -0
- package/native/index.d.ts +31 -1
- package/native/index.darwin-arm64.node +0 -0
- package/native/index.js +643 -361
- package/native/index.linux-arm64-gnu.node +0 -0
- package/native/index.linux-x64-gnu.node +0 -0
- package/native/index.win32-arm64-msvc.node +0 -0
- package/native/index.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
- package/wasm/pkg/ashwa_wasm.d.ts +14 -0
- package/wasm/pkg/ashwa_wasm.js +23 -0
- package/wasm/pkg/ashwa_wasm_bg.wasm +0 -0
- package/wasm/pkg/ashwa_wasm_bg.wasm.d.ts +1 -0
package/README.md
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
[](https://www.npmjs.com/package/@pid7/ashwa)
|
|
2
|
-
[](https://crates.io/crates/ashwa)
|
|
3
2
|
[](https://github.com/pid7-org/ashwa/actions/workflows/tests.yaml)
|
|
4
3
|
[](../LICENSE-MIT)
|
|
5
4
|
|
|
@@ -7,99 +6,196 @@
|
|
|
7
6
|
|
|
8
7
|
Hardware accelerated routines for single substring search
|
|
9
8
|
|
|
10
|
-
>
|
|
11
|
-
> Package is supported in both Node.js and browser environments.
|
|
9
|
+
> 💡 TIP: `@pid7/ashwa` runs seamlessly across Node.js, Bun, Deno, and modern browser / WebWorker environments.
|
|
12
10
|
|
|
13
|
-
>
|
|
14
|
-
>
|
|
15
|
-
|
|
11
|
+
> ℹ️ NOTE: The optimal execution backend (native N-API SIMD bindings for Node / Bun / Deno or WebAssembly
|
|
12
|
+
> SIMD for browsers) is automatically detected and selected at runtime with zero manual configuration.
|
|
13
|
+
|
|
14
|
+
## Index
|
|
15
|
+
|
|
16
|
+
- [Supported Platforms](#supported-platforms)
|
|
17
|
+
- [Installation](#installation)
|
|
18
|
+
- [Usage](#usage)
|
|
19
|
+
- [Node](#node)
|
|
20
|
+
- [Browser](#browser)
|
|
21
|
+
- [API Reference](#api-reference)
|
|
22
|
+
- [`searchOne`](#searchone)
|
|
23
|
+
- [`searchTwo`](#searchtwo)
|
|
24
|
+
- [`init`](#init)
|
|
25
|
+
- [`initSync`](#initsync)
|
|
26
|
+
- [`isNative`](#isnative)
|
|
27
|
+
- [Benchmarks](#benchmarks)
|
|
28
|
+
- [`searchOne`](#searchone-1)
|
|
29
|
+
- [`searchTwo`](#searchtwo-1)
|
|
16
30
|
|
|
17
31
|
## Supported Platforms
|
|
18
32
|
|
|
19
|
-
| Architecture | Target Platform
|
|
20
|
-
|
|
21
|
-
| x86_64 | Linux, macOS, Windows
|
|
22
|
-
| AArch64 (ARM64) | Apple Silicon, Linux ARM64
|
|
23
|
-
| WebAssembly | Browsers, Node.js (
|
|
33
|
+
| Architecture | Target Platform | Target ISA | Fallback |
|
|
34
|
+
|:----------------|:---------------------------------------|:-------------------------------------|:------------|
|
|
35
|
+
| x86_64 | Linux, macOS, Windows | AVX-512BW, AVX2, SSE4.2, SSSE3, SSE2 | 64-bit SWAR |
|
|
36
|
+
| AArch64 (ARM64) | Apple Silicon, Linux ARM64 | 128-bit ARM NEON | 64-bit SWAR |
|
|
37
|
+
| WebAssembly | Browsers, WebWorkers, Node.js (wasm32) | WASM SIMD128 (`simd128`) | 32-bit SWAR |
|
|
24
38
|
|
|
25
|
-
##
|
|
39
|
+
## Installation
|
|
26
40
|
|
|
27
|
-
Install `@pid7/ashwa`
|
|
41
|
+
Install `@pid7/ashwa` using your preferred package manager:
|
|
28
42
|
|
|
29
43
|
```bash
|
|
30
44
|
npm install @pid7/ashwa
|
|
31
45
|
```
|
|
32
46
|
|
|
33
|
-
##
|
|
47
|
+
## Usage
|
|
34
48
|
|
|
35
|
-
###
|
|
49
|
+
### Node
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
import { searchOne } from '@pid7/ashwa';
|
|
51
|
+
Native N-API bindings execute _synchronously_ with zero overhead and full hardware SIMD acceleration,
|
|
39
52
|
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
```js
|
|
54
|
+
import { searchOne, searchTwo } from '@pid7/ashwa';
|
|
42
55
|
|
|
43
|
-
const
|
|
44
|
-
|
|
56
|
+
const haystack = new TextEncoder().encode("The quick brown fox jumps over the lazy dog");
|
|
57
|
+
const indexOne = searchOne(haystack, "f".charCodeAt(0));
|
|
58
|
+
console.log(`Found 'f' at byte index: ${indexOne}`); // 16
|
|
45
59
|
```
|
|
46
60
|
|
|
47
|
-
###
|
|
61
|
+
### Browser
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
> In browser/WebWorker environments, WASM automatically initializes on the first call.
|
|
63
|
+
In browser and WebWorker runtimes, WebAssembly SIMD (`simd128`) is used. The WASM module automatically initializes asynchronously on the first search call, or can be pre-warmed during application startup:
|
|
51
64
|
|
|
52
|
-
```
|
|
53
|
-
import { init, searchOne } from '@pid7/ashwa';
|
|
65
|
+
```js
|
|
66
|
+
import { init, searchOne, searchTwo } from '@pid7/ashwa';
|
|
54
67
|
|
|
55
|
-
// Optional pre-
|
|
68
|
+
// Optional pre-init
|
|
56
69
|
await init();
|
|
57
70
|
|
|
58
|
-
const
|
|
71
|
+
const haystack = new TextEncoder().encode("The quick brown fox jumps over the lazy dog");
|
|
72
|
+
const index = await searchOne(haystack, "f".charCodeAt(0));
|
|
73
|
+
console.log(`Found 'f' at byte index: ${index}`); // 16
|
|
59
74
|
```
|
|
60
75
|
|
|
76
|
+
## API Reference
|
|
77
|
+
|
|
78
|
+
### `searchOne(haystack, needle)`
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
function searchOne(
|
|
82
|
+
haystack: Uint8Array,
|
|
83
|
+
needle: number,
|
|
84
|
+
): number | null | Promise<number | null>;
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Searches for the first occurrence of `needle` (byte value `0`–`255`) within `haystack`.
|
|
88
|
+
|
|
89
|
+
- Parameters:
|
|
90
|
+
- `haystack`: `Uint8Array` / `Buffer` — The byte sequence to search.
|
|
91
|
+
- `needle`: `number` — The target byte value (`0`–`255`) to locate.
|
|
92
|
+
- Returns:
|
|
93
|
+
- In Node.js / Bun / Deno: `number | null` (synchronous 0-based byte index, or `null` if not found).
|
|
94
|
+
- In Browser / WebWorker: `Promise<number | null>` (resolves to 0-based byte index, or `null` if not found).
|
|
95
|
+
|
|
96
|
+
### `searchTwo(haystack, needle)`
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
function searchTwo(
|
|
100
|
+
haystack: Uint8Array,
|
|
101
|
+
needle: Uint8Array | [number, number] | number[],
|
|
102
|
+
): number | null | Promise<number | null>;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Searches for the first occurrence of a two-byte `needle` within `haystack`.
|
|
106
|
+
|
|
107
|
+
- Parameters:
|
|
108
|
+
- `haystack`: `Uint8Array` / `Buffer` — The byte sequence to search.
|
|
109
|
+
- `needle`: `Uint8Array | [number, number] | number[]` — A 2-byte sequence to locate.
|
|
110
|
+
- Returns:
|
|
111
|
+
- In Node.js / Bun / Deno: `number | null` (synchronous 0-based byte index, or `null` if not found).
|
|
112
|
+
- In Browser / WebWorker: `Promise<number | null>` (resolves to 0-based byte index, or `null` if not found).
|
|
113
|
+
|
|
114
|
+
### `init(moduleOrPath?)`
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
function init(moduleOrPath?: any): Promise<void>;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Asynchronously pre-initializes the WebAssembly module backend for Browser / WebWorker environments. In native Node.js environments (`isNative === true`), this is a no-op that resolves immediately.
|
|
121
|
+
|
|
122
|
+
- Parameters:
|
|
123
|
+
- `moduleOrPath` *(optional)*: `WebAssembly.Module | Response | ArrayBuffer | string` — Custom WASM source or URL.
|
|
124
|
+
|
|
125
|
+
### `initSync(bytesOrModule?)`
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
function initSync(bytesOrModule?: any): void;
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Synchronously initializes the WebAssembly module backend with pre-compiled bytes or a `WebAssembly.Module`. In native Node.js environments, this is a no-op.
|
|
132
|
+
|
|
133
|
+
- Parameters:
|
|
134
|
+
- `bytesOrModule` *(optional)*: `ArrayBuffer | Uint8Array | WebAssembly.Module` — Pre-loaded WASM binary or module.
|
|
135
|
+
|
|
136
|
+
### `isNative`
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const isNative: boolean;
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Boolean flag indicating whether `@pid7/ashwa` is executing via native N-API bindings (`true`) or WebAssembly SIMD (`false`).
|
|
143
|
+
|
|
61
144
|
## Benchmarks
|
|
62
145
|
|
|
63
|
-
- [`searchOne`](#searchone)
|
|
146
|
+
- [`searchOne`](#searchone-1)
|
|
147
|
+
- [`searchTwo`](#searchtwo-1)
|
|
148
|
+
|
|
149
|
+
> Benchmarks are evaluated across dedicated AWS EC2 hardware environments on Node.js `v22.23.2`,
|
|
150
|
+
>
|
|
151
|
+
> * x86_64 (_x64_)
|
|
152
|
+
> * Instance: Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz (8C/16T)
|
|
153
|
+
> * ISA: _AVX-512BW_ (`+nightly`) · _WASM SIMD128_ (stable)
|
|
154
|
+
> * Cache: L1d: 384 KiB · L1i: 256 KiB · L2: 10 MiB · L3: 54 MiB
|
|
155
|
+
> * STREAM Triad: 20.94 GiB/s
|
|
156
|
+
>
|
|
157
|
+
> * AArch64 (_arm64_)
|
|
158
|
+
> * Instance: AWS Graviton3 ARM Neoverse-V1 (16C/16T)
|
|
159
|
+
> * ISA: _NEON_ (stable)
|
|
160
|
+
> * Cache: L1d: 1 MiB · L1i: 1 MiB · L2: 16 MiB · L3: 32 MiB
|
|
161
|
+
> * STREAM Triad: 75.12 GiB/s
|
|
64
162
|
|
|
65
163
|
### `searchOne`
|
|
66
164
|
|
|
67
165
|
#### Native Node.js (V8 N-API)
|
|
68
166
|
|
|
69
|
-
|
|
167
|
+
| Level | Payload | Latency (x64) | Latency (arm64) | Throughput (x64) | Throughput (arm64) |
|
|
168
|
+
|:-----------|:----------|:--------------|:----------------|:-----------------|:-------------------|
|
|
169
|
+
| L1 Cache | 32 KiB | 346.28 ns | 743.33 ns | 88.13 GiB/s | 41.06 GiB/s |
|
|
170
|
+
| L2 Cache | 512 KiB | 10.17 µs | 11.25 µs | 47.99 GiB/s | 43.42 GiB/s |
|
|
171
|
+
| L3 Cache | 16 MiB | 552.74 µs | 381.99 µs | 28.27 GiB/s | 40.90 GiB/s |
|
|
172
|
+
| RAM | 256 MiB | 20.22 ms | 9.96 ms | 12.36 GiB/s | 25.10 GiB/s |
|
|
70
173
|
|
|
71
|
-
|
|
72
|
-
|:-----------|:----------|:----------|:-------------|
|
|
73
|
-
| L1 Cache | 32 KiB | 640.15 ns | 47.67 GiB/s |
|
|
74
|
-
| L2 Cache | 512 KiB | 10.07 µs | 48.51 GiB/s |
|
|
75
|
-
| L3 Cache | 16 MiB | 540.35 µs | 28.92 GiB/s |
|
|
76
|
-
| RAM | 256 MiB | 23.88 ms | 10.47 GiB/s |
|
|
174
|
+
#### WebAssembly (WASM SIMD128)
|
|
77
175
|
|
|
78
|
-
|
|
79
|
-
|
|
176
|
+
| Level | Payload | Latency (x64) | Throughput (x64) |
|
|
177
|
+
|:-----------|:----------|:--------------|:-----------------|
|
|
178
|
+
| L1 Cache | 32 KiB | 1.62 µs | 18.82 GiB/s |
|
|
179
|
+
| L2 Cache | 512 KiB | 27.88 µs | 17.51 GiB/s |
|
|
180
|
+
| L3 Cache | 16 MiB | 1.86 ms | 8.38 GiB/s |
|
|
181
|
+
| RAM | 256 MiB | 44.56 ms | 5.61 GiB/s |
|
|
80
182
|
|
|
81
|
-
|
|
183
|
+
### `searchTwo`
|
|
82
184
|
|
|
83
|
-
|
|
84
|
-
|:-----------|:----------|:----------|:-------------|
|
|
85
|
-
| L1 Cache | 32 KiB | 743.33 ns | 41.06 GiB/s |
|
|
86
|
-
| L2 Cache | 512 KiB | 11.25 µs | 43.42 GiB/s |
|
|
87
|
-
| L3 Cache | 16 MiB | 381.99 µs | 40.90 GiB/s |
|
|
88
|
-
| RAM | 256 MiB | 9.96 ms | 25.10 GiB/s |
|
|
185
|
+
#### Native Node.js (V8 N-API)
|
|
89
186
|
|
|
90
|
-
|
|
91
|
-
|
|
187
|
+
| Level | Payload | Latency (x64) | Latency (arm64) | Throughput (x64) | Throughput (arm64) |
|
|
188
|
+
|:-----------|:----------|:--------------|:----------------|:-----------------|:-------------------|
|
|
189
|
+
| L1 Cache | 32 KiB | 647.01 ns | 1.57 µs | 47.17 GiB/s | 19.44 GiB/s |
|
|
190
|
+
| L2 Cache | 512 KiB | 17.41 µs | 24.37 µs | 28.04 GiB/s | 20.03 GiB/s |
|
|
191
|
+
| L3 Cache | 16 MiB | 657.09 µs | 788.85 µs | 23.78 GiB/s | 19.81 GiB/s |
|
|
192
|
+
| RAM | 256 MiB | 22.50 ms | 13.13 ms | 11.11 GiB/s | 19.04 GiB/s |
|
|
92
193
|
|
|
93
194
|
#### WebAssembly (WASM SIMD128)
|
|
94
195
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
|
98
|
-
|
|
99
|
-
|
|
|
100
|
-
|
|
|
101
|
-
| L3 Cache | 16 MiB | 1.81 ms | 8.63 GiB/s |
|
|
102
|
-
| RAM | 256 MiB | 44.63 ms | 5.60 GiB/s |
|
|
103
|
-
|
|
104
|
-
Benchmarked using Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz (8C/16T) ·
|
|
105
|
-
L1d: 384 KiB, L1i: 256 KiB, L2: 10 MiB, L3: 54 MiB · STREAM Triad: 20.63 GB/s · Node.js v22.23.2
|
|
196
|
+
| Level | Payload | Latency (x64) | Throughput (x64) |
|
|
197
|
+
|:-----------|:----------|:--------------|:-----------------|
|
|
198
|
+
| L1 Cache | 32 KiB | 2.17 µs | 14.06 GiB/s |
|
|
199
|
+
| L2 Cache | 512 KiB | 35.10 µs | 13.91 GiB/s |
|
|
200
|
+
| L3 Cache | 16 MiB | 3.69 ms | 4.23 GiB/s |
|
|
201
|
+
| RAM | 256 MiB | 51.67 ms | 4.84 GiB/s |
|
package/browser.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @module @pid7/ashwa/browser
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
7
|
+
const { readFileSync } = require("fs");
|
|
8
|
+
const { join } = require("path");
|
|
9
9
|
|
|
10
10
|
let wasmModule = null;
|
|
11
11
|
let initPromise = null;
|
|
@@ -27,8 +27,8 @@ async function init(moduleOrPath) {
|
|
|
27
27
|
process.versions != null &&
|
|
28
28
|
process.versions.node != null
|
|
29
29
|
) {
|
|
30
|
-
const wasmPath =
|
|
31
|
-
input =
|
|
30
|
+
const wasmPath = join(__dirname, "./wasm/pkg/ashwa_wasm_bg.wasm");
|
|
31
|
+
input = readFileSync(wasmPath);
|
|
32
32
|
}
|
|
33
33
|
const initOptions = input ? { module_or_path: input } : undefined;
|
|
34
34
|
initPromise = Promise.resolve(wasm.default(initOptions)).then(() => {
|
|
@@ -54,8 +54,8 @@ function initSync(bytesOrModule) {
|
|
|
54
54
|
process.versions != null &&
|
|
55
55
|
process.versions.node != null
|
|
56
56
|
) {
|
|
57
|
-
const wasmPath =
|
|
58
|
-
input =
|
|
57
|
+
const wasmPath = join(__dirname, "./wasm/pkg/ashwa_wasm_bg.wasm");
|
|
58
|
+
input = readFileSync(wasmPath);
|
|
59
59
|
}
|
|
60
60
|
wasm.initSync(input);
|
|
61
61
|
wasmModule = wasm;
|
|
@@ -77,6 +77,26 @@ async function searchOne(haystack, needle) {
|
|
|
77
77
|
return res !== undefined && res !== null ? Number(res) : null;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Searches for the first occurrence of a two-byte `needle` in `haystack` via WebAssembly SIMD.
|
|
82
|
+
* Automatically initializes WASM if not already loaded.
|
|
83
|
+
*
|
|
84
|
+
* @param {Uint8Array} haystack - Byte array to search.
|
|
85
|
+
* @param {Uint8Array|number[]} needle - 2-byte sequence to locate.
|
|
86
|
+
* @returns {Promise<number|null>} Resolves to 0-based matching index or null if not found.
|
|
87
|
+
*/
|
|
88
|
+
async function searchTwo(haystack, needle) {
|
|
89
|
+
const n = Array.isArray(needle) ? new Uint8Array(needle) : needle;
|
|
90
|
+
if (n == null || n.length !== 2) {
|
|
91
|
+
throw new TypeError("needle must be a 2-byte sequence");
|
|
92
|
+
}
|
|
93
|
+
if (!wasmModule) {
|
|
94
|
+
await init();
|
|
95
|
+
}
|
|
96
|
+
const res = wasmModule.searchTwo(haystack, n);
|
|
97
|
+
return res !== undefined && res !== null ? Number(res) : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
80
100
|
module.exports = {
|
|
81
101
|
/**
|
|
82
102
|
* `false` indicating WebAssembly execution mode.
|
|
@@ -85,4 +105,5 @@ module.exports = {
|
|
|
85
105
|
init,
|
|
86
106
|
initSync,
|
|
87
107
|
searchOne,
|
|
108
|
+
searchTwo,
|
|
88
109
|
};
|
package/browser.mjs
CHANGED
|
@@ -9,6 +9,7 @@ import { fileURLToPath } from "node:url";
|
|
|
9
9
|
import initWasm, {
|
|
10
10
|
initSync as wasmInitSync,
|
|
11
11
|
searchOne as wasmSearchOne,
|
|
12
|
+
searchTwo as wasmSearchTwo,
|
|
12
13
|
} from "./wasm/pkg/ashwa_wasm.js";
|
|
13
14
|
|
|
14
15
|
let isInitialized = false;
|
|
@@ -36,15 +37,17 @@ export async function init(moduleOrPath) {
|
|
|
36
37
|
process.versions.node != null
|
|
37
38
|
) {
|
|
38
39
|
const wasmPath = fileURLToPath(
|
|
39
|
-
new URL("./wasm/pkg/ashwa_wasm_bg.wasm", import.meta.url)
|
|
40
|
+
new URL("./wasm/pkg/ashwa_wasm_bg.wasm", import.meta.url),
|
|
40
41
|
);
|
|
41
42
|
input = fs.readFileSync(wasmPath);
|
|
42
43
|
}
|
|
44
|
+
|
|
43
45
|
const options = input ? { module_or_path: input } : undefined;
|
|
44
46
|
initPromise = initWasm(options).then(() => {
|
|
45
47
|
isInitialized = true;
|
|
46
48
|
});
|
|
47
49
|
}
|
|
50
|
+
|
|
48
51
|
await initPromise;
|
|
49
52
|
}
|
|
50
53
|
|
|
@@ -62,10 +65,12 @@ export function initSync(bytesOrModule) {
|
|
|
62
65
|
process.versions.node != null
|
|
63
66
|
) {
|
|
64
67
|
const wasmPath = fileURLToPath(
|
|
65
|
-
new URL("./wasm/pkg/ashwa_wasm_bg.wasm", import.meta.url)
|
|
68
|
+
new URL("./wasm/pkg/ashwa_wasm_bg.wasm", import.meta.url),
|
|
66
69
|
);
|
|
70
|
+
|
|
67
71
|
input = fs.readFileSync(wasmPath);
|
|
68
72
|
}
|
|
73
|
+
|
|
69
74
|
wasmInitSync(input);
|
|
70
75
|
isInitialized = true;
|
|
71
76
|
}
|
|
@@ -82,6 +87,29 @@ export async function searchOne(haystack, needle) {
|
|
|
82
87
|
if (!isInitialized) {
|
|
83
88
|
await init();
|
|
84
89
|
}
|
|
90
|
+
|
|
85
91
|
const res = wasmSearchOne(haystack, needle);
|
|
86
92
|
return res !== undefined && res !== null ? Number(res) : null;
|
|
87
93
|
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Searches for the first occurrence of a two-byte `needle` in `haystack` via WebAssembly SIMD.
|
|
97
|
+
* Automatically initializes WASM if not already loaded.
|
|
98
|
+
*
|
|
99
|
+
* @param {Uint8Array} haystack - Byte array to search.
|
|
100
|
+
* @param {Uint8Array|number[]} needle - 2-byte sequence to locate.
|
|
101
|
+
* @returns {Promise<number|null>} Resolves to 0-based matching index or null if not found.
|
|
102
|
+
*/
|
|
103
|
+
export async function searchTwo(haystack, needle) {
|
|
104
|
+
const n = Array.isArray(needle) ? new Uint8Array(needle) : needle;
|
|
105
|
+
if (n == null || n.length !== 2) {
|
|
106
|
+
throw new TypeError("needle must be a 2-byte sequence");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (!isInitialized) {
|
|
110
|
+
await init();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const res = wasmSearchTwo(haystack, n);
|
|
114
|
+
return res !== undefined && res !== null ? Number(res) : null;
|
|
115
|
+
}
|
package/index.d.ts
CHANGED
|
@@ -26,7 +26,33 @@
|
|
|
26
26
|
*/
|
|
27
27
|
export function searchOne(
|
|
28
28
|
haystack: Uint8Array,
|
|
29
|
-
needle: number
|
|
29
|
+
needle: number,
|
|
30
|
+
): number | null | Promise<number | null>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Searches for the first occurrence of a two-byte `needle` within `haystack` (Uint8Array / Buffer).
|
|
34
|
+
*
|
|
35
|
+
* Execution backend:
|
|
36
|
+
* - **Node.js / Bun / Deno**: Executes synchronously via N-API native bindings with SIMD vectorization (AVX-512BW, AVX2, SSE4.2, SSSE3, SSE2, ARM NEON).
|
|
37
|
+
* - **Browser / WebWorker**: Executes via WebAssembly SIMD (`simd128`), automatically initializing the WASM module on first invocation if not already loaded.
|
|
38
|
+
*
|
|
39
|
+
* @param haystack - The byte array/buffer to search.
|
|
40
|
+
* @param needle - A 2-byte sequence (`Uint8Array`, `Buffer`, or `[number, number]` / `number[]`) to locate.
|
|
41
|
+
* @returns The 0-based byte index of the first occurrence of `needle`, or `null` if not found.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```javascript
|
|
45
|
+
* import { searchTwo } from '@pid7/ashwa';
|
|
46
|
+
*
|
|
47
|
+
* const haystack = new TextEncoder().encode("Hello, World!");
|
|
48
|
+
* const needle = new Uint8Array(["W".charCodeAt(0), "o".charCodeAt(0)]);
|
|
49
|
+
* const index = await searchTwo(haystack, needle);
|
|
50
|
+
* console.log(index); // 7
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function searchTwo(
|
|
54
|
+
haystack: Uint8Array,
|
|
55
|
+
needle: Uint8Array | [number, number] | number[],
|
|
30
56
|
): number | null | Promise<number | null>;
|
|
31
57
|
|
|
32
58
|
/**
|
package/index.js
CHANGED
|
@@ -41,6 +41,23 @@ if (isNode) {
|
|
|
41
41
|
const res = native.searchOne(haystack, needle);
|
|
42
42
|
return res !== undefined && res !== null ? Number(res) : null;
|
|
43
43
|
},
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Searches for the first occurrence of a two-byte `needle` in `haystack`.
|
|
47
|
+
*
|
|
48
|
+
* @param {Uint8Array} haystack - Byte array to search.
|
|
49
|
+
* @param {Uint8Array|number[]} needle - 2-byte sequence to locate.
|
|
50
|
+
* @returns {number|null} 0-based index or null if not found.
|
|
51
|
+
*/
|
|
52
|
+
searchTwo(haystack, needle) {
|
|
53
|
+
const n = Array.isArray(needle) ? new Uint8Array(needle) : needle;
|
|
54
|
+
if (n == null || n.length !== 2) {
|
|
55
|
+
throw new TypeError("needle must be a 2-byte sequence");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const res = native.searchTwo(haystack, n);
|
|
59
|
+
return res !== undefined && res !== null ? Number(res) : null;
|
|
60
|
+
},
|
|
44
61
|
};
|
|
45
62
|
} else {
|
|
46
63
|
module.exports = require("./browser.js");
|
package/index.mjs
CHANGED
|
@@ -36,3 +36,20 @@ export function searchOne(haystack, needle) {
|
|
|
36
36
|
const res = native.searchOne(haystack, needle);
|
|
37
37
|
return res !== undefined && res !== null ? Number(res) : null;
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Searches for the first occurrence of a two-byte `needle` in `haystack`.
|
|
42
|
+
*
|
|
43
|
+
* @param {Uint8Array} haystack - Byte array to search.
|
|
44
|
+
* @param {Uint8Array|number[]} needle - 2-byte sequence to locate.
|
|
45
|
+
* @returns {number|null} 0-based index or null if not found.
|
|
46
|
+
*/
|
|
47
|
+
export function searchTwo(haystack, needle) {
|
|
48
|
+
const n = Array.isArray(needle) ? new Uint8Array(needle) : needle;
|
|
49
|
+
if (n == null || n.length !== 2) {
|
|
50
|
+
throw new TypeError("needle must be a 2-byte sequence");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const res = native.searchTwo(haystack, n);
|
|
54
|
+
return res !== undefined && res !== null ? Number(res) : null;
|
|
55
|
+
}
|
package/native/index.d.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
1
|
/* auto-generated by NAPI-RS */
|
|
2
2
|
/* eslint-disable */
|
|
3
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Searches for the first occurrence of `needle` in `haystack`.
|
|
5
|
+
*
|
|
6
|
+
* # Arguments
|
|
7
|
+
* * `haystack` - A byte slice (`&[u8]`) to search within.
|
|
8
|
+
* * `needle` - The byte (`u8`) to find.
|
|
9
|
+
*
|
|
10
|
+
* # Returns
|
|
11
|
+
* * `Some(index)` - The 0-based byte index of the first match.
|
|
12
|
+
* * `None` - If `needle` is not found.
|
|
13
|
+
*/
|
|
14
|
+
export declare function searchOne(
|
|
15
|
+
haystack: Uint8Array,
|
|
16
|
+
needle: number,
|
|
17
|
+
): number | null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Searches for the first occurrence of a two-byte `needle` in `haystack`.
|
|
21
|
+
*
|
|
22
|
+
* # Arguments
|
|
23
|
+
* * `haystack` - A byte slice (`&[u8]`) to search within.
|
|
24
|
+
* * `needle` - A 2-byte slice (`&[u8]`) to locate.
|
|
25
|
+
*
|
|
26
|
+
* # Returns
|
|
27
|
+
* * `Some(index)` - The 0-based byte index of the first match.
|
|
28
|
+
* * `None` - If `needle` is not found.
|
|
29
|
+
*/
|
|
30
|
+
export declare function searchTwo(
|
|
31
|
+
haystack: Uint8Array,
|
|
32
|
+
needle: Uint8Array,
|
|
33
|
+
): number | null;
|
|
Binary file
|