Coverage Report

Created: 2026-09-01 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/zxc/src/lib/zxc_internal.h
Line
Count
Source
1
/*
2
 * ZXC - High-performance lossless compression
3
 *
4
 * Copyright (c) 2025-2026 Bertrand Lebonnois and contributors.
5
 * SPDX-License-Identifier: BSD-3-Clause
6
 */
7
8
/**
9
 * @file zxc_internal.h
10
 * @brief Internal definitions, constants, SIMD helpers, and utility functions.
11
 *
12
 * This header is **not** part of the public API.  It is shared across the
13
 * library's translation units and contains:
14
 * - Platform detection and SIMD intrinsic includes.
15
 * - Compiler-abstraction macros (LIKELY, PREFETCH, MEMCPY, ALIGN, ...).
16
 * - Endianness detection and byte-swap helpers.
17
 * - File-format constants (magic word, header sizes, block sizes, ...).
18
 * - Inline helpers for hashing, endian-safe loads/stores, bit manipulation,
19
 *   aligned allocation, and bitstream reading.
20
 * - Internal function prototypes for chunk-level compression/decompression.
21
 *
22
 * @warning Do not include this header from user code; use the public headers
23
 *          zxc_buffer.h or zxc_stream.h instead.
24
 */
25
26
#ifndef ZXC_INTERNAL_H
27
#define ZXC_INTERNAL_H
28
29
#include "zxc_deps.h" /* libc deps: <limits.h>, <stdint.h>, <stdlib.h>, <string.h>,
30
                        and the ZXC_MALLOC / ZXC_ALIGNED_MALLOC macros.
31
                        Vendor this file to retarget non-libc environments. */
32
33
#include "../../include/zxc_buffer.h"
34
#include "../../include/zxc_constants.h"
35
#include "../../include/zxc_error.h"
36
#include "../../include/zxc_seekable.h"
37
#include "rapidhash.h"
38
39
#ifdef __cplusplus
40
extern "C" {
41
#endif
42
43
/**
44
 * @defgroup internal Internal Helpers
45
 * @brief Platform abstractions, constants, and utility functions (private).
46
 * @{
47
 */
48
49
/**
50
 * @name Atomic Qualifier
51
 * @brief Provides a portable atomic / volatile qualifier.
52
 *
53
 * If C11 atomics are available, @c ZXC_ATOMIC expands to @c _Atomic;
54
 * otherwise it falls back to @c volatile.
55
 * @{
56
 */
57
#if !defined(__cplusplus) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \
58
    !defined(__STDC_NO_ATOMICS__)
59
#include <stdatomic.h>
60
#define ZXC_ATOMIC _Atomic
61
#define ZXC_USE_C11_ATOMICS 1
62
#else
63
#define ZXC_ATOMIC volatile
64
#define ZXC_USE_C11_ATOMICS 0
65
#endif
66
/** @} */ /* end of Atomic Qualifier */
67
68
/**
69
 * @name SIMD Intrinsics & Compiler Macros
70
 * @brief Auto-detected SIMD feature macros for x86 (SSE/AVX) and ARM (NEON).
71
 *
72
 * Depending on the target architecture and compiler flags the following macros
73
 * may be defined:
74
 * - @c ZXC_USE_AVX512 - AVX-512F + AVX-512BW available.
75
 * - @c ZXC_USE_AVX2   - AVX2 available.
76
 * - @c ZXC_USE_SSE2   - SSE2 (x86-64 baseline) available.
77
 * - @c ZXC_USE_NEON64 - AArch64 NEON available.
78
 * - @c ZXC_USE_NEON32 - ARMv7 NEON available.
79
 *
80
 * Note: @c -mavx2 / @c -mavx512f imply @c __SSE2__, so @c ZXC_USE_SSE2 is
81
 * also defined in the AVX variants. The hand-written SIMD code paths therefore
82
 * order their preprocessor branches AVX512 -> AVX2 -> SSE2 so the widest
83
 * available path wins; the SSE2 branch is the active one in the @c _default
84
 * variant on x86-64 (no AVX2/AVX512 flags). SSE2 is the x86-64 baseline, so no
85
 * dedicated @c _sse2 variant exists: @c _default covers every 64-bit x86 CPU
86
 * (and i686 with @c -msse2). The handful of
87
 * operations that would otherwise require SSE4.1 (@c _mm_max_epu32,
88
 * @c _mm_blendv_epi8, @c _mm_packus_epi32) or SSSE3 (@c _mm_shuffle_epi8) are
89
 * emulated with pure SSE2 instruction sequences or fall back to scalar code.
90
 *
91
 * Define @c ZXC_DISABLE_SIMD to gate all hand-written SIMD paths (intrinsics,
92
 * inline assembly).  Compiler auto-vectorisation is unaffected.
93
 * @{
94
 */
95
#ifndef ZXC_DISABLE_SIMD
96
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386__) || defined(_M_IX86)
97
#include <immintrin.h>
98
#include <nmmintrin.h>
99
#if defined(__AVX512F__) && defined(__AVX512BW__)
100
#ifndef ZXC_USE_AVX512
101
#define ZXC_USE_AVX512
102
#endif
103
#endif
104
#if defined(__AVX2__)
105
#ifndef ZXC_USE_AVX2
106
#define ZXC_USE_AVX2
107
#endif
108
#endif
109
#if defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)
110
#ifndef ZXC_USE_SSE2
111
#define ZXC_USE_SSE2
112
#endif
113
#endif
114
#elif (defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64) || \
115
       defined(ZXC_USE_NEON32) || defined(ZXC_USE_NEON64))
116
#if !defined(_MSC_VER)
117
#include <arm_acle.h>
118
#endif
119
#include <arm_neon.h>
120
#if defined(__aarch64__) || defined(_M_ARM64)
121
#ifndef ZXC_USE_NEON64
122
#define ZXC_USE_NEON64
123
#endif
124
#else
125
#ifndef ZXC_USE_NEON32
126
#define ZXC_USE_NEON32
127
#endif
128
#endif
129
#endif
130
#endif    /* ZXC_DISABLE_SIMD */
131
/** @} */ /* end of SIMD Intrinsics */
132
133
/**
134
 * @name Compiler Abstractions
135
 * @brief Portable wrappers for branch hints, prefetch, memory ops, alignment,
136
 *        and forced inlining.
137
 * @{
138
 */
139
140
#if defined(__GNUC__) || defined(__clang__)
141
/** @def LIKELY
142
 * @brief Branch prediction hint: expression is likely true.
143
 * @param x Expression to evaluate.
144
 */
145
268M
#define LIKELY(x) (__builtin_expect(!!(x), 1))
146
147
/** @def UNLIKELY
148
 * @brief Branch prediction hint: expression is unlikely to be true.
149
 * @param x Expression to evaluate.
150
 */
151
28.1G
#define UNLIKELY(x) (__builtin_expect(!!(x), 0))
152
153
/** @def RESTRICT
154
 * @brief Pointer aliasing hint (maps to __restrict__).
155
 */
156
#define RESTRICT __restrict__
157
158
/** @def ZXC_PREFETCH_READ
159
 * @brief Prefetch data for reading.
160
 * @param ptr Pointer to data to prefetch.
161
 */
162
10.7G
#define ZXC_PREFETCH_READ(ptr) __builtin_prefetch((const void*)(ptr), 0, 3)
163
164
/** @def ZXC_MEMCPY
165
 * @brief Optimized memory copy using compiler built-in.
166
 */
167
42.3G
#define ZXC_MEMCPY(dst, src, n) __builtin_memcpy(dst, src, n)
168
169
/** @def ZXC_MEMSET
170
 * @brief Optimized memory set using compiler built-in.
171
 */
172
3.00M
#define ZXC_MEMSET(dst, val, n) __builtin_memset(dst, val, n)
173
174
/** @def ZXC_ALIGN
175
 * @brief Specifies memory alignment for a variable or structure.
176
 * @param x Alignment boundary in bytes (must be a power of 2).
177
 */
178
#define ZXC_ALIGN(x) __attribute__((aligned(x)))
179
180
/** @def ZXC_ALWAYS_INLINE
181
 * @brief Forces a function to be inlined at all optimization levels.
182
 */
183
#define ZXC_ALWAYS_INLINE inline __attribute__((always_inline))
184
185
/** @def ZXC_NOINLINE
186
 * @brief Prevents a function from being inlined into its callers.
187
 */
188
#define ZXC_NOINLINE __attribute__((noinline))
189
190
/** @def ZXC_COLD
191
 * @brief Marks a function as rarely executed: optimized for size and placed
192
 *        in a cold text section, away from the hot paths' i-cache footprint.
193
 *        No MSVC equivalent -- expands to nothing there (noinline still keeps
194
 *        the body out of the caller).
195
 */
196
#define ZXC_COLD __attribute__((cold))
197
198
#elif defined(_MSC_VER)
199
#include <intrin.h>
200
#if defined(_M_IX86) || defined(_M_X64) || defined(_M_AMD64)
201
#include <xmmintrin.h>
202
#define ZXC_PREFETCH_READ(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0)
203
#else
204
#define ZXC_PREFETCH_READ(ptr) __prefetch((const void*)(ptr))
205
#endif
206
#define LIKELY(x) (x)
207
#define UNLIKELY(x) (x)
208
#define RESTRICT __restrict
209
#pragma intrinsic(memcpy, memset)
210
#define ZXC_MEMCPY(dst, src, n) memcpy(dst, src, n)
211
#define ZXC_MEMSET(dst, val, n) memset(dst, val, n)
212
213
/** @def ZXC_ALIGN
214
 * @brief Specifies memory alignment for a variable or structure (MSVC).
215
 * @param x Alignment boundary in bytes (must be a power of 2).
216
 */
217
#define ZXC_ALIGN(x) __declspec(align(x))
218
219
/** @def ZXC_ALWAYS_INLINE
220
 * @brief Forces a function to be inlined at all optimization levels (MSVC).
221
 */
222
#define ZXC_ALWAYS_INLINE __forceinline
223
224
/** @def ZXC_NOINLINE
225
 * @brief Prevents a function from being inlined into its callers (MSVC).
226
 */
227
#define ZXC_NOINLINE __declspec(noinline)
228
/** @copydoc ZXC_COLD */
229
#define ZXC_COLD
230
#pragma intrinsic(_BitScanReverse)
231
#else
232
#define LIKELY(x) (x)
233
#define UNLIKELY(x) (x)
234
#define RESTRICT
235
#define ZXC_PREFETCH_READ(ptr)
236
#define ZXC_MEMCPY(dst, src, n) memcpy(dst, src, n)
237
#define ZXC_MEMSET(dst, val, n) memset(dst, val, n)
238
239
/** @def ZXC_ALWAYS_INLINE
240
 * @brief Forces a function to be inlined (fallback for non-GCC/Clang/MSVC compilers).
241
 */
242
#define ZXC_ALWAYS_INLINE inline
243
244
/** @def ZXC_NOINLINE
245
 * @brief Prevents inlining (best-effort no-op fallback for unknown compilers).
246
 */
247
#define ZXC_NOINLINE
248
#define ZXC_COLD
249
250
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
251
#include <stdalign.h>
252
/** @def ZXC_ALIGN
253
 * @brief Specifies memory alignment using C11 _Alignas.
254
 * @param x Alignment boundary in bytes (must be a power of 2).
255
 */
256
#define ZXC_ALIGN(x) _Alignas(x)
257
#else
258
/** @def ZXC_ALIGN
259
 * @brief No-op alignment macro for compilers without alignment support.
260
 * @param x Ignored (alignment not supported).
261
 */
262
#define ZXC_ALIGN(x)
263
#endif
264
#endif
265
/** @} */ /* end of Compiler Abstractions */
266
267
// Heap allocator and cache-line-aligned allocator macros are now defined
268
// in @c zxc_deps.h (included at the top of this header), so non-libc
269
// targets can override them by vendoring that single file.
270
271
/**
272
 * @name Endianness Detection
273
 * @brief Compile-time detection of host byte order.
274
 *
275
 * Defines exactly one of @c ZXC_LITTLE_ENDIAN or @c ZXC_BIG_ENDIAN.
276
 * @{
277
 */
