Skip to content

Commit e43ac89

Browse files
committed
docs: prepare 2.0.4 release notes and benchmarks
Refresh public release documentation for the 2.0.4 streaming/cancellation update. - rewrite README around the default native engine and object-style API - document cancellation, algorithm guidance, engine selection, and packaging notes - update benchmark comparison for native and Zig engines - add release notes for zig-files-hash v0.0.5 and C ABI v3 - move maintainer-only Zig ABI details into CONTRIBUTING
1 parent 1807199 commit e43ac89

4 files changed

Lines changed: 550 additions & 320 deletions

File tree

BENCHMARKS.md

Lines changed: 83 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -4,109 +4,104 @@ Release measurements for `native` vs `zig` engines.
44

55
## Environment
66

7-
- Android: ARM64 (AArch64), Android 15
8-
- iOS: Apple A15 (arm64), iOS 26.3
7+
- Android: physical ARM64 device, Android 15
8+
- iOS: physical Apple A15 device, iOS 26.3
99
- Build: Release
10+
- Payload: generated local cache file, 200 MiB
11+
- App settings: `samples=3`, `warmups=1`
1012
- Values below are medians (`ms`, lower is better)
1113

12-
## Key takeaway
14+
Each run reports the app-level median for three measured samples. The tables
15+
below use the median across repeated runs.
1316

14-
- Main Android bottleneck is SHA-2 in raw generic Zig builds.
15-
- Example: SHA-256 / 200 MiB is `220 ms` (native) vs `12818 ms` (Zig raw).
16-
- Current package routes Android `SHA-224` / `SHA-256` and
17-
`HMAC-SHA-224` / `HMAC-SHA-256` through native fallback in Zig mode.
18-
- If Zig is built with `-Dcpu=baseline+sha2`, SHA-256 / 200 MiB becomes
19-
`219 ms` (near native `220 ms`).
17+
This document is a current engine-selection snapshot, not a release-to-release
18+
speedup table. A slower row means "slower than `native` on that platform"; it
19+
does not by itself mean the Zig core regressed versus the previous release.
2020

21-
### Why keep Zig engine in React Native
21+
## Key Takeaways
2222

23-
- One portable hashing core with a stable C ABI, so behavior does not drift
24-
between Swift/Kotlin/other bindings as the library grows.
25-
- Faster delivery of new algorithms/features: implement once in Zig core, then
26-
expose through RN bindings without duplicating full crypto logic per platform.
27-
- Performance hedge instead of lock-in: keep both engines available and select
28-
the better path per platform/algorithm based on real measurements.
29-
- Current data already shows Zig wins in part of the matrix (for example iOS
30-
`SHA-256`, `SHA-224`, `HMAC-SHA-256`, `XXH3-64`) while native wins others.
31-
- Android deployment strategy is flexible: current package uses generic Zig +
32-
native SHA-2 fallback; an alternative is shipping two Zig variants
33-
(generic and `+sha2`) with runtime routing.
23+
- Zig `v0.0.5` improves the Zig path versus the previous benchmark set for
24+
visible cases such as `BLAKE3` and iOS `XXH3-64`. Release-to-release notes
25+
live in `CHANGELOG.md`.
26+
- Current iOS `zig` is faster than `native` for `SHA-256`, `SHA-224`,
27+
`HMAC-SHA-224`, `HMAC-SHA-256`, `BLAKE3`, and `XXH3-64`.
28+
- Current iOS `native` remains the better choice for `SHA-1` and the `SHA-384` /
29+
`SHA-512` family.
30+
- Android `zig` is close to `native` for `SHA-224`, `SHA-256`,
31+
`HMAC-SHA-224`, and `HMAC-SHA-256` because the shipped Zig runtime routes
32+
those algorithms through the native fallback.
33+
- Android `zig` is substantially faster for `SHA-512/224` and
34+
`SHA-512/256`, where the native implementation is much slower.
35+
- Android `native` is faster for `SHA-1`, `SHA-384`, `SHA-512`, `BLAKE3`,
36+
and `XXH3-64` in this measurement set.
3437

