Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
17 changes: 7 additions & 10 deletions src/js/node/zlib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ declare global {
_info: boolean | undefined;
_maxOutputLength: number;
reset(): void;
flush(kind?: number | (() => void), callback?: () => void): void;
flush(kind?: unknown, callback?: () => void): void;
close(callback?: () => void): void;
_processChunk(chunk: View, flushFlag: number, cb?: () => void): Buffer | undefined;
// What `zlibBuffer` keeps on the engine it drives.
Expand Down Expand Up @@ -372,23 +372,20 @@ const kFlushBuffers: Chunk[] = [];
}
}

ZlibBase.prototype.flush = function (this: $Node.ZlibBase, kind?: number | (() => void), callback?: () => void) {
ZlibBase.prototype.flush = function (this: $Node.ZlibBase, kind?: unknown, callback?: () => void) {
if (typeof kind === "function" || (kind === undefined && !callback)) {
callback = kind;
callback = kind as typeof callback;
kind = this._defaultFullFlushFlag;
}
// Node.js validates here too, in every stream state, with bounds from an own _flushBoundIdx: https://github.com/nodejs/node/blob/v26.12.1/lib/zlib.js#L383-L386
const flushBound = this[kFlushBound];
const flushFlag = checkRangesOrGetDefault(kind, "kind", flushBound[0], flushBound[1], this._defaultFullFlushFlag);
if (this.writableFinished) {
if (callback) process.nextTick(callback);
} else if (this.writableEnded) {
if (callback) this.once("end", callback);
} else {
if (!this.destroyed) {
// "2" and 2n index the same flush buffer as 2.
kind = kFlushBuffers[kind!]?.[kFlushFlag] ?? kind;
const flushBound = this[kFlushBound];
kind = checkRangesOrGetDefault(kind, "kind", flushBound[0], flushBound[1], this._defaultFullFlushFlag);
}
this.write(kFlushBuffers[kind!], "", callback);
this.write(kFlushBuffers[flushFlag], "", callback);
}
};

Expand Down
117 changes: 117 additions & 0 deletions test/js/node/zlib/zlib-flush-kind-fixture.cjs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 24 additions & 14 deletions test/js/node/zlib/zlib.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { deflateSync, gunzipSync, gzipSync, inflateSync } from "bun";
import { describe, expect, it } from "bun:test";
import { bunEnv, bunExe, isASAN, isDebug, tempDir, tmpdirSync } from "harness";
import { bunEnv, bunExe, isASAN, isDebug, nodeExeAtLeast, tempDir, tmpdirSync } from "harness";
import * as buffer from "node:buffer";
import { randomFillSync } from "node:crypto";
import * as fs from "node:fs";
Expand Down Expand Up @@ -106,19 +106,29 @@ describe("zlib", () => {
expect(() => gunzipSync(data, { library: "zlib" })).toThrow(new Error("incorrect header check"));
});

it("flush() takes a numeric string or a BigInt, and any kind after end()", async () => {
for (const kind of ["2", 2n]) {
const gzip = zlib.createGzip();
expect(Object.keys(gzip)).not.toContain("_flushBoundIdx");
const chunks = [];
gzip.on("data", chunk => chunks.push(chunk));
gzip.write("hello");
await new Promise(done => gzip.flush(kind, done));
const flushed = zlib.gunzipSync(Buffer.concat(chunks), { finishFlush: zlib.constants.Z_SYNC_FLUSH });
expect(flushed.toString()).toBe("hello");
gzip.end();
await new Promise(done => gzip.flush("none", done));
}
// Node.js checks the kind since v26.5.0, before it looks at the stream (nodejs/node#63746). The fixture has the assertions.
for (const [name, runtime] of [
["bun", bunExe()],
Comment thread
robobun marked this conversation as resolved.
Resolved
["node", nodeExeAtLeast("26.5.0")],
]) {
it.concurrent.skipIf(!runtime)(
`flush() refuses a kind that is not a number in the range of the codec, in every stream state (${name})`,
async () => {
await using proc = Bun.spawn({
cmd: [runtime, resolve(import.meta.dir, "zlib-flush-kind-fixture.cjs")],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect({ stdout, stderr, exitCode }).toEqual({ stdout: "ok\n", stderr: "", exitCode: 0 });
},
);
}

it("a stream has no _flushBoundIdx", () => {
// Node.js keeps the index of the flush bounds in this own property.
expect(Object.keys(zlib.createGzip())).not.toContain("_flushBoundIdx");
});

describe("libdeflate level validation", () => {
Expand Down
Loading