From 7fc2105e1fd663fb21f65dd14085a2d056254e58 Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Fri, 6 Nov 2026 15:17:44 -0600 Subject: [PATCH] Give back whole buffered input bytes after each inflate call so the remaining count stays exact --- src/inflate.ts | 11 ++++++----- test/zip.test.ts | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/inflate.ts b/src/inflate.ts index 920f16f..825ed76 100644 --- a/src/inflate.ts +++ b/src/inflate.ts @@ -168,11 +168,12 @@ export class Inflate { if (e !== UNDERFLOW) throw e; this.ip = ip; this.bitbuf = buf; this.bitcnt = cnt; } - if (this.stage === Stage.Done) { - // whole bytes still buffered belong to whatever follows the stream - this.ip -= this.bitcnt >> 3; - this.bitbuf = this.bitcnt = 0; - } + // give back whole buffered bytes, as zlib does, so a caller's remaining count stays exact + const back = Math.min(this.bitcnt >> 3, this.ip); + this.ip -= back; + this.bitcnt -= back << 3; + this.bitbuf &= (1 << this.bitcnt) - 1; + if (this.stage === Stage.Done) this.bitbuf = this.bitcnt = 0; this.finishCall(); return [this.ip, this.op - off]; } diff --git a/test/zip.test.ts b/test/zip.test.ts index d64d85f..3408eca 100644 --- a/test/zip.test.ts +++ b/test/zip.test.ts @@ -105,6 +105,25 @@ test("inflate stops at the end of the stream and leaves the bytes after it", () assert.equal(read, packed.length); }); +test("inflate reads exactly the stream when the output is small", () => { + const data = sample(300, 14); + const packed = deflateRawSync(data); + const input = Buffer.concat([packed, Buffer.from([1, 2, 3, 4, 5])]); + for (const size of [1, 2, 3, 4, 5, 8]) { + const z = new Inflate(true); + const out = new Uint8Array(size); + let pos = 0, total = 0; + while (!z.finished) { + const [read, written] = z.run(input.subarray(pos), out, 0, size); + assert.ok(read > 0 || written > 0, `no progress at size ${size}`); + pos += read; + total += written; + } + assert.equal(total, data.length); + assert.equal(pos, packed.length, `size ${size}`); + } +}); + test("inflate sees the end of the stream when the output fills on the last byte", () => { for (const wrap of [true, false]) { for (const size of [1, 10, 5000]) {