35-
## Quick comparison
38+
## Quick Comparison
3639

37-
### Android
40+
### iOS
3841

39-
| Scenario | Native | Zig raw (no fallback) | Zig (`-Dcpu=baseline+sha2`) |
42+
| Scenario | Native | Zig | Zig vs native |
4043
|---|---:|---:|---:|
41-
| SHA-256, file 16 MiB | 23 | 1038 | 20 |
42-
| SHA-256, file 200 MiB | 220 | 12818 | 219 |
43-
| SHA-512/224, file 200 MiB | 3490 | 725 | 723 |
44-
| BLAKE3, file 200 MiB | 298 | 555 | 551 |
45-
| XXH3-64, file 200 MiB | 79 | 73 | 76 |
44+
| SHA-256, file 200 MiB | 228 | 209 | 9% faster |
45+
| SHA-512, file 200 MiB | 317 | 1176 | 271% slower |
46+
| BLAKE3, file 200 MiB | 310 | 302 | 3% faster |
47+
| XXH3-64, file 200 MiB | 108 | 66 | 39% faster |
4648

47-
### iOS
49+
### Android
4850

49-
| Scenario | Native | Zig |
50-
|---|---:|---:|
51-
| SHA-256, file 16 MiB | 31 | 29 |
52-
| SHA-256, file 200 MiB | 262 | 221 |
53-
| BLAKE3, file 200 MiB | 339 | 728 |
54-
| XXH3-64, file 200 MiB | 120 | 81 |
51+
| Scenario | Native | Zig | Zig vs native |
52+
|---|---:|---:|---:|
53+
| SHA-256, file 200 MiB | 297 | 332 | 12% slower |
54+
| SHA-512/224, file 200 MiB | 3832 | 796 | 79% faster |
55+
| BLAKE3, file 200 MiB | 304 | 391 | 29% slower |
56+
| XXH3-64, file 200 MiB | 73 | 96 | 32% slower |
5557

56-
## Full matrix: Android 200 MiB
58+
## Full Matrix: iOS 200 MiB
5759