278
#ifndef ZXC_LITTLE_ENDIAN
279
#if defined(_WIN32) || defined(__LITTLE_ENDIAN__) || \
280
    (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
281
#define ZXC_LITTLE_ENDIAN
282
#elif defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
283
#define ZXC_BIG_ENDIAN
284
#else
285
#warning "Endianness not detected, defaulting to little-endian"
286
#define ZXC_LITTLE_ENDIAN
287
#endif
288
#endif
289
/** @} */ /* end of Endianness Detection */
290
291
/**
292
 * @name Byte-Swap Helpers
293
 * @brief 16/32/64-bit byte-swap macros (only defined under @c ZXC_BIG_ENDIAN).
294
 * @{
295
 */
296
#ifdef ZXC_BIG_ENDIAN
297
#if defined(__GNUC__) || defined(__clang__)
298
#define ZXC_BSWAP16(x) __builtin_bswap16(x)
299
#define ZXC_BSWAP32(x) __builtin_bswap32(x)
300
#define ZXC_BSWAP64(x) __builtin_bswap64(x)
301
#elif defined(_MSC_VER)
302
#define ZXC_BSWAP16(x) _byteswap_ushort(x)
303
#define ZXC_BSWAP32(x) _byteswap_ulong(x)
304
#define ZXC_BSWAP64(x) _byteswap_uint64(x)
305
#else
306
#define ZXC_BSWAP16(x) ((uint16_t)(((x) >> 8) | ((x) << 8)))
307
#define ZXC_BSWAP32(x) \
308
    ((uint32_t)(((x) >> 24) | (((x) >> 8) & 0xFF00) | (((x) << 8) & 0xFF0000) | ((x) << 24)))
309
#define ZXC_BSWAP64(x) \
310
    ((uint64_t)(((uint64_t)ZXC_BSWAP32((uint32_t)(x)) << 32) | ZXC_BSWAP32((uint32_t)((x) >> 32))))
311
#endif
312
#endif
313
/** @} */ /* end of Byte-Swap Helpers */
314
315
/**
316
 * @name File Format Constants
317
 * @brief Magic words, header sizes, block sizes, and related constants.
318
 * @{
319
 */
320
321
/** @brief Magic word identifying ZXC files (little-endian 0x9CB02EF5). */
322
34.9k
#define ZXC_MAGIC_WORD 0x9CB02EF5U
323
/** @brief Current on-disk file format version. The decoder accepts only this
324
 *  version; Older versions are rejected with ZXC_ERROR_BAD_VERSION. */
325
34.9k
#define ZXC_FILE_FORMAT_VERSION 8
326
327
/** @brief Safety padding appended to buffers to tolerate overruns. */
328
649k
#define ZXC_PAD_SIZE 32
329
/**
330
 * @brief Readable bytes a GLO/GHI payload guarantees after its literal section.
331
 *
332
 * A wire guarantee, not a buffer allowance - hence separate from
333
 * @ref ZXC_PAD_SIZE even though the values match. RAW literals point into the
334
 * caller's buffer and @ref zxc_decode_copy_literals overshoots by up to 31 B,
335
 * so it needs readable bytes behind it.
336
 *
337
 * The following sections usually supply them for free (tokens + offsets clear
338
 * 32 B from 16 sequences on); the encoder pads only the shortfall, inside the
339
 * extras section. Extras are read on demand, so an over-long end pointer is
340
 * harmless - no wire field, nothing to validate about the padding.
341
 */
342
170k
#define ZXC_BLOCK_LIT_SLACK 32
343
/**
344
 * @brief Tail padding required on the decompression destination buffer.
345
 *
346
 * The decoder's fast path uses speculative wild-copy writes and gates
347
 * fast-loop entry on @c d_end - ZXC_DECOMPRESS_TAIL_PAD. Sizing
348
 * @c dst_capacity to @c uncompressed_size + ZXC_DECOMPRESS_TAIL_PAD
349
 * guarantees the fast path is reachable and that tail bounds checks
350
 * never spuriously reject the last literals of a valid block.
351
 *
352
 * @see zxc_decompress_block_bound()
353
 */
354
264k
#define ZXC_DECOMPRESS_TAIL_PAD (ZXC_PAD_SIZE * 66)
355
/** @brief Assumed CPU cache line size for alignment. */
356
2.70M
#define ZXC_CACHE_LINE_SIZE 64
357
/** @brief Bitmask for cache-line alignment checks. */
358
2.70M
#define ZXC_ALIGNMENT_MASK (ZXC_CACHE_LINE_SIZE - 1)
359
/** @brief Round @p x up to the next cache-line boundary. */
360
1.35M
#define ZXC_ALIGN_CL(x) (((x) + ZXC_ALIGNMENT_MASK) & ~(size_t)ZXC_ALIGNMENT_MASK)
361
362
/**
363
 * @brief Number of @c uint64_t words needed to hold a bitmap of @p n_bits.
364
 *
365
 * Equivalent to @c ceil(n_bits / 64).
366
 */
367
83.9k
#define ZXC_BITMAP_WORDS(n_bits) (((n_bits) + 63) / 64)
368
369
/** @brief Bit flag in the Flags byte indicating checksum presence (bit 7). */
370
63.5k
#define ZXC_FILE_FLAG_HAS_CHECKSUM 0x80U
371
/** @brief Bit flag in the Flags byte indicating a dictionary is required (bit 6). */
372
48.3k
#define ZXC_FILE_FLAG_HAS_DICTIONARY 0x40U
373
/** @brief Mask for the checksum algorithm id (bits 0-3). */
374
#define ZXC_FILE_CHECKSUM_ALGO_MASK 0x0FU
375
376
/** @brief Magic word identifying ZXC dictionary files (.zxd). */
377
18.0k
#define ZXC_DICT_MAGIC 0x9CB0D1C7U
378
/** @brief Current dictionary file format version. A 128-byte packed Huffman
379
 *         code-lengths table (shared literal table) always follows the
380
 *         dictionary content. */
381
18.0k
#define ZXC_DICT_VERSION 1
382
/** @brief K-gram length scanned by the dictionary trainer. Aligned on the LZ
383
 *         minimum match length so trained patterns are matchable at encode time. */
384
47.3G
#define ZXC_DICT_KGRAM_LEN ZXC_LZ_MIN_MATCH_LEN
385
/** @brief Address bits for the dictionary trainer's k-gram frequency table. */
386
23.8G
#define ZXC_DICT_HASH_BITS 16
387
/** @brief Maximum number of candidate segments the dictionary trainer keeps. */
388
9.00k
#define ZXC_DICT_MAX_SEGMENTS (1U << 16)
389
/** @brief Target number of sampled k-gram positions for the trainer's frequency
390
 *  estimate. Bounds the count so 16-bit counters stay unsaturated on large
391
 *  corpora; the trainer strides the corpus to hit roughly this many positions. */
392
9.00k
#define ZXC_DICT_SAMPLE_TARGET (1U << 19)
393
/** @brief Number of buckets in the dictionary trainer's frequency table. */
394
#define ZXC_DICT_HASH_SIZE (1U << ZXC_DICT_HASH_BITS)
395
/** @brief Training block size for the shared-table literal statistics. */
396
207k
#define ZXC_DICT_HUF_TRAIN_BLOCK 4096U
397
/** @brief Cap on the corpus bytes compressed by the literal-table trainer: the
398
 *         histogram converges early, so past it slices are strided evenly instead. */
399
9.00k
#define ZXC_DICT_HUF_SAMPLE_BUDGET (8U << 20)
400
401
/** @brief Block header size: Type(1)+Flags(1)+Reserved(1)+Checksum(1)+CompSize(4). */
402
1.24M
#define ZXC_BLOCK_HEADER_SIZE 8
403
/** @brief Size of the per-block checksum field in bytes. */
404
115k
#define ZXC_BLOCK_CHECKSUM_SIZE 4
405
/** @brief Binary size of a GLO block sub-header. */
406
377k
#define ZXC_GLO_HEADER_BINARY_SIZE 12
407
/** @brief Binary size of a GHI block sub-header. */
408
37.4k
#define ZXC_GHI_HEADER_BINARY_SIZE 12
409
410
/** @brief Worst-case format overhead inside a single block beyond the outer
411
 *  8-byte block header and the optional 4-byte checksum.
412
 *
413
 *  Sub-header (12 B) + widest GLO section descriptors (8 B) + widest slack padding
414
 *  (@ref ZXC_BLOCK_LIT_SLACK) = 52 B, plus the customary 16 B of margin for
415
 *  future format evolution. Used by zxc_compress_block_bound() and
416
 *  zxc_compress_bound().
417
 */
418
53.4k
#define ZXC_BLOCK_FORMAT_OVERHEAD 68
419
420
/** @brief Checksum algorithm id for RapidHash (default, sole implementation). */
421
27.8k
#define ZXC_CHECKSUM_RAPIDHASH 0
422
423
/** @brief Size of the global checksum appended after EOF block (4 bytes). */
424
9.68k
#define ZXC_GLOBAL_CHECKSUM_SIZE 4
425
426
/** @name Seekable Format Constants
427
 *  @brief Seek table block appended between EOF block and footer.
428
 *
429
 *  The seek table is optional (opt-in at compression time) and allows
430
 *  random-access decompression by recording per-block compressed and
431
 *  decompressed sizes.  It uses a standard ZXC block header with
432
 *  @c block_type = @c ZXC_BLOCK_SEK.
433
 *
434
 *  Detection from the end of the file: the reader derives @c num_blocks
435
 *  from the file footer (total decompressed size) and file header (block size).
436
 *  It then seeks backward to validate the SEK block header.
437
 *  @{ */
438
/** @brief Per-block entry size: comp_size(4) only.  decomp_size is derived
439
 *  from the file header's block_size (all blocks except the last are full). */
440
72.3k
#define ZXC_SEEK_ENTRY_SIZE 4
441
/** @} */ /* end of Seekable Format Constants */
442
443
/** @name GLO Token Constants
444
 *  @brief 4-bit literal length / 4-bit match length / 16-bit offset.
445
 *  @{ */
446
/** @brief Bits for Literal Length in a GLO token. */
447
32.5M
#define ZXC_TOKEN_LIT_BITS 4
448
/** @brief Bits for Match Length in a GLO token. */
449
649M
#define ZXC_TOKEN_ML_BITS 4
450
/** @brief Mask to extract Literal Length from a GLO token. */
451
17.0M
#define ZXC_TOKEN_LL_MASK ((1U << ZXC_TOKEN_LIT_BITS) - 1)
452
/** @brief Mask to extract Match Length from a GLO token. */
453
649M
#define ZXC_TOKEN_ML_MASK ((1U << ZXC_TOKEN_ML_BITS) - 1)
454
/** @} */
455
456
/** @name GHI Sequence Constants
457
 *  @brief 8-bit literal length / 8-bit match length / 16-bit offset.
458
 *  @{ */
459
/** @brief Bits for Literal Length in a GHI sequence. */
460
7.30M
#define ZXC_SEQ_LL_BITS 8
461
/** @brief Bits for Match Length in a GHI sequence. */
462
14.5M
#define ZXC_SEQ_ML_BITS 8
463
/** @brief Bits for Offset in a GHI sequence. */
464
21.7M
#define ZXC_SEQ_OFF_BITS 16
465
/** @brief Mask to extract Literal Length from a GHI sequence. */
466
7.30M
#define ZXC_SEQ_LL_MASK ((1U << ZXC_SEQ_LL_BITS) - 1)
467
/** @brief Mask to extract Match Length from a GHI sequence. */
468
7.26M
#define ZXC_SEQ_ML_MASK ((1U << ZXC_SEQ_ML_BITS) - 1)
469
/** @brief Mask to extract Offset from a GHI sequence. */
470
7.26M
#define ZXC_SEQ_OFF_MASK ((1U << ZXC_SEQ_OFF_BITS) - 1)
471
/** @} */
472
473
/** @name Literal Stream Encoding
474
 *  @{ */
475
/** @brief Flag bit indicating an RLE run in the literal stream (0x80). */
476
2.40M
#define ZXC_LIT_RLE_FLAG 0x80U
477
/** @brief Mask to extract the run/literal length (lower 7 bits). */
478
1.44M
#define ZXC_LIT_LEN_MASK (ZXC_LIT_RLE_FLAG - 1)
479
/** @} */
480
481
/** @name LZ77 Constants
482
 *  @brief Hash table geometry, sliding window, and match parameters.
483
 *
484
 *  The hash table uses a split layout with 15-bit addressing (32 768 buckets):
485
 *  - `hash_table[]`: uint32_t, stores `(epoch << offset_bits) | position` (128 KB).
486
 *  - `hash_tags[]`:      uint8_t, stores an 8-bit tag for fast rejection (32 KB).
487
 *  Total: 160 KB.  The tag table fits in L1 cache, enabling a
488
 *  "filter-first" access pattern that avoids cold loads into hash_table
489
 *  on the ~60-75% of lookups where the tag mismatches.
490
 *  The 64 KB sliding window allows `chain_table` to use `uint16_t`.
491
 *  @{ */
492
/** @brief Address bits for the LZ77 hash table (2^15 = 32 768 buckets). */
493
1.26G
#define ZXC_LZ_HASH_BITS 15
494
/** @brief Marsaglia multiplicative hash constant for 4-byte hashing. */
495
23.9G
#define ZXC_LZ_HASH_PRIME1 0x2D35182DU
496
/** @brief Marsaglia/Vigna xorshift* multiplier for 5-byte hashing. */
497
1.19G
#define ZXC_LZ_HASH_PRIME2 0x2545F4914F6CDD1DULL
498
/** @brief Maximum number of entries in the hash table. */
499
175k
#define ZXC_LZ_HASH_SIZE (1U << ZXC_LZ_HASH_BITS)
500
/** @brief Sliding window size (64 KB). */
501
13.5G
#define ZXC_LZ_WINDOW_SIZE (1U << 16)
502
/** @brief Mask for ring-buffer indexing into chain_table (power-of-two window). */
503
11.9G
#define ZXC_LZ_WINDOW_MASK (ZXC_LZ_WINDOW_SIZE - 1U)
504
/** @brief Minimum match length for an LZ77 match. */
505
48.6G
#define ZXC_LZ_MIN_MATCH_LEN 5
506
/** @brief Maximum legitimate value a varint can decode to.
507
 *
508
 * A varint value represents (ll - MASK) or (ml - MASK) and is therefore always
509
 * strictly less than ZXC_BLOCK_SIZE_MAX (enforced by the Block API entry
510
 * points). The cap is set to (ZXC_BLOCK_SIZE_MAX - 1), which fits cleanly in a
511
 * 3-byte varint (21 bits): the decoder rejects any 4- or 5-byte encoding, and
512
 * the encoder refuses to emit values above this bound. Together they bound the
513
 * varint surface to exactly the format-defined block size limit. */
514
#define ZXC_MAX_VARINT_VALUE ((uint32_t)(ZXC_BLOCK_SIZE_MAX - 1U))
515
/** @brief Maximum decoded output of one sequence with inline ll/ml, used by the
516
 *         4x bounds checks to reserve the rest of a batch.
517
 *
518
 *         Keep it small - the loop margins scale with it. Widening it to 543
519
 *         once cost 2 percent of decode on silesia. */
520
#define ZXC_GLO_MAX_INLINE_OUT_PER_SEQ ((ZXC_TOKEN_LL_MASK - 1U) + ZXC_GLO_MAX_INLINE_ML) /* 33 */
521
/** @brief Longest match a GLO sequence carries without a varint extension.
522
 *
523
 * Below @ref ZXC_PAD_SIZE, so the inline path needs no length ladder: one
524
 * 32-byte store covers it. The escape path always yields more, so comparing
525
 * against this recovers "was the ml nibble inline". */
526
#define ZXC_GLO_MAX_INLINE_ML ((ZXC_TOKEN_ML_MASK - 1U) + ZXC_LZ_MIN_MATCH_LEN) /* 19 */
527
#define ZXC_GHI_MAX_INLINE_OUT_PER_SEQ \
528
    ((ZXC_SEQ_LL_MASK - 1U) + (ZXC_SEQ_ML_MASK - 1U) + ZXC_LZ_MIN_MATCH_LEN) /* 513 */
529
/** @brief Base bias added to encoded offsets (stored = actual - bias). */
530
198M
#define ZXC_LZ_OFFSET_BIAS 1
531
/** @brief Maximum allowed offset distance. */
532
779M
#define ZXC_LZ_MAX_DIST (ZXC_LZ_WINDOW_SIZE - 1)
533
534
/** @brief Match distance floor the encoder holds to at levels 1 to 5, sized to
535
 *         the decoder's widest match-copy arm.
536
 *
537
 *  Applied per block, and only where @ref ZXC_LZ_MINDIST_MAX_SHORT_PCT clears
538
 *  it; levels 6 and 7 keep every distance. Encoder policy: no format bit moves,
539
 *  so any decoder of the same format version still reads the result. */
540
1.24M
#define ZXC_LZ_MINDIST 32
541
542
/** @brief Probe sampling: one position per KB, clamped.
543
 *
544
 *  Proportional on purpose: a fixed count costs the same on a 4 KB block as on
545
 *  a 512 KB one, which measured as a third of the compression time on small,
546
 *  highly compressible inputs. */
547
25.3k
#define ZXC_LZ_MINDIST_PROBE_PER_KB 1024
548
42.4k
#define ZXC_LZ_MINDIST_PROBE_MIN 16
549
30.9k
#define ZXC_LZ_MINDIST_PROBE_MAX 64
550
551
/** @brief Short-distance hit rate, in percent, above which a block keeps its
552
 *         short match distances.
553
 *
554
 *  Measured: natural text 2-5 %, XML around 25 %, JSON 50-60 %, periodic data
555
 *  100 %. Decode gains follow the same order, so the cut sits just above text
556
 *  and below everything that measured neutral or worse. */
557
25.3k
#define ZXC_LZ_MINDIST_MAX_SHORT_PCT 20
558
/** @brief Bytes at the block end where match search stops (left as literals).
559
 *  Equals the 8-byte word the finder reads at each probe, so @c ip+8<=iend. */
560
199k
#define ZXC_LZ_SEARCH_MARGIN (sizeof(uint64_t))
561
/** @} */
562
563
/** @name Optimal Parser Tuning (level >= 6)
564
 *  @brief Static prices and complexity guards used by the level-6 optimal
565
 *         LZ77 parser DP.
566
 *  @{ */
567
/** @brief Static price (bits) of a match token before varint extras: 1 byte
568
 *         token + 2 byte offset. */
569
630M
#define ZXC_OPT_MATCH_COST_BASE ((uint32_t)(3U * CHAR_BIT))
570
/** @brief Threshold above which `find_best_match` is skipped at intra-match
571
 *         positions, keeping the parser O(N) on highly repetitive data. */
572
#define ZXC_OPT_LONG_MATCH_SKIP ((size_t)256)
573
/** @brief Minimum literal count for the sample-based Huffman cost estimator
574
 *         used by the optimal parser. Below this, the strided sample is too
575
 *         small for the resulting code-lengths to be statistically reliable,
576
 *         so the estimator falls back to RAW cost (8 bits/byte). */
577
#define ZXC_OPT_LIT_SAMPLE_MIN 1024
578
579
/** @} */
580
581
/** @name Hash Prime Constants
582
 *  @brief Mixing primes used by internal hash functions.
583
 *  @{ */
584
/** @brief Hash prime 1. */
585
313k
#define ZXC_HASH_PRIME1 0x9E3779B97F4A7C15ULL
586
/** @brief Hash prime 2. */
587
122k
#define ZXC_HASH_PRIME2 0xD2D84A61D2D84A61ULL
588
/** @} */
589
590
/** @name Huffman Codec Constants
591
 *  @brief Length-limited canonical Huffman codec for GLO literal sections
592
 *         (level >= 6) and level-7 token sections, in the PivCo layout.
593
 *
594
 *  On-disk section payload layout (FORMAT.md section 5.2.1):
595
 *  - @c ZXC_HUF_TABLE_SIZE bytes: @c ZXC_HUF_NUM_SYMBOLS code lengths
596
 *    packed two per byte (4 bits each). The same packed table is used as the
597
 *    per-block lengths header (enc_lit=2) and as the shared table carried by
598
 *    a .zxd dictionary (enc_lit=3, header omitted) -- hence the public
599
 *    constant.
600
 *  - Node runs: for every emitting node of the canonical code tree in BFS
601
 *    order, its branch bits (or packed D-bit residuals for flat subtree
602
 *    roots), LSB-first, each run padded to a byte boundary. All run sizes
603
 *    are derived (root count + popcounts), never stored.
604
 *  @{ */
605
/** @brief Maximum Huffman code length, in bits: the ceiling the decoder's
606
 *         on-wire validation accepts (and the level-7 encoder cap). Bounds
607
 *         the tree depth, hence the merge-level count and every stack array
608
 *         sized off the code length. The encoder caps codes per level via
609
 *         ::zxc_huf_enc_max_code_len. */
610
6.83M
#define ZXC_HUF_MAX_CODE_LEN_ULTRA 11
611
/** @brief Encoder code-length cap for levels up to ::ZXC_LEVEL_DENSITY (below
612
 *         ::ZXC_LEVEL_ULTRA): shallow 8-bit trees decode fastest (fewer merge
613
 *         levels, denser flat subtrees) -- this cap is the de-facto speed
614
 *         governor of level 6. */
615
105k
#define ZXC_HUF_MAX_CODE_LEN_DENSITY 8
616
/** @brief Alphabet size: one entry per possible byte value. */
617
313M
#define ZXC_HUF_NUM_SYMBOLS 256
618
619
/** @brief Upper bound on PivCo tree nodes (full binary tree over the alphabet). */
620
#define ZXC_PIVCO_MAX_NODES (2 * ZXC_HUF_NUM_SYMBOLS - 1)
621
622
/** @brief One PivCo Huffman tree node. */
623
typedef struct {
624
    int16_t child[2]; /* node index, -1 = absent */
625
    int16_t sym;      /* >= 0: leaf symbol; -1: internal */
626
} zxc_pivco_node_t;
627
628
/**
629
 * @brief Canonical Huffman tree in PivCo (level-ordered) form.
630
 *
631
 * Derived deterministically from the 128-byte packed code lengths by
632
 * zxc_huf_dict_tree_build / the section decoders; pure value type (index-based,
633
 * no internal pointers), safe to copy or embed. Embedded in ::zxc_cctx_t so a
634
 * dictionary's shared table is built ONCE at attach instead of per block.
635
 */
636
typedef struct {
637
    zxc_pivco_node_t nd[ZXC_PIVCO_MAX_NODES];
638
    int16_t bfs[ZXC_PIVCO_MAX_NODES]; /* node ids in BFS (== wire) order */
639
    int16_t lvl_start[ZXC_HUF_MAX_CODE_LEN_ULTRA + 2];
640
    int n_nodes;
641
    int max_depth;
642
    // Flat-subtree fast path: flat_d[nid] = D (>= 2) when nid roots a MAXIMAL
643
    // complete subtree with all leaves exactly D levels down. Its wire run is
644
    // the symbols' packed D-bit residuals instead of D partition bitmaps (same
645
    // bits, decode = unpack+lookup), and covered[nid] marks its strict
646
    // descendants, absent from the wire. Both sides derive this from the code
647
    // lengths, so nothing is signalled.
648
    uint8_t flat_d[ZXC_PIVCO_MAX_NODES];
649
    uint8_t covered[ZXC_PIVCO_MAX_NODES];
650
} zxc_pivco_tree_t;
651
652
/**
653
 * @brief Precomputed decode-side tables derived from a ::zxc_pivco_tree_t.
654
 *
655
 * Pure functions of the tree topology (no dependence on section data):
656
 * @c skip flags the children of leaf-pair parents (emitted directly by the
657
 * parent's XOR-blend, never materialised), and @c c2s_pool holds each flat
658
 * root's packed-code -> symbol table at @c c2s_off[nid]. Flat subtrees have
659
 * disjoint leaves, so the pool never exceeds ZXC_HUF_NUM_SYMBOLS entries; the
660
 * +16 slack covers zxc_pivco_unpack_flat's SIMD table loads, which round a
661
 * table up to 16 entries. Per-section trees rebuild these inline in
662
 * zxc_pivco_decode_core; dictionary trees build them ONCE at attach so the
663
 * small-block dict decode path stops repaying the DFS + fills per block.
664
 */
665
typedef struct {
666
    uint8_t skip[ZXC_PIVCO_MAX_NODES];          /**< 1 = child of a leaf-pair parent. */
667
    uint16_t c2s_off[ZXC_PIVCO_MAX_NODES];      /**< Flat roots: offset into c2s_pool. */
668
    uint8_t c2s_pool[ZXC_HUF_NUM_SYMBOLS + 16]; /**< Concatenated c2s tables. */
669
} zxc_pivco_decode_aux_t;
670
671
/**
672
 * @brief Frame-constant dictionary Huffman state, prebuilt once at attach.
673
 *
674
 * Bundles everything the per-block dict paths reuse: the PivCo @c tree (decoder
675
 * + estimator), the canonical @c codes / @c code_len (encoder), and the
676
 * decode-side @c dec tables. Carved from the context workspace only when
677
 * @c dict_size > 0, so no-dict contexts pay nothing for it. Built by
678
 * @ref zxc_huf_dict_tree_build via @c zxc_cctx_attach_dict_huf.
679
 */
680
typedef struct {
681
    zxc_pivco_tree_t tree;                 /**< PivCo tree from the shared literal table. */
682
    uint32_t codes[ZXC_HUF_NUM_SYMBOLS];   /**< Canonical codes (encoder side). */
683
    uint8_t code_len[ZXC_HUF_NUM_SYMBOLS]; /**< Unpacked code lengths. */
684
    zxc_pivco_decode_aux_t dec;            /**< Precomputed decoder tables. */
685
} zxc_dict_huf_state_t;
686
/** @brief RLE margin shift: source of the legacy below-ULTRA premium used by
687
 *         ::zxc_ss_prem_rle_q8 (256 >> shift reproduces the historical
688
 *         RLE-vs-RAW margin exactly). */
689
11.9k
#define ZXC_RLE_MARGIN_SHIFT 5
690
/** @brief Huffman margin shift: source of the legacy below-ULTRA premium used
691
 *         by ::zxc_ss_prem_huf_q8 (the frozen ::ZXC_HUF_MIN_LITERALS floor was
692
 *         also historically derived from it). */
693
0
#define ZXC_HUF_MARGIN_SHIFT 5
694
695
/** @name Encoder-side joint flat/length nudge (PivCo decode-speed shaping)
696
 *
697
 *  Package-merge minimizes section bits alone, but the PivCo decoder's cost
698
 *  also depends on the SHAPE of the code-length histogram: the reconstruction
699
 *  pass loop runs `max_depth + 1` times, and every maximal complete subtree
700
 *  (all leaves exactly D levels below its root) collapses D merge levels into
701
 *  a single unpack. ::zxc_huf_nudge_code_lengths reshapes freshly built
702
 *  lengths toward power-of-two class counts and shallower caps, adopting a
703
 *  candidate only when its modeled decode win clears the guard below at a
704
 *  bounded ratio cost. Wire-compatible by construction: adjusted lengths stay
705
 *  canonical, Kraft-exact and within the level cap, so any v7 decoder reads
706
 *  the section unchanged (selection is encoder policy, FORMAT.md 5.2.1).
707
 *  Idea from pivco-huffman issue #20 (dougallj). All knobs are
708
 *  `#ifndef`-guarded so an A/B build can override them from CFLAGS; in
709
 *  particular `-DZXC_HUF_NUDGE_MERGE_Q8=0` makes the guard reject every
710
 *  candidate, restoring archives byte-identical to the unadjusted encoder.
711
 *  @{ */
712
/** @brief Exchange rate (Q8 bits per modeled level-touch) in the candidate
713
 *         cost `J = 256*bits + lambda*touches`; 26 ~= 0.10 bit per touch. */
714
#ifndef ZXC_HUF_NUDGE_LAMBDA_Q8
715
118M
#define ZXC_HUF_NUDGE_LAMBDA_Q8 26
716
#endif
717
/** @brief Adoption guard, ratio side (permil): adopt only while
718
 *         `bits' * 1000 <= bits0 * ZXC_HUF_NUDGE_BITS_PERMIL` (<= +1.5%). */
719
#ifndef ZXC_HUF_NUDGE_BITS_PERMIL
720
157k
#define ZXC_HUF_NUDGE_BITS_PERMIL 1015
721
#endif
722
/** @brief Adoption guard, speed side (Q8): adopt only while
723
 *         `touches' * 256 <= touches0 * ZXC_HUF_NUDGE_MERGE_Q8` (<= ~0.90x). */
724
#ifndef ZXC_HUF_NUDGE_MERGE_Q8
725
122k
#define ZXC_HUF_NUDGE_MERGE_Q8 230
726
#endif
727
/** @brief Deepest flat-subtree depth with a SIMD unpacker (see
728
 *         zxc_pivco_unpack_flat); deeper flat roots fall back to the scalar
729
 *         bit-reader and must NOT be priced as free. */
730
291M
#define ZXC_HUF_NUDGE_FLAT_SIMD_MAX 6
731
/** @brief Extra level-touches charged per occurrence under a flat root deeper
732
 *         than ::ZXC_HUF_NUDGE_FLAT_SIMD_MAX (scalar bit-reader unpack path).
733
 *         Measured on M2 silesia sections: the scalar unpack costs ~18 SIMD
734
 *         touch-equivalents per occurrence even in its byte-aligned D = 8 best
735
 *         case (a mispriced 2 let the walk collapse a 256-symbol section into
736
 *         one all-8-bit flat root: modeled -30% touches, real -54% decode).
737
 *         24 keeps low-mass deep-flat tails adoptable while making
738
 *         all-the-mass deep flats impossible to justify. */
739
#ifndef ZXC_HUF_NUDGE_DEEP_FLAT_PENALTY
740
881k
#define ZXC_HUF_NUDGE_DEEP_FLAT_PENALTY 24
741
#endif
742
/** @brief Fixed per-pass overhead (occurrence-equivalents) charged per merge
743
 *         level, modeling the pass-loop and node-dispatch cost so shallower
744
 *         trees also win on small sections. */
745
#ifndef ZXC_HUF_NUDGE_LEVEL_COST
746
2.70M
#define ZXC_HUF_NUDGE_LEVEL_COST 64
747
#endif
748
/** @} */
749
750
/** @name Space-speed section selection
751
 *
752
 *  Section encodings are selected at EVERY level by pricing each candidate
753
 *  with a Lagrangian cost `J = compressed_size + decode_tax` and taking the
754
 *  minimum (Kraken-style `J = R + lambda*D` with a linear per-byte
755
 *  decode-time model). The tax charges the DECODE time a candidate adds over
756
 *  the RAW copy path, expressed in bytes:
757
 *  `tax = (n_decoded_bytes * PREM) >> 8`. Only the premium is per-level:
758
 *
759
 *  - Below DENSITY, the premiums reproduce the historical fixed margins
760
 *    EXACTLY (8 = 3.125% = the old `>> 5` margins), keeping the sub-DENSITY
761
 *    levels (1-5) output byte-stable.
762
 *  - At DENSITY and above the physical premiums apply: `PREM_HUF = 4` (1.56%
763
 *    -- the entropy decoder costs ~0.5 ns/B over a raw copy, so ~1 byte of
764
 *    size buys ~30 ns of decode; validated on silesia: ~0.8 pt of ratio for
765
 *    ~3% decode vs the legacy margin) and `PREM_RLE = 1` (0.39%: RLE decodes
766
 *    at near copy speed, only a token tax to break ties toward RAW).
767
 *  @{ */
768
/** @brief Decode tax of a candidate: `n` decoded bytes at premium `prem` (Q8). */
769
143k
#define ZXC_SS_TAX(n, prem_q8) (((size_t)(n) * (size_t)(prem_q8)) >> 8)
770
/**
771
 * @brief Per-level RLE-vs-RAW decode premium (Q8) for the space-speed selector.
772
 *
773
 * Feeds @ref ZXC_SS_TAX, which charges an RLE candidate `n * premium >> 8`
774
 * decode-tax bytes over the RAW copy path. Below @ref ZXC_LEVEL_DENSITY the
775
 * premium is `256 >> ZXC_RLE_MARGIN_SHIFT` (8 = 3.125%), reproducing the
776
 * historical RLE-vs-RAW margin exactly so those levels keep byte-stable
777
 * selection. At @ref ZXC_LEVEL_DENSITY and above the physical premium 1 (0.39%)
778
 * applies: RLE decodes at near copy speed, so it needs only a token tax to break
779
 * ties toward RAW.
780
 *
781
 * @param[in] level Compression level.
782
 * @return Decode premium in Q8 (RLE decode-tax bytes per 256 decoded bytes).
783
 */
784
90.0k
static inline int zxc_ss_prem_rle_q8(const int level) {
785
90.0k
    return (level >= ZXC_LEVEL_DENSITY) ? 1 : (256 >> ZXC_RLE_MARGIN_SHIFT);
786
90.0k
}
Unexecuted instantiation: zxc_common.c:zxc_ss_prem_rle_q8
zxc_compress.c:zxc_ss_prem_rle_q8
Line
Count
Source
784
90.0k
static inline int zxc_ss_prem_rle_q8(const int level) {
785
90.0k
    return (level >= ZXC_LEVEL_DENSITY) ? 1 : (256 >> ZXC_RLE_MARGIN_SHIFT);
786
90.0k
}
Unexecuted instantiation: zxc_decompress.c:zxc_ss_prem_rle_q8
Unexecuted instantiation: zxc_dict.c:zxc_ss_prem_rle_q8
Unexecuted instantiation: zxc_driver.c:zxc_ss_prem_rle_q8
Unexecuted instantiation: zxc_dispatch.c:zxc_ss_prem_rle_q8
Unexecuted instantiation: zxc_huffman.c:zxc_ss_prem_rle_q8
Unexecuted instantiation: zxc_pstream.c:zxc_ss_prem_rle_q8
Unexecuted instantiation: zxc_seekable.c:zxc_ss_prem_rle_q8
787
/**
788
 * @brief Per-level Huffman/PivCo-vs-RAW decode premium (Q8) for the space-speed
789
 *        selector.
790
 *
791
 * Feeds @ref ZXC_SS_TAX, which charges a Huffman literal (or token) candidate
792
 * `n * premium >> 8` decode-tax bytes over the RAW copy path, the level's
793
 * lambda folded with the entropy decoder's cost. Below @ref ZXC_LEVEL_DENSITY
794
 * the premium is `256 >> ZXC_HUF_MARGIN_SHIFT` (8 = 3.125%), matching the
795
 * historical Huffman margin against a RAW baseline. At @ref ZXC_LEVEL_DENSITY
796
 * and above the physical premium 4 (1.56%) applies, trading more decode time for
797
 * ratio (the entropy decoder costs ~0.5 ns/B over a raw copy; validated on
798
 * silesia).
799
 *
800
 * @param[in] level Compression level.
801
 * @return Decode premium in Q8 (Huffman decode-tax bytes per 256 decoded bytes).
802
 */
803
53.7k
static inline int zxc_ss_prem_huf_q8(const int level) {
804
53.7k
    return (level >= ZXC_LEVEL_DENSITY) ? 4 : (256 >> ZXC_HUF_MARGIN_SHIFT);
805
53.7k
}
Unexecuted instantiation: zxc_common.c:zxc_ss_prem_huf_q8
zxc_compress.c:zxc_ss_prem_huf_q8
Line
Count
Source
803
53.7k
static inline int zxc_ss_prem_huf_q8(const int level) {
804
53.7k
    return (level >= ZXC_LEVEL_DENSITY) ? 4 : (256 >> ZXC_HUF_MARGIN_SHIFT);
805
53.7k
}
Unexecuted instantiation: zxc_decompress.c:zxc_ss_prem_huf_q8
Unexecuted instantiation: zxc_dict.c:zxc_ss_prem_huf_q8
Unexecuted instantiation: zxc_driver.c:zxc_ss_prem_huf_q8
Unexecuted instantiation: zxc_dispatch.c:zxc_ss_prem_huf_q8
Unexecuted instantiation: zxc_huffman.c:zxc_ss_prem_huf_q8
Unexecuted instantiation: zxc_pstream.c:zxc_ss_prem_huf_q8
Unexecuted instantiation: zxc_seekable.c:zxc_ss_prem_huf_q8
806
/** @} */
807
/** @brief Absolute floor (in literals) below which a Huffman candidate is
808
 *         never evaluated.
809
 *
810
 *         Frozen byte-stability threshold. The value was originally derived
811
 *         from v6 wire geometry (128-byte lengths table + 6-byte sub-stream
812
 *         sizes) under the pre-Lagrangian `huf_total < baseline * (M-1)/M`
813
 *         call-site rule; neither input exists in v7 (sub-stream sizes are
814
 *         derived, selection uses J = size + lambda * tax), but raising or
815
 *         lowering the floor changes which blocks pick Huffman and therefore
816
 *         the emitted archive bytes, so it stays frozen at the historical
817
 *         value rather than being re-derived. */
818
93.9k
#define ZXC_HUF_MIN_LITERALS 139
819
/** @} */
820
821
/** @brief Clamps a resolved compression level to the supported ceiling.
822
 *
823
 *         Out-of-range levels above ::ZXC_LEVEL_ULTRA are silently clamped
824
 *         (never rejected) at every compress entry point, so `level = 99`
825
 *         behaves as ULTRA across the buffer, context, block and stream APIs
826
 *         and every language binding inherits the same policy. Levels <= 0
827
 *         select the caller's default before this is applied. */
828
44.4k
static inline int zxc_level_clamp(const int level) {
829
44.4k
    return (level > ZXC_LEVEL_ULTRA) ? ZXC_LEVEL_ULTRA : level;
830
44.4k
}
Unexecuted instantiation: zxc_common.c:zxc_level_clamp
Unexecuted instantiation: zxc_compress.c:zxc_level_clamp
Unexecuted instantiation: zxc_decompress.c:zxc_level_clamp
Unexecuted instantiation: zxc_dict.c:zxc_level_clamp
Unexecuted instantiation: zxc_driver.c:zxc_level_clamp
zxc_dispatch.c:zxc_level_clamp
Line
Count
Source
828
39.8k
static inline int zxc_level_clamp(const int level) {
829
39.8k
    return (level > ZXC_LEVEL_ULTRA) ? ZXC_LEVEL_ULTRA : level;
830
39.8k
}
Unexecuted instantiation: zxc_huffman.c:zxc_level_clamp
zxc_pstream.c:zxc_level_clamp
Line
Count
Source
828
4.60k
static inline int zxc_level_clamp(const int level) {
829
4.60k
    return (level > ZXC_LEVEL_ULTRA) ? ZXC_LEVEL_ULTRA : level;
830
4.60k
}
Unexecuted instantiation: zxc_seekable.c:zxc_level_clamp
831
832
/** @brief Dictionary length from a (possibly NULL) options struct.
833
 *
834
 *  Gated on @c dict itself: no dictionary pointer means no dictionary, whatever
835
 *  the sibling fields hold. Macros rather than functions because the compression
836
 *  and decompression option structs carry these fields without sharing a type. */
837
65.2k
#define ZXC_OPTS_DICT_SIZE(o) (((o) && (o)->dict) ? (o)->dict_size : (size_t)0)
838
/** @brief Shared literal Huffman table, gated on @c dict the same way. */
839
60.3k
#define ZXC_OPTS_DICT_HUF(o) (((o) && (o)->dict) ? (const uint8_t*)(o)->dict_huf : NULL)
840
/** @brief Compression level, 0 meaning the default, clamped to the highest level
841
 *         the encoder implements. */
842
44.4k
#define ZXC_OPTS_LEVEL(o, dflt) zxc_level_clamp(((o) && (o)->level > 0) ? (o)->level : (dflt))
843
/** @brief Block size, 0 meaning the default. */
844
44.4k
#define ZXC_OPTS_BLOCK_SIZE(o, dflt) (((o) && (o)->block_size > 0) ? (o)->block_size : (dflt))
845
846
/** @brief Encoder Huffman code-length cap for a compression @p level: levels below
847
 *         ::ZXC_LEVEL_ULTRA use ::ZXC_HUF_MAX_CODE_LEN_DENSITY, ::ZXC_LEVEL_ULTRA uses
848
 *         the full ::ZXC_HUF_MAX_CODE_LEN_ULTRA ceiling (denser codes, slower decode).
849
 *         Applies to both literal and token Huffman; the decoder always supports the
850
 *         ceiling, so lower-level streams decode unchanged. */
851
107k
static inline int zxc_huf_enc_max_code_len(const int level) {
852
107k
    return (level >= ZXC_LEVEL_ULTRA) ? ZXC_HUF_MAX_CODE_LEN_ULTRA : ZXC_HUF_MAX_CODE_LEN_DENSITY;
853
107k
}
Unexecuted instantiation: zxc_common.c:zxc_huf_enc_max_code_len
zxc_compress.c:zxc_huf_enc_max_code_len
Line
Count
Source
851
107k
static inline int zxc_huf_enc_max_code_len(const int level) {
852
107k
    return (level >= ZXC_LEVEL_ULTRA) ? ZXC_HUF_MAX_CODE_LEN_ULTRA : ZXC_HUF_MAX_CODE_LEN_DENSITY;
853
107k
}
Unexecuted instantiation: zxc_decompress.c:zxc_huf_enc_max_code_len
Unexecuted instantiation: zxc_dict.c:zxc_huf_enc_max_code_len
Unexecuted instantiation: zxc_driver.c:zxc_huf_enc_max_code_len
Unexecuted instantiation: zxc_dispatch.c:zxc_huf_enc_max_code_len
Unexecuted instantiation: zxc_huffman.c:zxc_huf_enc_max_code_len
Unexecuted instantiation: zxc_pstream.c:zxc_huf_enc_max_code_len
Unexecuted instantiation: zxc_seekable.c:zxc_huf_enc_max_code_len
854
855
/**
856
 * @brief Boundary package-merge work item.
857
 *
858
 * Each level holds at most `2 * ZXC_HUF_NUM_SYMBOLS` of these; exposed so
859
 * callers can size pre-allocated scratch via ::ZXC_HUF_BUILD_SCRATCH_SIZE.
860
 */
861
typedef struct {
862
    uint32_t weight; /**< Accumulated weight (summed frequency) of the package. */
863
    int16_t left;    /**< Left child index, or -1 for a leaf. */
864
    int16_t right;   /**< Right child index, or -1 for a leaf. */
865
    int16_t sym;     /**< Symbol index for a leaf, or -1 for an internal node. */
866
} zxc_huf_pm_item_t;
867
868
/** @brief Trace-back stack frame for the package-merge code-length recovery. */
869
typedef struct {
870
    int8_t lvl;  /**< Package-merge level being traced back. */
871
    int16_t idx; /**< Item index within that level. */
872
} zxc_huf_pm_frame_t;
873
874
/** @brief Per-level item bound: at most leaves + paired packages from the
875
 *         previous level. */
876
238k
#define ZXC_HUF_PM_LEVEL_BOUND (2 * ZXC_HUF_NUM_SYMBOLS)
877
878
/** @brief Worst-case scratch size (bytes) for ::zxc_huf_build_code_lengths.
879
 *         Carved by the function into items / counts / stack regions; sized
880
 *         for the worst-case alphabet (n = `ZXC_HUF_NUM_SYMBOLS`). Includes
881
 *         a small alignment slack between regions. */
882
#define ZXC_HUF_BUILD_SCRATCH_SIZE                                         \
883
84.8k
    ((size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * (size_t)ZXC_HUF_PM_LEVEL_BOUND * \
884
84.8k
         sizeof(zxc_huf_pm_item_t) +                                       \
885
84.8k
     8U + (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * sizeof(int) + 8U +          \
886
84.8k
     (size_t)ZXC_HUF_MAX_CODE_LEN_ULTRA * (size_t)ZXC_HUF_PM_LEVEL_BOUND * \
887
84.8k
         sizeof(zxc_huf_pm_frame_t))
888
889
/**
890
 * @brief The four DP partitions the optimal parser carves out of opt_scratch.
891
 *
892
 * One definition shared by compute_cctx_layout(), which reserves the region,
893
 * and zxc_lz77_optimal_parse_glo(), which carves it: the two cannot drift.
894
 */
895
static ZXC_ALWAYS_INLINE void zxc_opt_dp_sizes(const size_t chunk_size, size_t* RESTRICT sz_dp,
896
                                               size_t* RESTRICT sz_pl, size_t* RESTRICT sz_po,
897
119k
                                               size_t* RESTRICT sz_bm) {
898
119k
    *sz_dp = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint32_t));
899
119k
    *sz_pl = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint16_t));
