const MIN_MATCH = 3; const MAX_OFFSET = 0xffff; function writeExtLen(out, n) { while (n >= 255) { out.push(255); n -= 255; } out.push(n); } function readExtLen(input, pos, base) { let add = 0; while (true) { let b = input[pos++]; add += b; if (b < 255) break; } return [base + add, pos]; } function findLongestMatch(data, pos, window) { let n = data.length; let max_off = Math.min(window, pos); let best_len = 0; let best_off = 0; let max_match_len = n - pos; if (max_match_len < MIN_MATCH) return [0, 0]; let start = pos - max_off; for (let j = start; j < pos; j++) { if (data[j] === data[pos]) { let k = 0; while (k < max_match_len && data[j + k] === data[pos + k]) { k++; } if (k >= MIN_MATCH && k > best_len) { best_len = k; best_off = pos - j; if (best_len >= 255 + MIN_MATCH) break; } } } return [best_off, best_len]; } export function compress(input) { if (!(input instanceof Uint8Array)) throw new Error("compress expects Uint8Array"); let n = input.length; let i = 0; let anchor = 0; let out = []; let window = MAX_OFFSET; while (i < n) { let [off, match_len] = findLongestMatch(input, i, window); if (match_len >= MIN_MATCH) { let lit_len = i - anchor; let lit_nibble = Math.min(lit_len, 15); let match_nibble = Math.min(match_len - MIN_MATCH, 15); let token = lit_nibble * 16 + match_nibble; out.push(token); if (lit_len >= 15) { writeExtLen(out, lit_len - 15); } for (let k = anchor; k < i; k++) out.push(input[k]); out.push(off & 0xff, (off >> 8) & 0xff); let rem = match_len - MIN_MATCH; if (rem >= 15) { writeExtLen(out, rem - 15); } i += match_len; anchor = i; } else { i++; } } let final_lit = n - anchor; let tok_lit = Math.min(final_lit, 15); let token = tok_lit * 16; out.push(token); if (final_lit >= 15) { writeExtLen(out, final_lit - 15); } for (let k = anchor; k < n; k++) out.push(input[k]); return Uint8Array.from(out); } export function decompress(input) { if (!(input instanceof Uint8Array)) throw new Error("decompress expects Uint8Array"); let pos = 0; let n = input.length; let out = []; while (pos < n) { let token = input[pos++]; if (token === undefined) break; let lit_nibble = token >> 4; let match_nibble = token & 0xf; let lit_len = lit_nibble; if (lit_len === 15) { [lit_len, pos] = readExtLen(input, pos, 15); } for (let k = 0; k < lit_len; k++) { out.push(input[pos++]); } if (pos >= n) break; let off = input[pos] | (input[pos + 1] << 8); pos += 2; let match_len = match_nibble + MIN_MATCH; if (match_nibble === 15) { let extra; [extra, pos] = readExtLen(input, pos, 0); match_len += extra; } let sofarLen = out.length; let match_start = sofarLen - off; if (match_start < 0) throw new Error("Invalid offset"); for (let k = 0; k < match_len; k++) { out.push(out[match_start + k]); } } return Uint8Array.from(out); }