58-
| Algorithm | Native | Zig raw (no fallback) | Zig (`-Dcpu=baseline+sha2`) |
60+
| Algorithm | Native | Zig | Zig vs native |
61+
|---|---:|---:|---:|
62+
| SHA-256 | 228 | 209 | 9% faster |
63+
| MD5 | 587 | 561 | 5% faster |
64+
| SHA-1 | 222 | 552 | 149% slower |
65+
| SHA-224 | 227 | 207 | 9% faster |
66+
| SHA-384 | 316 | 1177 | 272% slower |
67+
| SHA-512 | 317 | 1176 | 271% slower |
68+
| SHA-512/224 | 315 | 1175 | 273% slower |
69+
| SHA-512/256 | 313 | 1173 | 275% slower |
70+
| HMAC-SHA-224 | 223 | 204 | 9% faster |
71+
| HMAC-SHA-256 | 223 | 207 | 7% faster |
72+
| HMAC-SHA-384 | 315 | 1173 | 272% slower |
73+
| HMAC-SHA-512 | 314 | 1174 | 274% slower |
74+
| HMAC-MD5 | 586 | 556 | 5% faster |
75+
| HMAC-SHA-1 | 214 | 549 | 157% slower |
76+
| BLAKE3 | 310 | 302 | 3% faster |
77+
| XXH3-64 | 108 | 66 | 39% faster |
78+
79+
## Full Matrix: Android 200 MiB
80+
81+
| Algorithm | Native | Zig | Zig vs native |
5982
|---|---:|---:|---:|
60-
| SHA-256 | 220 | 12818 | 219 |
61-
| MD5 | 561 | 536 | 523 |
62-
| SHA-1 | 221 | 509 | 501 |
63-
| SHA-224 | 215 | 12785 | 218 |
64-
| SHA-384 | 538 | 723 | 723 |
65-
| SHA-512 | 543 | 724 | 736 |
66-
| SHA-512/224 | 3490 | 725 | 723 |
67-
| SHA-512/256 | 3484 | 724 | 720 |
68-
| HMAC-SHA-224 | 233 | 12803 | 218 |
69-
| HMAC-SHA-256 | 227 | 12764 | 221 |
70-
| HMAC-SHA-384 | 549 | 728 | 725 |
71-
| HMAC-SHA-512 | 540 | 728 | 723 |
72-
| HMAC-MD5 | 566 | 537 | 526 |
73-
| HMAC-SHA-1 | 223 | 508 | 501 |
74-
| BLAKE3 | 298 | 555 | 551 |
75-
| XXH3-64 | 79 | 73 | 76 |
76-
77-
## Full matrix: iOS 200 MiB
78-
79-
| Algorithm | Native | Zig |
80-
|---|---:|---:|
81-
| SHA-256 | 262 | 221 |
82-
| MD5 | 627 | 607 |
83-
| SHA-1 | 258 | 602 |
84-
| SHA-224 | 265 | 240 |
85-
| SHA-384 | 344 | 1236 |
86-
| SHA-512 | 354 | 1239 |
87-
| SHA-512/224 | 343 | 1236 |
88-
| SHA-512/256 | 343 | 1236 |
89-
| HMAC-SHA-224 | 260 | 255 |
90-
| HMAC-SHA-256 | 258 | 253 |
91-
| HMAC-SHA-384 | 342 | 1240 |
92-
| HMAC-SHA-512 | 343 | 1246 |
93-
| HMAC-MD5 | 621 | 616 |
94-
| HMAC-SHA-1 | 256 | 601 |
95-
| BLAKE3 | 339 | 728 |
96-
| XXH3-64 | 120 | 81 |
97-
98-
## Size impact
99-
100-
- Android APK: `+0.49%` (`+81,920` bytes) with Zig engine prebuilts
101-
- Android AAB: `+0.42%` (`+51,551` bytes)
102-
- iOS app dir: `-0.20%` (`-60 KiB`)
103-
- iOS main binary: `-0.30%` (`-61,952` bytes)
83+
| SHA-256 | 297 | 332 | 12% slower |
84+
| MD5 | 585 | 605 | 3% slower |
85+
| SHA-1 | 228 | 581 | 155% slower |
86+
| SHA-224 | 222 | 227 | 2% slower |
87+
| SHA-384 | 552 | 818 | 48% slower |
88+
| SHA-512 | 549 | 808 | 47% slower |
89+
| SHA-512/224 | 3832 | 796 | 79% faster |
90+
| SHA-512/256 | 3868 | 795 | 79% faster |
91+
| HMAC-SHA-224 | 222 | 227 | 2% slower |
92+
| HMAC-SHA-256 | 222 | 225 | 1% slower |
93+
| HMAC-SHA-384 | 553 | 792 | 43% slower |
94+
| HMAC-SHA-512 | 553 | 797 | 44% slower |
95+
| HMAC-MD5 | 581 | 588 | 1% slower |
96+
| HMAC-SHA-1 | 229 | 557 | 143% slower |
97+
| BLAKE3 | 304 | 391 | 29% slower |
98+
| XXH3-64 | 73 | 96 | 32% slower |
10499

105100
## Notes
106101

107-
- `Zig raw (no fallback)` shows pure Zig-path timing without Android runtime
108-
fallback and without target-specific CPU features.
109-
- In real app flow with current Android fallback, `SHA-224` / `SHA-256` and
110-
matching HMAC variants run through native, so expected latency there is near
111-
the `Native` column, not `Zig raw`.
112-
- `XXH3-128` is native-only and not included in Zig comparisons.
102+
- Android `zig` currently uses native fallback for `SHA-224`, `SHA-256`,
103+
`HMAC-SHA-224`, and `HMAC-SHA-256` in the shipped generic prebuilt setup.
104+
- `XXH3-128` is native-only and is not included in Zig comparisons.
105+
- Simulator, emulator, and Debug measurements are useful for smoke-checking the
106+
benchmark flow, but they run under different CPU, filesystem, and build
107+
conditions. Use physical devices and Release builds for performance claims.

