castrum 0.7.0 → 0.9.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/LICENSE +21 -0
- package/README.md +322 -59
- package/castrum.linux-x64-gnu.node +0 -0
- package/dist/index.d.ts +3161 -0
- package/dist/index.js +7306 -0
- package/docs/ARCHITECTURE.md +415 -0
- package/docs/BENCHMARKS.md +307 -0
- package/docs/ENVIRONMENT.md +79 -0
- package/docs/GETTING_STARTED.md +209 -0
- package/docs/INGRESS.md +745 -0
- package/docs/REPO_MAP.md +259 -0
- package/docs/adr/0001-two-wire-formats.md +39 -0
- package/docs/adr/0002-zero-dom-fast-schema.md +38 -0
- package/docs/adr/0003-bun-builtins-delegation.md +42 -0
- package/docs/bun-builtins-decision-matrix.md +108 -0
- package/index.ts +51 -8
- package/package.json +73 -20
- package/src/data/json-rows.ts +13 -13
- package/src/ingress/body.ts +149 -0
- package/src/ingress/constants.ts +220 -48
- package/src/ingress/context.ts +136 -0
- package/src/ingress/decode/baked-result.ts +121 -0
- package/src/ingress/decode/fast-result.ts +160 -0
- package/src/ingress/decode/packed-sections.ts +56 -0
- package/src/ingress/decode/result-base.ts +172 -0
- package/src/ingress/errors.ts +72 -0
- package/src/ingress/fast.ts +152 -1021
- package/src/ingress/handlers.ts +563 -1001
- package/src/ingress/headers/baked-templates.ts +102 -0
- package/src/ingress/headers/cors.ts +44 -0
- package/src/ingress/headers/fast-templates.ts +238 -0
- package/src/ingress/headers/hsts.ts +83 -0
- package/src/ingress/health.ts +67 -0
- package/src/ingress/index.ts +130 -405
- package/src/ingress/metrics.ts +132 -0
- package/src/ingress/options.ts +197 -0
- package/src/ingress/packing/gather-raw-headers.ts +75 -0
- package/src/ingress/packing/header-packing.ts +75 -0
- package/src/ingress/packing/input-packer.ts +150 -0
- package/src/ingress/packing/scratch.ts +50 -0
- package/src/ingress/packing/select-headers.ts +96 -0
- package/src/ingress/response/error-bodies.ts +75 -0
- package/src/ingress/response/terminal.ts +59 -0
- package/src/ingress/routes/common.ts +38 -0
- package/src/ingress/routes/delete.ts +14 -0
- package/src/ingress/routes/echo.ts +107 -0
- package/src/ingress/routes/fallback.ts +23 -0
- package/src/ingress/routes/head.ts +37 -0
- package/src/ingress/routes/index.ts +10 -0
- package/src/ingress/routes/json-write.ts +153 -0
- package/src/ingress/routes/options.ts +36 -0
- package/src/ingress/routes/read.ts +45 -0
- package/src/ingress/server-node.ts +376 -0
- package/src/ingress/server.ts +443 -0
- package/src/ingress/shared.ts +132 -0
- package/src/ingress/status.ts +97 -0
- package/src/ingress/types.ts +157 -0
- package/src/integration/batch.ts +58 -0
- package/src/integration/index.ts +29 -0
- package/src/integration/pipeline.ts +217 -0
- package/src/integration/streaming.ts +79 -0
- package/src/integration/websocket.ts +92 -0
- package/src/loader/batch.ts +133 -0
- package/src/loader/cost.ts +104 -0
- package/src/loader/index.ts +930 -0
- package/src/loader/ops.ts +657 -0
- package/src/native/ffi.ts +1541 -0
- package/src/native/index.ts +8 -207
- package/src/native/loader.ts +194 -0
- package/src/native/types.ts +598 -0
- package/src/rust-ffi/addon.ts +10 -0
- package/src/rust-ffi/batch.ts +431 -0
- package/src/rust-ffi/client.ts +88 -0
- package/src/rust-ffi/context.ts +164 -0
- package/src/rust-ffi/index.ts +40 -541
- package/src/rust-ffi/options.ts +113 -0
- package/src/rust-ffi/packed.ts +253 -0
- package/src/rust-ffi/proven.ts +42 -0
- package/src/rust-ffi/scalar/crypto.ts +265 -0
- package/src/rust-ffi/scalar/factories.ts +233 -0
- package/src/rust-ffi/scalar/hashing.ts +72 -0
- package/src/rust-ffi/scalar/http.ts +206 -0
- package/src/rust-ffi/scalar/index.ts +31 -0
- package/src/rust-ffi/scalar/interface.ts +450 -0
- package/src/rust-ffi/scalar/json.ts +45 -0
- package/src/rust-ffi/scalar/payload.ts +124 -0
- package/src/rust-ffi/text.ts +95 -0
- package/src/selection.json +194 -0
- package/src/selection.ts +77 -0
- package/src/shared/adaptive.ts +68 -0
- package/src/shared/bench-classify.ts +209 -0
- package/src/shared/buffer-pool.ts +209 -0
- package/src/shared/bytes.ts +35 -5
- package/src/shared/env.ts +20 -0
- package/src/shared/log.ts +74 -0
- package/src/shared/metrics.ts +247 -0
- package/src/shared/packed.ts +350 -154
- package/src/shared/proven.ts +596 -0
- package/src/shared/request-id.ts +67 -0
- package/src/shared/response.ts +104 -0
- package/src/shared/runtime.ts +34 -0
- package/src/shared/trace.ts +81 -0
- package/src/shared/uuid.ts +31 -0
- package/Cargo.toml +0 -73
- package/bench.ts +0 -3
- package/rust/batch.rs +0 -195
- package/rust/cookie_parser.rs +0 -70
- package/rust/cors.rs +0 -263
- package/rust/hashing.rs +0 -164
- package/rust/headers.rs +0 -234
- package/rust/hmac_sha256.rs +0 -113
- package/rust/http_parser.rs +0 -132
- package/rust/ingress.rs +0 -616
- package/rust/ingress_constants.rs +0 -89
- package/rust/ip_trust.rs +0 -185
- package/rust/json_ops.rs +0 -66
- package/rust/json_patch_ops.rs +0 -25
- package/rust/json_schema.rs +0 -151
- package/rust/json_ser.rs +0 -429
- package/rust/lib.rs +0 -48
- package/rust/method.rs +0 -141
- package/rust/mime_lookup.rs +0 -76
- package/rust/output.rs +0 -238
- package/rust/proxy.rs +0 -90
- package/rust/query_parser.rs +0 -141
- package/rust/random_token.rs +0 -52
- package/rust/rate_limit.rs +0 -203
- package/rust/terminal.rs +0 -95
- package/rust/unit_tests.rs +0 -843
- package/rust/url_codec.rs +0 -133
- package/rust/util.rs +0 -537
- package/rust/validation.rs +0 -107
- package/rust/websocket.rs +0 -23
- package/src/baseline/index.ts +0 -12
- package/src/baseline/tasks/cookie.ts +0 -22
- package/src/baseline/tasks/hashing.ts +0 -18
- package/src/baseline/tasks/hmac.ts +0 -38
- package/src/baseline/tasks/http.ts +0 -62
- package/src/baseline/tasks/json-patch.ts +0 -12
- package/src/baseline/tasks/json.ts +0 -50
- package/src/baseline/tasks/mime.ts +0 -5
- package/src/baseline/tasks/query.ts +0 -14
- package/src/baseline/tasks/token.ts +0 -15
- package/src/baseline/tasks/url.ts +0 -11
- package/src/baseline/tasks/validation.ts +0 -19
- package/src/baseline/tasks/websocket.ts +0 -14
- package/src/bench/assert.ts +0 -70
- package/src/bench/checks.ts +0 -227
- package/src/bench/checksum.ts +0 -27
- package/src/bench/comparisons.ts +0 -70
- package/src/bench/concurrent-worker.ts +0 -135
- package/src/bench/fixtures.ts +0 -233
- package/src/bench/index.ts +0 -2
- package/src/bench/measure.ts +0 -177
- package/src/bench/now.ts +0 -3
- package/src/bench/report.ts +0 -79
- package/src/bench/run.ts +0 -57
- package/src/bench/tasks/complex.ts +0 -232
- package/src/bench/tasks/concurrent.ts +0 -213
- package/src/bench/tasks/cookie.ts +0 -44
- package/src/bench/tasks/hashing.ts +0 -33
- package/src/bench/tasks/hmac.ts +0 -36
- package/src/bench/tasks/http.ts +0 -50
- package/src/bench/tasks/index.ts +0 -52
- package/src/bench/tasks/json-patch.ts +0 -21
- package/src/bench/tasks/json.ts +0 -33
- package/src/bench/tasks/mime.ts +0 -21
- package/src/bench/tasks/query.ts +0 -44
- package/src/bench/tasks/stress.ts +0 -142
- package/src/bench/tasks/token.ts +0 -20
- package/src/bench/tasks/url.ts +0 -34
- package/src/bench/tasks/validation.ts +0 -57
- package/src/bench/tasks/websocket.ts +0 -21
- package/src/bench/types.ts +0 -60
- package/src/ingress/packed-input.ts +0 -59
- package/src/shared/json.ts +0 -18
|
@@ -0,0 +1,596 @@
|
|
|
1
|
+
// src/shared/proven.ts — Performance-proven export registry (SINGLE SOURCE OF TRUTH).
|
|
2
|
+
//
|
|
3
|
+
// Every public `rust.<name>` function is classified by how it performs against
|
|
4
|
+
// its JavaScript baseline in the CPU benchmark (`bun run check`, which writes
|
|
5
|
+
// `bench/results/cpu/latest.json`):
|
|
6
|
+
//
|
|
7
|
+
// - "proven" Rust consistently wins in release/perf builds.
|
|
8
|
+
// - "parity" Rust is within ~±15% of the JS baseline (no clear win).
|
|
9
|
+
// - "not-competitive" Rust loses to the JS baseline (e.g. DOM + napi
|
|
10
|
+
// marshaling costs, or a highly optimized host built-in
|
|
11
|
+
// like Bun's JSON.parse).
|
|
12
|
+
// - "unmeasured" No direct comparison task exists (infra/config only).
|
|
13
|
+
//
|
|
14
|
+
// This module is PURE DATA — it must NOT import the addon or the `rust` client,
|
|
15
|
+
// so the audit script (`scripts/check-proven.ts`) can load it without dlopening
|
|
16
|
+
// the native addon.
|
|
17
|
+
|
|
18
|
+
export type PerformanceStatus = 'proven' | 'parity' | 'not-competitive' | 'unmeasured'
|
|
19
|
+
|
|
20
|
+
export interface ProvenEntry {
|
|
21
|
+
/** Public `rust.<name>` method key. */
|
|
22
|
+
name: string
|
|
23
|
+
/** Benchmark comparison label (see src/bench/comparisons.ts). */
|
|
24
|
+
label: string
|
|
25
|
+
/** Baseline task name in the CPU report. */
|
|
26
|
+
nativeTask: string
|
|
27
|
+
/** Rust task name in the CPU report. */
|
|
28
|
+
rustTask: string
|
|
29
|
+
status: PerformanceStatus
|
|
30
|
+
/** Typical rust-vs-baseline ratio from release/perf builds (rust faster = >1). */
|
|
31
|
+
typicalRatio?: number
|
|
32
|
+
/** Why this classification. */
|
|
33
|
+
note?: string
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Curated registry. Classifications are based on release/perf-build results
|
|
38
|
+
* (docs + prior runs); the audit script re-checks them against the latest
|
|
39
|
+
* report. Borderline wins are kept as "parity" so the `proven` surface stays
|
|
40
|
+
* honest.
|
|
41
|
+
*
|
|
42
|
+
* `as const satisfies` keeps the literal `name`/`status` types so the
|
|
43
|
+
* `ProvenKey` type (below) can be derived at the type level.
|
|
44
|
+
*/
|
|
45
|
+
export const PROVEN_SURFACE = [
|
|
46
|
+
// ── Clear wins (Rust > baseline) ──
|
|
47
|
+
{
|
|
48
|
+
name: 'fnv1a64',
|
|
49
|
+
label: 'FNV-1a 64',
|
|
50
|
+
nativeTask: 'native:fnv1a64',
|
|
51
|
+
rustTask: 'rust:fnv1a64',
|
|
52
|
+
status: 'proven',
|
|
53
|
+
typicalRatio: 11,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'jsonValid',
|
|
57
|
+
label: 'JSON valid',
|
|
58
|
+
nativeTask: 'native:json_valid',
|
|
59
|
+
rustTask: 'rust:json_valid',
|
|
60
|
+
status: 'proven',
|
|
61
|
+
typicalRatio: 3,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'jsonSumIds',
|
|
65
|
+
label: 'JSON sum',
|
|
66
|
+
nativeTask: 'native:json_sum',
|
|
67
|
+
rustTask: 'rust:json_sum',
|
|
68
|
+
status: 'proven',
|
|
69
|
+
typicalRatio: 3,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'httpParseRequestPacked',
|
|
73
|
+
label: 'HTTP parse',
|
|
74
|
+
nativeTask: 'native:http_parse',
|
|
75
|
+
rustTask: 'rust:http_parse',
|
|
76
|
+
status: 'proven',
|
|
77
|
+
typicalRatio: 4,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'queryParsePacked',
|
|
81
|
+
label: 'Query parse',
|
|
82
|
+
nativeTask: 'native:query_parse',
|
|
83
|
+
rustTask: 'rust:query_parse',
|
|
84
|
+
status: 'proven',
|
|
85
|
+
typicalRatio: 4,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: 'cookieParsePacked',
|
|
89
|
+
label: 'Cookie parse',
|
|
90
|
+
nativeTask: 'native:cookie_parse',
|
|
91
|
+
rustTask: 'rust:cookie_parse',
|
|
92
|
+
status: 'proven',
|
|
93
|
+
typicalRatio: 2.5,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'wsAcceptKey',
|
|
97
|
+
label: 'WebSocket accept',
|
|
98
|
+
nativeTask: 'native:ws_accept_key',
|
|
99
|
+
rustTask: 'rust:ws_accept_key',
|
|
100
|
+
status: 'proven',
|
|
101
|
+
typicalRatio: 1.6,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'jsonPatch',
|
|
105
|
+
label: 'JSON Patch',
|
|
106
|
+
nativeTask: 'native:json_patch',
|
|
107
|
+
rustTask: 'rust:json_patch',
|
|
108
|
+
status: 'proven',
|
|
109
|
+
typicalRatio: 1.2,
|
|
110
|
+
note: 'Modest but real win; swings ~0.9-1.5x run-to-run (DOM marshal dominated).',
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
name: 'hmacSha256',
|
|
114
|
+
label: 'HMAC sign',
|
|
115
|
+
nativeTask: 'native:hmac_sha256',
|
|
116
|
+
rustTask: 'rust:hmac_sha256',
|
|
117
|
+
status: 'proven',
|
|
118
|
+
typicalRatio: 1.5,
|
|
119
|
+
note: 'Classification vs the node:crypto baseline. Under Bun the public rust.hmacSha256 delegates to Bun.CryptoHasher (BUN_WINS — hex re-encoded to the same contract); this row measures the raw addon (see src/bench/raw-native.ts).',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'hmacSha256Verify',
|
|
123
|
+
label: 'HMAC verify',
|
|
124
|
+
nativeTask: 'native:hmac_verify',
|
|
125
|
+
rustTask: 'rust:hmac_verify',
|
|
126
|
+
status: 'proven',
|
|
127
|
+
typicalRatio: 2.4,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'validateEmail',
|
|
131
|
+
label: 'Email validation',
|
|
132
|
+
nativeTask: 'native:validate_email',
|
|
133
|
+
rustTask: 'rust:validate_email',
|
|
134
|
+
status: 'proven',
|
|
135
|
+
typicalRatio: 3,
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'validateIpv6',
|
|
139
|
+
label: 'IPv6 validation',
|
|
140
|
+
nativeTask: 'native:validate_ipv6',
|
|
141
|
+
rustTask: 'rust:validate_ipv6',
|
|
142
|
+
status: 'proven',
|
|
143
|
+
typicalRatio: 1.3,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
name: 'urlEncode',
|
|
147
|
+
label: 'URL encode',
|
|
148
|
+
nativeTask: 'native:url_encode',
|
|
149
|
+
rustTask: 'rust:url_encode',
|
|
150
|
+
status: 'not-competitive',
|
|
151
|
+
note: 'Public rust.urlEncode delegates to encodeURIComponent under Bun (BUN_WINS — ~3-4x faster than the FFI crossing; skips the C-ABI call). The rust: column measures the RAW addon (src/bench/raw-native.ts): the small-input FFI crossing loses (~0.58x), but LARGE inputs win (rust:url_encode_large ~1.45x) — the honest report no longer self-compares the built-in. Rust addon remains the Node path. Byte parity pinned by test/unit/features/url.test.ts + test/unit/features/wiring.test.ts.',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: 'urlDecode',
|
|
155
|
+
label: 'URL decode',
|
|
156
|
+
nativeTask: 'native:url_decode',
|
|
157
|
+
rustTask: 'rust:url_decode',
|
|
158
|
+
status: 'not-competitive',
|
|
159
|
+
note: 'Public rust.urlDecode delegates to decodeURIComponent under Bun (BUN_WINS — ~4-8x faster than the FFI crossing; strict UTF-8 semantics match: both throw on malformed/invalid). The rust: column measures the RAW addon (src/bench/raw-native.ts): the small-input FFI crossing loses (~0.55x), but LARGE inputs win (rust:url_decode_large ~3.45x) — the honest report no longer self-compares the built-in. Rust addon remains the Node path and the raw-bytes urlDecodeBytes stays native. Parity pinned by test/unit/features/url.test.ts + test/unit/features/wiring.test.ts.',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: 'jwtVerify',
|
|
163
|
+
label: 'JWT verify',
|
|
164
|
+
nativeTask: 'native:jwt_verify',
|
|
165
|
+
rustTask: 'rust:jwt_verify',
|
|
166
|
+
status: 'proven',
|
|
167
|
+
typicalRatio: 1.4,
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
name: 'passwordHash',
|
|
171
|
+
label: 'Password hash',
|
|
172
|
+
nativeTask: 'native:password_hash',
|
|
173
|
+
rustTask: 'rust:password_hash',
|
|
174
|
+
status: 'proven',
|
|
175
|
+
typicalRatio: 19,
|
|
176
|
+
note: 'argon2id vs node:crypto scrypt.',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
name: 'aeadEncrypt',
|
|
180
|
+
label: 'AEAD encrypt',
|
|
181
|
+
nativeTask: 'native:aead_encrypt',
|
|
182
|
+
rustTask: 'rust:aead_encrypt',
|
|
183
|
+
status: 'proven',
|
|
184
|
+
typicalRatio: 1.6,
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
name: 'aeadDecrypt',
|
|
188
|
+
label: 'AEAD decrypt',
|
|
189
|
+
nativeTask: 'native:aead_decrypt',
|
|
190
|
+
rustTask: 'rust:aead_decrypt',
|
|
191
|
+
status: 'proven',
|
|
192
|
+
typicalRatio: 2,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'gzipDecompress',
|
|
196
|
+
label: 'Gzip decompress',
|
|
197
|
+
nativeTask: 'native:gzip_decompress',
|
|
198
|
+
rustTask: 'rust:gzip_decompress',
|
|
199
|
+
status: 'proven',
|
|
200
|
+
typicalRatio: 1.4,
|
|
201
|
+
note: "zlib-rs vs node zlib; swings 1.0-1.4x run-to-run. Deliberately NOT delegated to Bun.gunzipSync under Bun (NOT in BUN_WINS): Bun's decompressor has no output-size bound, and the native path keeps the 64 MiB decompression-bomb cap.",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'brotliDecompress',
|
|
205
|
+
label: 'Brotli decompress',
|
|
206
|
+
nativeTask: 'native:brotli_decompress',
|
|
207
|
+
rustTask: 'rust:brotli_decompress',
|
|
208
|
+
status: 'proven',
|
|
209
|
+
typicalRatio: 1.9,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: 'multipartParse',
|
|
213
|
+
label: 'Multipart parse',
|
|
214
|
+
nativeTask: 'native:multipart_parse',
|
|
215
|
+
rustTask: 'rust:multipart_parse',
|
|
216
|
+
status: 'proven',
|
|
217
|
+
typicalRatio: 2.3,
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
name: 'wsFrameEncode',
|
|
221
|
+
label: 'WS frame encode',
|
|
222
|
+
nativeTask: 'native:ws_frame_encode',
|
|
223
|
+
rustTask: 'rust:ws_frame_encode',
|
|
224
|
+
status: 'proven',
|
|
225
|
+
typicalRatio: 1.8,
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'wsFrameDecode',
|
|
229
|
+
label: 'WS frame decode',
|
|
230
|
+
nativeTask: 'native:ws_frame_decode',
|
|
231
|
+
rustTask: 'rust:ws_frame_decode',
|
|
232
|
+
status: 'proven',
|
|
233
|
+
typicalRatio: 2,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
name: 'createSchemaValidator',
|
|
237
|
+
label: 'JSON schema validate',
|
|
238
|
+
nativeTask: 'native:json_schema_validate',
|
|
239
|
+
rustTask: 'rust:json_schema_validate',
|
|
240
|
+
status: 'proven',
|
|
241
|
+
note: 'Zero-DOM fast path validates raw bytes for the common keyword subset (scalar ~1.2-2.4x, batch ~1.0-1.56x vs ajv); unsupported keywords fall back to the jsonschema crate.',
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
// ── Parity (no clear win) ──
|
|
245
|
+
{
|
|
246
|
+
name: 'crc32',
|
|
247
|
+
label: 'CRC32',
|
|
248
|
+
nativeTask: 'native:crc32',
|
|
249
|
+
rustTask: 'rust:crc32',
|
|
250
|
+
status: 'not-competitive',
|
|
251
|
+
note: 'Raw addon vs the same-engine JS crc-32 loop (~0.23-0.44x across runs). Under Bun the public rust.crc32 delegates to Bun.hash.crc32 (BUN_WINS, ~2.8-8.4x) — this row measures the raw addon (src/bench/raw-native.ts).',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
name: 'xxh3',
|
|
255
|
+
label: 'XXH3-64',
|
|
256
|
+
nativeTask: 'diag:bun_hash_xxh3',
|
|
257
|
+
rustTask: 'diag:xxh3',
|
|
258
|
+
status: 'not-competitive',
|
|
259
|
+
note: 'Bun.hash.xxHash3 wins ~4x (in-process C++ beats the FFI crossing). The public rust.xxh3 now DELEGATES to Bun.hash.xxHash3 under Bun (BUN_WINS); the addon remains the Node/non-Bun fast path. See docs/bun-builtins-decision-matrix.md.',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
name: 'randomToken',
|
|
263
|
+
label: 'Random token',
|
|
264
|
+
nativeTask: 'native:random_token',
|
|
265
|
+
rustTask: 'rust:random_token',
|
|
266
|
+
status: 'parity',
|
|
267
|
+
typicalRatio: 1.1,
|
|
268
|
+
note: 'Raw addon vs crypto.getRandomValues baseline. Under Bun the public rust.randomToken delegates to crypto.getRandomValues + native hex (BUN_WINS, same 2n-hex format + 16 MiB guard) — this row measures the raw addon (src/bench/raw-native.ts).',
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
name: 'validateUuid',
|
|
272
|
+
label: 'UUID validation',
|
|
273
|
+
nativeTask: 'native:validate_uuid',
|
|
274
|
+
rustTask: 'rust:validate_uuid',
|
|
275
|
+
status: 'parity',
|
|
276
|
+
typicalRatio: 1,
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
name: 'validateIpv4',
|
|
280
|
+
label: 'IPv4 validation',
|
|
281
|
+
nativeTask: 'native:validate_ipv4',
|
|
282
|
+
rustTask: 'rust:validate_ipv4',
|
|
283
|
+
status: 'parity',
|
|
284
|
+
typicalRatio: 1.05,
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: 'mimeFromExtension',
|
|
288
|
+
label: 'MIME lookup',
|
|
289
|
+
nativeTask: 'native:mime',
|
|
290
|
+
rustTask: 'rust:mime',
|
|
291
|
+
status: 'parity',
|
|
292
|
+
typicalRatio: 1.4,
|
|
293
|
+
note: 'Consistently ~1.38-1.44x rust after the 2026-08-12 marshal reduction (cachedMime no longer per-call slices; text.mimeFromExtension memoizes the decoded string). phf table lookup.',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'gzipCompress',
|
|
297
|
+
label: 'Gzip compress',
|
|
298
|
+
nativeTask: 'native:gzip_compress',
|
|
299
|
+
rustTask: 'rust:gzip_compress',
|
|
300
|
+
status: 'parity',
|
|
301
|
+
typicalRatio: 1,
|
|
302
|
+
note: 'Raw addon vs node zlib baseline. Under Bun the public rust.gzipCompress delegates to Bun.gzipSync (BUN_WINS, ~2x; decompression-parity with the addon — only the header OS byte differs) — this row measures the raw addon (src/bench/raw-native.ts).',
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
name: 'sseEncodeEvent',
|
|
306
|
+
label: 'SSE encode',
|
|
307
|
+
nativeTask: 'native:sse_encode',
|
|
308
|
+
rustTask: 'rust:sse_encode',
|
|
309
|
+
status: 'parity',
|
|
310
|
+
typicalRatio: 1.1,
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
// ── New primitives (2026-08-11; measured vs Bun built-ins / node:crypto) ──
|
|
314
|
+
{
|
|
315
|
+
name: 'passwordHashBcrypt',
|
|
316
|
+
label: 'Password hash (bcrypt)',
|
|
317
|
+
nativeTask: 'diag:bun_password_bcrypt_hash',
|
|
318
|
+
rustTask: 'diag:bcrypt_hash',
|
|
319
|
+
status: 'parity',
|
|
320
|
+
note: 'Bun.password.bcrypt ~1.24x faster at cost 10 (noisy KDF); rust is self-contained and non-Bun. See docs/bun-builtins-decision-matrix.md.',
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
name: 'passwordVerifyBcrypt',
|
|
324
|
+
label: 'Password verify (bcrypt)',
|
|
325
|
+
nativeTask: 'diag:bun_password_bcrypt_verify',
|
|
326
|
+
rustTask: 'diag:bcrypt_verify',
|
|
327
|
+
status: 'parity',
|
|
328
|
+
note: 'Rust ~1.49x vs Bun.password.verify at cost 10; KDF timings noisy — parity for gate stability.',
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
name: 'pbkdf2Sha256',
|
|
332
|
+
label: 'PBKDF2-HMAC-SHA256',
|
|
333
|
+
nativeTask: 'diag:pbkdf2_sha256',
|
|
334
|
+
rustTask: 'diag:pbkdf2_sha256_rust',
|
|
335
|
+
status: 'parity',
|
|
336
|
+
note: '~1.08x vs node:crypto pbkdf2Sync at 100k rounds (parity); Bun has no synchronous PBKDF2.',
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
// ── Not competitive (JS baseline wins) ──
|
|
340
|
+
{
|
|
341
|
+
name: 'jsonParse',
|
|
342
|
+
label: 'JSON parse',
|
|
343
|
+
nativeTask: 'native:json_parse',
|
|
344
|
+
rustTask: 'rust:json_parse',
|
|
345
|
+
status: 'not-competitive',
|
|
346
|
+
note: "Bun's JSON.parse beats the native DOM + napi marshaling path (~5x).",
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
name: 'jwtSign',
|
|
350
|
+
label: 'JWT sign',
|
|
351
|
+
nativeTask: 'native:jwt_sign',
|
|
352
|
+
rustTask: 'rust:jwt_sign',
|
|
353
|
+
status: 'not-competitive',
|
|
354
|
+
note: 'Base64 + claims DOM marshal overhead. Use jwtSignBytes (claims as JSON bytes) which skips the napi Value marshal and flips to a ~1.07x win.',
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: 'jwtSignBytes',
|
|
358
|
+
label: 'JWT sign (bytes)',
|
|
359
|
+
nativeTask: 'native:jwt_sign',
|
|
360
|
+
rustTask: 'rust:jwt_sign_bytes',
|
|
361
|
+
status: 'parity',
|
|
362
|
+
note: 'Byte-JSON claims overload avoids the napi serde_json::Value DOM marshal; flips jwtSign from a ~1.3x loss to ~1.07x (parity).',
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
name: 'brotliCompress',
|
|
366
|
+
label: 'Brotli compress',
|
|
367
|
+
nativeTask: 'native:brotli_compress',
|
|
368
|
+
rustTask: 'rust:brotli_compress',
|
|
369
|
+
status: 'not-competitive',
|
|
370
|
+
note: 'The native brotli baseline wins for small inputs.',
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
name: 'templateRender',
|
|
374
|
+
label: 'Template render',
|
|
375
|
+
nativeTask: 'native:template_render',
|
|
376
|
+
rustTask: 'rust:template_render',
|
|
377
|
+
status: 'not-competitive',
|
|
378
|
+
note: 'The allocating render marshals the context via napi DOM (~1.5x loss). Use TemplateRenderer.renderBytes (JSON-bytes context) which avoids it and wins ~1.37x.',
|
|
379
|
+
},
|
|
380
|
+
|
|
381
|
+
// ── Framework actions (2026-08-07; release-build numbers) ──
|
|
382
|
+
{
|
|
383
|
+
name: 'formParsePacked',
|
|
384
|
+
label: 'Form parse',
|
|
385
|
+
nativeTask: 'native:form_parse',
|
|
386
|
+
rustTask: 'rust:form_parse',
|
|
387
|
+
status: 'proven',
|
|
388
|
+
typicalRatio: 5.8,
|
|
389
|
+
note: 'Benchmarked via the FormParser instance (same parser core).',
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
name: 'parseMediaType',
|
|
393
|
+
label: 'Media type parse',
|
|
394
|
+
nativeTask: 'native:media_type_parse',
|
|
395
|
+
rustTask: 'rust:media_type_parse',
|
|
396
|
+
status: 'not-competitive',
|
|
397
|
+
note: 'Consistent ~0.71-0.81x (sub-µs FFI crossing vs the JS baseline); use MediaTypeMatcher (precompiled expected) for the zero-alloc match path.',
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: 'etag',
|
|
401
|
+
label: 'ETag',
|
|
402
|
+
nativeTask: 'native:etag',
|
|
403
|
+
rustTask: 'rust:etag',
|
|
404
|
+
status: 'parity',
|
|
405
|
+
typicalRatio: 1.06,
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: 'httpDate',
|
|
409
|
+
label: 'HTTP date',
|
|
410
|
+
nativeTask: 'native:http_date',
|
|
411
|
+
rustTask: 'rust:http_date',
|
|
412
|
+
status: 'not-competitive',
|
|
413
|
+
note: 'Delegates to Date.toUTCString() under Bun (BUN_WINS — ~3.7x faster than the FFI crossing; byte-identical RFC 1123 for HTTP timestamps, incl. leap years/epoch). Rust addon remains the Node path; pooled httpDateInto stays native.',
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: 'createConditionalRequest',
|
|
417
|
+
label: 'Conditional request',
|
|
418
|
+
nativeTask: 'native:conditional',
|
|
419
|
+
rustTask: 'rust:conditional',
|
|
420
|
+
status: 'proven',
|
|
421
|
+
typicalRatio: 1.4,
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: 'createAcceptNegotiator',
|
|
425
|
+
label: 'Accept-Encoding negotiate',
|
|
426
|
+
nativeTask: 'native:accept_negotiate',
|
|
427
|
+
rustTask: 'rust:accept_negotiate',
|
|
428
|
+
status: 'proven',
|
|
429
|
+
typicalRatio: 4,
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
name: 'base64Encode',
|
|
433
|
+
label: 'Base64 encode',
|
|
434
|
+
nativeTask: 'native:base64_encode',
|
|
435
|
+
rustTask: 'rust:base64_encode',
|
|
436
|
+
status: 'not-competitive',
|
|
437
|
+
note: 'Delegates to Buffer base64 under Bun for the standard padded, non-url-safe case (BUN_WINS — ~2x faster than the FFI crossing); url-safe/unpadded falls through to native. Rust addon remains the Node path. Parity pinned by test/unit/features/encoding.test.ts.',
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'base64Decode',
|
|
441
|
+
label: 'Base64 decode',
|
|
442
|
+
nativeTask: 'native:base64_decode',
|
|
443
|
+
rustTask: 'rust:base64_decode',
|
|
444
|
+
status: 'parity',
|
|
445
|
+
typicalRatio: 1.17,
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
name: 'hexEncode',
|
|
449
|
+
label: 'Hex encode',
|
|
450
|
+
nativeTask: 'native:hex_encode',
|
|
451
|
+
rustTask: 'rust:hex_encode',
|
|
452
|
+
status: 'not-competitive',
|
|
453
|
+
note: "Buffer.toString('hex') is native SIMD; pooled hexEncodeInto cuts the loss from ~2.2x to ~1.35x.",
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
name: 'hexDecode',
|
|
457
|
+
label: 'Hex decode',
|
|
458
|
+
nativeTask: 'native:hex_decode',
|
|
459
|
+
rustTask: 'rust:hex_decode',
|
|
460
|
+
status: 'parity',
|
|
461
|
+
typicalRatio: 0.78,
|
|
462
|
+
note: 'Slightly slower than Buffer hex decode (~1.3x).',
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: 'signCookie',
|
|
466
|
+
label: 'Cookie sign',
|
|
467
|
+
nativeTask: 'native:cookie_sign',
|
|
468
|
+
rustTask: 'rust:cookie_sign',
|
|
469
|
+
status: 'proven',
|
|
470
|
+
typicalRatio: 9,
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
name: 'verifyCookie',
|
|
474
|
+
label: 'Cookie verify',
|
|
475
|
+
nativeTask: 'native:cookie_verify',
|
|
476
|
+
rustTask: 'rust:cookie_verify',
|
|
477
|
+
status: 'proven',
|
|
478
|
+
typicalRatio: 2.1,
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
name: 'csrfToken',
|
|
482
|
+
label: 'CSRF create',
|
|
483
|
+
nativeTask: 'native:csrf_create',
|
|
484
|
+
rustTask: 'rust:csrf_create',
|
|
485
|
+
status: 'proven',
|
|
486
|
+
typicalRatio: 13.8,
|
|
487
|
+
},
|
|
488
|
+
{
|
|
489
|
+
name: 'csrfVerify',
|
|
490
|
+
label: 'CSRF verify',
|
|
491
|
+
nativeTask: 'native:csrf_verify',
|
|
492
|
+
rustTask: 'rust:csrf_verify',
|
|
493
|
+
status: 'proven',
|
|
494
|
+
typicalRatio: 2.7,
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: 'urlResolve',
|
|
498
|
+
label: 'URL resolve',
|
|
499
|
+
nativeTask: 'native:url_resolve',
|
|
500
|
+
rustTask: 'rust:url_resolve',
|
|
501
|
+
status: 'parity',
|
|
502
|
+
typicalRatio: 1.11,
|
|
503
|
+
note: 'Noisy ~1.5µs op; swings 0.74-1.03x run-to-run. UrlBuilder reuses the parsed base.',
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
name: 'urlEncodeQuery',
|
|
507
|
+
label: 'URL query build',
|
|
508
|
+
nativeTask: 'native:url_encode_query',
|
|
509
|
+
rustTask: 'rust:url_encode_query',
|
|
510
|
+
status: 'not-competitive',
|
|
511
|
+
note: 'encodeURIComponent baseline wins (~1.2-1.35x).',
|
|
512
|
+
},
|
|
513
|
+
|
|
514
|
+
// ── Unmeasured (no direct comparison task) ──
|
|
515
|
+
{
|
|
516
|
+
name: 'passwordVerify',
|
|
517
|
+
label: 'Password verify',
|
|
518
|
+
nativeTask: '',
|
|
519
|
+
rustTask: '',
|
|
520
|
+
status: 'unmeasured',
|
|
521
|
+
note: 'Depends on the hash cost; no standalone comparison.',
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
name: 'urlDecodeBytes',
|
|
525
|
+
label: 'URL decode (bytes)',
|
|
526
|
+
nativeTask: '',
|
|
527
|
+
rustTask: '',
|
|
528
|
+
status: 'unmeasured',
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
name: 'configure',
|
|
532
|
+
label: 'configure',
|
|
533
|
+
nativeTask: '',
|
|
534
|
+
rustTask: '',
|
|
535
|
+
status: 'unmeasured',
|
|
536
|
+
note: 'Config only.',
|
|
537
|
+
},
|
|
538
|
+
{
|
|
539
|
+
name: 'initThreadPool',
|
|
540
|
+
label: 'initThreadPool',
|
|
541
|
+
nativeTask: '',
|
|
542
|
+
rustTask: '',
|
|
543
|
+
status: 'unmeasured',
|
|
544
|
+
note: 'Runtime setup only.',
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
name: 'rayonNumThreads',
|
|
548
|
+
label: 'rayonNumThreads',
|
|
549
|
+
nativeTask: '',
|
|
550
|
+
rustTask: '',
|
|
551
|
+
status: 'unmeasured',
|
|
552
|
+
note: 'Introspection only.',
|
|
553
|
+
},
|
|
554
|
+
] as const satisfies readonly ProvenEntry[]
|
|
555
|
+
|
|
556
|
+
/** An entry of the registry with a specific status (type-level). */
|
|
557
|
+
type ProvenEntryOf<S extends PerformanceStatus> = Extract<
|
|
558
|
+
(typeof PROVEN_SURFACE)[number],
|
|
559
|
+
{ status: S }
|
|
560
|
+
>
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* The `rust.<name>` keys whose status is "proven" — the only keys exposed on
|
|
564
|
+
* the `proven` surface. Derived from the registry so the type can never drift
|
|
565
|
+
* from the data.
|
|
566
|
+
*/
|
|
567
|
+
export type ProvenKey = ProvenEntryOf<'proven'>['name']
|
|
568
|
+
|
|
569
|
+
const byName = new Map<string, ProvenEntry>(PROVEN_SURFACE.map((e) => [e.name, e]))
|
|
570
|
+
|
|
571
|
+
/** Look up the status of a public function by its `rust.<name>` key. */
|
|
572
|
+
export function provenStatus(name: string): PerformanceStatus | undefined {
|
|
573
|
+
return byName.get(name)?.status
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/** Whether a function is part of the performance-proven surface. */
|
|
577
|
+
export function isProven(name: string): boolean {
|
|
578
|
+
return provenStatus(name) === 'proven'
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/** The entries that are performance-proven (Rust clearly wins). */
|
|
582
|
+
export function provenSurface(): ProvenEntry[] {
|
|
583
|
+
return PROVEN_SURFACE.filter((e) => e.status === 'proven')
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/** Aggregate summary of the registry (status → count). */
|
|
587
|
+
export function provenSummary(): Record<PerformanceStatus, number> {
|
|
588
|
+
const out: Record<PerformanceStatus, number> = {
|
|
589
|
+
proven: 0,
|
|
590
|
+
parity: 0,
|
|
591
|
+
'not-competitive': 0,
|
|
592
|
+
unmeasured: 0,
|
|
593
|
+
}
|
|
594
|
+
for (const e of PROVEN_SURFACE) out[e.status] += 1
|
|
595
|
+
return out
|
|
596
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// src/shared/request-id.ts — fast counter-based request ID generator.
|
|
2
|
+
//
|
|
3
|
+
// Used by both the fast and pre-baked ingress paths. Produces a 16-byte hex
|
|
4
|
+
// string (as bytes) from a per-process counter seeded with boot randomness and
|
|
5
|
+
// an optional worker ID — no crypto, no allocation per call (reuses buffers).
|
|
6
|
+
//
|
|
7
|
+
// SIDE-EFFECT NOTE (deliberate, kept for zero-alloc hot-path perf): the module
|
|
8
|
+
// holds mutable shared state — a counter and reusable BINARY/HEX scratch
|
|
9
|
+
// buffers. `generateRequestId()` returns a SINGLE shared HEX buffer that is
|
|
10
|
+
// overwritten on the next call. Callers must decode or copy the returned bytes
|
|
11
|
+
// before the next call; the alias is confined to this module so the hazard is
|
|
12
|
+
// local and documented here.
|
|
13
|
+
|
|
14
|
+
const HEX_LOOKUP = new Uint8Array(256 * 2)
|
|
15
|
+
for (let i = 0; i < 256; i++) {
|
|
16
|
+
HEX_LOOKUP[i * 2] = '0123456789abcdef'.charCodeAt(i >> 4)
|
|
17
|
+
HEX_LOOKUP[i * 2 + 1] = '0123456789abcdef'.charCodeAt(i & 0x0f)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const BINARY = new Uint8Array(8)
|
|
21
|
+
const HEX = new Uint8Array(16)
|
|
22
|
+
|
|
23
|
+
const WORKER_ID = Number(process.env.INGRESS_WORKER_ID || 0) & 0xffff
|
|
24
|
+
// Boot randomness: one webcrypto draw, falling back to Math.random for hosts
|
|
25
|
+
// (older Node) without a `crypto` global. `[0]` is `number | undefined` under
|
|
26
|
+
// noUncheckedIndexedAccess — the `??` supplies the fallback for that too.
|
|
27
|
+
const BOOT_RANDOM =
|
|
28
|
+
typeof crypto !== 'undefined'
|
|
29
|
+
? (crypto.getRandomValues(new Uint32Array(1))[0] ?? (Math.random() * 0xffffffff) >>> 0)
|
|
30
|
+
: (Math.random() * 0xffffffff) >>> 0
|
|
31
|
+
|
|
32
|
+
const COUNTER = {
|
|
33
|
+
hi: (BOOT_RANDOM ^ (WORKER_ID << 16)) >>> 0,
|
|
34
|
+
lo: 0,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Generate the next request ID as 16 lowercase hex bytes (zero-alloc).
|
|
39
|
+
*
|
|
40
|
+
* @returns A shared buffer aliased across calls — decode/copy before the next
|
|
41
|
+
* `generateRequestId()` invocation.
|
|
42
|
+
*/
|
|
43
|
+
export function generateRequestId(): Uint8Array {
|
|
44
|
+
if (COUNTER.lo === 0xffffffff) {
|
|
45
|
+
COUNTER.lo = 0
|
|
46
|
+
COUNTER.hi = (COUNTER.hi + 1) >>> 0
|
|
47
|
+
} else {
|
|
48
|
+
COUNTER.lo++
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
BINARY[0] = (COUNTER.hi >>> 24) & 0xff
|
|
52
|
+
BINARY[1] = (COUNTER.hi >>> 16) & 0xff
|
|
53
|
+
BINARY[2] = (COUNTER.hi >>> 8) & 0xff
|
|
54
|
+
BINARY[3] = COUNTER.hi & 0xff
|
|
55
|
+
BINARY[4] = (COUNTER.lo >>> 24) & 0xff
|
|
56
|
+
BINARY[5] = (COUNTER.lo >>> 16) & 0xff
|
|
57
|
+
BINARY[6] = (COUNTER.lo >>> 8) & 0xff
|
|
58
|
+
BINARY[7] = COUNTER.lo & 0xff
|
|
59
|
+
|
|
60
|
+
for (let i = 0; i < 8; i++) {
|
|
61
|
+
const b = BINARY[i] ?? 0
|
|
62
|
+
HEX[i * 2] = HEX_LOOKUP[b * 2] ?? 0
|
|
63
|
+
HEX[i * 2 + 1] = HEX_LOOKUP[b * 2 + 1] ?? 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return HEX
|
|
67
|
+
}
|