// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; use deno_core::Resource; use deno_core::ResourceId; use deno_core::ToJsBuffer; use flate2::write::DeflateDecoder; use flate2::write::DeflateEncoder; use flate2::write::GzDecoder; use flate2::write::GzEncoder; use flate2::write::ZlibDecoder; use flate2::write::ZlibEncoder; use flate2::Compression; use std::borrow::Cow; use std::cell::RefCell; use std::io::Write; use std::rc::Rc; #[derive(Debug)] struct CompressionResource(RefCell); /// https://wicg.github.io/compression/#supported-formats #[derive(Debug)] enum Inner { DeflateDecoder(ZlibDecoder>), DeflateEncoder(ZlibEncoder>), DeflateRawDecoder(DeflateDecoder>), DeflateRawEncoder(DeflateEncoder>), GzDecoder(GzDecoder>), GzEncoder(GzEncoder>), } impl Resource for CompressionResource { fn name(&self) -> Cow { "compression".into() } } #[op2(fast)] #[smi] pub fn op_compression_new( state: &mut OpState, #[string] format: &str, is_decoder: bool, ) -> ResourceId { let w = Vec::new(); let inner = match (format, is_decoder) { ("deflate", true) => Inner::DeflateDecoder(ZlibDecoder::new(w)), ("deflate", false) => { Inner::DeflateEncoder(ZlibEncoder::new(w, Compression::default())) } ("deflate-raw", true) => Inner::DeflateRawDecoder(DeflateDecoder::new(w)), ("deflate-raw", false) => { Inner::DeflateRawEncoder(DeflateEncoder::new(w, Compression::default())) } ("gzip", true) => Inner::GzDecoder(GzDecoder::new(w)), ("gzip", false) => { Inner::GzEncoder(GzEncoder::new(w, Compression::default())) } _ => unreachable!(), }; let resource = CompressionResource(RefCell::new(inner)); state.resource_table.add(resource) } #[op2] #[serde] pub fn op_compression_write( state: &mut OpState, #[smi] rid: ResourceId, #[anybuffer] input: &[u8], ) -> Result { let resource = state.resource_table.get::(rid)?; let mut inner = resource.0.borrow_mut(); let out: Vec = match &mut *inner { Inner::DeflateDecoder(d) => { d.write_all(input).map_err(|e| type_error(e.to_string()))?; d.flush()?; d.get_mut().drain(..) } Inner::DeflateEncoder(d) => { d.write_all(input).map_err(|e| type_error(e.to_string()))?; d.flush()?; d.get_mut().drain(..) } Inner::DeflateRawDecoder(d) => { d.write_all(input).map_err(|e| type_error(e.to_string()))?; d.flush()?; d.get_mut().drain(..) } Inner::DeflateRawEncoder(d) => { d.write_all(input).map_err(|e| type_error(e.to_string()))?; d.flush()?; d.get_mut().drain(..) } Inner::GzDecoder(d) => { d.write_all(input).map_err(|e| type_error(e.to_string()))?; d.flush()?; d.get_mut().drain(..) } Inner::GzEncoder(d) => { d.write_all(input).map_err(|e| type_error(e.to_string()))?; d.flush()?; d.get_mut().drain(..) } } .collect(); Ok(out.into()) } #[op2] #[serde] pub fn op_compression_finish( state: &mut OpState, #[smi] rid: ResourceId, report_errors: bool, ) -> Result { let resource = state.resource_table.take::(rid)?; let resource = Rc::try_unwrap(resource).unwrap(); let inner = resource.0.into_inner(); let out = match inner { Inner::DeflateDecoder(d) => { d.finish().map_err(|e| type_error(e.to_string())) } Inner::DeflateEncoder(d) => { d.finish().map_err(|e| type_error(e.to_string())) } Inner::DeflateRawDecoder(d) => { d.finish().map_err(|e| type_error(e.to_string())) } Inner::DeflateRawEncoder(d) => { d.finish().map_err(|e| type_error(e.to_string())) } Inner::GzDecoder(d) => d.finish().map_err(|e| type_error(e.to_string())), Inner::GzEncoder(d) => d.finish().map_err(|e| type_error(e.to_string())), }; match out { Err(err) => { if report_errors { Err(err) } else { Ok(Vec::with_capacity(0).into()) } } Ok(out) => Ok(out.into()), } }