900
119k
    *sz_po = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint16_t));
901
119k
    *sz_bm = ZXC_ALIGN_CL(ZXC_BITMAP_WORDS(chunk_size + 1) * sizeof(uint64_t));
902
119k
}
zxc_common.c:zxc_opt_dp_sizes
Line
Count
Source
897
35.4k
                                               size_t* RESTRICT sz_bm) {
898
35.4k
    *sz_dp = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint32_t));
899
35.4k
    *sz_pl = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint16_t));
900
35.4k
    *sz_po = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint16_t));
901
35.4k
    *sz_bm = ZXC_ALIGN_CL(ZXC_BITMAP_WORDS(chunk_size + 1) * sizeof(uint64_t));
902
35.4k
}
zxc_compress.c:zxc_opt_dp_sizes
Line
Count
Source
897
83.9k
                                               size_t* RESTRICT sz_bm) {
898
83.9k
    *sz_dp = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint32_t));
899
83.9k
    *sz_pl = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint16_t));
900
83.9k
    *sz_po = ZXC_ALIGN_CL((chunk_size + 1) * sizeof(uint16_t));
901
83.9k
    *sz_bm = ZXC_ALIGN_CL(ZXC_BITMAP_WORDS(chunk_size + 1) * sizeof(uint64_t));
902
83.9k
}
Unexecuted instantiation: zxc_decompress.c:zxc_opt_dp_sizes
Unexecuted instantiation: zxc_dict.c:zxc_opt_dp_sizes
Unexecuted instantiation: zxc_driver.c:zxc_opt_dp_sizes
Unexecuted instantiation: zxc_dispatch.c:zxc_opt_dp_sizes
Unexecuted instantiation: zxc_huffman.c:zxc_opt_dp_sizes
Unexecuted instantiation: zxc_pstream.c:zxc_opt_dp_sizes
Unexecuted instantiation: zxc_seekable.c:zxc_opt_dp_sizes
903
904
/** @name Block Size Helpers
905
 *  @brief Runtime helpers for variable block sizes.
906
 *  @{ */
907
908
/**
909
 * @brief Integer log-base-2 for a 32-bit value.
910
 * @param v Must be a power of two (returns 0 for zero).
911
 * @return Floor of log2(v).
912
 */
913
758M
static ZXC_ALWAYS_INLINE uint32_t zxc_log2_u32(const uint32_t v) {
914
#ifdef _MSC_VER
915
    unsigned long index;
916
    return (v == 0) ? 0 : (_BitScanReverse(&index, v) ? index : 0);
917
#else
918
758M
    return (v == 0) ? 0 : (uint32_t)(31 - __builtin_clz(v));
919
758M
#endif
920
758M
}
zxc_common.c:zxc_log2_u32
Line
Count
Source
913
212k
static ZXC_ALWAYS_INLINE uint32_t zxc_log2_u32(const uint32_t v) {
914
#ifdef _MSC_VER
915
    unsigned long index;
916
    return (v == 0) ? 0 : (_BitScanReverse(&index, v) ? index : 0);
917
#else
918
212k
    return (v == 0) ? 0 : (uint32_t)(31 - __builtin_clz(v));
919
212k
#endif
920
212k
}
Unexecuted instantiation: zxc_compress.c:zxc_log2_u32
Unexecuted instantiation: zxc_decompress.c:zxc_log2_u32
Unexecuted instantiation: zxc_dict.c:zxc_log2_u32
Unexecuted instantiation: zxc_driver.c:zxc_log2_u32
Unexecuted instantiation: zxc_dispatch.c:zxc_log2_u32
zxc_huffman.c:zxc_log2_u32
Line
Count
Source
913
758M
static ZXC_ALWAYS_INLINE uint32_t zxc_log2_u32(const uint32_t v) {
914
#ifdef _MSC_VER
915
    unsigned long index;
916
    return (v == 0) ? 0 : (_BitScanReverse(&index, v) ? index : 0);
917
#else
918
758M
    return (v == 0) ? 0 : (uint32_t)(31 - __builtin_clz(v));
919
758M
#endif
920
758M
}
Unexecuted instantiation: zxc_pstream.c:zxc_log2_u32
Unexecuted instantiation: zxc_seekable.c:zxc_log2_u32
921
922
/**
923
 * @brief Branchless bit_ceil: smallest power of two >= v, clamped to ZXC_BLOCK_SIZE_MIN.
924
 * @param[in] v Input size (must be > 0).
925
 * @return Smallest power of two >= @p v, clamped up to @ref ZXC_BLOCK_SIZE_MIN.
926
 */
