From 583fde024f7bc18bebb069cc1fcff147db219270 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Wed, 9 Sep 2026 02:24:49 +0800 Subject: [PATCH] refactor(compression): share the Brotli backend with curl Replace brotli2 with brotlic so stream codecs and curl use the same brotlic-sys 0.2.2 dependency. Remove the separate brotli-sys dependency and preserve compression quality 5 and streaming finish handling. Validation: cargo fmt --all; cargo clippy --workspace --all-targets --all-features -- -D warnings. Tests were skipped as requested. --- Cargo.lock | 21 ++---- moli-renderer-v8/Cargo.toml | 2 +- .../streams/compression/codec.rs | 66 ++++++++----------- 3 files changed, 33 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dbffb2960..6d7cacbb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -382,23 +382,12 @@ dependencies = [ ] [[package]] -name = "brotli-sys" -version = "0.3.2" +name = "brotlic" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4445dea95f4c2b41cde57cc9fee236ae4dbae88d8fcbdb4750fc1bb5d86aaecd" +checksum = "f552f56f302af0006c32b50bfa2bdb4696fd6ba33c3ab9f6225fefdb1efdc680" dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "brotli2" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cb036c3eade309815c15ddbacec5b22c4d1f3983a774ab2eac2e3e9ea85568e" -dependencies = [ - "brotli-sys", - "libc", + "brotlic-sys", ] [[package]] @@ -2877,7 +2866,7 @@ dependencies = [ "anyhow", "app_units", "base64 0.22.1", - "brotli2", + "brotlic", "crossbeam-channel", "cssparser", "curl", diff --git a/moli-renderer-v8/Cargo.toml b/moli-renderer-v8/Cargo.toml index cfbb849d4..755a759cf 100644 --- a/moli-renderer-v8/Cargo.toml +++ b/moli-renderer-v8/Cargo.toml @@ -18,7 +18,7 @@ wpt-extensions = [] anyhow = "1.0.100" app_units = "0.7.8" base64 = "0.22" -brotli2 = "0.3.2" +brotlic = "0.8.2" crossbeam-channel = "0.5.15" cssparser = "0.37" data-url = "0.3.2" diff --git a/moli-renderer-v8/src/context_bootstrap/streams/compression/codec.rs b/moli-renderer-v8/src/context_bootstrap/streams/compression/codec.rs index 5f62ea35d..c099a98f4 100644 --- a/moli-renderer-v8/src/context_bootstrap/streams/compression/codec.rs +++ b/moli-renderer-v8/src/context_bootstrap/streams/compression/codec.rs @@ -1,11 +1,9 @@ //! Streaming codecs, independent of V8 and the Streams lifecycle. //! One decoder accepts exactly one stream/member, including any checksum. -use brotli2::{ - CompressParams, - raw::{ - CoStatus, Compress as BrotliCompress, CompressOp, DeStatus, Decompress as BrotliDecompress, - }, +use brotlic::{ + BrotliDecoder, BrotliEncoder, BrotliEncoderOptions, Quality, decode::DecoderInfo, + encode::BrotliOperation, }; use flate2::{Compress, Compression, Decompress, FlushCompress, FlushDecompress, Status}; @@ -30,8 +28,8 @@ impl Format { } enum Engine { - BrotliCompress(BrotliCompress), - BrotliDecompress(BrotliDecompress), + BrotliCompress(BrotliEncoder), + BrotliDecompress(BrotliDecoder), Compress(Compress), Decompress(Decompress), } @@ -44,12 +42,14 @@ pub(super) struct Codec { impl Codec { pub(super) fn new(format: Format, decompress: bool) -> Self { let engine = match (format, decompress) { - (Format::Brotli, true) => Engine::BrotliDecompress(BrotliDecompress::new()), + (Format::Brotli, true) => Engine::BrotliDecompress(BrotliDecoder::new()), (Format::Brotli, false) => { - let mut encoder = BrotliCompress::new(); // Moderate quality for interactive streaming; retain the // standard window size and generic compression mode. - encoder.set_params(CompressParams::new().quality(5)); + let encoder = BrotliEncoderOptions::new() + .quality(Quality::new(5).expect("Brotli quality 5 must be valid")) + .build() + .expect("fixed Brotli encoder parameters must be valid"); Engine::BrotliCompress(encoder) } (Format::Gzip, true) => Engine::Decompress(Decompress::new_gzip(15)), @@ -92,40 +92,28 @@ impl Codec { let mut output = vec![0; 16 * 1024]; let (status, consumed, produced) = match &mut self.engine { Engine::BrotliCompress(engine) => { - let (before_in, before_out) = (input.len(), output.len()); - let mut remaining_in = input; - let mut remaining_out = output.as_mut_slice(); - let status = engine - .compress( - if finish { - CompressOp::Finish - } else { - CompressOp::Process - }, - &mut remaining_in, - &mut remaining_out, - ) - // Process completion is not the end of the stream. - .map(|status| finish && status == CoStatus::Finished) - .map_err(|_| "Compression failed"); + let operation = if finish { + BrotliOperation::Finish + } else { + BrotliOperation::Process + }; + let Ok(result) = engine.compress(input, &mut output, operation) else { + return (chunks, Err("Compression failed")); + }; ( - status, - before_in - remaining_in.len(), - before_out - remaining_out.len(), + Ok(engine.is_finished()), + result.bytes_read, + result.bytes_written, ) } Engine::BrotliDecompress(engine) => { - let (before_in, before_out) = (input.len(), output.len()); - let mut remaining_in = input; - let mut remaining_out = output.as_mut_slice(); - let status = engine - .decompress(&mut remaining_in, &mut remaining_out) - .map(|status| status == DeStatus::Finished) - .map_err(|_| "The compressed data was not valid"); + let Ok(result) = engine.decompress(input, &mut output) else { + return (chunks, Err("The compressed data was not valid")); + }; ( - status, - before_in - remaining_in.len(), - before_out - remaining_out.len(), + Ok(result.info == DecoderInfo::Finished), + result.bytes_read, + result.bytes_written, ) } Engine::Compress(engine) => {