ZXC 0.15.0
High-performance asymmetric lossless compression library
Loading...
Searching...
No Matches
zxc_huffman.c
Go to the documentation of this file.
1// SPDX-License-Identifier: BSD-3-Clause
2/*
3 * ZXC - High-performance lossless compression
4 *
5 * Copyright (c) Bertrand Lebonnois and contributors.
6 *
7 */
8
34// Function Multi-Versioning Support
35// With ZXC_FUNCTION_SUFFIX defined (e.g. _avx2), each variant TU gets its own
36// copy under a unique symbol, which the runtime dispatcher routes to. The
37// defines precede zxc_internal.h so the header's prototypes take the same
38// suffix as the definitions below.
39#ifdef ZXC_FUNCTION_SUFFIX
40#define ZXC_CAT_IMPL(x, y) x##y
41#define ZXC_CAT(x, y) ZXC_CAT_IMPL(x, y)
42#define zxc_huf_build_code_lengths ZXC_CAT(zxc_huf_build_code_lengths, ZXC_FUNCTION_SUFFIX)
43#define zxc_huf_pack_lengths ZXC_CAT(zxc_huf_pack_lengths, ZXC_FUNCTION_SUFFIX)
44#define zxc_huf_unpack_lengths ZXC_CAT(zxc_huf_unpack_lengths, ZXC_FUNCTION_SUFFIX)
45#define zxc_huf_calc_size ZXC_CAT(zxc_huf_calc_size, ZXC_FUNCTION_SUFFIX)
46#define zxc_huf_calc_size_dict ZXC_CAT(zxc_huf_calc_size_dict, ZXC_FUNCTION_SUFFIX)
47#define zxc_huf_encode_section ZXC_CAT(zxc_huf_encode_section, ZXC_FUNCTION_SUFFIX)
48#define zxc_huf_encode_section_dict ZXC_CAT(zxc_huf_encode_section_dict, ZXC_FUNCTION_SUFFIX)
49#define zxc_huf_decode_section ZXC_CAT(zxc_huf_decode_section, ZXC_FUNCTION_SUFFIX)
50#define zxc_huf_decode_section_dict ZXC_CAT(zxc_huf_decode_section_dict, ZXC_FUNCTION_SUFFIX)
51#endif
52
53// Mark the primary variant (only _default, or a no-suffix build) so ISA-
54// independent cold code compiles once, not in every per-ISA copy. Keyed off the
55// suffix value, so every build gets it with no extra flag.
56#ifdef ZXC_FUNCTION_SUFFIX
57#define ZXC_PRIMARY__default 1
58#define ZXC_PRIMARY__avx2 0
59#define ZXC_PRIMARY__avx512 0
60#define ZXC_PRIMARY__neon32 0
61#define ZXC_PRIMARY_CAT_(a, b) a##b
62#define ZXC_PRIMARY_CAT(a, b) ZXC_PRIMARY_CAT_(a, b)
63#if ZXC_PRIMARY_CAT(ZXC_PRIMARY_, ZXC_FUNCTION_SUFFIX)
64#define ZXC_VARIANT_PRIMARY 1
65#endif
66#else
67#define ZXC_VARIANT_PRIMARY 1
68#endif
69
70#include "../../include/zxc_error.h"
71#include "zxc_internal.h"
72#include "zxc_pivco_tables.h"
73
74// ===========================================================================
75// Length-limited Huffman: boundary package-merge
76// ===========================================================================
77//
78// Builds optimal length-limited Huffman code lengths (max length
79// ZXC_HUF_MAX_CODE_LEN_ULTRA) on 256-symbol alphabets. Package-merge is run for
80// ZXC_HUF_MAX_CODE_LEN_ULTRA levels; each level holds up to 2N items (leaves +
81// paired packages). Selection of the cheapest 2N - 2 items at level
82// ZXC_HUF_MAX_CODE_LEN_ULTRA gives the appearance count of each leaf, which is
83// its code length.
84
86
88
90
108static void pm_leaves_sort(pm_leaf_t* RESTRICT leaves, const int n) {
109 // One bucket per possible value of floor(log2(weight)) for a 32-bit
110 // weight, i.e. 32 buckets.
111 enum { NUM_BUCKETS = 32 };
112 int count[NUM_BUCKETS];
113 int offset[NUM_BUCKETS + 1]; /* +1 sentinel = n, avoids end-of-bucket branch. */
114 uint8_t bucket_of[ZXC_HUF_NUM_SYMBOLS];
116
117 ZXC_MEMSET(count, 0, sizeof(count));
118 for (int i = 0; i < n; i++) {
119 const unsigned b = zxc_log2_u32(leaves[i].w);
120 bucket_of[i] = (uint8_t)b;
121 count[b]++;
122 }
123
124 int acc = 0;
125 for (int b = 0; b < NUM_BUCKETS; b++) {
126 offset[b] = acc;
127 acc += count[b];
128 }
129 offset[NUM_BUCKETS] = n;
130
131 int pos[NUM_BUCKETS];
132 ZXC_MEMCPY(pos, offset, sizeof(pos));
133 for (int i = 0; i < n; i++) {
134 tmp[pos[bucket_of[i]]++] = leaves[i];
135 }
136
137 for (int b = 0; b < NUM_BUCKETS; b++) {
138 if (count[b] < 2) continue;
139 const int s = offset[b];
140 const int e = offset[b + 1];
141 for (int i = s + 1; i < e; i++) {
142 const pm_leaf_t key = tmp[i];
143 int j = i - 1;
144 while (j >= s && (tmp[j].w > key.w || (tmp[j].w == key.w && tmp[j].sym > key.sym))) {
145 tmp[j + 1] = tmp[j];
146 j--;
147 }
148 tmp[j + 1] = key;
149 }
150 }
151
152 ZXC_MEMCPY(leaves, tmp, (size_t)n * sizeof(pm_leaf_t));
153}
154
163int zxc_huf_build_code_lengths(const uint32_t* RESTRICT freq, uint8_t* RESTRICT code_len,
164 void* RESTRICT scratch, const int max_code_len) {
165 ZXC_MEMSET(code_len, 0, ZXC_HUF_NUM_SYMBOLS);
166
168 int n = 0;
169 for (int i = 0; i < ZXC_HUF_NUM_SYMBOLS; i++) {
170 if (freq[i] > 0) {
171 leaves[n].w = freq[i];
172 leaves[n].sym = (int16_t)i;
173 n++;
174 }
175 }
176 if (UNLIKELY(n == 0)) return ZXC_ERROR_CORRUPT_DATA;
177 if (n == 1) {
178 code_len[leaves[0].sym] = 1;
179 return ZXC_OK;
180 }
181
182 pm_leaves_sort(leaves, n);
183
184 // Callers pass max_code_len >= 8, and n <= 256 <= 2^max_code_len, so the
185 // length limit is always feasible (a full 8-bit code covers all 256 symbols).
186 const int max_per_level = 2 * n;
187
188 // Working buffers: either carve from caller-provided scratch (sized for
189 // the worst-case alphabet) or fall back to per-call malloc/free.
190 pm_item_t* items;
191 int* counts;
192 frame_t* stack;
193 pm_item_t* owned_items = NULL;
194 int* owned_counts = NULL;
195 frame_t* owned_stack = NULL;
196 if (scratch) {
197 uint8_t* p = (uint8_t*)scratch;
198 items = (pm_item_t*)p;
199 p +=
201 p = (uint8_t*)(((uintptr_t)p + 7U) & ~(uintptr_t)7U);
202 counts = (int*)p;
203 ZXC_MEMSET(counts, 0, (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * sizeof(int));
204 p += (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * sizeof(int);
205 p = (uint8_t*)(((uintptr_t)p + 7U) & ~(uintptr_t)7U);
206 stack = (frame_t*)p;
207 } else {
208 owned_items = (pm_item_t*)ZXC_MALLOC((size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA *
209 (size_t)max_per_level * sizeof(pm_item_t));
210 owned_counts = (int*)ZXC_CALLOC((size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA, sizeof(int));
211 owned_stack = (frame_t*)ZXC_MALLOC((size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA *
212 (size_t)max_per_level * sizeof(frame_t));
213 if (UNLIKELY(!owned_items || !owned_counts || !owned_stack)) {
214 ZXC_FREE(owned_items);
215 ZXC_FREE(owned_counts);
216 ZXC_FREE(owned_stack);
217 return ZXC_ERROR_MEMORY;
218 }
219 items = owned_items;
220 counts = owned_counts;
221 stack = owned_stack;
222 }
223#define ITEM(k, i) items[(size_t)(k) * (size_t)max_per_level + (size_t)(i)]
224
225 // Level 0 (logical level 1): the leaves themselves, already sorted.
226 for (int i = 0; i < n; i++) {
227 ITEM(0, i).weight = leaves[i].w;
228 ITEM(0, i).left = -1;
229 ITEM(0, i).right = -1;
230 ITEM(0, i).sym = leaves[i].sym;
231 }
232 counts[0] = n;
233
234 // Levels 1..max_code_len-1: merge sorted leaves with sorted packages from the previous
235 // level.
236 for (int k = 1; k < max_code_len; k++) {
237 const int prev = counts[k - 1];
238 const int packs = prev / 2;
239 int li = 0;
240 int pi = 0;
241 int n_lvl = 0;
242 while (li < n || pi < packs) {
243 const uint32_t wl = (li < n) ? leaves[li].w : UINT32_MAX;
244 const uint32_t wp =
245 (pi < packs)
246 ? (uint32_t)(ITEM(k - 1, 2 * pi).weight + ITEM(k - 1, 2 * pi + 1).weight)
247 : UINT32_MAX;
248 if (wl <= wp && li < n) {
249 ITEM(k, n_lvl).weight = wl;
250 ITEM(k, n_lvl).left = -1;
251 ITEM(k, n_lvl).right = -1;
252 ITEM(k, n_lvl).sym = leaves[li].sym;
253 li++;
254 } else {
255 ITEM(k, n_lvl).weight = wp;
256 ITEM(k, n_lvl).left = (int16_t)(2 * pi);
257 ITEM(k, n_lvl).right = (int16_t)(2 * pi + 1);
258 ITEM(k, n_lvl).sym = -1;
259 pi++;
260 }
261 n_lvl++;
262 }
263 counts[k] = n_lvl;
264 }
265
266 // Step 3: take first 2n-2 items at level max_code_len-1; trace back, counting leaf
267 // appearances.
268 int n_take = 2 * n - 2;
269 if (n_take > counts[max_code_len - 1]) n_take = counts[max_code_len - 1];
270
271 // Worst case stack depth: (max_code_len * n_take) frames; bounded by
272 // max_code_len * 2n <= ZXC_HUF_MAX_CODE_LEN_ULTRA * 2n. `stack` was set up earlier from
273 // scratch (or the local malloc fallback), sized for the ceiling.
274 int sp = 0;
275 for (int i = 0; i < n_take; i++) {
276 stack[sp].lvl = (int8_t)(max_code_len - 1);
277 stack[sp].idx = (int16_t)i;
278 sp++;
279 }
280 while (sp > 0) {
281 frame_t f = stack[--sp];
282 const pm_item_t* it = &ITEM(f.lvl, f.idx);
283 if (it->sym >= 0) {
284 code_len[it->sym]++;
285 } else {
286 stack[sp].lvl = (int8_t)(f.lvl - 1);
287 stack[sp].idx = it->left;
288 sp++;
289 stack[sp].lvl = (int8_t)(f.lvl - 1);
290 stack[sp].idx = it->right;
291 sp++;
292 }
293 }
294
295 if (owned_items) {
296 ZXC_FREE(owned_items);
297 ZXC_FREE(owned_counts);
298 ZXC_FREE(owned_stack);
299 }
300#undef ITEM
301 return ZXC_OK;
302}
303
304// ===========================================================================
305// Encoder-side joint flat/length nudge (idea: pivco-huffman issue #20)
306// ===========================================================================
307//
308// Package-merge minimizes bits alone; the PivCo decoder's cost also depends on
309// the SHAPE of the length histogram (pass count = max_depth + 1, and maximal
310// complete subtrees collapse into single unpacks -- see the decode section).
311// The functions below reshape the class counts toward power-of-two boundaries
312// and shallower caps under an adoption guard, trading a bounded ratio loss for
313// fewer modeled decode level-touches.
314//
315// Everything is exact integer arithmetic on the canonical code space. With
316// leaves assigned in (length, symbol) ascending order -- the canonical rule of
317// zxc_pivco_tree_build -- the depth-l leaves occupy one contiguous ALIGNED run
318// [S_{l-1}, S_l) of the code space at 2^-11 slot granularity, where
319// S_l = sum_{j<=l} bl_count[j] << (11-j) (each prior term is divisible by
320// 2^(12-l), so runs start on even slot boundaries). The maximal dyadic (buddy)
321// blocks of each run are EXACTLY the decoder's maximal flat subtrees, so any
322// candidate histogram is priced without building a tree.
323//
324// Compiled once in the primary variant: this is ISA-independent cold encoder
325// policy, and a single copy guarantees cross-ISA identical archives. For an
326// A/B build without the nudge, set ZXC_HUF_NUDGE_MERGE_Q8 to 0 in
327// zxc_internal.h: the guard then rejects every candidate.
328#if defined(ZXC_VARIANT_PRIMARY)
329
331typedef struct {
332 uint64_t bits;
333 uint64_t touches;
334} zxc_huf_nudge_cost_t;
335
344static int zxc_huf_nudge_classes(const uint8_t* RESTRICT code_len, uint32_t* RESTRICT blc) {
345 ZXC_MEMSET(blc, 0, (ZXC_HUF_MAX_CODE_LEN_ULTRA + 1) * sizeof(uint32_t));
346 int n = 0;
347 for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS; s++) {
348 const int l = code_len[s];
349 if (!l) continue;
350 blc[l]++;
351 n++;
352 }
353 return n;
354}
355
365static void zxc_huf_nudge_pf_canonical(const uint8_t* RESTRICT code_len,
366 const uint32_t* RESTRICT freq, const uint32_t* RESTRICT blc,
367 uint64_t* RESTRICT pf) {
368 uint32_t pos[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1];
369 uint32_t acc = 0;
370 for (int l = 1; l <= ZXC_HUF_MAX_CODE_LEN_ULTRA; l++) {
371 pos[l] = acc;
372 acc += blc[l];
373 }
374 uint32_t val[ZXC_HUF_NUM_SYMBOLS];
375 for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS; s++) {
376 const int l = code_len[s];
377 if (l) val[pos[l]++] = freq[s];
378 }
379 pf[0] = 0;
380 for (uint32_t i = 0; i < acc; i++) pf[i + 1] = pf[i] + val[i];
381}
382
401static void zxc_huf_nudge_eval(const uint32_t* RESTRICT blc, const uint64_t* RESTRICT pf,
402 zxc_huf_nudge_cost_t* RESTRICT out) {
403 enum { LU = ZXC_HUF_MAX_CODE_LEN_ULTRA };
404 uint64_t bits = 0;
405 uint64_t touches = 0;
406 int max_len = 0;
407 uint32_t S = 0; /* code-space cursor, in 2^-LU slots */
408 uint32_t base = 0; /* leaf index where the current run starts */
409 for (int l = 1; l <= LU; l++) {
410 if (!blc[l]) continue;
411 max_len = l;
412 bits += (uint64_t)l * (pf[base + blc[l]] - pf[base]);
413 const uint32_t w = 1U << (LU - l);
414 const uint32_t end = S + blc[l] * w;
415 uint32_t x = S;
416 while (x < end) {
417 const uint32_t wx = x ? (x & (0U - x)) : (1U << LU);
418 const uint32_t wr = 1U << zxc_log2_u32(end - x);
419 const uint32_t W = wx < wr ? wx : wr;
420 const int D = (int)zxc_log2_u32(W) - (LU - l);
421 const uint32_t i0 = base + ((x - S) >> (LU - l));
422 const uint64_t mass = pf[i0 + (W >> (LU - l))] - pf[i0];
423 int t;
424 if (D == 0) {
425 t = l + 1; /* lone leaf: one memset + merges above */
426 } else if (D == 1) {
427 t = l; /* leaf pair: parent XOR-blend at depth l-1 */
428 } else {
429 t = (l - D) + 1; /* flat root: one unpack + merges above */
431 }
432 touches += mass * (uint64_t)t;
433 x += W;
434 }
435 S = end;
436 base += blc[l];
437 }
438 touches += (uint64_t)ZXC_HUF_NUDGE_LEVEL_COST * (uint64_t)(max_len + 1);
439 out->bits = bits;
440 out->touches = touches;
441}
442
449static ZXC_ALWAYS_INLINE uint64_t zxc_huf_nudge_j(const zxc_huf_nudge_cost_t* c) {
450 return 256U * c->bits + (uint64_t)ZXC_HUF_NUDGE_LAMBDA_Q8 * c->touches;
451}
452
472static uint32_t zxc_huf_nudge_clamp(const uint32_t want, const uint32_t s, const uint32_t n_rem,
473 const int l, const int cap) {
474 if (n_rem <= s || l >= cap) return n_rem; /* forced finish */
475 const uint32_t m = 1U << (cap - l);
476 const uint32_t lo = (2 * s > n_rem) ? 2 * s - n_rem : 0;
477 uint32_t hi = s - 1;
478 const uint32_t cap_hi = (s * m - n_rem) / (m - 1);
479 if (cap_hi < hi) hi = cap_hi;
480 const uint32_t c = want < lo ? lo : want;
481 return c > hi ? hi : c;
482}
483
495static void zxc_huf_nudge_complete(uint32_t* RESTRICT blc, const int l, uint32_t s, uint32_t n_rem,
496 const uint32_t* RESTRICT blc0, const int cap) {
497 for (int j = l + 1; j <= cap && n_rem; j++) {
498 const uint32_t c = zxc_huf_nudge_clamp(blc0[j], s, n_rem, j, cap);
499 blc[j] = c;
500 n_rem -= c;
501 s = 2 * (s - c);
502 }
503}
504
525static void zxc_huf_nudge_walk(const uint32_t* RESTRICT blc0, const uint64_t* RESTRICT pf_rank,
526 const int n, const int cap, uint32_t* RESTRICT out_blc) {
527 enum { LU = ZXC_HUF_MAX_CODE_LEN_ULTRA };
528 ZXC_MEMSET(out_blc, 0, (LU + 1) * sizeof(uint32_t));
529 uint32_t s = 2; /* the root's two slots at level 1 (n >= 4 => root is internal) */
530 uint32_t n_rem = (uint32_t)n;
531 for (int l = 1; l <= cap && n_rem; l++) {
532 struct {
533 uint32_t c;
534 int flat_d; /* > 0: complete as one uniform run at depth l + flat_d */
535 } cand[6];
536 int n_cand = 0;
537 const uint32_t c_base = zxc_huf_nudge_clamp(blc0[l], s, n_rem, l, cap);
538 cand[n_cand].c = c_base;
539 cand[n_cand++].flat_d = 0;
540 if (c_base < n_rem) {
541 // Shape the internal-node count i = s - c toward few set bits.
542 const uint32_t i_base = s - c_base;
543 const uint32_t i_dn = 1U << zxc_log2_u32(i_base);
544 const uint32_t rest = i_base - i_dn;
545 const uint32_t shapes[3] = {i_dn, i_dn << 1,
546 i_dn | (rest ? 1U << zxc_log2_u32(rest) : 0U)};
547 for (int k = 0; k < 3; k++) {
548 const uint32_t want = s > shapes[k] ? s - shapes[k] : 0;
549 const uint32_t c = zxc_huf_nudge_clamp(want, s, n_rem, l, cap);
550 int dup = 0;
551 for (int p = 0; p < n_cand; p++) dup |= (cand[p].c == c);
552 if (!dup) {
553 cand[n_cand].c = c;
554 cand[n_cand++].flat_d = 0;
555 }
556 }
557 // Uniform finish: c leaves here, the rest as i complete depth-D
558 // subtrees (c*(2^D - 1) == s*2^D - n_rem must divide exactly).
559 for (int d = 1; d <= cap - l && n_cand < 6; d++) {
560 const uint32_t den = (1U << d) - 1;
561 const int32_t num = (int32_t)(s << d) - (int32_t)n_rem;
562 if (num < 0 || (uint32_t)num % den) continue;
563 const uint32_t c = (uint32_t)num / den;
564 if (c >= s || c >= n_rem) continue; /* need i >= 1 and a remainder */
565 cand[n_cand].c = c;
566 cand[n_cand++].flat_d = d;
567 break; /* the shallowest exact finish is the aggressive one */
568 }
569 }
570 uint64_t best_j = UINT64_MAX;
571 uint32_t best_c = c_base;
572 for (int k = 0; k < n_cand; k++) {
573 uint32_t tmp[LU + 1];
574 ZXC_MEMCPY(tmp, out_blc, sizeof(tmp));
575 tmp[l] = cand[k].c;
576 const uint32_t rem = n_rem - cand[k].c;
577 if (rem) {
578 if (cand[k].flat_d)
579 tmp[l + cand[k].flat_d] = rem;
580 else
581 zxc_huf_nudge_complete(tmp, l, 2 * (s - cand[k].c), rem, blc0, cap);
582 }
583 zxc_huf_nudge_cost_t cc;
584 zxc_huf_nudge_eval(tmp, pf_rank, &cc);
585 const uint64_t j = zxc_huf_nudge_j(&cc);
586 if (j < best_j) {
587 best_j = j;
588 best_c = cand[k].c;
589 }
590 }
591 out_blc[l] = best_c;
592 n_rem -= best_c;
593 s = 2 * (s - best_c);
594 }
595}
596
618static uint64_t zxc_huf_nudge_dp_run_j(const int lu, const int lc, const int g_log2,
619 const uint32_t s, const uint32_t c,
620 const uint64_t* RESTRICT pfg, const uint32_t k) {
621 if (!c) return 0;
622 const int lr = lc + g_log2; /* real code length of these leaves */
623 const uint32_t w = 1U << (lu - lc);
624 const uint32_t S = (1U << lu) - s * w;
625 const uint32_t end = S + c * w;
626 const uint64_t bits = (uint64_t)lr * (pfg[k + c] - pfg[k]);
627 uint64_t touches = 0;
628 uint32_t x = S;
629 while (x < end) {
630 const uint32_t wx = x ? (x & (0U - x)) : (1U << lu);
631 const uint32_t wr = 1U << zxc_log2_u32(end - x);
632 const uint32_t W = wx < wr ? wx : wr;
633 const int d = (int)zxc_log2_u32(W >> (lu - lc)) + g_log2;
634 const uint32_t i0 = k + ((x - S) >> (lu - lc));
635 const uint64_t mass = pfg[i0 + (W >> (lu - lc))] - pfg[i0];
636 int t;
637 if (d == 0) {
638 t = lr + 1;
639 } else if (d == 1) {
640 t = lr;
641 } else {
642 t = (lr - d) + 1;
644 }
645 touches += mass * (uint64_t)t;
646 x += W;
647 }
648 return 256U * bits + (uint64_t)ZXC_HUF_NUDGE_LAMBDA_Q8 * touches;
649}
650
674static int zxc_huf_nudge_dp_solve(zxc_huf_nudge_ws_t* ws, const uint64_t* RESTRICT pfg, const int m,
675 const int cap_c, const int lu, const int g_log2,
676 uint32_t* RESTRICT out_cblc) {
677 if (UNLIKELY(m < 2 || m > ZXC_HUF_NUDGE_DP_M || cap_c < 1 ||
679 return 0;
680
681 const size_t plane = (size_t)(m + 1) * (size_t)(m + 1);
682 const size_t arrive_cnt = (size_t)(cap_c + 1) * plane;
683 uint64_t* jcur = ws->jcur;
684 uint64_t* jnxt = ws->jnxt;
685 uint8_t* const arrive = ws->arrive;
686 int ok = 0;
687
688#define ZXC_NUDGE_DP_IDX(k, s) ((size_t)(k) * (size_t)(m + 1) + (size_t)(s))
689 for (size_t i = 0; i < plane; i++) jcur[i] = UINT64_MAX;
690 jcur[ZXC_NUDGE_DP_IDX(0, 2)] = 0;
691 uint64_t best_j = UINT64_MAX;
692 int best_l = 0;
693 int best_k = 0;
694 int best_s = 0;
695 for (int lc = 1; lc <= cap_c; lc++) {
696 for (size_t i = 0; i < plane; i++) jnxt[i] = UINT64_MAX;
697 for (int k = 0; k < m; k++) {
698 const uint32_t n_rem = (uint32_t)(m - k);
699 for (uint32_t s = 1; s <= n_rem; s++) {
700 const size_t from = ZXC_NUDGE_DP_IDX(k, s);
701 if (UNLIKELY(from >= plane)) continue;
702 const uint64_t j0 = jcur[from];
703 if (j0 == UINT64_MAX) continue;
704 if (s == n_rem) {
705 // Forced finish: fill every slot, close the tree here.
706 const uint64_t j =
707 j0 + zxc_huf_nudge_dp_run_j(lu, lc, g_log2, s, s, pfg, (uint32_t)k) +
709 (uint64_t)(lc + g_log2 + 1);
710 if (j < best_j) {
711 best_j = j;
712 best_l = lc;
713 best_k = k;
714 best_s = (int)s;
715 }
716 continue;
717 }
718 if (lc == cap_c) continue; /* must finish at the cap */
719 const uint32_t mm = 1U << (cap_c - lc);
720 const uint32_t lo = (2 * s > n_rem) ? 2 * s - n_rem : 0;
721 uint32_t hi = s - 1;
722 const uint32_t cap_hi = (s * mm - n_rem) / (mm - 1);
723 if (cap_hi < hi) hi = cap_hi;
724 for (uint32_t c = lo; c <= hi; c++) {
725 const uint64_t j =
726 j0 + zxc_huf_nudge_dp_run_j(lu, lc, g_log2, s, c, pfg, (uint32_t)k);
727 const size_t to = ZXC_NUDGE_DP_IDX(k + (int)c, 2 * (s - c));
728 const size_t arr = (size_t)(lc + 1) * plane + to;
729
730 if (UNLIKELY(to >= plane || arr >= arrive_cnt)) continue;
731 if (j < jnxt[to]) {
732 jnxt[to] = j;
733 arrive[arr] = (uint8_t)c;
734 }
735 }
736 }
737 }
738 uint64_t* tmp = jcur;
739 jcur = jnxt;
740 jnxt = tmp;
741 }
742 if (best_j == UINT64_MAX) goto done;
743 ZXC_MEMSET(out_cblc, 0, (ZXC_HUF_MAX_CODE_LEN_ULTRA + 1) * sizeof(uint32_t));
744 out_cblc[best_l] = (uint32_t)best_s;
745 {
746 int k = best_k;
747 int s = best_s;
748 for (int lc = best_l; lc > 1; lc--) {
749 const uint32_t c = arrive[(size_t)lc * plane + ZXC_NUDGE_DP_IDX(k, s)];
750 out_cblc[lc - 1] = c;
751 s = s / 2 + (int)c;
752 k -= (int)c;
753 }
754 if (UNLIKELY(k != 0 || s != 2)) goto done; /* backtrack corruption: drop candidate */
755 }
756 ok = 1;
757done:
758#undef ZXC_NUDGE_DP_IDX
759 return ok;
760}
761
770static void zxc_huf_nudge_assign(const uint32_t* RESTRICT blc, const int16_t* RESTRICT sym_order,
771 uint8_t* RESTRICT cand_len) {
772 ZXC_MEMSET(cand_len, 0, ZXC_HUF_NUM_SYMBOLS);
773 int r = 0;
774 for (int l = 1; l <= ZXC_HUF_MAX_CODE_LEN_ULTRA; l++)
775 for (uint32_t k = 0; k < blc[l]; k++) cand_len[sym_order[r++]] = (uint8_t)l;
776}
777
778void zxc_huf_nudge_cost(const uint8_t* RESTRICT code_len, const uint32_t* RESTRICT freq,
779 uint64_t* RESTRICT bits, uint64_t* RESTRICT touches) {
780 uint32_t blc[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1];
781 (void)zxc_huf_nudge_classes(code_len, blc);
782 uint64_t pf[ZXC_HUF_NUM_SYMBOLS + 1];
783 zxc_huf_nudge_pf_canonical(code_len, freq, blc, pf);
784 zxc_huf_nudge_cost_t c;
785 zxc_huf_nudge_eval(blc, pf, &c);
786 *bits = c.bits;
787 *touches = c.touches;
788}
789
790int zxc_huf_nudge_code_lengths(const uint32_t* RESTRICT freq, uint8_t* RESTRICT code_len,
791 void* RESTRICT scratch, const size_t scratch_cap,
792 const int max_code_len) {
793 enum { LU = ZXC_HUF_MAX_CODE_LEN_ULTRA };
794 uint32_t blc0[LU + 1];
795 const int n = zxc_huf_nudge_classes(code_len, blc0);
796 // Degenerate alphabets have no shape freedom (and n == 1 is format-pinned
797 // to code length exactly 1): leave them untouched.
798 if (n < 4) return 0;
799
800 // Tables and DP planes live past the rebuilds' region of the scratch when it
801 // has the room (opt_scratch does), else in one allocation: no 10 KiB frame.
802 const int owned = !scratch || scratch_cap < ZXC_HUF_NUDGE_SCRATCH_SIZE;
803 zxc_huf_nudge_ws_t* const ws =
804 owned ? (zxc_huf_nudge_ws_t*)ZXC_MALLOC(sizeof(*ws))
805 : (zxc_huf_nudge_ws_t*)((uint8_t*)scratch + ZXC_HUF_NUDGE_SCRATCH_OFF);
806 if (UNLIKELY(!ws)) return 0;
807
808 // Baseline cost, exact (canonical-order frequency weighting).
809 uint64_t* const pf = ws->pf;
810 zxc_huf_nudge_pf_canonical(code_len, freq, blc0, pf);
811 zxc_huf_nudge_cost_t c0;
812 zxc_huf_nudge_eval(blc0, pf, &c0);
813
814 // Frequency-rank order shared by every candidate (rank 0 = most frequent;
815 // pm_leaves_sort is ascending, ties on symbol, so read it backwards).
816 pm_leaf_t* const leaves = ws->leaves;
817 int k = 0;
818 for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS; s++) {
819 if (!freq[s]) continue;
820 leaves[k].w = freq[s];
821 leaves[k].sym = (int16_t)s;
822 k++;
823 }
824 pm_leaves_sort(leaves, n);
825 int16_t* const sym_order = ws->sym_order;
826 uint64_t* const pf_rank = ws->pf_rank;
827 pf_rank[0] = 0;
828 for (int r = 0; r < n; r++) {
829 sym_order[r] = leaves[n - 1 - r].sym;
830 pf_rank[r + 1] = pf_rank[r] + leaves[n - 1 - r].w;
831 }
832
833 // Candidates: the greedy ledger walk, package-merge rebuilt at reduced caps
834 // (the pass loop runs max_depth + 1 times, so depth cuts attack the decoder's
835 // biggest fixed cost), and the slot-ledger DP below. All Kraft-exact.
836 uint8_t (*const cand)[ZXC_HUF_NUM_SYMBOLS] = ws->cand;
837 int n_cand = 0;
838 {
839 uint32_t blc_w[LU + 1];
840 zxc_huf_nudge_walk(blc0, pf_rank, n, max_code_len, blc_w);
841 zxc_huf_nudge_assign(blc_w, sym_order, cand[n_cand]);
842 n_cand++;
843 }
844 int max_len0 = 0;
845 for (int l = LU; l >= 1; l--) {
846 if (blc0[l]) {
847 max_len0 = l;
848 break;
849 }
850 }
851 if (max_len0 >= 2) {
852 for (int cut = 1; cut <= 2; cut++) {
853 const int cap2 = max_len0 - cut;
854 if (cap2 < 2 || ((uint32_t)1 << cap2) < (uint32_t)n) break;
855 if (zxc_huf_build_code_lengths(freq, cand[n_cand], scratch, cap2) != ZXC_OK) break;
856 n_cand++;
857 }
858 }
859
860 // Slot-ledger DP: optimal class counts under the rank-weighted model, over
861 // groups of 2^G frequency-adjacent symbols (G = 0 is exact; coarser tiers
862 // keep the plane cache-resident, bounding the call to a few hundred us).
863 // When G does not divide n the lightest group is padded with zero-freq
864 // ghost leaves on unused byte values - wire-legal, empty runs, and the
865 // evaluator prices the burnt code space like anything else.
866 {
867 int g_log2 = 0; /* coarsen until the groups fit the DP plane */
868 while (((n + (1 << g_log2) - 1) >> g_log2) > ZXC_HUF_NUDGE_DP_M) g_log2++;
869 const int g = 1 << g_log2;
870 const int m = (n + g - 1) / g;
871 const int cap_c = max_code_len - g_log2;
872 if (m >= 2 && cap_c >= 1 && m <= (1 << cap_c)) {
873 uint64_t* const pfg = ws->pfg;
874 for (int j2 = 0; j2 <= m; j2++) {
875 int r = j2 * g;
876 if (r > n) r = n;
877 pfg[j2] = pf_rank[r];
878 }
879 uint32_t cblc[LU + 1];
880 if (zxc_huf_nudge_dp_solve(ws, pfg, m, cap_c, LU - g_log2, g_log2, cblc)) {
881 uint8_t* const cl = cand[n_cand];
882 ZXC_MEMSET(cl, 0, ZXC_HUF_NUM_SYMBOLS);
883 int r = 0;
884 int ghosts = 0;
885 uint8_t ghost_len = 0;
886 for (int lc = 1; lc <= cap_c; lc++) {
887 for (uint32_t q = 0; q < cblc[lc]; q++) {
888 for (int e = 0; e < g; e++, r++) {
889 if (r < n) {
890 cl[sym_order[r]] = (uint8_t)(lc + g_log2);
891 } else {
892 ghost_len = (uint8_t)(lc + g_log2);
893 ghosts++;
894 }
895 }
896 }
897 }
898 for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS && ghosts; s++) {
899 if (freq[s] == 0 && cl[s] == 0) {
900 cl[s] = ghost_len;
901 ghosts--;
902 }
903 }
904 n_cand++;
905 }
906 }
907 }
908
909 // Adopt the cheapest candidate clearing both guard rails and strictly
910 // beating the baseline J; otherwise leave code_len byte-for-byte alone, so a
911 // rejection never depends on package-merge tie assignment.
912 const uint64_t j0 = zxc_huf_nudge_j(&c0);
913 uint64_t best_j = j0;
914 int best = -1;
915 for (int ci = 0; ci < n_cand; ci++) {
916 // Every live symbol must keep a code; ghost-padded candidates carry
917 // MORE nonzero lengths than n, which is fine.
918 int valid = 1;
919 for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS; s++) {
920 if (freq[s] != 0 && cand[ci][s] == 0) {
921 valid = 0;
922 break;
923 }
924 }
925 if (!valid) continue;
926 uint32_t blc[LU + 1];
927 (void)zxc_huf_nudge_classes(cand[ci], blc);
928 zxc_huf_nudge_pf_canonical(cand[ci], freq, blc, pf);
929 zxc_huf_nudge_cost_t c1;
930 zxc_huf_nudge_eval(blc, pf, &c1);
931 if (c1.bits * 1000 > c0.bits * ZXC_HUF_NUDGE_BITS_PERMIL) continue;
932 if (c1.touches * 256 > c0.touches * ZXC_HUF_NUDGE_MERGE_Q8) continue;
933 const uint64_t j = zxc_huf_nudge_j(&c1);
934 if (j < best_j) {
935 best_j = j;
936 best = ci;
937 }
938 }
939 if (best >= 0) ZXC_MEMCPY(code_len, cand[best], ZXC_HUF_NUM_SYMBOLS);
940 if (owned) ZXC_FREE(ws);
941 return best >= 0;
942}
943#endif /* ZXC_VARIANT_PRIMARY */
944
945// ===========================================================================
946// 128-byte length header: 256 x 4-bit lengths, low nibble first.
947// =========================================================================
948
949void zxc_huf_pack_lengths(const uint8_t* RESTRICT code_len, uint8_t* RESTRICT out) {
950 for (int i = 0; i < ZXC_HUF_NUM_SYMBOLS; i += 2) {
951 const uint8_t lo = code_len[i] & 0x0F;
952 const uint8_t hi = code_len[i + 1] & 0x0F;
953 out[i >> 1] = (uint8_t)(lo | (hi << 4));
954 }
955}
956
957int zxc_huf_unpack_lengths(const uint8_t* RESTRICT in, uint8_t* RESTRICT code_len) {
958 int max_len = 0;
959 int n_present = 0;
960 for (int i = 0; i < ZXC_HUF_NUM_SYMBOLS; i += 2) {
961 const uint8_t b = in[i >> 1];
962 const uint8_t lo = b & 0x0F;
963 const uint8_t hi = (uint8_t)(b >> 4);
964 code_len[i] = lo;
965 code_len[i + 1] = hi;
966 if (lo > max_len) max_len = lo;
967 if (hi > max_len) max_len = hi;
968 if (lo) n_present++;
969 if (hi) n_present++;
970 }
971 if (UNLIKELY(max_len > ZXC_HUF_MAX_CODE_LEN_ULTRA || n_present == 0))
973 return ZXC_OK;
974}
975
976// ===========================================================================
977// PivCo-Huffman section codec (enc 2/3)
978// ===========================================================================
979//
980// Level-ordered layout and the merge-based decode are from PivCo-Huffman
981// by Marcin Zukowski (https://github.com/MarcinZukowski/pivco-huffman).
982// Implemented independently here.
983//
984// Layout: [128-byte packed code lengths (literal sections only)] then, for
985// every EMITTING node of the canonical code tree in BFS order, that node's
986// run: one branch bit per symbol routed through the node (0 = left child,
987// 1 = right), in sequence order -- or, for flat subtree roots, D packed bits
988// per symbol (bit j = branch at relative depth j). Runs are LSB-first within
989// bytes and byte-padded; descendants of a flat root emit nothing.
990//
991// The decoder recovers each node's symbol count for free: the root handles
992// n symbols, and popcounting a node's bits yields its right child's count.
993// Reconstruction runs bottom-up, one level at a time: leaves are runs of a
994// single symbol; an internal node MERGES its two children's sequences under
995// the control of its bits. Merges are branch-free shuffles (16 outputs per
996// step on AArch64 via a two-register TBL, 8 on ARMv7 via VTBL4, 64 on
997// AVX-512-VBMI2 via vpexpandb), which is what makes this layout
998// decode faster than the serial bit-chain of the classic 4-stream layout on
999// any target with a 16-byte shuffle.
1000//
1001// Level buffers ping-pong between `scratch` (odd depths) and `dst` (even
1002// depths, final output at depth 0), so one n-sized scratch suffices. Both
1003// buffers need read slack past `n` (speculative 16-byte kernel loads):
1004// ZXC_PAD_SIZE on dst (already true for lit/token buffers), and
1005// ZXC_PIVCO_SCRATCH_PAD on scratch.
1006
1007static ZXC_ALWAYS_INLINE int zxc_pivco_popcnt32(const uint32_t v) { return (int)ZXC_POPCOUNT32(v); }
1008
1009static ZXC_ALWAYS_INLINE int zxc_pivco_popcnt64(const uint64_t v) { return (int)ZXC_POPCOUNT64(v); }
1010
1012static ZXC_ALWAYS_INLINE int zxc_pivco_leaves(const zxc_pivco_tree_t* RESTRICT t, const int d) {
1013 return t->leaf_base[d + 1] - t->leaf_base[d];
1014}
1015
1017static ZXC_ALWAYS_INLINE int zxc_pivco_flat_leaves(const zxc_pivco_tree_t* RESTRICT t, const int g,
1018 const int d, const int D) {
1019 const int e = d + D;
1020 return t->leaf_base[e] + t->base[e] - t->node_base[e] + ((g - t->base[d]) << D);
1021}
1022
1024static void zxc_pivco_bitrev_permute(uint8_t* RESTRICT s, const int D) {
1025 const unsigned m = 1U << D;
1026 unsigned c = 0; /* r bit-reversed */
1027 for (unsigned r = 0; r < m; r++) {
1028 if (r < c) {
1029 const uint8_t x = s[r];
1030 s[r] = s[c];
1031 s[c] = x;
1032 }
1033 unsigned bit = m >> 1; /* reversed increment */
1034 for (; c & bit; bit >>= 1) c ^= bit;
1035 c |= bit;
1036 }
1037}
1038
1048static int zxc_pivco_tree_build(const uint8_t* RESTRICT code_len, zxc_pivco_tree_t* RESTRICT t) {
1049 uint16_t bl_count[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1] = {0};
1050 int n_present = 0;
1051 int max_depth = 0;
1052 for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS; s++) {
1053 const int l = code_len[s];
1054 if (l == 0) continue;
1055 if (UNLIKELY(l > ZXC_HUF_MAX_CODE_LEN_ULTRA)) return -1;
1056 bl_count[l]++;
1057 n_present++;
1058 if (l > max_depth) max_depth = l;
1059 }
1060 if (UNLIKELY(n_present == 0)) return -1;
1061 if (n_present >= 2) {
1062 uint32_t kraft = 0;
1063 for (int l = 1; l <= ZXC_HUF_MAX_CODE_LEN_ULTRA; l++)
1064 kraft += (uint32_t)bl_count[l] << (ZXC_HUF_MAX_CODE_LEN_ULTRA - l);
1065 if (UNLIKELY(kraft != (1U << ZXC_HUF_MAX_CODE_LEN_ULTRA))) return -1;
1066 } else if (UNLIKELY(bl_count[1] != 1)) {
1067 return -1; /* the lone symbol is pinned at length 1 */
1068 }
1069 t->max_depth = max_depth;
1070
1071 // Two children per internal node above, one under the lone symbol's root.
1072 // The d-bit prefixes start at `first`.
1073 unsigned width = 1;
1074 unsigned first = 0;
1075 t->node_base[0] = 0;
1076 t->leaf_base[0] = 0;
1077 for (int d = 0; d <= max_depth; d++) {
1078 t->base[d] = (int16_t)(t->node_base[d] - (int)first);
1079 t->node_base[d + 1] = (uint16_t)(t->node_base[d] + width);
1080 t->leaf_base[d + 1] = (uint16_t)(t->leaf_base[d] + bl_count[d]);
1081 first = 2 * (first + bl_count[d]);
1082 width = (d == 0 && n_present == 1) ? 1U : 2 * (width - bl_count[d]);
1083 }
1084
1085 uint16_t pos[ZXC_HUF_MAX_CODE_LEN_ULTRA + 1];
1086 for (int d = 0; d <= max_depth; d++) pos[d] = t->leaf_base[d];
1087 for (int s = 0; s < ZXC_HUF_NUM_SYMBOLS; s++)
1088 if (code_len[s]) t->syms[pos[code_len[s]]++] = (uint8_t)s;
1089
1090 // FORMAT RULE: a flat root is a MAXIMAL complete subtree of depth D >= 2
1091 // (D = 1 is a leaf pair). Bottom-up, flat_d holds complete heights: 0 at
1092 // leaves and the slot, INCOMPLETE across depths.
1093 enum { INCOMPLETE = 0xFE };
1094 t->flat_d[t->node_base[max_depth + 1]] = 0;
1095 for (int d = max_depth; d >= 0; d--) {
1096 uint8_t* const h = t->flat_d + t->node_base[d];
1097 const uint8_t* const hc = t->flat_d + t->node_base[d + 1];
1098 const int L = zxc_pivco_leaves(t, d);
1099 const int N = t->node_base[d + 1] - t->node_base[d];
1100 for (int i = 0; i < L; i++) h[i] = 0;
1101 for (int i = L; i < N; i++) {
1102 const uint8_t a = hc[2 * (i - L)];
1103 const uint8_t b = hc[2 * (i - L) + 1];
1104 h[i] = (a == b && a != INCOMPLETE) ? (uint8_t)(a + 1) : (uint8_t)INCOMPLETE;
1105 }
1106 }
1107 // Top-down: the first height >= 2 on a path is a flat root, the internal
1108 // nodes below are covered. The wire packs a path LSB first, so leaf r has
1109 // packed code r bit-reversed: store the leaves in that order.
1110 for (int d = 0; d < max_depth; d++) {
1111 uint8_t* const fd = t->flat_d + t->node_base[d];
1112 const uint8_t* const up =
1113 d ? t->flat_d + t->node_base[d - 1] + zxc_pivco_leaves(t, d - 1) : NULL;
1114 const int N = t->node_base[d + 1] - t->node_base[d];
1115 for (int i = zxc_pivco_leaves(t, d); i < N; i++) {
1116 if (up && up[i >> 1]) {
1117 fd[i] = ZXC_PIVCO_COVERED;
1118 } else if (fd[i] < 2 || fd[i] == INCOMPLETE) {
1119 fd[i] = 0;
1120 } else {
1121 const int g = t->node_base[d] + i;
1122 zxc_pivco_bitrev_permute(t->syms + zxc_pivco_flat_leaves(t, g, d, fd[i]), fd[i]);
1123 }
1124 }
1125 }
1126 return 0;
1127}
1128
1135static void zxc_pivco_counts(const zxc_pivco_tree_t* RESTRICT t, const uint32_t* RESTRICT freq,
1136 uint32_t* RESTRICT count) {
1137 count[t->node_base[t->max_depth + 1]] = 0;
1138 for (int d = t->max_depth; d >= 0; d--) {
1139 const int L = zxc_pivco_leaves(t, d);
1140 const int N = t->node_base[d + 1] - t->node_base[d];
1141 uint32_t* const cnt = count + t->node_base[d];
1142 const uint32_t* const ccnt = count + t->node_base[d + 1];
1143 const uint8_t* const sym = t->syms + t->leaf_base[d];
1144 for (int i = 0; i < L; i++) cnt[i] = freq[sym[i]];
1145 for (int i = L; i < N; i++) cnt[i] = ccnt[2 * (i - L)] + ccnt[2 * (i - L) + 1];
1146 }
1147}
1148
1150static void zxc_pivco_codes(const zxc_pivco_tree_t* RESTRICT t, uint32_t* RESTRICT codes) {
1151 for (int d = 1; d <= t->max_depth; d++)
1152 for (int i = 0; i < zxc_pivco_leaves(t, d); i++)
1153 codes[t->syms[t->leaf_base[d] + i]] = (uint32_t)(t->node_base[d] + i - t->base[d]);
1154}
1155
1178static ZXC_ALWAYS_INLINE size_t zxc_pivco_run_bytes(const uint32_t count, const uint8_t flat_d) {
1179 return flat_d ? ((size_t)count * flat_d + 7) / 8 : ((size_t)count + 7) / 8;
1180}
1181
1188static size_t zxc_pivco_layout(const zxc_pivco_tree_t* RESTRICT t, const uint32_t* RESTRICT count,
1189 uint32_t* RESTRICT cur) {
1190 size_t total = 0;
1191 for (int d = 0; d < t->max_depth; d++) {
1192 for (int g = t->node_base[d] + zxc_pivco_leaves(t, d); g < t->node_base[d + 1]; g++) {
1193 if (t->flat_d[g] == ZXC_PIVCO_COVERED) continue;
1194 if (cur) cur[g] = (uint32_t)(total << 3);
1195 total += zxc_pivco_run_bytes(count[g], t->flat_d[g]);
1196 }
1197 }
1198 return total;
1199}
1200
1201size_t zxc_huf_calc_size(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1202 const int with_header) {
1204 if (UNLIKELY(zxc_pivco_tree_build(code_len, &t) != 0)) return SIZE_MAX;
1205 const size_t body = zxc_huf_calc_size_dict(freq, code_len, &t);
1206 if (UNLIKELY(body == SIZE_MAX)) return SIZE_MAX;
1207 return body + (with_header ? (size_t)ZXC_HUF_TABLE_SIZE : 0);
1208}
1209
1215size_t zxc_huf_calc_size_dict(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1216 const zxc_pivco_tree_t* RESTRICT tree) {
1217 // Encodability is part of the estimate: a histogram symbol the table has no
1218 // code for would be silently ignored below and undercount. Report the
1219 // candidate unencodable instead.
1220 for (int k = 0; k < ZXC_HUF_NUM_SYMBOLS; k++)
1221 if (UNLIKELY(freq[k] != 0 && code_len[k] == 0)) return SIZE_MAX;
1222 uint32_t count[ZXC_PIVCO_NODE_SLOTS];
1223 zxc_pivco_counts(tree, freq, count);
1224 return zxc_pivco_layout(tree, count, NULL);
1225}
1226
1234static int zxc_pivco_encode_core(const uint8_t* RESTRICT literals, const size_t n_literals,
1235 const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1236 const zxc_pivco_tree_t* RESTRICT t, const uint32_t* RESTRICT codes,
1237 uint8_t* RESTRICT dst, const size_t dst_cap,
1238 const int with_header) {
1239 if (UNLIKELY(n_literals == 0)) return ZXC_ERROR_CORRUPT_DATA;
1240 // Every literal present in the histogram must have a code (a dict table
1241 // may lack codes for symbols unseen in training: the caller then falls
1242 // back to a per-block table, but a direct call must fail loudly).
1243 for (int k = 0; k < ZXC_HUF_NUM_SYMBOLS; k++)
1244 if (UNLIKELY(freq[k] != 0 && code_len[k] == 0)) return ZXC_ERROR_CORRUPT_DATA;
1245
1246 uint32_t count[ZXC_PIVCO_NODE_SLOTS];
1247 zxc_pivco_counts(t, freq, count);
1248 uint32_t cur[ZXC_PIVCO_MAX_NODES]; /* per-node write cursor, in payload bits */
1249 const size_t payload = zxc_pivco_layout(t, count, cur);
1250 const size_t hdr = with_header ? (size_t)ZXC_HUF_TABLE_SIZE : 0;
1251 // +2: the packed-code emitter uses a 3-byte read-modify-write that may
1252 // touch up to 2 bytes past the payload end.
1253 if (UNLIKELY(hdr + payload + 2 > dst_cap)) return ZXC_ERROR_DST_TOO_SMALL;
1254
1255 if (with_header) zxc_huf_pack_lengths(code_len, dst);
1256 uint8_t* const out = dst + hdr;
1257 ZXC_MEMSET(out, 0, payload + 2);
1258
1259 // Emit MSB-first while descending. At a flat root, emit the code's last D
1260 // bits, its packed code, in one shot and stop. Batching the per-bit
1261 // read-modify-write buys nothing: accumulators would still touch memory
1262 // once per bit, and flat roots absorb the dense levels.
1263 for (size_t i = 0; i < n_literals; i++) {
1264 const uint8_t s = literals[i];
1265 const uint32_t c = codes[s];
1266 const int l = code_len[s];
1267 for (int d = 0; d < l; d++) {
1268 const int g = t->base[d] + (int)(c >> (l - d));
1269 const int fd = t->flat_d[g];
1270 if (fd) {
1271 const uint32_t p = cur[g];
1272 cur[g] += (uint32_t)fd;
1273 uint8_t* q = out + (p >> 3);
1274 const uint32_t sh = p & 7U;
1275 uint32_t w = (uint32_t)q[0] | ((uint32_t)q[1] << 8) | ((uint32_t)q[2] << 16);
1276 w |= (c & ((1U << fd) - 1U)) << sh; /* fd <= 8, sh <= 7: fits 24 bits */
1277 q[0] = (uint8_t)w;
1278 q[1] = (uint8_t)(w >> 8);
1279 q[2] = (uint8_t)(w >> 16);
1280 break;
1281 }
1282 const uint32_t bit = (c >> (l - 1 - d)) & 1U;
1283 const uint32_t p = cur[g]++;
1284 out[p >> 3] |= (uint8_t)(bit << (p & 7));
1285 }
1286 }
1287 return (int)(hdr + payload);
1288}
1289
1307int zxc_huf_encode_section(const uint8_t* RESTRICT literals, const size_t n_literals,
1308 const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1309 uint8_t* RESTRICT dst, const size_t dst_cap) {
1311 uint32_t codes[ZXC_HUF_NUM_SYMBOLS];
1312 if (UNLIKELY(zxc_pivco_tree_build(code_len, &t) != 0)) return ZXC_ERROR_CORRUPT_DATA;
1313 zxc_pivco_codes(&t, codes);
1314 return zxc_pivco_encode_core(literals, n_literals, freq, code_len, &t, codes, dst, dst_cap, 1);
1315}
1316
1325int zxc_huf_encode_section_dict(const uint8_t* RESTRICT literals, const size_t n_literals,
1326 const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1327 const zxc_pivco_tree_t* RESTRICT tree,
1328 const uint32_t* RESTRICT codes, uint8_t* RESTRICT dst,
1329 const size_t dst_cap) {
1330 return zxc_pivco_encode_core(literals, n_literals, freq, code_len, tree, codes, dst, dst_cap,
1331 0);
1332}
1333
1334// ISA-independent cold dict setup
1335#if defined(ZXC_VARIANT_PRIMARY)
1336
1343int zxc_huf_dict_tree_build(const uint8_t* RESTRICT packed_lengths, zxc_pivco_tree_t* RESTRICT tree,
1344 uint32_t* RESTRICT codes, uint8_t* RESTRICT code_len) {
1345 const int rc = zxc_huf_unpack_lengths(packed_lengths, code_len);
1346 if (UNLIKELY(rc != ZXC_OK)) return rc;
1347 if (UNLIKELY(zxc_pivco_tree_build(code_len, tree) != 0)) return ZXC_ERROR_CORRUPT_DATA;
1348 zxc_pivco_codes(tree, codes);
1349 return ZXC_OK;
1350}
1351#endif /* ZXC_VARIANT_PRIMARY */
1352
1360#define ZXC_PIVCO_MERGE_STEP(w, pc) \
1361 do { \
1362 rp += (size_t)(pc); \
1363 lp += (size_t)((w) - (pc)); \
1364 i += (w); \
1365 } while (0)
1366
1376static ZXC_ALWAYS_INLINE void zxc_pivco_merge(uint8_t* RESTRICT out, const uint8_t* RESTRICT src,
1377 const size_t nl, const size_t nr,
1378 const uint8_t* RESTRICT bits) {
1379 const uint8_t* L = src;
1380 const uint8_t* R = src + nl;
1381 const size_t n = nl + nr;
1382 size_t i = 0;
1383 size_t lp = 0;
1384 size_t rp = 0;
1385#if defined(ZXC_USE_NEON64)
1386 // 16 outputs per step: two-register TBL over {L[0..15], R[0..15]} with the
1387 // signed-index tables (see zxc_pivco_tables.h).
1388 while (i + 16 <= n) {
1389 const uint8_t b0 = bits[i >> 3];
1390 const uint8_t b1 = bits[(i >> 3) + 1];
1391 uint8x16x2_t tb;
1392 tb.val[0] = vld1q_u8(L + lp);
1393 tb.val[1] = vld1q_u8(R + rp);
1394 const int pc0 = zxc_pivco_popcnt32(b0);
1395 const uint8x8_t ia = vld1_u8(zxc_pivco_idxa_u8[b0]);
1396 const uint8x8_t ib = vld1_u8(zxc_pivco_idxb_pre[pc0][b1]);
1397 vst1q_u8(out + i, vqtbl2q_u8(tb, vcombine_u8(ia, ib)));
1398 const int pc = pc0 + zxc_pivco_popcnt32(b1);
1399 ZXC_PIVCO_MERGE_STEP(16, pc);
1400 }
1401#else /* x86 tiers */
1402#if defined(ZXC_USE_AVX512) && defined(__AVX512VBMI2__)
1403 // 64 outputs per step: the merge IS a pair of byte expands - L's bytes fill
1404 // the control word's 0-bit lanes, R's the 1-bit lanes. The masked expand
1405 // loads only touch the bytes they consume, so the speculative reads stay
1406 // fault-safe without extra buffer slack.
1407 const __m512i zero = _mm512_setzero_si512();
1408 while (i + 64 <= n) {
1409 uint64_t ctrl;
1410 ZXC_MEMCPY(&ctrl, bits + (i >> 3), 8);
1411 const __m512i vl =
1412 _mm512_mask_expandloadu_epi8(zero, (__mmask64)~ctrl, (const void*)(L + lp));
1413 const __m512i outv =
1414 _mm512_mask_expandloadu_epi8(vl, (__mmask64)ctrl, (const void*)(R + rp));
1415 _mm512_storeu_si512((void*)(out + i), outv);
1416 const int pc = zxc_pivco_popcnt64(ctrl);
1417 ZXC_PIVCO_MERGE_STEP(64, pc);
1418 }
1419#endif
1420#if defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || \
1421 (defined(ZXC_USE_SSE2) && defined(__SSSE3__))
1422 // 16 outputs/step: pshufb(L, ix+0x70) | pshufb(R, ix-16) selects L[ix] or
1423 // R[ix-16] (out-of-range lanes zero out); 32/step unrolls it twice.
1424#if defined(ZXC_USE_AVX2)
1425 // Same thing at 256 bits, 32 outputs/step. Both selects take the index
1426 // unbiased: table lanes all sit in [0, 31], so pshufb masks to 4 bits and
1427 // bit 4 is the L/R selector - shifted into the sign it drives the blend,
1428 // and both bias vectors go away. L and R stay two 128-bit loads: the
1429 // second cursor is lp + (16 - pcA), never lp + 16.
1430 while (i + 32 <= n) {
1431 const uint8_t* cb = bits + (i >> 3);
1432 const int pcA0 = zxc_pivco_popcnt32(cb[0]);
1433 const int pcA = pcA0 + zxc_pivco_popcnt32(cb[1]);
1434 const int pcB0 = zxc_pivco_popcnt32(cb[2]);
1435 const int pcB = pcB0 + zxc_pivco_popcnt32(cb[3]);
1436 const size_t lpB = lp + (size_t)(16 - pcA);
1437 const size_t rpB = rp + (size_t)pcA;
1438 const __m256i vl = _mm256_inserti128_si256(
1439 _mm256_castsi128_si256(_mm_loadu_si128((const __m128i*)(const void*)(L + lp))),
1440 _mm_loadu_si128((const __m128i*)(const void*)(L + lpB)), 1);
1441 const __m256i vr = _mm256_inserti128_si256(
1442 _mm256_castsi128_si256(_mm_loadu_si128((const __m128i*)(const void*)(R + rp))),
1443 _mm_loadu_si128((const __m128i*)(const void*)(R + rpB)), 1);
1444 const __m128i ixA = _mm_unpacklo_epi64(
1445 _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxa_u8[cb[0]]),
1446 _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxb_pre[pcA0][cb[1]]));
1447 const __m128i ixB = _mm_unpacklo_epi64(
1448 _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxa_u8[cb[2]]),
1449 _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxb_pre[pcB0][cb[3]]));
1450 const __m256i ix = _mm256_inserti128_si256(_mm256_castsi128_si256(ixA), ixB, 1);
1451 _mm256_storeu_si256(
1452 (__m256i*)(void*)(out + i),
1453 _mm256_blendv_epi8(_mm256_shuffle_epi8(vl, ix), _mm256_shuffle_epi8(vr, ix),
1454 _mm256_slli_epi16(ix, 3)));
1455 lp = lpB + (size_t)(16 - pcB);
1456 rp = rpB + (size_t)pcB;
1457 i += 32;
1458 }
1459#else
1460 while (i + 32 <= n) {
1461 const uint8_t* cb = bits + (i >> 3);
1462 const int pcA0 = zxc_pivco_popcnt32(cb[0]);
1463 const int pcA = pcA0 + zxc_pivco_popcnt32(cb[1]);
1464 const int pcB0 = zxc_pivco_popcnt32(cb[2]);
1465 const int pcB = pcB0 + zxc_pivco_popcnt32(cb[3]);
1466 const size_t lpB = lp + (size_t)(16 - pcA);
1467 const size_t rpB = rp + (size_t)pcA;
1468 const __m128i vlA = _mm_loadu_si128((const __m128i*)(const void*)(L + lp));
1469 const __m128i vrA = _mm_loadu_si128((const __m128i*)(const void*)(R + rp));
1470 const __m128i vlB = _mm_loadu_si128((const __m128i*)(const void*)(L + lpB));
1471 const __m128i vrB = _mm_loadu_si128((const __m128i*)(const void*)(R + rpB));
1472 const __m128i iaA = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxa_u8[cb[0]]);
1473 const __m128i ibA =
1474 _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxb_pre[pcA0][cb[1]]);
1475 const __m128i iaB = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxa_u8[cb[2]]);
1476 const __m128i ibB =
1477 _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxb_pre[pcB0][cb[3]]);
1478 const __m128i ixA = _mm_unpacklo_epi64(iaA, ibA);
1479 const __m128i ixB = _mm_unpacklo_epi64(iaB, ibB);
1480 const __m128i selA =
1481 _mm_or_si128(_mm_shuffle_epi8(vlA, _mm_add_epi8(ixA, _mm_set1_epi8(0x70))),
1482 _mm_shuffle_epi8(vrA, _mm_sub_epi8(ixA, _mm_set1_epi8(16))));
1483 const __m128i selB =
1484 _mm_or_si128(_mm_shuffle_epi8(vlB, _mm_add_epi8(ixB, _mm_set1_epi8(0x70))),
1485 _mm_shuffle_epi8(vrB, _mm_sub_epi8(ixB, _mm_set1_epi8(16))));
1486 _mm_storeu_si128((__m128i*)(void*)(out + i), selA);
1487 _mm_storeu_si128((__m128i*)(void*)(out + i + 16), selB);
1488 lp = lpB + (size_t)(16 - pcB);
1489 rp = rpB + (size_t)pcB;
1490 i += 32;
1491 }
1492#endif
1493 while (i + 16 <= n) {
1494 const uint8_t b0 = bits[i >> 3];
1495 const uint8_t b1 = bits[(i >> 3) + 1];
1496 const __m128i vl = _mm_loadu_si128((const __m128i*)(const void*)(L + lp));
1497 const __m128i vr = _mm_loadu_si128((const __m128i*)(const void*)(R + rp));
1498 const int pc0 = zxc_pivco_popcnt32(b0);
1499 const __m128i ia = _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxa_u8[b0]);
1500 const __m128i ib =
1501 _mm_loadl_epi64((const __m128i*)(const void*)zxc_pivco_idxb_pre[pc0][b1]);
1502 const __m128i ix = _mm_unpacklo_epi64(ia, ib);
1503 const __m128i sell = _mm_shuffle_epi8(vl, _mm_add_epi8(ix, _mm_set1_epi8(0x70)));
1504 const __m128i selr = _mm_shuffle_epi8(vr, _mm_sub_epi8(ix, _mm_set1_epi8(16)));
1505 _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_or_si128(sell, selr));
1506 const int pc = pc0 + zxc_pivco_popcnt32(b1);
1507 ZXC_PIVCO_MERGE_STEP(16, pc);
1508 }
1509#elif defined(ZXC_USE_NEON32)
1510 // 8 outputs per step: four-register VTBL over {L[0..7], -, R[0..7], -}. The
1511 // shared index tables address a 32-lane view, and an 8-output step only
1512 // touches lanes 0..7 and 16..23, so the two spare d-registers stay zero.
1513 while (i + 8 <= n) {
1514 const uint8_t b = bits[i >> 3];
1515 uint8x8x4_t tb;
1516 tb.val[0] = vld1_u8(L + lp);
1517 tb.val[1] = vdup_n_u8(0);
1518 tb.val[2] = vld1_u8(R + rp);
1519 tb.val[3] = vdup_n_u8(0);
1520 vst1_u8(out + i, vtbl4_u8(tb, vld1_u8(zxc_pivco_idxa_u8[b])));
1521 const int pc = zxc_pivco_popcnt32(b);
1522 ZXC_PIVCO_MERGE_STEP(8, pc);
1523 }
1524#endif
1525#endif /* x86 tiers */
1526 // Portable 8-output path: byte selects from a 24-byte scratch laid out
1527 // as the low half of the 32-lane view the shared index tables assume
1528 // (L at offset 0, R at offset 16).
1529 while (i + 8 <= n) {
1530 const uint8_t b = bits[i >> 3];
1531 uint8_t comb[24];
1532 ZXC_MEMCPY(comb, L + lp, 8);
1533 ZXC_MEMCPY(comb + 16, R + rp, 8);
1534 const uint8_t* ix = zxc_pivco_idxa_u8[b];
1535 for (int j = 0; j < 8; j++) out[i + (size_t)j] = comb[ix[j]];
1536 const int pc = zxc_pivco_popcnt32(b);
1537 ZXC_PIVCO_MERGE_STEP(8, pc);
1538 }
1539 while (i < n) {
1540 const int bit = (bits[i >> 3] >> (i & 7)) & 1;
1541 out[i++] = bit ? R[rp++] : L[lp++];
1542 }
1543}
1544
1545#undef ZXC_PIVCO_MERGE_STEP
1546
1555static void zxc_pivco_unpack_flat(uint8_t* RESTRICT out, const size_t n, const int D,
1556 const uint8_t* RESTRICT bits, const uint8_t* RESTRICT c2s) {
1557 size_t i = 0;
1558#if defined(ZXC_USE_NEON64)
1559 if (D == 2) {
1560 uint8_t c2s16[16];
1561 for (int k = 0; k < 16; k++) c2s16[k] = c2s[k & 3];
1562 const uint8x16_t vc2s = vld1q_u8(c2s16);
1563 static const uint8_t rep4[16] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3};
1564 static const int8_t sh2[16] = {0, -2, -4, -6, 0, -2, -4, -6, 0, -2, -4, -6, 0, -2, -4, -6};
1565 const uint8x16_t vrep = vld1q_u8(rep4);
1566 const int8x16_t vsh = vld1q_s8(sh2);
1567 const uint8x16_t vmask = vdupq_n_u8(0x03);
1568 while (i + 16 <= n) {
1569 uint8x16_t raw = vdupq_n_u8(0);
1570 raw = vreinterpretq_u8_u32(
1571 vsetq_lane_u32(zxc_le32(bits + ((i * 2) >> 3)), vreinterpretq_u32_u8(raw), 0));
1572 const uint8x16_t rep = vqtbl1q_u8(raw, vrep);
1573 const uint8x16_t codes = vandq_u8(vshlq_u8(rep, vsh), vmask);
1574 vst1q_u8(out + i, vqtbl1q_u8(vc2s, codes));
1575 i += 16;
1576 }
1577 } else if (D == 3) {
1578 // Odd D straddle bytes: build a 16-bit window {byte_j,byte_j+1} per lane
1579 // (byte_j = Di>>3), right-shift by Di&7, mask D bits. 16/step, two u16x8
1580 // halves sharing the shift vector. D=3: 6 source bytes.
1581 uint8_t c2s16[16];
1582 for (int k = 0; k < 16; k++) c2s16[k] = c2s[k & 7];
1583 const uint8x16_t vc2s = vld1q_u8(c2s16); /* 8 entries, padded to 16 */
1584 static const uint8_t idxA[16] = {0, 1, 0, 1, 0, 1, 1, 2, 1, 2, 1, 2, 2, 3, 2, 3};
1585 static const uint8_t idxB[16] = {3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 5, 5, 6, 5, 6};
1586 static const int16_t sh8[8] = {0, -3, -6, -1, -4, -7, -2, -5};
1587 const uint8x16_t vidxA = vld1q_u8(idxA);
1588 const uint8x16_t vidxB = vld1q_u8(idxB);
1589 const int16x8_t vsh = vld1q_s16(sh8);
1590 const uint16x8_t vmask = vdupq_n_u16(0x07);
1591 while (i + 16 <= n) {
1592 const size_t off = (i * 3) >> 3;
1593 uint32_t w0;
1594 uint16_t w1;
1595 ZXC_MEMCPY(&w0, bits + off, 4);
1596 ZXC_MEMCPY(&w1, bits + off + 4, 2); /* exactly 6 bytes read */
1597 uint8x16_t raw = vdupq_n_u8(0);
1598 raw = vreinterpretq_u8_u32(vsetq_lane_u32(w0, vreinterpretq_u32_u8(raw), 0));
1599 raw = vreinterpretq_u8_u16(vsetq_lane_u16(w1, vreinterpretq_u16_u8(raw), 2));
1600 const uint16x8_t winA = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxA));
1601 const uint16x8_t winB = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxB));
1602 const uint8x8_t cA = vmovn_u16(vandq_u16(vshlq_u16(winA, vsh), vmask));
1603 const uint8x8_t cB = vmovn_u16(vandq_u16(vshlq_u16(winB, vsh), vmask));
1604 vst1q_u8(out + i, vqtbl1q_u8(vc2s, vcombine_u8(cA, cB)));
1605 i += 16;
1606 }
1607 } else if (D == 4) {
1608 const uint8x16_t vc2s = vld1q_u8(c2s); /* 16 entries */
1609 static const uint8_t rep2[16] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7};
1610 static const int8_t sh4[16] = {0, -4, 0, -4, 0, -4, 0, -4, 0, -4, 0, -4, 0, -4, 0, -4};
1611 const uint8x16_t vrep = vld1q_u8(rep2);
1612 const int8x16_t vsh = vld1q_s8(sh4);
1613 const uint8x16_t vmask = vdupq_n_u8(0x0F);
1614 while (i + 16 <= n) {
1615 uint8x16_t raw = vdupq_n_u8(0);
1616 raw = vreinterpretq_u8_u64(
1617 vsetq_lane_u64(zxc_le64(bits + ((i * 4) >> 3)), vreinterpretq_u64_u8(raw), 0));
1618 const uint8x16_t rep = vqtbl1q_u8(raw, vrep);
1619 const uint8x16_t codes = vandq_u8(vshlq_u8(rep, vsh), vmask);
1620 vst1q_u8(out + i, vqtbl1q_u8(vc2s, codes));
1621 i += 16;
1622 }
1623 } else if (D == 5) {
1624 // Same window scheme as D=3; c2s has 32 entries (vqtbl2q). 10 bytes.
1625 const uint8x16x2_t vc2s = vld1q_u8_x2(c2s);
1626 static const uint8_t idxA[16] = {0, 1, 0, 1, 1, 2, 1, 2, 2, 3, 3, 4, 3, 4, 4, 5};
1627 static const uint8_t idxB[16] = {5, 6, 5, 6, 6, 7, 6, 7, 7, 8, 8, 9, 8, 9, 9, 10};
1628 static const int16_t sh8[8] = {0, -5, -2, -7, -4, -1, -6, -3};
1629 const uint8x16_t vidxA = vld1q_u8(idxA);
1630 const uint8x16_t vidxB = vld1q_u8(idxB);
1631 const int16x8_t vsh = vld1q_s16(sh8);
1632 const uint16x8_t vmask = vdupq_n_u16(0x1F);
1633 while (i + 16 <= n) {
1634 const size_t off = (i * 5) >> 3;
1635 uint64_t w0;
1636 uint16_t w1;
1637 ZXC_MEMCPY(&w0, bits + off, 8);
1638 ZXC_MEMCPY(&w1, bits + off + 8, 2);
1639 uint8x16_t raw = vdupq_n_u8(0);
1640 raw = vreinterpretq_u8_u64(vsetq_lane_u64(w0, vreinterpretq_u64_u8(raw), 0));
1641 raw = vreinterpretq_u8_u16(vsetq_lane_u16(w1, vreinterpretq_u16_u8(raw), 4));
1642 const uint16x8_t winA = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxA));
1643 const uint16x8_t winB = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxB));
1644 const uint8x8_t cA = vmovn_u16(vandq_u16(vshlq_u16(winA, vsh), vmask));
1645 const uint8x8_t cB = vmovn_u16(vandq_u16(vshlq_u16(winB, vsh), vmask));
1646 vst1q_u8(out + i, vqtbl2q_u8(vc2s, vcombine_u8(cA, cB)));
1647 i += 16;
1648 }
1649 } else if (D == 6) {
1650 // Same window scheme as D=3; c2s has 64 entries (vqtbl4q). 12 bytes.
1651 const uint8x16x4_t vc2s = vld1q_u8_x4(c2s);
1652 static const uint8_t idxA[16] = {0, 1, 0, 1, 1, 2, 2, 3, 3, 4, 3, 4, 4, 5, 5, 6};
1653 static const uint8_t idxB[16] = {6, 7, 6, 7, 7, 8, 8, 9, 9, 10, 9, 10, 10, 11, 11, 12};
1654 static const int16_t sh8[8] = {0, -6, -4, -2, 0, -6, -4, -2};
1655 const uint8x16_t vidxA = vld1q_u8(idxA);
1656 const uint8x16_t vidxB = vld1q_u8(idxB);
1657 const int16x8_t vsh = vld1q_s16(sh8);
1658 const uint16x8_t vmask = vdupq_n_u16(0x3F);
1659 while (i + 16 <= n) {
1660 const size_t off = (i * 6) >> 3;
1661 uint64_t w0;
1662 uint32_t w1;
1663 ZXC_MEMCPY(&w0, bits + off, 8);
1664 ZXC_MEMCPY(&w1, bits + off + 8, 4);
1665 uint8x16_t raw = vdupq_n_u8(0);
1666 raw = vreinterpretq_u8_u64(vsetq_lane_u64(w0, vreinterpretq_u64_u8(raw), 0));
1667 raw = vreinterpretq_u8_u32(vsetq_lane_u32(w1, vreinterpretq_u32_u8(raw), 2));
1668 const uint16x8_t winA = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxA));
1669 const uint16x8_t winB = vreinterpretq_u16_u8(vqtbl1q_u8(raw, vidxB));
1670 const uint8x8_t cA = vmovn_u16(vandq_u16(vshlq_u16(winA, vsh), vmask));
1671 const uint8x8_t cB = vmovn_u16(vandq_u16(vshlq_u16(winB, vsh), vmask));
1672 vst1q_u8(out + i, vqtbl4q_u8(vc2s, vcombine_u8(cA, cB)));
1673 i += 16;
1674 }
1675 }
1676#elif defined(ZXC_USE_NEON32)
1677 // d-register mirrors of the AArch64 kernels: 8 codes per step, c2s lookup
1678 // via VTBL (16-entry table = two d-registers).
1679 if (D == 2) {
1680 uint8_t c2s8[8];
1681 for (int k = 0; k < 8; k++) c2s8[k] = c2s[k & 3];
1682 const uint8x8_t vc2s = vld1_u8(c2s8);
1683 static const uint8_t rep4[8] = {0, 0, 0, 0, 1, 1, 1, 1};
1684 static const int8_t sh2[8] = {0, -2, -4, -6, 0, -2, -4, -6};
1685 const uint8x8_t vrep = vld1_u8(rep4);
1686 const int8x8_t vsh = vld1_s8(sh2);
1687 const uint8x8_t vmask = vdup_n_u8(0x03);
1688 while (i + 8 <= n) {
1689 const uint16_t w = (uint16_t)((uint16_t)bits[(i * 2) >> 3] |
1690 ((uint16_t)bits[((i * 2) >> 3) + 1] << 8));
1691 const uint8x8_t raw = vreinterpret_u8_u16(vdup_n_u16(w));
1692 const uint8x8_t rep = vtbl1_u8(raw, vrep);
1693 const uint8x8_t codes = vand_u8(vshl_u8(rep, vsh), vmask);
1694 vst1_u8(out + i, vtbl1_u8(vc2s, codes));
1695 i += 8;
1696 }
1697 } else if (D == 3) {
1698 // 8 codes/step. 3-bit codes straddle bytes, so build a 16-bit window
1699 // {byte_j, byte_j+1} per lane via two VTBLs, then extract bits [s,s+3)
1700 // by multiply-by-2^(13-s) then >>13 - ARMv7 has no per-lane u16 variable
1701 // shift. 8 codes fit in 3 source bytes.
1702 const uint8x8_t vc2s = vld1_u8(c2s); /* 8 entries (2^3) */
1703 static const uint8_t idxlo[8] = {0, 1, 0, 1, 0, 1, 1, 2};
1704 static const uint8_t idxhi[8] = {1, 2, 1, 2, 2, 3, 2, 3};
1705 static const uint16_t mul8[8] = {8192, 1024, 128, 4096, 512, 64, 2048, 256};
1706 const uint8x8_t vidxlo = vld1_u8(idxlo);
1707 const uint8x8_t vidxhi = vld1_u8(idxhi);
1708 const uint16x8_t vmul = vld1q_u16(mul8);
1709 const uint16x8_t vmask = vdupq_n_u16(0x07);
1710 while (i + 8 <= n) {
1711 const size_t off = (i * 3) >> 3;
1712 const uint32_t w = (uint32_t)bits[off] | ((uint32_t)bits[off + 1] << 8) |
1713 ((uint32_t)bits[off + 2] << 16); /* exactly 3 bytes */
1714 const uint8x8_t raw = vreinterpret_u8_u32(vdup_n_u32(w));
1715 const uint8x8_t wlo = vtbl1_u8(raw, vidxlo);
1716 const uint8x8_t whi = vtbl1_u8(raw, vidxhi);
1717 const uint16x8_t win = vreinterpretq_u16_u8(vcombine_u8(wlo, whi));
1718 const uint16x8_t codes16 = vandq_u16(vshrq_n_u16(vmulq_u16(win, vmul), 13), vmask);
1719 vst1_u8(out + i, vtbl1_u8(vc2s, vmovn_u16(codes16)));
1720 i += 8;
1721 }
1722 } else if (D == 4) {
1723 /* Register by register: GCC has no AArch32 vld1_u8_x2/_x4 before 14.
1724 * Loaded once per call, so the tuple form bought nothing anyway. */
1725 uint8x8x2_t vc2s;
1726 vc2s.val[0] = vld1_u8(c2s);
1727 vc2s.val[1] = vld1_u8(c2s + 8);
1728 static const uint8_t rep2[8] = {0, 0, 1, 1, 2, 2, 3, 3};
1729 static const int8_t sh4[8] = {0, -4, 0, -4, 0, -4, 0, -4};
1730 const uint8x8_t vrep = vld1_u8(rep2);
1731 const int8x8_t vsh = vld1_s8(sh4);
1732 const uint8x8_t vmask = vdup_n_u8(0x0F);
1733 while (i + 8 <= n) {
1734 uint32_t w;
1735 ZXC_MEMCPY(&w, bits + ((i * 4) >> 3), 4);
1736 const uint8x8_t raw = vreinterpret_u8_u32(vdup_n_u32(w));
1737 const uint8x8_t rep = vtbl1_u8(raw, vrep);
1738 const uint8x8_t codes = vand_u8(vshl_u8(rep, vsh), vmask);
1739 vst1_u8(out + i, vtbl2_u8(vc2s, codes));
1740 i += 8;
1741 }
1742 } else if (D == 5) {
1743 // Same window scheme as D=3; c2s has 32 entries (VTBL4). 5 source bytes.
1744 // Register by register: see D == 4.
1745 uint8x8x4_t vc2s;
1746 vc2s.val[0] = vld1_u8(c2s);
1747 vc2s.val[1] = vld1_u8(c2s + 8);
1748 vc2s.val[2] = vld1_u8(c2s + 16);
1749 vc2s.val[3] = vld1_u8(c2s + 24);
1750 static const uint8_t idxlo[8] = {0, 1, 0, 1, 1, 2, 1, 2};
1751 static const uint8_t idxhi[8] = {2, 3, 3, 4, 3, 4, 4, 5};
1752 static const uint16_t mul8[8] = {2048, 64, 512, 16, 128, 1024, 32, 256};
1753 const uint8x8_t vidxlo = vld1_u8(idxlo);
1754 const uint8x8_t vidxhi = vld1_u8(idxhi);
1755 const uint16x8_t vmul = vld1q_u16(mul8);
1756 const uint16x8_t vmask = vdupq_n_u16(0x1F);
1757 while (i + 8 <= n) {
1758 uint64_t w = 0;
1759 ZXC_MEMCPY(&w, bits + ((i * 5) >> 3), 5);
1760 const uint8x8_t raw = vcreate_u8(w);
1761 const uint16x8_t win =
1762 vreinterpretq_u16_u8(vcombine_u8(vtbl1_u8(raw, vidxlo), vtbl1_u8(raw, vidxhi)));
1763 const uint8x8_t codes =
1764 vmovn_u16(vandq_u16(vshrq_n_u16(vmulq_u16(win, vmul), 11), vmask));
1765 vst1_u8(out + i, vtbl4_u8(vc2s, codes));
1766 i += 8;
1767 }
1768 } else if (D == 6) {
1769 // Same window scheme; c2s has 64 entries: two VTBL4 halves, select by
1770 // code bit 5. 6 source bytes.
1771 uint8x8x4_t lo, hi; // register by register: see D == 4
1772 lo.val[0] = vld1_u8(c2s);
1773 lo.val[1] = vld1_u8(c2s + 8);
1774 lo.val[2] = vld1_u8(c2s + 16);
1775 lo.val[3] = vld1_u8(c2s + 24);
1776 hi.val[0] = vld1_u8(c2s + 32);
1777 hi.val[1] = vld1_u8(c2s + 40);
1778 hi.val[2] = vld1_u8(c2s + 48);
1779 hi.val[3] = vld1_u8(c2s + 56);
1780 static const uint8_t idxlo[8] = {0, 1, 0, 1, 1, 2, 2, 3};
1781 static const uint8_t idxhi[8] = {3, 4, 3, 4, 4, 5, 5, 6};
1782 static const uint16_t mul8[8] = {1024, 16, 64, 256, 1024, 16, 64, 256};
1783 const uint8x8_t vidxlo = vld1_u8(idxlo);
1784 const uint8x8_t vidxhi = vld1_u8(idxhi);
1785 const uint16x8_t vmul = vld1q_u16(mul8);
1786 const uint16x8_t vmask = vdupq_n_u16(0x3F);
1787 while (i + 8 <= n) {
1788 uint64_t w = 0;
1789 ZXC_MEMCPY(&w, bits + ((i * 6) >> 3), 6);
1790 const uint8x8_t raw = vcreate_u8(w);
1791 const uint16x8_t win =
1792 vreinterpretq_u16_u8(vcombine_u8(vtbl1_u8(raw, vidxlo), vtbl1_u8(raw, vidxhi)));
1793 const uint8x8_t codes =
1794 vmovn_u16(vandq_u16(vshrq_n_u16(vmulq_u16(win, vmul), 10), vmask));
1795 const uint8x8_t sel =
1796 vbsl_u8(vcge_u8(codes, vdup_n_u8(32)), vtbl4_u8(hi, vsub_u8(codes, vdup_n_u8(32))),
1797 vtbl4_u8(lo, codes));
1798 vst1_u8(out + i, sel);
1799 i += 8;
1800 }
1801 }
1802#elif defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || \
1803 (defined(ZXC_USE_SSE2) && defined(__SSE4_1__))
1804#if defined(ZXC_USE_AVX512) && defined(__AVX512VBMI__)
1805 /* vpmultishiftqb reads an 8-bit field at an arbitrary bit offset: the
1806 * unaligned D-bit read, in one instruction. Two facts carry the rest -
1807 * 64 codes are exactly 8*D bytes and byte-aligned, so the tables are the
1808 * same every step; and only the low D bits of a field survive, so bytes
1809 * past 8*D are irrelevant and both accesses can be clamped to the run.
1810 * Clamping the store also ends the run, hence nothing narrower below. */
1811 if (D >= 2 && D <= 6) {
1812 uint8_t arr[64];
1813 uint8_t shf[64];
1814 for (int k = 0; k < 8; k++) {
1815 for (int j = 0; j < 8; j++) {
1816 arr[k * 8 + j] = (uint8_t)(k * D + j);
1817 shf[k * 8 + j] = (uint8_t)(j * D);
1818 }
1819 }
1820 const __m512i varr = _mm512_loadu_si512((const void*)arr);
1821 const __m512i vshf = _mm512_loadu_si512((const void*)shf);
1822 const __m512i vand = _mm512_set1_epi8((char)((1 << D) - 1));
1823 /* c2s holds 2^D entries and nothing is guaranteed past them. */
1824 const __mmask64 tm = (D == 6) ? ~(__mmask64)0 : (((__mmask64)1 << (1 << D)) - 1);
1825 const __m512i vc2s = _mm512_maskz_loadu_epi8(tm, (const void*)c2s);
1826 const size_t nbytes = zxc_pivco_run_bytes((uint32_t)n, (uint8_t)D);
1827 while (i < n) {
1828 const size_t off = ((size_t)i * (size_t)D) >> 3;
1829 const size_t avail = nbytes - off;
1830 const size_t left = n - i;
1831 const __mmask64 lm = (avail >= 64) ? ~(__mmask64)0 : (((__mmask64)1 << avail) - 1);
1832 const __mmask64 sm = (left >= 64) ? ~(__mmask64)0 : (((__mmask64)1 << left) - 1);
1833 const __m512i raw = _mm512_maskz_loadu_epi8(lm, (const void*)(bits + off));
1834 const __m512i q = _mm512_permutexvar_epi8(varr, raw);
1835 const __m512i codes = _mm512_and_si512(_mm512_multishift_epi64_epi8(vshf, q), vand);
1836 _mm512_mask_storeu_epi8((void*)(out + i), sm, _mm512_permutexvar_epi8(codes, vc2s));
1837 i += 64;
1838 }
1839 if (i > n) i = n;
1840 }
1841#else
1842#if defined(ZXC_USE_AVX2)
1843 /* Codes 16..31 start exactly 2*D bytes in, so the upper half runs the same
1844 * pattern over a shifted source: one shuffle vector serves both, and packus
1845 * leaves each half's codes in order in its own lane - no cross-lane fixup.
1846 * The bound keeps both 16-byte reads inside the run; the exact-length
1847 * 16-wide loops below take the tail. */
1848 if (D >= 2 && D <= 6) {
1849 uint8_t sa[16];
1850 uint8_t sb[16];
1851 uint16_t mu[8];
1852 for (int q = 0; q < 8; q++) {
1853 const int ba = (q * D) >> 3;
1854 sa[2 * q] = (uint8_t)ba;
1855 sa[2 * q + 1] = (uint8_t)(ba + 1);
1856 sb[2 * q] = (uint8_t)(ba + D); /* code q+8 sits D bytes further */
1857 sb[2 * q + 1] = (uint8_t)(ba + D + 1);
1858 mu[q] = (uint16_t)(1U << (16 - D - ((q * D) & 7)));
1859 }
1860 const __m256i vsa = _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i*)(void*)sa));
1861 const __m256i vsb = _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i*)(void*)sb));
1862 const __m256i vmu = _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i*)(void*)mu));
1863 const __m256i vand = _mm256_set1_epi8((char)((1 << D) - 1));
1864 uint8_t tab[64];
1865 for (int e = 0; e < 64; e++) tab[e] = c2s[e & ((1 << D) - 1)];
1866 const __m256i t0 = _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i*)(void*)tab));
1867 const __m256i t1 =
1868 _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i*)(void*)(tab + 16)));
1869 const __m256i t2 =
1870 _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i*)(void*)(tab + 32)));
1871 const __m256i t3 =
1872 _mm256_broadcastsi128_si256(_mm_loadu_si128((const __m128i*)(void*)(tab + 48)));
1873 const size_t nbytes = zxc_pivco_run_bytes((uint32_t)n, (uint8_t)D);
1874 while (i + 32 <= n && ((size_t)i * (size_t)D >> 3) + (size_t)(2 * D) + 16 <= nbytes) {
1875 const uint8_t* src = bits + ((size_t)i * (size_t)D >> 3);
1876 const __m256i raw = _mm256_inserti128_si256(
1877 _mm256_castsi128_si256(_mm_loadu_si128((const __m128i*)(const void*)src)),
1878 _mm_loadu_si128((const __m128i*)(const void*)(src + 2 * D)), 1);
1879 const __m256i ca =
1880 _mm256_srli_epi16(_mm256_mullo_epi16(_mm256_shuffle_epi8(raw, vsa), vmu), 16 - D);
1881 const __m256i cb =
1882 _mm256_srli_epi16(_mm256_mullo_epi16(_mm256_shuffle_epi8(raw, vsb), vmu), 16 - D);
1883 const __m256i codes = _mm256_and_si256(_mm256_packus_epi16(ca, cb), vand);
1884 __m256i sym;
1885 if (D <= 4) {
1886 sym = _mm256_shuffle_epi8(t0, codes);
1887 } else {
1888 const __m256i b4 = _mm256_slli_epi16(codes, 3); /* code bit 4 -> sign */
1889 const __m256i lo = _mm256_blendv_epi8(_mm256_shuffle_epi8(t0, codes),
1890 _mm256_shuffle_epi8(t1, codes), b4);
1891 if (D == 5) {
1892 sym = lo;
1893 } else {
1894 const __m256i hi = _mm256_blendv_epi8(_mm256_shuffle_epi8(t2, codes),
1895 _mm256_shuffle_epi8(t3, codes), b4);
1896 sym = _mm256_blendv_epi8(lo, hi, _mm256_slli_epi16(codes, 2));
1897 }
1898 }
1899 _mm256_storeu_si256((__m256i*)(void*)(out + i), sym);
1900 i += 32;
1901 }
1902 }
1903#endif
1904 if (D == 4) {
1905 const __m128i vc2s = _mm_loadu_si128((const __m128i*)(const void*)c2s);
1906 const __m128i vrep = _mm_setr_epi8(0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7);
1907 const __m128i vmask = _mm_set1_epi8(0x0F);
1908 // even lanes keep low nibble, odd lanes take the high nibble: shift the
1909 // replicated bytes right by 4 via a 16-bit shift + odd-lane select.
1910 const __m128i vodd = _mm_set1_epi16(0x0F00 - 0x0F00 + (short)0xFF00);
1911 while (i + 16 <= n) {
1912 const __m128i raw =
1913 _mm_loadl_epi64((const __m128i*)(const void*)(bits + ((i * 4) >> 3)));
1914 const __m128i rep = _mm_shuffle_epi8(raw, vrep);
1915 const __m128i hi = _mm_and_si128(_mm_srli_epi16(rep, 4), _mm_set1_epi8(0x0F));
1916 const __m128i lo = _mm_and_si128(rep, vmask);
1917 const __m128i codes = _mm_blendv_epi8(lo, hi, vodd);
1918 _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_shuffle_epi8(vc2s, codes));
1919 i += 16;
1920 }
1921 } else if (D == 2) {
1922 // 16 codes from 4 bytes. SSE has no per-lane byte shift, so shift by
1923 // multiply: (b << (6 - 2j)) >> 6 keeps bits [2j+1:2j]. Bytes spread to
1924 // u16 lanes, times {64,16,4,1}, shifted, then packed back.
1925 uint8_t c2s16[16];
1926 for (int k = 0; k < 16; k++) c2s16[k] = c2s[k & 3];
1927 const __m128i vc2s = _mm_loadu_si128((const __m128i*)(const void*)c2s16);
1928 const __m128i vspreadA =
1929 _mm_setr_epi8(0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 0, (char)0x80, 1, (char)0x80,
1930 1, (char)0x80, 1, (char)0x80, 1, (char)0x80);
1931 const __m128i vspreadB =
1932 _mm_setr_epi8(2, (char)0x80, 2, (char)0x80, 2, (char)0x80, 2, (char)0x80, 3, (char)0x80,
1933 3, (char)0x80, 3, (char)0x80, 3, (char)0x80);
1934 const __m128i vmul = _mm_setr_epi16(64, 16, 4, 1, 64, 16, 4, 1);
1935 while (i + 16 <= n) {
1936 const __m128i raw = _mm_cvtsi32_si128(
1937 (int)((uint32_t)bits[(i * 2) >> 3] | ((uint32_t)bits[((i * 2) >> 3) + 1] << 8) |
1938 ((uint32_t)bits[((i * 2) >> 3) + 2] << 16) |
1939 ((uint32_t)bits[((i * 2) >> 3) + 3] << 24)));
1940 const __m128i a16 =
1941 _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, vspreadA), vmul), 6);
1942 const __m128i b16 =
1943 _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, vspreadB), vmul), 6);
1944 const __m128i codes = _mm_and_si128(_mm_packus_epi16(a16, b16), _mm_set1_epi8(3));
1945 _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_shuffle_epi8(vc2s, codes));
1946 i += 16;
1947 }
1948 } else if (D == 3) {
1949 // 16-bit window {byte_j,byte_j+1} per lane (byte_j = Di>>3); SSE has no
1950 // per-lane u16 shift, so extract bits [s,s+D) of window w by w*2^(16-D-s)
1951 // (field lands in the top D bits) then >>(16-D); s_j = Dj&7. D=3: 6 bytes.
1952 uint8_t c2s16[16];
1953 for (int k = 0; k < 16; k++) c2s16[k] = c2s[k & 7];
1954 const __m128i vc2s = _mm_loadu_si128((const __m128i*)(const void*)c2s16);
1955 const __m128i shufA = _mm_setr_epi8(0, 1, 0, 1, 0, 1, 1, 2, 1, 2, 1, 2, 2, 3, 2, 3);
1956 const __m128i shufB = _mm_setr_epi8(3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 4, 5, 5, 6, 5, 6);
1957 const __m128i vmul = _mm_setr_epi16(8192, 1024, 128, 4096, 512, 64, 2048, 256);
1958 while (i + 16 <= n) {
1959 const size_t off = (i * 3) >> 3;
1960 uint32_t w0;
1961 uint16_t w1;
1962 ZXC_MEMCPY(&w0, bits + off, 4);
1963 ZXC_MEMCPY(&w1, bits + off + 4, 2); /* exactly 6 bytes read */
1964 const __m128i raw = _mm_insert_epi16(_mm_cvtsi32_si128((int)w0), w1, 2);
1965 const __m128i cA =
1966 _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufA), vmul), 13);
1967 const __m128i cB =
1968 _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufB), vmul), 13);
1969 const __m128i codes = _mm_and_si128(_mm_packus_epi16(cA, cB), _mm_set1_epi8(7));
1970 _mm_storeu_si128((__m128i*)(void*)(out + i), _mm_shuffle_epi8(vc2s, codes));
1971 i += 16;
1972 }
1973 } else if (D == 5) {
1974 // c2s has 32 entries: pshufb does 16, so select lo/hi by code bit 4.
1975 const __m128i vlo = _mm_loadu_si128((const __m128i*)(const void*)c2s);
1976 const __m128i vhi = _mm_loadu_si128((const __m128i*)(const void*)(c2s + 16));
1977 const __m128i shufA = _mm_setr_epi8(0, 1, 0, 1, 1, 2, 1, 2, 2, 3, 3, 4, 3, 4, 4, 5);
1978 const __m128i shufB = _mm_setr_epi8(5, 6, 5, 6, 6, 7, 6, 7, 7, 8, 8, 9, 8, 9, 9, 10);
1979 const __m128i vmul = _mm_setr_epi16(2048, 64, 512, 16, 128, 1024, 32, 256);
1980 while (i + 16 <= n) {
1981 const size_t off = (i * 5) >> 3;
1982 uint16_t w1;
1983 ZXC_MEMCPY(&w1, bits + off + 8, 2); /* 10 source bytes */
1984 const __m128i raw =
1985 _mm_insert_epi16(_mm_loadl_epi64((const __m128i*)(const void*)(bits + off)), w1, 4);
1986 const __m128i cA =
1987 _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufA), vmul), 11);
1988 const __m128i cB =
1989 _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufB), vmul), 11);
1990 const __m128i codes = _mm_and_si128(_mm_packus_epi16(cA, cB), _mm_set1_epi8(0x1F));
1991 const __m128i sel =
1992 _mm_blendv_epi8(_mm_shuffle_epi8(vlo, codes), _mm_shuffle_epi8(vhi, codes),
1993 _mm_slli_epi16(codes, 3));
1994 _mm_storeu_si128((__m128i*)(void*)(out + i), sel);
1995 i += 16;
1996 }
1997 } else if (D == 6) {
1998 // c2s has 64 entries: 4 pshufb sub-tables, select by code bits 4-5.
1999 const __m128i t0 = _mm_loadu_si128((const __m128i*)(const void*)c2s);
2000 const __m128i t1 = _mm_loadu_si128((const __m128i*)(const void*)(c2s + 16));
2001 const __m128i t2 = _mm_loadu_si128((const __m128i*)(const void*)(c2s + 32));
2002 const __m128i t3 = _mm_loadu_si128((const __m128i*)(const void*)(c2s + 48));
2003 const __m128i shufA = _mm_setr_epi8(0, 1, 0, 1, 1, 2, 2, 3, 3, 4, 3, 4, 4, 5, 5, 6);
2004 const __m128i shufB = _mm_setr_epi8(6, 7, 6, 7, 7, 8, 8, 9, 9, 10, 9, 10, 10, 11, 11, 12);
2005 const __m128i vmul = _mm_setr_epi16(1024, 16, 64, 256, 1024, 16, 64, 256);
2006 while (i + 16 <= n) {
2007 const size_t off = (i * 6) >> 3;
2008 uint32_t w1;
2009 ZXC_MEMCPY(&w1, bits + off + 8, 4); /* 12 source bytes */
2010 const __m128i raw = _mm_insert_epi32(
2011 _mm_loadl_epi64((const __m128i*)(const void*)(bits + off)), (int)w1, 2);
2012 const __m128i cA =
2013 _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufA), vmul), 10);
2014 const __m128i cB =
2015 _mm_srli_epi16(_mm_mullo_epi16(_mm_shuffle_epi8(raw, shufB), vmul), 10);
2016 const __m128i codes = _mm_and_si128(_mm_packus_epi16(cA, cB), _mm_set1_epi8(0x3F));
2017 const __m128i m4 = _mm_slli_epi16(codes, 3);
2018 const __m128i lo =
2019 _mm_blendv_epi8(_mm_shuffle_epi8(t0, codes), _mm_shuffle_epi8(t1, codes), m4);
2020 const __m128i hi =
2021 _mm_blendv_epi8(_mm_shuffle_epi8(t2, codes), _mm_shuffle_epi8(t3, codes), m4);
2022 _mm_storeu_si128((__m128i*)(void*)(out + i),
2023 _mm_blendv_epi8(lo, hi, _mm_slli_epi16(codes, 2)));
2024 i += 16;
2025 }
2026 }
2027#endif /* AVX-512 VBMI, else AVX2 + SSE */
2028#endif
2029 // Generic scalar bit-reader (any D).
2030 {
2031 uint64_t bitpos = i * (uint64_t)D;
2032 const uint32_t m = (1U << D) - 1U;
2033 const size_t fbytes = (size_t)(((uint64_t)n * (uint64_t)D + 7U) >> 3);
2034 while (i < n) {
2035 const size_t byte = (size_t)(bitpos >> 3);
2036 uint32_t w = bits[byte];
2037 if (byte + 1 < fbytes) w |= (uint32_t)bits[byte + 1] << 8;
2038 if (D > 8 && byte + 2 < fbytes) w |= (uint32_t)bits[byte + 2] << 16;
2039 out[i++] = c2s[(w >> (bitpos & 7U)) & m];
2040 bitpos += (uint64_t)D;
2041 }
2042 }
2043}
2044
2053static ZXC_ALWAYS_INLINE void zxc_pivco_emit_leaf_pair(uint8_t* RESTRICT out, const size_t n,
2054 const uint8_t sym0, const uint8_t sym1,
2055 const uint8_t* RESTRICT bits) {
2056 size_t i = 0;
2057#if defined(ZXC_USE_NEON64)
2058 const uint8x16_t vsym0 = vdupq_n_u8(sym0);
2059 const uint8x16_t vdelta = vdupq_n_u8((uint8_t)(sym0 ^ sym1));
2060 static const uint8_t rep_idx[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1};
2061 static const uint8_t bit_sel[16] = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128};
2062 const uint8x16_t vrep = vld1q_u8(rep_idx);
2063 const uint8x16_t vsel = vld1q_u8(bit_sel);
2064 while (i + 16 <= n) {
2065 uint8x16_t ctrl = vdupq_n_u8(0);
2066 ctrl = vsetq_lane_u8(bits[i >> 3], ctrl, 0);
2067 ctrl = vsetq_lane_u8(bits[(i >> 3) + 1], ctrl, 1);
2068 const uint8x16_t rep = vqtbl1q_u8(ctrl, vrep);
2069 const uint8x16_t mask = vtstq_u8(rep, vsel);
2070 vst1q_u8(out + i, veorq_u8(vsym0, vandq_u8(vdelta, mask)));
2071 i += 16;
2072 }
2073#elif defined(ZXC_USE_NEON32)
2074 const uint8x8_t vsym0 = vdup_n_u8(sym0);
2075 const uint8x8_t vdelta = vdup_n_u8((uint8_t)(sym0 ^ sym1));
2076 static const uint8_t bit_sel8[8] = {1, 2, 4, 8, 16, 32, 64, 128};
2077 const uint8x8_t vsel = vld1_u8(bit_sel8);
2078 while (i + 8 <= n) {
2079 const uint8x8_t mask = vtst_u8(vdup_n_u8(bits[i >> 3]), vsel);
2080 vst1_u8(out + i, veor_u8(vsym0, vand_u8(vdelta, mask)));
2081 i += 8;
2082 }
2083#elif defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || \
2084 (defined(ZXC_USE_SSE2) && defined(__SSSE3__))
2085#if defined(ZXC_USE_AVX512)
2086 /* The control word is already the blend mask; the masked store takes the
2087 * tail, hence nothing narrower below. */
2088 {
2089 const __m512i z0 = _mm512_set1_epi8((char)sym0);
2090 const __m512i z1 = _mm512_set1_epi8((char)sym1);
2091 while (i < n) {
2092 const size_t left = n - i;
2093 const size_t cb = (left + 7) >> 3;
2094 uint64_t ctrl = 0;
2095 ZXC_MEMCPY(&ctrl, bits + (i >> 3), cb < 8 ? cb : 8); /* not past the run */
2096 const __mmask64 sm = (left >= 64) ? ~(__mmask64)0 : (((__mmask64)1 << left) - 1);
2097 _mm512_mask_storeu_epi8((void*)(out + i), sm,
2098 _mm512_mask_blend_epi8((__mmask64)ctrl, z0, z1));
2099 i += 64;
2100 }
2101 if (i > n) i = n;
2102 }
2103#else
2104#if defined(ZXC_USE_AVX2)
2105 /* Broadcasting the control dword lets each 128-bit half pick its own two
2106 * bytes out of it. */
2107 {
2108 const __m256i y0 = _mm256_set1_epi8((char)sym0);
2109 const __m256i ydelta = _mm256_set1_epi8((char)(uint8_t)(sym0 ^ sym1));
2110 const __m256i yrep = _mm256_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2,
2111 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
2112 const __m256i ysel =
2113 _mm256_setr_epi8(1, 2, 4, 8, 16, 32, 64, (char)128, 1, 2, 4, 8, 16, 32, 64, (char)128,
2114 1, 2, 4, 8, 16, 32, 64, (char)128, 1, 2, 4, 8, 16, 32, 64, (char)128);
2115 while (i + 32 <= n) {
2116 uint32_t c4;
2117 ZXC_MEMCPY(&c4, bits + (i >> 3), 4);
2118 const __m256i rep = _mm256_shuffle_epi8(_mm256_set1_epi32((int)c4), yrep);
2119 const __m256i mask = _mm256_cmpeq_epi8(_mm256_and_si256(rep, ysel), ysel);
2120 _mm256_storeu_si256((__m256i*)(void*)(out + i),
2121 _mm256_xor_si256(y0, _mm256_and_si256(ydelta, mask)));
2122 i += 32;
2123 }
2124 }
2125#endif
2126 const __m128i vsym0 = _mm_set1_epi8((char)sym0);
2127 const __m128i vdelta = _mm_set1_epi8((char)(uint8_t)(sym0 ^ sym1));
2128 const __m128i vrep = _mm_setr_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
2129 const __m128i vsel =
2130 _mm_setr_epi8(1, 2, 4, 8, 16, 32, 64, (char)128, 1, 2, 4, 8, 16, 32, 64, (char)128);
2131 while (i + 16 <= n) {
2132 const __m128i ctrl = _mm_cvtsi32_si128(bits[i >> 3] | ((int)bits[(i >> 3) + 1] << 8));
2133 const __m128i rep = _mm_shuffle_epi8(ctrl, vrep);
2134 // lanes where the selected bit is set -> 0xFF
2135 const __m128i mask = _mm_cmpeq_epi8(_mm_and_si128(rep, vsel), vsel);
2136 _mm_storeu_si128((__m128i*)(void*)(out + i),
2137 _mm_xor_si128(vsym0, _mm_and_si128(vdelta, mask)));
2138 i += 16;
2139 }
2140#endif /* AVX-512, else AVX2 + SSE */
2141#endif
2142 while (i < n) {
2143 const int bit = (bits[i >> 3] >> (i & 7)) & 1;
2144 out[i++] = bit ? sym1 : sym0;
2145 }
2146}
2147
2158static int zxc_pivco_decode_core(const uint8_t* RESTRICT payload, const size_t payload_size,
2159 uint8_t* RESTRICT dst, const size_t n,
2160 const zxc_pivco_tree_t* RESTRICT t, uint8_t* RESTRICT scratch) {
2161 if (UNLIKELY(n == 0)) return ZXC_ERROR_CORRUPT_DATA;
2162
2163 // Pass 1, in wire order: node counts from the partition bits, and where
2164 // each depth's runs start. Leaf-pair children (emitted by their parent) and
2165 // a flat root's inside get count 0: empty runs, nothing to flag.
2166 uint32_t count[ZXC_PIVCO_NODE_SLOTS];
2167 const uint8_t* lvl_run[ZXC_HUF_MAX_CODE_LEN_ULTRA];
2168 const int slot = t->node_base[t->max_depth + 1];
2169 count[0] = (uint32_t)n;
2170 count[slot] = 0;
2171 const uint8_t* p = payload;
2172 const uint8_t* const pend = payload + payload_size;
2173 for (int d = 0; d < t->max_depth; d++) {
2174 lvl_run[d] = p;
2175 const int L = zxc_pivco_leaves(t, d);
2176 const int N = t->node_base[d + 1] - t->node_base[d];
2177 const int Lc = zxc_pivco_leaves(t, d + 1);
2178 const uint32_t* const cnt = count + t->node_base[d];
2179 uint32_t* const ccnt = count + t->node_base[d + 1];
2180 const uint8_t* const fd = t->flat_d + t->node_base[d];
2181 for (int i = L; i < N; i++) {
2182 const uint32_t c = cnt[i];
2183 const int k2 = 2 * (i - L); /* first child */
2184 if (fd[i]) {
2185 // Packed-code run: no partition below, nothing to popcount.
2186 const size_t fbytes = zxc_pivco_run_bytes(c, fd[i]);
2187 if (UNLIKELY((size_t)(pend - p) < fbytes)) return ZXC_ERROR_CORRUPT_DATA;
2188 p += fbytes;
2189 ccnt[k2] = 0;
2190 ccnt[k2 + 1] = 0;
2191 continue;
2192 }
2193 const size_t nbytes = zxc_pivco_run_bytes(c, 0); /* merge node: c partition bits */
2194 if (UNLIKELY((size_t)(pend - p) < nbytes)) return ZXC_ERROR_CORRUPT_DATA;
2195 // popcount of the c valid bits = the right child's count. All but the
2196 // last byte counted fast, the last one with its padding masked - if the
2197 // 8-byte loop swallowed it unmasked, padding bits would inflate `ones`.
2198 uint32_t ones = 0;
2199 if (nbytes) {
2200 const size_t full = nbytes - 1;
2201 size_t k = 0;
2202 for (; k + 8 <= full; k += 8) {
2203 uint64_t w;
2204 ZXC_MEMCPY(&w, p + k, 8);
2205 ones += (uint32_t)zxc_pivco_popcnt64(w);
2206 }
2207 for (; k < full; k++) ones += (uint32_t)zxc_pivco_popcnt32(p[k]);
2208 uint8_t last = p[full];
2209 if (c & 7U) last &= (uint8_t)((1U << (c & 7U)) - 1U);
2210 ones += (uint32_t)zxc_pivco_popcnt32(last);
2211 }
2212 p += nbytes;
2213 if (UNLIKELY(ones > c)) return ZXC_ERROR_CORRUPT_DATA;
2214 const int leaf_pair = k2 + 1 < Lc;
2215 ccnt[k2] = leaf_pair ? 0 : c - ones;
2216 ccnt[k2 + 1] = leaf_pair ? 0 : ones;
2217 }
2218 }
2219 // The slot stands for the lone-symbol root's missing child: no 1 bits.
2220 if (UNLIKELY(count[slot] != 0)) return ZXC_ERROR_CORRUPT_DATA;
2221
2222 // Pass 2, bottom-up. Runs and children follow position order, so two
2223 // running offsets replace per-node tables.
2224 for (int d = t->max_depth; d >= 0; d--) {
2225 uint8_t* const buf_d = (d & 1) ? scratch : dst;
2226 const uint8_t* const buf_c = (d & 1) ? dst : scratch; /* children at d+1 (read-only) */
2227 const int L = zxc_pivco_leaves(t, d);
2228 const int N = t->node_base[d + 1] - t->node_base[d];
2229 const uint32_t* const cnt = count + t->node_base[d];
2230 const uint8_t* const sym = t->syms + t->leaf_base[d];
2231 size_t off = 0;
2232 for (int i = 0; i < L; i++) {
2233 if (cnt[i]) ZXC_MEMSET(buf_d + off, sym[i], cnt[i]);
2234 off += cnt[i];
2235 }
2236 if (d == t->max_depth) continue; /* leaves only, as in pass 1 */
2237 const uint8_t* run = lvl_run[d];
2238 const uint32_t* const ccnt = count + t->node_base[d + 1];
2239 const uint8_t* const csym = t->syms + t->leaf_base[d + 1];
2240 const int Lc = zxc_pivco_leaves(t, d + 1);
2241 const uint8_t* const fd = t->flat_d + t->node_base[d];
2242 size_t coff = 0;
2243 for (int i = L; i < N; i++) {
2244 const uint32_t c = cnt[i];
2245 const int k2 = 2 * (i - L);
2246 if (c) {
2247 if (fd[i]) {
2248 const int D = fd[i];
2249 const int leaves = zxc_pivco_flat_leaves(t, t->node_base[d] + i, d, D);
2250 zxc_pivco_unpack_flat(buf_d + off, c, D, run, t->syms + leaves);
2251 } else if (k2 + 1 < Lc) {
2252 zxc_pivco_emit_leaf_pair(buf_d + off, c, csym[k2], csym[k2 + 1], run);
2253 } else {
2254 const uint32_t nl = ccnt[k2];
2255 zxc_pivco_merge(buf_d + off, buf_c + coff, nl, c - nl, run);
2256 }
2257 }
2258 run += zxc_pivco_run_bytes(c, fd[i]);
2259 off += c;
2260 coff += ccnt[k2] + ccnt[k2 + 1];
2261 }
2262 }
2263 return ZXC_OK;
2264}
2265
2281int zxc_huf_decode_section(const uint8_t* RESTRICT payload, const size_t payload_size,
2282 uint8_t* RESTRICT dst, const size_t n, uint8_t* RESTRICT scratch) {
2283 if (UNLIKELY(payload_size < (size_t)ZXC_HUF_TABLE_SIZE)) return ZXC_ERROR_CORRUPT_DATA;
2284 uint8_t code_len[ZXC_HUF_NUM_SYMBOLS];
2285 const int rc = zxc_huf_unpack_lengths(payload, code_len);
2286 if (UNLIKELY(rc != ZXC_OK)) return rc;
2288 if (UNLIKELY(zxc_pivco_tree_build(code_len, &t) != 0)) return ZXC_ERROR_CORRUPT_DATA;
2289 return zxc_pivco_decode_core(payload + ZXC_HUF_TABLE_SIZE, payload_size - ZXC_HUF_TABLE_SIZE,
2290 dst, n, &t, scratch);
2291}
2292
2300int zxc_huf_decode_section_dict(const uint8_t* RESTRICT payload, const size_t payload_size,
2301 uint8_t* RESTRICT dst, const size_t n,
2302 const zxc_pivco_tree_t* RESTRICT tree, uint8_t* RESTRICT scratch) {
2303 return zxc_pivco_decode_core(payload, payload_size, dst, n, tree, scratch);
2304}
#define ZXC_HUF_TABLE_SIZE
Size of a packed literal Huffman code-lengths table (256 symbols, 4 bits each): the shared table in a...
@ ZXC_ERROR_CORRUPT_DATA
Corrupted compressed data.
Definition zxc_error.h:56
@ ZXC_ERROR_MEMORY
Memory allocation failure.
Definition zxc_error.h:41
@ ZXC_ERROR_DST_TOO_SMALL
Destination buffer too small.
Definition zxc_error.h:44
@ ZXC_OK
Success (no error).
Definition zxc_error.h:38
#define ZXC_MALLOC(size)
Heap allocator.
Definition zxc_deps.h:68
#define ZXC_ALWAYS_INLINE
Forces a function to be inlined (fallback for non-GCC/Clang/MSVC compilers).
#define ZXC_HUF_NUDGE_LEVEL_COST
Fixed per-pass overhead (occurrence-equivalents) charged per merge level, modeling the pass-loop and ...
#define ZXC_HUF_NUDGE_DEEP_FLAT_PENALTY
Extra level-touches charged per occurrence under a flat root deeper than ZXC_PIVCO_UNPACK_FLAT_SIMD_M...
#define ZXC_HUF_NUDGE_MERGE_Q8
Adoption guard, speed side (Q8): adopt only while ‘touches’ * 256 <= touches0 * ZXC_HUF_NUDGE_MERGE_Q...
#define ZXC_HUF_MAX_CODE_LEN_ULTRA
Maximum Huffman code length, in bits: the ceiling the decoder's on-wire validation accepts (and the l...
#define ZXC_PIVCO_MAX_NODES
Upper bound on PivCo tree nodes (full binary tree over the alphabet).
#define ZXC_CALLOC(nmemb, size)
Zero-initialised heap allocator.
Definition zxc_deps.h:74
#define ZXC_HUF_NUDGE_BITS_PERMIL
Adoption guard, ratio side (permil): adopt only while ‘bits’ * 1000 <= bits0 * ZXC_HUF_NUDGE_BITS_PER...
#define ZXC_FREE(ptr)
Heap deallocator.
Definition zxc_deps.h:86
#define ZXC_PIVCO_UNPACK_FLAT_SIMD_MAX
Deepest flat subtree zxc_pivco_unpack_flat unpacks with a SIMD kernel (its D == 2....
#define ZXC_PIVCO_COVERED
flat_d of a node inside a flat root: no run, no children.
#define ZXC_PIVCO_NODE_SLOTS
Node-indexed array size: every node, plus a slot for the lone-symbol root's missing child.
#define ZXC_HUF_NUDGE_LAMBDA_Q8
Exchange rate (Q8 bits per modeled level-touch) in the candidate cost J = 256*bits + lambda*touches; ...
#define ZXC_HUF_NUM_SYMBOLS
Alphabet size: one entry per possible byte value.
Workspace of zxc_huf_nudge_code_lengths: tables and DP planes, once a 10 KiB frame and a per-block al...
int16_t sym_order[256]
Symbols by descending frequency.
uint64_t pfg[64+1]
Group-mass prefix sums.
uint64_t jcur[(64+1) *(64+1)]
DP plane.
uint64_t pf_rank[256+1]
Frequency-rank prefix sums.
uint64_t pf[256+1]
Canonical-order prefix sums.
uint8_t arrive[(11+1) *(64+1) *(64+1)]
Per-level arrival choice, c <= ZXC_HUF_NUDGE_DP_M.
zxc_huf_pm_leaf_t leaves[256]
Live symbols, sorted.
uint64_t jnxt[(64+1) *(64+1)]
Next plane.
uint8_t cand[4][256]
Candidate code lengths.
Trace-back stack frame for the package-merge code-length recovery.
int16_t idx
Item index within that level.
int8_t lvl
Package-merge level being traced back.
Boundary package-merge work item.
int16_t right
Right child index, or -1 for a leaf.
int16_t sym
Symbol index for a leaf, or -1 for an internal node.
int16_t left
Left child index, or -1 for a leaf.
A live symbol and its weight, the package-merge sort key.
uint32_t w
Frequency.
int16_t sym
Byte value.
A PivCo code tree: its canonical shape, by depth.
static void zxc_pivco_merge(uint8_t *out, const uint8_t *src, const size_t nl, const size_t nr, const uint8_t *bits)
Merge two adjacent child sequences under control bits.
static size_t zxc_pivco_layout(const zxc_pivco_tree_t *t, const uint32_t *count, uint32_t *cur)
Payload size, and each run's first bit as a cursor if cur is set.
static void zxc_huf_nudge_pf_canonical(const uint8_t *code_len, const uint32_t *freq, const uint32_t *blc, uint64_t *pf)
Frequency prefix sums over the leaves in canonical (length, symbol) order: pf[i] = mass of the first ...
static int zxc_huf_nudge_classes(const uint8_t *code_len, uint32_t *blc)
Per-length class counts of a code-length vector.
static void zxc_huf_nudge_eval(const uint32_t *blc, const uint64_t *pf, zxc_huf_nudge_cost_t *out)
Modeled (bits, level-touches) of one class-count histogram.
static void zxc_huf_nudge_walk(const uint32_t *blc0, const uint64_t *pf_rank, const int n, const int cap, uint32_t *out_blc)
Greedy shallow-to-deep walk over the slot ledger.
static int zxc_pivco_encode_core(const uint8_t *literals, const size_t n_literals, const uint32_t *freq, const uint8_t *code_len, const zxc_pivco_tree_t *t, const uint32_t *codes, uint8_t *dst, const size_t dst_cap, const int with_header)
Shared PivCo section encoder body.
static void zxc_huf_nudge_assign(const uint32_t *blc, const int16_t *sym_order, uint8_t *cand_len)
Assign a class-count multiset to symbols: frequency rank r takes the r-th shortest slot (rearrangemen...
size_t zxc_huf_calc_size(const uint32_t *freq, const uint8_t *code_len, const int with_header)
Exact encoded size (bytes) of a PivCo section for this histogram and code lengths; includes the 128-b...
static int zxc_huf_nudge_dp_solve(zxc_huf_nudge_ws_t *ws, const uint64_t *pfg, const int m, const int cap_c, const int lu, const int g_log2, uint32_t *out_cblc)
Slot-ledger dynamic program: optimal class counts for the model.
static int zxc_pivco_leaves(const zxc_pivco_tree_t *t, const int d)
Leaves at depth d.
static uint64_t zxc_huf_nudge_j(const zxc_huf_nudge_cost_t *c)
Candidate cost J = 256*bits + lambda*touches (Q8 bit units).
static void zxc_pivco_emit_leaf_pair(uint8_t *out, const size_t n, const uint8_t sym0, const uint8_t sym1, const uint8_t *bits)
Direct emission for a node whose two children are BOTH leaves.
static void zxc_pivco_bitrev_permute(uint8_t *s, const int D)
Reorders s[0, 2^D) by bit-reversed index (an involution).
static uint64_t zxc_huf_nudge_dp_run_j(const int lu, const int lc, const int g_log2, const uint32_t s, const uint32_t c, const uint64_t *pfg, const uint32_t k)
Exact rank-weighted J of one leaf run placed at coarse level lc.
int zxc_huf_decode_section_dict(const uint8_t *payload, const size_t payload_size, uint8_t *dst, const size_t n, const zxc_pivco_tree_t *tree, uint8_t *scratch)
Decode a shared-dictionary literal section (no inline header).
int zxc_huf_nudge_code_lengths(const uint32_t *freq, uint8_t *code_len, void *scratch, const size_t scratch_cap, const int max_code_len)
Optionally reshape freshly built code lengths for faster PivCo decode.
int zxc_huf_build_code_lengths(const uint32_t *freq, uint8_t *code_len, void *scratch, const int max_code_len)
Build length-limited canonical Huffman code lengths.
static void pm_leaves_sort(pm_leaf_t *leaves, const int n)
Sort pm_leaf_t array by ascending weight, ties broken by ascending symbol.
size_t zxc_huf_calc_size_dict(const uint32_t *freq, const uint8_t *code_len, const zxc_pivco_tree_t *tree)
Sizing core shared with zxc_huf_calc_size: exact payload size of one PivCo section for a prebuilt tre...
void zxc_huf_nudge_cost(const uint8_t *code_len, const uint32_t *freq, uint64_t *bits, uint64_t *touches)
Modeled (bits, level-touches) decode cost of one code-length vector.
static int zxc_pivco_tree_build(const uint8_t *code_len, zxc_pivco_tree_t *t)
Build the canonical code tree from per-symbol lengths.
int zxc_huf_encode_section_dict(const uint8_t *literals, const size_t n_literals, const uint32_t *freq, const uint8_t *code_len, const zxc_pivco_tree_t *tree, const uint32_t *codes, uint8_t *dst, const size_t dst_cap)
Encode a literal section against a shared dictionary table.
static void zxc_pivco_unpack_flat(uint8_t *out, const size_t n, const int D, const uint8_t *bits, const uint8_t *c2s)
Decode a flat subtree's packed D-bit code run into symbols.
static void zxc_pivco_counts(const zxc_pivco_tree_t *t, const uint32_t *freq, uint32_t *count)
Per-node symbol counts from the histogram (encoder / size estimate).
static uint32_t zxc_huf_nudge_clamp(const uint32_t want, const uint32_t s, const uint32_t n_rem, const int l, const int cap)
Clamp a desired class count into the feasible set at one walk level.
#define ZXC_PIVCO_MERGE_STEP(w, pc)
Advances the merge cursors past one fixed-width step.
static int zxc_pivco_flat_leaves(const zxc_pivco_tree_t *t, const int g, const int d, const int D)
syms index of flat root g's first leaf: its prefix, then D zero bits.
int zxc_huf_decode_section(const uint8_t *payload, const size_t payload_size, uint8_t *dst, const size_t n, uint8_t *scratch)
Decode a literal/token section (PivCo layout, inline lengths header).
static void zxc_huf_nudge_complete(uint32_t *blc, const int l, uint32_t s, uint32_t n_rem, const uint32_t *blc0, const int cap)
Baseline-following completion: fill levels l+1..cap with the baseline class counts clamped into feasi...
static size_t zxc_pivco_run_bytes(const uint32_t count, const uint8_t flat_d)
Byte size of one PivCo node's on-wire bit run.
void zxc_huf_pack_lengths(const uint8_t *code_len, uint8_t *out)
Pack per-symbol code lengths into the 128-byte (4-bit nibble) header.
int zxc_huf_dict_tree_build(const uint8_t *packed_lengths, zxc_pivco_tree_t *tree, uint32_t *codes, uint8_t *code_len)
Prebuild a dict table's decode/encode state (tree-at-attach).
int zxc_huf_encode_section(const uint8_t *literals, const size_t n_literals, const uint32_t *freq, const uint8_t *code_len, uint8_t *dst, const size_t dst_cap)
Encode a literal/token section (PivCo layout, inline lengths header).
int zxc_huf_unpack_lengths(const uint8_t *in, uint8_t *code_len)
Unpack and structurally validate a 128-byte packed lengths header.
static int zxc_pivco_decode_core(const uint8_t *payload, const size_t payload_size, uint8_t *dst, const size_t n, const zxc_pivco_tree_t *t, uint8_t *scratch)
Shared PivCo section decoder body (code lengths already unpacked).
static void zxc_pivco_codes(const zxc_pivco_tree_t *t, uint32_t *codes)
Code of every leaf: the prefix of its slot (see zxc_pivco_tree_t).
Internal definitions, constants, SIMD helpers, and utility functions.
static uint64_t zxc_le64(const void *p)
Reads a little-endian 64-bit value from a possibly unaligned address.
#define ZXC_HUF_NUDGE_DP_M
Slot-ledger DP bound, coarse symbols per plane axis; the nudge groups symbols to stay under it.
static uint32_t zxc_le32(const void *p)
Reads a little-endian 32-bit value from a possibly unaligned address.
#define ZXC_HUF_NUDGE_SCRATCH_OFF
Offset of the zxc_huf_nudge_ws_t inside a nudge scratch.
#define ZXC_HUF_NUDGE_SCRATCH_SIZE
Scratch size (bytes) for zxc_huf_nudge_code_lengths: builder's, then workspace.
static uint32_t zxc_log2_u32(const uint32_t v)
Integer log-base-2 for a 32-bit value.
#define ZXC_HUF_PM_LEVEL_BOUND
Per-level item bound: at most leaves + paired packages from the previous level.
Shuffle-index tables for the PivCo merge routines (declarations).