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)
34#include "../../include/zxc_error.h"
47 const uint64_t v5 = val & 0xFFFFFFFFFFULL;
50 const uint64_t v4 = val ^ (val >> 15);
62 return (uint8_t)(val4 ^ (val4 >> 16));
72 const uint32_t epoch_mark) {
73 return ((head & ~offset_mask) == epoch_mark) ? (head & offset_mask) : 0;
83 const uint32_t dist = cur_pos - prev_idx;
85 return (uint16_t)(dist & valid);
97#if defined(ZXC_USE_AVX512)
100#define ZXC_RLE_CTZ(x) zxc_ctz64(x)
101typedef uint64_t zxc_rle_mask_t;
103 return (uint64_t)_mm512_cmpeq_epi8_mask(_mm512_loadu_si512((
const void*)p),
104 _mm512_loadu_si512((
const void*)(p + 1)));
106#elif defined(ZXC_USE_AVX2)
107#define ZXC_RLE_WIN 32
109#define ZXC_RLE_CTZ(x) zxc_ctz32(x)
110typedef uint32_t zxc_rle_mask_t;
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));
116#elif defined(ZXC_USE_SSE2)
117#define ZXC_RLE_WIN 16
119#define ZXC_RLE_CTZ(x) zxc_ctz32(x)
120typedef uint32_t zxc_rle_mask_t;
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));
126#elif defined(ZXC_USE_NEON64)
127#define ZXC_RLE_WIN 16
129#define ZXC_RLE_CTZ(x) zxc_ctz64(x)
130typedef uint64_t zxc_rle_mask_t;
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);
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)
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));
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);
206 if (LIKELY(val < (1U << 7))) {
207 dst[0] = (uint8_t)val;
212 if (LIKELY(val < (1U << 14))) {
213 dst[0] = (uint8_t)(0x80 | (val & 0x3F));
214 dst[1] = (uint8_t)(val >> 6);
220 dst[0] = (uint8_t)(0xC0 | (val & 0x1F));
221 dst[1] = (uint8_t)(val >> 5);
222 dst[2] = (uint8_t)(val >> 13);
233 const uint32_t val,
const uint32_t mask) {
234 if (LIKELY(val < mask))
return 1;
236 if (UNLIKELY(n == 0))
return 0;
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) {
266 buf_offsets[*seq_c] = off_biased;
267 if (off_biased > *max_offset) *max_offset = off_biased;
277 size_t* RESTRICT r,
const uint8_t* to) {
278 while (extras + *r < to) extras[(*w)++] = extras[(*r)++];
283 size_t* RESTRICT r,
const uint8_t* end) {
284 const uint8_t* p = extras + *r;
315 uint8_t* RESTRICT side) {
317 while (order > 2 && (n_seq >> order) < 16) order--;
318 const uint32_t hmask = (1U << order) - 1U;
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));
324 uint32_t hist = 0, n_esc = 0;
325 for (uint32_t i = 0; i < n_seq; i++) {
327 side[i] = (uint8_t)hist;
328 ctx_hist[i & 3][esc][hist]++;
329 hist = ((hist << 1) | esc) & hmask;
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];
339 ctx_bad[c] = (n1 < n0);
340 bad_esc += ctx_bad[c] ? n1 : 0;
341 errors += (n0 < n1) ? n0 : n1;
343 return bad_esc != 0 &&
375 uint8_t* RESTRICT extras,
size_t* RESTRICT extras_sz,
377 const uint32_t n_seq,
const uint8_t cap) {
384 const uint8_t*
const extras_end = extras + *extras_sz;
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]];
394 const uint8_t* p = extras + r;
396 if (bad && code <= cap) {
397 side[i] = (uint8_t)code;
399 r = (size_t)(p - extras);
405 if (extra == 0)
return n_seq;
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];
419 uint32_t code = side[i];
425 tokens[k] = (uint8_t)(ll_bits | p);
430 tokens[k] = (uint8_t)(ll_bits | code);
433 return n_seq + extra;
443 const uint8_t* anchor,
const uint32_t ll,
444 const uint8_t* iend) {
450 ZXC_MEMCPY(literals + *lit_c, anchor, ll);
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++;
493 if (d != 0)
return from + (uint32_t)(
zxc_ctz64(d) >> 3) >= need;
494 from +=
sizeof(uint64_t);
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,
513 if (need > (
size_t)(iend - lp))
return 0;
516 const uint32_t val = (uint32_t)val8;
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);
526 const uint8_t*
const ref = src + idx;
535 if (UNLIKELY(delta == 0))
break;
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,
572 const int use_hash5 = (level >= 3);
577 uint32_t cur_val = (uint32_t)cur_val8;
583 const uint32_t cur_pos = (uint32_t)(ip - src);
586 const uint8_t stored_tag = hash_tags[h];
591 match_idx =
zxc_epoch_pos(hash_table[h], offset_mask, epoch_mark);
597 const int skip_head = (match_idx != 0) & (stored_tag != cur_tag);
600 hash_table[h] = epoch_mark | cur_pos;
601 hash_tags[h] = cur_tag;
612 last_off <= cur_pos) {
613 const uint8_t*
const rep_ref = src + (cur_pos - last_off);
615 uint32_t mlen =
sizeof(uint32_t);
616 const uint8_t*
const limit_8 = iend -
sizeof(uint64_t);
617 while (ip + mlen < limit_8) {
620 mlen +=
sizeof(uint64_t);
622 mlen += (uint32_t)(
zxc_ctz64(diff) >> 3);
626 while (ip + mlen < iend && rep_ref[mlen] == ip[mlen]) mlen++;
630 if (UNLIKELY(best.len >= (uint32_t)p.
sufficient_len || ip + best.len >= iend))
631 goto _finalize_match;
635 if (match_idx == 0)
goto _finalize_match;
641 const uint32_t next_idx = match_idx - delta;
642 match_idx = (delta != 0) ? next_idx : 0;
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;
654 const uint32_t next_idx = match_idx - delta;
655 ZXC_PREFETCH_READ(src + next_idx + best.len - 3);
659 const int should_compare = (cur_pos - match_idx) >= p.
min_offset &&
663 if (should_compare) {
664 uint32_t mlen =
sizeof(uint32_t);
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;
672 while (ip + mlen < scalar_limit) {
675 mlen +=
sizeof(uint64_t);
678 goto _match_len_done;
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)
693 goto _match_len_done;
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)
707 goto _match_len_done;
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);
723 goto _match_len_done;
726#elif defined(ZXC_USE_NEON64)
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)) {
737 goto _match_len_done;
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;
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))
759 mlen += (uint32_t)(
zxc_ctz64(~mask) >> 2);
760 goto _match_len_done;
764#elif defined(ZXC_USE_NEON32)
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);
779 uint8x16_t v_diff = vmvnq_u8(v_cmp);
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);
788 (uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 2) |
789 ((uint64_t)vgetq_lane_u32(vreinterpretq_u32_u8(v_diff), 3)
792 goto _match_len_done;
797 while (ip + mlen < limit_8) {
800 mlen +=
sizeof(uint64_t);
803 goto _match_len_done;
806 while (ip + mlen < iend && ref[mlen] == ip[mlen]) mlen++;
809 const int better = (mlen > best.len);
810 best.len = better ? mlen : best.len;
811 best.ref = better ? ref : best.ref;
813 if (UNLIKELY(best.len >= (uint32_t)p.
sufficient_len || ip + best.len >= iend))
break;
816 match_idx = (delta != 0) ? next_idx : 0;
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]) {
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) ||
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)))
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]) {
859 parent_len[p + L] = (uint16_t)L;
860 parent_off[p + L] = off_biased;
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);
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]);
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);
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);
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));
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]);
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);
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);
1005 for (; L < L_end; L++)
zxc_opt_relax(dp, parent_len, parent_off, p, L, nxt, off_biased);
1030 void* RESTRICT scratch) {
1034 const size_t step = (src_sz > 4096) ? (src_sz >> 12) : 1U;
1036 for (
size_t i = 0; i < src_sz; i += step) {
1049 uint32_t total_bits = 0;
1051 total_bits += hist[k] * (uint32_t)code_len[k];
1053 const uint32_t n = (uint32_t)sampled;
1054 const uint32_t avg = (total_bits + n - 1) / n;
1058 return (avg < CHAR_BIT) ? avg : CHAR_BIT;
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) {
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;
1128 if (block_sz > 0) ZXC_MEMCPY(literals, src_base, block_sz);
1129 *lit_c_out = block_sz;
1132 *max_offset_out = 0;
1137 const uint8_t*
const search_limit = src + search_limit_pos;
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);
1168 ZXC_MEMSET(dp + 1, 0xFF, block_sz *
sizeof(uint32_t));
1169 ZXC_MEMSET(match_end_bits, 0, sz_bm);
1175 size_t skip_until = 0;
1177 uint32_t last_off = 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;
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;
1191 if (p < skip_until)
continue;
1197 const uint8_t* ip = src_base + p;
1199 src, ip, iend, search_limit, ip, hash_table, hash_tags, chain_table,
1200 epoch_mark, offset_mask, level, lzp_opt, last_off);
1203 const uint32_t off = (uint32_t)(ip - m.ref);
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;
1217 const size_t L_max_plus = L_max + 1;
1219 const size_t L_v1_end = L_cheap_end + 128;
1221 const uint32_t nxt_v1 = nxt_cheap + CHAR_BIT;
1228 const int covered = prev_len > L_max && prev_p + 1 == p && dp[p] >= dp[p - 1];
1233 if (L_max_plus >= L_cheap_end)
1234 zxc_opt_relax(dp, parent_len, parent_off, p, L_cheap_end - 1, nxt_cheap,
1236 if (L_max_plus >= L_v1_end)
1237 zxc_opt_relax(dp, parent_len, parent_off, p, L_v1_end - 1, nxt_v1,
1243 (L_max_plus < L_cheap_end) ? L_max_plus : L_cheap_end, nxt_cheap,
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);
1255 for (; L < L_max_plus; L++) {
1263 zxc_opt_relax(dp, parent_len, parent_off, p, L, dp[p] + cost, off_biased);
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;
1286 size_t pos = block_sz;
1288 const uint32_t L = parent_len[pos];
1292 match_end_bits[pos >> 6] |= (uint64_t)1 << (pos & 63);
1303 size_t extras_sz = 0;
1304 uint16_t max_offset = 0;
1305 size_t lit_start = 0;
1307 for (
size_t word_idx = 0; word_idx < n_bm_words; word_idx++) {
1308 uint64_t w = match_end_bits[word_idx];
1310 const size_t pos = (word_idx << 6) + (
size_t)
zxc_ctz64(w);
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;
1316 const size_t LL = match_start - lit_start;
1318 ZXC_MEMCPY(literals + lit_c, src_base + lit_start, LL);
1321 const uint32_t ll = (uint32_t)LL;
1323 if (UNLIKELY(!
zxc_glo_put_seq(buf_tokens, buf_offsets, buf_extras, &seq_c, &extras_sz,
1324 &max_offset, ll, ml, off_biased)))
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);
1339 *extras_sz_out = extras_sz;
1340 *max_offset_out = max_offset;
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) {
1366 const int use_hash5 = (level >= 3);
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);
1376 const uint32_t cur_pos = (uint32_t)i;
1378 hash_table[h] = epoch_mark | cur_pos;
1382 for (
size_t i = half; i < limit; i++) {
1383 const uint64_t val8 =
zxc_le64(src + i);
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);
1388 hash_table[h] = epoch_mark | cur_pos;
1417 const size_t src_sz, uint8_t* RESTRICT dst,
size_t dst_cap,
1418 size_t* RESTRICT out_sz) {
1421 const int level = ctx->compression_level;
1422 const size_t dict_sz = ctx->dict_size;
1430 if (UNLIKELY(ctx->epoch >= ctx->max_epoch)) {
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;
1440 zxc_lz_seed_dict(src, dict_sz, ctx->hash_table, ctx->hash_tags, ctx->chain_table,
1441 epoch_mark, offset_mask, level);
1443 const uint8_t* ip = src + dict_sz;
1444 const uint8_t* iend = src + src_sz;
1445 const uint8_t* anchor = ip;
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;
1458 size_t extras_sz = 0;
1459 uint16_t max_offset = 0;
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;
1471 while (LIKELY(ip < search_limit)) {
1472 const size_t dist = (size_t)(ip - anchor);
1474 if (UNLIKELY(ip + step >= search_limit)) step = 1;
1476 if (LIKELY(ip + step +
sizeof(uint64_t) <= iend)) {
1477 const uint64_t v_next =
zxc_le64(ip + step);
1480 ZXC_PREFETCH_READ(&hash_tags[h_next]);
1481 ZXC_PREFETCH_READ(&hash_table[h_next]);
1484 const zxc_match_t m =
1486 chain_table, epoch_mark, offset_mask, level, lzp,
1491 const uint32_t ll = (uint32_t)(ip - anchor);
1493 const uint32_t off = (uint32_t)(ip - m.ref);
1498 if (UNLIKELY(!
zxc_glo_put_seq(buf_tokens, buf_offsets, buf_extras, &seq_c, &extras_sz,
1499 &max_offset, ll, ml, off_biased)))
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;
1509 const uint32_t prev_idx =
1511 hash_table[h_u] = epoch_mark | pos_u;
1524 const size_t last_lits = (size_t)(iend - anchor);
1525 if (last_lits > 0) {
1526 ZXC_MEMCPY(literals + lit_c, anchor, last_lits);
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);
1536 if (UNLIKELY(ctx->lit_freq_acc)) {
1537 for (
size_t i = 0; i < lit_c; i++) ctx->lit_freq_acc[literals[i]]++;
1541 size_t rle_size = 0;
1542 int enc_lit = ZXC_SECTION_ENCODING_RAW;
1543 size_t best_j = lit_c;
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;
1550 while (LIKELY(p < p_end)) {
1551 const uint8_t b = *p;
1552 const uint8_t* run_start = p++;
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) {
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) {
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) {
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);
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)) {
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);
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);
1616 const uint64_t lo = ((uint64_t)l1 << 32) | l0;
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;
1633 while (p < p_end && *p == b) p++;
1635#if defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || defined(ZXC_USE_NEON64) || \
1636 defined(ZXC_USE_NEON32) || defined(ZXC_USE_SSE2)
1639 const size_t run = (size_t)(p - run_start);
1644 const size_t full_chunks = run / 131;
1645 const size_t rem = run - full_chunks * 131;
1646 rle_size += full_chunks * 2;
1651 rle_size += 1 + rem;
1654 const uint8_t* lit_start = run_start;
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;
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)) {
1671 const size_t seg = (size_t)(p + rpos - lit_start);
1672 rle_size += seg + ((seg + 127) >> 7);
1674 p += rpos + rpairs + 1;
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)));
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;
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;
1707 while (p < p_end_4) {
1708 if (UNLIKELY(p[0] == p[1] && p[1] == p[2] && p[2] == p[3]))
break;
1711 if (p >= p_end_4) p = p_end;
1713#if defined(ZXC_USE_AVX512) || defined(ZXC_USE_AVX2) || defined(ZXC_USE_NEON64) || \
1714 defined(ZXC_USE_NEON32) || defined(ZXC_USE_SSE2)
1717 const size_t lit_run = (size_t)(p - lit_start);
1720 rle_size += lit_run + ((lit_run + 127) >> 7);
1727 if (rle_j < best_j) {
1728 enc_lit = ZXC_SECTION_ENCODING_RLE;
1737 size_t huf_total_size = SIZE_MAX;
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]]++;
1752 for (; i < lit_c; i++) freq0[literals[i]]++;
1754 uint32_t*
const freq = lit_freq;
1756 freq[k] = freq0[k] + freq1[k] + freq2[k] + freq3[k];
1766 if (huf_total_size != SIZE_MAX) {
1768 if (huf_j < best_j) {
1769 enc_lit = ZXC_SECTION_ENCODING_HUFFMAN;
1779 size_t huf_dict_total_size = SIZE_MAX;
1783 if (huf_total_size == SIZE_MAX) {
1785 for (
size_t i = 0; i < lit_c; i++) lit_freq[literals[i]]++;
1789 huf_dict_total_size =
1791 if (huf_dict_total_size != SIZE_MAX) {
1795 const size_t huf_dict_j =
1798 if (huf_dict_j < best_j) enc_lit = ZXC_SECTION_ENCODING_HUFFMAN_DICT;
1806 uint8_t enc_tok = ZXC_SECTION_ENCODING_RAW;
1807 size_t tok_huf_size = 0;
1810 ZXC_MEMSET(tfreq, 0,
sizeof(tfreq));
1811 for (uint32_t i = 0; i < seq_c; i++) tfreq[buf_tokens[i]]++;
1819 if (tok_huf_size != SIZE_MAX &&
1821 enc_tok = ZXC_SECTION_ENCODING_HUFFMAN;
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);
1833 .n_literals = (uint32_t)lit_c,
1834 .enc_lit = (uint8_t)enc_lit,
1837 .enc_off = (uint8_t)use_8bit_off};
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
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;
1848 if (UNLIKELY(ghs < 0))
return ghs;
1850 uint8_t* p_curr = p + ghs;
1855 if (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN) {
1858 if (UNLIKELY(written < 0))
return written;
1861 }
else if (enc_lit == ZXC_SECTION_ENCODING_HUFFMAN_DICT) {
1864 &ctx->dict_huf->tree, ctx->dict_huf->codes, p_curr, rem);
1865 if (UNLIKELY(written < 0))
return written;
1868 }
else if (enc_lit == ZXC_SECTION_ENCODING_RLE) {
1870 const uint8_t* lit_ptr = literals;
1871 const uint8_t*
const lit_end = literals + lit_c;
1873 while (lit_ptr < lit_end) {
1874 uint8_t b = *lit_ptr;
1875 const uint8_t* run_start = lit_ptr++;
1877 while (lit_ptr < lit_end && *lit_ptr == b) lit_ptr++;
1878 size_t run = (size_t)(lit_ptr - run_start);
1883 size_t chunk = (run > 131) ? 131 : run;
1890 *p_curr++ = (uint8_t)(run - 1);
1891 ZXC_MEMCPY(p_curr, lit_ptr - run, run);
1896 const uint8_t* lit_run_start = run_start;
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])) {
1906 size_t lit_run = (size_t)(lit_ptr - lit_run_start);
1907 const uint8_t* src_ptr = lit_run_start;
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);
1921 ZXC_MEMCPY(p_curr, literals, lit_c);
1928 if (enc_tok == ZXC_SECTION_ENCODING_HUFFMAN) {
1931 if (UNLIKELY(written < 0))
return written;
1935 ZXC_MEMCPY(p_curr, buf_tokens, seq_c);
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];
1956 for (; i < seq_c; i++) {
1957 *p_curr++ = (uint8_t)buf_offsets[i];
1961#ifdef ZXC_BIG_ENDIAN
1962 for (uint32_t i = 0; i < seq_c; i++) {
1964 p_curr +=
sizeof(uint16_t);
1967 ZXC_MEMCPY(p_curr, buf_offsets, seq_c *
sizeof(uint16_t));
1968 p_curr += seq_c *
sizeof(uint16_t);
1978 const size_t behind_lit = sz_tok + sz_off + sz_ext;
1983 ZXC_MEMCPY(p_curr, buf_extras, extras_sz);
1984 p_curr += extras_sz;
1986 ZXC_MEMSET(p_curr, 0, pad);
1991 if (UNLIKELY(hw < 0))
return hw;
2018 const size_t src_sz, uint8_t* RESTRICT dst,
const size_t dst_cap,
2019 size_t* RESTRICT
const out_sz) {
2022 const int level = ctx->compression_level;
2023 const size_t dict_sz = ctx->dict_size;
2031 if (UNLIKELY(ctx->epoch >= ctx->max_epoch)) {
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;
2041 zxc_lz_seed_dict(src, dict_sz, ctx->hash_table, ctx->hash_tags, ctx->chain_table,
2042 epoch_mark, offset_mask, level);
2044 const uint8_t* ip = src + dict_sz;
2045 const uint8_t* iend = src + src_sz;
2046 const uint8_t* anchor = ip;
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;
2057 size_t extras_c = 0;
2060 while (LIKELY(ip < search_limit)) {
2061 size_t dist = (size_t)(ip - anchor);
2063 if (UNLIKELY(ip + step >= search_limit)) step = 1;
2067 if (LIKELY(ip + step +
sizeof(uint64_t) <= iend)) {
2068 const uint64_t v_next =
zxc_le64(ip + step);
2071 ZXC_PREFETCH_READ(&hash_tags[h_next]);
2072 ZXC_PREFETCH_READ(&hash_table[h_next]);
2075 const zxc_match_t m =
2077 chain_table, epoch_mark, offset_mask, level, lzp,
2082 const uint32_t ll = (uint32_t)(ip - anchor);
2084 const uint32_t off = (uint32_t)(ip - m.ref);
2093 buf_sequences[seq_c] = seq_val;
2107 const size_t last_lits = (size_t)(iend - anchor);
2108 if (last_lits > 0) {
2109 ZXC_MEMCPY(literals + lit_c, anchor, last_lits);
2118 .n_literals = (uint32_t)lit_c,
2119 .enc_lit = ZXC_SECTION_ENCODING_RAW,
2125 if (UNLIKELY(ghs < 0))
return ghs;
2127 uint8_t* p_curr = p + ghs;
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;
2136 const size_t behind_lit = sz_seq + sz_ext;
2141 ZXC_MEMCPY(p_curr, literals, lit_c);
2145#ifdef ZXC_BIG_ENDIAN
2146 for (uint32_t i = 0; i < seq_c; i++) {
2148 p_curr +=
sizeof(uint32_t);
2151 ZXC_MEMCPY(p_curr, buf_sequences, sz_seq);
2156 ZXC_MEMCPY(p_curr, buf_extras, sz_ext);
2159 ZXC_MEMSET(p_curr, 0, pad);
2164 if (UNLIKELY(hw < 0))
return hw;
2187 uint8_t* RESTRICT
const dst,
const size_t dst_cap,
2188 size_t* RESTRICT
const out_sz) {
2198 if (UNLIKELY(hw < 0))
return hw;
2218 const size_t src_sz, uint8_t* RESTRICT dst,
const size_t dst_cap,
2219 const uint64_t block_index) {
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;
2228 if (ctx->compression_level <= 2)
2234 if (UNLIKELY(res !=
ZXC_OK || w >= block_sz)) {
2236 if (UNLIKELY(res !=
ZXC_OK))
return res;
2239 if (ctx->checksum_enabled) {
@ ZXC_ERROR_OVERFLOW
Buffer overflow detected during processing.
@ ZXC_ERROR_DST_TOO_SMALL
Destination buffer too small.
@ ZXC_OK
Success (no error).
#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.
Compression / decompression context.
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.
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.
int zxc_write_ghi_header(uint8_t *dst, const size_t rem, const zxc_gnr_header_t *gh)
Serialises a GHI block header.
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.