weftcodec 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- weftcodec/__init__.py +62 -0
- weftcodec/__main__.py +6 -0
- weftcodec/ai_modes.py +3360 -0
- weftcodec/cli.py +252 -0
- weftcodec/crc32c.py +33 -0
- weftcodec/dcp.py +2108 -0
- weftcodec/dependency.py +224 -0
- weftcodec/errors.py +119 -0
- weftcodec/frame.py +455 -0
- weftcodec/hashing.py +56 -0
- weftcodec/huffman.py +212 -0
- weftcodec/identity.py +141 -0
- weftcodec/limits.py +58 -0
- weftcodec/modes.py +718 -0
- weftcodec/orbax.py +1734 -0
- weftcodec/service.py +959 -0
- weftcodec/torch.py +94 -0
- weftcodec/varint.py +88 -0
- weftcodec/wcb.py +310 -0
- weftcodec-1.2.0.dist-info/METADATA +257 -0
- weftcodec-1.2.0.dist-info/RECORD +24 -0
- weftcodec-1.2.0.dist-info/WHEEL +5 -0
- weftcodec-1.2.0.dist-info/entry_points.txt +2 -0
- weftcodec-1.2.0.dist-info/top_level.txt +1 -0
weftcodec/huffman.py
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""`huffman-canonical-v1` decoder (WEFT-AI-CODEC §5.3.1).
|
|
2
|
+
|
|
3
|
+
The serialized form is::
|
|
4
|
+
|
|
5
|
+
u8 code_lengths[256] # 0 = symbol absent
|
|
6
|
+
|| bitstream # MSB-first, zero-padded to a byte boundary
|
|
7
|
+
|
|
8
|
+
The decoder rebuilds the canonical table from the 256 lengths and rejects, in
|
|
9
|
+
this order:
|
|
10
|
+
|
|
11
|
+
1. a length greater than 24 -> ``NonCanonical``;
|
|
12
|
+
2. an over-subscribed or incomplete prefix table (Kraft sum, 24-bit
|
|
13
|
+
resolution, not exactly ``2**24``) -> ``NonCanonical``, except the
|
|
14
|
+
single-symbol form (one used symbol with length 1);
|
|
15
|
+
3. a truncated bitstream -> ``Corrupt``;
|
|
16
|
+
4. extra trailing bytes, or padding bits that are not zero -> ``Corrupt``;
|
|
17
|
+
5. an output length other than the declared ``element_count`` -> ``Corrupt``.
|
|
18
|
+
|
|
19
|
+
``decode`` consumes the whole slice; ``decode_prefix`` consumes exactly the
|
|
20
|
+
256-byte table plus this stream's own padded bitstream and returns that length
|
|
21
|
+
so callers can concatenate independently coded streams (modes 11/12/13/14/15/
|
|
22
|
+
16).
|
|
23
|
+
|
|
24
|
+
This is a deliberately straightforward canonical decoder: performance is not
|
|
25
|
+
the goal, exactness and identical error classes are.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from .errors import Corrupt, NonCanonical
|
|
31
|
+
|
|
32
|
+
TABLE_BYTES = 256
|
|
33
|
+
MAX_CODE_BITS = 24
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class _Canonical:
|
|
37
|
+
"""Canonical tables for one 256-byte code-length header."""
|
|
38
|
+
|
|
39
|
+
__slots__ = ("counts", "first_code", "first_index", "symbols", "max_len")
|
|
40
|
+
|
|
41
|
+
def __init__(self) -> None:
|
|
42
|
+
self.counts = [0] * (MAX_CODE_BITS + 1)
|
|
43
|
+
self.first_code = [0] * (MAX_CODE_BITS + 1)
|
|
44
|
+
self.first_index = [0] * (MAX_CODE_BITS + 1)
|
|
45
|
+
self.symbols = []
|
|
46
|
+
self.max_len = 0
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _build_table(lengths: bytes, expected_len: int) -> "tuple[_Canonical | None, bytes | None, int]":
|
|
50
|
+
"""Return ``(tables, single_symbol_output, used)``.
|
|
51
|
+
|
|
52
|
+
``tables`` is ``None`` for the empty and single-symbol forms. The second
|
|
53
|
+
element is the pre-filled output for a single-symbol table.
|
|
54
|
+
"""
|
|
55
|
+
if len(lengths) < TABLE_BYTES:
|
|
56
|
+
raise Corrupt(
|
|
57
|
+
"huffman-canonical-v1 stream is %d bytes, shorter than its %d-byte "
|
|
58
|
+
"code-length table" % (len(lengths), TABLE_BYTES)
|
|
59
|
+
)
|
|
60
|
+
lengths = lengths[:TABLE_BYTES]
|
|
61
|
+
counts = [0] * (MAX_CODE_BITS + 1)
|
|
62
|
+
used = 0
|
|
63
|
+
max_len = 0
|
|
64
|
+
for symbol, length in enumerate(lengths):
|
|
65
|
+
if length > MAX_CODE_BITS:
|
|
66
|
+
raise NonCanonical(
|
|
67
|
+
"huffman-canonical-v1 symbol %d has code length %d > %d"
|
|
68
|
+
% (symbol, length, MAX_CODE_BITS)
|
|
69
|
+
)
|
|
70
|
+
if length:
|
|
71
|
+
used += 1
|
|
72
|
+
counts[length] += 1
|
|
73
|
+
if length > max_len:
|
|
74
|
+
max_len = length
|
|
75
|
+
|
|
76
|
+
if used == 0:
|
|
77
|
+
if expected_len != 0:
|
|
78
|
+
raise Corrupt(
|
|
79
|
+
"huffman-canonical-v1 table has no symbols but the descriptor "
|
|
80
|
+
"declares a non-empty plane"
|
|
81
|
+
)
|
|
82
|
+
return None, b"", 0
|
|
83
|
+
|
|
84
|
+
if used == 1:
|
|
85
|
+
if max_len != 1:
|
|
86
|
+
raise NonCanonical(
|
|
87
|
+
"huffman-canonical-v1 single-symbol table must use code length 1"
|
|
88
|
+
)
|
|
89
|
+
if expected_len == 0:
|
|
90
|
+
raise NonCanonical(
|
|
91
|
+
"huffman-canonical-v1 non-empty table for an empty plane"
|
|
92
|
+
)
|
|
93
|
+
symbol = next(index for index, length in enumerate(lengths) if length)
|
|
94
|
+
return None, bytes([symbol]) * expected_len, 1
|
|
95
|
+
|
|
96
|
+
if expected_len == 0:
|
|
97
|
+
raise NonCanonical(
|
|
98
|
+
"huffman-canonical-v1 non-empty table for an empty plane"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Kraft equality proves a complete prefix code without building a table.
|
|
102
|
+
space = 1 << MAX_CODE_BITS
|
|
103
|
+
used_space = 0
|
|
104
|
+
for length in range(1, MAX_CODE_BITS + 1):
|
|
105
|
+
used_space += counts[length] << (MAX_CODE_BITS - length)
|
|
106
|
+
if used_space > space:
|
|
107
|
+
raise NonCanonical(
|
|
108
|
+
"huffman-canonical-v1 code lengths are over-subscribed"
|
|
109
|
+
)
|
|
110
|
+
if used_space != space:
|
|
111
|
+
raise NonCanonical("huffman-canonical-v1 code lengths are incomplete")
|
|
112
|
+
|
|
113
|
+
table = _Canonical()
|
|
114
|
+
table.counts = counts
|
|
115
|
+
table.max_len = max_len
|
|
116
|
+
# Canonical assignment: sort used symbols by (code length, symbol).
|
|
117
|
+
order = sorted(
|
|
118
|
+
(length, symbol) for symbol, length in enumerate(lengths) if length
|
|
119
|
+
)
|
|
120
|
+
code = 0
|
|
121
|
+
previous = 0
|
|
122
|
+
assigned = [0] * (MAX_CODE_BITS + 1)
|
|
123
|
+
symbols = []
|
|
124
|
+
for length, symbol in order:
|
|
125
|
+
code <<= length - previous
|
|
126
|
+
if assigned[length] == 0:
|
|
127
|
+
table.first_code[length] = code
|
|
128
|
+
table.first_index[length] = len(symbols)
|
|
129
|
+
assigned[length] += 1
|
|
130
|
+
symbols.append(symbol)
|
|
131
|
+
code += 1
|
|
132
|
+
previous = length
|
|
133
|
+
table.symbols = symbols
|
|
134
|
+
return table, None, used
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _decode_prefix_tables(
|
|
138
|
+
table: _Canonical, stream: bytes, expected_len: int
|
|
139
|
+
) -> "tuple[bytes, int]":
|
|
140
|
+
out = bytearray()
|
|
141
|
+
bit = 0
|
|
142
|
+
total_bits = len(stream) * 8
|
|
143
|
+
current_code = 0
|
|
144
|
+
current_len = 0
|
|
145
|
+
counts = table.counts
|
|
146
|
+
first_code = table.first_code
|
|
147
|
+
first_index = table.first_index
|
|
148
|
+
symbols = table.symbols
|
|
149
|
+
max_len = table.max_len
|
|
150
|
+
|
|
151
|
+
while len(out) < expected_len:
|
|
152
|
+
if bit >= total_bits:
|
|
153
|
+
raise Corrupt(
|
|
154
|
+
"huffman-canonical-v1 bitstream ended after %d of %d bytes"
|
|
155
|
+
% (len(out), expected_len)
|
|
156
|
+
)
|
|
157
|
+
byte = stream[bit >> 3]
|
|
158
|
+
current_code = (current_code << 1) | ((byte >> (7 - (bit & 7))) & 1)
|
|
159
|
+
current_len += 1
|
|
160
|
+
bit += 1
|
|
161
|
+
|
|
162
|
+
slot = current_len
|
|
163
|
+
if slot <= max_len:
|
|
164
|
+
first = first_code[slot]
|
|
165
|
+
if current_code >= first:
|
|
166
|
+
offset = current_code - first
|
|
167
|
+
if offset < counts[slot]:
|
|
168
|
+
out.append(symbols[first_index[slot] + offset])
|
|
169
|
+
current_code = 0
|
|
170
|
+
current_len = 0
|
|
171
|
+
continue
|
|
172
|
+
if current_len >= max_len:
|
|
173
|
+
raise Corrupt(
|
|
174
|
+
"huffman-canonical-v1 bitstream contains an invalid code"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
consumed_stream = (bit + 7) // 8
|
|
178
|
+
used_bits_in_last = bit % 8
|
|
179
|
+
if used_bits_in_last:
|
|
180
|
+
last = stream[consumed_stream - 1]
|
|
181
|
+
mask = (1 << (8 - used_bits_in_last)) - 1
|
|
182
|
+
if last & mask:
|
|
183
|
+
raise Corrupt("huffman-canonical-v1 padding bits must be zero")
|
|
184
|
+
return bytes(out), TABLE_BYTES + consumed_stream
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def decode_prefix(encoded: bytes, expected_len: int):
|
|
188
|
+
"""Decode one stream and return ``(bytes, consumed)``.
|
|
189
|
+
|
|
190
|
+
``consumed`` is the 256-byte table plus this stream's own padded bitstream.
|
|
191
|
+
Bytes after that prefix belong to the caller.
|
|
192
|
+
"""
|
|
193
|
+
if len(encoded) < TABLE_BYTES:
|
|
194
|
+
raise Corrupt(
|
|
195
|
+
"huffman-canonical-v1 stream is %d bytes, shorter than its %d-byte "
|
|
196
|
+
"code-length table" % (len(encoded), TABLE_BYTES)
|
|
197
|
+
)
|
|
198
|
+
table, single, _used = _build_table(encoded[:TABLE_BYTES], expected_len)
|
|
199
|
+
if table is None:
|
|
200
|
+
return single, TABLE_BYTES
|
|
201
|
+
return _decode_prefix_tables(table, encoded[TABLE_BYTES:], expected_len)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def decode(encoded: bytes, expected_len: int) -> bytes:
|
|
205
|
+
"""Decode exactly one complete stream; any trailing byte is ``Corrupt``."""
|
|
206
|
+
out, consumed = decode_prefix(encoded, expected_len)
|
|
207
|
+
if consumed != len(encoded):
|
|
208
|
+
raise Corrupt(
|
|
209
|
+
"huffman-canonical-v1 bitstream has trailing bytes after the "
|
|
210
|
+
"declared output"
|
|
211
|
+
)
|
|
212
|
+
return out
|
weftcodec/identity.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""Identity helpers: domain-separated BLAKE3 and frame verification."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from dataclasses import asdict, dataclass
|
|
7
|
+
|
|
8
|
+
from .dependency import Resolver
|
|
9
|
+
from .frame import Frame, parse
|
|
10
|
+
from .hashing import (
|
|
11
|
+
DOMAIN_CHUNK,
|
|
12
|
+
HASH_LEN,
|
|
13
|
+
content_root,
|
|
14
|
+
content_root_hex,
|
|
15
|
+
hash_domain,
|
|
16
|
+
hash_domain_hex,
|
|
17
|
+
)
|
|
18
|
+
from .limits import Limits
|
|
19
|
+
from .modes import decode_frame
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"DOMAIN_CHUNK",
|
|
23
|
+
"HASH_LEN",
|
|
24
|
+
"VerifyResult",
|
|
25
|
+
"content_root",
|
|
26
|
+
"content_root_hex",
|
|
27
|
+
"hash_domain",
|
|
28
|
+
"hash_domain_hex",
|
|
29
|
+
"verify_bytes",
|
|
30
|
+
"verify_frame",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class VerifyResult:
|
|
36
|
+
"""Structured result of parsing and decoding one frame."""
|
|
37
|
+
|
|
38
|
+
ok: bool
|
|
39
|
+
mode: "str | None" = None
|
|
40
|
+
mode_value: "int | None" = None
|
|
41
|
+
domain: "int | None" = None
|
|
42
|
+
flags: "list[str] | None" = None
|
|
43
|
+
features: "list[str] | None" = None
|
|
44
|
+
decoded_len: "int | None" = None
|
|
45
|
+
body_len: "int | None" = None
|
|
46
|
+
chunk_count: "int | None" = None
|
|
47
|
+
dependency_count: "int | None" = None
|
|
48
|
+
level: "int | None" = None
|
|
49
|
+
content_hash: "str | None" = None
|
|
50
|
+
computed_content_hash: "str | None" = None
|
|
51
|
+
error_class: "str | None" = None
|
|
52
|
+
error: "str | None" = None
|
|
53
|
+
|
|
54
|
+
def to_dict(self) -> dict:
|
|
55
|
+
return asdict(self)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _fill_from_header(result: VerifyResult, frame: Frame) -> None:
|
|
59
|
+
header = frame.header
|
|
60
|
+
result.mode = header.mode_name
|
|
61
|
+
result.mode_value = header.mode
|
|
62
|
+
result.domain = header.domain
|
|
63
|
+
result.flags = header.flag_names()
|
|
64
|
+
result.features = header.feature_names()
|
|
65
|
+
result.decoded_len = header.decoded_len
|
|
66
|
+
result.body_len = header.body_len
|
|
67
|
+
result.chunk_count = header.chunk_count
|
|
68
|
+
result.dependency_count = header.dep_count
|
|
69
|
+
result.level = header.level
|
|
70
|
+
result.content_hash = header.content_hash.hex()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def verify_bytes(
|
|
74
|
+
data: bytes,
|
|
75
|
+
resolver: "Resolver | None" = None,
|
|
76
|
+
limits: "Limits | None" = None,
|
|
77
|
+
base_frame: "bytes | None" = None,
|
|
78
|
+
) -> VerifyResult:
|
|
79
|
+
"""Parse, decode, and verify every hash in ``data``."""
|
|
80
|
+
limits = limits or Limits()
|
|
81
|
+
result = VerifyResult(ok=False)
|
|
82
|
+
try:
|
|
83
|
+
frame = parse(data, limits)
|
|
84
|
+
except Exception as exc: # typed Weft errors plus no unexpected swallowing
|
|
85
|
+
return _failure(result, exc)
|
|
86
|
+
_fill_from_header(result, frame)
|
|
87
|
+
try:
|
|
88
|
+
plaintext = decode_frame(frame, resolver, limits, base_frame)
|
|
89
|
+
except Exception as exc:
|
|
90
|
+
return _failure(result, exc, keep_header=True)
|
|
91
|
+
result.computed_content_hash = content_root_hex(plaintext)
|
|
92
|
+
result.ok = True
|
|
93
|
+
return result
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def verify_frame(
|
|
97
|
+
path: "str | os.PathLike[str]",
|
|
98
|
+
resolver: "Resolver | None" = None,
|
|
99
|
+
limits: "Limits | None" = None,
|
|
100
|
+
base_frame: "bytes | None" = None,
|
|
101
|
+
) -> VerifyResult:
|
|
102
|
+
"""Read ``path``, parse, decode, and verify it. Never raises for codec errors."""
|
|
103
|
+
limits = limits or Limits()
|
|
104
|
+
result = VerifyResult(ok=False)
|
|
105
|
+
try:
|
|
106
|
+
size = os.path.getsize(path)
|
|
107
|
+
except OSError as exc:
|
|
108
|
+
from .errors import Io
|
|
109
|
+
|
|
110
|
+
return _failure(result, Io("cannot stat %s: %s" % (path, exc)))
|
|
111
|
+
if size > limits.max_frame_bytes:
|
|
112
|
+
from .errors import LimitExceeded
|
|
113
|
+
|
|
114
|
+
return _failure(
|
|
115
|
+
result,
|
|
116
|
+
LimitExceeded(
|
|
117
|
+
"frame length %d exceeds limit %d" % (size, limits.max_frame_bytes)
|
|
118
|
+
),
|
|
119
|
+
)
|
|
120
|
+
try:
|
|
121
|
+
with open(path, "rb") as handle:
|
|
122
|
+
data = handle.read()
|
|
123
|
+
except OSError as exc:
|
|
124
|
+
from .errors import Io
|
|
125
|
+
|
|
126
|
+
return _failure(result, Io("cannot read %s: %s" % (path, exc)))
|
|
127
|
+
return verify_bytes(data, resolver, limits, base_frame)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _failure(
|
|
131
|
+
result: VerifyResult, exc: Exception, keep_header: bool = False
|
|
132
|
+
) -> VerifyResult:
|
|
133
|
+
error_class = getattr(exc, "error_class", None)
|
|
134
|
+
if error_class is None:
|
|
135
|
+
raise exc
|
|
136
|
+
result.ok = False
|
|
137
|
+
result.error_class = error_class
|
|
138
|
+
result.error = str(exc)
|
|
139
|
+
if not keep_header:
|
|
140
|
+
result.mode = None
|
|
141
|
+
return result
|
weftcodec/limits.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Decoder/encoder resource limits (WEFTCODEC-FORMAT §10).
|
|
2
|
+
|
|
3
|
+
Defaults are the normative v1 values. Every field can be lowered; nothing in
|
|
4
|
+
this package raises a limit above the frozen default.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, fields, replace
|
|
10
|
+
|
|
11
|
+
DEFAULT_MAX_FRAME_BYTES = 1 << 30
|
|
12
|
+
DEFAULT_MAX_DECODED_BYTES = 4 << 30
|
|
13
|
+
DEFAULT_MAX_DEPENDENCY_DEPTH = 8
|
|
14
|
+
DEFAULT_MAX_DEPENDENCY_COUNT = 64
|
|
15
|
+
DEFAULT_MAX_TOTAL_FETCHED_BYTES = 8 << 30
|
|
16
|
+
DEFAULT_MAX_CHUNKS = 1_048_576
|
|
17
|
+
DEFAULT_MAX_DICTIONARY_BYTES = 64 << 20
|
|
18
|
+
DEFAULT_MAX_STRUCTURED_DEPTH = 64
|
|
19
|
+
DEFAULT_MAX_STRUCTURED_ITEMS = 1_000_000
|
|
20
|
+
DEFAULT_MAX_STRING_BYTES = 256 << 20
|
|
21
|
+
DEFAULT_MAX_DELTA_OPS = 16_777_216
|
|
22
|
+
DEFAULT_MAX_TENSOR_ELEMENTS = 1 << 32
|
|
23
|
+
DEFAULT_MAX_TENSOR_RANK = 8
|
|
24
|
+
DEFAULT_MAX_TOKENS = 1 << 24
|
|
25
|
+
DEFAULT_MAX_SPARSE_RUNS = 1 << 24
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(frozen=True)
|
|
29
|
+
class Limits:
|
|
30
|
+
max_frame_bytes: int = DEFAULT_MAX_FRAME_BYTES
|
|
31
|
+
max_decoded_bytes: int = DEFAULT_MAX_DECODED_BYTES
|
|
32
|
+
max_dependency_depth: int = DEFAULT_MAX_DEPENDENCY_DEPTH
|
|
33
|
+
max_dependency_count: int = DEFAULT_MAX_DEPENDENCY_COUNT
|
|
34
|
+
max_total_fetched_bytes: int = DEFAULT_MAX_TOTAL_FETCHED_BYTES
|
|
35
|
+
max_chunks: int = DEFAULT_MAX_CHUNKS
|
|
36
|
+
max_dictionary_bytes: int = DEFAULT_MAX_DICTIONARY_BYTES
|
|
37
|
+
max_structured_depth: int = DEFAULT_MAX_STRUCTURED_DEPTH
|
|
38
|
+
max_structured_items: int = DEFAULT_MAX_STRUCTURED_ITEMS
|
|
39
|
+
max_string_bytes: int = DEFAULT_MAX_STRING_BYTES
|
|
40
|
+
max_delta_ops: int = DEFAULT_MAX_DELTA_OPS
|
|
41
|
+
max_tensor_elements: int = DEFAULT_MAX_TENSOR_ELEMENTS
|
|
42
|
+
max_tensor_rank: int = DEFAULT_MAX_TENSOR_RANK
|
|
43
|
+
max_tokens: int = DEFAULT_MAX_TOKENS
|
|
44
|
+
max_sparse_runs: int = DEFAULT_MAX_SPARSE_RUNS
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def from_overrides(cls, overrides: "dict[str, int] | None") -> "Limits":
|
|
48
|
+
"""Build limits from a vector's ``limits`` object.
|
|
49
|
+
|
|
50
|
+
Unknown keys are rejected so a typo can never silently widen a bound.
|
|
51
|
+
"""
|
|
52
|
+
if not overrides:
|
|
53
|
+
return cls()
|
|
54
|
+
known = {f.name for f in fields(cls)}
|
|
55
|
+
unknown = set(overrides) - known
|
|
56
|
+
if unknown:
|
|
57
|
+
raise ValueError("unknown limit(s): %s" % ", ".join(sorted(unknown)))
|
|
58
|
+
return replace(cls(), **{k: int(v) for k, v in overrides.items()})
|