927
22.9k
static ZXC_ALWAYS_INLINE size_t zxc_block_size_ceil(const size_t v) {
928
22.9k
    uint64_t x = (uint64_t)v - 1;
929
22.9k
    x |= x >> 1;
930
22.9k
    x |= x >> 2;
931
22.9k
    x |= x >> 4;
932
22.9k
    x |= x >> 8;
933
22.9k
    x |= x >> 16;
934
22.9k
    x |= x >> 32;
935
22.9k
    x++;
936
22.9k
    const size_t bs = (size_t)x;
937
22.9k
    return (bs < ZXC_BLOCK_SIZE_MIN) ? ZXC_BLOCK_SIZE_MIN : bs;
938
22.9k
}
Unexecuted instantiation: zxc_common.c:zxc_block_size_ceil
Unexecuted instantiation: zxc_compress.c:zxc_block_size_ceil
Unexecuted instantiation: zxc_decompress.c:zxc_block_size_ceil
zxc_dict.c:zxc_block_size_ceil
Line
Count
Source
927
9.00k
static ZXC_ALWAYS_INLINE size_t zxc_block_size_ceil(const size_t v) {
928
9.00k
    uint64_t x = (uint64_t)v - 1;
929
9.00k
    x |= x >> 1;
930
9.00k
    x |= x >> 2;
931
9.00k
    x |= x >> 4;
932
9.00k
    x |= x >> 8;
933
9.00k
    x |= x >> 16;
934
9.00k
    x |= x >> 32;
935
9.00k
    x++;
936
9.00k
    const size_t bs = (size_t)x;
937
9.00k
    return (bs < ZXC_BLOCK_SIZE_MIN) ? ZXC_BLOCK_SIZE_MIN : bs;
938
9.00k
}
Unexecuted instantiation: zxc_driver.c:zxc_block_size_ceil
zxc_dispatch.c:zxc_block_size_ceil
Line
Count
Source
927
13.9k
static ZXC_ALWAYS_INLINE size_t zxc_block_size_ceil(const size_t v) {
928
13.9k
    uint64_t x = (uint64_t)v - 1;
929
13.9k
    x |= x >> 1;
930
13.9k
    x |= x >> 2;
931
13.9k
    x |= x >> 4;
932
13.9k
    x |= x >> 8;
933
13.9k
    x |= x >> 16;
934
13.9k
    x |= x >> 32;
935
13.9k
    x++;
936
13.9k
    const size_t bs = (size_t)x;
937
13.9k
    return (bs < ZXC_BLOCK_SIZE_MIN) ? ZXC_BLOCK_SIZE_MIN : bs;
938
13.9k
}
Unexecuted instantiation: zxc_huffman.c:zxc_block_size_ceil
Unexecuted instantiation: zxc_pstream.c:zxc_block_size_ceil
Unexecuted instantiation: zxc_seekable.c:zxc_block_size_ceil
939
940
/**
941
 * @brief Validates a block size.
942
 * Must be a power of two in [ZXC_BLOCK_SIZE_MIN, ZXC_BLOCK_SIZE_MAX].
943
 * @param[in] bs Block size to validate.
944
 * @return 1 if valid, 0 otherwise.
945
 */
946
34.9k
static ZXC_ALWAYS_INLINE int zxc_validate_block_size(const size_t bs) {
947
34.9k
    return bs >= ZXC_BLOCK_SIZE_MIN && bs <= ZXC_BLOCK_SIZE_MAX && (bs & (bs - 1)) == 0;
948
34.9k
}
Unexecuted instantiation: zxc_common.c:zxc_validate_block_size
Unexecuted instantiation: zxc_compress.c:zxc_validate_block_size
Unexecuted instantiation: zxc_decompress.c:zxc_validate_block_size
Unexecuted instantiation: zxc_dict.c:zxc_validate_block_size
Unexecuted instantiation: zxc_driver.c:zxc_validate_block_size
zxc_dispatch.c:zxc_validate_block_size
Line
Count
Source
946
34.9k
static ZXC_ALWAYS_INLINE int zxc_validate_block_size(const size_t bs) {
947
34.9k
    return bs >= ZXC_BLOCK_SIZE_MIN && bs <= ZXC_BLOCK_SIZE_MAX && (bs & (bs - 1)) == 0;
948
34.9k
}
Unexecuted instantiation: zxc_huffman.c:zxc_validate_block_size
Unexecuted instantiation: zxc_pstream.c:zxc_validate_block_size
Unexecuted instantiation: zxc_seekable.c:zxc_validate_block_size
949
/** @} */
950
951
/** @} */ /* end of File Format Constants */
952
953
/**
954
 * @struct zxc_lz77_params_t
955
 * @brief Search parameters for LZ77 compression levels.
956
 *
957
 * Each compression level maps to a specific set of parameters that control the
958
 * trade-off between compression speed and ratio.  Higher search depths and lazy
959
 * matching improve ratio at the expense of throughput; larger step values
960
 * accelerate literal scanning but may miss short matches.
961
 */
962
typedef struct {
963
    /** Maximum number of candidates explored in the hash chain per position.
964
     *  Higher values find better matches but increase CPU cost linearly. */
965
    int search_depth;
966
967
    /** "Good enough" match length: once a match reaches this threshold the
968
     *  chain walk stops immediately, avoiding wasted effort on an already
969
     *  excellent match. */
970
    int sufficient_len;
971
972
    /** Enable lazy matching.  When set, after finding a match at position
973
     *  @c ip the compressor probes @c ip+1 (and @c ip+2 for level >= 4) to
974
     *  see if a longer match exists.  If so, a literal is emitted and the
975
     *  better match is taken instead.  Improves ratio but costs extra work. */
976
    int use_lazy;
977
978
    /** Maximum number of candidates explored during lazy evaluation (same
979
     *  semantics as @ref search_depth but applied to the ip+1 / ip+2 probes).
980
     *  Only meaningful when @ref use_lazy is non-zero. */
981
    int lazy_attempts;
982
983
    /** Skip lazy evaluation when the current match length already reaches
984
     *  this threshold: a match this long is unlikely to be beaten at the
985
     *  next byte.  Set to 0 when @ref use_lazy is disabled. */
986
    int lazy_len_threshold;
987
988
    /** Base step size when advancing through unmatched literals.
989
     *  1 = test every byte (best ratio), 4 = skip aggressively (fastest). */
990
    uint32_t step_base;
991
992
    /** Acceleration factor for step size: @c step = step_base + (distance >> step_shift).
993
     *  A larger value keeps the step conservative (grows slowly with distance);
994
     *  a smaller value ramps up quickly, skipping more in long literal runs. */
995
    uint32_t step_shift;
996
997
    /** Shortest match distance the parser may emit; 1 = unconstrained. Skipped
998
     *  candidates do not end the chain walk, which continues to a legal one
999
     *  further back. See @ref ZXC_LZ_MINDIST. */
1000
    uint32_t min_offset;
1001
} zxc_lz77_params_t;
1002
1003
/**
1004
 * @brief Retrieves LZ77 compression parameters based on the specified compression level.
1005
 *
1006
 * This inline function returns the appropriate LZ77 parameters configuration
1007
 * for the given compression level.
1008
 *
1009
 * @param[in] level Compression level; out-of-range values clamp to level 1.
1010
 * @return The tuning tuple for that level.
1011
 */
1012
202k
static ZXC_ALWAYS_INLINE zxc_lz77_params_t zxc_get_lz77_params(const int level) {
1013
    // The distance floor stops at level 5: the slow levels keep every distance.
1014
    // search_depth, sufficient_len, use_lazy, lazy_attempts, lazy_len_threshold, step_base,
1015
    // step_shift, min_offset
1016
202k
    static const zxc_lz77_params_t table[7] = {
1017
202k
        {3, 16, 0, 0, 0, 4, 4, ZXC_LZ_MINDIST},       // fallback
1018
202k
        {3, 16, 0, 0, 0, 4, 4, ZXC_LZ_MINDIST},       // level 1
1019
202k
        {3, 18, 0, 0, 0, 3, 6, ZXC_LZ_MINDIST},       // level 2
1020
202k
        {3, 16, 1, 4, 128, 1, 4, ZXC_LZ_MINDIST},     // level 3
1021
202k
        {3, 18, 1, 4, 128, 1, 5, ZXC_LZ_MINDIST},     // level 4
1022
202k
        {64, 256, 1, 16, 128, 1, 8, ZXC_LZ_MINDIST},  // level 5
1023
202k
        {64, 256, 0, 0, 0, 1, 8, 1}                   // level 6
1024
202k
    };
1025
202k
    return (level >= ZXC_LEVEL_ULTRA)
1026
202k
               ? (zxc_lz77_params_t){128, 256, 0, 0, 0, 1, 8, 1}
1027
202k
               : table[level < ZXC_LEVEL_FASTEST ? ZXC_LEVEL_FASTEST : level];
1028
202k
}
Unexecuted instantiation: zxc_common.c:zxc_get_lz77_params
zxc_compress.c:zxc_get_lz77_params
Line
Count
Source
1012
202k
static ZXC_ALWAYS_INLINE zxc_lz77_params_t zxc_get_lz77_params(const int level) {
1013
    // The distance floor stops at level 5: the slow levels keep every distance.
1014
    // search_depth, sufficient_len, use_lazy, lazy_attempts, lazy_len_threshold, step_base,
1015
    // step_shift, min_offset
1016
202k
    static const zxc_lz77_params_t table[7] = {
1017
202k
        {3, 16, 0, 0, 0, 4, 4, ZXC_LZ_MINDIST},       // fallback
1018
202k
        {3, 16, 0, 0, 0, 4, 4, ZXC_LZ_MINDIST},       // level 1
1019
202k
        {3, 18, 0, 0, 0, 3, 6, ZXC_LZ_MINDIST},       // level 2
1020
202k
        {3, 16, 1, 4, 128, 1, 4, ZXC_LZ_MINDIST},     // level 3
1021
202k
        {3, 18, 1, 4, 128, 1, 5, ZXC_LZ_MINDIST},     // level 4
1022
202k
        {64, 256, 1, 16, 128, 1, 8, ZXC_LZ_MINDIST},  // level 5
1023
202k
        {64, 256, 0, 0, 0, 1, 8, 1}                   // level 6
1024
202k
    };
1025
202k
    return (level >= ZXC_LEVEL_ULTRA)
1026
202k
               ? (zxc_lz77_params_t){128, 256, 0, 0, 0, 1, 8, 1}
1027
202k
               : table[level < ZXC_LEVEL_FASTEST ? ZXC_LEVEL_FASTEST : level];
1028
202k
}
Unexecuted instantiation: zxc_decompress.c:zxc_get_lz77_params
Unexecuted instantiation: zxc_dict.c:zxc_get_lz77_params
Unexecuted instantiation: zxc_driver.c:zxc_get_lz77_params
Unexecuted instantiation: zxc_dispatch.c:zxc_get_lz77_params
Unexecuted instantiation: zxc_huffman.c:zxc_get_lz77_params
Unexecuted instantiation: zxc_pstream.c:zxc_get_lz77_params
Unexecuted instantiation: zxc_seekable.c:zxc_get_lz77_params
1029
1030
/**
1031
 * @enum zxc_block_type_t
1032
 * @brief Block types, i.e. which encoder produced a block's payload.
1033
 *
1034
 * - `ZXC_BLOCK_RAW` (0): stored as-is. Used when the data is incompressible or
1035
 *   when compressing it would make the block bigger.
1036
 * - `ZXC_BLOCK_GLO` (1): the general path, LZ77 plus bitpacked sequences,
1037
 *   levels 3 and up. Carries 0, 4 or 8 bytes of section descriptors depending
1038
 *   on which streams need an explicit compressed size.
1039
 * - `ZXC_BLOCK_GHI` (2): the speed path, levels 1 and 2. Fixed 4-byte sequence
1040
 *   records and always-RAW literals make every section size derivable from the
1041
 *   header, so it carries no descriptor at all.
1042
 * - `ZXC_BLOCK_SEK` (254): seek table, holding per-block compressed and
1043
 *   decompressed sizes. Sits between the EOF block and the file footer.
1044
 * - `ZXC_BLOCK_EOF` (255): end-of-file marker.
1045
 */
1046
typedef enum {
1047
    ZXC_BLOCK_RAW = 0,
1048
    ZXC_BLOCK_GLO = 1,
1049
    ZXC_BLOCK_GHI = 2,
1050
    ZXC_BLOCK_SEK = 254,
1051
    ZXC_BLOCK_EOF = 255
1052
} zxc_block_type_t;
1053
1054
/**
1055
 * @enum zxc_section_encoding_t
1056
 * @brief Specifies the encoding methods used for internal data sections.
1057
 *
1058
 * These modes determine how specific components (like literals, match lengths,
1059
 * or offsets) are stored within a block.
1060
 * - `ZXC_SECTION_ENCODING_RAW`: Data is stored uncompressed.
1061
 * - `ZXC_SECTION_ENCODING_RLE`: Run-Length Encoding.
1062
 * - `ZXC_SECTION_ENCODING_HUFFMAN`: canonical Huffman in the PivCo layout
1063
 *   (level-ordered branch runs, max 11-bit codes -- FORMAT.md section 5.2.1).
1064
 *   Valid for the literal stream (`enc_lit`, level >= 6) and the token
1065
 *   stream (`enc_tok`, level 7) of GLO blocks.
1066
 * - `ZXC_SECTION_ENCODING_HUFFMAN_DICT`: same payload as HUFFMAN but the
1067
 *   128-byte code-lengths header is omitted: codes come from the shared
1068
 *   table carried by the dictionary (.zxd). Only valid for `enc_lit` of GLO
1069
 *   blocks in dictionary-compressed archives; requires the same dictionary
1070
 *   (content + table, bound by dict_id) at decode time.
1071
 */
1072
typedef enum {
1073
    ZXC_SECTION_ENCODING_RAW = 0,
1074
    ZXC_SECTION_ENCODING_RLE = 1,
1075
    ZXC_SECTION_ENCODING_HUFFMAN = 2,
1076
    ZXC_SECTION_ENCODING_HUFFMAN_DICT = 3
1077
} zxc_section_encoding_t;
1078
1079
/**
1080
 * @struct zxc_gnr_header_t
1081
 * @brief Header specific to General (LZ-based) compression blocks.
1082
 *
1083
 * This header follows the main block header when the block type is GLO/GHI. It
1084
 * describes the layout of sequences and literals.
1085
 *
1086
 * @var zxc_gnr_header_t::n_sequences
1087
 * The total count of LZ sequences in the block.
1088
 * @var zxc_gnr_header_t::n_literals
1089
 * The total count of literal bytes.
1090
 * @var zxc_gnr_header_t::enc_lit
1091
 * Encoding method used for the literal stream.
1092
 * @var zxc_gnr_header_t::enc_tok
1093
 * GLO only: encoding of the token section, whose bytes each pack a literal
1094
 * length and a match length nibble. Only RAW and HUFFMAN (level 7) occur.
1095
 * @var zxc_gnr_header_t::enc_mlen
1096
 * Reserved, written 0 and ignored on decode. Match lengths have no stream of
1097
 * their own: they share the token byte and spill into the extras.
1098
 * @var zxc_gnr_header_t::enc_off
1099
 * GLO only: width of the offset stream (1 = 1-byte, 0 = 2-byte). GHI has no
1100
 * offset stream, so it writes 0 and ignores the field on decode.
1101
 */
1102
typedef struct {
1103
    uint32_t n_sequences;  // Number of sequences
1104
    uint32_t n_literals;   // Number of literals
1105
    uint8_t enc_lit;       // Literal stream encoding
1106
    uint8_t enc_tok;       // Token section encoding (GLO only)
1107
    uint8_t enc_mlen;      // Reserved (see above)
1108
    uint8_t enc_off;       // Offset stream width (GLO only; ignored on decode in GHI)
1109
} zxc_gnr_header_t;
1110
1111
/**
1112
 * ============================================================================
1113
 * MEMORY & ENDIANNESS HELPERS
1114
 * ============================================================================
1115
 * Functions to handle unaligned memory access and Little Endian conversion.
1116
 */
1117
1118
/**
1119
 * @brief Reads a little-endian 16-bit value from a possibly unaligned address.
1120
 *
1121
 * The memcpy is what makes the unaligned read defined; compilers fold it into
1122
 * a single load. Byte-swapped on big-endian hosts, so the wire stays LE.
1123
 *
1124
 * @param[in] p Address to read from.
1125
 * @return The value, in host order.
1126
 */
1127
388k
static ZXC_ALWAYS_INLINE uint16_t zxc_le16(const void* p) {
1128
388k
    uint16_t v;
1129
388k
    ZXC_MEMCPY(&v, p, sizeof(v));
1130
#ifdef ZXC_BIG_ENDIAN
1131
    return ZXC_BSWAP16(v);
1132
#else
1133
388k
    return v;
1134
388k
#endif
1135
388k
}
zxc_common.c:zxc_le16
Line
Count
Source
1127
53.0k
static ZXC_ALWAYS_INLINE uint16_t zxc_le16(const void* p) {
1128
53.0k
    uint16_t v;
1129
53.0k
    ZXC_MEMCPY(&v, p, sizeof(v));
1130
#ifdef ZXC_BIG_ENDIAN
1131
    return ZXC_BSWAP16(v);
1132
#else
1133
53.0k
    return v;
1134
53.0k
#endif
1135
53.0k
}
Unexecuted instantiation: zxc_compress.c:zxc_le16
zxc_decompress.c:zxc_le16
Line
Count
Source
1127
302k
static ZXC_ALWAYS_INLINE uint16_t zxc_le16(const void* p) {
1128
302k
    uint16_t v;
1129
302k
    ZXC_MEMCPY(&v, p, sizeof(v));
1130
#ifdef ZXC_BIG_ENDIAN
1131
    return ZXC_BSWAP16(v);
1132
#else
1133
302k
    return v;
1134
302k
#endif
1135
302k
}
zxc_dict.c:zxc_le16
Line
Count
Source
1127
32.9k
static ZXC_ALWAYS_INLINE uint16_t zxc_le16(const void* p) {
1128
32.9k
    uint16_t v;
1129
32.9k
    ZXC_MEMCPY(&v, p, sizeof(v));
1130
#ifdef ZXC_BIG_ENDIAN
1131
    return ZXC_BSWAP16(v);
1132
#else
1133
32.9k
    return v;
1134
32.9k
#endif
1135
32.9k
}
Unexecuted instantiation: zxc_driver.c:zxc_le16
Unexecuted instantiation: zxc_dispatch.c:zxc_le16
Unexecuted instantiation: zxc_huffman.c:zxc_le16
Unexecuted instantiation: zxc_pstream.c:zxc_le16
Unexecuted instantiation: zxc_seekable.c:zxc_le16
1136
1137
/**
1138
 * @brief Reads a little-endian 32-bit value from a possibly unaligned address.
1139
 *
1140
 * The memcpy is what makes the unaligned read defined; compilers fold it into
1141
 * a single load. Byte-swapped on big-endian hosts, so the wire stays LE.
1142
 *
1143
 * @param[in] p Address to read from.
1144
 * @return The value, in host order.
1145
 */
