castrum 0.7.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.
Files changed (86) hide show
  1. package/Cargo.toml +73 -0
  2. package/README.md +432 -0
  3. package/bench.ts +3 -0
  4. package/castrum.linux-x64-gnu.node +0 -0
  5. package/index.ts +24 -0
  6. package/package.json +94 -0
  7. package/rust/batch.rs +195 -0
  8. package/rust/cookie_parser.rs +70 -0
  9. package/rust/cors.rs +263 -0
  10. package/rust/hashing.rs +164 -0
  11. package/rust/headers.rs +234 -0
  12. package/rust/hmac_sha256.rs +113 -0
  13. package/rust/http_parser.rs +132 -0
  14. package/rust/ingress.rs +616 -0
  15. package/rust/ingress_constants.rs +89 -0
  16. package/rust/ip_trust.rs +185 -0
  17. package/rust/json_ops.rs +66 -0
  18. package/rust/json_patch_ops.rs +25 -0
  19. package/rust/json_schema.rs +151 -0
  20. package/rust/json_ser.rs +429 -0
  21. package/rust/lib.rs +48 -0
  22. package/rust/method.rs +141 -0
  23. package/rust/mime_lookup.rs +76 -0
  24. package/rust/output.rs +238 -0
  25. package/rust/proxy.rs +90 -0
  26. package/rust/query_parser.rs +141 -0
  27. package/rust/random_token.rs +52 -0
  28. package/rust/rate_limit.rs +203 -0
  29. package/rust/terminal.rs +95 -0
  30. package/rust/unit_tests.rs +843 -0
  31. package/rust/url_codec.rs +133 -0
  32. package/rust/util.rs +537 -0
  33. package/rust/validation.rs +107 -0
  34. package/rust/websocket.rs +23 -0
  35. package/src/baseline/index.ts +12 -0
  36. package/src/baseline/tasks/cookie.ts +22 -0
  37. package/src/baseline/tasks/hashing.ts +18 -0
  38. package/src/baseline/tasks/hmac.ts +38 -0
  39. package/src/baseline/tasks/http.ts +62 -0
  40. package/src/baseline/tasks/json-patch.ts +12 -0
  41. package/src/baseline/tasks/json.ts +50 -0
  42. package/src/baseline/tasks/mime.ts +5 -0
  43. package/src/baseline/tasks/query.ts +14 -0
  44. package/src/baseline/tasks/token.ts +15 -0
  45. package/src/baseline/tasks/url.ts +11 -0
  46. package/src/baseline/tasks/validation.ts +19 -0
  47. package/src/baseline/tasks/websocket.ts +14 -0
  48. package/src/bench/assert.ts +70 -0
  49. package/src/bench/checks.ts +227 -0
  50. package/src/bench/checksum.ts +27 -0
  51. package/src/bench/comparisons.ts +70 -0
  52. package/src/bench/concurrent-worker.ts +135 -0
  53. package/src/bench/fixtures.ts +233 -0
  54. package/src/bench/index.ts +2 -0
  55. package/src/bench/measure.ts +177 -0
  56. package/src/bench/now.ts +3 -0
  57. package/src/bench/report.ts +79 -0
  58. package/src/bench/run.ts +57 -0
  59. package/src/bench/tasks/complex.ts +232 -0
  60. package/src/bench/tasks/concurrent.ts +213 -0
  61. package/src/bench/tasks/cookie.ts +44 -0
  62. package/src/bench/tasks/hashing.ts +33 -0
  63. package/src/bench/tasks/hmac.ts +36 -0
  64. package/src/bench/tasks/http.ts +50 -0
  65. package/src/bench/tasks/index.ts +52 -0
  66. package/src/bench/tasks/json-patch.ts +21 -0
  67. package/src/bench/tasks/json.ts +33 -0
  68. package/src/bench/tasks/mime.ts +21 -0
  69. package/src/bench/tasks/query.ts +44 -0
  70. package/src/bench/tasks/stress.ts +142 -0
  71. package/src/bench/tasks/token.ts +20 -0
  72. package/src/bench/tasks/url.ts +34 -0
  73. package/src/bench/tasks/validation.ts +57 -0
  74. package/src/bench/tasks/websocket.ts +21 -0
  75. package/src/bench/types.ts +60 -0
  76. package/src/data/json-rows.ts +33 -0
  77. package/src/ingress/constants.ts +56 -0
  78. package/src/ingress/fast.ts +1082 -0
  79. package/src/ingress/handlers.ts +1202 -0
  80. package/src/ingress/index.ts +457 -0
  81. package/src/ingress/packed-input.ts +59 -0
  82. package/src/native/index.ts +207 -0
  83. package/src/rust-ffi/index.ts +555 -0
  84. package/src/shared/bytes.ts +36 -0
  85. package/src/shared/json.ts +18 -0
  86. package/src/shared/packed.ts +316 -0
