zrip 0.8.2

Fast, memory-safe Rust implementation of Zstandard compression (levels -8..4)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
# Design

Architecture, performance techniques, and divergences from C zstd. Each
section explains what zrip does, how it differs, and why.


## Encode pipeline

### Fast strategy (`crates/encode/src/fast.rs`)

Port of C zstd's `ZSTD_compressBlock_fast_noDict_generic` 4-cursor match finder.
The pipeline probes positions ip0, ip1=ip0+1, ip2=ip0+step, ip3=ip2+1 per
iteration, reusing hash computations across shifts. Amortizes loop overhead
and branch mispredictions vs a single-position-per-iteration approach. Rep
offset is checked at the step-ahead position (ip2) only.

Rep2 post-match chaining (`rep2_match_loop`): after any match, immediately
checks `rep_offset2` for consecutive rep matches (zero-literal-length
sequences). Chains matches without re-entering the main loop. 1-byte
backward extension on rep matches (C zstd style), multi-byte backward
extension on regular matches.

Const-generic dispatch on minimum match length via
`compress_fast_block_impl::<HASH_LOG, MLS>`:

| MLS | Hash function | Used at levels |
|-----|---------------|----------------|
| 4   | `hash4` (PRIME32_1) | 1..2 |
| 5   | `hash5` (PRIME64_1) | -8..-1 |
| 7   | `hash7` (custom PRIME7) | custom params only |

Hash table prefetch on x86_64 (`_mm_prefetch`) and aarch64 (inline asm
`prfm pldl1keep`) hides memory latency in the match-finding loop.

Incompressibility bail-out: at probe intervals, if
`total_match_bytes * 6 < scanned`, the compressor clears sequences and returns
early (the block is emitted raw).


### DFast strategy (`crates/encode/src/dfast.rs`)

Dual hash tables: short (4-byte matches, `chain_log`) + long (8-byte matches,
`hash_log`). Same 4-cursor pipeline adapted for two tables with prefetch for
both. `search_log` controls when to try improving a short match with a long
match. Same incompressibility bail-out as Fast.


### Level parameters (`crates/encode/src/strategy.rs`)

Based on C zstd's `ZSTD_defaultCParameters` table, with L-8 added by zrip.
Level 0 is not a separate parameter set; it maps to the library default,
currently level 1.

| Level | Strategy | Hash table | Window | Min match | Target | Search log | Search strength | Notes |
|------:|:---------|:-----------|:-------|:---------:|-------:|-----------:|----------------:|:------|
| -8 | Fast | 32 KiB | 512 KiB | 5 | 7 | 0 | 7 | `force_raw_literals` (zrip-only) |
| -7 | Fast | 64 KiB | 512 KiB | 5 | 9 | 0 | 7 | Tiny inputs use target 6 |
| -6 | Fast | 64 KiB | 512 KiB | 5 | 7 | 0 | 7 | |
| -5 | Fast | 64 KiB | 512 KiB | 5 | 6 | 0 | 7 | |
| -4 | Fast | 64 KiB | 512 KiB | 5 | 5 | 0 | 7 | |
| -3 | Fast | 64 KiB | 512 KiB | 5 | 4 | 0 | 7 | |
| -2 | Fast | 64 KiB | 512 KiB | 5 | 3 | 0 | 7 | |
| -1 | Fast | 64 KiB | 512 KiB | 5 | 2 | 0 | 7 | |
| 1 | Fast | 64 KiB | 512 KiB | 4 | 1 | 0 | 8 | default |
| 2 | Fast | 512 KiB | 1 MiB | 4 | 1 | 0 | 8 | |
| 3 | DFast | 2x 1 MiB | 2 MiB | 4 | 1 | 1 | 5 | Dual hash; 32-128 KiB uses strength 7 |
| 4 | DFast | 2x 4 MiB | 16 MiB | 4 | 1 | 0 | 8 | Dual hash |

Negative levels trade ratio for throughput by increasing `target_length`
(skip acceleration). Higher `target_length` means more positions are skipped
on consecutive match-finding misses.

**L-8** skips Huffman table construction and always emits raw literal blocks
with predefined FSE tables. This eliminates the most expensive part of the
encode pipeline (Huffman tree build, stream encoding, custom FSE table
estimation) at the cost of compression ratio. The result is a valid zstd
frame that any decoder handles, but with LZ4-class encode throughput.