1146
34.8G
static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) {
1147
34.8G
    uint32_t v;
1148
34.8G
    ZXC_MEMCPY(&v, p, sizeof(v));
1149
#ifdef ZXC_BIG_ENDIAN
1150
    return ZXC_BSWAP32(v);
1151
#else
1152
34.8G
    return v;
1153
34.8G
#endif
1154
34.8G
}
zxc_common.c:zxc_le32
Line
Count
Source
1146
511k
static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) {
1147
511k
    uint32_t v;
1148
511k
    ZXC_MEMCPY(&v, p, sizeof(v));
1149
#ifdef ZXC_BIG_ENDIAN
1150
    return ZXC_BSWAP32(v);
1151
#else
1152
511k
    return v;
1153
511k
#endif
1154
511k
}
zxc_compress.c:zxc_le32
Line
Count
Source
1146
10.9G
static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) {
1147
10.9G
    uint32_t v;
1148
10.9G
    ZXC_MEMCPY(&v, p, sizeof(v));
1149
#ifdef ZXC_BIG_ENDIAN
1150
    return ZXC_BSWAP32(v);
1151
#else
1152
10.9G
    return v;
1153
10.9G
#endif
1154
10.9G
}
zxc_decompress.c:zxc_le32
Line
Count
Source
1146
13.8M
static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) {
1147
13.8M
    uint32_t v;
1148
13.8M
    ZXC_MEMCPY(&v, p, sizeof(v));
1149
#ifdef ZXC_BIG_ENDIAN
1150
    return ZXC_BSWAP32(v);
1151
#else
1152
13.8M
    return v;
1153
13.8M
#endif
1154
13.8M
}
zxc_dict.c:zxc_le32
Line
Count
Source
1146
23.8G
static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) {
1147
23.8G
    uint32_t v;
1148
23.8G
    ZXC_MEMCPY(&v, p, sizeof(v));
1149
#ifdef ZXC_BIG_ENDIAN
1150
    return ZXC_BSWAP32(v);
1151
#else
1152
23.8G
    return v;
1153
23.8G
#endif
1154
23.8G
}
Unexecuted instantiation: zxc_driver.c:zxc_le32
zxc_dispatch.c:zxc_le32
Line
Count
Source
1146
18.2k
static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) {
1147
18.2k
    uint32_t v;
1148
18.2k
    ZXC_MEMCPY(&v, p, sizeof(v));
1149
#ifdef ZXC_BIG_ENDIAN
1150
    return ZXC_BSWAP32(v);
1151
#else
1152
18.2k
    return v;
1153
18.2k
#endif
1154
18.2k
}
Unexecuted instantiation: zxc_huffman.c:zxc_le32
zxc_pstream.c:zxc_le32
Line
Count
Source
1146
4.29k
static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) {
1147
4.29k
    uint32_t v;
1148
4.29k
    ZXC_MEMCPY(&v, p, sizeof(v));
1149
#ifdef ZXC_BIG_ENDIAN
1150
    return ZXC_BSWAP32(v);
1151
#else
1152
4.29k
    return v;
1153
4.29k
#endif
1154
4.29k
}
zxc_seekable.c:zxc_le32
Line
Count
Source
1146
10.6k
static ZXC_ALWAYS_INLINE uint32_t zxc_le32(const void* p) {
1147
10.6k
    uint32_t v;
1148
10.6k
    ZXC_MEMCPY(&v, p, sizeof(v));
1149
#ifdef ZXC_BIG_ENDIAN
1150
    return ZXC_BSWAP32(v);
1151
#else
1152
10.6k
    return v;
1153
10.6k
#endif
1154
10.6k
}
1155
1156
/**
1157
 * @brief Reports whether a block leans on back-references shorter than
1158
 *        @ref ZXC_LZ_MINDIST.
1159
 *
1160
 * Asks one question per sampled position: is there a 4-byte repeat inside the
1161
 * distance the floor would forbid? Blocks that answer yes often -- periodic
1162
 * runs, JSON keys, XML tags -- lose size and decode time under it.
1163
 *
1164
 * @param[in] blk  Block payload, past any dictionary prefix.
1165
 * @param[in] size Payload size in bytes.
1166
 * @return 1 when the block must keep its short distances, 0 when the floor is safe.
1167
 */
1168
static ZXC_ALWAYS_INLINE int zxc_block_is_short_dist_bound(const uint8_t* const blk,
1169
28.0k
                                                           const size_t size) {
1170
28.0k
    const size_t d_max = ZXC_LZ_MINDIST;
1171
    // Too small to sample, and too small to gain: keep every distance.
1172
28.0k
    if (size < d_max + sizeof(uint32_t)) return 1;
1173
1174
25.3k
    size_t want = size / ZXC_LZ_MINDIST_PROBE_PER_KB;
1175
25.3k
    if (want < ZXC_LZ_MINDIST_PROBE_MIN) want = ZXC_LZ_MINDIST_PROBE_MIN;
1176
25.3k
    if (want > ZXC_LZ_MINDIST_PROBE_MAX) want = ZXC_LZ_MINDIST_PROBE_MAX;
1177
25.3k
    size_t stride = size / want;
1178
25.3k
    if (stride < d_max) stride = d_max;
1179
1180
25.3k
    const size_t planned = (size - sizeof(uint32_t) - d_max) / stride + 1;
1181
25.3k
    const size_t bar = (size_t)ZXC_LZ_MINDIST_MAX_SHORT_PCT * planned;
1182
1183
25.3k
    size_t samples = 0;
1184
25.3k
    size_t hits = 0;
1185
360k
    for (size_t i = d_max; i + sizeof(uint32_t) <= size; i += stride) {
1186
360k
        const uint32_t cur = zxc_le32(blk + i);
1187
360k
        samples++;
1188
8.05M
        for (size_t d = 1; d < d_max; d++) {
1189
7.81M
            if (zxc_le32(blk + i - d) == cur) {
1190
121k
                hits++;
1191
121k
                break;
1192
121k
            }
1193
7.81M
        }
1194
360k
        if (hits * 100U > bar) return 1;                           // can only rise
1195
341k
        if ((hits + (planned - samples)) * 100U <= bar) return 0;  // out of reach
1196
341k
    }
1197
0
    return hits * 100U > bar;
1198
25.3k
}
Unexecuted instantiation: zxc_common.c:zxc_block_is_short_dist_bound
zxc_compress.c:zxc_block_is_short_dist_bound
Line
Count
Source
1169
28.0k
                                                           const size_t size) {
1170
28.0k
    const size_t d_max = ZXC_LZ_MINDIST;
1171
    // Too small to sample, and too small to gain: keep every distance.
1172
28.0k
    if (size < d_max + sizeof(uint32_t)) return 1;
1173
1174
25.3k
    size_t want = size / ZXC_LZ_MINDIST_PROBE_PER_KB;
1175
25.3k
    if (want < ZXC_LZ_MINDIST_PROBE_MIN) want = ZXC_LZ_MINDIST_PROBE_MIN;
1176
25.3k
    if (want > ZXC_LZ_MINDIST_PROBE_MAX) want = ZXC_LZ_MINDIST_PROBE_MAX;
1177
25.3k
    size_t stride = size / want;
1178
25.3k
    if (stride < d_max) stride = d_max;
1179
1180
25.3k
    const size_t planned = (size - sizeof(uint32_t) - d_max) / stride + 1;
1181
25.3k
    const size_t bar = (size_t)ZXC_LZ_MINDIST_MAX_SHORT_PCT * planned;
1182
1183
25.3k
    size_t samples = 0;
1184
25.3k
    size_t hits = 0;
1185
360k
    for (size_t i = d_max; i + sizeof(uint32_t) <= size; i += stride) {
1186
360k
        const uint32_t cur = zxc_le32(blk + i);
1187
360k
        samples++;
1188
8.05M
        for (size_t d = 1; d < d_max; d++) {
1189
7.81M
            if (zxc_le32(blk + i - d) == cur) {
1190
121k
                hits++;
1191
121k
                break;
1192
121k
            }
1193
7.81M
        }
1194
360k
        if (hits * 100U > bar) return 1;                           // can only rise
1195
341k
        if ((hits + (planned - samples)) * 100U <= bar) return 0;  // out of reach
1196
341k
    }
1197
0
    return hits * 100U > bar;
1198
25.3k
}
Unexecuted instantiation: zxc_decompress.c:zxc_block_is_short_dist_bound
Unexecuted instantiation: zxc_dict.c:zxc_block_is_short_dist_bound
Unexecuted instantiation: zxc_driver.c:zxc_block_is_short_dist_bound
Unexecuted instantiation: zxc_dispatch.c:zxc_block_is_short_dist_bound
Unexecuted instantiation: zxc_huffman.c:zxc_block_is_short_dist_bound
Unexecuted instantiation: zxc_pstream.c:zxc_block_is_short_dist_bound
Unexecuted instantiation: zxc_seekable.c:zxc_block_is_short_dist_bound
1199
1200
/**
1201
 * @brief Reads a little-endian 64-bit value from a possibly unaligned address.
1202
 *
1203
 * The memcpy is what makes the unaligned read defined; compilers fold it into
1204
 * a single load. Byte-swapped on big-endian hosts, so the wire stays LE.
1205
 *
1206
 * @param[in] p Address to read from.
1207
 * @return The value, in host order.
1208
 */
1209
7.23G
static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) {
1210
7.23G
    uint64_t v;
1211
7.23G
    ZXC_MEMCPY(&v, p, sizeof(v));
1212
#ifdef ZXC_BIG_ENDIAN
1213
    return ZXC_BSWAP64(v);
1214
#else
1215
7.23G
    return v;
1216
7.23G
#endif
1217
7.23G
}
zxc_common.c:zxc_le64
Line
Count
Source
1209
489k
static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) {
1210
489k
    uint64_t v;
1211
489k
    ZXC_MEMCPY(&v, p, sizeof(v));
1212
#ifdef ZXC_BIG_ENDIAN
1213
    return ZXC_BSWAP64(v);
1214
#else
1215
489k
    return v;
1216
489k
#endif
1217
489k
}
zxc_compress.c:zxc_le64
Line
Count
Source
1209
7.23G
static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) {
1210
7.23G
    uint64_t v;
1211
7.23G
    ZXC_MEMCPY(&v, p, sizeof(v));
1212
#ifdef ZXC_BIG_ENDIAN
1213
    return ZXC_BSWAP64(v);
1214
#else
1215
7.23G
    return v;
1216
7.23G
#endif
1217
7.23G
}
zxc_decompress.c:zxc_le64
Line
Count
Source
1209
3.67M
static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) {
1210
3.67M
    uint64_t v;
1211
3.67M
    ZXC_MEMCPY(&v, p, sizeof(v));
1212
#ifdef ZXC_BIG_ENDIAN
1213
    return ZXC_BSWAP64(v);
1214
#else
1215
3.67M
    return v;
1216
3.67M
#endif
1217
3.67M
}
zxc_dict.c:zxc_le64
Line
Count
Source
1209
68.7k
static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) {
1210
68.7k
    uint64_t v;
1211
68.7k
    ZXC_MEMCPY(&v, p, sizeof(v));
1212
#ifdef ZXC_BIG_ENDIAN
1213
    return ZXC_BSWAP64(v);
1214
#else
1215
68.7k
    return v;
1216
68.7k
#endif
1217
68.7k
}
Unexecuted instantiation: zxc_driver.c:zxc_le64
zxc_dispatch.c:zxc_le64
Line
Count
Source
1209
30.0k
static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) {
1210
30.0k
    uint64_t v;
1211
30.0k
    ZXC_MEMCPY(&v, p, sizeof(v));
1212
#ifdef ZXC_BIG_ENDIAN
1213
    return ZXC_BSWAP64(v);
1214
#else
1215
30.0k
    return v;
1216
30.0k
#endif
1217
30.0k
}
Unexecuted instantiation: zxc_huffman.c:zxc_le64
zxc_pstream.c:zxc_le64
Line
Count
Source
1209
3.12k
static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) {
1210
3.12k
    uint64_t v;
1211
3.12k
    ZXC_MEMCPY(&v, p, sizeof(v));
1212
#ifdef ZXC_BIG_ENDIAN
1213
    return ZXC_BSWAP64(v);
1214
#else
1215
3.12k
    return v;
1216
3.12k
#endif
1217
3.12k
}
zxc_seekable.c:zxc_le64
Line
Count
Source
1209
9.45k
static ZXC_ALWAYS_INLINE uint64_t zxc_le64(const void* p) {
1210
9.45k
    uint64_t v;
1211
9.45k
    ZXC_MEMCPY(&v, p, sizeof(v));
1212
#ifdef ZXC_BIG_ENDIAN
1213
    return ZXC_BSWAP64(v);
1214
#else
1215
9.45k
    return v;
1216
9.45k
#endif
1217
9.45k
}
1218
1219
/**
1220
 * @brief Writes a 16-bit value little-endian to a possibly unaligned address.
1221
 *
1222
 * Mirror of zxc_le16(): the memcpy makes the unaligned store defined, and the
1223
 * value is byte-swapped on big-endian hosts so the wire stays LE.
1224
 *
1225
 * @param[out] p Address to write to, at least 2 bytes.
1226
 * @param[in]  v Value, in host order.
1227
 */
1228
105k
static ZXC_ALWAYS_INLINE void zxc_store_le16(void* p, const uint16_t v) {
1229
#ifdef ZXC_BIG_ENDIAN
1230
    const uint16_t s = ZXC_BSWAP16(v);
1231
    ZXC_MEMCPY(p, &s, sizeof(s));
1232
#else
1233
105k
    ZXC_MEMCPY(p, &v, sizeof(v));
1234
105k
#endif
1235
105k
}
zxc_common.c:zxc_store_le16
Line
Count
Source
1228
69.9k
static ZXC_ALWAYS_INLINE void zxc_store_le16(void* p, const uint16_t v) {
1229
#ifdef ZXC_BIG_ENDIAN
1230
    const uint16_t s = ZXC_BSWAP16(v);
1231
    ZXC_MEMCPY(p, &s, sizeof(s));
1232
#else
1233
69.9k
    ZXC_MEMCPY(p, &v, sizeof(v));
1234
69.9k
#endif
1235
69.9k
}
Unexecuted instantiation: zxc_compress.c:zxc_store_le16
Unexecuted instantiation: zxc_decompress.c:zxc_store_le16
zxc_dict.c:zxc_store_le16
Line
Count
Source
1228
36.0k
static ZXC_ALWAYS_INLINE void zxc_store_le16(void* p, const uint16_t v) {
1229
#ifdef ZXC_BIG_ENDIAN
1230
    const uint16_t s = ZXC_BSWAP16(v);
1231
    ZXC_MEMCPY(p, &s, sizeof(s));
1232
#else
1233
36.0k
    ZXC_MEMCPY(p, &v, sizeof(v));
1234
36.0k
#endif
1235
36.0k
}
Unexecuted instantiation: zxc_driver.c:zxc_store_le16
Unexecuted instantiation: zxc_dispatch.c:zxc_store_le16
Unexecuted instantiation: zxc_huffman.c:zxc_store_le16
Unexecuted instantiation: zxc_pstream.c:zxc_store_le16
Unexecuted instantiation: zxc_seekable.c:zxc_store_le16
1236
1237
/**
1238
 * @brief Writes a 32-bit value little-endian to a possibly unaligned address.
1239
 *
1240
 * Mirror of zxc_le32(); see zxc_store_le16() for the memcpy and endianness
1241
 * rationale.
1242
 *
1243
 * @param[out] p Address to write to, at least 4 bytes.
1244
 * @param[in]  v Value, in host order.
1245
 */
1246
595k
static ZXC_ALWAYS_INLINE void zxc_store_le32(void* p, const uint32_t v) {
1247
#ifdef ZXC_BIG_ENDIAN
1248
    const uint32_t s = ZXC_BSWAP32(v);
1249
    ZXC_MEMCPY(p, &s, sizeof(s));
1250
#else
1251
595k
    ZXC_MEMCPY(p, &v, sizeof(v));
1252
595k
#endif
1253
595k
}
zxc_common.c:zxc_store_le32
Line
Count
Source
1246
503k
static ZXC_ALWAYS_INLINE void zxc_store_le32(void* p, const uint32_t v) {
1247
#ifdef ZXC_BIG_ENDIAN
1248
    const uint32_t s = ZXC_BSWAP32(v);
1249
    ZXC_MEMCPY(p, &s, sizeof(s));
1250
#else
1251
503k
    ZXC_MEMCPY(p, &v, sizeof(v));
1252
503k
#endif
1253
503k
}
zxc_compress.c:zxc_store_le32
Line
Count
Source
1246
11.3k
static ZXC_ALWAYS_INLINE void zxc_store_le32(void* p, const uint32_t v) {
1247
#ifdef ZXC_BIG_ENDIAN
1248
    const uint32_t s = ZXC_BSWAP32(v);
1249
    ZXC_MEMCPY(p, &s, sizeof(s));
1250
#else
1251
11.3k
    ZXC_MEMCPY(p, &v, sizeof(v));
1252
11.3k
#endif
1253
11.3k
}
Unexecuted instantiation: zxc_decompress.c:zxc_store_le32
zxc_dict.c:zxc_store_le32
Line
Count
Source
1246
70.3k
static ZXC_ALWAYS_INLINE void zxc_store_le32(void* p, const uint32_t v) {
1247
#ifdef ZXC_BIG_ENDIAN
1248
    const uint32_t s = ZXC_BSWAP32(v);
1249
    ZXC_MEMCPY(p, &s, sizeof(s));
1250
#else
1251
70.3k
    ZXC_MEMCPY(p, &v, sizeof(v));
1252
70.3k
#endif
1253
70.3k
}
Unexecuted instantiation: zxc_driver.c:zxc_store_le32
Unexecuted instantiation: zxc_dispatch.c:zxc_store_le32
Unexecuted instantiation: zxc_huffman.c:zxc_store_le32
Unexecuted instantiation: zxc_pstream.c:zxc_store_le32
zxc_seekable.c:zxc_store_le32
Line
Count
Source
1246
10.6k
static ZXC_ALWAYS_INLINE void zxc_store_le32(void* p, const uint32_t v) {
1247
#ifdef ZXC_BIG_ENDIAN
1248
    const uint32_t s = ZXC_BSWAP32(v);
1249
    ZXC_MEMCPY(p, &s, sizeof(s));
1250
#else
1251
10.6k
    ZXC_MEMCPY(p, &v, sizeof(v));
1252
10.6k
#endif
1253
10.6k
}
1254
1255
/**
1256
 * @brief Writes a 64-bit value little-endian to a possibly unaligned address.
1257
 *
1258
 * Mirror of zxc_le64(); see zxc_store_le16() for the memcpy and endianness
1259
 * rationale.
1260
 *
1261
 * @param[out] p Address to write to, at least 8 bytes.
1262
 * @param[in]  v Value, in host order.
1263
 */
1264
34.9k
static ZXC_ALWAYS_INLINE void zxc_store_le64(void* p, const uint64_t v) {
1265
#ifdef ZXC_BIG_ENDIAN
1266
    const uint64_t s = ZXC_BSWAP64(v);
1267
    ZXC_MEMCPY(p, &s, sizeof(s));
1268
#else
1269
34.9k
    ZXC_MEMCPY(p, &v, sizeof(v));
1270
34.9k
#endif
1271
34.9k
}
zxc_common.c:zxc_store_le64
Line
Count
Source
1264
34.9k
static ZXC_ALWAYS_INLINE void zxc_store_le64(void* p, const uint64_t v) {
1265
#ifdef ZXC_BIG_ENDIAN
1266
    const uint64_t s = ZXC_BSWAP64(v);
1267
    ZXC_MEMCPY(p, &s, sizeof(s));
1268
#else
1269
34.9k
    ZXC_MEMCPY(p, &v, sizeof(v));
1270
34.9k
#endif
1271
34.9k
}
Unexecuted instantiation: zxc_compress.c:zxc_store_le64
Unexecuted instantiation: zxc_decompress.c:zxc_store_le64
Unexecuted instantiation: zxc_dict.c:zxc_store_le64
Unexecuted instantiation: zxc_driver.c:zxc_store_le64
Unexecuted instantiation: zxc_dispatch.c:zxc_store_le64
Unexecuted instantiation: zxc_huffman.c:zxc_store_le64
Unexecuted instantiation: zxc_pstream.c:zxc_store_le64
Unexecuted instantiation: zxc_seekable.c:zxc_store_le64
1272
1273
/**
1274
 * @brief Computes the 1-byte checksum for block headers.
1275
 *
1276
 * Implementation based on Marsaglia's Xorshift (PRNG) principles.
1277
 *
1278
 * @param[in] p The 8 header bytes to hash.
1279
 * @return The checksum byte.
1280
 */
