ZXC 0.15.0
High-performance asymmetric lossless compression library
Loading...
Searching...
No Matches
zxc_compress.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
18// Function Multi-Versioning Support
19// With ZXC_FUNCTION_SUFFIX defined (e.g. _avx2), rename the entry point AND the
20// Huffman entry points this TU consumes. The defines precede zxc_internal.h so
21// the header's prototypes get the suffix too, keeping callers and callees matched.
22#ifdef ZXC_FUNCTION_SUFFIX
23#define ZXC_CAT_IMPL(x, y) x##y
24#define ZXC_CAT(x, y) ZXC_CAT_IMPL(x, y)
25#define zxc_compress_chunk_wrapper ZXC_CAT(zxc_compress_chunk_wrapper, ZXC_FUNCTION_SUFFIX)
26#define zxc_huf_build_code_lengths ZXC_CAT(zxc_huf_build_code_lengths, ZXC_FUNCTION_SUFFIX)
27#define zxc_huf_unpack_lengths ZXC_CAT(zxc_huf_unpack_lengths, ZXC_FUNCTION_SUFFIX)
28#define zxc_huf_calc_size ZXC_CAT(zxc_huf_calc_size, ZXC_FUNCTION_SUFFIX)
29#define zxc_huf_calc_size_dict ZXC_CAT(zxc_huf_calc_size_dict, ZXC_FUNCTION_SUFFIX)
30#define zxc_huf_encode_section ZXC_CAT(zxc_huf_encode_section, ZXC_FUNCTION_SUFFIX)
31#define zxc_huf_encode_section_dict ZXC_CAT(zxc_huf_encode_section_dict, ZXC_FUNCTION_SUFFIX)
32#endif
33
34#include "../../include/zxc_error.h"
35#include "zxc_internal.h"
36
45static ZXC_ALWAYS_INLINE uint32_t zxc_hash_func(const uint64_t val, const int use_hash5) {
46 if (use_hash5) {
47 const uint64_t v5 = val & 0xFFFFFFFFFFULL;
48 return (uint32_t)((v5 * ZXC_HASH_XORSHIFT64) >> (64 - ZXC_LZ_HASH_BITS));
49 } else {
50 const uint64_t v4 = val ^ (val >> 15);
51 return ((uint32_t)v4 * ZXC_HASH_MULT32) >> (32 - ZXC_LZ_HASH_BITS);
52 }
53}
54
61static ZXC_ALWAYS_INLINE uint8_t zxc_hash_tag(const uint32_t val4) {
62 return (uint8_t)(val4 ^ (val4 >> 16));
63}
64
71static ZXC_ALWAYS_INLINE uint32_t zxc_epoch_pos(const uint32_t head, const uint32_t offset_mask,
72 const uint32_t epoch_mark) {
73 return ((head & ~offset_mask) == epoch_mark) ? (head & offset_mask) : 0;
74}
75
82static ZXC_ALWAYS_INLINE uint16_t zxc_chain_delta(const uint32_t cur_pos, const uint32_t prev_idx) {
83 const uint32_t dist = cur_pos - prev_idx;
84 const uint32_t valid = 0U - (uint32_t)((prev_idx != 0) & (dist < ZXC_LZ_WINDOW_SIZE));
85 return (uint16_t)(dist & valid);
86}
87
97#if defined(ZXC_USE_AVX512)
98#define ZXC_RLE_WIN 64
99#define ZXC_RLE_BPB 1
100#define ZXC_RLE_CTZ(x) zxc_ctz64(x)
101typedef uint64_t zxc_rle_mask_t;
102static ZXC_ALWAYS_INLINE zxc_rle_mask_t zxc_rle_pair_mask(const uint8_t* p) {
103 return (uint64_t)_mm512_cmpeq_epi8_mask(_mm512_loadu_si512((const void*)p),
104 _mm512_loadu_si512((const void*)(p + 1)));
105}
106#elif defined(ZXC_USE_AVX2)
107#define ZXC_RLE_WIN 32
108#define ZXC_RLE_BPB 1
109#define ZXC_RLE_CTZ(x) zxc_ctz32(x)
110typedef uint32_t zxc_rle_mask_t;
111static ZXC_ALWAYS_INLINE zxc_rle_mask_t zxc_rle_pair_mask(const uint8_t* p) {
112 const __m256i v0 = _mm256_loadu_si256((const __m256i*)p);
113 const __m256i v1 = _mm256_loadu_si256((const __m256i*)(p + 1));
114 return (uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v0, v1));
115}
116#elif defined(ZXC_USE_SSE2)
117#define ZXC_RLE_WIN 16
118#define ZXC_RLE_BPB 1
119#define ZXC_RLE_CTZ(x) zxc_ctz32(x)
120typedef uint32_t zxc_rle_mask_t;
121static ZXC_ALWAYS_INLINE zxc_rle_mask_t zxc_rle_pair_mask(const uint8_t* p) {
122 const __m128i v0 = _mm_loadu_si128((const __m128i*)p);
123 const __m128i v1 = _mm_loadu_si128((const __m128i*)(p + 1));
124 return (uint32_t)_mm_movemask_epi8(_mm_cmpeq_epi8(v0, v1));
125}
126#elif defined(ZXC_USE_NEON64)
127#define ZXC_RLE_WIN 16
128#define ZXC_RLE_BPB 4
129#define ZXC_RLE_CTZ(x) zxc_ctz64(x)
130typedef uint64_t zxc_rle_mask_t;
131static ZXC_ALWAYS_INLINE zxc_rle_mask_t zxc_rle_pair_mask(const uint8_t* p) {
132 const uint8x16_t eq = vceqq_u8(vld1q_u8(p), vld1q_u8(p + 1));
133 return vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(eq), 4)), 0);
134}
135#endif
136
137// SSE2 selected, not merely available: guards the two helpers below.
138#if defined(ZXC_USE_SSE2) && !(defined(ZXC_USE_AVX512) && defined(__AVX512VL__)) && \
139 !defined(ZXC_USE_AVX2) && !defined(ZXC_USE_NEON64) && !defined(ZXC_USE_NEON32)
140#define ZXC_OPT_SSE2
141#endif
142
143#if defined(ZXC_OPT_SSE2)
156static ZXC_ALWAYS_INLINE __m128i zxc_mm_blendv_epi8_sse2(__m128i a, __m128i b, __m128i mask) {
157 return _mm_or_si128(_mm_and_si128(mask, b), _mm_andnot_si128(mask, a));
158}
159
172static ZXC_ALWAYS_INLINE __m128i zxc_mm_packus_epi32_sse2(__m128i a, __m128i b) {
173 const __m128i bias32 = _mm_set1_epi32(0x8000);
174 const __m128i bias16 = _mm_set1_epi16((short)0x8000);
175 const __m128i pa = _mm_sub_epi32(a, bias32);
176 const __m128i pb = _mm_sub_epi32(b, bias32);
177 return _mm_add_epi16(_mm_packs_epi32(pa, pb), bias16);
178}
179#endif
180
197static ZXC_ALWAYS_INLINE size_t zxc_write_varint(uint8_t* RESTRICT dst, const uint32_t val) {
198 // Values above ZXC_MAX_VARINT_VALUE are out-of-spec and the decoder would
199 // reject them. Unreachable from the Block API, so this is defence in depth;
200 // callers must treat a return of 0 as an encoding error.
201 if (UNLIKELY(val > ZXC_MAX_VARINT_VALUE)) {
202 return 0;
203 }
204
205 // 1 byte: 0xxxxxxx (7 bits) = 2^7 = 128
206 if (LIKELY(val < (1U << 7))) {
207 dst[0] = (uint8_t)val;
208 return 1;
209 }
210
211 // 2 bytes: 10xxxxxx xxxxxxxx (14 bits) = 2^14 = 16384
212 if (LIKELY(val < (1U << 14))) {
213 dst[0] = (uint8_t)(0x80 | (val & 0x3F));
214 dst[1] = (uint8_t)(val >> 6);
215 return 2;
216 }
217
218 // 3 bytes: 110xxxxx xxxxxxxx xxxxxxxx (21 bits) -> max emittable value,
219 // matching ZXC_MAX_VARINT_VALUE = ZXC_BLOCK_SIZE_MAX - 1.
220 dst[0] = (uint8_t)(0xC0 | (val & 0x1F));
221 dst[1] = (uint8_t)(val >> 5);
222 dst[2] = (uint8_t)(val >> 13);
223 return 3;
224}
225
232static ZXC_ALWAYS_INLINE int zxc_emit_extra(uint8_t* RESTRICT extras, size_t* RESTRICT used,
233 const uint32_t val, const uint32_t mask) {
234 if (LIKELY(val < mask)) return 1;
235 const size_t n = zxc_write_varint(extras + *used, val - mask);
236 if (UNLIKELY(n == 0)) return 0;
237 *used += n;
238 return 1;
239}
240
257static ZXC_ALWAYS_INLINE int zxc_glo_put_seq(uint8_t* RESTRICT buf_tokens,
258 uint16_t* RESTRICT buf_offsets,
259 uint8_t* RESTRICT buf_extras, uint32_t* RESTRICT seq_c,
260 size_t* RESTRICT extras_sz,
261 uint16_t* RESTRICT max_offset, const uint32_t ll,
262 const uint32_t ml, const uint16_t off_biased) {
263 const uint8_t ll_code = (ll >= ZXC_TOKEN_LL_MASK) ? ZXC_TOKEN_LL_MASK : (uint8_t)ll;
264 const uint8_t ml_code = (ml >= ZXC_TOKEN_ML_MASK) ? ZXC_TOKEN_ML_MASK : (uint8_t)ml;
265 buf_tokens[*seq_c] = (uint8_t)((ll_code << ZXC_TOKEN_LIT_BITS) | ml_code);
266 buf_offsets[*seq_c] = off_biased;
267 if (off_biased > *max_offset) *max_offset = off_biased;
268 if (UNLIKELY(!zxc_emit_extra(buf_extras, extras_sz, ll, ZXC_TOKEN_LL_MASK) ||
269 !zxc_emit_extra(buf_extras, extras_sz, ml, ZXC_TOKEN_ML_MASK)))
270 return 0;
271 (*seq_c)++;
272 return 1;
273}
274
276static ZXC_ALWAYS_INLINE void zxc_extras_move(uint8_t* extras, size_t* RESTRICT w,
277 size_t* RESTRICT r, const uint8_t* to) {
278 while (extras + *r < to) extras[(*w)++] = extras[(*r)++];
279}
280
282static ZXC_ALWAYS_INLINE void zxc_varint_keep(uint8_t* extras, size_t* RESTRICT w,
283 size_t* RESTRICT r, const uint8_t* end) {
284 const uint8_t* p = extras + *r;
285 (void)zxc_read_varint(&p, end);
286 zxc_extras_move(extras, w, r, p);
287}
288
290static ZXC_ALWAYS_INLINE uint32_t zxc_glo_split_pieces(const uint32_t code) {
292}
293
296static ZXC_ALWAYS_INLINE uint32_t zxc_glo_split_piece(const uint32_t code) {
297 const uint32_t rest = code - ZXC_LZ_MIN_MATCH_LEN;
298 return (rest < ZXC_GLO_INLINE_ML_CODE) ? rest : ZXC_GLO_INLINE_ML_CODE;
299}
300
313static int zxc_glo_split_analyze(const uint8_t* RESTRICT tokens, const uint32_t n_seq,
314 zxc_glo_split_hist_t* RESTRICT hist_buf, uint8_t* RESTRICT ctx_bad,
315 uint8_t* RESTRICT side) {
316 uint32_t order = ZXC_GLO_SPLIT_CTX_BITS;
317 while (order > 2 && (n_seq >> order) < 16) order--;
318 const uint32_t hmask = (1U << order) - 1U;
319 uint32_t (*const ctx_hist)[2][1U << ZXC_GLO_SPLIT_CTX_BITS] = *hist_buf;
320 for (uint32_t l = 0; l < 4; l++) {
321 ZXC_MEMSET(ctx_hist[l][0], 0, (hmask + 1) * sizeof(uint32_t));
322 ZXC_MEMSET(ctx_hist[l][1], 0, (hmask + 1) * sizeof(uint32_t));
323 }
324 uint32_t hist = 0, n_esc = 0;
325 for (uint32_t i = 0; i < n_seq; i++) {
326 const uint32_t esc = (tokens[i] & ZXC_TOKEN_ML_MASK) == ZXC_TOKEN_ML_MASK;
327 side[i] = (uint8_t)hist;
328 ctx_hist[i & 3][esc][hist]++;
329 hist = ((hist << 1) | esc) & hmask;
330 n_esc += esc;
331 }
332 uint32_t errors = 0, bad_esc = 0;
333 for (uint32_t c = 0; c <= hmask; c++) {
334 uint32_t n0 = 0, n1 = 0;
335 for (uint32_t l = 0; l < 4; l++) {
336 n0 += ctx_hist[l][0][c];
337 n1 += ctx_hist[l][1][c];
338 }
339 ctx_bad[c] = (n1 < n0); // escape a strict minority in its context
340 bad_esc += ctx_bad[c] ? n1 : 0;
341 errors += (n0 < n1) ? n0 : n1;
342 }
343 return bad_esc != 0 &&
344 (uint64_t)errors * 100 >= (uint64_t)ZXC_GLO_SPLIT_MIN_MISPREDICT_PCT * n_esc;
345}
346
374static uint32_t zxc_glo_split_block(uint8_t* RESTRICT tokens, uint16_t* RESTRICT offsets,
375 uint8_t* RESTRICT extras, size_t* RESTRICT extras_sz,
376 uint8_t* RESTRICT side, zxc_glo_split_hist_t* RESTRICT hist_buf,
377 const uint32_t n_seq, const uint8_t cap) {
378 if (cap <= ZXC_GLO_INLINE_ML_CODE || n_seq == 0) return n_seq;
379 uint8_t ctx_bad[1U << ZXC_GLO_SPLIT_CTX_BITS];
380 if (!zxc_glo_split_analyze(tokens, n_seq, hist_buf, ctx_bad, side)) return n_seq;
381
382 // 1. Mark the matches to split (bad context, within the cap) and drop their
383 // varints; the other extras move forward.
384 const uint8_t* const extras_end = extras + *extras_sz;
385 size_t r = 0, w = 0;
386 uint32_t extra = 0;
387 for (uint32_t i = 0; i < n_seq; i++) {
388 const uint8_t tok = tokens[i];
389 const int bad = ctx_bad[side[i]];
390 side[i] = 0;
392 zxc_varint_keep(extras, &w, &r, extras_end);
393 if ((tok & ZXC_TOKEN_ML_MASK) != ZXC_TOKEN_ML_MASK) continue;
394 const uint8_t* p = extras + r;
395 const uint32_t code = zxc_read_varint(&p, extras_end) + ZXC_TOKEN_ML_MASK;
396 if (bad && code <= cap) {
397 side[i] = (uint8_t)code;
398 extra += zxc_glo_split_pieces(code) - 1;
399 r = (size_t)(p - extras);
400 } else {
401 zxc_extras_move(extras, &w, &r, p);
402 }
403 }
404 *extras_sz = w;
405 if (extra == 0) return n_seq;
406
407 // 2. Expand backward. A marked sequence spans its pieces, written forward: the
408 // first keeps the literal nibble and its escape, the last takes the remainder.
409 uint32_t out = n_seq + extra;
410 for (uint32_t i = n_seq; i-- > 0;) {
411 const uint8_t tok = tokens[i];
412 const uint16_t off = offsets[i];
413 if (side[i] == 0) {
414 out--;
415 tokens[out] = tok;
416 offsets[out] = off;
417 continue;
418 }
419 uint32_t code = side[i];
420 uint8_t ll_bits = tok & (uint8_t)(ZXC_TOKEN_LL_MASK << ZXC_TOKEN_LIT_BITS);
421 out -= zxc_glo_split_pieces(code);
422 uint32_t k = out;
423 while (code > ZXC_GLO_INLINE_ML_CODE) {
424 const uint32_t p = zxc_glo_split_piece(code);
425 tokens[k] = (uint8_t)(ll_bits | p);
426 offsets[k++] = off;
427 ll_bits = 0;
428 code -= p + ZXC_LZ_MIN_MATCH_LEN;
429 }
430 tokens[k] = (uint8_t)(ll_bits | code);
431 offsets[k] = off;
432 }
433 return n_seq + extra;
434}
435
442static ZXC_ALWAYS_INLINE void zxc_flush_literals(uint8_t* literals, size_t* lit_c,
443 const uint8_t* anchor, const uint32_t ll,
444 const uint8_t* iend) {
445 if (LIKELY(anchor + ZXC_PAD_SIZE <= iend)) {
446 zxc_copy32(literals + *lit_c, anchor);
447 if (UNLIKELY(ll > ZXC_PAD_SIZE))
448 ZXC_MEMCPY(literals + *lit_c + ZXC_PAD_SIZE, anchor + ZXC_PAD_SIZE, ll - ZXC_PAD_SIZE);
449 } else {
450 ZXC_MEMCPY(literals + *lit_c, anchor, ll);
451 }
452 *lit_c += ll;
453}
454
465typedef struct {
466 const uint8_t* ref;
467 uint32_t len;
468 uint32_t backtrack;
469} zxc_match_t;
470
484static ZXC_ALWAYS_INLINE int zxc_match_reaches(const uint8_t* cur, const uint8_t* ref,
485 uint32_t from, const uint32_t need,
486 const uint8_t* iend) {
487 while (from < need) {
488 if (UNLIKELY((size_t)(iend - cur) - from < sizeof(uint64_t))) {
489 while (from < need && cur[from] == ref[from]) from++;
490 return from >= need;
491 }
492 const uint64_t d = zxc_le64(cur + from) ^ zxc_le64(ref + from);
493 if (d != 0) return from + (uint32_t)(zxc_ctz64(d) >> 3) >= need;
494 from += sizeof(uint64_t);
495 }
496 return 1;
497}
498
506static ZXC_ALWAYS_INLINE int zxc_lazy_probe(const uint8_t* src, const uint8_t* lp,
507 const uint8_t* iend, const uint32_t need,
508 const uint32_t* RESTRICT hash_table,
509 const uint8_t* RESTRICT hash_tags,
510 const uint16_t* RESTRICT chain_table,
511 const uint32_t epoch_mark, const uint32_t offset_mask,
512 const int use_hash5, const zxc_lz77_params_t p) {
513 if (need > (size_t)(iend - lp)) return 0; // too close to the end to win
514
515 const uint64_t val8 = zxc_le64(lp);
516 const uint32_t val = (uint32_t)val8;
517 const uint32_t h = zxc_hash_func(val8, use_hash5);
518 const uint32_t pos = (uint32_t)(lp - src);
519 const uint32_t gate = zxc_le32(lp + need - 4);
520 uint32_t idx = zxc_epoch_pos(hash_table[h], offset_mask, epoch_mark);
521 // A head with another tag is another string: walk past it.
522 int skip = (hash_tags[h] != zxc_hash_tag(val));
523
524 for (int att = p.lazy_attempts; idx > 0 && att > 0; att--) {
525 if (UNLIKELY(pos - idx > ZXC_LZ_MAX_DIST)) break;
526 const uint8_t* const ref = src + idx;
527
528 // Filtered here too: a lazy hit only cancels best.ref, so a match
529 // we could never emit would drop a legal one for nothing.
530 if (pos - idx >= p.min_offset && !skip && zxc_le32(ref + need - 4) == gate &&
531 zxc_le32(ref) == val && zxc_match_reaches(lp, ref, sizeof(uint32_t), need, iend))
532 return 1;
533
534 const uint16_t delta = chain_table[idx & ZXC_LZ_WINDOW_MASK];
535 if (UNLIKELY(delta == 0)) break;
536 idx -= delta;
537 skip = 0;
538 }
539 return 0;
540}
541
568 const uint8_t* src, const uint8_t* ip, const uint8_t* iend, const uint8_t* search_limit,
569 const uint8_t* anchor, uint32_t* RESTRICT hash_table, uint8_t* RESTRICT hash_tags,
570 uint16_t* RESTRICT chain_table, const uint32_t epoch_mark, const uint32_t offset_mask,
571 const int level, const zxc_lz77_params_t p, const uint32_t last_off) {
572 const int use_hash5 = (level >= 3);
573 // Sentinel length just below the minimum, so any valid match replaces it.
574 zxc_match_t best = (zxc_match_t){NULL, ZXC_LZ_MIN_MATCH_LEN - 1, 0};
575
576 uint64_t cur_val8 = zxc_le64(ip);
577 uint32_t cur_val = (uint32_t)cur_val8;
578 uint32_t h = zxc_hash_func(cur_val8, use_hash5);
579
580 // 8-bit tag: XOR fold of first 4 bytes for fast rejection
581 const uint8_t cur_tag = zxc_hash_tag(cur_val);
582
583 const uint32_t cur_pos = (uint32_t)(ip - src);
584
585 // Tag-first filter on fast levels.
586 const uint8_t stored_tag = hash_tags[h];
587 uint32_t match_idx;
588 if (level <= ZXC_LEVEL_FAST && stored_tag != cur_tag) {
589 match_idx = 0;
590 } else {
591 match_idx = zxc_epoch_pos(hash_table[h], offset_mask, epoch_mark);
592 }
593
594 // skip_head still drives the chain walk on level >= 3 (advances past the
595 // mismatched head without comparing). On level <= 2 it is always 0 here:
596 // either match_idx == 0 (filter-skip) or stored_tag == cur_tag.
597 const int skip_head = (match_idx != 0) & (stored_tag != cur_tag);
598
599 // Split table writes
600 hash_table[h] = epoch_mark | cur_pos;
601 hash_tags[h] = cur_tag;
602
603 chain_table[cur_pos & ZXC_LZ_WINDOW_MASK] = zxc_chain_delta(cur_pos, match_idx);
604
605 int attempts = p.search_depth;
606
607 // Repeat-offset seed (level-6 parser passes last_off; others pass 0).
608 // Probing the previous offset first often finds the longest match right
609 // away, speeding up the chain walk. It only raises best.len, never lowers
610 // it, so the result is unchanged - this is purely a speed optimization.
611 if (last_off != 0U && last_off >= p.min_offset && last_off <= (uint32_t)ZXC_LZ_MAX_DIST &&
612 last_off <= cur_pos) {
613 const uint8_t* const rep_ref = src + (cur_pos - last_off);
614 if (zxc_le32(rep_ref) == cur_val) {
615 uint32_t mlen = sizeof(uint32_t);
616 const uint8_t* const limit_8 = iend - sizeof(uint64_t);
617 while (ip + mlen < limit_8) {
618 const uint64_t diff = zxc_le64(ip + mlen) ^ zxc_le64(rep_ref + mlen);
619 if (diff == 0) {
620 mlen += sizeof(uint64_t);
621 } else {
622 mlen += (uint32_t)(zxc_ctz64(diff) >> 3);
623 goto _rep_done;
624 }
625 }
626 while (ip + mlen < iend && rep_ref[mlen] == ip[mlen]) mlen++;
627 _rep_done:;
628 best.len = mlen;
629 best.ref = rep_ref;
630 if (UNLIKELY(best.len >= (uint32_t)p.sufficient_len || ip + best.len >= iend))
631 goto _finalize_match;
632 }
633 }
634
635 if (match_idx == 0) goto _finalize_match;
636
637 // Optimization: If head tag doesn't match, advance immediately without loading the first
638 // mismatch.
639 if (skip_head) {
640 const uint16_t delta = chain_table[match_idx & ZXC_LZ_WINDOW_MASK];
641 const uint32_t next_idx = match_idx - delta;
642 match_idx = (delta != 0) ? next_idx : 0;
643 attempts--;
644 }
645
646 while (match_idx > 0) {
647 if (UNLIKELY(attempts-- < 0 || cur_pos - match_idx > ZXC_LZ_MAX_DIST)) break;
648 const uint8_t* ref = src + match_idx;
649
650 // Load the next chain link early (before the compare) so its address
651 // resolves while we prefetch. The prefetch aims at the next gate for
652 // the current best.len; a longer match found here moves that gate.
653 const uint16_t delta = chain_table[match_idx & ZXC_LZ_WINDOW_MASK];
654 const uint32_t next_idx = match_idx - delta;
655 ZXC_PREFETCH_READ(src + next_idx + best.len - 3);
656
657 // Gate first: most candidates share the head and fail on bytes
658 // [best.len-3, best.len], which a longer match must also cover.
659 const int should_compare = (cur_pos - match_idx) >= p.min_offset &&
660 zxc_le32(ref + best.len - 3) == zxc_le32(ip + best.len - 3) &&
661 zxc_le32(ref) == cur_val;
662
663 if (should_compare) {
664 uint32_t mlen = sizeof(uint32_t); // We already know the first 4 bytes match
665
666 // Fast path: Scalar 64-bit comparison for short matches (=< 64 bytes)
667 // Most matches are short, so this avoids SIMD overhead for common cases
668 const uint8_t* limit_8 = iend - sizeof(uint64_t);
669 const uint8_t* scalar_limit = ip + mlen + 64;
670 if (scalar_limit > limit_8) scalar_limit = limit_8;
671
672 while (ip + mlen < scalar_limit) {
673 uint64_t diff = zxc_le64(ip + mlen) ^ zxc_le64(ref + mlen);
674 if (diff == 0)
675 mlen += sizeof(uint64_t);
676 else {
677 mlen += (uint32_t)zxc_ctz64(diff) >> 3;
678 goto _match_len_done;
679 }
680 }
681
682 // Long match path: Use SIMD for matches exceeding 64 bytes
683#if defined(ZXC_USE_AVX512)
684 const uint8_t* limit_64 = iend - 64;
685 while (ip + mlen < limit_64) {
686 const __m512i v_src = _mm512_loadu_si512((const void*)(ip + mlen));
687 const __m512i v_ref = _mm512_loadu_si512((const void*)(ref + mlen));
688 const __mmask64 mask = _mm512_cmpeq_epi8_mask(v_src, v_ref);
689 if (mask == 0xFFFFFFFFFFFFFFFF)
690 mlen += 64;
691 else {
692 mlen += (uint32_t)zxc_ctz64(~mask);
693 goto _match_len_done;
694 }
695 }
696#elif defined(ZXC_USE_AVX2)
697 const uint8_t* limit_32 = iend - 32;
698 while (ip + mlen < limit_32) {
699 const __m256i v_src = _mm256_loadu_si256((const __m256i*)(ip + mlen));
700 const __m256i v_ref = _mm256_loadu_si256((const __m256i*)(ref + mlen));
701 const __m256i v_cmp = _mm256_cmpeq_epi8(v_src, v_ref);
702 const uint32_t mask = (uint32_t)_mm256_movemask_epi8(v_cmp);
703 if (mask == 0xFFFFFFFF)
704 mlen += 32;
705 else {
706 mlen += (uint32_t)zxc_ctz32(~mask);
707 goto _match_len_done;
708 }
709 }
710#elif defined(ZXC_USE_SSE2)
711 const uint8_t* limit_16 = iend - 16;
712 while (ip + mlen < limit_16) {
713 const __m128i v_src = _mm_loadu_si128((const __m128i*)(ip + mlen));
714 const __m128i v_ref = _mm_loadu_si128((const __m128i*)(ref + mlen));
715 const __m128i v_cmp = _mm_cmpeq_epi8(v_src, v_ref);
716 const uint32_t mask = (uint32_t)_mm_movemask_epi8(v_cmp);
717 if (mask == 0xFFFFU)
718 mlen += 16;
719 else {
720 // mask != 0xFFFF => a differing byte exists in bits 0..15,
721 // so the lowest set bit of ~mask lies in that range.
722 mlen += (uint32_t)zxc_ctz32(~mask);
723 goto _match_len_done;
724 }
725 }
726#elif defined(ZXC_USE_NEON64)
727 {
728 const uint8_t* limit_32 = iend - 32;
729 while (ip + mlen < limit_32) {
730 const uint8x16_t s0 = vld1q_u8(ip + mlen);
731 const uint8x16_t r0 = vld1q_u8(ref + mlen);
732 const uint8x16_t c0 = vceqq_u8(s0, r0);
733 const uint64_t m0 = vget_lane_u64(
734 vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(c0), 4)), 0);
735 if (UNLIKELY(m0 != ~(uint64_t)0)) {
736 mlen += (uint32_t)(zxc_ctz64(~m0) >> 2);
737 goto _match_len_done;
738 }
739 const uint8x16_t s1 = vld1q_u8(ip + mlen + 16);
740 const uint8x16_t r1 = vld1q_u8(ref + mlen + 16);
741 const uint8x16_t c1 = vceqq_u8(s1, r1);
742 const uint64_t m1 = vget_lane_u64(
743 vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(c1), 4)), 0);
744 if (UNLIKELY(m1 != ~(uint64_t)0)) {
745 mlen += 16 + (uint32_t)(zxc_ctz64(~m1) >> 2);
746 goto _match_len_done;
747 }
748 mlen += 32;
749 }
750 if (ip + mlen < iend - 16) {
751 const uint8x16_t v_src = vld1q_u8(ip + mlen);
752 const uint8x16_t v_ref = vld1q_u8(ref + mlen);
753 const uint8x16_t v_cmp = vceqq_u8(v_src, v_ref);
754 const uint64_t mask = vget_lane_u64(
755 vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(v_cmp), 4)), 0);
756 if (LIKELY(mask == ~(uint64_t)0))
757 mlen += 16;
758 else {
759 mlen += (uint32_t)(zxc_ctz64(~mask) >> 2);
760 goto _match_len_done;
761 }
762 }
763 }
764#elif defined(ZXC_USE_NEON32)
765 {
766 const uint8_t* limit_16 = iend - 16;
767 while (ip + mlen < limit_16) {
768 const uint8x16_t v_src = vld1q_u8(ip + mlen);
769 const uint8x16_t v_ref = vld1q_u8(ref + mlen);
770 const uint8x16_t v_cmp = vceqq_u8(v_src, v_ref);
771 uint8x8_t p1 = vpmin_u8(vget_low_u8(v_cmp), vget_high_u8(v_cmp));
772 uint8x8_t p2 = vpmin_u8(p1, p1);
773 uint8x8_t p3 = vpmin_u8(p2, p2);
774 uint8x8_t p4 = vpmin_u8(p3, p3);
775 uint8_t min_val = vget_lane_u8(p4, 0);
776 if (min_val == 0xFF)
777 mlen += 16;
778 else {
779 uint8x16_t v_diff = vmvnq_u8(v_cmp);
780 uint64_t lo =
781 (uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 0) |
782 ((uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 1) << 32);
783 if (lo != 0)
784 mlen += (zxc_ctz64(lo) >> 3);
785 else
786 mlen +=
787 8 + (zxc_ctz64(
788 (uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 2) |
789 ((uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 3)
790 << 32)) >>
791 3);
792 goto _match_len_done;
793 }
794 }
795 }
796#endif
797 while (ip + mlen < limit_8) {
798 const uint64_t diff = zxc_le64(ip + mlen) ^ zxc_le64(ref + mlen);
799 if (diff == 0)
800 mlen += sizeof(uint64_t);
801 else {
802 mlen += (uint32_t)zxc_ctz64(diff) >> 3;
803 goto _match_len_done;
804 }
805 }
806 while (ip + mlen < iend && ref[mlen] == ip[mlen]) mlen++;
807
808 _match_len_done:;
809 const int better = (mlen > best.len);
810 best.len = better ? mlen : best.len;
811 best.ref = better ? ref : best.ref;
812
813 if (UNLIKELY(best.len >= (uint32_t)p.sufficient_len || ip + best.len >= iend)) break;
814 }
815
816 match_idx = (delta != 0) ? next_idx : 0;
817 }
818
819_finalize_match:
820 if (best.ref) {
821 // Backtrack to extend match backwards
822 const uint8_t* b_ip = ip;
823 const uint8_t* b_ref = best.ref;
824 while (b_ip > anchor && b_ref > src && b_ip[-1] == b_ref[-1]) {
825 b_ip--;
826 b_ref--;
827 best.len++;
828 best.backtrack++;
829 }
830 best.ref = b_ref;
831 }
832
833 // Lazy: a match two bytes longer at ip+1, or from level 4 three bytes longer
834 // at ip+2, cancels this one.
835 if (p.use_lazy && best.ref && best.len < (uint32_t)p.lazy_len_threshold &&
836 ip + 1 < search_limit) {
837 if (zxc_lazy_probe(src, ip + 1, iend, best.len + 2, hash_table, hash_tags, chain_table,
838 epoch_mark, offset_mask, use_hash5, p) ||
839 (level >= ZXC_LEVEL_BALANCED && ip + 2 < search_limit &&
840 zxc_lazy_probe(src, ip + 2, iend, best.len + 3, hash_table, hash_tags, chain_table,
841 epoch_mark, offset_mask, use_hash5, p)))
842 best.ref = NULL;
843 }
844
845 return best;
846}
847
853static ZXC_ALWAYS_INLINE void zxc_opt_relax(uint32_t* RESTRICT dp, uint16_t* RESTRICT parent_len,
854 uint16_t* RESTRICT parent_off, const size_t p,
855 const size_t L, const uint32_t nxt,
856 const uint16_t off_biased) {
857 if (nxt < dp[p + L]) {
858 dp[p + L] = nxt;
859 parent_len[p + L] = (uint16_t)L;
860 parent_off[p + L] = off_biased;
861 }
862}
863
903// codeql[cpp/unused-static-function]: false positive
905 uint32_t* RESTRICT dp, uint16_t* RESTRICT parent_len, uint16_t* RESTRICT parent_off,
906 const size_t p, size_t L, const size_t L_end, const uint32_t nxt, const uint16_t off_biased) {
907#if defined(ZXC_USE_AVX512) && defined(__AVX512VL__)
908 if (L + 16 <= L_end) {
909 const __m512i v_inc =
910 _mm512_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15);
911 const __m512i v_nxt = _mm512_set1_epi32((int)nxt);
912 const __m256i v_off = _mm256_set1_epi16((int16_t)off_biased);
913 for (; L + 16 <= L_end; L += 16) {
914 const __m512i v_L_lanes = _mm512_add_epi32(v_inc, _mm512_set1_epi32((int)L));
915 const __m512i v_dp = _mm512_loadu_si512((const void*)&dp[p + L]);
916 const __mmask16 m = _mm512_cmplt_epu32_mask(v_nxt, v_dp);
917 _mm512_mask_storeu_epi32(&dp[p + L], m, v_nxt);
918 const __m256i v_L_u16 = _mm512_cvtusepi32_epi16(v_L_lanes);
919 _mm256_mask_storeu_epi16((void*)&parent_len[p + L], m, v_L_u16);
920 _mm256_mask_storeu_epi16((void*)&parent_off[p + L], m, v_off);
921 }
922 }
923#elif defined(ZXC_USE_AVX2)
924 if (L + 8 <= L_end) {
925 const __m256i v_inc = _mm256_setr_epi32(0, 1, 2, 3, 4, 5, 6, 7);
926 const __m256i v_nxt = _mm256_set1_epi32((int)nxt);
927 const __m256i v_bias = _mm256_set1_epi32((int)0x80000000);
928 const __m256i v_nxt_b = _mm256_xor_si256(v_nxt, v_bias);
929 const __m128i v_off = _mm_set1_epi16((int16_t)off_biased);
930 for (; L + 8 <= L_end; L += 8) {
931 const __m256i v_L_lanes = _mm256_add_epi32(v_inc, _mm256_set1_epi32((int)L));
932 const __m256i v_dp = _mm256_loadu_si256((const __m256i*)&dp[p + L]);
933 // Unsigned-compare-via-bias trick:
934 // (dp ^ 0x80000000) > (nxt ^ 0x80000000) iff dp > nxt
935 // because XOR with the sign bit maps unsigned ordering to
936 // signed ordering. AVX2 only has signed cmpgt for 32-bit.
937 const __m256i v_dp_b = _mm256_xor_si256(v_dp, v_bias);
938 const __m256i v_mask = _mm256_cmpgt_epi32(v_dp_b, v_nxt_b);
939 const __m256i v_dp_new = _mm256_blendv_epi8(v_dp, v_nxt, v_mask);
940 _mm256_storeu_si256((__m256i*)&dp[p + L], v_dp_new);
941 // Pack 8x int32 mask -> 8x int16 mask with signed saturation:
942 // 0xFFFFFFFF -> 0xFFFF, 0x00000000 -> 0x0000.
943 const __m128i v_mask16 = _mm_packs_epi32(_mm256_castsi256_si128(v_mask),
944 _mm256_extracti128_si256(v_mask, 1));
945 const __m128i v_L_u16 = _mm_packus_epi32(_mm256_castsi256_si128(v_L_lanes),
946 _mm256_extracti128_si256(v_L_lanes, 1));
947 const __m128i v_pl = _mm_loadu_si128((const __m128i*)&parent_len[p + L]);
948 const __m128i v_pl_new = _mm_blendv_epi8(v_pl, v_L_u16, v_mask16);
949 _mm_storeu_si128((__m128i*)&parent_len[p + L], v_pl_new);
950 const __m128i v_po = _mm_loadu_si128((const __m128i*)&parent_off[p + L]);
951 const __m128i v_po_new = _mm_blendv_epi8(v_po, v_off, v_mask16);
952 _mm_storeu_si128((__m128i*)&parent_off[p + L], v_po_new);
953 }
954 }
955#elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32)
956 if (L + 4 <= L_end) {
957 static const uint32_t k_inc_array[4] = {0, 1, 2, 3};
958 const uint32x4_t v_inc = vld1q_u32(k_inc_array);
959 const uint32x4_t v_nxt = vdupq_n_u32(nxt);
960 const uint16x4_t v_off = vdup_n_u16(off_biased);
961 for (; L + 4 <= L_end; L += 4) {
962 const uint32x4_t v_L_lanes = vaddq_u32(v_inc, vdupq_n_u32((uint32_t)L));
963 const uint32x4_t v_dp = vld1q_u32(&dp[p + L]);
964 const uint32x4_t v_mask = vcgtq_u32(v_dp, v_nxt);
965 vst1q_u32(&dp[p + L], vbslq_u32(v_mask, v_nxt, v_dp));
966 const uint16x4_t v_mask16 = vmovn_u32(v_mask);
967 const uint16x4_t v_L_u16 = vqmovn_u32(v_L_lanes);
968 const uint16x4_t v_pl = vld1_u16(&parent_len[p + L]);
969 vst1_u16(&parent_len[p + L], vbsl_u16(v_mask16, v_L_u16, v_pl));
970 const uint16x4_t v_po = vld1_u16(&parent_off[p + L]);
971 vst1_u16(&parent_off[p + L], vbsl_u16(v_mask16, v_off, v_po));
972 }
973 }
974#elif defined(ZXC_OPT_SSE2)
975 if (L + 4 <= L_end) {
976 const __m128i v_inc = _mm_setr_epi32(0, 1, 2, 3);
977 const __m128i v_nxt = _mm_set1_epi32((int)nxt);
978 const __m128i v_bias = _mm_set1_epi32((int)0x80000000);
979 const __m128i v_nxt_b = _mm_xor_si128(v_nxt, v_bias);
980 const __m128i v_off = _mm_set1_epi16((short)off_biased);
981 for (; L + 4 <= L_end; L += 4) {
982 const __m128i v_L_lanes = _mm_add_epi32(v_inc, _mm_set1_epi32((int)L));
983 const __m128i v_dp = _mm_loadu_si128((const __m128i*)&dp[p + L]);
984 // Unsigned compare via sign-bit bias (SSE2 cmpgt is signed only):
985 // (dp ^ 0x80000000) > (nxt ^ 0x80000000) iff dp > nxt.
986 const __m128i v_dp_b = _mm_xor_si128(v_dp, v_bias);
987 const __m128i v_mask = _mm_cmpgt_epi32(v_dp_b, v_nxt_b);
988 const __m128i v_dp_new = zxc_mm_blendv_epi8_sse2(v_dp, v_nxt, v_mask);
989 _mm_storeu_si128((__m128i*)&dp[p + L], v_dp_new);
990 // Narrow the 4x int32 mask / length lanes to 4x int16 (low 64 bits).
991 // packs: 0xFFFFFFFF -> 0xFFFF, 0 -> 0; packus (SSE2-emulated): u32->u16.
992 const __m128i v_mask16 = _mm_packs_epi32(v_mask, v_mask);
993 const __m128i v_L_u16 = zxc_mm_packus_epi32_sse2(v_L_lanes, v_L_lanes);
994 __m128i v_pl = _mm_loadl_epi64((const __m128i*)&parent_len[p + L]);
995 v_pl = zxc_mm_blendv_epi8_sse2(v_pl, v_L_u16, v_mask16);
996 _mm_storel_epi64((__m128i*)&parent_len[p + L], v_pl);
997 __m128i v_po = _mm_loadl_epi64((const __m128i*)&parent_off[p + L]);
998 v_po = zxc_mm_blendv_epi8_sse2(v_po, v_off, v_mask16);
999 _mm_storel_epi64((__m128i*)&parent_off[p + L], v_po);
1000 }
1001 }
1002#endif
1003 // Scalar tail (and full path on archs without SIMD).
1004 // L < L_end <= UINT16_MAX (caller precondition), so the cast is lossless.
1005 for (; L < L_end; L++) zxc_opt_relax(dp, parent_len, parent_off, p, L, nxt, off_biased);
1006 return L;
1007}
1008
1028// codeql[cpp/unused-static-function]: false positive
1029static uint32_t zxc_opt_estimate_lit_bits(const uint8_t* RESTRICT src, const size_t src_sz,
1030 void* RESTRICT scratch) {
1031 if (UNLIKELY(src_sz < ZXC_OPT_LIT_SAMPLE_MIN)) return CHAR_BIT;
1032
1033 uint32_t hist[ZXC_HUF_NUM_SYMBOLS] = {0};
1034 const size_t step = (src_sz > 4096) ? (src_sz >> 12) : 1U;
1035 size_t sampled = 0;
1036 for (size_t i = 0; i < src_sz; i += step) {
1037 hist[src[i]]++;
1038 sampled++;
1039 }
1040
1041 uint8_t code_len[ZXC_HUF_NUM_SYMBOLS];
1042 if (UNLIKELY(zxc_huf_build_code_lengths(hist, code_len, scratch,
1044 return CHAR_BIT;
1045
1046 // Sample-weighted sum of code lengths = predicted Huffman bits; divide by
1047 // the sample count for bits/byte, rounded up. The DP is integer, and
1048 // rounding up favours matches over fractional-cost literals.
1049 uint32_t total_bits = 0;
1050 for (int k = 0; k < ZXC_HUF_NUM_SYMBOLS; k++) {
1051 total_bits += hist[k] * (uint32_t)code_len[k];
1052 }
1053 const uint32_t n = (uint32_t)sampled;
1054 const uint32_t avg = (total_bits + n - 1) / n;
1055
1056 // Cap at RAW cost: if Huffman can't beat 8 bits/byte on the sample,
1057 // the encoder will pick RAW anyway and 8 is the actual literal cost.
1058 return (avg < CHAR_BIT) ? avg : CHAR_BIT;
1059}
1060
1106static int zxc_lz77_optimal_parse_glo(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src,
1107 const size_t src_sz, uint32_t* RESTRICT hash_table,
1108 uint8_t* RESTRICT hash_tags, uint16_t* RESTRICT chain_table,
1109 const uint32_t epoch_mark, const uint32_t offset_mask,
1110 const int level, uint8_t* RESTRICT literals,
1111 uint8_t* RESTRICT buf_tokens, uint16_t* RESTRICT buf_offsets,
1112 uint8_t* RESTRICT buf_extras, uint32_t* RESTRICT seq_c_out,
1113 size_t* RESTRICT lit_c_out, size_t* RESTRICT extras_sz_out,
1114 uint16_t* RESTRICT max_offset_out) {
1115 zxc_lz77_params_t lzp_opt = zxc_get_lz77_params(level);
1116 lzp_opt.use_lazy = 0; // guard
1117
1118 // With a dictionary, src = [dict | block]. The DP arrays index from the block
1119 // start, so src_base points at the first block byte for literal copies while
1120 // src stays the match finder's base.
1121 const size_t dict_sz = ctx->dict_size;
1122 const size_t block_sz = src_sz - dict_sz;
1123 const uint8_t* const src_base = src + dict_sz;
1124 const uint8_t* const iend = src + src_sz;
1125
1126 // Block too small for any match: emit all as literals.
1127 if (UNLIKELY(block_sz < ZXC_LZ_SEARCH_MARGIN + 1)) {
1128 if (block_sz > 0) ZXC_MEMCPY(literals, src_base, block_sz);
1129 *lit_c_out = block_sz;
1130 *seq_c_out = 0;
1131 *extras_sz_out = 0;
1132 *max_offset_out = 0;
1133 return 0;
1134 }
1135
1136 const size_t search_limit_pos = block_sz - ZXC_LZ_SEARCH_MARGIN;
1137 const uint8_t* const search_limit = src + search_limit_pos;
1138
1139 // DP arrays carved from ctx->opt_scratch, one allocation reused across
1140 // blocks, each sub-buffer cache-line padded.
1141 //
1142 // dp : (chunk+1) x u32: min cost to reach position p.
1143 // parent_len : (chunk+1) x u16: 0 = literal, >= MIN_MATCH = match.
1144 // parent_off : (chunk+1) x u16: biased match offset (distance-1).
1145 // match_end_bits : one bit per position, set when a match on the chosen
1146 // path ends there - a forward actions[] stack at 1/64
1147 // the cost.
1148 //
1149 // The same buffer doubles as package-merge scratch for the code-length
1150 // builder: that scratch is live before the DP and after the parse is read
1151 // out, never during, so capacity is just the larger of the two.
1152 size_t sz_dp;
1153 size_t sz_pl;
1154 size_t sz_po;
1155 size_t sz_bm;
1156 zxc_opt_dp_sizes(ctx->chunk_size, &sz_dp, &sz_pl, &sz_po, &sz_bm);
1157 const size_t n_bm_words = ZXC_BITMAP_WORDS(ctx->chunk_size + 1);
1158
1159 // Per-block literal cost (sample only block data, not dict prefix):
1160 const uint32_t lit_cost = zxc_opt_estimate_lit_bits(src_base, block_sz, ctx->opt_scratch);
1161
1162 uint32_t* const dp = (uint32_t*)ctx->opt_scratch;
1163 uint16_t* const parent_len = (uint16_t*)(ctx->opt_scratch + sz_dp);
1164 uint16_t* const parent_off = (uint16_t*)(ctx->opt_scratch + sz_dp + sz_pl);
1165 uint64_t* const match_end_bits = (uint64_t*)(ctx->opt_scratch + sz_dp + sz_pl + sz_po);
1166
1167 dp[0] = 0;
1168 ZXC_MEMSET(dp + 1, 0xFF, block_sz * sizeof(uint32_t));
1169 ZXC_MEMSET(match_end_bits, 0, sz_bm);
1170
1171 // Forward DP: visit every position, update reachable successors.
1172 // `skip_until` suppresses find_best_match strictly inside the last long
1173 // match - the transition from its start already covers dp[p+1..p+L], and
1174 // re-searching there is what makes the parser quadratic on repetitive data.
1175 size_t skip_until = 0;
1176 // Rolling repeat-offset seed for find_best_match
1177 uint32_t last_off = 0;
1178 // Previous matched position and the length relaxed from it.
1179 size_t prev_p = 0;
1180 size_t prev_len = 0;
1181 for (size_t p = 0; p < search_limit_pos; p++) {
1182 if (UNLIKELY(dp[p] == UINT32_MAX)) continue;
1183
1184 // Literal transition.
1185 const uint32_t lit_next = dp[p] + lit_cost;
1186 if (lit_next < dp[p + 1]) {
1187 dp[p + 1] = lit_next;
1188 parent_len[p + 1] = 0;
1189 }
1190
1191 if (p < skip_until) continue;
1192
1193 // Match transition: no lazy, no backtrack (anchor=ip). Sub-lengths are
1194 // iterated because any L <= max_L matches at the same offset and may end
1195 // at a more useful DP position. ip is absolute so dict references
1196 // resolve against src.
1197 const uint8_t* ip = src_base + p;
1198 const zxc_match_t m = zxc_lz77_find_best_match(
1199 src, ip, iend, search_limit, /*anchor=*/ip, hash_table, hash_tags, chain_table,
1200 epoch_mark, offset_mask, level, lzp_opt, last_off);
1201
1202 if (m.ref) {
1203 const uint32_t off = (uint32_t)(ip - m.ref);
1204 if (off > 0 && off <= ZXC_LZ_WINDOW_SIZE) {
1205 last_off = off;
1206 const size_t L_max_raw = (m.len > block_sz - p) ? (block_sz - p) : (size_t)m.len;
1207 const size_t L_max = (L_max_raw > UINT16_MAX) ? UINT16_MAX : L_max_raw;
1208
1209 // Cost is piecewise constant in varint segments, so split
1210 // [MIN_MATCH, L_max] into:
1211 // 1. cheap : v < ML_MASK -> base
1212 // 2. varint1 : v in [ML_MASK, ML_MASK + 128) -> base + 8
1213 // 3. varint2+: v >= ML_MASK + 128 -> base + 16, ...
1214 // The first two have a constant nxt and vectorize; the third is
1215 // rare enough to stay scalar.
1216 const uint16_t off_biased = (uint16_t)(off - ZXC_LZ_OFFSET_BIAS);
1217 const size_t L_max_plus = L_max + 1;
1218 const size_t L_cheap_end = ZXC_LZ_MIN_MATCH_LEN + ZXC_TOKEN_ML_MASK;
1219 const size_t L_v1_end = L_cheap_end + 128;
1220 const uint32_t nxt_cheap = dp[p] + ZXC_OPT_MATCH_COST_BASE;
1221 const uint32_t nxt_v1 = nxt_cheap + CHAR_BIT;
1222
1223 // Covered: each processed position q bounds dp[q+l] by
1224 // dp[q] + cost(l), by relaxing or through its own predecessor.
1225 // With dp[p] >= dp[p-1], length l only wins where cost(l) <
1226 // cost(l+1): the last length of the two classes below
1227 // ZXC_OPT_LONG_MATCH_SKIP. Costs ignore the offset.
1228 const int covered = prev_len > L_max && prev_p + 1 == p && dp[p] >= dp[p - 1];
1229 prev_p = p;
1230 prev_len = L_max;
1231
1232 if (covered) {
1233 if (L_max_plus >= L_cheap_end)
1234 zxc_opt_relax(dp, parent_len, parent_off, p, L_cheap_end - 1, nxt_cheap,
1235 off_biased);
1236 if (L_max_plus >= L_v1_end)
1237 zxc_opt_relax(dp, parent_len, parent_off, p, L_v1_end - 1, nxt_v1,
1238 off_biased);
1239 } else {
1240 // 1. Cheap range.
1242 dp, parent_len, parent_off, p, ZXC_LZ_MIN_MATCH_LEN,
1243 (L_max_plus < L_cheap_end) ? L_max_plus : L_cheap_end, nxt_cheap,
1244 off_biased);
1245
1246 // 2. First varint level (1-byte extension).
1247 if (L < L_max_plus)
1249 dp, parent_len, parent_off, p, L,
1250 (L_max_plus < L_v1_end) ? L_max_plus : L_v1_end, nxt_v1, off_biased);
1251
1252 // 3. Higher varint levels: variable cost, kept scalar.
1253 // Reached only by L >= ML_MASK + 128 + MIN_MATCH, so the
1254 // v >= ML_MASK guard from the original loop is implied.
1255 for (; L < L_max_plus; L++) {
1256 uint32_t cost = ZXC_OPT_MATCH_COST_BASE;
1257 uint32_t v = (uint32_t)(L - ZXC_LZ_MIN_MATCH_LEN) - ZXC_TOKEN_ML_MASK;
1258 cost += CHAR_BIT;
1259 while (v >= 128) {
1260 v >>= 7;
1261 cost += CHAR_BIT;
1262 }
1263 zxc_opt_relax(dp, parent_len, parent_off, p, L, dp[p] + cost, off_biased);
1264 }
1265 }
1266 if (UNLIKELY(L_max >= ZXC_OPT_LONG_MATCH_SKIP)) skip_until = p + L_max - 1;
1267 }
1268 }
1269 }
1270
1271 // Tail (last ZXC_LZ_SEARCH_MARGIN bytes) can only be literals: the match finder
1272 // stops at search_limit so its 8-byte probe reads stay in bounds.
1273 for (size_t p = search_limit_pos; p < block_sz; p++) {
1274 if (UNLIKELY(dp[p] == UINT32_MAX)) continue;
1275 const uint32_t lit_next = dp[p] + lit_cost;
1276 if (lit_next < dp[p + 1]) {
1277 dp[p + 1] = lit_next;
1278 parent_len[p + 1] = 0;
1279 }
1280 }
1281
1282 // Backtrack from src_sz to 0. Only match endpoints are recorded; literals
1283 // are the unmarked runs between them, rebuilt at emission from lit_start,
1284 // so they need no backtrack storage.
1285 {
1286 size_t pos = block_sz;
1287 while (pos > 0) {
1288 const uint32_t L = parent_len[pos];
1289 if (L == 0) {
1290 pos -= 1;
1291 } else {
1292 match_end_bits[pos >> 6] |= (uint64_t)1 << (pos & 63);
1293 pos -= L;
1294 }
1295 }
1296 }
1297
1298 // Forward emission: walk match_end_bits word-by-word, peeling set bits
1299 // with ctzll. Each set bit gives a match endpoint; parent_len/parent_off
1300 // at that position recover (length, offset).
1301 uint32_t seq_c = 0;
1302 size_t lit_c = 0;
1303 size_t extras_sz = 0;
1304 uint16_t max_offset = 0;
1305 size_t lit_start = 0;
1306
1307 for (size_t word_idx = 0; word_idx < n_bm_words; word_idx++) {
1308 uint64_t w = match_end_bits[word_idx];
1309 while (w) {
1310 const size_t pos = (word_idx << 6) + (size_t)zxc_ctz64(w);
1311 w &= w - 1;
1312 const uint32_t L = parent_len[pos];
1313 const uint16_t off_biased = parent_off[pos];
1314 const size_t match_start = pos - L;
1315
1316 const size_t LL = match_start - lit_start;
1317 if (LL > 0) {
1318 ZXC_MEMCPY(literals + lit_c, src_base + lit_start, LL);
1319 lit_c += LL;
1320 }
1321 const uint32_t ll = (uint32_t)LL;
1322 const uint32_t ml = L - ZXC_LZ_MIN_MATCH_LEN;
1323 if (UNLIKELY(!zxc_glo_put_seq(buf_tokens, buf_offsets, buf_extras, &seq_c, &extras_sz,
1324 &max_offset, ll, ml, off_biased)))
1325 return ZXC_ERROR_OVERFLOW;
1326 lit_start = pos;
1327 }
1328 }
1329
1330 // Tail literals after the last match (or all literals if no match).
1331 if (lit_start < block_sz) {
1332 const size_t tail = block_sz - lit_start;
1333 ZXC_MEMCPY(literals + lit_c, src_base + lit_start, tail);
1334 lit_c += tail;
1335 }
1336
1337 *seq_c_out = seq_c;
1338 *lit_c_out = lit_c;
1339 *extras_sz_out = extras_sz;
1340 *max_offset_out = max_offset;
1341 return 0;
1342}
1343
1360static void zxc_lz_seed_dict(const uint8_t* RESTRICT src, const size_t dict_size,
1361 uint32_t* RESTRICT hash_table, uint8_t* RESTRICT hash_tags,
1362 uint16_t* RESTRICT chain_table, const uint32_t epoch_mark,
1363 const uint32_t offset_mask, const int level) {
1364 if (UNLIKELY(dict_size < ZXC_LZ_MIN_MATCH_LEN)) return;
1365
1366 const int use_hash5 = (level >= 3);
1367 const size_t limit = dict_size - (ZXC_LZ_MIN_MATCH_LEN - 1);
1368
1369 // Sparse seeding for the first half, dense for the second half.
1370 // Positions near the end of the dict produce shorter offsets and are
1371 // more likely to yield matches, so they deserve full coverage.
1372 const size_t half = limit / 2;
1373 for (size_t i = 0; i < half; i += 4) {
1374 const uint64_t val8 = zxc_le64(src + i);
1375 const uint32_t h = zxc_hash_func(val8, use_hash5);
1376 const uint32_t cur_pos = (uint32_t)i;
1377
1378 hash_table[h] = epoch_mark | cur_pos;
1379 hash_tags[h] = zxc_hash_tag((uint32_t)val8);
1380 chain_table[cur_pos & ZXC_LZ_WINDOW_MASK] = 0;
1381 }
1382 for (size_t i = half; i < limit; i++) {
1383 const uint64_t val8 = zxc_le64(src + i);
1384 const uint32_t h = zxc_hash_func(val8, use_hash5);
1385 const uint32_t cur_pos = (uint32_t)i;
1386 const uint32_t prev_idx = zxc_epoch_pos(hash_table[h], offset_mask, epoch_mark);
1387
1388 hash_table[h] = epoch_mark | cur_pos;
1389 hash_tags[h] = zxc_hash_tag((uint32_t)val8);
1390 chain_table[cur_pos & ZXC_LZ_WINDOW_MASK] = zxc_chain_delta(cur_pos, prev_idx);
1391 }
1392}
1393
1416static int zxc_encode_block_glo(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src,
1417 const size_t src_sz, uint8_t* RESTRICT dst, size_t dst_cap,
1418 size_t* RESTRICT out_sz) {
1419 if (UNLIKELY(dst_cap < ZXC_BLOCK_HEADER_SIZE)) return ZXC_ERROR_DST_TOO_SMALL;
1420
1421 const int level = ctx->compression_level;
1422 const size_t dict_sz = ctx->dict_size;
1423
1425 // The floor is a candidate until the block itself clears it.
1426 if (lzp.min_offset > 1 && zxc_block_is_short_dist_bound(src + dict_sz, src_sz - dict_sz))
1427 lzp.min_offset = 1;
1428
1429 ctx->epoch++;
1430 if (UNLIKELY(ctx->epoch >= ctx->max_epoch)) {
1431 ZXC_MEMSET(ctx->hash_table, 0, ZXC_LZ_HASH_SIZE * sizeof(uint32_t));
1432 ZXC_MEMSET(ctx->hash_tags, 0, ZXC_LZ_HASH_SIZE * sizeof(uint8_t));
1433 ctx->epoch = 1;
1434 }
1435 const uint32_t offset_bits = ctx->offset_bits;
1436 const uint32_t offset_mask = ctx->offset_mask;
1437 const uint32_t epoch_mark = ctx->epoch << offset_bits;
1438
1439 if (dict_sz > 0)
1440 zxc_lz_seed_dict(src, dict_sz, ctx->hash_table, ctx->hash_tags, ctx->chain_table,
1441 epoch_mark, offset_mask, level);
1442
1443 const uint8_t* ip = src + dict_sz;
1444 const uint8_t* iend = src + src_sz;
1445 const uint8_t* anchor = ip;
1446 const uint8_t* search_limit = iend - ZXC_LZ_SEARCH_MARGIN;
1447
1448 uint32_t* const hash_table = ctx->hash_table;
1449 uint8_t* const hash_tags = ctx->hash_tags;
1450 uint16_t* const chain_table = ctx->chain_table;
1451 uint8_t* const literals = ctx->literals;
1452 uint8_t* const buf_tokens = ctx->buf_tokens;
1453 uint16_t* const buf_offsets = ctx->buf_offsets;
1454 uint8_t* const buf_extras = ctx->buf_extras;
1455
1456 uint32_t seq_c = 0;
1457 size_t lit_c = 0;
1458 size_t extras_sz = 0;
1459 uint16_t max_offset = 0; // Track max offset for 1-byte/2-byte mode decision
1460
1461 // Level 6+: price-based optimal parser (fills outputs and skips the
1462 // lazy loop + last_lits handling below via `goto parse_done`).
1463 if (level >= ZXC_LEVEL_DENSITY) {
1464 const int rc = zxc_lz77_optimal_parse_glo(
1465 ctx, src, src_sz, hash_table, hash_tags, chain_table, epoch_mark, offset_mask, level,
1466 literals, buf_tokens, buf_offsets, buf_extras, &seq_c, &lit_c, &extras_sz, &max_offset);
1467 if (UNLIKELY(rc != 0)) return rc;
1468 goto parse_done;
1469 }
1470
1471 while (LIKELY(ip < search_limit)) {
1472 const size_t dist = (size_t)(ip - anchor);
1473 size_t step = lzp.step_base + (dist >> lzp.step_shift);
1474 if (UNLIKELY(ip + step >= search_limit)) step = 1;
1475
1476 if (LIKELY(ip + step + sizeof(uint64_t) <= iend)) {
1477 const uint64_t v_next = zxc_le64(ip + step);
1478 // cppcheck-suppress unreadVariable
1479 const uint32_t h_next = zxc_hash_func(v_next, 1);
1480 ZXC_PREFETCH_READ(&hash_tags[h_next]);
1481 ZXC_PREFETCH_READ(&hash_table[h_next]);
1482 }
1483
1484 const zxc_match_t m =
1485 zxc_lz77_find_best_match(src, ip, iend, search_limit, anchor, hash_table, hash_tags,
1486 chain_table, epoch_mark, offset_mask, level, lzp,
1487 /*last_off=*/0U);
1488
1489 if (m.ref) {
1490 ip -= m.backtrack;
1491 const uint32_t ll = (uint32_t)(ip - anchor);
1492 const uint32_t ml = m.len - ZXC_LZ_MIN_MATCH_LEN;
1493 const uint32_t off = (uint32_t)(ip - m.ref);
1494 const uint16_t off_biased = (uint16_t)(off - ZXC_LZ_OFFSET_BIAS);
1495
1496 if (ll > 0) zxc_flush_literals(literals, &lit_c, anchor, ll, iend);
1497
1498 if (UNLIKELY(!zxc_glo_put_seq(buf_tokens, buf_offsets, buf_extras, &seq_c, &extras_sz,
1499 &max_offset, ll, ml, off_biased)))
1500 return ZXC_ERROR_OVERFLOW;
1501
1502 if (m.len > 2 && level > ZXC_LEVEL_BALANCED) {
1503 const uint8_t* match_end = ip + m.len;
1504 if (match_end < iend - 7) {
1505 const uint32_t pos_u = (uint32_t)((match_end - 2) - src);
1506 const uint64_t val_u8 = zxc_le64(match_end - 2);
1507 const uint32_t val_u = (uint32_t)val_u8;
1508 const uint32_t h_u = zxc_hash_func(val_u8, 1);
1509 const uint32_t prev_idx =
1510 zxc_epoch_pos(hash_table[h_u], offset_mask, epoch_mark);
1511 hash_table[h_u] = epoch_mark | pos_u;
1512 hash_tags[h_u] = zxc_hash_tag(val_u);
1513 chain_table[pos_u & ZXC_LZ_WINDOW_MASK] = zxc_chain_delta(pos_u, prev_idx);
1514 }
1515 }
1516
1517 ip += m.len;
1518 anchor = ip;
1519 } else {
1520 ip += step;
1521 }
1522 }
1523
1524 const size_t last_lits = (size_t)(iend - anchor);
1525 if (last_lits > 0) {
1526 ZXC_MEMCPY(literals + lit_c, anchor, last_lits);
1527 lit_c += last_lits;
1528 }
1529
1530parse_done:;
1531 // Per-block match splitting on the parsed block (see zxc_glo_split_block).
1532 seq_c = zxc_glo_split_block(buf_tokens, buf_offsets, buf_extras, &extras_sz, ctx->buf_split,
1533 ctx->buf_split_hist, seq_c, lzp.split_max);
1534 // Dictionary-table trainer hook: accumulate the REAL post-LZ literal
1535 // frequencies (see zxc_train_dict_huf). Cold path, NULL outside training.
1536 if (UNLIKELY(ctx->lit_freq_acc)) {
1537 for (size_t i = 0; i < lit_c; i++) ctx->lit_freq_acc[literals[i]]++;
1538 }
1539
1540 // --- RLE ANALYSIS ---
1541 size_t rle_size = 0;
1542 int enc_lit = ZXC_SECTION_ENCODING_RAW;
1543 size_t best_j = lit_c;
1544
1545 if (lit_c > 0) {
1546 const uint8_t* p = literals;
1547 const uint8_t* const p_end = literals + lit_c;
1548 const uint8_t* const p_end_4 = p_end - 3; // Safe limit for 4-byte lookahead
1549
1550 while (LIKELY(p < p_end)) {
1551 const uint8_t b = *p;
1552 const uint8_t* run_start = p++;
1553
1554 // Fast run counting with early SIMD exit
1555#if defined(ZXC_USE_AVX512)
1556 const __m512i vb = _mm512_set1_epi8((char)b);
1557 while (p <= p_end - 64) {
1558 const __m512i v = _mm512_loadu_si512((const void*)p);
1559 const __mmask64 mask = _mm512_cmpeq_epi8_mask(v, vb);
1560 if (mask != 0xFFFFFFFFFFFFFFFFULL) {
1561 p += (size_t)zxc_ctz64(~mask);
1562 goto _run_done;
1563 }
1564 p += 64;
1565 }
1566#elif defined(ZXC_USE_AVX2)
1567 const __m256i vb = _mm256_set1_epi8((char)b);
1568 while (p <= p_end - 32) {
1569 const __m256i v = _mm256_loadu_si256((const __m256i*)p);
1570 const uint32_t mask = (uint32_t)_mm256_movemask_epi8(_mm256_cmpeq_epi8(v, vb));
1571 if (mask != 0xFFFFFFFF) {
1572 p += zxc_ctz32(~mask);
1573 goto _run_done;
1574 }
1575 p += 32;
1576 }
1577#elif defined(ZXC_USE_SSE2)
1578 const __m128i vb = _mm_set1_epi8((char)b);
1579 while (p <= p_end - 16) {
1580 const __m128i v = _mm_loadu_si128((const __m128i*)p);
1581 const uint32_t mask = (uint32_t)_mm_movemask_epi8(_mm_cmpeq_epi8(v, vb));
1582 if (mask != 0xFFFFU) {
1583 p += zxc_ctz32(~mask);
1584 goto _run_done;
1585 }
1586 p += 16;
1587 }
1588#elif defined(ZXC_USE_NEON64)
1589 const uint8x16_t vb = vdupq_n_u8(b);
1590 while (p <= p_end - 16) {
1591 const uint8x16_t v = vld1q_u8(p);
1592 const uint8x16_t eq = vceqq_u8(v, vb);
1593 // SHRN nibble-mask: see find_best_match above for rationale.
1594 const uint64_t mask =
1595 vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(eq), 4)), 0);
1596 if (LIKELY(mask == ~(uint64_t)0)) {
1597 p += 16;
1598 } else {
1599 p += (size_t)(zxc_ctz64(~mask) >> 2);
1600 goto _run_done;
1601 }
1602 }
1603#elif defined(ZXC_USE_NEON32)
1604 uint8x16_t vb = vdupq_n_u8(b);
1605 while (p <= p_end - 16) {
1606 uint8x16_t v = vld1q_u8(p);
1607 uint8x16_t eq = vceqq_u8(v, vb);
1608 uint8x16_t not_eq = vmvnq_u8(eq);
1609
1610 // 32-bit ARM NEON doesn't always support vgetq_lane_u64 / vreinterpretq_u64_u8 so
1611 // we treat the 128-bit vector as 4 x 32-bit lanes */
1612 const uint32x4_t neq32 = vreinterpretq_u32_u8(not_eq);
1613 const uint32_t l0 = vgetq_lane_u32(neq32, 0);
1614 const uint32_t l1 = vgetq_lane_u32(neq32, 1);
1615
1616 const uint64_t lo = ((uint64_t)l1 << 32) | l0;
1617 if (lo != 0) {
1618 p += (size_t)(zxc_ctz64(lo) >> 3);
1619 goto _run_done;
1620 }
1621
1622 const uint32_t h0 = vgetq_lane_u32(neq32, 2);
1623 const uint32_t h1 = vgetq_lane_u32(neq32, 3);
1624 const uint64_t hi = ((uint64_t)h1 << 32) | h0;
1625
1626 if (hi != 0) {
1627 p += 8 + (zxc_ctz64(hi) >> 3);
1628 goto _run_done;
1629 }
1630 p += 16;
1631 }
1632#endif
1633 while (p < p_end && *p == b) p++;
1634
1635#if defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || defined(ZXC_USE_NEON64) || \
1636 defined(ZXC_USE_NEON32) || defined(ZXC_USE_SSE2)
1637 _run_done:;
1638#endif
1639 const size_t run = (size_t)(p - run_start);
1640
1641 if (run >= 4) {
1642 // RLE run: 2 bytes per 131 values, then remainder
1643 // Branchless: full_chunks * 2 + remainder handling
1644 const size_t full_chunks = run / 131;
1645 const size_t rem = run - full_chunks * 131; // Avoid modulo
1646 rle_size += full_chunks * 2;
1647 // Remainder: if >= 4 -> 2 bytes (RLE), else 1 + rem (literal)
1648 if (rem >= 4)
1649 rle_size += 2;
1650 else if (rem > 0)
1651 rle_size += 1 + rem;
1652 } else {
1653 // Literal run: scan ahead with fast SIMD lookahead
1654 const uint8_t* lit_start = run_start;
1655
1656#if defined(ZXC_RLE_WIN)
1657 while (p <= p_end_4 - ZXC_RLE_WIN) {
1658 const zxc_rle_mask_t m = zxc_rle_pair_mask(p);
1659 const zxc_rle_mask_t mask = m & (m >> ZXC_RLE_BPB) & (m >> (2 * ZXC_RLE_BPB));
1660 if (LIKELY(mask == 0)) {
1661 p += ZXC_RLE_WIN - 2;
1662 continue;
1663 }
1664 const unsigned rpos = (unsigned)(ZXC_RLE_CTZ(mask) / ZXC_RLE_BPB);
1665 const unsigned rpairs =
1666 (unsigned)(ZXC_RLE_CTZ(~(m >> (rpos * ZXC_RLE_BPB))) / ZXC_RLE_BPB);
1667 if (UNLIKELY(rpos + rpairs >= ZXC_RLE_WIN)) {
1668 p += rpos;
1669 goto _lit_done; /* run may extend past the window */
1670 }
1671 const size_t seg = (size_t)(p + rpos - lit_start);
1672 rle_size += seg + ((seg + 127) >> 7);
1673 rle_size += 2; /* run fits one chunk: full_chunks=0, rem>=4 */
1674 p += rpos + rpairs + 1;
1675 lit_start = p;
1676 }
1677#elif defined(ZXC_USE_NEON32)
1678 while (p <= p_end_4 - 16) {
1679 uint8x16_t v0 = vld1q_u8(p);
1680 uint8x16_t v1 = vld1q_u8(p + 1);
1681 const uint8x16_t pe = vceqq_u8(v0, v1);
1682 const uint8x16_t zv = vdupq_n_u8(0);
1683 const uint8x16_t eq =
1684 vandq_u8(pe, vandq_u8(vextq_u8(pe, zv, 1), vextq_u8(pe, zv, 2)));
1685
1686 uint32x4_t eq32 = vreinterpretq_u32_u8(eq);
1687 uint32_t l0 = vgetq_lane_u32(eq32, 0);
1688 uint32_t l1 = vgetq_lane_u32(eq32, 1);
1689 uint64_t lo = ((uint64_t)l1 << 32) | l0;
1690
1691 if (lo != 0) {
1692 p += (zxc_ctz64(lo) >> 3);
1693 goto _lit_done;
1694 }
1695
1696 uint32_t h0 = vgetq_lane_u32(eq32, 2);
1697 uint32_t h1 = vgetq_lane_u32(eq32, 3);
1698 uint64_t hi = ((uint64_t)h1 << 32) | h0;
1699
1700 if (hi != 0) {
1701 p += 8 + (zxc_ctz64(hi) >> 3);
1702 goto _lit_done;
1703 }
1704 p += 14;
1705 }
1706#endif
1707 while (p < p_end_4) {
1708 if (UNLIKELY(p[0] == p[1] && p[1] == p[2] && p[2] == p[3])) break;
1709 p++;
1710 }
1711 if (p >= p_end_4) p = p_end;
1712
1713#if defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || defined(ZXC_USE_NEON64) || \
1714 defined(ZXC_USE_NEON32) || defined(ZXC_USE_SSE2)
1715 _lit_done:;
1716#endif
1717 const size_t lit_run = (size_t)(p - lit_start);
1718 // 1 header per 128 bytes + all data bytes
1719 // lit_run + ceil(lit_run / 128)
1720 rle_size += lit_run + ((lit_run + 127) >> 7);
1721 }
1722 }
1723
1724 // RLE over RAW: space-speed J comparison. The per-level premium
1725 // reproduces the historical margin exactly below ULTRA.
1726 const size_t rle_j = rle_size + ZXC_SS_TAX(lit_c, zxc_ss_prem_rle_q8(level));
1727 if (rle_j < best_j) {
1728 enc_lit = ZXC_SECTION_ENCODING_RLE;
1729 best_j = rle_j;
1730 }
1731 }
1732
1733 // Level >= 6: Huffman as a third literal-encoding candidate. Histogram,
1734 // length-limited code lengths, exact PivCo section size, and switch if its
1735 // J (size + decode tax) beats the current winner's.
1736 uint8_t huf_code_len[ZXC_HUF_NUM_SYMBOLS];
1737 size_t huf_total_size = SIZE_MAX;
1738 uint32_t lit_freq[ZXC_HUF_NUM_SYMBOLS]; /* valid iff huf_total_size != SIZE_MAX */
1739 if (level >= ZXC_LEVEL_DENSITY && lit_c >= ZXC_HUF_MIN_LITERALS) {
1740 uint32_t freq0[ZXC_HUF_NUM_SYMBOLS] = {0};
1741 uint32_t freq1[ZXC_HUF_NUM_SYMBOLS] = {0};
1742 uint32_t freq2[ZXC_HUF_NUM_SYMBOLS] = {0};
1743 uint32_t freq3[ZXC_HUF_NUM_SYMBOLS] = {0};
1744 {
1745 size_t i = 0;
1746 for (; i + 4 <= lit_c; i += 4) {
1747 freq0[literals[i + 0]]++;
1748 freq1[literals[i + 1]]++;
1749 freq2[literals[i + 2]]++;
1750 freq3[literals[i + 3]]++;
1751 }
1752 for (; i < lit_c; i++) freq0[literals[i]]++;
1753 }
1754 uint32_t* const freq = lit_freq;
1755 for (int k = 0; k < ZXC_HUF_NUM_SYMBOLS; k++) {
1756 freq[k] = freq0[k] + freq1[k] + freq2[k] + freq3[k];
1757 }
1758
1759 if (zxc_huf_build_code_lengths(freq, huf_code_len, ctx->opt_scratch,
1760 zxc_huf_enc_max_code_len(level)) == ZXC_OK) {
1761 (void)zxc_huf_nudge_code_lengths(freq, huf_code_len, ctx->opt_scratch,
1762 ctx->opt_scratch_cap, zxc_huf_enc_max_code_len(level));
1763 huf_total_size = zxc_huf_calc_size(freq, huf_code_len, 1);
1764 // Space-speed: the entropy candidate must beat the current winner's
1765 // J, paying its own decode tax over the copy path.
1766 if (huf_total_size != SIZE_MAX) {
1767 const size_t huf_j = huf_total_size + ZXC_SS_TAX(lit_c, zxc_ss_prem_huf_q8(level));
1768 if (huf_j < best_j) {
1769 enc_lit = ZXC_SECTION_ENCODING_HUFFMAN;
1770 best_j = huf_j;
1771 }
1772 }
1773 }
1774 }
1775
1776 // Shared dictionary table (level >= 6, dict with a table): the HUFFMAN
1777 // bitstream minus the 128-byte header, so it stays viable far below
1778 // ZXC_HUF_MIN_LITERALS. A literal without a code drops the candidate.
1779 size_t huf_dict_total_size = SIZE_MAX;
1780 if (level >= ZXC_LEVEL_DENSITY && ctx->dict_huf_tree_ok && lit_c > 0) {
1781 // The dict candidate runs on sections too small for the per-block
1782 // table, so the histogram may not have been built above.
1783 if (huf_total_size == SIZE_MAX) {
1784 ZXC_MEMSET(lit_freq, 0, sizeof(uint32_t) * ZXC_HUF_NUM_SYMBOLS);
1785 for (size_t i = 0; i < lit_c; i++) lit_freq[literals[i]]++;
1786 }
1787 // zxc_huf_calc_size returns SIZE_MAX for unencodable candidates
1788 // (a literal byte without a code in the shared table).
1789 huf_dict_total_size =
1790 zxc_huf_calc_size_dict(lit_freq, ctx->dict_huf->code_len, &ctx->dict_huf->tree);
1791 if (huf_dict_total_size != SIZE_MAX) {
1792 // Same J rule against the running winner: vs the per-block Huffman
1793 // candidate the equal entropy taxes cancel (pure size comparison),
1794 // while RAW/RLE baselines keep their decode-tax edge.
1795 const size_t huf_dict_j =
1796 huf_dict_total_size + ZXC_SS_TAX(lit_c, zxc_ss_prem_huf_q8(level));
1797 // Last candidate: no need to keep best_j updated past this point.
1798 if (huf_dict_j < best_j) enc_lit = ZXC_SECTION_ENCODING_HUFFMAN_DICT;
1799 }
1800 }
1801
1802 // Level-7 (ULTRA): Huffman-code the token stream with the literal codec -
1803 // tokens are a <= 256-symbol alphabet. Same J rule; the decoder picks the
1804 // path from gh.enc_tok. opt_scratch is free by now.
1805 uint8_t tok_code_len[ZXC_HUF_NUM_SYMBOLS];
1806 uint8_t enc_tok = ZXC_SECTION_ENCODING_RAW;
1807 size_t tok_huf_size = 0;
1808 uint32_t tfreq[ZXC_HUF_NUM_SYMBOLS];
1809 if (level >= ZXC_LEVEL_ULTRA && seq_c >= ZXC_HUF_MIN_LITERALS) {
1810 ZXC_MEMSET(tfreq, 0, sizeof(tfreq)); /* only the ULTRA path reads tfreq */
1811 for (uint32_t i = 0; i < seq_c; i++) tfreq[buf_tokens[i]]++;
1812 if (zxc_huf_build_code_lengths(tfreq, tok_code_len, ctx->opt_scratch,
1813 zxc_huf_enc_max_code_len(level)) == ZXC_OK) {
1814 (void)zxc_huf_nudge_code_lengths(tfreq, tok_code_len, ctx->opt_scratch,
1815 ctx->opt_scratch_cap, zxc_huf_enc_max_code_len(level));
1816 tok_huf_size = zxc_huf_calc_size(tfreq, tok_code_len, 1);
1817 // Space-speed J comparison (this path is ULTRA-only): the PivCo
1818 // token section pays the same decode tax as PivCo literals.
1819 if (tok_huf_size != SIZE_MAX &&
1820 tok_huf_size + ZXC_SS_TAX((size_t)seq_c, zxc_ss_prem_huf_q8(level)) < (size_t)seq_c)
1821 enc_tok = ZXC_SECTION_ENCODING_HUFFMAN;
1822 }
1823 }
1824
1825 zxc_block_header_t bh = {.block_type = ZXC_BLOCK_GLO};
1826 uint8_t* const p = dst + ZXC_BLOCK_HEADER_SIZE;
1827 size_t rem = dst_cap - ZXC_BLOCK_HEADER_SIZE;
1828
1829 const int use_8bit_off = (max_offset <= 255) ? 1 : 0;
1830 const size_t off_stream_size = use_8bit_off ? seq_c : (seq_c * 2);
1831
1832 const zxc_gnr_header_t gh = {.n_sequences = seq_c,
1833 .n_literals = (uint32_t)lit_c,
1834 .enc_lit = (uint8_t)enc_lit,
1835 .enc_tok = enc_tok,
1836 .enc_mlen = 0,
1837 .enc_off = (uint8_t)use_8bit_off};
1838
1839 const size_t sz_lit = (enc_lit == ZXC_SECTION_ENCODING_RLE) ? rle_size
1840 : (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN) ? huf_total_size
1841 : (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN_DICT) ? huf_dict_total_size
1842 : lit_c;
1843 const size_t sz_tok = (enc_tok == ZXC_SECTION_ENCODING_HUFFMAN) ? tok_huf_size : seq_c;
1844 const size_t sz_off = off_stream_size;
1845 const size_t sz_ext = extras_sz;
1846
1847 const int ghs = zxc_write_glo_header_and_desc(p, rem, &gh, (uint32_t)sz_lit, (uint32_t)sz_tok);
1848 if (UNLIKELY(ghs < 0)) return ghs;
1849
1850 uint8_t* p_curr = p + ghs;
1851 rem -= (size_t)ghs;
1852
1853 if (UNLIKELY(rem < sz_lit)) return ZXC_ERROR_DST_TOO_SMALL;
1854
1855 if (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN) {
1856 const int written =
1857 zxc_huf_encode_section(literals, lit_c, lit_freq, huf_code_len, p_curr, rem);
1858 if (UNLIKELY(written < 0)) return written;
1859 if (UNLIKELY((size_t)written != huf_total_size)) return ZXC_ERROR_DST_TOO_SMALL;
1860 p_curr += written;
1861 } else if (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN_DICT) {
1862 const int written =
1863 zxc_huf_encode_section_dict(literals, lit_c, lit_freq, ctx->dict_huf->code_len,
1864 &ctx->dict_huf->tree, ctx->dict_huf->codes, p_curr, rem);
1865 if (UNLIKELY(written < 0)) return written;
1866 if (UNLIKELY((size_t)written != huf_dict_total_size)) return ZXC_ERROR_DST_TOO_SMALL;
1867 p_curr += written;
1868 } else if (enc_lit == ZXC_SECTION_ENCODING_RLE) {
1869 // Write RLE - optimized single-pass encoding
1870 const uint8_t* lit_ptr = literals;
1871 const uint8_t* const lit_end = literals + lit_c;
1872
1873 while (lit_ptr < lit_end) {
1874 uint8_t b = *lit_ptr;
1875 const uint8_t* run_start = lit_ptr++;
1876
1877 while (lit_ptr < lit_end && *lit_ptr == b) lit_ptr++;
1878 size_t run = (size_t)(lit_ptr - run_start);
1879
1880 if (run >= 4) {
1881 // RLE runs: emit 2-byte tokens (header + value)
1882 while (run >= 4) {
1883 size_t chunk = (run > 131) ? 131 : run;
1884 *p_curr++ = (uint8_t)(ZXC_LIT_RLE_FLAG | (chunk - 4));
1885 *p_curr++ = b;
1886 run -= chunk;
1887 }
1888 // Leftover < 4 bytes: emit as literal
1889 if (run > 0) {
1890 *p_curr++ = (uint8_t)(run - 1);
1891 ZXC_MEMCPY(p_curr, lit_ptr - run, run);
1892 p_curr += run;
1893 }
1894 } else {
1895 // Literal run: scan ahead to find next RLE opportunity
1896 const uint8_t* lit_run_start = run_start;
1897
1898 while (lit_ptr < lit_end) {
1899 if (UNLIKELY(lit_ptr + 3 < lit_end && lit_ptr[0] == lit_ptr[1] &&
1900 lit_ptr[1] == lit_ptr[2] && lit_ptr[2] == lit_ptr[3])) {
1901 break;
1902 }
1903 lit_ptr++;
1904 }
1905
1906 size_t lit_run = (size_t)(lit_ptr - lit_run_start);
1907 const uint8_t* src_ptr = lit_run_start;
1908
1909 // Emit literal chunks (max 128 bytes each)
1910 while (lit_run > 0) {
1911 size_t chunk = (lit_run > 128) ? 128 : lit_run;
1912 *p_curr++ = (uint8_t)(chunk - 1);
1913 ZXC_MEMCPY(p_curr, src_ptr, chunk);
1914 p_curr += chunk;
1915 src_ptr += chunk;
1916 lit_run -= chunk;
1917 }
1918 }
1919 }
1920 } else {
1921 ZXC_MEMCPY(p_curr, literals, lit_c);
1922 p_curr += lit_c;
1923 }
1924 rem -= sz_lit;
1925
1926 if (UNLIKELY(rem < sz_tok)) return ZXC_ERROR_DST_TOO_SMALL;
1927
1928 if (enc_tok == ZXC_SECTION_ENCODING_HUFFMAN) {
1929 const int written =
1930 zxc_huf_encode_section(buf_tokens, seq_c, tfreq, tok_code_len, p_curr, rem);
1931 if (UNLIKELY(written < 0)) return written;
1932 if (UNLIKELY((size_t)written != tok_huf_size)) return ZXC_ERROR_DST_TOO_SMALL;
1933 p_curr += written;
1934 } else {
1935 ZXC_MEMCPY(p_curr, buf_tokens, seq_c);
1936 p_curr += seq_c;
1937 }
1938 rem -= sz_tok;
1939
1940 if (UNLIKELY(rem < sz_off)) return ZXC_ERROR_DST_TOO_SMALL;
1941
1942 if (use_8bit_off) {
1943 // Write 1-byte offsets - unroll for better throughput
1944 uint32_t i = 0;
1945 for (; i + 8 <= seq_c; i += 8) {
1946 p_curr[0] = (uint8_t)buf_offsets[i + 0];
1947 p_curr[1] = (uint8_t)buf_offsets[i + 1];
1948 p_curr[2] = (uint8_t)buf_offsets[i + 2];
1949 p_curr[3] = (uint8_t)buf_offsets[i + 3];
1950 p_curr[4] = (uint8_t)buf_offsets[i + 4];
1951 p_curr[5] = (uint8_t)buf_offsets[i + 5];
1952 p_curr[6] = (uint8_t)buf_offsets[i + 6];
1953 p_curr[7] = (uint8_t)buf_offsets[i + 7];
1954 p_curr += 8;
1955 }
1956 for (; i < seq_c; i++) {
1957 *p_curr++ = (uint8_t)buf_offsets[i];
1958 }
1959 } else {
1960 // Write 2-byte offsets in little-endian order
1961#ifdef ZXC_BIG_ENDIAN
1962 for (uint32_t i = 0; i < seq_c; i++) {
1963 zxc_store_le16(p_curr, buf_offsets[i]);
1964 p_curr += sizeof(uint16_t);
1965 }
1966#else
1967 ZXC_MEMCPY(p_curr, buf_offsets, seq_c * sizeof(uint16_t));
1968 p_curr += seq_c * sizeof(uint16_t);
1969#endif
1970 }
1971 rem -= sz_off;
1972
1973 // The sections behind the literals must total ZXC_BLOCK_LIT_SLACK bytes so
1974 // the decoder's literal wild-copy can overshoot. Tokens and offsets alone
1975 // clear it from 16 sequences on, so pad is 0 on any real block. It rides
1976 // inside the extras section, which the decoder sizes from the payload
1977 // residue - hence no wire field.
1978 const size_t behind_lit = sz_tok + sz_off + sz_ext;
1979 const size_t pad = (behind_lit < ZXC_BLOCK_LIT_SLACK) ? ZXC_BLOCK_LIT_SLACK - behind_lit : 0;
1980
1981 if (UNLIKELY(rem < sz_ext + pad)) return ZXC_ERROR_DST_TOO_SMALL;
1982
1983 ZXC_MEMCPY(p_curr, buf_extras, extras_sz);
1984 p_curr += extras_sz;
1985
1986 ZXC_MEMSET(p_curr, 0, pad);
1987 p_curr += pad;
1988
1989 bh.comp_size = (uint32_t)(p_curr - (dst + ZXC_BLOCK_HEADER_SIZE));
1990 const int hw = zxc_write_block_header(dst, dst_cap, &bh);
1991 if (UNLIKELY(hw < 0)) return hw;
1992
1993 // Checksum will be appended by the wrapper
1994 *out_sz = ZXC_BLOCK_HEADER_SIZE + bh.comp_size;
1995 return ZXC_OK;
1996}
1997
2017static int zxc_encode_block_ghi(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src,
2018 const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap,
2019 size_t* RESTRICT const out_sz) {
2020 if (UNLIKELY(dst_cap < ZXC_BLOCK_HEADER_SIZE)) return ZXC_ERROR_DST_TOO_SMALL;
2021
2022 const int level = ctx->compression_level;
2023 const size_t dict_sz = ctx->dict_size;
2024
2026 // The floor is a candidate until the block itself clears it.
2027 if (lzp.min_offset > 1 && zxc_block_is_short_dist_bound(src + dict_sz, src_sz - dict_sz))
2028 lzp.min_offset = 1;
2029
2030 ctx->epoch++;
2031 if (UNLIKELY(ctx->epoch >= ctx->max_epoch)) {
2032 ZXC_MEMSET(ctx->hash_table, 0, ZXC_LZ_HASH_SIZE * sizeof(uint32_t));
2033 ZXC_MEMSET(ctx->hash_tags, 0, ZXC_LZ_HASH_SIZE * sizeof(uint8_t));
2034 ctx->epoch = 1;
2035 }
2036 const uint32_t offset_bits = ctx->offset_bits;
2037 const uint32_t offset_mask = ctx->offset_mask;
2038 const uint32_t epoch_mark = ctx->epoch << offset_bits;
2039
2040 if (dict_sz > 0)
2041 zxc_lz_seed_dict(src, dict_sz, ctx->hash_table, ctx->hash_tags, ctx->chain_table,
2042 epoch_mark, offset_mask, level);
2043
2044 const uint8_t* ip = src + dict_sz;
2045 const uint8_t* iend = src + src_sz;
2046 const uint8_t* anchor = ip;
2047 const uint8_t* search_limit = iend - ZXC_LZ_SEARCH_MARGIN;
2048
2049 uint32_t* const hash_table = ctx->hash_table;
2050 uint8_t* const hash_tags = ctx->hash_tags;
2051 uint8_t* const buf_extras = ctx->buf_extras;
2052 uint16_t* const chain_table = ctx->chain_table;
2053 uint8_t* const literals = ctx->literals;
2054 uint32_t* const buf_sequences = ctx->buf_sequences;
2055
2056 uint32_t seq_c = 0;
2057 size_t extras_c = 0;
2058 size_t lit_c = 0;
2059
2060 while (LIKELY(ip < search_limit)) {
2061 size_t dist = (size_t)(ip - anchor);
2062 size_t step = lzp.step_base + (dist >> lzp.step_shift);
2063 if (UNLIKELY(ip + step >= search_limit)) step = 1;
2064
2065 ZXC_PREFETCH_READ(ip + step * 4 + ZXC_CACHE_LINE_SIZE);
2066
2067 if (LIKELY(ip + step + sizeof(uint64_t) <= iend)) {
2068 const uint64_t v_next = zxc_le64(ip + step);
2069 // cppcheck-suppress unreadVariable
2070 const uint32_t h_next = zxc_hash_func(v_next, 0);
2071 ZXC_PREFETCH_READ(&hash_tags[h_next]);
2072 ZXC_PREFETCH_READ(&hash_table[h_next]);
2073 }
2074
2075 const zxc_match_t m =
2076 zxc_lz77_find_best_match(src, ip, iend, search_limit, anchor, hash_table, hash_tags,
2077 chain_table, epoch_mark, offset_mask, level, lzp,
2078 /*last_off=*/0U);
2079
2080 if (m.ref) {
2081 ip -= m.backtrack;
2082 const uint32_t ll = (uint32_t)(ip - anchor);
2083 const uint32_t ml = m.len - ZXC_LZ_MIN_MATCH_LEN;
2084 const uint32_t off = (uint32_t)(ip - m.ref);
2085
2086 if (ll > 0) zxc_flush_literals(literals, &lit_c, anchor, ll, iend);
2087
2088 const uint32_t ll_write = (ll >= ZXC_SEQ_LL_MASK) ? 255U : ll;
2089 const uint32_t ml_write = (ml >= ZXC_SEQ_ML_MASK) ? 255U : ml;
2090 const uint32_t seq_val = (ll_write << (ZXC_SEQ_ML_BITS + ZXC_SEQ_OFF_BITS)) |
2091 (ml_write << ZXC_SEQ_OFF_BITS) |
2093 buf_sequences[seq_c] = seq_val;
2094 seq_c++;
2095
2096 if (UNLIKELY(!zxc_emit_extra(buf_extras, &extras_c, ll, ZXC_SEQ_LL_MASK) ||
2097 !zxc_emit_extra(buf_extras, &extras_c, ml, ZXC_SEQ_ML_MASK)))
2098 return ZXC_ERROR_OVERFLOW;
2099
2100 ip += m.len;
2101 anchor = ip;
2102 } else {
2103 ip += step;
2104 }
2105 }
2106
2107 const size_t last_lits = (size_t)(iend - anchor);
2108 if (last_lits > 0) {
2109 ZXC_MEMCPY(literals + lit_c, anchor, last_lits);
2110 lit_c += last_lits;
2111 }
2112
2113 zxc_block_header_t bh = {.block_type = ZXC_BLOCK_GHI};
2114 uint8_t* const p = dst + ZXC_BLOCK_HEADER_SIZE;
2115 size_t rem = dst_cap - ZXC_BLOCK_HEADER_SIZE;
2116
2117 const zxc_gnr_header_t gh = {.n_sequences = seq_c,
2118 .n_literals = (uint32_t)lit_c,
2119 .enc_lit = ZXC_SECTION_ENCODING_RAW,
2120 .enc_tok = 0,
2121 .enc_mlen = 0,
2122 .enc_off = 0};
2123
2124 const int ghs = zxc_write_ghi_header(p, rem, &gh);
2125 if (UNLIKELY(ghs < 0)) return ghs;
2126
2127 uint8_t* p_curr = p + ghs;
2128 rem -= (size_t)ghs;
2129
2130 const size_t sz_lit = lit_c;
2131 const size_t sz_seq = seq_c * sizeof(uint32_t);
2132 const size_t sz_ext = extras_c;
2133
2134 // See the GLO twin. GHI literals are always RAW, so the slack always
2135 // matters here.
2136 const size_t behind_lit = sz_seq + sz_ext;
2137 const size_t pad = (behind_lit < ZXC_BLOCK_LIT_SLACK) ? ZXC_BLOCK_LIT_SLACK - behind_lit : 0;
2138
2139 if (UNLIKELY(rem < sz_lit + sz_seq + sz_ext + pad)) return ZXC_ERROR_DST_TOO_SMALL;
2140
2141 ZXC_MEMCPY(p_curr, literals, lit_c);
2142 p_curr += lit_c;
2143
2144 // Write sequences in little-endian order
2145#ifdef ZXC_BIG_ENDIAN
2146 for (uint32_t i = 0; i < seq_c; i++) {
2147 zxc_store_le32(p_curr, buf_sequences[i]);
2148 p_curr += sizeof(uint32_t);
2149 }
2150#else
2151 ZXC_MEMCPY(p_curr, buf_sequences, sz_seq);
2152 p_curr += sz_seq;
2153#endif
2154
2155 // --- WRITE EXTRAS ---
2156 ZXC_MEMCPY(p_curr, buf_extras, sz_ext);
2157 p_curr += sz_ext;
2158
2159 ZXC_MEMSET(p_curr, 0, pad);
2160 p_curr += pad;
2161
2162 bh.comp_size = (uint32_t)(p_curr - (dst + ZXC_BLOCK_HEADER_SIZE));
2163 const int hw = zxc_write_block_header(dst, dst_cap, &bh);
2164 if (UNLIKELY(hw < 0)) return hw;
2165
2166 // Checksum will be appended by the wrapper
2167 *out_sz = ZXC_BLOCK_HEADER_SIZE + bh.comp_size;
2168 return ZXC_OK;
2169}
2170
2186static int zxc_encode_block_raw(const uint8_t* RESTRICT src, const size_t src_sz,
2187 uint8_t* RESTRICT const dst, const size_t dst_cap,
2188 size_t* RESTRICT const out_sz) {
2189 if (UNLIKELY(dst_cap < ZXC_BLOCK_HEADER_SIZE + src_sz)) return ZXC_ERROR_DST_TOO_SMALL;
2190
2192 bh.block_type = ZXC_BLOCK_RAW;
2193 bh.block_flags = 0; // Checksum flag moved to file header
2194 bh.reserved = 0;
2195 bh.comp_size = (uint32_t)src_sz;
2196
2197 const int hw = zxc_write_block_header(dst, dst_cap, &bh);
2198 if (UNLIKELY(hw < 0)) return hw;
2199
2200 ZXC_MEMCPY(dst + ZXC_BLOCK_HEADER_SIZE, src, src_sz);
2201
2202 // Checksum will be appended by the wrapper
2203 *out_sz = ZXC_BLOCK_HEADER_SIZE + src_sz;
2204 return ZXC_OK;
2205}
2206
2216// cppcheck-suppress unusedFunction
2217int zxc_compress_chunk_wrapper(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT chunk,
2218 const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap,
2219 const uint64_t block_index) {
2220 if (UNLIKELY(dst_cap < ZXC_BLOCK_HEADER_SIZE)) return ZXC_ERROR_DST_TOO_SMALL;
2221
2222 const size_t dict_sz = ctx->dict_size;
2223 const size_t block_sz = src_sz - dict_sz;
2224 const uint8_t* block_data = chunk + dict_sz;
2225 size_t w = 0;
2226 int res = ZXC_OK;
2227
2228 if (ctx->compression_level <= 2)
2229 res = zxc_encode_block_ghi(ctx, chunk, src_sz, dst, dst_cap, &w);
2230 else
2231 res = zxc_encode_block_glo(ctx, chunk, src_sz, dst, dst_cap, &w);
2232
2233 // Check expansion against block data size (excluding dict prefix).
2234 if (UNLIKELY(res != ZXC_OK || w >= block_sz)) {
2235 res = zxc_encode_block_raw(block_data, block_sz, dst, dst_cap, &w);
2236 if (UNLIKELY(res != ZXC_OK)) return res;
2237 }
2238
2239 if (ctx->checksum_enabled) {
2240 if (UNLIKELY(w + ZXC_BLOCK_CHECKSUM_SIZE > dst_cap)) return ZXC_ERROR_OVERFLOW;
2241
2242 zxc_store_le32(dst + w,
2243 zxc_checksum(block_data, block_sz, block_index, ZXC_CHECKSUM_RAPIDHASH));
2245 }
2246
2247 return (int)w;
2248}
@ ZXC_ERROR_OVERFLOW
Buffer overflow detected during processing.
Definition zxc_error.h:58
@ 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_SEQ_OFF_MASK
Mask to extract Offset from a GHI sequence.
static int zxc_ss_prem_rle_q8(const int level)
Per-level RLE-vs-RAW decode premium (Q8) for the space-speed selector.
#define ZXC_BITMAP_WORDS(n_bits)
Number of uint64_t words needed to hold a bitmap of n_bits.
#define ZXC_ALWAYS_INLINE
Forces a function to be inlined (fallback for non-GCC/Clang/MSVC compilers).
#define ZXC_HUF_MIN_LITERALS
Absolute floor (in literals) below which a Huffman candidate is never evaluated.
#define ZXC_OPT_LONG_MATCH_SKIP
Threshold above which find_best_match is skipped at intra-match positions, keeping the parser O(N) on...
#define ZXC_SEQ_ML_MASK
Mask to extract Match Length from a GHI sequence.
#define ZXC_CHECKSUM_RAPIDHASH
Checksum algorithm id for RapidHash (default, sole implementation).
#define ZXC_LZ_MAX_DIST
Maximum allowed offset distance.
#define ZXC_OPT_MATCH_COST_BASE
Static price (bits) of a match token before varint extras: 1 byte token + 2 byte offset.
#define ZXC_MAX_VARINT_VALUE
Maximum legitimate value a varint can decode to.
#define ZXC_GLO_INLINE_ML_CODE
Longest match length code (length minus ZXC_LZ_MIN_MATCH_LEN) a GLO token carries inline; the code ab...
static int zxc_ss_prem_huf_q8(const int level)
Per-level Huffman/PivCo-vs-RAW decode premium (Q8) for the space-speed selector.
#define ZXC_LZ_SEARCH_MARGIN
Bytes at the block end where match search stops (left as literals).
#define ZXC_TOKEN_ML_MASK
Mask to extract Match Length from a GLO token.
#define ZXC_TOKEN_LL_MASK
Mask to extract Literal Length from a GLO token.
#define ZXC_LZ_HASH_SIZE
Maximum number of entries in the hash table.
#define ZXC_SEQ_ML_BITS
Bits for Match Length in a GHI sequence.
#define ZXC_LZ_HASH_BITS
Address bits for the LZ77 hash table (2^15 = 32 768 buckets).
#define ZXC_LZ_MIN_MATCH_LEN
Minimum match length for an LZ77 match.
#define ZXC_OPT_LIT_SAMPLE_MIN
Minimum literal count for the sample-based Huffman cost estimator used by the optimal parser.
#define ZXC_LZ_WINDOW_SIZE
Sliding window size (64 KB).
#define ZXC_PAD_SIZE
Safety padding appended to buffers to tolerate overruns.
#define ZXC_SS_TAX(n, prem_q8)
Decode tax of a candidate: n decoded bytes at premium prem (Q8).
#define ZXC_LZ_OFFSET_BIAS
Base bias added to encoded offsets (stored = actual - bias).
#define ZXC_HASH_MULT32
Marsaglia 32-bit multiplier: the 4-byte LZ and dictionary hash.
#define ZXC_BLOCK_HEADER_SIZE
Block header size: Type(1)+Flags(1)+Reserved(1)+Checksum(1)+CompSize(4).
#define ZXC_HUF_MAX_CODE_LEN_DENSITY
Encoder code-length cap for levels up to ZXC_LEVEL_DENSITY (below ZXC_LEVEL_ULTRA): shallow 8-bit tre...
#define ZXC_BLOCK_LIT_SLACK
Readable bytes a GLO/GHI payload guarantees after its literal section.
#define ZXC_GLO_MAX_INLINE_ML
Longest match a GLO sequence carries without a varint extension.
#define ZXC_SEQ_OFF_BITS
Bits for Offset in a GHI sequence.
#define ZXC_HASH_XORSHIFT64
Vigna's xorshift* multiplier: the 5-byte LZ hash and the digest fold.
static uint32_t zxc_read_varint(const uint8_t **ptr, const uint8_t *end)
Reads a Prefix Varint encoded integer.
#define ZXC_LIT_RLE_FLAG
Flag bit indicating an RLE run in the literal stream (0x80).
#define ZXC_BLOCK_CHECKSUM_SIZE
Size of the per-block checksum field in bytes.
#define ZXC_CACHE_LINE_SIZE
Assumed CPU cache line size for alignment.
#define ZXC_LZ_WINDOW_MASK
Mask for ring-buffer indexing into chain_table (power-of-two window).
#define ZXC_TOKEN_LIT_BITS
Bits for Literal Length in a GLO token.
#define ZXC_HUF_NUM_SYMBOLS
Alphabet size: one entry per possible byte value.
#define ZXC_SEQ_LL_MASK
Mask to extract Literal Length from a GHI sequence.
@ ZXC_LEVEL_ULTRA
Maximum density: Huffman-coded literals and sequence tokens.
@ ZXC_LEVEL_DENSITY
Higher density: adds Huffman-coded literals on top of COMPACT.
@ ZXC_LEVEL_BALANCED
Balanced trade-off between ratio and decode speed.
@ ZXC_LEVEL_FAST
Fast compression; slightly better ratio than FASTEST.
On-disk header structure for a ZXC block (8 bytes, little-endian).
uint8_t reserved
Reserved for future protocol extensions.
uint32_t comp_size
Compressed size excluding this header.
uint8_t block_type
Block type (see zxc_block_type_t).
uint8_t block_flags
Flags (e.g., checksum presence).
Compression / decompression context.
Header specific to General (LZ-based) compression blocks.
uint32_t n_sequences
The total count of LZ sequences in the block.
Search parameters for LZ77 compression levels.
int lazy_attempts
Maximum number of candidates explored during lazy evaluation (same semantics as search_depth but appl...
int search_depth
Maximum number of candidates explored in the hash chain per position.
int use_lazy
Enable lazy matching.
int lazy_len_threshold
Skip lazy evaluation when the current match length already reaches this threshold: a match this long ...
uint32_t step_shift
Acceleration factor for step size: step = step_base + (distance >> step_shift).
uint8_t split_max
Longest match re-emitted as inline pieces (zxc_glo_split_block), in the token's units (length - ZXC_L...
int sufficient_len
"Good enough" match length: once a match reaches this threshold the chain walk stops immediately,...
uint32_t min_offset
Shortest match distance the parser may emit; 1 = unconstrained.
uint32_t step_base
Base step size when advancing through unmatched literals.
int zxc_write_glo_header_and_desc(uint8_t *dst, const size_t rem, const zxc_gnr_header_t *gh, const uint32_t lit_comp, const uint32_t tok_comp)
Serialises a GLO block header followed by its section descriptors.
Definition zxc_common.c:940
int zxc_write_block_header(uint8_t *dst, const size_t dst_capacity, const zxc_block_header_t *bh)
Serialises a block header (8 bytes) into dst.
Definition zxc_common.c:684
int zxc_write_ghi_header(uint8_t *dst, const size_t rem, const zxc_gnr_header_t *gh)
Serialises a GHI block header.
Definition zxc_common.c:997
static uint32_t zxc_glo_split_block(uint8_t *tokens, uint16_t *offsets, uint8_t *extras, size_t *extras_sz, uint8_t *side, zxc_glo_split_hist_t *hist_buf, const uint32_t n_seq, const uint8_t cap)
Per-block match splitting: escaped matches re-emitted as inline pieces.
static int zxc_match_reaches(const uint8_t *cur, const uint8_t *ref, uint32_t from, const uint32_t need, const uint8_t *iend)
Tells whether a match reaches a given length, without measuring it.
static void zxc_opt_relax(uint32_t *dp, uint16_t *parent_len, uint16_t *parent_off, const size_t p, const size_t L, const uint32_t nxt, const uint16_t off_biased)
Relaxes dp[p + L] with a match of length L costing nxt in total.
static int zxc_encode_block_ghi(zxc_cctx_t *ctx, const uint8_t *src, const size_t src_sz, uint8_t *dst, const size_t dst_cap, size_t *const out_sz)
Encodes one block in the GHI format (levels 1 and 2, the speed path).
int zxc_compress_chunk_wrapper(zxc_cctx_t *ctx, const uint8_t *chunk, const size_t src_sz, uint8_t *dst, const size_t dst_cap, const uint64_t block_index)
Compresses one chunk into a single ZXC block (the compression hot path).
static int zxc_glo_put_seq(uint8_t *buf_tokens, uint16_t *buf_offsets, uint8_t *buf_extras, uint32_t *seq_c, size_t *extras_sz, uint16_t *max_offset, const uint32_t ll, const uint32_t ml, const uint16_t off_biased)
Emits one GLO sequence (token, offset, LL/ML escapes) and updates max_offset, the block's widest bias...
static void zxc_lz_seed_dict(const uint8_t *src, const size_t dict_size, uint32_t *hash_table, uint8_t *hash_tags, uint16_t *chain_table, const uint32_t epoch_mark, const uint32_t offset_mask, const int level)
Seeds the hash/chain tables from dictionary content prepended to src.
static void zxc_varint_keep(uint8_t *extras, size_t *w, size_t *r, const uint8_t *end)
Keeps the varint at extras[*r]: moves it down to extras[*w].
static int zxc_glo_split_analyze(const uint8_t *tokens, const uint32_t n_seq, zxc_glo_split_hist_t *hist_buf, uint8_t *ctx_bad, uint8_t *side)
Predictability of the block's ML-escape branch, for the split decision.
static void zxc_extras_move(uint8_t *extras, size_t *w, size_t *r, const uint8_t *to)
Moves extras[*r, to) down to extras[*w] (w <= r), advancing both.
static void zxc_flush_literals(uint8_t *literals, size_t *lit_c, const uint8_t *anchor, const uint32_t ll, const uint8_t *iend)
Stages a run of literals into the literal stream.
static uint8_t zxc_hash_tag(const uint32_t val4)
8-bit rejection tag: XOR fold of the first four bytes at a position.
static size_t zxc_opt_dp_update_const_cost(uint32_t *dp, uint16_t *parent_len, uint16_t *parent_off, const size_t p, size_t L, const size_t L_end, const uint32_t nxt, const uint16_t off_biased)
Update dp[p + L_start .
static int zxc_emit_extra(uint8_t *extras, size_t *used, const uint32_t val, const uint32_t mask)
Appends a saturated token field's excess to the extras stream.
static size_t zxc_write_varint(uint8_t *dst, const uint32_t val)
Pair-equality mask for the RLE lookahead: element i is set when p[i] == p[i+1].
static int zxc_encode_block_glo(zxc_cctx_t *ctx, const uint8_t *src, const size_t src_sz, uint8_t *dst, size_t dst_cap, size_t *out_sz)
Encodes one block in the GLO format (levels 3 and up).
static uint16_t zxc_chain_delta(const uint32_t cur_pos, const uint32_t prev_idx)
Chain link from a position back to the one the hash slot held.
static int zxc_lazy_probe(const uint8_t *src, const uint8_t *lp, const uint8_t *iend, const uint32_t need, const uint32_t *hash_table, const uint8_t *hash_tags, const uint16_t *chain_table, const uint32_t epoch_mark, const uint32_t offset_mask, const int use_hash5, const zxc_lz77_params_t p)
Tells whether lp holds a match of at least need bytes, within the lazy search budget.
static uint32_t zxc_glo_split_piece(const uint32_t code)
Next piece of a match of length code code above the inline reach: the longest inline one that leaves ...
static uint32_t zxc_epoch_pos(const uint32_t head, const uint32_t offset_mask, const uint32_t epoch_mark)
Decodes a hash-table slot into a block position, 0 if it is stale.
static uint32_t zxc_opt_estimate_lit_bits(const uint8_t *src, const size_t src_sz, void *scratch)
Estimate per-block literal cost from a sampled histogram passed through the actual length-limited Huf...
static uint32_t zxc_hash_func(const uint64_t val, const int use_hash5)
Computes a hash value for either a 4-byte or 5-byte sequence.
static uint32_t zxc_glo_split_pieces(const uint32_t code)
Inline pieces a match of length code code splits into: ceil(length / 19).
static int zxc_encode_block_raw(const uint8_t *src, const size_t src_sz, uint8_t *const dst, const size_t dst_cap, size_t *const out_sz)
Encodes a RAW (stored) block: header plus a verbatim copy.
static int zxc_lz77_optimal_parse_glo(zxc_cctx_t *ctx, const uint8_t *src, const size_t src_sz, uint32_t *hash_table, uint8_t *hash_tags, uint16_t *chain_table, const uint32_t epoch_mark, const uint32_t offset_mask, const int level, uint8_t *literals, uint8_t *buf_tokens, uint16_t *buf_offsets, uint8_t *buf_extras, uint32_t *seq_c_out, size_t *lit_c_out, size_t *extras_sz_out, uint16_t *max_offset_out)
Static price-based optimal LZ77 parser for level 6.
static zxc_match_t zxc_lz77_find_best_match(const uint8_t *src, const uint8_t *ip, const uint8_t *iend, const uint8_t *search_limit, const uint8_t *anchor, uint32_t *hash_table, uint8_t *hash_tags, uint16_t *chain_table, const uint32_t epoch_mark, const uint32_t offset_mask, const int level, const zxc_lz77_params_t p, const uint32_t last_off)
Finds the best matching sequence for LZ77 compression.
size_t zxc_huf_calc_size(const uint32_t *freq, const uint8_t *code_len, const int with_header)
Un-suffixed forwarders for the PivCo section codec (tests, tools).
int zxc_huf_build_code_lengths(const uint32_t *freq, uint8_t *code_len, void *scratch, const int max_code_len)
Build length-limited per-symbol Huffman code lengths from frequencies.
size_t zxc_huf_calc_size_dict(const uint32_t *freq, const uint8_t *code_len, const zxc_pivco_tree_t *tree)
zxc_huf_calc_size for a dict section: prebuilt tree, no header.
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 PivCo section against a prebuilt dict tree/codes (no header).
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 PivCo literal section (128-byte lengths header + payload).
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.
Internal definitions, constants, SIMD helpers, and utility functions.
#define ZXC_GLO_SPLIT_MIN_MISPREDICT_PCT
Mispredict rate, in percent of a block's escapes, from which the block is split.
static uint64_t zxc_le64(const void *p)
Reads a little-endian 64-bit value from a possibly unaligned address.
static int zxc_ctz32(const uint32_t x)
Counts trailing zeros, returning 32 for a zero input.
static uint32_t zxc_le32(const void *p)
Reads a little-endian 32-bit value from a possibly unaligned address.
#define ZXC_GLO_SPLIT_CTX_BITS
Longest escape history a context keys on; small blocks shorten it (down to 2 bits) towards 16 samples...
static void zxc_copy32(void *dst, const void *src)
Copies 32 bytes from source to destination using SIMD when available.
static int zxc_huf_enc_max_code_len(const int level)
Encoder Huffman code-length cap for a compression level: levels below ZXC_LEVEL_ULTRA use ZXC_HUF_MAX...
static zxc_lz77_params_t zxc_get_lz77_params(const int level)
Retrieves LZ77 compression parameters based on the specified compression level.
static void zxc_store_le16(void *p, const uint16_t v)
Writes a 16-bit value little-endian to a possibly unaligned address.
uint32_t zxc_glo_split_hist_t[4][2][1U<< 8]
zxc_glo_split_analyze's escape histograms: four rotating lanes of [escape][context] counts,...
static void zxc_store_le32(void *p, const uint32_t v)
Writes a 32-bit value little-endian to a possibly unaligned address.
static int zxc_ctz64(const uint64_t x)
Counts trailing zeros, returning 64 for a zero input.
static int zxc_block_is_short_dist_bound(const uint8_t *const blk, const size_t size)
Reports whether a block leans on back-references shorter than ZXC_LZ_MINDIST.
static uint32_t zxc_checksum(const void *input, const size_t len, const uint64_t seed, const uint8_t hash_method)
Calculates a 32-bit hash for a given input buffer.
static void zxc_opt_dp_sizes(const size_t chunk_size, size_t *sz_dp, size_t *sz_pl, size_t *sz_po, size_t *sz_bm)
The four DP partitions the optimal parser carves out of opt_scratch.