**L-7 through L2** use the full encode pipeline: Huffman-compressed literals
(with treeless reuse across blocks) and predefined or custom FSE tables for
sequences, whichever produces smaller output.

**L3 and L4** use the DFast strategy with two hash tables (short 4-byte and
long 8-byte matches) for better match quality at lower throughput.

**Two-tier raw literals decision.** Avoids the expensive
build-Huffman-table-encode-discard cycle for blocks where Huffman overhead
exceeds savings:

1. Size override (`strategy.rs:apply_raw_literals_size_override`): L-8 always
   uses raw literals. Other Fast levels only force raw literals when
   `min_match >= 5`, `target_length == 7`, and the input is <= 16 KiB. With
   the built-in table, this applies to L-6. Other levels rely on the entropy
   pre-check.

2. Entropy pre-check (`block_encoder.rs:huf_worth_trying`): for blocks
   <= 32 KiB that passed the size ramp, estimates compressed size from a
   byte histogram using integer-only fixed-point log2. Skips Huffman if
   estimated savings (including tree description overhead) are below
   ~3%. Also bails immediately if `max_sym > 128` (high byte range means
   expensive tree description for marginal gain). Blocks > 32 KiB always
   try Huffman since tree overhead is negligible at that scale.


## Block encoder (`crates/encode/src/block_encoder.rs`)

Per-block decisions:

**Literals**: Level -8 always emits raw literal blocks (skips Huffman tree
construction entirely). Other Fast levels may also force raw literals for
small inputs through the per-level raw-literal size ramp in
`strategy.rs:apply_raw_literals_size_override`. All other blocks try
Huffman-compressed literals with treeless reuse across blocks via
`prev_huffman`. For literal blocks >= 1024 bytes, encodes into 4 independent
streams to enable decoder-side parallelism.

**3-way FSE table mode selection.** Compares predefined, custom
(data-derived), and repeat (carry-over from previous block or dict) FSE
encodings. Picks the smallest. Estimates predefined cost without full
encoding to skip the custom path when it cannot win. Blocks with < 64
sequences always use predefined tables (custom table header overhead exceeds
savings).

**Pre-packed sequence batch** (`pack_sequences_and_literals`). Pre-computes
all LL/ML/OF codes, extra bits, and rep-offset resolution into `PackedSeq`
structs in a single pass. FSE encoding then iterates the packed array without
recomputing.

**SymbolTT encoding scheme.** Two-field per-symbol struct (`delta_nb_bits`,
`delta_find_state`) encodes variable-width emission and state transition in
one lookup. ~1.3 KiB total footprint vs ~20 KiB for full tables.

**Incompressibility fast path**: `block_looks_incompressible()` samples the
first 1024 bytes. Skips compression if >= 200 distinct byte values and max
frequency < 1/24. Applied before entering the match finder.

**Fallback**: If the compressed block >= source size, rep offsets are reverted
and a raw block is emitted.

**Predefined encode table caching.** `LazyLock` builds LL/ML/OF predefined
encode tables once per process.


## Dictionary compression

`train_dict_fastcover()` in `crates/core/src/dict/mod.rs`: FastCOVER segment
selection + `finalize_dictionary()` produces standard zstd dictionary format
(magic + dict_id + Huffman table + FSE tables + rep offsets + content).
`FastCoverParams` defaults: k=2048, d=8, accel=1. Full COVER also exists in
`core/src/dict/cover.rs`.

### 3-tier dict setup (`context.rs:compress_with_prepared`)

Adapts setup cost to input size:

| Tier | Input size | Hash setup | Match finder | Literals |
|------|-----------|-----------|-------------|----------|
| A (micro) | <= per-level threshold | zero small input table | `compress_fast_attached` (linear scan, dual-lookup) | raw |
| B (small) | threshold-16 KiB | zero moderate input table | `compress_fast_attached` (linear scan, dual-lookup) | Huffman from dict |
| C (large) | > 16 KiB | copy full snapshot | `compress_fast_block` (4-cursor pipeline) | Huffman from dict |

Thresholds: `ATTACH_THRESHOLD` (16 KiB), per-level raw literals ramp (see
level parameters above). DFast strategy (levels 3-4) always uses the copy
path.

### Dict attachment (`fast.rs:compress_fast_attached`)

For tiers A and B, the PreparedDict's hash snapshot is referenced as a frozen
read-only table. A separate zeroed input hash table accumulates entries from
the current input. Two hash lookups per position (input table first, dict
table fallback). Zero-copy: no `memcpy` of the dict hash per frame.