1281
313k
static ZXC_ALWAYS_INLINE uint8_t zxc_hash8(const uint8_t* p) {
1282
313k
    const uint64_t v = zxc_le64(p);
1283
313k
    uint64_t h = v ^ ZXC_HASH_PRIME1;
1284
313k
    h ^= h << 13;
1285
313k
    h ^= h >> 7;
1286
313k
    h ^= h << 17;
1287
313k
    return (uint8_t)((h >> 32) ^ h);
1288
313k
}
zxc_common.c:zxc_hash8
Line
Count
Source
1281
313k
static ZXC_ALWAYS_INLINE uint8_t zxc_hash8(const uint8_t* p) {
1282
313k
    const uint64_t v = zxc_le64(p);
1283
313k
    uint64_t h = v ^ ZXC_HASH_PRIME1;
1284
313k
    h ^= h << 13;
1285
313k
    h ^= h >> 7;
1286
313k
    h ^= h << 17;
1287
313k
    return (uint8_t)((h >> 32) ^ h);
1288
313k
}
Unexecuted instantiation: zxc_compress.c:zxc_hash8
Unexecuted instantiation: zxc_decompress.c:zxc_hash8
Unexecuted instantiation: zxc_dict.c:zxc_hash8
Unexecuted instantiation: zxc_driver.c:zxc_hash8
Unexecuted instantiation: zxc_dispatch.c:zxc_hash8
Unexecuted instantiation: zxc_huffman.c:zxc_hash8
Unexecuted instantiation: zxc_pstream.c:zxc_hash8
Unexecuted instantiation: zxc_seekable.c:zxc_hash8
1289
1290
/**
1291
 * @brief Computes the 2-byte checksum for file headers.
1292
 *
1293
 * Implementation based on Marsaglia's Xorshift (PRNG) principles.
1294
 *
1295
 * @param[in] p The 16 header bytes to hash.
1296
 * @return The checksum halfword.
1297
 */
1298
122k
static ZXC_ALWAYS_INLINE uint16_t zxc_hash16(const uint8_t* p) {
1299
122k
    const uint64_t v1 = zxc_le64(p);
1300
122k
    const uint64_t v2 = zxc_le64(p + 8);
1301
122k
    uint64_t h = v1 ^ v2 ^ ZXC_HASH_PRIME2;
1302
122k
    h ^= h << 13;
1303
122k
    h ^= h >> 7;
1304
122k
    h ^= h << 17;
1305
122k
    const uint32_t res = (uint32_t)((h >> 32) ^ h);
1306
122k
    return (uint16_t)((res >> 16) ^ res);
1307
122k
}
zxc_common.c:zxc_hash16
Line
Count
Source
1298
88.0k
static ZXC_ALWAYS_INLINE uint16_t zxc_hash16(const uint8_t* p) {
1299
88.0k
    const uint64_t v1 = zxc_le64(p);
1300
88.0k
    const uint64_t v2 = zxc_le64(p + 8);
1301
88.0k
    uint64_t h = v1 ^ v2 ^ ZXC_HASH_PRIME2;
1302
88.0k
    h ^= h << 13;
1303
88.0k
    h ^= h >> 7;
1304
88.0k
    h ^= h << 17;
1305
88.0k
    const uint32_t res = (uint32_t)((h >> 32) ^ h);
1306
88.0k
    return (uint16_t)((res >> 16) ^ res);
1307
88.0k
}
Unexecuted instantiation: zxc_compress.c:zxc_hash16
Unexecuted instantiation: zxc_decompress.c:zxc_hash16
zxc_dict.c:zxc_hash16
Line
Count
Source
1298
34.3k
static ZXC_ALWAYS_INLINE uint16_t zxc_hash16(const uint8_t* p) {
1299
34.3k
    const uint64_t v1 = zxc_le64(p);
1300
34.3k
    const uint64_t v2 = zxc_le64(p + 8);
1301
34.3k
    uint64_t h = v1 ^ v2 ^ ZXC_HASH_PRIME2;
1302
34.3k
    h ^= h << 13;
1303
34.3k
    h ^= h >> 7;
1304
34.3k
    h ^= h << 17;
1305
34.3k
    const uint32_t res = (uint32_t)((h >> 32) ^ h);
1306
34.3k
    return (uint16_t)((res >> 16) ^ res);
1307
34.3k
}
Unexecuted instantiation: zxc_driver.c:zxc_hash16
Unexecuted instantiation: zxc_dispatch.c:zxc_hash16
Unexecuted instantiation: zxc_huffman.c:zxc_hash16
Unexecuted instantiation: zxc_pstream.c:zxc_hash16
Unexecuted instantiation: zxc_seekable.c:zxc_hash16
1308
1309
/**
1310
 * @brief Copies exactly 16 bytes as one vector move where the ISA has one.
1311
 *
1312
 * SSE2 on x86, NEON on ARM, memcpy elsewhere. Fixed width: the caller must
1313
 * have 16 readable and 16 writable bytes.
1314
 *
1315
 * @param[out] dst Pointer to the destination memory block.
1316
 * @param[in] src Pointer to the source memory block.
1317
 */
1318
33.3M
static ZXC_ALWAYS_INLINE void zxc_copy16(void* dst, const void* src) {
1319
33.3M
#if defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512) || defined(ZXC_USE_SSE2)
1320
    // x86 SSE2/AVX2/AVX512: Single 128-bit unaligned load/store
1321
33.3M
    _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src));
1322
#elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32)
1323
    vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src));
1324
#else
1325
    ZXC_MEMCPY(dst, src, 16);
1326
#endif
1327
33.3M
}
Unexecuted instantiation: zxc_common.c:zxc_copy16
Unexecuted instantiation: zxc_compress.c:zxc_copy16
zxc_decompress.c:zxc_copy16
Line
Count
Source
1318
33.3M
static ZXC_ALWAYS_INLINE void zxc_copy16(void* dst, const void* src) {
1319
33.3M
#if defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512) || defined(ZXC_USE_SSE2)
1320
    // x86 SSE2/AVX2/AVX512: Single 128-bit unaligned load/store
1321
33.3M
    _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src));
1322
#elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32)
1323
    vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src));
1324
#else
1325
    ZXC_MEMCPY(dst, src, 16);
1326
#endif
1327
33.3M
}
Unexecuted instantiation: zxc_dict.c:zxc_copy16
Unexecuted instantiation: zxc_driver.c:zxc_copy16
Unexecuted instantiation: zxc_dispatch.c:zxc_copy16
Unexecuted instantiation: zxc_huffman.c:zxc_copy16
Unexecuted instantiation: zxc_pstream.c:zxc_copy16
Unexecuted instantiation: zxc_seekable.c:zxc_copy16
1328
1329
/**
1330
 * @brief Copies 32 bytes from source to destination using SIMD when available.
1331
 *
1332
 * Uses AVX2 on x86, NEON on ARM64/ARM32, or two 16-byte copies as fallback.
1333
 *
1334
 * @param[out] dst Pointer to the destination memory block.
1335
 * @param[in] src Pointer to the source memory block.
1336
 */
1337
135M
static ZXC_ALWAYS_INLINE void zxc_copy32(void* dst, const void* src) {
1338
#if defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512)
1339
    // AVX2/AVX512: Single 256-bit (32 byte) unaligned load/store
1340
    _mm256_storeu_si256((__m256i*)dst, _mm256_loadu_si256((const __m256i*)src));
1341
#elif defined(ZXC_USE_SSE2)
1342
    // SSE2: Two 128-bit (16 byte) unaligned load/stores (no 256-bit regs)
1343
135M
    _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src));
1344
135M
    _mm_storeu_si128((__m128i*)((uint8_t*)dst + 16),
1345
135M
                     _mm_loadu_si128((const __m128i*)((const uint8_t*)src + 16)));
1346
#elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32)
1347
    // NEON: Two 128-bit (16 byte) unaligned load/stores
1348
    vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src));
1349
    vst1q_u8((uint8_t*)dst + 16, vld1q_u8((const uint8_t*)src + 16));
1350
#else
1351
    ZXC_MEMCPY(dst, src, 32);
1352
#endif
1353
135M
}
Unexecuted instantiation: zxc_common.c:zxc_copy32
zxc_compress.c:zxc_copy32
Line
Count
Source
1337
5.58M
static ZXC_ALWAYS_INLINE void zxc_copy32(void* dst, const void* src) {
1338
#if defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512)
1339
    // AVX2/AVX512: Single 256-bit (32 byte) unaligned load/store
1340
    _mm256_storeu_si256((__m256i*)dst, _mm256_loadu_si256((const __m256i*)src));
1341
#elif defined(ZXC_USE_SSE2)
1342
    // SSE2: Two 128-bit (16 byte) unaligned load/stores (no 256-bit regs)
1343
5.58M
    _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src));
1344
5.58M
    _mm_storeu_si128((__m128i*)((uint8_t*)dst + 16),
1345
5.58M
                     _mm_loadu_si128((const __m128i*)((const uint8_t*)src + 16)));
1346
#elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32)
1347
    // NEON: Two 128-bit (16 byte) unaligned load/stores
1348
    vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src));
1349
    vst1q_u8((uint8_t*)dst + 16, vld1q_u8((const uint8_t*)src + 16));
1350
#else
1351
    ZXC_MEMCPY(dst, src, 32);
1352
#endif
1353
5.58M
}
zxc_decompress.c:zxc_copy32
Line
Count
Source
1337
130M
static ZXC_ALWAYS_INLINE void zxc_copy32(void* dst, const void* src) {
1338
#if defined(ZXC_USE_AVX2) || defined(ZXC_USE_AVX512)
1339
    // AVX2/AVX512: Single 256-bit (32 byte) unaligned load/store
1340
    _mm256_storeu_si256((__m256i*)dst, _mm256_loadu_si256((const __m256i*)src));
1341
#elif defined(ZXC_USE_SSE2)
1342
    // SSE2: Two 128-bit (16 byte) unaligned load/stores (no 256-bit regs)
1343
130M
    _mm_storeu_si128((__m128i*)dst, _mm_loadu_si128((const __m128i*)src));
1344
130M
    _mm_storeu_si128((__m128i*)((uint8_t*)dst + 16),
1345
130M
                     _mm_loadu_si128((const __m128i*)((const uint8_t*)src + 16)));
1346
#elif defined(ZXC_USE_NEON64) || defined(ZXC_USE_NEON32)
1347
    // NEON: Two 128-bit (16 byte) unaligned load/stores
1348
    vst1q_u8((uint8_t*)dst, vld1q_u8((const uint8_t*)src));
1349
    vst1q_u8((uint8_t*)dst + 16, vld1q_u8((const uint8_t*)src + 16));
1350
#else
1351
    ZXC_MEMCPY(dst, src, 32);
1352
#endif
1353
130M
}
Unexecuted instantiation: zxc_dict.c:zxc_copy32
Unexecuted instantiation: zxc_driver.c:zxc_copy32
Unexecuted instantiation: zxc_dispatch.c:zxc_copy32
Unexecuted instantiation: zxc_huffman.c:zxc_copy32
Unexecuted instantiation: zxc_pstream.c:zxc_copy32
Unexecuted instantiation: zxc_seekable.c:zxc_copy32
1354
1355
/**
1356
 * @brief Counts trailing zeros, returning 32 for a zero input.
1357
 *
1358
 * The zero case is the reason for the guard: both `__builtin_ctz` and
1359
 * `_BitScanForward` leave the result undefined there.
1360
 *
1361
 * @param[in] x Value to scan.
1362
 * @return Trailing zero count, in [0, 32].
1363
 */
1364
13.0M
static ZXC_ALWAYS_INLINE int zxc_ctz32(const uint32_t x) {
1365
13.0M
    if (x == 0) return 32;
1366
13.0M
#if defined(__GNUC__) || defined(__clang__)
1367
13.0M
    return __builtin_ctz(x);
1368
#elif defined(_MSC_VER)
1369
    unsigned long r;
1370
    _BitScanForward(&r, x);
1371
    return (int)r;
1372
#else
1373
    // Fallback De Bruijn (32 bits)
1374
    static const int DeBruijn32[32] = {0,  1,  28, 2,  29, 14, 24, 3,  30, 22, 20,
1375
                                       15, 25, 17, 4,  8,  31, 27, 13, 23, 21, 19,
1376
                                       16, 7,  26, 12, 18, 6,  11, 5,  10, 9};
1377
    return DeBruijn32[((uint32_t)((x & (0U - x)) * 0x077CB531U)) >> 27];
1378
#endif
1379
13.0M
}
Unexecuted instantiation: zxc_common.c:zxc_ctz32
zxc_compress.c:zxc_ctz32
Line
Count
Source
1364
13.0M
static ZXC_ALWAYS_INLINE int zxc_ctz32(const uint32_t x) {
1365
13.0M
    if (x == 0) return 32;
1366
13.0M
#if defined(__GNUC__) || defined(__clang__)
1367
13.0M
    return __builtin_ctz(x);
1368
#elif defined(_MSC_VER)
1369
    unsigned long r;
1370
    _BitScanForward(&r, x);
1371
    return (int)r;
1372
#else
1373
    // Fallback De Bruijn (32 bits)
1374
    static const int DeBruijn32[32] = {0,  1,  28, 2,  29, 14, 24, 3,  30, 22, 20,
1375
                                       15, 25, 17, 4,  8,  31, 27, 13, 23, 21, 19,
1376
                                       16, 7,  26, 12, 18, 6,  11, 5,  10, 9};
1377
    return DeBruijn32[((uint32_t)((x & (0U - x)) * 0x077CB531U)) >> 27];
1378
#endif
1379
13.0M
}
Unexecuted instantiation: zxc_decompress.c:zxc_ctz32
Unexecuted instantiation: zxc_dict.c:zxc_ctz32
Unexecuted instantiation: zxc_driver.c:zxc_ctz32
Unexecuted instantiation: zxc_dispatch.c:zxc_ctz32
Unexecuted instantiation: zxc_huffman.c:zxc_ctz32
Unexecuted instantiation: zxc_pstream.c:zxc_ctz32
Unexecuted instantiation: zxc_seekable.c:zxc_ctz32
1380
1381
/**
1382
 * @brief Counts trailing zeros, returning 64 for a zero input.
1383
 *
1384
 * The zero case is the reason for the guard: both `__builtin_ctzll` and
1385
 * `_BitScanForward64` leave the result undefined there.
1386
 *
1387
 * @param[in] x Value to scan.
1388
 * @return Trailing zero count, in [0, 64].
1389
 */
1390
1.62G
static ZXC_ALWAYS_INLINE int zxc_ctz64(const uint64_t x) {
1391
1.62G
    if (x == 0) return 64;
1392
1.62G
#if defined(__GNUC__) || defined(__clang__)
1393
1.62G
    return __builtin_ctzll(x);
1394
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
1395
    unsigned long r;
1396
    _BitScanForward64(&r, x);
1397
    return (int)r;
1398
#elif defined(_MSC_VER)
1399
    // Use two 32-bit scans to avoid fragile 64-bit De Bruijn multiplication.
1400
    unsigned long r;
1401
    const uint32_t lo = (uint32_t)x;
1402
    if (_BitScanForward(&r, lo)) return (int)r;
1403
    _BitScanForward(&r, (uint32_t)(x >> 32));
1404
    return 32 + (int)r;
1405
#else
1406
    // Fallback De Bruijn for non-GCC/non-MSVC compilers
1407
    static const int Debruijn64[64] = {
1408
        0,  1,  48, 2,  57, 49, 28, 3,  61, 58, 50, 42, 38, 29, 17, 4,  62, 55, 59, 36, 53, 51,
1409
        43, 22, 45, 39, 33, 30, 24, 18, 12, 5,  63, 47, 56, 27, 60, 41, 37, 16, 54, 35, 52, 21,
1410
        44, 32, 23, 11, 46, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9,  13, 8,  7,  6};
1411
    return Debruijn64[((x & (0ULL - x)) * 0x03F79D71B4CA8B09ULL) >> 58];
1412
#endif
1413
1.62G
}
Unexecuted instantiation: zxc_common.c:zxc_ctz64
zxc_compress.c:zxc_ctz64
Line
Count
Source
1390
1.62G
static ZXC_ALWAYS_INLINE int zxc_ctz64(const uint64_t x) {
1391
1.62G
    if (x == 0) return 64;
1392
1.62G
#if defined(__GNUC__) || defined(__clang__)
1393
1.62G
    return __builtin_ctzll(x);
1394
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_ARM64))
1395
    unsigned long r;
1396
    _BitScanForward64(&r, x);
1397
    return (int)r;
1398
#elif defined(_MSC_VER)
1399
    // Use two 32-bit scans to avoid fragile 64-bit De Bruijn multiplication.
1400
    unsigned long r;
1401
    const uint32_t lo = (uint32_t)x;
1402
    if (_BitScanForward(&r, lo)) return (int)r;
1403
    _BitScanForward(&r, (uint32_t)(x >> 32));
1404
    return 32 + (int)r;
1405
#else
1406
    // Fallback De Bruijn for non-GCC/non-MSVC compilers
1407
    static const int Debruijn64[64] = {
1408
        0,  1,  48, 2,  57, 49, 28, 3,  61, 58, 50, 42, 38, 29, 17, 4,  62, 55, 59, 36, 53, 51,
1409
        43, 22, 45, 39, 33, 30, 24, 18, 12, 5,  63, 47, 56, 27, 60, 41, 37, 16, 54, 35, 52, 21,
1410
        44, 32, 23, 11, 46, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9,  13, 8,  7,  6};
1411
    return Debruijn64[((x & (0ULL - x)) * 0x03F79D71B4CA8B09ULL) >> 58];
1412
#endif
1413
1.62G
}
Unexecuted instantiation: zxc_decompress.c:zxc_ctz64
Unexecuted instantiation: zxc_dict.c:zxc_ctz64
Unexecuted instantiation: zxc_driver.c:zxc_ctz64
Unexecuted instantiation: zxc_dispatch.c:zxc_ctz64
Unexecuted instantiation: zxc_huffman.c:zxc_ctz64
Unexecuted instantiation: zxc_pstream.c:zxc_ctz64
Unexecuted instantiation: zxc_seekable.c:zxc_ctz64
1414
1415
/**
1416
 * @brief Allocates aligned memory (`_aligned_malloc` on Windows, else `posix_memalign`).
1417
 *
1418
 * @param[in] size      Bytes to allocate.
1419
 * @param[in] alignment Power of two, and a multiple of `sizeof(void*)`.
1420
 * @return The block, or NULL on failure. Free it with zxc_aligned_free(), not
1421
 *         `free()`: the Windows allocator is a separate one.
1422
 */
1423
void* zxc_aligned_malloc(const size_t size, const size_t alignment);
1424
1425
/**
1426
 * @brief Frees a zxc_aligned_malloc() block (`_aligned_free` on Windows, else `free`).
1427
 *
1428
 * @param[in] ptr Block to free; NULL is a no-op.
1429
 */
1430
void zxc_aligned_free(void* ptr);
1431
1432
// ============================================================================
1433
// COMPRESSION CONTEXT & STRUCTS
1434
// ============================================================================
1435
1436
// INTERNAL API
1437
// ------------
1438
1439
/**
1440
 * @brief Calculates a 32-bit hash for a given input buffer.
1441
 * @param[in] input Pointer to the data buffer.
1442
 * @param[in] len Length of the data in bytes.
1443
 * @param[in] hash_method Checksum algorithm identifier (e.g., ZXC_CHECKSUM_RAPIDHASH).
1444
 * @return The calculated 32-bit hash value.
1445
 */
