open-standards/wai/live-conformance/_rfc8878_zstd.py
Transaction Science 6c8eb07afe
All checks were successful
joule-moq-remote host agent / joule-moq-remote · does this change touch it (pull_request) Successful in 9s
joule-moq-room room service / joule-moq-room · does this change touch it (pull_request) Successful in 10s
joule-moq-web browser client / joule-moq-web · does this change touch it (pull_request) Successful in 9s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 9s
wai-app and Create / wai-app · does this change touch it (pull_request) Successful in 6s
wai-audit collector and player integration / does this change touch it (pull_request) Successful in 7s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (pull_request) Successful in 8s
WAI energy receipts / does this change touch it (pull_request) Successful in 6s
WAI examples / WAI examples · does this change touch them (pull_request) Successful in 6s
wai-fleet service / does this change touch it (pull_request) Successful in 6s
WAI operation catalogue / does this change touch it (pull_request) Successful in 13s
WAI conformance / does this change touch it (pull_request) Successful in 13s
wai-key-proxy / does this change touch it (pull_request) Successful in 6s
wai-ledger service / does this change touch it (pull_request) Successful in 6s
WAI neural sink gates / does this change touch it (pull_request) Successful in 5s
wai-quantum studio and desktop / does this change touch it (pull_request) Successful in 7s
wai-sink-android native sink library / does this change touch it (pull_request) Successful in 6s
wai-quantum for JavaScript / does this change touch it (pull_request) Successful in 9s
wai-sink-swift native sink package / does this change touch it (pull_request) Successful in 7s
wai-ops-gpu integer operators on GPUs / does this change touch it (pull_request) Successful in 14s
wai-pack / wai-pack · does this change touch it (pull_request) Successful in 15s
WAI Studio / does this change touch it (pull_request) Successful in 6s
wai-web reach builds / wai-web reach · does this change touch it (pull_request) Successful in 7s
wai-web wasm32 / does this change touch it (pull_request) Successful in 13s
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (pull_request) Successful in 3m27s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (pull_request) Successful in 33s
wai-audit collector and player integration / collector · linux (pull_request) Successful in 1m8s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (pull_request) Successful in 2m56s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 33s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (pull_request) Successful in 3m29s
wai-fleet service / service · linux (pull_request) Successful in 2m40s
wai-key-proxy / proxy · linux (pull_request) Successful in 1m5s
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m17s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m50s
wai-quantum studio and desktop / desktop app · windows (pull_request) Successful in 9m14s
wai-ledger service / service · linux (pull_request) Successful in 2m4s
WAI operation catalogue / the catalogue and every door · linux (pull_request) Successful in 5m10s
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (pull_request) Successful in 3m51s
WAI neural sink gates / the exact JPEG-AI decoder reports its path (pinned inputs, required) (pull_request) Successful in 2m38s
wai-pack / wai-pack · linux (pull_request) Successful in 1m47s
wai-quantum studio and desktop / studio · browser (pull_request) Successful in 3m57s
wai-quantum for JavaScript / javascript · linux (pull_request) Successful in 2m57s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (pull_request) Successful in 2m31s
WAI examples / WAI examples · portable · linux (pull_request) Successful in 8m25s
wai-ops-gpu integer operators on GPUs / builds · no adapter is not a pass (linux) (pull_request) Successful in 3m32s
wai-web reach builds / wai-web reach builds · the package of record, every output as recorded (pull_request) Successful in 2m36s
wai-quantum studio and desktop / desktop app · linux (pull_request) Successful in 5m39s
wai-web reach builds / wai-web reach builds · this tree's wai-web, every rung on its engine line (pull_request) Successful in 4m29s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 5m56s
WAI Studio / Studio in WebKit · linux (pull_request) Successful in 11m58s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 30m34s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 38m39s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (pull_request) Successful in 1m5s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 1m47s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 10m35s
wai-fleet service / service · macos (pull_request) Successful in 42s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (pull_request) Successful in 10m37s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 55s
wai-ledger service / service · macos (pull_request) Successful in 1m10s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 2m1s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 16m26s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 12m18s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 6m34s
wai-pack / wai-pack · macos (pull_request) Successful in 2m57s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 7m29s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 14m38s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 11m1s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 15m43s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 15m44s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 12m18s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m24s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (pull_request) Successful in 1m11s
joule-moq-room room service / joule-moq-room, every target (pull_request) Successful in 3m35s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 6m14s
WAI examples / WAI examples · android-app · linux-arm64 (pull_request) Successful in 5m41s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 44s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 12m22s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 16s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 32s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 2m3s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m38s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 1m53s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 17m20s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 25s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 15s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 6m59s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 18m2s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h12m0s
wai/prior-carriage-spec, converted to the layout of the fix: no shared append point
The change of wai/prior-carriage-spec (98192fe63..26834bb4c, 6 commits), on the fix branch: its
changelog entries as files of wai/changes/, its wai-conformance.yml edits as step files,
corpus files and test tags, and every other file as the branch has it.

