diff --git a/src/index.ts b/src/index.ts index f1d0d07..1cad6bf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -662,7 +662,10 @@ export function decompress(dat: Uint8Array, buf?: Uint8Array, dic?: Uint8Array) if (dic) rdic(dic, st); if (nb) { buf = null; - if (st.w.length == st.u) { + // st.w is the dictionary history after rdic(), not a spare output + // buffer; reusing it as output would alias reads with writes and + // corrupt the result. Only take the fast path without a dictionary. + if (!dic && st.w.length == st.u) { bufs.push(buf = st.w); ol += st.u; } diff --git a/tests/simple_cases_test.ts b/tests/simple_cases_test.ts index 04fcb55..bb86380 100644 --- a/tests/simple_cases_test.ts +++ b/tests/simple_cases_test.ts @@ -109,4 +109,16 @@ Deno.test("Decompression of Lorem ipsum text using a dictionary", () => { let shouldBeLoremIpsum = new TextDecoder().decode(output); assertEquals(expected, shouldBeLoremIpsum); } -}); \ No newline at end of file +}); +Deno.test("Dictionary decompression when dictionary length equals output size", () => { + // 200-byte raw dictionary; payload is identical to it, so it compresses to a + // single back-reference into the dictionary and decodes back to the dict. + const dictionary = new Uint8Array(200); + for (let i = 0; i < 200; i++) dictionary[i] = (i * 7 + 13) % 256; + const compressed = new Uint8Array([ + 0x28, 0xB5, 0x2F, 0xFD, 0x24, 0xC8, 0x4D, 0x00, 0x00, 0x10, 0x0D, 0x14, + 0x01, 0x00, 0xC3, 0x25, 0x65, 0xB0, 0x43, 0xDF, 0x16, 0xB6 + ]); + const decompressed = fzstd.decompress(compressed, undefined, dictionary); + assertEquals(dictionary, decompressed); +});