@@ -0,0 +1,177 @@
1
+ import { checksumValue } from "./checksum";
2
+ import { nowMs } from "./now";
3
+ import type {
4
+ BenchResult,
5
+ ConcurrentBenchTask,
6
+ ConcurrentBenchResult,
7
+ StressBenchResult,
8
+ } from "./types";
9
+
10
+ const concurrentWorkerUrl = new URL("./concurrent-worker.ts", import.meta.url);
11
+
12
+ export function bench(
13
+ name: string,
14
+ fn: () => unknown,
15
+ iterations = 100,
16
+ warmup = 10,
17
+ ): BenchResult {
18
+ const safeIterations = Math.max(1, iterations);
19
+ const safeWarmup = Math.max(0, warmup);
20
+
21
+ let checksum = 0n;
22
+
23
+ for (let i = 0; i < safeWarmup; i++) {
24
+ checksum += checksumValue(fn());
25
+ }
26
+
27
+ const samples: number[] = new Array(safeIterations);
28
+
29
+ for (let i = 0; i < safeIterations; i++) {
30
+ const start = nowMs();
31
+ checksum += checksumValue(fn());
32
+ samples[i] = nowMs() - start;
33
+ }
34
+
35
+ samples.sort((a, b) => a - b);
36
+
37
+ const total = samples.reduce((a, b) => a + b, 0);
38
+ const avg = total / safeIterations;
39
+
40
+ return {
41
+ name,
42
+ iterations: safeIterations,
43
+ avgMs: avg,
44
+ p50Ms: samples[Math.floor(safeIterations * 0.5)] ?? 0,
45
+ p95Ms: samples[Math.floor(safeIterations * 0.95)] ?? 0,
46
+ opsPerSec: 1000 / Math.max(avg, 1e-9),
47
+ checksum: checksum.toString(),
48
+ };
49
+ }
50
+
51
+ export async function benchConcurrent(
52
+ task: ConcurrentBenchTask,
53
+ ): Promise<ConcurrentBenchResult> {
54
+ const concurrency = Math.max(1, task.concurrency);
55
+ const iterations = Math.max(1, task.iterationsPerSlot);
56
+ const warmup = Math.max(0, task.warmupPerSlot);
57
+
58
+ const workers: Worker[] = [];
59
+
60
+ try {
61
+ await Promise.all(
62
+ Array.from({ length: concurrency }, async () => {
63
+ const worker = new Worker(concurrentWorkerUrl, { type: "module" });
64
+ workers.push(worker);
65
+
66
+ await new Promise<void>((resolve, reject) => {
67
+ worker.onmessage = (e: any) => {
68
+ const msg = e.data;
69
+ if (msg?.type === "ready") {
70
+ resolve();
71
+ } else if (msg?.type === "error") {
72
+ reject(new Error(msg.message ?? "worker init error"));
73
+ }
74
+ };
75
+
76
+ worker.onerror = (e: any) => {
77
+ reject(new Error(e?.message ?? "worker error"));
78
+ };
79
+
80
+ worker.postMessage({
81
+ type: "init",
82
+ op: task.op,
83
+ payload: task.payload,
84
+ warmup,
85
+ });
86
+ });
87
+ }),
88
+ );
89
+
90
+ const resultPromises = workers.map(
91
+ (worker) =>
92
+ new Promise<bigint>((resolve, reject) => {
93
+ worker.onmessage = (e: any) => {
94
+ const msg = e.data;
95
+ if (msg?.type === "result") {
96
+ resolve(BigInt(msg.checksum ?? "0"));
97
+ } else if (msg?.type === "error") {
98
+ reject(new Error(msg.message ?? "worker runtime error"));
99
+ }
100
+ };
101
+
102
+ worker.onerror = (e: any) => {
103
+ reject(new Error(e?.message ?? "worker error"));
104
+ };
105
+ }),
106
+ );
107
+
108
+ const start = nowMs();
109
+
110
+ for (const worker of workers) {
111
+ worker.postMessage({
112
+ type: "run",
113
+ iterations,
114
+ });
115
+ }
116
+
117
+ const checksums = await Promise.all(resultPromises);
118
+ const totalMs = nowMs() - start;
119
+
120
+ const checksum = checksums.reduce((a, b) => a + b, 0n);
121
+ const totalOps = concurrency * iterations;
122
+
123
+ return {
124
+ name: task.name,
125
+ iterations,
126
+ concurrency,
127
+ totalOps,
128
+ totalMs,
129
+ avgMs: totalMs / Math.max(totalOps, 1),
130
+ p50Ms: 0,
131
+ p95Ms: 0,
132
+ opsPerSec: (totalOps / Math.max(totalMs, 1e-9)) * 1000,
133
+ checksum: checksum.toString(),
134
+ };
135
+ } finally {
136
+ for (const worker of workers) {
137
+ worker.terminate();
138
+ }
139
+ }
140
+ }
141
+
142
+ export function benchStress(
143
+ name: string,
144
+ fn: () => unknown,
145
+ durationMs: number,
146
+ warmupMs = 100,
147
+ ): StressBenchResult {
148
+ const safeDuration = Math.max(100, durationMs);
149
+ const safeWarmup = Math.max(0, warmupMs);
150
+
151
+ const warmupEnd = nowMs() + safeWarmup;
152
+ while (nowMs() < warmupEnd) {
153
+ checksumValue(fn());
154
+ }
155
+
156
+ let checksum = 0n;
157
+ let ops = 0;
158
+
159
+ const start = nowMs();
160
+ const end = start + safeDuration;
161
+
162
+ while (nowMs() < end) {
163
+ checksum += checksumValue(fn());
164
+ ops++;
165
+ }
166
+
167
+ const elapsed = nowMs() - start;
168
+
169
+ return {
170
+ name,
171
+ durationMs: elapsed,
172
+ totalOps: ops,
173
+ opsPerSec: (ops / Math.max(elapsed, 1e-9)) * 1000,
174
+ avgMs: elapsed / Math.max(ops, 1),
175
+ checksum: checksum.toString(),
176
+ };
177
+ }
@@ -0,0 +1,3 @@
1
+ export function nowMs(): number {
2
+ return Bun.nanoseconds() / 1_000_000;
3
+ }
@@ -0,0 +1,79 @@
1
+ import type { BenchResult, ComparisonReport, ConcurrentBenchResult, StressBenchResult } from "./types";
2
+
3
+ export function printResults(results: BenchResult[]): void {
4
+ console.table(
5
+ results.map((r) => ({
6
+ name: r.name,
7
+ iters: r.iterations,
8
+ "avg ms": r.avgMs.toFixed(4),
9
+ "p50 ms": r.p50Ms.toFixed(4),
10
+ "p95 ms": r.p95Ms.toFixed(4),
11
+ "ops/s": r.opsPerSec.toFixed(1),
12
+ checksum:
13
+ r.checksum.length > 16 ? `${r.checksum.slice(0, 16)}…` : r.checksum,
14
+ })),
15
+ );
16
+ }
17
+
18
+ export function printConcurrentResults(results: ConcurrentBenchResult[]): void {
19
+ console.table(
20
+ results.map((r) => ({
21
+ name: r.name,
22
+ concurrency: r.concurrency,
23
+ "ops/slot": r.iterations,
24
+ "total ops": r.totalOps,
25
+ "total ms": r.totalMs.toFixed(2),
26
+ "avg ms/op": r.avgMs.toFixed(6),
27
+ "ops/s": r.opsPerSec.toFixed(1),
28
+ checksum: r.checksum.length > 16 ? `${r.checksum.slice(0, 16)}…` : r.checksum,
29
+ })),
30
+ );
31
+ }
32
+
33
+ export function printStressResults(results: StressBenchResult[]): void {
34
+ console.table(
35
+ results.map((r) => ({
36
+ name: r.name,
37
+ "duration ms": r.durationMs.toFixed(0),
38
+ "total ops": r.totalOps,
39
+ "ops/s": r.opsPerSec.toFixed(1),
40
+ "avg ms": r.avgMs.toFixed(6),
41
+ checksum: r.checksum.length > 16 ? `${r.checksum.slice(0, 16)}…` : r.checksum,
42
+ })),
43
+ );
44
+ }
45
+
46
+ function printComparison(
47
+ results: (BenchResult | ConcurrentBenchResult | StressBenchResult)[],
48
+ report: ComparisonReport,
49
+ ): void {
50
+ const n = results.find((x) => x.name === report.nativeName);
51
+ const r = results.find((x) => x.name === report.rustName);
52
+
53
+ if (!n || !r) {
54
+ return;
55
+ }
56
+
57
+ const nAvg = n.avgMs;
58
+ const rAvg = r.avgMs;
59
+ const ratio = nAvg / Math.max(rAvg, 1e-9);
60
+
61
+ if (ratio >= 1) {
62
+ console.log(
63
+ `${report.label}: Rust ${rAvg.toFixed(4)}ms vs Native ${nAvg.toFixed(4)}ms → Rust ${ratio.toFixed(2)}x faster`,
64
+ );
65
+ } else {
66
+ console.log(
67
+ `${report.label}: Rust ${rAvg.toFixed(4)}ms vs Native ${nAvg.toFixed(4)}ms → Native ${(1 / ratio).toFixed(2)}x faster`,
68
+ );
69
+ }
70
+ }
71
+
72
+ export function printSummary(
73
+ results: (BenchResult | ConcurrentBenchResult | StressBenchResult)[],
74
+ reports: ComparisonReport[],
75
+ ): void {
76
+ for (const report of reports) {
77
+ printComparison(results, report);
78
+ }
79
+ }
@@ -0,0 +1,57 @@
1
+ import { runCorrectnessChecks, runComplexCorrectnessChecks } from "./checks";
2
+ import { comparisonReports } from "./comparisons";
3
+ import { createFixtures, createComplexFixtures } from "./fixtures";
4
+ import { bench, benchConcurrent, benchStress } from "./measure";
5
+ import { printResults, printConcurrentResults, printStressResults, printSummary } from "./report";
6
+ import { createAllTasks, createComplexTasks, createConcurrentTasks, createStressTasks } from "./tasks";
7
+
8
+ export async function runBenchmark(): Promise<void> {
9
+ const fixtures = createFixtures();
10
+ const complexFixtures = createComplexFixtures();
11
+
12
+ runCorrectnessChecks(fixtures);
13
+ runComplexCorrectnessChecks(fixtures, complexFixtures);
14
+
15
+ // ── Standard sequential benchmarks ──
16
+ console.log("\n═══ Standard Benchmarks ═══");
17
+ const tasks = createAllTasks(fixtures);
18
+ const results = tasks.map((task) =>
19
+ bench(task.name, task.run, task.iterations, task.warmup),
20
+ );
21
+ printResults(results);
22
+
23
+ // ── Complex payload benchmarks ──
24
+ console.log("\n═══ Complex Payload Benchmarks ═══");
25
+ const complexTasks = createComplexTasks(complexFixtures);
26
+ const complexResults = complexTasks.map((task) =>
27
+ bench(task.name, task.run, task.iterations, task.warmup),
28
+ );
29
+ printResults(complexResults);
30
+
31
+ // ── Concurrent burst benchmarks ──
32
+ console.log("\n═══ Concurrent Burst Benchmarks ═══");
33
+ const concurrentTasks = createConcurrentTasks(fixtures, complexFixtures);
34
+ const concurrentResults: import("./types").ConcurrentBenchResult[] = [];
35
+ for (const task of concurrentTasks) {
36
+ concurrentResults.push(await benchConcurrent(task));
37
+ }
38
+ printConcurrentResults(concurrentResults);
39
+
40
+ // ── Stress benchmarks (fixed duration) ──
41
+ console.log("\n═══ Stress Benchmarks (2s each) ═══");
42
+ const stressTasksList = createStressTasks(fixtures, complexFixtures);
43
+ const stressResults: import("./types").StressBenchResult[] = [];
44
+ for (const task of stressTasksList) {
45
+ stressResults.push(
46
+ benchStress(task.name, task.run, task.durationMs, task.warmupMs),
47
+ );
48
+ }
49
+ printStressResults(stressResults);
50
+
51
+ // ── Combined summary ──
52
+ console.log("\n═══ Practical Summary ═══");
53
+ printSummary(
54
+ [...results, ...complexResults, ...concurrentResults, ...stressResults],
55
+ comparisonReports,
56
+ );
57
+ }
@@ -0,0 +1,232 @@
1
+ import * as native from "../../baseline";
2
+ import { rust, rustBatch } from "../../rust-ffi";
3
+ import type { ComplexFixtures } from "../fixtures";
4
+ import type { BenchTask } from "../types";
5
+
6
+ export function complexTasks(c: ComplexFixtures): BenchTask[] {
7
+ return [
8
+ // ── Large JSON payloads ──
9
+ {
10
+ name: "native:json_valid_large",
11
+ run: () => native.nativeJsonValid(c.jsonLarge),
12
+ iterations: 20,
13
+ warmup: 5,
14
+ },
15
+ {
16
+ name: "rust:json_valid_large",
17
+ run: () => rust.jsonValid(c.jsonLarge),
18
+ iterations: 20,
19
+ warmup: 5,
20
+ },
21
+ {
22
+ name: "native:json_sum_large",
23
+ run: () => native.nativeJsonSum(c.jsonLarge),
24
+ iterations: 20,
25
+ warmup: 5,
26
+ },
27
+ {
28
+ name: "rust:json_sum_large",
29
+ run: () => rust.jsonSumIds(c.jsonLarge),
30
+ iterations: 20,
31
+ warmup: 5,
32
+ },
33
+
34
+ // ── Huge JSON payloads ──
35
+ {
36
+ name: "native:json_valid_huge",
37
+ run: () => native.nativeJsonValid(c.jsonHuge),
38
+ iterations: 10,
39
+ warmup: 3,
40
+ },
41
+ {
42
+ name: "rust:json_valid_huge",
43
+ run: () => rust.jsonValid(c.jsonHuge),
44
+ iterations: 10,
45
+ warmup: 3,
46
+ },
47
+
48
+ // ── Deep nested JSON ──
49
+ {
50
+ name: "native:json_valid_deep",
51
+ run: () => native.nativeJsonValid(c.jsonNestedDeep),
52
+ iterations: 100,
53
+ warmup: 20,
54
+ },
55
+ {
56
+ name: "rust:json_valid_deep",
57
+ run: () => rust.jsonValid(c.jsonNestedDeep),
58
+ iterations: 100,
59
+ warmup: 20,
60
+ },
61
+
62
+ // ── Complex HTTP ──
63
+ {
64
+ name: "native:http_parse_complex",
65
+ run: () => native.nativeHttpParseRequestPacked(c.httpComplex).byteLength,
66
+ iterations: 200,
67
+ warmup: 50,
68
+ },
69
+ {
70
+ name: "rust:http_parse_complex",
71
+ run: () => rust.httpParseRequestPacked(c.httpComplex).byteLength,
72
+ iterations: 200,
73
+ warmup: 50,
74
+ },
75
+
76
+ // ── Huge HTTP ──
77
+ {
78
+ name: "native:http_parse_huge",
79
+ run: () => native.nativeHttpParseRequestPacked(c.httpHuge).byteLength,
80
+ iterations: 100,
81
+ warmup: 20,
82
+ },
83
+ {
84
+ name: "rust:http_parse_huge",
85
+ run: () => rust.httpParseRequestPacked(c.httpHuge).byteLength,
86
+ iterations: 100,
87
+ warmup: 20,
88
+ },
89
+
90
+ // ── Large cookie string ──
91
+ {
92
+ name: "native:cookie_parse_large",
93
+ run: () => native.nativeCookieParsePacked(c.cookieLarge).byteLength,
94
+ iterations: 200,
95
+ warmup: 50,
96
+ },
97
+ {
98
+ name: "rust:cookie_parse_large",
99
+ run: () => rust.cookieParsePacked(c.cookieLarge).byteLength,
100
+ iterations: 200,
101
+ warmup: 50,
102
+ },
103
+
104
+ // ── Complex query string ──
105
+ {
106
+ name: "native:query_parse_complex",
107
+ run: () => native.nativeQueryParsePacked(c.queryComplex).byteLength,
108
+ iterations: 100,
109
+ warmup: 20,
110
+ },
111
+ {
112
+ name: "rust:query_parse_complex",
113
+ run: () => rust.queryParsePacked(c.queryComplex).byteLength,
114
+ iterations: 100,
115
+ warmup: 20,
116
+ },
117
+
118
+ // ── Large URL encode/decode ──
119
+ {
120
+ name: "native:url_encode_large",
121
+ run: () => native.nativeUrlEncode(c.urlLarge).length,
122
+ iterations: 100,
123
+ warmup: 20,
124
+ },
125
+ {
126
+ name: "rust:url_encode_large",
127
+ run: () => rust.urlEncode(c.urlLarge).byteLength,
128
+ iterations: 100,
129
+ warmup: 20,
130
+ },
131
+ {
132
+ name: "native:url_decode_large",
133
+ run: () => native.nativeUrlDecode(c.urlLarge).length,
134
+ iterations: 100,
135
+ warmup: 20,
136
+ },
137
+ {
138
+ name: "rust:url_decode_large",
139
+ run: () => rust.urlDecode(c.urlLarge).byteLength,
140
+ iterations: 100,
141
+ warmup: 20,
142
+ },
143
+
144
+ // ── Batch operations ──
145
+ {
146
+ name: "native:json_valid_batch",
147
+ run: () =>
148
+ c.batchJsonDocs.reduce(
149
+ (acc, doc) => acc + (native.nativeJsonValid(doc) ? 1 : 0),
150
+ 0,
151
+ ),
152
+ iterations: 50,
153
+ warmup: 10,
154
+ },
155
+ {
156
+ name: "rust:json_valid_batch",
157
+ run: () => rustBatch.jsonValid(c.batchJsonDocs).reduce((acc, b) => acc + b, 0),
158
+ iterations: 50,
159
+ warmup: 10,
160
+ },
161
+ {
162
+ name: "native:validate_email_batch",
163
+ run: () =>
164
+ c.batchEmails.reduce(
165
+ (acc, e) => acc + (native.nativeValidateEmail(e) ? 1 : 0),
166
+ 0,
167
+ ),
168
+ iterations: 50,
169
+ warmup: 10,
170
+ },
171
+ {
172
+ name: "rust:validate_email_batch",
173
+ run: () =>
174
+ rustBatch.validateEmail(c.batchEmails).reduce((acc, b) => acc + b, 0),
175
+ iterations: 50,
176
+ warmup: 10,
177
+ },
178
+ {
179
+ name: "native:validate_uuid_batch",
180
+ run: () =>
181
+ c.batchUuids.reduce(
182
+ (acc, u) => acc + (native.nativeValidateUuid(u) ? 1 : 0),
183
+ 0,
184
+ ),
185
+ iterations: 50,
186
+ warmup: 10,
187
+ },
188
+ {
189
+ name: "rust:validate_uuid_batch",
190
+ run: () =>
191
+ rustBatch.validateUuid(c.batchUuids).reduce((acc, b) => acc + b, 0),
192
+ iterations: 50,
193
+ warmup: 10,
194
+ },
195
+ {
196
+ name: "native:validate_ipv4_batch",
197
+ run: () =>
198
+ c.batchIpv4s.reduce(
199
+ (acc, ip) => acc + (native.nativeValidateIpv4(ip) ? 1 : 0),
200
+ 0,
201
+ ),
202
+ iterations: 50,
203
+ warmup: 10,
204
+ },
205
+ {
206
+ name: "rust:validate_ipv4_batch",
207
+ run: () =>
208
+ rustBatch.validateIpv4(c.batchIpv4s).reduce((acc, b) => acc + b, 0),
209
+ iterations: 50,
210
+ warmup: 10,
211
+ },
212
+ {
213
+ name: "native:query_parse_batch",
214
+ run: () =>
215
+ c.batchQueries.reduce(
216
+ (acc, q) => acc + native.nativeQueryParsePacked(q).byteLength,
217
+ 0,
218
+ ),
219
+ iterations: 30,
220
+ warmup: 5,
221
+ },
222
+ {
223
+ name: "rust:query_parse_batch",
224
+ run: () =>
225
+ rustBatch
226
+ .queryParse(c.batchQueries)
227
+ .reduce((acc, bytes) => acc + bytes.byteLength, 0),
228
+ iterations: 30,
229
+ warmup: 5,
230
+ },
231
+ ];
232
+ }