From a9cc4631ca4b5e2bb64943435d0d8242292b168d Mon Sep 17 00:00:00 2001 From: Curran McConnell Date: Thu, 7 Sep 2023 23:41:33 -0400 Subject: [PATCH] fix(ext/node/ops/zlib/brotli): Allow decompressing more than 4096 bytes (#20301) Fixes https://github.com/denoland/deno/issues/19816 In that issue, I suggest switching over the other brotli functionality to the Rust API provided by the `brotli` crate. Here, I only do that with the `brotli_decompress` function to fix the bug with buffers longer than 4096 bytes. --- cli/tests/unit_node/zlib_test.ts | 7 +++++++ ext/node/ops/zlib/brotli.rs | 36 ++++++-------------------------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/cli/tests/unit_node/zlib_test.ts b/cli/tests/unit_node/zlib_test.ts index 6b09c50b08..b878b7cc32 100644 --- a/cli/tests/unit_node/zlib_test.ts +++ b/cli/tests/unit_node/zlib_test.ts @@ -62,6 +62,13 @@ Deno.test("brotli compression", async () => { } }); +Deno.test("brotli end-to-end with 4097 bytes", () => { + const a = "a".repeat(4097); + const compressed = brotliCompressSync(a); + const decompressed = brotliDecompressSync(compressed); + assertEquals(decompressed.toString(), a); +}); + Deno.test( "zlib create deflate with dictionary", { sanitizeResources: false }, diff --git a/ext/node/ops/zlib/brotli.rs b/ext/node/ops/zlib/brotli.rs index f3b5001aab..b4d9709702 100644 --- a/ext/node/ops/zlib/brotli.rs +++ b/ext/node/ops/zlib/brotli.rs @@ -4,6 +4,7 @@ use brotli::ffi::compressor::*; use brotli::ffi::decompressor::ffi::interface::BrotliDecoderResult; use brotli::ffi::decompressor::ffi::BrotliDecoderState; use brotli::ffi::decompressor::*; +use brotli::Decompressor; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::op; @@ -11,6 +12,7 @@ use deno_core::JsBuffer; use deno_core::OpState; use deno_core::Resource; use deno_core::ToJsBuffer; +use std::io::Read; fn encoder_mode(mode: u32) -> Result { if mode > 6 { @@ -214,36 +216,10 @@ pub fn op_brotli_compress_stream_end( } fn brotli_decompress(buffer: &[u8]) -> Result { - let in_buffer = buffer.as_ptr(); - let in_size = buffer.len(); - - let mut out = vec![0u8; 4096]; - loop { - let out_buffer = out.as_mut_ptr(); - let mut out_size = out.len(); - // SAFETY: TODO(littledivy) - match unsafe { - CBrotliDecoderDecompress( - in_size, - in_buffer, - &mut out_size as *mut usize, - out_buffer, - ) - } { - BrotliDecoderResult::BROTLI_DECODER_RESULT_SUCCESS => { - out.truncate(out_size); - return Ok(out.into()); - } - BrotliDecoderResult::BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT => { - let new_size = out.len() * 2; - if new_size < out.len() { - return Err(type_error("Failed to decompress")); - } - out.resize(new_size, 0); - } - _ => return Err(type_error("Failed to decompress")), - } - } + let mut output = Vec::with_capacity(4096); + let mut decompressor = Decompressor::new(buffer, buffer.len()); + decompressor.read_to_end(&mut output)?; + Ok(output.into()) } #[op]