Why a separate loop: the 4-cursor pipeline has ~15 live values for x86_64's
15 GPRs, with 5 stack spills per iteration. Adding dict attachment state
(dict hash pointer, second hash value, dict match candidate) would push to
~17+ live values and worsen spills. The simpler linear-scan loop has fewer
live variables and tolerates the extra state. For small inputs, the pipeline
startup cost dominates anyway.

### Hash log clamping (`strategy.rs:level_params_for_size`)

Clamps `hash_log`, `chain_log`, and `window_log` to `src_log` for small
inputs. The input-side hash table in the attached path uses this clamped size
while the dict-side table keeps the level's full `hash_log`. Reduces table
init cost without losing dict coverage.

### Hash table prefill (`fast.rs:prefill_hash_table`)

Fills the hash table from the dict prefix with strided sampling (step =
prefix_len / hash_size), then densely hashes the last 64 bytes. Balances
coverage against initialization cost.

### Dict entropy table carryover (`context.rs:PreparedDict::new`)

Converts the dictionary's decode FSE/Huffman tables to encode tables at
construction time. The first block uses dict tables as FSE "repeat" mode.

### Encoder prefix mode

Concatenates dict content + source, pre-fills hash table from dict content,
then runs the normal match finder over the combined buffer.


## Streaming

`crates/encode/src/streaming.rs`: `FrameEncoder<W: Write>` buffers input up to
128 KiB (`MAX_BLOCK_SIZE`), then flushes as a compressed block. Frame header
includes magic, descriptor (with content checksum flag), window descriptor, and
optional dict ID. `finish()` flushes the final block with `last=true` and writes
the XXH64 content checksum (lower 32 bits).

Cross-block matching: the encoder maintains a persistent window buffer
(`window_buf`) containing previous blocks' output. Hash table positions reference
into this combined view, allowing matches across block boundaries.

**Hash table position reduction** (`streaming.rs:reduce_hash_table`). When the
window buffer exceeds 2x window size, shifts all hash entries by `shift` and
compacts the window. Preserves hash state across blocks.

`Options` API controls window size and LDM: `Options::default().window_log(24).ldm(true)`.
`FrameEncoder::with_options(writer, level, &opts)` applies these to the frame.

`reset()` finishes the current frame, swaps the writer, and reuses all internal
buffers (hash tables, workspace, block encoder).

First block with a dictionary uses `compress_fast_with_prefix_reuse` /
`compress_dfast_with_prefix_reuse`. Subsequent blocks clear hash tables and run
standard block compression.


## Long distance matching (LDM)

`crates/encode/src/ldm.rs`. Two-pass integration with Fast/DFast strategies.

### Gear hash sampling

The gear hash is a 1-byte-at-a-time rolling hash:
`rolling = (rolling << 1) + GEAR_TABLE[byte]`, where `GEAR_TABLE` is 256
random `u64` values. After each byte, `rolling & stop_mask` is checked. When
it equals zero, the position is a "split point" where LDM samples a candidate.
`stop_mask` is derived from `hash_rate_log`: it has `hash_rate_log` bits set in
the upper part of the accumulator, so roughly 1 in `2^hash_rate_log` positions
trigger a check. At the default `hash_rate_log=6`, about 1 in 64 positions are
sampled.

The `feed()` method processes input in 4-byte unrolled batches (`LDM_BATCH_SIZE`
= 64 split points per call) for cache efficiency.

### Hash table

The table stores `LdmEntry` values: `offset: u32` (position in the input) +
`checksum: u32` (upper 32 bits of xxh64 over `min_match_length` bytes at that
position). At `hash_log=20`, this is 1M entries = 8 MiB. The table does not
store input data, so a fixed-size table covers arbitrarily large inputs.

Entries are organized into buckets of `1 << bucket_size_log` slots (default 8).
The lower bits of the xxh64 select a bucket, the upper 32 bits become the
checksum. Each bucket has a `u8` write counter in `bucket_offsets[]` that wraps
with `(counter + 1) & bucket_mask`. This is a per-bucket ring buffer: new
entries overwrite whichever slot the counter points at, round-robin. No age
tracking, no LRU.

When a split fires, if an existing entry in the selected bucket has a matching
checksum, the actual bytes at both positions are compared to confirm a match.
Backward and forward extension finds the full match length.