1446
static ZXC_ALWAYS_INLINE uint32_t zxc_checksum(const void* RESTRICT input, const size_t len,
1447
69.1k
                                               const uint8_t hash_method) {
1448
69.1k
    (void)hash_method; /* single algorithm for now; extend when adding more */
1449
69.1k
    const uint64_t hash = rapidhash(input, len);
1450
1451
69.1k
    return (uint32_t)(hash ^ (hash >> (sizeof(uint32_t) * CHAR_BIT)));
1452
69.1k
}
Unexecuted instantiation: zxc_common.c:zxc_checksum
zxc_compress.c:zxc_checksum
Line
Count
Source
1447
11.3k
                                               const uint8_t hash_method) {
1448
11.3k
    (void)hash_method; /* single algorithm for now; extend when adding more */
1449
11.3k
    const uint64_t hash = rapidhash(input, len);
1450
1451
    return (uint32_t)(hash ^ (hash >> (sizeof(uint32_t) * CHAR_BIT)));
1452
11.3k
}
zxc_decompress.c:zxc_checksum
Line
Count
Source
1447
5.93k
                                               const uint8_t hash_method) {
1448
5.93k
    (void)hash_method; /* single algorithm for now; extend when adding more */
1449
5.93k
    const uint64_t hash = rapidhash(input, len);
1450
1451
    return (uint32_t)(hash ^ (hash >> (sizeof(uint32_t) * CHAR_BIT)));
1452
5.93k
}
zxc_dict.c:zxc_checksum
Line
Count
Source
1447
51.8k
                                               const uint8_t hash_method) {
1448
51.8k
    (void)hash_method; /* single algorithm for now; extend when adding more */
1449
51.8k
    const uint64_t hash = rapidhash(input, len);
1450
1451
    return (uint32_t)(hash ^ (hash >> (sizeof(uint32_t) * CHAR_BIT)));
1452
51.8k
}
Unexecuted instantiation: zxc_driver.c:zxc_checksum
Unexecuted instantiation: zxc_dispatch.c:zxc_checksum
Unexecuted instantiation: zxc_huffman.c:zxc_checksum
Unexecuted instantiation: zxc_pstream.c:zxc_checksum
Unexecuted instantiation: zxc_seekable.c:zxc_checksum
1453
1454
/**
1455
 * @brief Seeded variant of @ref zxc_checksum, for chaining a hash over
1456
 *        non-contiguous buffers: `zxc_checksum_seed(b, bn, zxc_checksum(a, an, m), m)`
1457
 *        hashes each byte once without a concat copy.
1458
 * @param[in] input Pointer to the data buffer.
1459
 * @param[in] len Length of the data in bytes.
1460
 * @param[in] seed Previous 32-bit checksum to chain from.
1461
 * @param[in] hash_method Checksum algorithm identifier (e.g., ZXC_CHECKSUM_RAPIDHASH).
1462
 * @return The calculated 32-bit hash value.
1463
 */
1464
static ZXC_ALWAYS_INLINE uint32_t zxc_checksum_seed(const void* RESTRICT input, const size_t len,
1465
                                                    const uint32_t seed,
1466
33.8k
                                                    const uint8_t hash_method) {
1467
33.8k
    (void)hash_method; /* single algorithm for now; extend when adding more */
1468
33.8k
    const uint64_t hash = rapidhash_withSeed(input, len, seed);
1469
1470
33.8k
    return (uint32_t)(hash ^ (hash >> (sizeof(uint32_t) * CHAR_BIT)));
1471
33.8k
}
Unexecuted instantiation: zxc_common.c:zxc_checksum_seed
Unexecuted instantiation: zxc_compress.c:zxc_checksum_seed
Unexecuted instantiation: zxc_decompress.c:zxc_checksum_seed
zxc_dict.c:zxc_checksum_seed
Line
Count
Source
1466
33.8k
                                                    const uint8_t hash_method) {
1467
33.8k
    (void)hash_method; /* single algorithm for now; extend when adding more */
1468
33.8k
    const uint64_t hash = rapidhash_withSeed(input, len, seed);
1469
1470
    return (uint32_t)(hash ^ (hash >> (sizeof(uint32_t) * CHAR_BIT)));
1471
33.8k
}
Unexecuted instantiation: zxc_driver.c:zxc_checksum_seed
Unexecuted instantiation: zxc_dispatch.c:zxc_checksum_seed
Unexecuted instantiation: zxc_huffman.c:zxc_checksum_seed
Unexecuted instantiation: zxc_pstream.c:zxc_checksum_seed
Unexecuted instantiation: zxc_seekable.c:zxc_checksum_seed
1472
1473
/**
1474
 * @brief Folds a block hash into the running global checksum.
1475
 *
1476
 * `result = rotl32(hash, 1) ^ block_hash`. The rotate is what makes the result
1477
 * depend on block order, so a reordered archive fails the global check.
1478
 *
1479
 * @param[in] hash The current running hash value.
1480
 * @param[in] block_hash The hash of the new block to combine.
1481
 * @return The updated combined hash value.
1482
 */
1483
static ZXC_ALWAYS_INLINE uint32_t zxc_hash_combine_rotate(const uint32_t hash,
1484
17.2k
                                                          const uint32_t block_hash) {
1485
17.2k
    return ((hash << 1) | (hash >> 31)) ^ block_hash;
1486
17.2k
}
Unexecuted instantiation: zxc_common.c:zxc_hash_combine_rotate
Unexecuted instantiation: zxc_compress.c:zxc_hash_combine_rotate
Unexecuted instantiation: zxc_decompress.c:zxc_hash_combine_rotate
Unexecuted instantiation: zxc_dict.c:zxc_hash_combine_rotate
Unexecuted instantiation: zxc_driver.c:zxc_hash_combine_rotate
zxc_dispatch.c:zxc_hash_combine_rotate
Line
Count
Source
1484
13.9k
                                                          const uint32_t block_hash) {
1485
13.9k
    return ((hash << 1) | (hash >> 31)) ^ block_hash;
1486
13.9k
}
Unexecuted instantiation: zxc_huffman.c:zxc_hash_combine_rotate
zxc_pstream.c:zxc_hash_combine_rotate
Line
Count
Source
1484
3.27k
                                                          const uint32_t block_hash) {
1485
3.27k
    return ((hash << 1) | (hash >> 31)) ^ block_hash;
1486
3.27k
}
Unexecuted instantiation: zxc_seekable.c:zxc_hash_combine_rotate
1487
1488
/**
1489
 * @brief Writes a GLO sub-header followed by its section descriptors.
1490
 *
1491
 * They hold only the two sizes the header cannot imply, and are 0, 4 or 8 bytes
1492
 * wide accordingly.
1493
 *
1494
 * @param[out] dst      Pointer to the destination buffer.
1495
 * @param[in]  rem      The remaining space in the destination buffer.
1496
 * @param[in]  gh       Pointer to the generic header structure to write.
1497
 * @param[in]  lit_comp Compressed size of the literal section.
1498
 * @param[in]  tok_comp Compressed size of the token section.
1499
 * @return The number of bytes written, or a negative error code if the buffer
1500
 *         is too small.
1501
 */
1502
int zxc_write_glo_header_and_desc(uint8_t* RESTRICT dst, const size_t rem,
1503
                                  const zxc_gnr_header_t* RESTRICT gh, const uint32_t lit_comp,
1504
                                  const uint32_t tok_comp);
1505
1506
/**
1507
 * @brief Reads a GLO sub-header and its section descriptors from a source buffer.
1508
 *
1509
 * Sizes absent from the descriptors are reconstructed from the header, so both
1510
 * outputs are always populated.
1511
 *
1512
 * @param[in]  src      Pointer to the source buffer.
1513
 * @param[in]  len      The length of the source buffer available for reading.
1514
 * @param[out] gh       Pointer to the generic header structure to populate.
1515
 * @param[out] lit_comp Receives the literal section's compressed size.
1516
 * @param[out] tok_comp Receives the token section's compressed size.
1517
 * @return Bytes consumed (header + table), or a negative zxc_error_t code.
1518
 */
1519
int zxc_read_glo_header_and_desc(const uint8_t* RESTRICT src, const size_t len,
1520
                                 zxc_gnr_header_t* RESTRICT gh, uint32_t* RESTRICT lit_comp,
1521
                                 uint32_t* RESTRICT tok_comp);
1522
1523
/**
1524
 * @brief Writes a GHI sub-header. GHI carries no section descriptors.
1525
 *
1526
 * @param[out] dst Pointer to the destination buffer.
1527
 * @param[in]  rem Remaining size available in the destination buffer.
1528
 * @param[in]  gh  Pointer to the GNR header structure containing header information.
1529
 * @return The number of bytes written, or a negative error code on failure.
1530
 */
1531
int zxc_write_ghi_header(uint8_t* RESTRICT dst, const size_t rem,
1532
                         const zxc_gnr_header_t* RESTRICT gh);
1533
1534
/**
1535
 * @brief Reads a GHI sub-header from a buffer.
1536
 *
1537
 * @param[in]  src Pointer to the source buffer containing the record data.
1538
 * @param[in]  len Length of the source buffer in bytes.
1539
 * @param[out] gh  Pointer to a zxc_gnr_header_t structure to store the parsed header.
1540
 * @return ZXC_OK on success, or a negative zxc_error_t code on failure.
1541
 */
1542
int zxc_read_ghi_header(const uint8_t* RESTRICT src, const size_t len,
1543
                        zxc_gnr_header_t* RESTRICT gh);
1544
1545
// ============================================================================
1546
// Huffman codec for the GLO literal stream (level >= 6).
1547
//
1548
// On-disk layout, decoder geometry and tunables: see
1549
// @ref ZXC_HUF_MAX_CODE_LEN_ULTRA and the surrounding "Huffman Codec Constants"
1550
// group above.
1551
// ============================================================================
1552
1553
/**
1554
 * @brief Build length-limited canonical Huffman code lengths from a frequency table.
1555
 *
1556
 * Uses the boundary package-merge algorithm capped at `ZXC_HUF_MAX_CODE_LEN_ULTRA`.
1557
 * Symbols with `freq[i] == 0` get `code_len[i] == 0`; others receive a value
1558
 * in `[1, ZXC_HUF_MAX_CODE_LEN_ULTRA]`.
1559
 *
1560
 * @param[in]  freq     Frequency table of length `ZXC_HUF_NUM_SYMBOLS`.
1561
 * @param[out] code_len Output code-length array of length `ZXC_HUF_NUM_SYMBOLS`.
1562
 * @param[in]  scratch  Optional caller-owned scratch buffer of at least
1563
 *                      ::ZXC_HUF_BUILD_SCRATCH_SIZE bytes. If `NULL`, the
1564
 *                      function allocates its own working memory and frees
1565
 *                      it before returning.
1566
 * @param[in]  max_code_len Code-length ceiling in `[1, ZXC_HUF_MAX_CODE_LEN_ULTRA]`;
1567
 *                      package-merge is run for this many levels (see
1568
 *                      ::zxc_huf_enc_max_code_len for the per-level value).
1569
 * @return `ZXC_OK` on success, negative `zxc_error_t` code on failure.
1570
 */
1571
int zxc_huf_build_code_lengths(const uint32_t* RESTRICT freq, uint8_t* RESTRICT code_len,
1572
                               void* RESTRICT scratch, int max_code_len);
1573
1574
/**
1575
 * @brief Optionally reshape freshly built code lengths for faster PivCo decode.
1576
 *
1577
 * Explores a small set of Kraft-exact alternatives to @p code_len (a greedy
1578
 * slot-ledger walk toward power-of-two class counts, package-merge rebuilds
1579
 * at reduced depth caps, and the slot-ledger dynamic program at a coarse
1580
 * granularity picked from the alphabet size) and prices each against the
1581
 * modeled decode cost of zxc_pivco_decode_core. The cheapest candidate that
1582
 * clears the adoption guard (<= +::ZXC_HUF_NUDGE_BITS_PERMIL ratio cost AND
1583
 * <= ::ZXC_HUF_NUDGE_MERGE_Q8 modeled level-touches) replaces @p code_len;
1584
 * otherwise the array is left byte-for-byte untouched, so a rejected nudge
1585
 * emits an archive identical to the unadjusted encoder. Coarse-DP candidates
1586
 * may pad the tree with zero-frequency "ghost" leaves on unused byte values;
1587
 * the wire carries them as empty runs.
1588
 *
1589
 * Encoder policy only: any adopted output is canonical, Kraft-exact and capped
1590
 * at @p max_code_len, so the wire format and every deployed decoder are
1591
 * unaffected. Compiled once in the primary variant (ISA-independent decision
1592
 * code), guaranteeing cross-ISA identical archives. Cost is a few hundred
1593
 * microseconds per table at most (DP plane sizes are capped by the coarse
1594
 * granularity), which the ULTRA-only and trainer call sites absorb.
1595
 *
1596
 * @param[in]     freq         Frequency table of length `ZXC_HUF_NUM_SYMBOLS`.
1597
 * @param[in,out] code_len     Lengths from ::zxc_huf_build_code_lengths.
1598
 * @param[in]     scratch      Optional ::ZXC_HUF_BUILD_SCRATCH_SIZE scratch for
1599
 *                             the reduced-cap rebuilds (NULL = allocate).
1600
 * @param[in]     max_code_len Cap the caller built with (level cap).
1601
 * @return 1 if @p code_len was adjusted, 0 if kept.
1602
 */
1603
int zxc_huf_nudge_code_lengths(const uint32_t* RESTRICT freq, uint8_t* RESTRICT code_len,
1604
                               void* RESTRICT scratch, int max_code_len);
1605
1606
/**
1607
 * @brief Modeled (bits, level-touches) decode cost of one code-length vector.
1608
 *
1609
 * Introspection hook for the nudge's cost model (exact, canonical-order
1610
 * frequency weighting); the unit tests cross-check it against the real
1611
 * tree built by the decoder. @p code_len must be structurally valid.
1612
 */
1613
void zxc_huf_nudge_cost(const uint8_t* RESTRICT code_len, const uint32_t* RESTRICT freq,
1614
                        uint64_t* RESTRICT bits, uint64_t* RESTRICT touches);
1615
1616
/**
1617
 * @brief Pack per-symbol code lengths into the 128-byte (4-bit nibble) header.
1618
 *
1619
 * Nibble order follows the byte: `code_len[2*i]` low, `code_len[2*i + 1]` high.
1620
 * Lengths above 15 are silently truncated, so the caller must have capped at
1621
 * `ZXC_HUF_MAX_CODE_LEN_ULTRA` (<= 15) first.
1622
 *
1623
 * @param[in]  code_len Per-symbol lengths, `ZXC_HUF_NUM_SYMBOLS` entries.
1624
 * @param[out] out      `ZXC_HUF_TABLE_SIZE` bytes of packed header.
1625
 */
1626
void zxc_huf_pack_lengths(const uint8_t* RESTRICT code_len, uint8_t* RESTRICT out);
1627
1628
/**
1629
 * @brief Unpack and structurally validate a 128-byte packed lengths header.
1630
 *
1631
 * Inverts ::zxc_huf_pack_lengths and checks the two structural invariants: no
1632
 * length above `ZXC_HUF_MAX_CODE_LEN_ULTRA`, and at least one symbol present.
1633
 * Kraft consistency is not checked here; the tree build does that later.
1634
 *
1635
 * @param[in]  in       128-byte packed header.
1636
 * @param[out] code_len Per-symbol lengths, `ZXC_HUF_NUM_SYMBOLS` entries.
1637
 * @return `ZXC_OK`, or `ZXC_ERROR_CORRUPT_DATA` if a length is too large or the
1638
 *         table is empty.
1639
 */
1640
int zxc_huf_unpack_lengths(const uint8_t* RESTRICT in, uint8_t* RESTRICT code_len);
1641
1642
// --------------------------------------------------------------------------
1643
// PivCo-Huffman section codec (enc 2/3)
1644
//
1645
// Layout from PivCo-Huffman by Marcin Zukowski
1646
// (https://github.com/MarcinZukowski/pivco-huffman); implemented
1647
// independently here. See zxc_huffman.c for the codec.
1648
//
1649
// Same code bits as canonical Huffman, reordered by tree LEVEL: for each
1650
// internal node in BFS order, its branch bits (one per symbol routed through
1651
// it, LSB-first, byte-aligned). No size fields - the decoder derives every run
1652
// length from the root count and popcounts. Decoding is bottom-up level merges,
1653
// shuffle-parallel and gather-free.
1654
// --------------------------------------------------------------------------
1655
1656
/** @brief Extra scratch slack required past `n` by the PivCo decoder. */
1657
7.62k
#define ZXC_PIVCO_SCRATCH_PAD 32
1658
1659
/** @brief Exact encoded size (bytes) of a PivCo section for this histogram and
1660
 *  code lengths; includes the 128-byte lengths header when @p with_header. */
1661
size_t zxc_huf_calc_size(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1662
                         int with_header);
1663
1664
/** @brief Encode a PivCo literal section (128-byte lengths header + payload). */
1665
int zxc_huf_encode_section(const uint8_t* RESTRICT literals, size_t n_literals,
1666
                           const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1667
                           uint8_t* RESTRICT dst, size_t dst_cap);
1668
1669
/** @brief Unpack a dict table's 128-byte packed lengths and prebuild its PivCo
1670
 *  tree, canonical codes, code lengths and decoder tables (tree-at-attach).
1671
 *  All outputs are frame-constant; per-block encode/estimate/decode then skip
1672
 *  the rebuild. */
1673
int zxc_huf_dict_tree_build(const uint8_t* RESTRICT packed_lengths, zxc_pivco_tree_t* RESTRICT tree,
1674
                            uint32_t* RESTRICT codes, uint8_t* RESTRICT code_len,
1675
                            zxc_pivco_decode_aux_t* RESTRICT aux);
1676
1677
/** @brief zxc_huf_calc_size for a dict section: prebuilt @p tree, no header. */
1678
size_t zxc_huf_calc_size_dict(const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1679
                              const zxc_pivco_tree_t* RESTRICT tree);
1680
1681
/** @brief Encode a PivCo section against a prebuilt dict tree/codes (no header). */
1682
int zxc_huf_encode_section_dict(const uint8_t* RESTRICT literals, size_t n_literals,
1683
                                const uint32_t* RESTRICT freq, const uint8_t* RESTRICT code_len,
1684
                                const zxc_pivco_tree_t* RESTRICT tree,
1685
                                const uint32_t* RESTRICT codes, uint8_t* RESTRICT dst,
1686
                                size_t dst_cap);
1687
1688
/** @brief Decode a PivCo literal section into @p dst (exactly @p n bytes).
1689
 *  @p dst needs ZXC_PAD_SIZE slack, @p scratch at least n + ZXC_PIVCO_SCRATCH_PAD. */
1690
int zxc_huf_decode_section(const uint8_t* RESTRICT payload, size_t payload_size,
1691
                           uint8_t* RESTRICT dst, size_t n, uint8_t* RESTRICT scratch);
1692
1693
/** @brief Decode a PivCo dict section against a prebuilt dict @p tree and its
1694
 *  attach-time decoder tables @p aux. */
1695
int zxc_huf_decode_section_dict(const uint8_t* RESTRICT payload, size_t payload_size,
1696
                                uint8_t* RESTRICT dst, size_t n,
1697
                                const zxc_pivco_tree_t* RESTRICT tree,
1698
                                const zxc_pivco_decode_aux_t* RESTRICT aux,
1699
                                uint8_t* RESTRICT scratch);
1700
1701
// ---------------------------------------------------------------------------
1702
// Compression / decompression context.
1703
//
1704
// The context owns the working buffers (hash table, sequence buffers, scratch)
1705
// that encoder and decoder reuse across blocks. It stays private - the public
1706
// APIs already wrap it opaquely - so the layout can evolve (cache-line
1707
// placement, extra scratch arenas) without breaking the ABI.
1708
// ---------------------------------------------------------------------------
1709
1710
/**
1711
 * @struct zxc_cctx_t
1712
 * @brief Compression / decompression context.
1713
 *
1714
 * Holds the buffers reused across blocks to avoid repeated allocations.
1715
 *
1716
 * **Key fields:**
1717
 * - @c hash_table: epoch-tagged positions (`ZXC_LZ_HASH_SIZE` * 4 bytes).
1718
 * - @c hash_tags:  8-bit tags for fast match rejection
1719
 *   (`ZXC_LZ_HASH_SIZE` * 1 byte).
1720
 * - @c chain_table: collision chain storing the *previous* occurrence of a
1721
 *   hash, forming a linked list per bucket and enabling history traversal.
1722
 * - @c epoch: drives "lazy hash table invalidation". Instead of memset-ing
1723
 *   the hash table for every block, we store `(epoch << offset_bits) | offset`; an
1724
 *   entry whose stored epoch differs from `ctx->epoch` is treated as empty.
1725
 */
