Skip to content

Commit 0f90aaa

Browse files
committed
fix(streaming): split hash_file into feed+finalize for FRB compatibility FRB drops the Vec<u8> return when a function also has StreamSink. Stream_hash_file now only feeds data Dart calls hasherFinalize() to obtain the digest.
1 parent 5e52a47 commit 0f90aaa

2 files changed

Lines changed: 29 additions & 11 deletions

File tree

rust/src/api/hashing/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ impl HasherHandle {
3232
guard.update(data)
3333
}
3434

35+
#[allow(dead_code)] // used by streaming hash tests
3536
pub(crate) fn finalize_raw(&self) -> Result<Vec<u8>, CryptoError> {
3637
let guard = self
3738
.inner

rust/src/api/streaming.rs

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -302,15 +302,16 @@ pub(crate) fn decrypt_file_impl(
302302

303303
// -- Streaming hash (no encryption padding) -----------------------------------
304304

305-
/// Hash a file in streaming 64KB chunks.
305+
/// Feed an entire file into the hasher in 64KB chunks. Does NOT finalize.
306306
///
307307
/// Resets the hasher first to ensure a clean state, then feeds raw file bytes
308-
/// (no padding). The digest matches `blake3_hash(fs::read(path))`.
309-
pub(crate) fn hash_file_impl(
308+
/// (no padding). Caller must call `finalize_raw()` / `hasherFinalize()` to
309+
/// obtain the digest.
310+
pub(crate) fn hash_file_feed(
310311
hasher: &HasherHandle,
311312
file_path: &str,
312313
on_progress: &dyn Fn(f64),
313-
) -> Result<Vec<u8>, CryptoError> {
314+
) -> Result<(), CryptoError> {
314315
hasher.reset_raw()?;
315316

316317
let file = File::open(file_path)
@@ -336,9 +337,22 @@ pub(crate) fn hash_file_impl(
336337
}
337338
}
338339

339-
let digest = hasher.finalize_raw()?;
340340
on_progress(1.0);
341-
Ok(digest)
341+
Ok(())
342+
}
343+
344+
/// Hash a file in streaming 64KB chunks (feed + finalize).
345+
///
346+
/// Convenience wrapper: feeds the entire file then finalizes.
347+
/// The digest matches `blake3_hash(fs::read(path))`.
348+
#[cfg(test)]
349+
fn hash_file_impl(
350+
hasher: &HasherHandle,
351+
file_path: &str,
352+
on_progress: &dyn Fn(f64),
353+
) -> Result<Vec<u8>, CryptoError> {
354+
hash_file_feed(hasher, file_path, on_progress)?;
355+
hasher.finalize_raw()
342356
}
343357

344358
// -- FRB entry points (thin wrappers) ----------------------------------------
@@ -373,17 +387,20 @@ pub fn stream_decrypt_file(
373387
})
374388
}
375389

376-
/// Hash a file using streaming 64KB chunks.
390+
/// Hash a file using streaming 64KB chunks — feeds data only, does NOT finalize.
377391
///
378-
/// Reads raw file bytes (no encryption padding) so the digest matches
379-
/// one-shot `blake3_hash()` / `sha3_hash()` output.
392+
/// Reads raw file bytes (no encryption padding) and feeds them to the hasher.
380393
/// Progress (0.0..1.0) is pushed to `progress_sink`.
394+
///
395+
/// After the stream completes, call `hasherFinalize()` from Dart to obtain
396+
/// the digest. This two-step design is required because FRB cannot return
397+
/// both a Stream and a value from the same function.
381398
pub fn stream_hash_file(
382399
hasher: &HasherHandle,
383400
file_path: String,
384401
progress_sink: StreamSink<f64>,
385-
) -> Result<Vec<u8>, CryptoError> {
386-
hash_file_impl(hasher, &file_path, &|p| {
402+
) -> Result<(), CryptoError> {
403+
hash_file_feed(hasher, &file_path, &|p| {
387404
let _ = progress_sink.add(p);
388405
})
389406
}

0 commit comments

Comments
 (0)