### Two-pass compression

First pass: `generate_sequences()` scans the input with the gear hash, probes
the hash table at each split point, and collects `RawLdmSeq` entries (literal
length, match length, offset). Second pass: `compress_block()` iterates over
the LDM sequences. For each gap between LDM matches, the regular Fast or DFast
compressor runs on that segment and emits short-range sequences. LDM sequences
and gap-fill sequences are interleaved into the final sequence list.

On the first block, `fill_only()` populates the hash table without searching
(there is no prior context to match against).

### Parameters

`LdmParams` controls: `hash_log` (table size), `bucket_size_log` (slots per
bucket), `min_match_length` (minimum bytes for a valid LDM match), and
`hash_rate_log` (sampling density). `LdmParams::default_for_window_log(wl)`
computes defaults for a given window size.

### Streaming and cross-block

In streaming mode, `LdmState` persists across blocks: the hash table retains
entries from previous blocks. `reduce_positions(shift)` subtracts `shift` from
all stored offsets when the window buffer compacts, discarding entries that
would point before the new buffer start.

### Memory and decoder impact

The encoder allocates the 8 MiB LDM hash table plus a window buffer of
`1 << window_log` bytes. The decoder does not know LDM was used. LDM sequences
are standard zstd sequences with larger offsets. The decoder allocates a window
buffer matching the `window_size` declared in the frame header, which is set by
`window_log`. At `window_log=27` this is ~128 MiB on each side.

Enable via `Options::ldm(true)` (requires the `ldm` feature, on by default).


## Decode pipeline

### Sequence execution (`crates/decode/src/exec.rs`)

`#![forbid(unsafe_code)]`. A single safe Rust implementation handles all
platforms. `SeqTable` stores fixed-size 512-entry FSE sequence tables indexed
with `state & 511`. In default builds the backing storage is
`[MaybeUninit<FseSeqDecodeEntry>; 512]`, so table promotion initializes only
the states that the FSE accuracy log can reach. The hot loop copies each
`FseSeqDecodeEntry` out of the table, letting LLVM keep fields in registers.
Under `paranoid`, `SeqTable` uses fully initialized
`[FseSeqDecodeEntry; 512]` storage and the hot loop borrows entries by
reference to avoid copying initialized padding-heavy structs.

2-sequence unrolled decode loop: decodes 2 sequences per iteration while the
bit buffer has >= 16 bytes remaining. Cuts loop overhead in half.

Pre-allocated output buffer: reserves `MAX_BLOCK_SIZE + 64` bytes upfront so
wild copies never trigger reallocation.

