Skip to content

perf(sdk): native CRC-32 on Node, slice-by-16 elsewhere (DSPX-5232) - #1096

Draft
dmihalcik-virtru wants to merge 1 commit into
DSPX-5232-segment-bufferfrom
DSPX-5232-crc
Draft

dmihalcik-virtru wants to merge 1 commit into
DSPX-5232-segment-bufferfrom
DSPX-5232-crc

Conversation

@dmihalcik-virtru

Copy link
Copy Markdown
Member

What / why

DSPX-5232, part 2 of 2. Stacked on #1095; review only the top commit until that merges.

Every encrypted output byte goes through CRC-32 for the ZIP data descriptors. The byte-at-a-time JS loop in buffer-crc32.ts ran at about 500 MiB/s and capped encrypt near 400 MiB/s (DSPX-5124 analysis item #11). ZIP requires the CRC on every entry, so it can't be skipped. It has to be fast instead.

  • Node ≥ 20.15 / 22.2 and Bun: uses zlib's built-in crc32, reached through process.getBuiltinModule('node:zlib') rather than an import. Browser bundles therefore never see node:zlib; karma's webpack bundle and web-test-runner both build and pass.
  • Everywhere else (browsers, older Node, Deno without Node compat): a slice-by-16 table loop.
  • unsigned(buf, previous) keeps its signature. The module adds portableCrc32 and crc32Backend: 'zlib' | 'portable'. npm run bench:encrypt now reports which backend it measured.

Results

Node 24.21.0, Apple M5 Pro, 1 GiB in 64 KiB chunks (npm run bench:encrypt):

segment main #1095 #1095 + this PR
2 MiB 307 MiB/s 421 MiB/s 2,376 MiB/s
16 MiB 59 465 2,835
64 MiB 15 448 2,470
256 MiB 4 437 2,273

CRC-32 alone:

implementation throughput
old JS loop ~500 MiB/s
slice-by-16, V8 ~2,200 MiB/s
slice-by-16, JSC (Bun) ~2,500 MiB/s
zlib, Node ~13–33 GB/s

This meets the ticket's acceptance criterion of encrypt ≥ 1 GB/s in Node.

xtest paired A/B (js@main vs. this stack, local platform): 7 of 7 cells pass with no regressions. js-encrypt-32MiB wall ratio is 0.923 [0.903, 0.978]. Startup (about 1.1 s per CLI call) hides most of the gain at those sizes; larger xtest payloads are being reworked in opentdf/tests#622.

How to test

cd lib && npm run build && npm run bench:encrypt
npm test   # tests/mocha/unit/crc32.spec.ts (Node + karma), tests/web/tdf3/crc32.test.ts (wtr)

The tests check both implementations against the standard check values. They also check continuation across every split point, and compare portableCrc32 against a bit-at-a-time reference CRC on every length and alignment around a 16-byte block. Under Node they assert that the zlib backend is the one in use. Separately, unzip -t found no CRC errors in a TDF written by the js CLI with this change, and main's CLI decrypts it.

Risk

Touches ZIP integrity metadata, though not cryptographic integrity: readers don't check the CRC today. A wrong CRC would make TDFs fail strict ZIP tools, which is what the reference-equivalence tests guard against. lib/package.json still lists the unused buffer-crc32 npm dependency; removing it is left for a follow-up to avoid lockfile churn here.

Every encrypted output byte goes through CRC-32 for the ZIP data
descriptors, and the byte-at-a-time JS loop (about 500 MiB/s) capped
encrypt near 400 MiB/s.

Use zlib's native crc32 where the runtime has it (Node >= 20.15 / 22.2,
Bun). It is reached through process.getBuiltinModule('node:zlib') rather
than an import, so browser bundles never see node:zlib. Everywhere else,
a slice-by-16 table loop runs at about 2.2 GB/s in V8 (4x the old loop).

With the segment-buffer fix, encrypt goes from about 420 MiB/s to
2.3-2.8 GB/s in Node (1 GiB in 64 KiB chunks, 2-256 MiB segments).
npm run bench:encrypt now reports which CRC-32 backend it measured.

Signed-off-by: Dave Mihalcik <dmihalcik@virtru.com>
@github-actions

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai

coderabbitai Bot commented Oct 19, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant