Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ext/node): use cppgc for node:zlib #24267

Merged
merged 3 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ext/node/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,6 @@ deno_core::extension!(deno_node,
ops::zlib::op_zlib_close,
ops::zlib::op_zlib_close_if_pending,
ops::zlib::op_zlib_write,
ops::zlib::op_zlib_write_async,
ops::zlib::op_zlib_init,
ops::zlib::op_zlib_reset,
ops::zlib::brotli::op_brotli_compress,
Expand Down
112 changes: 33 additions & 79 deletions ext/node/ops/zlib/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use deno_core::error::bad_resource_id;
use deno_core::error::type_error;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::OpState;
use std::borrow::Cow;
use std::cell::RefCell;
use std::future::Future;
use std::rc::Rc;
use zlib::*;

mod alloc;
Expand All @@ -29,14 +25,6 @@ fn check(condition: bool, msg: &str) -> Result<(), AnyError> {
}
}

#[inline]
fn zlib(state: &mut OpState, handle: u32) -> Result<Rc<Zlib>, AnyError> {
state
.resource_table
.get::<Zlib>(handle)
.map_err(|_| bad_resource_id())
}

#[derive(Default)]
struct ZlibInner {
dictionary: Option<Vec<u8>>,
Expand Down Expand Up @@ -242,7 +230,7 @@ impl ZlibInner {
}

struct Zlib {
inner: RefCell<ZlibInner>,
inner: RefCell<Option<ZlibInner>>,
}

impl deno_core::Resource for Zlib {
Expand All @@ -251,76 +239,39 @@ impl deno_core::Resource for Zlib {
}
}

#[op2(fast)]
#[smi]
pub fn op_zlib_new(
state: &mut OpState,
#[smi] mode: i32,
) -> Result<u32, AnyError> {
#[op2]
#[cppgc]
pub fn op_zlib_new(#[smi] mode: i32) -> Result<Zlib, AnyError> {
let mode = Mode::try_from(mode)?;

let inner = ZlibInner {
mode,
..Default::default()
};

Ok(state.resource_table.add(Zlib {
inner: RefCell::new(inner),
}))
Ok(Zlib {
inner: RefCell::new(Some(inner)),
})
}

#[op2(fast)]
pub fn op_zlib_close(
state: &mut OpState,
#[smi] handle: u32,
) -> Result<(), AnyError> {
let resource = zlib(state, handle)?;
let mut zlib = resource.inner.borrow_mut();
pub fn op_zlib_close(#[cppgc] resource: &Zlib) -> Result<(), AnyError> {
let mut resource = resource.inner.borrow_mut();
let zlib = resource
.as_mut()
.ok_or_else(|| type_error("zlib not initialized"))?;

// If there is a pending write, defer the close until the write is done.
zlib.close()?;

Ok(())
}

#[allow(clippy::too_many_arguments)]
#[op2(async)]
#[serde]
pub fn op_zlib_write_async(
state: Rc<RefCell<OpState>>,
#[smi] handle: u32,
#[smi] flush: i32,
#[buffer] input: &[u8],
#[smi] in_off: u32,
#[smi] in_len: u32,
#[buffer] out: &mut [u8],
#[smi] out_off: u32,
#[smi] out_len: u32,
) -> Result<impl Future<Output = Result<(i32, u32, u32), AnyError>>, AnyError> {
let mut state_mut = state.borrow_mut();
let resource = zlib(&mut state_mut, handle)?;

let mut strm = resource.inner.borrow_mut();
let flush = Flush::try_from(flush)?;
strm.start_write(input, in_off, in_len, out, out_off, out_len, flush)?;

let state = state.clone();
Ok(async move {
let mut state_mut = state.borrow_mut();
let resource = zlib(&mut state_mut, handle)?;
let mut zlib = resource.inner.borrow_mut();

zlib.do_write(flush)?;
Ok((zlib.err, zlib.strm.avail_out, zlib.strm.avail_in))
})
}

#[allow(clippy::too_many_arguments)]
#[op2(fast)]
#[smi]
pub fn op_zlib_write(
state: &mut OpState,
#[smi] handle: u32,
#[cppgc] resource: &Zlib,
#[smi] flush: i32,
#[buffer] input: &[u8],
#[smi] in_off: u32,
Expand All @@ -330,9 +281,11 @@ pub fn op_zlib_write(
#[smi] out_len: u32,
#[buffer] result: &mut [u32],
) -> Result<i32, AnyError> {
let resource = zlib(state, handle)?;

let mut zlib = resource.inner.borrow_mut();
let zlib = zlib
.as_mut()
.ok_or_else(|| type_error("zlib not initialized"))?;

let flush = Flush::try_from(flush)?;
zlib.start_write(input, in_off, in_len, out, out_off, out_len, flush)?;
zlib.do_write(flush)?;
Expand All @@ -346,16 +299,17 @@ pub fn op_zlib_write(
#[op2(fast)]
#[smi]
pub fn op_zlib_init(
state: &mut OpState,
#[smi] handle: u32,
#[cppgc] resource: &Zlib,
#[smi] level: i32,
#[smi] window_bits: i32,
#[smi] mem_level: i32,
#[smi] strategy: i32,
#[buffer] dictionary: &[u8],
) -> Result<i32, AnyError> {
let resource = zlib(state, handle)?;
let mut zlib = resource.inner.borrow_mut();
let zlib = zlib
.as_mut()
.ok_or_else(|| type_error("zlib not initialized"))?;

check((8..=15).contains(&window_bits), "invalid windowBits")?;
check((-1..=9).contains(&level), "invalid level")?;
Expand Down Expand Up @@ -392,33 +346,33 @@ pub fn op_zlib_init(

#[op2(fast)]
#[smi]
pub fn op_zlib_reset(
state: &mut OpState,
#[smi] handle: u32,
) -> Result<i32, AnyError> {
let resource = zlib(state, handle)?;

pub fn op_zlib_reset(#[cppgc] resource: &Zlib) -> Result<i32, AnyError> {
let mut zlib = resource.inner.borrow_mut();
let zlib = zlib
.as_mut()
.ok_or_else(|| type_error("zlib not initialized"))?;

zlib.reset_stream()?;

Ok(zlib.err)
}

#[op2(fast)]
pub fn op_zlib_close_if_pending(
state: &mut OpState,
#[smi] handle: u32,
#[cppgc] resource: &Zlib,
) -> Result<(), AnyError> {
let resource = zlib(state, handle)?;
let pending_close = {
let mut zlib = resource.inner.borrow_mut();
let zlib = zlib
.as_mut()
.ok_or_else(|| type_error("zlib not initialized"))?;

zlib.write_in_progress = false;
zlib.pending_close
};
if pending_close {
drop(resource);
if let Ok(res) = state.resource_table.take_any(handle) {
res.close();
if let Some(mut res) = resource.inner.borrow_mut().take() {
let _ = res.close();
}
}

Expand Down
28 changes: 15 additions & 13 deletions ext/node/polyfills/_zlib_binding.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ import {
op_zlib_new,
op_zlib_reset,
op_zlib_write,
op_zlib_write_async,
} from "ext:core/ops";
import process from "node:process";

const writeResult = new Uint32Array(2);

Expand Down Expand Up @@ -124,18 +124,20 @@ class Zlib {
out_off,
out_len,
) {
op_zlib_write_async(
this.#handle,
flush ?? Z_NO_FLUSH,
input,
in_off,
in_len,
out,
out_off,
out_len,
).then(([err, availOut, availIn]) => {
if (this.#checkError(err)) {
this.callback(availIn, availOut);
process.nextTick(() => {
const res = this.writeSync(
flush ?? Z_NO_FLUSH,
input,
in_off,
in_len,
out,
out_off,
out_len,
);

if (res) {
const [availOut, availIn] = res;
this.callback(availOut, availIn);
}
});

Expand Down
10 changes: 2 additions & 8 deletions tests/unit_node/zlib_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Deno.test("brotli compression async", async () => {
assertEquals(decompressed.toString(), "hello world");
});

Deno.test("gzip compression sync", { sanitizeResources: false }, () => {
Deno.test("gzip compression sync", () => {
const buf = Buffer.from("hello world");
const compressed = gzipSync(buf);
const decompressed = unzipSync(compressed);
Expand Down Expand Up @@ -94,7 +94,6 @@ Deno.test("brotli end-to-end with 4097 bytes", () => {

Deno.test(
"zlib create deflate with dictionary",
{ sanitizeResources: false },
async () => {
const { promise, resolve } = Promise.withResolvers<void>();
const handle = createDeflate({
Expand All @@ -111,8 +110,6 @@ Deno.test(

Deno.test(
"zlib flush i32",
// FIXME: Handle is not closed properly
{ sanitizeResources: false },
function () {
const handle = createDeflate({
// @ts-expect-error: passing non-int flush value
Expand Down Expand Up @@ -142,7 +139,6 @@ Deno.test("should work with a buffer from an encoded string", () => {

Deno.test(
"zlib compression with dataview",
{ sanitizeResources: false },
() => {
const buf = Buffer.from("hello world");
const compressed = gzipSync(new DataView(buf.buffer));
Expand All @@ -151,9 +147,7 @@ Deno.test(
},
);

Deno.test("zlib compression with an encoded string", {
sanitizeResources: false,
}, () => {
Deno.test("zlib compression with an encoded string", () => {
const encoder = new TextEncoder();
const buffer = encoder.encode("hello world");
const compressed = gzipSync(buffer);
Expand Down