Literal copies and match copies use wild-copy functions in `fast_vec.rs`
(see [Wild copies](#wild-copies) below).

`fearless_simd::dispatch!` compiles the function with platform target features,
enabling the compiler to generate BMI2 bit extraction instructions (`shlx`,
`shrx`) and auto-vectorize bitstream operations.


### FSE tables

**Fixed-size tables with mask** (`crates/decode/src/seq_table.rs`).
`SeqTable` uses 512-entry storage with `& FSE_SEQ_TABLE_MASK` instead of
variable-size `Vec` indexing. This removes per-block sequence table
allocation and uses bitwise masking instead of modular arithmetic.

**Promoted FSE tables** (`seq_table.rs:SeqTable::promote_ll` etc.).
`FseSeqDecodeEntry` pre-computes `extra_bits` and `baseline_value` from the
LL/ML/OF code tables. Decode reads one struct instead of doing a separate
baseline/bits table lookup per symbol.

**In-place table building** (`sequences.rs:parse_sequence_tables_ws`). Builds
decode tables into workspace buffers instead of returning new Vecs.

**Predefined table caching** (`sequences.rs`). `LazyLock` for LL/ML/OF
predefined tables in `std` builds. Built once, then `SeqTable`-cloned per use.


### Bitstream (`crates/core/src/bitstream/reader_reverse.rs`)

**bitsConsumed model.** Tracks consumed bits instead of available bits.
Peek uses `(container << consumed) >> (64 - n)` (2 ops) vs the 3-op
available-bits model.

**Branchless bit reads** (`read_bits_branchless`).
`(container << (consumed & 63)) >> 1 >> (63 - n)` avoids the n==0 branch
in the hot FSE decode loop.

**Fast refill** (`refill_fast`). Byte-aligned reload without bounds checking
when the pointer is far from buffer start.

**Branch hints** (`hint.rs:likely`, `unlikely`). `#[cold]` function call to
hint branch prediction. Used for error paths and loop exits.


### Huffman 4-stream decoder (`crates/core/src/huffman/decode_4stream.rs`)

Interleaved 4-stream parallel decode: the compressed literals are split into 4
segments, each with an independent `ReverseBitReader`. The fast loop decodes 5
symbols per stream per iteration (20 symbols total), checking both output bounds
and bitstream bounds. Index-based output with safe slice indexing and inline
`refill!`/`decode_one!` macros.

4-symbol unrolled 1-stream inner loop: decodes 4 symbols per iteration when
bits remaining >= 4 * table_log.

BMI2 variant via `#[target_feature(enable = "bmi2")]` for faster bit extraction
(PEXT/PDEP). Dispatched via `cpu_tier() >= CpuTier::Bmi2`.
Slow path finishes remaining symbols one stream at a time near exhaustion.

Treeless block reuse: `huf_valid` flag allows treeless literal blocks to reuse
the previous Huffman decode table with no re-parse or rebuild.

Literal buffer padding: appends 32 zero bytes past literal data. Enables
unconditional SIMD loads in the sequence decode loop without bounds checks.


### Wild copies (`crates/decode/src/fast_vec.rs`)

All unsafe gated by `#[cfg(not(feature = "paranoid"))]`.

- **16-byte chunk copy** (`copy_16`). Two unaligned u64 load/stores for
  literal and match copies.
- **fast_extend_from_slice**. Tiered: >= 16 bytes uses 16-byte chunks,
  8-15 uses two 8-byte copies, 4-7 uses two 4-byte copies, < 4 uses
  `copy_nonoverlapping`. All paths use `set_len` to skip initialization.
- **wild_copy_match with offset dispatch**. Five paths by offset: >= 16
  (non-overlapping 16-byte copies), == 1 (RLE via `write_bytes`), 8-15
  (overlapping 8-byte copies), 2-7 (pattern stamping via
  `build_pattern_u64`).
- **build_pattern_u64**. Builds an 8-byte repeating pattern from `offset`
  source bytes without reading uninitialized memory.

Under `paranoid`, these fall back to `extend_from_slice` and
`extend_from_within`.


## CPU feature dispatch

Two dispatch mechanisms coexist:

**Decoder:** `fearless_simd::dispatch!` with a cached `OnceLock<Level>`. The
macro compiles `decode_execute_sequences` with platform-specific target features
(AVX2+BMI2 on x86_64, NEON on aarch64, SIMD128 on wasm32) and selects the best
version at runtime when the `simd` feature is enabled. Zero `unsafe` in user
code. Works under `paranoid` since `fearless_simd` requires no unsafe. Without
`std` or `simd`, the scalar path runs directly.

**Huffman BMI2:** `crates/core/src/simd/mod.rs` defines a `CpuTier` enum
(`Bmi2`/`Avx2` on x86_64). Runtime detection cached in `OnceLock<CpuTier>`.
The Huffman 4-stream decoder dispatches to a `#[target_feature(enable = "bmi2")]`
variant for faster bit extraction.

**Compile-time tier for no_std.** Falls back to `cfg!(target_feature = "...")`
checks when std is unavailable.


## Compile-time specialization

Const generics eliminate dead code at compile time:

- `compress_fast_block_impl::<HASH_LOG, MLS>`: LLVM specializes per MLS (4, 5, 7),
  dead-code eliminates unused hash/match paths.
- `compress_dfast_block_impl::<HASH_LOG, SHORT_LOG, MLS>`: same for DFast.
- `hash_pos::<HASH_LOG, MLS>`: selects hash4/hash5/hash7 at compile time.
- Strategy dispatch (`Fast` vs `DFast`) happens at block boundaries, not
  per-position.


## Allocation reuse

**CompressContext** (`crates/encode/src/context.rs`). Hash tables, output
buffer, sequence vec, and workspace are allocated once and reused across calls.

**BlockEncodeWorkspace** (`crates/encode/src/block_encoder.rs`). All scratch
buffers (lit_buf, lit_section, pred_seq, cust_seq, repeat_seq, huf_concat,
huf_stream, writer bufs, packed_seqs) allocated once, reused across blocks.

**Cow output borrowing** (`context.rs:take_or_borrow_output`). Small outputs
return `Cow::Borrowed` (zero allocation), large outputs return `Cow::Owned`
(moves the buffer out).

**DecompressContext** (`crates/decode/src/context.rs`). Output buffer and
`BlockDecodeWorkspace` allocated once, reused across frames.

**BlockDecodeWorkspace** (`crates/decode/src/lib.rs`). All scratch vectors
(literal_buf, huf_table, huf_all_weights, huf_rank_count, huf_rank_start,
fse_dist, fse_symbol_next, fse_build_buf) allocated once.

**Dict FSE/Huffman caching** (`crates/decode/src/lib.rs:cache_dict`).
Pre-promotes dict FSE decode tables (LL/ML/OF) and Huffman table at
construction time. Each frame clones the cached promoted tables instead of
rebuilding from raw dictionary entries. Avoids 3 intermediate Vec allocations
per frame.


## Unsafe boundary

Decode execution (`exec.rs`) and sequence parsing (`sequences.rs`) are
`#![forbid(unsafe_code)]`. Platform dispatch is handled by
`fearless_simd::dispatch!`, which encapsulates `#[target_feature]` calling
internally.

Encoder unsafe is confined to `encode/src/primitives.rs` (16 blocks):

- **Unchecked hash table access** (`hash_load`, `hash_store`).
  `get_unchecked`/`get_unchecked_mut` eliminates bounds checks in the
  hot loop.
- **Unaligned 4/8-byte reads** (`rd32`, `rd64`). `read_unaligned` for
  hash input, match confirmation, and match counting.
- **Pointer-based match counting** (`count_match_raw`). Compares 8 bytes
  at a time via XOR + `trailing_zeros()`. Falls to a byte loop for the
  tail.
- **16-byte literal copy** (`copy_literals_fast`). For len <= 16: two
  unconditional 8-byte copies (overlapping ok). For len > 16:
  `copy_nonoverlapping`.
- **Unchecked bitstream flush** (`bitstream_flush`). Direct
  `write_unaligned` into Vec at known-valid position.
- **Unchecked Vec write-at** (`vec_write_at`). Writes into uninitialized
  Vec capacity without updating len.
- **Cold panic path** (`cold_rep_panic`). `#[cold] #[inline(never)]`
  keeps panic code out of the hot path.

Each function has a `debug_assert!` guard. Pattern:
```rust
debug_assert!(pos + 4 <= src.len());
unsafe { (src.as_ptr().add(pos) as *const u32).read_unaligned() }
```

Core crate unsafe follows the same pattern in `bitstream/`, `huffman/`,
`xxhash/` primitives modules.

Decoder support unsafe lives in `decode/src/fast_vec.rs` (wild-copy and Vec
length primitives) and `decode/src/seq_table.rs` (`MaybeUninit` FSE table
reads).


## Core bitstream primitives

**Unaligned u64 read/write** (`bitstream/primitives.rs`). `read_unaligned`/
`write_unaligned` for 8-byte operations in all bit readers and writers.

**BitWriter batch flush** (`bitstream/writer.rs`). Accumulates bits in a u64
register, flushes when >= 32 bits. Single unaligned write per flush.


## `paranoid` feature

`cargo build --features paranoid` adds `#![forbid(unsafe_code)]` to all four
crate roots. `fearless_simd` dispatch still works (it requires no unsafe), so
the decoder gets full SIMD multiversioning under paranoid. What changes:

- Encoder and core `primitives.rs` switch to safe alternatives via dual
  `#[cfg(feature = "paranoid")]` / `#[cfg(not(feature = "paranoid"))]` bodies:
  direct indexing, `from_le_bytes`, `resize` in place of unchecked operations.
- Decoder wild-copy (`fast_vec.rs`) falls back to `extend_from_slice` and
  `extend_from_within`.
- Huffman BMI2 `#[target_feature]` dispatch is gated out (uses `unsafe` call).


## Cross-cutting

**Nightly optimize attributes.** `#![cfg_attr(feature = "nightly",
feature(optimize_attribute))]` on all three crates. Enables per-function
`#[optimize]` control on nightly builds.

**no_std + alloc.** All core codec logic works without std. Predefined table
caching degrades gracefully (rebuilds per use in no_std vs cached via
`LazyLock` in std).


## Scope

Greedy, Lazy, BtLazy2, BtOpt, and BtUltra strategies are out of scope.
zrip targets the Fast/DFast region of the speed-ratio curve (levels -7 through 4)
where encode throughput matters more than compression ratio.