13#include "../../include/zxc_dict.h"
15#include "../../include/zxc_buffer.h"
29uint32_t
zxc_dict_id(
const void* RESTRICT dict,
const size_t dict_size,
30 const void* RESTRICT huf_lengths) {
31 if (UNLIKELY(!dict || dict_size == 0))
return 0;
33 if (!huf_lengths)
return base;
59 const uint8_t* p = (
const uint8_t*)buf;
79int64_t
zxc_dict_save(
const void* RESTRICT content,
const size_t content_size,
80 const void* RESTRICT huf_lengths,
void* RESTRICT buf,
81 const size_t buf_capacity) {
88 uint8_t* dst = (uint8_t*)buf;
102 return (int64_t)total;
113 const void** RESTRICT content_out,
size_t* RESTRICT content_size_out,
114 const void** RESTRICT huf_out, uint32_t* RESTRICT dict_id_out) {
118 const uint8_t* src = (
const uint8_t*)buf;
123 const size_t content_size =
zxc_le16(src + 6);
130 ZXC_MEMCPY(temp, src,
sizeof(temp));
132 const uint16_t expected_sum =
zxc_hash16(temp);
136 const uint8_t* huf = content + content_size;
139 const uint32_t
id =
zxc_dict_id(content, content_size, huf);
142 *content_out = content;
143 *content_size_out = content_size;
144 if (huf_out) *huf_out = huf;
145 if (dict_id_out) *dict_id_out = id;
158 const uint8_t* src = (
const uint8_t*)buf;
161 const size_t content_size =
zxc_le16(src + 6);
162 if (UNLIKELY(content_size == 0 ||
219 size_t child = 2 * root + 1;
220 if (child >= n)
break;
221 if (child + 1 < n && a[child + 1].score < a[child].score) child++;
222 if (a[root].score <= a[child].score)
break;
223 const zxc_dict_seg_t t = a[root];
245 if (UNLIKELY(n < 2))
return;
247 for (
size_t end = n; end > 1;) {
249 const zxc_dict_seg_t t = a[0];
263int64_t
zxc_train_dict(
const void*
const* RESTRICT samples,
const size_t* RESTRICT sample_sizes,
264 const size_t n_samples,
void* RESTRICT dict_buf,
265 const size_t dict_capacity) {
266 if (UNLIKELY(!samples || !sample_sizes || n_samples == 0 || !dict_buf || dict_capacity == 0))
271 size_t corpus_size = 0;
272 for (
size_t i = 0; i < n_samples; i++) corpus_size += sample_sizes[i];
276 uint16_t* freq = NULL;
277 zxc_dict_seg_t* segs = NULL;
278 uint8_t* corpus = (uint8_t*)
ZXC_MALLOC(corpus_size);
282 for (
size_t i = 0; i < n_samples; i++) {
283 if (sample_sizes[i] > 0) ZXC_MEMCPY(corpus + pos, samples[i], sample_sizes[i]);
284 pos += sample_sizes[i];
290 if (UNLIKELY(!freq)) {
300 if (freq_stride < 1) freq_stride = 1;
301 for (
size_t i = 0; i < kgram_limit; i += freq_stride) {
303 if (freq[h] < UINT16_MAX) freq[h]++;
312 if (seg_alloc > 0 && corpus_size / seg_alloc > stride) stride = corpus_size / seg_alloc;
314 segs = (zxc_dict_seg_t*)
ZXC_MALLOC(seg_alloc *
sizeof(zxc_dict_seg_t));
315 if (UNLIKELY(!segs)) {
321 for (
size_t i = 0; i +
ZXC_DICT_KGRAM_LEN <= corpus_size && n_segs < seg_alloc; i += stride) {
323 const uint16_t f = freq[h];
328 uint32_t coverage = f;
337 segs[n_segs].offset = (uint32_t)i;
338 segs[n_segs].length = (uint16_t)(end - i);
339 segs[n_segs].score = coverage;
343 if (UNLIKELY(n_segs == 0)) {
345 const size_t copy = (corpus_size < dict_capacity) ? corpus_size : dict_capacity;
346 ZXC_MEMCPY(dict_buf, corpus + corpus_size - copy, copy);
356 uint8_t* out = (uint8_t*)dict_buf;
360 for (
size_t i = 0; i < n_segs && total < dict_capacity; i++) {
361 const size_t seg_off = segs[i].offset;
362 const size_t seg_end = seg_off + segs[i].length;
369 if (cur * 2 < segs[i].score)
continue;
371 size_t copy = segs[i].length;
372 if (copy > dict_capacity - total) copy = dict_capacity - total;
380 segs[n_sel].offset = (uint32_t)seg_off;
381 segs[n_sel].length = (uint16_t)copy;
396 for (
size_t i = n_sel; i-- > 0;) {
397 ZXC_MEMCPY(out + filled, corpus + segs[i].offset, segs[i].length);
398 filled += segs[i].length;
403 if (UNLIKELY(filled == 0)) {
404 const size_t tail = (corpus_size < dict_capacity) ? corpus_size : dict_capacity;
405 ZXC_MEMCPY(out, corpus + corpus_size - tail, tail);
409 rc = (int64_t)filled;
438 const size_t n_samples,
const void* RESTRICT dict,
const size_t dict_size,
439 uint8_t* RESTRICT huf_lengths_out) {
440 if (UNLIKELY(!samples || !sample_sizes || n_samples == 0 || !dict || dict_size == 0 ||
455 uint8_t* out_scratch = (uint8_t*)
ZXC_MALLOC(out_cap);
456 if (UNLIKELY(!out_scratch)) {
465 ZXC_MEMCPY(work, dict, dict_size);
469 size_t corpus_total = 0;
470 for (
size_t s = 0; s < n_samples; s++) corpus_total += sample_sizes[s];
471 const size_t stride =
477 size_t slice_idx = 0;
478 for (
size_t s = 0; s < n_samples && rc ==
ZXC_OK; s++) {
479 const uint8_t* sample = (
const uint8_t*)samples[s];
480 const size_t sample_size = sample_sizes[s];
481 if (!sample || sample_size == 0)
continue;
484 if (slice_idx % stride != 0)
continue;
486 ? (sample_size - off)
488 ZXC_MEMCPY(work + dict_size, sample + off, slice);
491 if (UNLIKELY(r < 0)) {
540int64_t
zxc_dict_train(
const void*
const* RESTRICT samples,
const size_t* RESTRICT sample_sizes,
541 const size_t n_samples,
void* RESTRICT zxd_buf,
const size_t zxd_capacity) {
542 if (UNLIKELY(!samples || !sample_sizes || n_samples == 0 || !zxd_buf || zxd_capacity == 0))
553 const int64_t content_size =
555 if (UNLIKELY(content_size <= 0)) {
565 (
size_t)content_size, huf);
566 if (UNLIKELY(hrc !=
ZXC_OK)) {
572 out =
zxc_dict_save(content, (
size_t)content_size, huf, zxd_buf, zxd_capacity);
uint64_t zxc_compress_bound(const size_t input_size)
Maximum compressed size for an input of input_size bytes.
int64_t zxc_dict_train(const void *const *samples, const size_t *sample_sizes, const size_t n_samples, void *zxd_buf, const size_t zxd_capacity)
One-shot training: sample buffers in, ready-to-write .zxd bytes out.
int zxc_train_dict_huf(const void *const *samples, const size_t *sample_sizes, const size_t n_samples, const void *dict, const size_t dict_size, uint8_t *huf_lengths_out)
Trains the shared literal Huffman table for a dictionary (Tier-2).
int64_t zxc_train_dict(const void *const *samples, const size_t *sample_sizes, const size_t n_samples, void *dict_buf, const size_t dict_capacity)
Trains a raw dictionary from sample buffers by k-gram coverage.
size_t zxc_dict_save_bound(const size_t content_size)
Worst-case .zxd byte size for a dictionary of content_size bytes.
const void * zxc_dict_huf(const void *buf, const size_t buf_size)
Returns a pointer to the packed Huffman table inside a .zxd buffer.
int zxc_dict_load(const void *buf, const size_t buf_size, const void **content_out, size_t *content_size_out, const void **huf_out, uint32_t *dict_id_out)
Parses a .zxd buffer, returning in-buffer views of content and table.
int64_t zxc_dict_save(const void *content, const size_t content_size, const void *huf_lengths, void *buf, const size_t buf_capacity)
Serializes dictionary content + Huffman table into the .zxd format.
uint32_t zxc_dict_get_id(const void *buf, const size_t buf_size)
Extracts the stored dict_id from a serialized .zxd buffer.
uint32_t zxc_dict_id(const void *dict, const size_t dict_size, const void *huf_lengths)
Computes the dictionary identifier for dict (and optional table).
#define ZXC_DICT_SIZE_MAX
Maximum dictionary content size in bytes (64 KB - 1).
#define ZXC_DICT_MAGIC
Magic word opening a .zxd dictionary file, stored little-endian (C7 D1 B0 9C).
#define ZXC_DICT_HEADER_SIZE
Size of the .zxd dictionary file header in bytes.
#define ZXC_HUF_TABLE_SIZE
Size of a packed literal Huffman code-lengths table (256 symbols, 4 bits each): the shared table in a...
@ ZXC_ERROR_BAD_MAGIC
Invalid magic word in file header.
@ ZXC_ERROR_BAD_VERSION
Unsupported file format version.
@ ZXC_ERROR_CORRUPT_DATA
Corrupted compressed data.
@ ZXC_ERROR_NULL_INPUT
Required input pointer is NULL.
@ ZXC_ERROR_MEMORY
Memory allocation failure.
@ ZXC_ERROR_BAD_HEADER
Corrupted or invalid header (checksum mismatch).
@ ZXC_ERROR_DST_TOO_SMALL
Destination buffer too small.
@ ZXC_ERROR_SRC_TOO_SMALL
Source buffer too small or truncated input.
@ ZXC_ERROR_BAD_CHECKSUM
A block's decoded bytes fail its checksum, or the archive digest mismatches.
@ ZXC_OK
Success (no error).
@ ZXC_ERROR_DICT_TOO_LARGE
Dictionary exceeds maximum allowed size.
#define ZXC_MALLOC(size)
Heap allocator.
#define ZXC_CHECKSUM_RAPIDHASH
Checksum algorithm id for RapidHash (default, sole implementation).
#define ZXC_DICT_MAX_SEGMENTS
Maximum number of candidate segments the dictionary trainer keeps.
#define ZXC_DICT_VERSION
Current dictionary file format version.
#define ZXC_CALLOC(nmemb, size)
Zero-initialised heap allocator.
#define ZXC_DICT_HASH_SIZE
Number of buckets in the dictionary trainer's frequency table.
#define ZXC_FREE(ptr)
Heap deallocator.
#define ZXC_HASH_MULT32
Marsaglia 32-bit multiplier: the 4-byte LZ and dictionary hash.
#define ZXC_HUF_MAX_CODE_LEN_DENSITY
Encoder code-length cap for levels up to ZXC_LEVEL_DENSITY (below ZXC_LEVEL_ULTRA): shallow 8-bit tre...
#define ZXC_DICT_HUF_TRAIN_BLOCK
Training block size for the shared-table literal statistics.
#define ZXC_DICT_SAMPLE_TARGET
Target number of sampled k-gram positions for the trainer's frequency estimate.
#define ZXC_DICT_HUF_SAMPLE_BUDGET
Cap on the corpus bytes compressed by the literal-table trainer: the histogram converges early,...
#define ZXC_DICT_KGRAM_LEN
K-gram length scanned by the dictionary trainer.
#define ZXC_DICT_HASH_BITS
Address bits for the dictionary trainer's k-gram frequency table.
#define ZXC_HUF_NUM_SYMBOLS
Alphabet size: one entry per possible byte value.
@ ZXC_LEVEL_DENSITY
Higher density: adds Huffman-coded literals on top of COMPACT.
Compression / decompression context.
uint32_t * lit_freq_acc
Trainer hook: when non-NULL, the GLO encoder accumulates post-LZ literal byte frequencies here (256 e...
uint8_t * dict_buffer
[dict | data] concat scratch carved from memory_block when dict_size > 0 (NULL otherwise).
void zxc_cctx_free(zxc_cctx_t *ctx)
Releases all resources owned by a compression context.
int zxc_cctx_init(zxc_cctx_t *ctx, const size_t chunk_size, const int mode, const int level, const int checksum_enabled, const size_t dict_size)
Initialises a compression / decompression context, allocating the persistent buffer with ZXC_ALIGNED_...
int zxc_compress_chunk_wrapper(zxc_cctx_t *ctx, const uint8_t *chunk, const size_t src_sz, uint8_t *dst, const size_t dst_cap, const uint64_t block_index)
Compresses one chunk into a single ZXC block (the compression hot path).
static void zxc_dict_sift_down(zxc_dict_seg_t *a, size_t root, const size_t n)
Restore the min-heap property at root over the range a[0..n).
static void zxc_dict_sort_segs_desc(zxc_dict_seg_t *a, const size_t n)
Sort a[0..n) by zxc_dict_seg_t::score in descending order.
static uint32_t zxc_dict_hash(const uint8_t *p)
Hashes the k-gram at p into a frequency-table bucket index.
int zxc_huf_build_code_lengths(const uint32_t *freq, uint8_t *code_len, void *scratch, const int max_code_len)
Build length-limited per-symbol Huffman code lengths from frequencies.
void zxc_huf_pack_lengths(const uint8_t *code_len, uint8_t *out)
Pack per-symbol code lengths into the 128-byte nibble header.
int zxc_huf_nudge_code_lengths(const uint32_t *freq, uint8_t *code_len, void *scratch, const size_t scratch_cap, const int max_code_len)
Optionally reshape freshly built code lengths for faster PivCo decode.
Internal definitions, constants, SIMD helpers, and utility functions.
static uint32_t zxc_le32(const void *p)
Reads a little-endian 32-bit value from a possibly unaligned address.
static size_t zxc_block_size_ceil(const size_t v)
Branchless bit_ceil: smallest power of two >= v, clamped to ZXC_BLOCK_SIZE_MIN.
static uint16_t zxc_le16(const void *p)
Reads a little-endian 16-bit value from a possibly unaligned address.
static uint16_t zxc_hash16(const uint8_t *p)
Computes the 2-byte checksum for file and dictionary headers.
static void zxc_store_le16(void *p, const uint16_t v)
Writes a 16-bit value little-endian to a possibly unaligned address.
static void zxc_store_le32(void *p, const uint32_t v)
Writes a 32-bit value little-endian to a possibly unaligned address.
static uint32_t zxc_checksum(const void *input, const size_t len, const uint64_t seed, const uint8_t hash_method)
Calculates a 32-bit hash for a given input buffer.