- e5db9b313 wai: where a parameter set is fetched — set roots, location hints, and <base> defined
- 29fe43ea2 wai: a parameter set as an RFC 9842 compression dictionary, with a vector a stock client checks
- faaeb6c73 wai: when a parameter set is worth holding — amortisation charged per sink, bootstrap and generations
- 9a820db4a wai: a parameter set's usage statement, sealed sets released by key, leakage, and the privacy of held sets
- 89a9f9924 wai: the chunk-tree prior form, wai.prior.tree — a set verified piece by piece from any peers
- 26834bb4c wai: one playlist line of set roots, inner forms as the layouts list them, and a listing's usage statement read or the set refused

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-05 01:45:33 -05:00

526 lines
18 KiB
Python

"""A Zstandard decoder written from RFC 8878, in pure Python, for the prior-carriage verifier.
It decodes one frame against a raw-content dictionary (RFC 8878 §5: the dictionary's bytes are
history before the frame's first byte, and the repeat offsets start at 1, 4 and 8), checks the
frame's content size and its XXH64 checksum when they are stated, and refuses anything it does
not read with `ZstdError`. It shares no code with wai-rs, which decodes with the Zstandard
library. It is written for the corpus's small frames, not for speed.
"""
class ZstdError(Exception):
pass
MAGIC = 0xFD2FB528
MAX_BLOCK = 128 * 1024
def frame_window(frame):
"""(Window_Size, header length, content size or None, checksum flag) of a frame header."""
if len(frame) < 6 or int.from_bytes(frame[:4], "little") != MAGIC:
raise ZstdError("no frame magic")
fhd = frame[4]
if fhd & 0x08:
raise ZstdError("reserved bit set")
single = bool(fhd & 0x20)
did = (0, 1, 2, 4)[fhd & 3]
fcs = {0: 1 if single else 0, 1: 2, 2: 4, 3: 8}[fhd >> 6]
at = 5
window = None
if not single:
wd = frame[at]
at += 1
base = 1 << (10 + (wd >> 3))
window = base + (base // 8) * (wd & 7)
at += did
if len(frame) < at + fcs:
raise ZstdError("header truncated")
size = int.from_bytes(frame[at:at + fcs], "little") if fcs else None
if fcs == 2:
size += 256
at += fcs
if window is None:
window = size
return window, at, size, bool(fhd & 0x04)
# --- bit readers --------------------------------------------------------------------------------
class Forward:
"""Bits read from the least significant bit of each byte, first byte first (§4.1.1)."""
def __init__(self, data):
self.v = int.from_bytes(data, "little")
self.n = len(data) * 8
self.pos = 0
def read(self, k):
if self.pos + k > self.n:
raise ZstdError("table description past its end")
x = (self.v >> self.pos) & ((1 << k) - 1)
self.pos += k
return x
def peek(self, k):
return (self.v >> self.pos) & ((1 << k) - 1)
class Backward:
"""A bitstream read from its end (§4.1): the highest set bit of the last byte ends it."""
def __init__(self, data):
if not data or data[-1] == 0:
raise ZstdError("bitstream without its end mark")
self.v = int.from_bytes(data, "little")
self.left = self.v.bit_length() - 1
def read(self, k):
if k == 0:
return 0
self.left -= k
if self.left >= 0:
return (self.v >> self.left) & ((1 << k) - 1)
return (self.v << -self.left) & ((1 << k) - 1)
def peek(self, k):
if self.left >= k:
return (self.v >> (self.left - k)) & ((1 << k) - 1)
return (self.v << (k - self.left)) & ((1 << k) - 1)
def overflowed(self):
return self.left < 0
# --- FSE (§4.1) ---------------------------------------------------------------------------------
def read_distribution(br, max_symbol, max_log):
"""An FSE table description: (accuracy log, normalised counts)."""
log = br.read(4) + 5
if log > max_log:
raise ZstdError("accuracy log too large")
remaining = (1 << log) + 1
threshold = 1 << log
bits = log + 1
counts = []
previous_zero = False
while remaining > 1 and len(counts) <= max_symbol:
if previous_zero:
while True:
flag = br.read(2)
counts.extend([0] * flag)
if flag != 3:
break
if len(counts) > max_symbol:
break
top = (2 * threshold - 1) - remaining
low = br.peek(bits - 1)
if low < top:
value = low
br.read(bits - 1)
else:
value = br.peek(bits)
if value >= threshold:
value -= top
br.read(bits)
count = value - 1
remaining -= -count if count < 0 else count
counts.append(count)
previous_zero = count == 0
while remaining < threshold:
bits -= 1
threshold >>= 1
if remaining != 1 or len(counts) > max_symbol + 1:
raise ZstdError("distribution does not sum")
return log, counts
def build_table(log, counts):
"""The decoding table: per state, (symbol, bits, baseline)."""
size = 1 << log
symbol = [0] * size
high = size - 1
for s, c in enumerate(counts):
if c == -1:
symbol[high] = s
high -= 1
step = (size >> 1) + (size >> 3) + 3
pos = 0
for s, c in enumerate(counts):
for _ in range(max(c, 0)):
symbol[pos] = s
pos = (pos + step) & (size - 1)
while pos > high:
pos = (pos + step) & (size - 1)
if pos != 0:
raise ZstdError("distribution does not spread")
nxt = [1 if c == -1 else c for c in counts]
table = []
for u in range(size):
s = symbol[u]
x = nxt[s]
nxt[s] += 1
nb = log - (x.bit_length() - 1)
table.append((s, nb, (x << nb) - size))
return table
# --- Huffman literals (§4.2) --------------------------------------------------------------------
def huffman_weights(data):
"""(table, max bits, bytes read) from a Huffman tree description."""
if not data:
raise ZstdError("no tree description")
head = data[0]
if head < 128:
stream = data[1:1 + head]
if len(stream) != head:
raise ZstdError("tree description truncated")
fb = Forward(stream)
log, counts = read_distribution(fb, 255, 6)
table = build_table(log, counts)
used = (fb.pos + 7) // 8
bb = Backward(stream[used:])
s1, s2 = bb.read(log), bb.read(log)
weights = []
# Two states, alternating, until a state update reads past the stream's start; the
# other state then gives the last weight (§4.2.1.2).
while True:
sym, nb, base = table[s1]
weights.append(sym)
s1 = base + bb.read(nb)
if bb.overflowed():
weights.append(table[s2][0])
break
sym, nb, base = table[s2]
weights.append(sym)
s2 = base + bb.read(nb)
if bb.overflowed():
weights.append(table[s1][0])
break
if len(weights) > 255:
raise ZstdError("too many weights")
used = 1 + head
else:
n = head - 127
used = 1 + (n + 1) // 2
if len(data) < used:
raise ZstdError("tree description truncated")
weights = []
for i in range(n):
byte = data[1 + i // 2]
weights.append(byte >> 4 if i % 2 == 0 else byte & 15)
if any(w > 11 for w in weights):
raise ZstdError("weight too large")
total = sum(1 << (w - 1) for w in weights if w)
if total == 0:
raise ZstdError("no weights")
maxbits = total.bit_length()
rest = (1 << maxbits) - total
if rest & (rest - 1):
raise ZstdError("weights do not complete a tree")
weights.append(rest.bit_length())
if maxbits > 11:
raise ZstdError("code too long")
table = [None] * (1 << maxbits)
pos = 0
for w in range(1, 12):
for s, ws in enumerate(weights):
if ws == w:
nb = maxbits + 1 - w
for _ in range(1 << (w - 1)):
table[pos] = (s, nb)
pos += 1
if pos != len(table):
raise ZstdError("weights do not fill the table")
return (table, maxbits), used
def huffman_stream(data, tree, count):
table, maxbits = tree
br = Backward(data)
out = bytearray()
for _ in range(count):
s, nb = table[br.peek(maxbits)]
br.read(nb)
out.append(s)
if br.left != 0:
raise ZstdError("Huffman stream not consumed exactly")
return bytes(out)
def literals(block, state):
"""(literal bytes, bytes read) of a Literals_Section (§3.1.1.3.1)."""
b0 = block[0]
kind, fmt = b0 & 3, (b0 >> 2) & 3
if kind in (0, 1):
if fmt in (0, 2):
hl, size = 1, b0 >> 3
elif fmt == 1:
hl, size = 2, int.from_bytes(block[:2], "little") >> 4
else:
hl, size = 3, int.from_bytes(block[:3], "little") >> 4
if kind == 0:
if len(block) < hl + size:
raise ZstdError("raw literals truncated")
return block[hl:hl + size], hl + size
if len(block) < hl + 1:
raise ZstdError("RLE literals truncated")
return bytes([block[hl]]) * size, hl + 1
lhc = int.from_bytes(block[:5].ljust(5, b"\0"), "little")
if fmt in (0, 1):
hl, size, csize, streams = 3, (lhc >> 4) & 0x3FF, (lhc >> 14) & 0x3FF, 1 if fmt == 0 else 4
elif fmt == 2:
hl, size, csize, streams = 4, (lhc >> 4) & 0x3FFF, (lhc >> 18) & 0x3FFF, 4
else:
hl, size, csize, streams = 5, (lhc >> 4) & 0x3FFFF, (lhc >> 22) & 0x3FFFF, 4
body = block[hl:hl + csize]
if len(body) != csize:
raise ZstdError("compressed literals truncated")
at = 0
if kind == 2:
state["huffman"], at = huffman_weights(body)
elif state.get("huffman") is None:
raise ZstdError("treeless literals with no previous tree")
tree = state["huffman"]
if streams == 1:
out = huffman_stream(body[at:], tree, size)
else:
if len(body) < at + 6:
raise ZstdError("jump table truncated")
s1, s2, s3 = (int.from_bytes(body[at + 2 * i:at + 2 * i + 2], "little") for i in range(3))
at += 6
seg = (size + 3) // 4
parts = []
for i, n in enumerate((s1, s2, s3, len(body) - at - s1 - s2 - s3)):
if n < 0:
raise ZstdError("jump table past the literals")
parts.append(huffman_stream(body[at:at + n], tree, seg if i < 3 else size - 3 * seg))
at += n
out = b"".join(parts)
return out, hl + csize
# --- sequences (§3.1.1.3.2) ---------------------------------------------------------------------
LL_BASE = list(range(16)) + [16, 18, 20, 22, 24, 28, 32, 40, 48, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384,
32768, 65536]
LL_BITS = [0] * 16 + [1, 1, 1, 1, 2, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
ML_BASE = [n + 3 for n in range(32)] + [35, 37, 39, 41, 43, 47, 51, 59, 67, 83, 99, 131, 259, 515, 1027, 2051,
4099, 8195, 16387, 32771, 65539]
ML_BITS = [0] * 32 + [1, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
LL_DEFAULT = (6, [4, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 1, 1, 1, 1, 1,
-1, -1, -1, -1])
ML_DEFAULT = (6, [1, 4, 3, 2, 2, 2, 2, 2, 2] + [1] * 37 + [-1] * 7)
OF_DEFAULT = (5, [1, 1, 1, 1, 1, 1, 2, 2, 2] + [1] * 15 + [-1] * 5)
KINDS = (("ll", 35, 9, LL_DEFAULT), ("of", 31, 8, OF_DEFAULT), ("ml", 52, 9, ML_DEFAULT))
def sequences(block, state):
"""The sequences of a Sequences_Section: [(literals length, offset value, match length)]."""
if not block:
raise ZstdError("sequences section missing")
b0 = block[0]
if b0 == 0:
if len(block) != 1:
raise ZstdError("bytes after an empty sequences section")
return []
if b0 < 128:
n, at = b0, 1
elif b0 < 255:
n, at = ((b0 - 128) << 8) + block[1], 2
else:
n, at = block[1] + (block[2] << 8) + 0x7F00, 3
modes = block[at]
at += 1
if modes & 3:
raise ZstdError("reserved bits in the modes")
tables = {}
for (name, maxsym, maxlog, default), shift in zip(KINDS, (6, 4, 2)):
mode = (modes >> shift) & 3
if mode == 0:
tables[name] = build_table(*default)
elif mode == 1:
if block[at] > maxsym:
raise ZstdError("RLE symbol out of range")
tables[name] = [(block[at], 0, 0)]
at += 1
elif mode == 2:
fb = Forward(block[at:])
log, counts = read_distribution(fb, maxsym, maxlog)
tables[name] = build_table(log, counts)
at += (fb.pos + 7) // 8
else:
if state.get(name) is None:
raise ZstdError("repeat mode with no previous table")
tables[name] = state[name]
state[name] = tables[name]
br = Backward(block[at:])
def log_of(t):
return len(t).bit_length() - 1
ll, of, ml = (br.read(log_of(tables[k])) for k in ("ll", "of", "ml"))
out = []
for i in range(n):
ofc = tables["of"][of][0]
mlc = tables["ml"][ml][0]
llc = tables["ll"][ll][0]
if ofc > 31 or mlc > 52 or llc > 35:
raise ZstdError("code out of range")
offset = (1 << ofc) + br.read(ofc)
match = ML_BASE[mlc] + br.read(ML_BITS[mlc])
lit = LL_BASE[llc] + br.read(LL_BITS[llc])
out.append((lit, offset, match))
if i + 1 < n:
for k in ("ll", "ml", "of"):
s, nb, base = tables[k][{"ll": ll, "ml": ml, "of": of}[k]]
v = base + br.read(nb)
if k == "ll":
ll = v
elif k == "ml":
ml = v
else:
of = v
if br.left != 0:
raise ZstdError("sequence bitstream not consumed exactly")
return out
def execute(seqs, lits, history, reps):
"""Appends the block's output to `history` (dictionary and frame so far)."""
at = 0
for lit, value, match in seqs:
if at + lit > len(lits):
raise ZstdError("literals overrun")
history += lits[at:at + lit]
at += lit
if value > 3:
offset = value - 3
reps[:] = [offset, reps[0], reps[1]]
else:
idx = value + (1 if lit == 0 else 0)
if idx == 1:
offset = reps[0]
elif idx == 2:
offset = reps[1]
reps[:] = [offset, reps[0], reps[2]]
elif idx == 3:
offset = reps[2]
reps[:] = [offset, reps[0], reps[1]]
else:
offset = reps[0] - 1
reps[:] = [offset, reps[0], reps[1]]
if offset <= 0 or offset > len(history):
raise ZstdError("offset past the history")
start = len(history) - offset
for j in range(match):
history.append(history[start + j])
history += lits[at:]
# --- XXH64 --------------------------------------------------------------------------------------
M64 = (1 << 64) - 1
P1, P2, P3, P4, P5 = (0x9E3779B185EBCA87, 0xC2B2AE3D27D4EB4F, 0x165667B19E3779F9, 0x85EBCA77C2B2AE63,
0x27D4EB2F165667C5)
def _rotl(x, r):
return ((x << r) | (x >> (64 - r))) & M64
def _round(acc, lane):
return (_rotl((acc + lane * P2) & M64, 31) * P1) & M64
def xxh64(data, seed=0):
n = len(data)
i = 0
if n >= 32:
v = [(seed + P1 + P2) & M64, (seed + P2) & M64, seed, (seed - P1) & M64]
while i + 32 <= n:
for k in range(4):
v[k] = _round(v[k], int.from_bytes(data[i + 8 * k:i + 8 * k + 8], "little"))
i += 32
h = (_rotl(v[0], 1) + _rotl(v[1], 7) + _rotl(v[2], 12) + _rotl(v[3], 18)) & M64
for k in range(4):
h = ((h ^ _round(0, v[k])) * P1 + P4) & M64
else:
h = (seed + P5) & M64
h = (h + n) & M64
while i + 8 <= n:
h ^= _round(0, int.from_bytes(data[i:i + 8], "little"))
h = (_rotl(h, 27) * P1 + P4) & M64
i += 8
if i + 4 <= n:
h ^= (int.from_bytes(data[i:i + 4], "little") * P1) & M64
h = (_rotl(h, 23) * P2 + P3) & M64
i += 4
while i < n:
h ^= (data[i] * P5) & M64
h = (_rotl(h, 11) * P1) & M64
i += 1
h ^= h >> 33
h = (h * P2) & M64
h ^= h >> 29
h = (h * P3) & M64
h ^= h >> 32
return h
# --- a frame ------------------------------------------------------------------------------------
def decode(frame, dictionary=b"", limit=None):
"""The content of one whole frame, decoded against `dictionary` as raw content; nothing may
follow the frame. With `limit`, a frame whose content would pass it raises `TooLarge`."""
window, at, size, checksum = frame_window(frame)
history = bytearray(dictionary)
base = len(history)
reps = [1, 4, 8]
state = {}
while True:
if len(frame) < at + 3:
raise ZstdError("block header truncated")
h = int.from_bytes(frame[at:at + 3], "little")
at += 3
last, kind, bsize = h & 1, (h >> 1) & 3, h >> 3
if bsize > min(window, MAX_BLOCK) and kind != 1:
raise ZstdError("block too large")
if kind == 0:
if len(frame) < at + bsize:
raise ZstdError("raw block truncated")
history += frame[at:at + bsize]
at += bsize
elif kind == 1:
if len(frame) < at + 1:
raise ZstdError("RLE block truncated")
history += bytes([frame[at]]) * bsize
at += 1
elif kind == 2:
block = frame[at:at + bsize]
if len(block) != bsize:
raise ZstdError("compressed block truncated")
lits, used = literals(block, state)
execute(sequences(block[used:], state), lits, history, reps)
at += bsize
else:
raise ZstdError("reserved block type")
if limit is not None and len(history) - base > limit:
raise TooLarge()
if last:
break
content = bytes(history[base:])
if size is not None and len(content) != size:
raise ZstdError("content size not as stated")
if checksum:
if frame[at:at + 4] != (xxh64(content) & 0xFFFFFFFF).to_bytes(4, "little"):
raise ZstdError("checksum mismatch")
at += 4
if at != len(frame):
raise ZstdError("bytes after the frame")
return content
class TooLarge(Exception):
pass