1726
typedef struct {
1727
    // Hot zone: random access / high frequency.
1728
    // Kept at the start to ensure they reside in the first cache line (64 bytes).
1729
    uint32_t* hash_table;  /**< Hash table for LZ77 match positions (epoch|pos). */
1730
    uint8_t* hash_tags;    /**< Split tag table for fast match rejection (8-bit tags). */
1731
    uint16_t* chain_table; /**< Chain table for collision resolution. */
1732
    void* memory_block;    /**< Single allocation block owner. */
1733
    uint32_t epoch;        /**< Current epoch for lazy hash table invalidation. */
1734
1735
    // Warm zone: sequential access per sequence.
1736
    uint32_t* buf_sequences; /**< Buffer for sequence records (packed: LL(8)|ML(8)|Offset(16)). */
1737
    uint8_t* buf_tokens;     /**< Buffer for token sequences. */
1738
    uint16_t* buf_offsets;   /**< Buffer for offsets. */
1739
    uint8_t* buf_extras;     /**< Buffer for extra lengths (vbytes for LL/ML). */
1740
    uint8_t* literals;       /**< Buffer for literal bytes. */
1741
1742
    // Cold zone: configuration / scratch / resizeable.
1743
    uint8_t* lit_buffer;            /**< Scratch buffer for literals (RLE / Huffman). */
1744
    size_t lit_buffer_cap;          /**< Current capacity of the scratch buffer. */
1745
    uint8_t* work_buf;              /**< Padded scratch buffer for buffer-API decompression. */
1746
    size_t work_buf_cap;            /**< Capacity of the work buffer. */
1747
    uint8_t* tok_buffer;            /**< Decode scratch for a Huffman-coded GLO token
1748
                                         section (enc_tok == HUFFMAN); NULL on compress.
1749
                                         Heap decode contexts defer it (with pivco_scratch)
1750
                                         to the first entropy section, see entropy_block. */
1751
    size_t tok_buffer_cap;          /**< Capacity of tok_buffer in bytes. */
1752
    uint8_t* pivco_scratch;         /**< Level ping-pong scratch for PivCo decode. */
1753
    size_t pivco_scratch_cap;       /**< Capacity of pivco_scratch in bytes. */
1754
    void* entropy_block;            /**< Lazy allocation backing tok_buffer + pivco_scratch
1755
                                         (heap decode contexts, first entropy block only).
1756
                                         NULL on compress contexts and static workspaces.
1757
                                         Freed by zxc_cctx_free. */
1758
    uint8_t* opt_scratch;           /**< Optimal-parser DP scratch (level >= 6 only,
1759
                                         lazy-allocated, packs dp/parent_len/parent_off/actions).
1760
                                         Also reused as transient scratch for the
1761
                                         length-limited Huffman code-length builder. */
1762
    size_t opt_scratch_cap;         /**< Current capacity of opt_scratch in bytes. */
1763
    int checksum_enabled;           /**< 1 if checksum calculation/verification is enabled. */
1764
    int compression_level;          /**< Compression level. */
1765
    size_t dict_size;               /**< Dictionary prefill size (0 = no dictionary). */
1766
    uint8_t* dict_buffer;           /**< [dict | data] concat scratch carved from memory_block
1767
                                         when dict_size > 0 (NULL otherwise). */
1768
    size_t dict_buffer_cap;         /**< Capacity of dict_buffer in bytes (0 = none). */
1769
    zxc_dict_huf_state_t* dict_huf; /**< Tree-at-attach state (PivCo tree + codes + code
1770
                                         lengths), carved from the workspace only when
1771
                                         dict_size > 0 (NULL otherwise); built once by
1772
                                         zxc_cctx_attach_dict_huf. Valid iff dict_huf_tree_ok. */
1773
    int dict_huf_tree_ok;           /**< 1 when *dict_huf is built and valid. */
1774
    uint32_t* lit_freq_acc;         /**< Trainer hook: when non-NULL, the GLO encoder
1775
                                         accumulates post-LZ literal byte frequencies here
1776
                                         (256 entries). NULL outside dictionary training. */
1777
1778
    // Block-size derived parameters (computed once at init).
1779
    size_t chunk_size;    /**< Effective block size in bytes. */
1780
    uint32_t offset_bits; /**< log2(chunk_size) - governs epoch_mark shift. */
1781
    uint32_t offset_mask; /**< (1U << offset_bits) - 1 */
1782
    uint32_t max_epoch;   /**< 1U << (32 - offset_bits) */
1783
} zxc_cctx_t;
1784
1785
/**
1786
 * @brief Initialises a ZXC compression / decompression context in place.
1787
 *
1788
 * Allocates the internal buffers (hash table, sequence buffers, scratch) sized
1789
 * for @p chunk_size and the requested @p mode.
1790
 *
1791
 * @param[out] ctx               Context to initialise.
1792
 * @param[in]  chunk_size        Block size driving buffer sizing.
1793
 * @param[in]  mode              1 for compression, 0 for decompression.
1794
 * @param[in]  level             Compression level (ignored when @p mode == 0).
1795
 * @param[in]  checksum_enabled  Non-zero to enable checksum computation.
1796
 * @param[in]  dict_size         Dictionary prefill size; when > 0 an extra
1797
 *                               [dict | data] concat buffer is carved into the
1798
 *                               workspace and @c ctx->dict_buffer is set.
1799
 *
1800
 * @return @c ZXC_OK on success, or a negative @ref zxc_error_t code (notably
1801
 *         @c ZXC_ERROR_MEMORY on allocation failure).
1802
 */
1803
int zxc_cctx_init(zxc_cctx_t* ctx, const size_t chunk_size, const int mode, const int level,
1804
                  const int checksum_enabled, const size_t dict_size);
1805
1806
/**
1807
 * @brief Attach the shared dictionary literal table to an initialised context.
1808
 *
1809
 * Validates the 128-byte packed code-lengths header and builds the PivCo tree,
1810
 * canonical codes and decoder tables ONCE into the context (tree-at-attach);
1811
 * per-block encode/estimate/decode reuse them. @p lengths need only be valid
1812
 * during this call (everything is copied into the context workspace). A NULL
1813
 * @p lengths is a no-op.
1814
 *
1815
 * @return @ref ZXC_OK on success, @ref ZXC_ERROR_CORRUPT_DATA if the lengths
1816
 *         header is structurally invalid (bad nibble, Kraft inequality).
1817
 */
1818
int zxc_cctx_attach_dict_huf(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT lengths);
1819
1820
/**
1821
 * @brief Returns the byte count that @ref zxc_cctx_init would allocate for
1822
 *        the given parameters.
1823
 *
1824
 * Used by the static-cctx public API to size a caller-supplied workspace
1825
 * before calling @ref zxc_cctx_init_in_workspace.
1826
 *
1827
 * @param[in] chunk_size  Block size in bytes (must satisfy
1828
 *                        @ref zxc_validate_block_size).
1829
 * @param[in] mode        1 = compression, 0 = decompression.
1830
 * @param[in] level       Compression level (only consulted when @p mode == 1).
1831
 * @param[in] dict_size   Dictionary prefill size; when > 0 the figure includes
1832
 *                        the [dict | data] concat buffer.
1833
 * @return Size in bytes, or 0 if the parameters are invalid.
1834
 */
1835
size_t zxc_cctx_compute_workspace_size(const size_t chunk_size, const int mode, const int level,
1836
                                       const size_t dict_size);
1837
1838
/**
1839
 * @brief Initialises a compression / decompression context inside a
1840
 *        caller-supplied workspace.
1841
 *
1842
 * Identical to @ref zxc_cctx_init except that the persistent buffer is
1843
 * carved out of @p workspace instead of being @c ZXC_ALIGNED_MALLOC'd
1844
 * internally.  @p workspace must be cache-line aligned and at least as
1845
 * large as @ref zxc_cctx_compute_workspace_size for the same parameters.
1846
 *
1847
 * The caller owns @p workspace and must keep it alive for the lifetime of
1848
 * @p ctx.  @ref zxc_cctx_free becomes a no-op for contexts initialised
1849
 * this way (the workspace is not freed by the library).
1850
 *
1851
 * @param[out] ctx               Context to initialise (zeroed on entry).
1852
 * @param[in]  workspace         Caller-allocated, cache-line-aligned buffer.
1853
 * @param[in]  workspace_size    Capacity of @p workspace in bytes.
1854
 * @param[in]  chunk_size        Block size in bytes.
1855
 * @param[in]  mode              1 = compression, 0 = decompression.
1856
 * @param[in]  level             Compression level (ignored when @p mode == 0).
1857
 * @param[in]  checksum_enabled  Non-zero to enable checksum computation.
1858
 * @param[in]  dict_size         Dictionary prefill size; when > 0 the workspace
1859
 *                               must include the [dict | data] concat buffer and
1860
 *                               @c ctx->dict_buffer is set into it.
1861
 * @param[in]  defer_entropy_scratch  Non-zero (heap decode contexts only) to
1862
 *                               leave the tok/PivCo decode scratch out of the
1863
 *                               partition; it is then lazily allocated by
1864
 *                               @ref zxc_cctx_alloc_entropy_scratch on the
1865
 *                               first entropy section. Static workspaces must
1866
 *                               pass 0 (no-allocation contract).
1867
 * @return @c ZXC_OK on success, @c ZXC_ERROR_DST_TOO_SMALL if the workspace
1868
 *         is too small, or another negative @ref zxc_error_t.
1869
 */
1870
int zxc_cctx_init_in_workspace(zxc_cctx_t* RESTRICT ctx, void* RESTRICT workspace,
1871
                               const size_t workspace_size, const size_t chunk_size, const int mode,
1872
                               const int level, const int checksum_enabled, const size_t dict_size,
1873
                               const int defer_entropy_scratch);
1874
1875
/**
1876
 * @brief Lazily allocates the decode-side entropy scratch (tok_buffer +
1877
 *        pivco_scratch) for a heap context initialised with deferral.
1878
 *
1879
 * No-op when the scratch is already present (static workspaces pre-carve it;
1880
 * subsequent entropy blocks reuse the first allocation). The block is owned
1881
 * by the context (@c entropy_block) and released by @ref zxc_cctx_free.
1882
 *
1883
 * @return @ref ZXC_OK, or @ref ZXC_ERROR_MEMORY on allocation failure.
1884
 */
1885
int zxc_cctx_alloc_entropy_scratch(zxc_cctx_t* ctx);
1886
1887
/**
1888
 * @brief Releases the internal buffers owned by a context.
1889
 *
1890
 * Does NOT free @p ctx itself - the caller owns the struct storage. The
1891
 * context may safely be re-initialised with zxc_cctx_init() afterwards.
1892
 *
1893
 * @param[in,out] ctx Context whose buffers should be released.
1894
 */
1895
void zxc_cctx_free(zxc_cctx_t* ctx);
1896
1897
/**
1898
 * @brief Decompresses one chunk through the runtime ISA dispatch.
1899
 *
1900
 * Un-suffixed entry point: it loads the resolved variant pointer (`_default`,
1901
 * `_avx2`, `_avx512`, ...), running the one-time CPU detection on the first
1902
 * call, and routes to the dict variant when the context carries a dictionary.
1903
 *
1904
 * @param[in]  ctx     Context holding the decode state and dictionary, if any.
1905
 * @param[in]  src     Compressed chunk.
1906
 * @param[in]  src_sz  Size of @p src in bytes.
1907
 * @param[out] dst     Destination buffer.
1908
 * @param[in]  dst_cap Capacity of @p dst.
1909
 * @return Bytes decoded (> 0), or a negative @ref zxc_error_t.
1910
 */
1911
int zxc_decompress_chunk_wrapper(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src,
1912
                                 const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap);
1913
int zxc_decompress_chunk_wrapper_dict(const zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src,
1914
                                      const size_t src_sz, uint8_t* RESTRICT dst,
1915
                                      const size_t dst_cap);
1916
1917
/**
1918
 * @brief Compresses one chunk through the runtime ISA dispatch.
1919
 *
1920
 * Counterpart of zxc_decompress_chunk_wrapper(): same lazily-resolved variant
1921
 * pointer, same one-time CPU detection on the first call.
1922
 *
1923
 * @param[in,out] ctx     Compression context: configuration and working buffers.
1924
 * @param[in]     src     Raw data to compress.
1925
 * @param[in]     src_sz  Size of @p src in bytes.
1926
 * @param[out]    dst     Destination buffer.
1927
 * @param[in]     dst_cap Capacity of @p dst.
1928
 * @return Bytes written (> 0), or a negative @ref zxc_error_t.
1929
 */
1930
int zxc_compress_chunk_wrapper(zxc_cctx_t* RESTRICT ctx, const uint8_t* RESTRICT src,
1931
                               const size_t src_sz, uint8_t* RESTRICT dst, const size_t dst_cap);
1932
1933
// ---------------------------------------------------------------------------
1934
// Internal frame primitives.
1935
//
1936
// Read/write the ZXC file header, block header and footer. Kept internal:
1937
// exposing them would freeze on-disk details (block_flags layout, footer
1938
// composition) that stay free to evolve until the format is declared stable.
1939
// ---------------------------------------------------------------------------
1940
1941
/**
1942
 * @brief On-disk header structure for a ZXC block (8 bytes, little-endian).
1943
 *
1944
 * @c raw_size is not stored in the header; decoders derive it from Section
1945
 * Descriptors within the compressed payload.
1946
 */
1947
typedef struct {
1948
    uint8_t block_type;  /**< Block type (see @ref zxc_block_type_t). */
1949
    uint8_t block_flags; /**< Flags (e.g., checksum presence). */
1950
    uint8_t reserved;    /**< Reserved for future protocol extensions. */
1951
    uint8_t header_crc;  /**< Header integrity checksum (1 byte). */
1952
    uint32_t comp_size;  /**< Compressed size excluding this header. */
1953
} zxc_block_header_t;
1954
1955
/**
1956
 * @brief Writes the standard ZXC file header into @p dst.
1957
 *
1958
 * Stores the magic word (little-endian) and the version number into the
1959
 * provided buffer, after checking that it has sufficient capacity.
1960
 *
1961
 * @param[out] dst           Destination buffer.
1962
 * @param[in]  dst_capacity  Total capacity of @p dst in bytes.
1963
 * @param[in]  chunk_size    Block size to encode in the header.
1964
 * @param[in]  has_checksum  Non-zero if the checksum bit must be set.
1965
 * @param[in]  dict_id       Dictionary ID (0 = no dictionary).
1966
 *
1967
 * @return Number of bytes written (@c ZXC_FILE_HEADER_SIZE) on success,
1968
 *         or @c ZXC_ERROR_DST_TOO_SMALL if @p dst_capacity is insufficient.
1969
 */
1970
int zxc_write_file_header(uint8_t* RESTRICT dst, const size_t dst_capacity, const size_t chunk_size,
1971
                          const int has_checksum, const uint32_t dict_id);
1972
1973
/**
1974
 * @brief Validates and reads the ZXC file header from @p src.
1975
 *
1976
 * Checks that the source buffer is large enough to contain a ZXC file header
1977
 * and that the magic word and version number match the expected format.
1978
 *
1979
 * @param[in]  src               Pointer to the source buffer.
1980
 * @param[in]  src_size          Size of the source buffer in bytes.
1981
 * @param[out] out_block_size    Optional pointer that receives the recommended
1982
 *                               block size. May be @c NULL.
1983
 * @param[out] out_has_checksum  Optional pointer that receives the checksum
1984
 *                               flag. May be @c NULL.
1985
 * @param[out] out_dict_id       Optional pointer that receives the dictionary
1986
 *                               ID (0 if none). May be @c NULL.
1987
 *
1988
 * @return @c ZXC_OK on success, or a negative error code (e.g.
1989
 *         @c ZXC_ERROR_SRC_TOO_SMALL, @c ZXC_ERROR_BAD_MAGIC,
1990
 *         @c ZXC_ERROR_BAD_VERSION).
1991
 */
1992
int zxc_read_file_header(const uint8_t* RESTRICT src, const size_t src_size, size_t* out_block_size,
1993
                         int* out_has_checksum, uint32_t* out_dict_id);
1994
1995
/**
1996
 * @brief Encodes a block header into @p dst.
1997
 *
1998
 * Serialises the contents of a @ref zxc_block_header_t structure into a byte
1999
 * array in little-endian format, after checking that @p dst has sufficient
2000
 * capacity.
2001
 *
2002
 * @param[out] dst           Destination buffer.
2003
 * @param[in]  dst_capacity  Total capacity of @p dst in bytes.
2004
 * @param[in]  bh            Source block header structure to serialise.
2005
 *
2006
 * @return Number of bytes written (@c ZXC_BLOCK_HEADER_SIZE) on success,
2007
 *         or @c ZXC_ERROR_DST_TOO_SMALL if @p dst_capacity is insufficient.
2008
 */
2009
int zxc_write_block_header(uint8_t* RESTRICT dst, const size_t dst_capacity,
2010
                           const zxc_block_header_t* bh);
2011
2012
/**
2013
 * @brief Reads and parses a ZXC block header from @p src.
2014
 *
2015
 * Extracts the block type, flags, reserved fields, and compressed size from
2016
 * the first @c ZXC_BLOCK_HEADER_SIZE bytes of @p src. Multi-byte fields are
2017
 * decoded as little-endian.
2018
 *
2019
 * @param[in]  src       Source buffer holding the encoded block header.
2020
 * @param[in]  src_size  Size of @p src in bytes.
2021
 * @param[out] bh        Block header structure populated with the parsed data.
2022
 *
2023
 * @return @c ZXC_OK on success, or @c ZXC_ERROR_SRC_TOO_SMALL if @p src is
2024
 *         smaller than @c ZXC_BLOCK_HEADER_SIZE.
2025
 */
2026
int zxc_read_block_header(const uint8_t* RESTRICT src, const size_t src_size,
2027
                          zxc_block_header_t* bh);
2028
2029
/**
2030
 * @brief Writes the ZXC file footer into @p dst.
2031
 *
2032
 * The footer stores the original uncompressed size and an optional global
2033
 * checksum. It is always @c ZXC_FILE_FOOTER_SIZE (12) bytes long.
2034
 *
2035
 * @param[out] dst               Destination buffer.
2036
 * @param[in]  dst_capacity      Total capacity of @p dst in bytes.
2037
 * @param[in]  src_size          Original uncompressed size of the data.
2038
 * @param[in]  global_hash       Global checksum hash (used only when
2039
 *                               @p checksum_enabled is non-zero).
2040
 * @param[in]  checksum_enabled  Non-zero if the checksum should be emitted.
2041
 *
2042
 * @return Number of bytes written (@c ZXC_FILE_FOOTER_SIZE) on success,
2043
 *         or @c ZXC_ERROR_DST_TOO_SMALL on failure.
2044
 */
2045
int zxc_write_file_footer(uint8_t* RESTRICT dst, const size_t dst_capacity, const uint64_t src_size,
2046
                          const uint32_t global_hash, const int checksum_enabled);
2047
2048
// ---------------------------------------------------------------------------
2049
// Seekable cross-TU hooks (defined in zxc_seekable.c, consumed by the
2050
// FILE*-flavored open helper in zxc_driver.c).
2051
// -------------------------------------------------------------------------
2052
2053
/**
2054
 * @brief Hands ownership of a heap-allocated reader context to a seekable
2055
 *        handle.  The context will be released via @c ZXC_FREE when
2056
 *        @ref zxc_seekable_free is called on @p s.
2057
 *
2058
 * Safe to call exactly once per handle.  Intended for thin wrappers that
2059
 * build a @ref zxc_reader_t over their own allocated state
2060
 * (@ref zxc_seekable_open_file) and need that state to outlive the call
2061
 * site.
2062
 *
2063
 * @param[in,out] s    Seekable handle returned by @ref zxc_seekable_open_reader.
2064
 * @param[in]     ctx  Pointer previously returned by @c ZXC_MALLOC / @c ZXC_CALLOC.
2065
 */
2066
void zxc_seekable_attach_owned_ctx(zxc_seekable* s, void* ctx);
2067
2068
/** @} */ /* end of internal */
2069
2070
#ifdef __cplusplus
2071
}
2072
#endif
2073
2074
#endif  // ZXC_INTERNAL_H