CHANGELOG.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,157 @@
11
# Releases
22

3+
## v2.0.4 - Zig C ABI v3, streaming files, and cancellation
4+
5+
This release updates the Zig core to `zig-files-hash` `v0.0.5`, moves file
6+
hashing fully onto streaming paths, and adds AbortController-style cancellation
7+
for both the `native` and `zig` engines.
8+
9+
Existing positional JavaScript calls still work, but the new object-style
10+
request API is now the recommended API shape because it can carry
11+
`algorithm`, `hashOptions`, and `signal` without placeholder arguments.
12+
13+
### Why cancellation matters
14+
15+
Hashing a large video, archive, or offline map can take long enough for the
16+
user to change their mind. With `AbortController`, apps can stop native hashing
17+
when the user leaves the screen, picks another file, closes a modal, retries an
18+
upload, or cancels a verification step.
19+
20+
That means less wasted CPU and battery, fewer stale promises updating old UI
21+
state, and a cleaner integration with the same cancellation pattern developers
22+
already use for `fetch`.
23+
24+
### Added
25+
26+
- Added object-style request overloads for both public hash functions:
27+
- `fileHash(filePath, { algorithm, hashOptions, signal })`
28+
- `stringHash(text, { algorithm, encoding, hashOptions, signal })`
29+
- Added `HashRequest`, `StringHashRequest`, `HashAbortSignal`, and
30+
`HashAbortError` TypeScript types.
31+
- Added optional `AbortSignal` support for cancelling work the app no longer
32+
needs. Aborting rejects with `E_CANCELLED` and normalizes the JS error name
33+
to `AbortError`.
34+
- Added native `cancelOperation(operationId)` plumbing behind the JS API.
35+
Operation ids are internal; users should cancel with `AbortController`.
36+
- Added cancellation coverage for file hashing and string hashing in both
37+
engines:
38+
- Android native engine checks cancellation between stream chunks and closes
39+
the active `InputStream` on cancel.
40+
- Android Zig engine forwards cancellation into the Zig operation state.
41+
- iOS native engine tracks `Operation` instances and rejects cancelled work
42+
with `E_CANCELLED`.
43+
- iOS Zig engine tracks operations and forwards cancellation into the Zig
44+
operation state.
45+
- Added benchmark controls to the example app, including generated local test
46+
files, warmups, samples, per-algorithm results, and cancellation.
47+
48+
### Changed
49+
50+
- File hashing now uses streaming implementations throughout the runtime.
51+
The Zig file path no longer depends on the old one-shot file API:
52+
- Android Zig reads React Native file/content URIs through an `InputStream`
53+
and feeds `zfh_hasher_*`.
54+
- iOS Zig streams file URLs/paths through `zfh_hasher_*`.
55+
- Native engines continue to stream file data from disk.
56+
- Updated the bundled Zig core to `zig-files-hash` `v0.0.5`.
57+
- Updated the Zig integration to C ABI v3 (`ZFH_API_VERSION = 3`) and the new
58+
request/operation-state API.
59+
- Updated Zig error mapping for the v3 ABI, including
60+
`ZFH_OUTPUT_BUFFER_TOO_SMALL`, `ZFH_OPERATION_CANCELED`, and
61+
`ZFH_INVALID_STATE`.
62+
- `fileHash(path)` and `fileHash(path, {})` both default to `SHA-256`.
63+
- `stringHash(text)` and `stringHash(text, {})` both default to `SHA-256` with
64+
`utf8` input encoding.
65+
- The example app now uses the fresh object-style API and exposes cancel
66+
buttons for file hashing, string hashing, and benchmark runs.
67+
- `scripts/check-packed-prebuilts.sh` now uses an isolated npm cache while
68+
packing and verifies the generated Zig C ABI header in the package.
69+
70+
### Deprecated
71+
72+
- Deprecated positional overloads:
73+
- `fileHash(filePath, algorithm, options)`
74+
- `stringHash(text, algorithm, encoding, options)`
75+
- `hashString(...)` remains as a deprecated alias for `stringHash(...)`.
76+
77+
Recommended replacement:
78+
79+
```ts
80+
const digest = await fileHash(fileUri, {
81+
algorithm: 'SHA-256',
82+
});
83+
```
84+
85+
Cancelable flow:
86+
87+
```ts
88+
const controller = new AbortController();
89+
90+
const promise = fileHash(fileUri, {
91+
algorithm: 'SHA-256',
92+
signal: controller.signal,
93+
});
94+
95+
controller.abort();
96+
97+
await promise; // rejects with E_CANCELLED / AbortError
98+
```
99+
100+
For keyed algorithms:
101+
102+
```ts
103+
await fileHash(fileUri, {
104+
algorithm: 'HMAC-SHA-256',
105+
hashOptions: {
106+
key: 'secret',
107+
keyEncoding: 'utf8',
108+
},
109+
});
110+
```
111+
112+
### Compatibility
113+
114+
- Hash output remains lowercase hex.
115+
- Key rules are unchanged:
116+
- `HMAC-*` algorithms require `hashOptions.key`.
117+
- `BLAKE3` uses keyed mode only when `hashOptions.key` is provided.
118+
- Other algorithms reject `hashOptions.key`.
119+
- `HashOptions.mode` remains removed and still rejects with
120+
`E_INVALID_ARGUMENT`.
121+
- `XXH3-128` remains native-only.
122+
- Consumers using the published package do not need Zig installed. Custom
123+
source/prebuilt builds must use the `zig-files-hash` C ABI v3 core.
124+
- Cancellation is cooperative. Long file hashes stop at chunk boundaries; small
125+
`stringHash` calls can finish before an abort is observed.
126+
127+
### Performance
128+
129+
- Refreshed physical-device Release benchmarks for the current `native` and
130+
`zig` engines. Full current tables live in `BENCHMARKS.md`.
131+
- The refreshed benchmark tables compare the current `zig` engine with the
132+
current `native` engine. They should be read separately from the
133+
release-to-release Zig speedups below.
134+
- Compared with the previous benchmark set, Zig `BLAKE3` is much faster:
135+
- iOS 200 MiB: `728 ms` -> `~302 ms`
136+
- Android 200 MiB: `551 ms` -> `~391 ms`
137+
- Zig `XXH3-64` improved on iOS: `81 ms` -> `~66 ms` for 200 MiB.
138+
- Zig `SHA-224` / `SHA-256` and matching HMAC variants remain competitive on
139+
iOS and stay near native on Android through the existing native fallback.
140+
- Native remains the faster choice for iOS `SHA-1` and the iOS
141+
`SHA-384` / `SHA-512` family.
142+
- On Android, Zig remains the faster choice for `SHA-512/224` and
143+
`SHA-512/256`; native remains faster for `SHA-1`, `SHA-384`, `SHA-512`,
144+
`BLAKE3`, and `XXH3-64` in the current device run.
145+
146+
### Testing
147+
148+
- Expanded Jest coverage for the object request API, default values, abort
149+
handling, native cancellation calls, and `AbortError` normalization.
150+
- Updated runtime diagnostics tests for Zig C ABI v3 and `zig-files-hash`
151+
`v0.0.5`.
152+
153+
---
154+
3155
## v2.0.3 - iOS Zig engine fix and React Native 0.85 template refresh
4156

5157
This release fixes iOS Zig engine prebuilts for Xcode 26 and refreshes the

CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ Prebuilt output locations:
4242
- `third_party/zig-files-hash-prebuilt/android/<ABI>/libzig_files_hash.a`
4343
- `third_party/zig-files-hash-prebuilt/ios/ZigFilesHash.xcframework`
4444

45+
The bundled Zig core is `zig-files-hash v0.0.5` with C ABI v3
46+
(`ZFH_API_VERSION = 3`). Custom source/prebuilt builds must use a compatible
47+
Zig C ABI.
48+
4549
### Release builds on physical devices (native vs zig)
4650

4751
Use these commands for manual smoke/performance verification in Release mode.

0 commit comments

Comments
 (0)