Repository: denoland/deno
Pull request: fix(ext/node): zlib.crc32()

Fixes https://github.com/denoland/deno/issues/26845

Changed files:
filename: ext/node/lib.rs
patch: @@ -345,6 +345,7 @@ deno_core::extension!(deno_node,
     ops::zlib::op_zlib_write,
     ops::zlib::op_zlib_init,
     ops::zlib::op_zlib_reset,
+    ops::zlib::op_zlib_crc32,
     ops::zlib::brotli::op_brotli_compress,
     ops::zlib::brotli::op_brotli_compress_async,
     ops::zlib::brotli::op_create_brotli_compress,

filename: ext/node/ops/zlib/mod.rs
patch: @@ -1,6 +1,7 @@
 // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
 
 use deno_core::op2;
+use libc::c_ulong;
 use std::borrow::Cow;
 use std::cell::RefCell;
 use zlib::*;
@@ -381,6 +382,15 @@ pub fn op_zlib_close_if_pending(
   Ok(())
 }
 
+#[op2(fast)]
+#[smi]
+pub fn op_zlib_crc32(#[buffer] data: &[u8], #[smi] value: u32) -> u32 {
+  // SAFETY: `data` is a valid buffer.
+  unsafe {
+    zlib::crc32(value as c_ulong, data.as_ptr(), data.len() as u32) as u32
+  }
+}
+
 #[cfg(test)]
 mod tests {
   use super::*;

filename: ext/node/polyfills/zlib.ts
patch: @@ -40,6 +40,58 @@ import {
   createBrotliCompress,
   createBrotliDecompress,
 } from "ext:deno_node/_brotli.js";
+import { ERR_INVALID_ARG_TYPE } from "ext:deno_node/internal/errors.ts";
+import { validateUint32 } from "ext:deno_node/internal/validators.mjs";
+import { op_zlib_crc32 } from "ext:core/ops";
+import { core, primordials } from "ext:core/mod.js";
+import { TextEncoder } from "ext:deno_web/08_text_encoding.js";
+const {
+  Uint8Array,
+  TypedArrayPrototypeGetBuffer,
+  TypedArrayPrototypeGetByteLength,
+  TypedArrayPrototypeGetByteOffset,
+  DataViewPrototypeGetBuffer,
+  DataViewPrototypeGetByteLength,
+  DataViewPrototypeGetByteOffset,
+} = primordials;
+const { isTypedArray, isDataView } = core;
+
+const enc = new TextEncoder();
+const toU8 = (input) => {
+  if (typeof input === "string") {
+    return enc.encode(input);
+  }
+
+  if (isTypedArray(input)) {
+    return new Uint8Array(
+      TypedArrayPrototypeGetBuffer(input),
+      TypedArrayPrototypeGetByteOffset(input),
+      TypedArrayPrototypeGetByteLength(input),
+    );
+  } else if (isDataView(input)) {
+    return new Uint8Array(
+      DataViewPrototypeGetBuffer(input),
+      DataViewPrototypeGetByteOffset(input),
+      DataViewPrototypeGetByteLength(input),
+    );
+  }
+
+  return input;
+};
+
+export function crc32(data, value = 0) {
+  if (typeof data !== "string" && !isArrayBufferView(data)) {
+    throw new ERR_INVALID_ARG_TYPE("data", [
+      "Buffer",
+      "TypedArray",
+      "DataView",
+      "string",
+    ], data);
+  }
+  validateUint32(value, "value");
+
+  return op_zlib_crc32(toU8(data), value);
+}
 
 export class Options {
   constructor() {
@@ -87,6 +139,7 @@ export default {
   BrotliOptions,
   codes,
   constants,
+  crc32,
   createBrotliCompress,
   createBrotliDecompress,
   createDeflate,

filename: tests/unit_node/zlib_test.ts
patch: @@ -7,6 +7,7 @@ import {
   brotliCompressSync,
   brotliDecompressSync,
   constants,
+  crc32,
   createBrotliCompress,
   createBrotliDecompress,
   createDeflate,
@@ -225,3 +226,7 @@ Deno.test("gzip() and gzipSync() accept ArrayBuffer", async () => {
   const outputSync = gzipSync(buf);
   assert(outputSync instanceof Buffer);
 });
+
+Deno.test("crc32()", () => {
+  assertEquals(crc32("hello world"), 222957957);
+});