40#include "../../include/zxc_seekable.h"
42#include "../../include/zxc_dict.h"
43#include "../../include/zxc_error.h"
60 uint64_t total_decomp;
70#define ZXC_SEEK_MAX_FRAMES (1U << 20)
73#define ZXC_SEEK_TAIL_HDRS (2 * ZXC_BLOCK_HEADER_SIZE)
84struct zxc_seekable_s {
93 void* owned_reader_ctx;
96 zxc_seek_frame_t* frames;
99 uint64_t total_decomp;
100 uint32_t max_block_size;
102 int verify_checksums;
108 int dctx_initialized;
109 const zxc_seek_dict_t* dctx_dict;
114 zxc_seek_dict_t* dicts;
116 size_t max_dict_size;
141 const uint64_t off) {
142 if (UNLIKELY(off > src->size || (uint64_t)len > src->size - off))
145 ZXC_MEMCPY(dst, src->data + off, len);
148 const int64_t r = src->rdr->read_at(src->rdr->ctx, dst, len, off);
150 if (UNLIKELY(r != (int64_t)len))
return (r < 0 && r >= INT_MIN) ? (int)r :
ZXC_ERROR_IO;
158 const zxc_seek_source_t src = {s->src, s->src ? NULL : &s->reader, s->src_size};
182 size_t footer_len = 0;
186 const uint64_t frame = end - start;
187 const uint32_t block_size = (uint32_t)fi.
block_size;
193 if (UNLIKELY(seek_block_total + footer_len >
204 const uint64_t max_span =
205 num_blocks > UINT64_MAX / entry_max ? UINT64_MAX : num_blocks * entry_max;
206 if (UNLIKELY(data_area < min_span || data_area > max_span))
return 0;
209 f->num_blocks = num_blocks;
212 f->eof_off = eof_off;
213 f->entry_max = entry_max;
214 f->block_size = block_size;
225 zxc_seek_source_t src = *source;
226 zxc_seek_frame_t* frames = NULL;
229 uint64_t end = src.size;
233 cap = cap ? 2 * cap : 4;
234 zxc_seek_frame_t*
const grown =
235 (zxc_seek_frame_t*)
ZXC_REALLOC(frames, cap *
sizeof(*frames));
236 if (UNLIKELY(!grown))
goto fail;
244 const zxc_seek_frame_t*
const f = &frames[0];
249 f->total_decomp, f->block_size, NULL) !=
ZXC_OK))
252 end = frames[n++].base;
258 uint32_t max_block_size = 0;
259 for (
size_t i = 0; i < n / 2; i++) {
260 const zxc_seek_frame_t t = frames[i];
261 frames[i] = frames[n - 1 - i];
262 frames[n - 1 - i] = t;
264 for (
size_t i = 0; i < n; i++) {
265 zxc_seek_frame_t*
const f = &frames[i];
266 if (UNLIKELY(f->total_decomp > UINT64_MAX - decomp))
goto fail;
267 f->decomp_base = decomp;
268 f->block_base = blocks;
269 decomp += f->total_decomp;
270 blocks += f->num_blocks;
271 if (f->block_size > max_block_size) max_block_size = f->block_size;
275 if (UNLIKELY(!s))
goto fail;
276 if (src.rdr) s->reader = *src.rdr;
278 s->src_size = src.size;
281 s->num_blocks = blocks;
282 s->total_decomp = decomp;
283 s->max_block_size = max_block_size;
284 s->verify_checksums = 0;
295 size_t hi = s->num_frames;
297 const size_t mid = lo + (hi - lo) / 2;
298 if (s->frames[mid].block_base <= idx)
303 return &s->frames[lo - 1];
309 size_t hi = s->num_frames;
311 const size_t mid = lo + (hi - lo) / 2;
312 if (s->frames[mid].decomp_base <= offset)
317 const zxc_seek_frame_t*
const f = &s->frames[lo - 1];
318 return f->block_base + ((offset - f->decomp_base) >>
zxc_ctz32(f->block_size));
350 const uint64_t first,
const uint32_t n, uint64_t* RESTRICT starts,
351 uint32_t* RESTRICT sizes, uint8_t* RESTRICT raw) {
357 for (uint64_t g = g0; g <= g1; g++)
368 if (rc ==
ZXC_OK && g0 != 0)
370 if (UNLIKELY(rc !=
ZXC_OK))
return rc;
373 f->total_decomp, f->block_size, NULL) !=
ZXC_OK))
377 for (uint64_t g = g0; g <= g1; g++) {
381 : pos < ZXC_FILE_HEADER_SIZE || pos > f->eof_off))
387 if (UNLIKELY(sz < ZXC_BLOCK_HEADER_SIZE || sz > f->entry_max))
390 if (idx >= first && idx < first + n) {
391 starts[idx - first] = f->base + pos;
392 sizes[idx - first] = sz;
397 if (UNLIKELY(pos > f->eof_off || (g == last_group && pos != f->eof_off)))
410 if (UNLIKELY(!src || src_size == 0))
return NULL;
411 const zxc_seek_source_t source = {(
const uint8_t*)src, NULL, (uint64_t)src_size};
427 if (UNLIKELY(!r || !r->
read_at || r->
size == 0))
return NULL;
428 const zxc_seek_source_t source = {NULL, r, r->
size};
441 return s ? s->total_decomp : 0;
448 if (UNLIKELY(!s || block_idx >= s->num_blocks))
return 0;
453 if (UNLIKELY(
zxc_seek_load_spans(s, f, block_idx - f->block_base, 1, &start, &size, raw) !=
471 const uint64_t idx) {
472 const uint64_t start = idx * (uint64_t)block_size;
473 const uint64_t remaining = total_decomp - start;
474 return (remaining >= (uint64_t)block_size) ? block_size : (uint32_t)remaining;
479 if (UNLIKELY(!s || block_idx >= s->num_blocks))
return 0;
495 const zxc_seek_frame_t* f) {
496 const zxc_seek_dict_t* d = NULL;
497 for (
size_t i = 0; f->dict_id && i < s->num_dicts && !d; i++)
498 if (s->dicts[i].id == f->dict_id) d = &s->dicts[i];
501 if (d) ZXC_MEMCPY(ctx->
dict_buffer, d->data, d->size);
529 const int has_cs, uint8_t* buf,
const size_t buf_cap) {
533 if (UNLIKELY(rc !=
ZXC_OK))
return rc;
537 if (UNLIKELY(hdr_res !=
ZXC_OK))
return hdr_res;
538 const uint64_t on_disk =
554 const uint64_t offset,
const size_t len) {
555 if (UNLIKELY(len == 0))
return 0;
558 if (UNLIKELY(offset > s->total_decomp || len > s->total_decomp - offset))
562 if (!s->dctx_initialized) {
563 if (UNLIKELY(
zxc_cctx_init(&s->dctx, (
size_t)s->max_block_size, 0, 0, 0,
564 s->max_dict_size) !=
ZXC_OK))
566 s->dctx_initialized = 1;
574 uint8_t* out = (uint8_t*)dst;
575 size_t remaining = len;
581 uint64_t bi = blk_start;
582 while (bi <= blk_end) {
583 while (bi >= f->block_base + f->num_blocks) f++;
584 const uint64_t local = bi - f->block_base;
585 const uint64_t left = blk_end - bi + 1;
586 const uint64_t in_frame = f->num_blocks - local;
588 uint32_t n = left < to_group_end ? (uint32_t)left : to_group_end;
589 if (in_frame < n) n = (uint32_t)in_frame;
591 if (UNLIKELY(span_res < 0))
return span_res;
595 for (uint32_t k = 0; k < n; k++)
596 if (sizes[k] > need) need = sizes[k];
598 if (s->read_buf_cap < need) {
599 uint8_t*
const nb = (uint8_t*)
ZXC_REALLOC(s->read_buf, (
size_t)need);
602 s->read_buf_cap = (size_t)need;
604 uint8_t*
const read_buf = s->read_buf;
608 if (UNLIKELY(dict_res !=
ZXC_OK))
return dict_res;
609 s->dctx.chunk_size = f->block_size;
610 s->dctx.checksum_enabled = f->has_checksums && s->verify_checksums;
613 for (uint32_t k = 0; k < n; k++, bi++) {
614 const uint64_t lb = local + k;
616 read_buf, s->read_buf_cap);
617 if (UNLIKELY(read_res < 0))
return read_res;
623 s->dctx.dict_buffer ? s->dctx.dict_buffer + s->dctx.dict_size : s->dctx.work_buf;
625 dec_dst, work_sz, lb);
626 if (UNLIKELY(dec_res < 0))
return dec_res;
627 if (UNLIKELY((uint32_t)dec_res !=
632 const uint64_t blk_decomp_start = f->decomp_base + lb * f->block_size;
634 (offset > blk_decomp_start) ? (
size_t)(offset - blk_decomp_start) : 0;
635 const size_t avail = (size_t)dec_res - skip;
636 const size_t copy = (avail < remaining) ? avail : remaining;
638 ZXC_MEMCPY(out, dec_dst + skip, copy);
667 const zxc_seek_frame_t* frame;
689 zxc_seek_mt_job_t* jobs;
694 pthread_mutex_t lock;
695} zxc_seek_mt_shared_t;
706 for (uint32_t i = first; i < sh->num_jobs; i += sh->stride) sh->jobs[i].result = code;
724 zxc_seek_mt_shared_t*
const sh = (zxc_seek_mt_shared_t*)arg;
725 zxc_seek_mt_job_t*
const jobs = sh->jobs;
729 pthread_mutex_lock(&sh->lock);
730 const uint32_t first = sh->next++;
731 pthread_mutex_unlock(&sh->lock);
735 if (UNLIKELY(
zxc_cctx_init(&dctx, (
size_t)s->max_block_size, 0, 0, 0, s->max_dict_size) !=
742 const zxc_seek_dict_t* held = NULL;
746 for (uint32_t i = first; i < sh->num_jobs; i += sh->stride) {
747 if (jobs[i].csz > max_csz) max_csz = jobs[i].csz;
750 if (UNLIKELY(!read_buf)) {
758 for (uint32_t i = first; i < sh->num_jobs; i += sh->stride) {
759 zxc_seek_mt_job_t*
const job = &jobs[i];
763 if (UNLIKELY(read_res < 0)) {
764 job->result = read_res;
769 if (UNLIKELY(dict_res !=
ZXC_OK)) {
770 job->result = dict_res;
777 &dctx, read_buf, (
size_t)read_res, dec_dst,
780 if (UNLIKELY(dec_res < 0)) {
781 job->result = dec_res;
784 if (UNLIKELY((uint32_t)dec_res != job->decomp_sz)) {
790 ZXC_MEMCPY(sh->dst_base + job->dst_off, dec_dst + job->skip, job->copy_len);
808 const uint64_t offset,
const size_t len,
int n_threads) {
809 if (UNLIKELY(len == 0))
return 0;
812 if (UNLIKELY(offset > s->total_decomp || len > s->total_decomp - offset))
817 const uint64_t jobs_64 = blk_end - blk_start + 1;
824 if (n_threads <= 1 || jobs_64 <= 1 || jobs_64 > UINT32_MAX) {
827 const uint32_t num_jobs = (uint32_t)jobs_64;
830 if ((uint32_t)n_threads > num_jobs) n_threads = (
int)num_jobs;
833 zxc_seek_mt_job_t*
const jobs =
834 (zxc_seek_mt_job_t*)
ZXC_CALLOC(num_jobs,
sizeof(zxc_seek_mt_job_t));
835 uint64_t*
const starts = (uint64_t*)
ZXC_CALLOC(num_jobs,
sizeof(uint64_t));
836 uint32_t*
const sizes = (uint32_t*)
ZXC_CALLOC(num_jobs,
sizeof(uint32_t));
837 if (UNLIKELY(!jobs || !starts || !sizes)) {
849 size_t remaining = len;
851 for (uint32_t i = 0; i < num_jobs && plan_res ==
ZXC_OK;) {
852 const uint64_t bi = blk_start + i;
853 while (bi >= f->block_base + f->num_blocks) f++;
854 const uint64_t local = bi - f->block_base;
855 const uint64_t in_frame = f->num_blocks - local;
856 const uint32_t n = in_frame < num_jobs - i ? (uint32_t)in_frame : num_jobs - i;
861 for (uint32_t k = 0; k < n && plan_res ==
ZXC_OK; k++, i++) {
862 const uint64_t lb = local + k;
863 const uint64_t blk_decomp_start = f->decomp_base + lb * f->block_size;
865 (offset > blk_decomp_start) ? (
size_t)(offset - blk_decomp_start) : 0;
867 const size_t avail = blk_decomp_sz - skip;
868 const size_t copy = (avail < remaining) ? avail : remaining;
870 jobs[i].block_idx = lb;
871 jobs[i].off = starts[i];
872 jobs[i].csz = sizes[i];
873 jobs[i].block_size = f->block_size;
875 jobs[i].decomp_sz = blk_decomp_sz;
876 jobs[i].has_cs = f->has_checksums;
877 jobs[i].dst_off = out_off;
879 jobs[i].copy_len = copy;
890 if (UNLIKELY(plan_res !=
ZXC_OK)) {
896 pthread_t*
const threads = (pthread_t*)
ZXC_MALLOC((
size_t)n_threads *
sizeof(pthread_t));
897 if (UNLIKELY(!threads)) {
904 zxc_seek_mt_shared_t sh;
907 sh.dst_base = (uint8_t*)dst;
908 sh.num_jobs = num_jobs;
909 sh.stride = (uint32_t)n_threads;
911 pthread_mutex_init(&sh.lock, NULL);
914 for (
int t = 0; t < n_threads; t++) {
923 for (
int t = 0; t < launched; t++) pthread_join(threads[t], NULL);
925 pthread_mutex_destroy(&sh.lock);
929 int64_t result = (int64_t)len;
930 for (uint32_t i = 0; i < num_jobs; i++) {
931 if (jobs[i].result < 0) {
932 result = (int64_t)jobs[i].result;
948 if (UNLIKELY(!s))
return;
951 for (
size_t i = 0; i < s->num_dicts; i++)
ZXC_FREE(s->dicts[i].data);
966 s->verify_checksums = enabled ? 1 : 0;
979 const void* dict_huf) {
982 const uint32_t
id =
zxc_dict_id(dict, dict_size, (
const uint8_t*)dict_huf);
985 for (
size_t i = 0; i < s->num_frames && !used; i++) {
986 used = s->frames[i].dict_id == id;
987 any |= s->frames[i].dict_id != 0;
993 zxc_seek_dict_t* d = NULL;
994 for (
size_t i = 0; i < s->num_dicts && !d; i++)
995 if (s->dicts[i].id ==
id) d = &s->dicts[i];
997 zxc_seek_dict_t*
const grown =
998 (zxc_seek_dict_t*)
ZXC_REALLOC(s->dicts, (s->num_dicts + 1) *
sizeof(*s->dicts));
1001 d = &s->dicts[s->num_dicts];
1002 ZXC_MEMSET(d, 0,
sizeof(*d));
1004 uint8_t*
const copy = (uint8_t*)
ZXC_MALLOC(dict_size);
1006 ZXC_MEMCPY(copy, dict, dict_size);
1007 if (d == &s->dicts[s->num_dicts]) s->num_dicts++;
1010 d->size = dict_size;
1012 d->has_huf = dict_huf != NULL;
1014 if (dict_size > s->max_dict_size) s->max_dict_size = dict_size;
1018 if (s->dctx_initialized) {
1020 s->dctx_initialized = 0;
1034 if (s) s->owned_reader_ctx = ctx;
uint32_t zxc_dict_id(const void *dict, size_t dict_size, const void *huf_lengths)
Computes the dictionary ID for the given content and optional table.
#define ZXC_DICT_SIZE_MAX
Maximum dictionary content size in bytes (64 KB - 1).
#define ZXC_HUF_TABLE_SIZE
Size of a packed literal Huffman code-lengths table (256 symbols, 4 bits each): the shared table in a...
@ ZXC_ERROR_CORRUPT_DATA
Corrupted compressed data.
@ ZXC_ERROR_NULL_INPUT
Required input pointer is NULL.
@ ZXC_ERROR_IO
Read/write/seek failure on file.
@ ZXC_ERROR_DICT_MISMATCH
Provided dictionary ID does not match the file header.
@ ZXC_ERROR_MEMORY
Memory allocation failure.
@ ZXC_ERROR_DST_TOO_SMALL
Destination buffer too small.
@ ZXC_ERROR_SRC_TOO_SMALL
Source buffer too small or truncated input.
@ ZXC_ERROR_DICT_REQUIRED
File requires a dictionary but none was provided.
@ ZXC_OK
Success (no error).
@ ZXC_ERROR_DICT_TOO_LARGE
Dictionary exceeds maximum allowed size.
#define ZXC_MALLOC(size)
Heap allocator.
static uint32_t zxc_seek_group_len(const uint64_t nblocks, const uint64_t g)
Blocks in group g of nblocks: ZXC_SEEK_GROUP, fewer for the last.
#define ZXC_SEEK_GROUP
Blocks per group.
static uint64_t zxc_seek_table_bytes(const uint64_t nblocks)
Byte size of the groups for nblocks blocks, in 64 bits.
static uint64_t zxc_seek_group_count(const uint64_t nblocks)
Groups holding nblocks blocks; the last one may be partial.
#define ZXC_SEEK_SIZE_ENTRY
One block's on-disk size, u32.
#define ZXC_CALLOC(nmemb, size)
Zero-initialised heap allocator.
#define ZXC_SEEK_GROUP_BYTES
Full group: anchor + ZXC_SEEK_GROUP sizes.
#define ZXC_FREE(ptr)
Heap deallocator.
#define ZXC_PAD_SIZE
Safety padding appended to buffers to tolerate overruns.
#define ZXC_BLOCK_HEADER_SIZE
Block header size: Type(1)+Flags(1)+Reserved(1)+Checksum(1)+CompSize(4).
#define ZXC_REALLOC(ptr, size)
In-place / move heap reallocator.
#define ZXC_BLOCK_CHECKSUM_SIZE
Size of the per-block checksum field in bytes.
#define ZXC_SEEK_ANCHOR_SIZE
Group anchor: offset of its first block, u64.
#define ZXC_DECOMPRESS_TAIL_PAD
Tail padding required on the decompression destination buffer.
int64_t zxc_seekable_decompress_range_mt(zxc_seekable *s, void *dst, const size_t dst_capacity, const uint64_t offset, const size_t len, int n_threads)
Multi-threaded variant of zxc_seekable_decompress_range.
void zxc_seekable_free(zxc_seekable *s)
Releases a seekable handle and every resource it owns.
int64_t zxc_seekable_decompress_range(zxc_seekable *s, void *dst, const size_t dst_capacity, const uint64_t offset, const size_t len)
Decompresses the byte range [offset, offset + len) into dst.
uint64_t zxc_seekable_get_num_blocks(const zxc_seekable *s)
Number of blocks in the archive.
uint32_t zxc_seekable_get_block_decomp_size(const zxc_seekable *s, const uint64_t block_idx)
Decompressed byte size of a given block.
uint64_t zxc_seekable_get_decompressed_size(const zxc_seekable *s)
Total decompressed size of the archive.
int zxc_seekable_set_dict(zxc_seekable *s, const void *dict, const size_t dict_size, const void *dict_huf)
Installs the dictionary needed to decode a dict-compressed archive.
uint32_t zxc_seekable_get_block_comp_size(const zxc_seekable *s, const uint64_t block_idx)
Compressed byte size of a given block, from its seek table entry.
int zxc_seekable_set_checksum(zxc_seekable *s, const int enabled)
Turns per-block checksum verification on or off.
zxc_seekable * zxc_seekable_open(const void *src, const size_t src_size)
Opens a seekable archive held entirely in a memory buffer.
zxc_seekable * zxc_seekable_open_reader(const zxc_reader_t *r)
Opens a seekable archive over a caller-supplied random-access reader.
struct zxc_seekable_s zxc_seekable
Opaque handle for a seekable ZXC archive.
#define ZXC_MAX_THREADS
Maximum n_threads for zxc_stream_compress / zxc_stream_decompress.
Compression / decompression context.
uint8_t * work_buf
Padded scratch buffer for buffer-API decompression.
size_t chunk_size
Effective block size in bytes.
uint8_t * dict_buffer
[dict | data] concat scratch carved from memory_block when dict_size > 0 (NULL otherwise).
size_t dict_size
Dictionary prefill size (0 = no dictionary).
int checksum_enabled
1 if checksum calculation/verification is enabled.
What a frame's header and footer declare, read without decoding.
uint8_t has_checksum
1 when blocks carry checksums and the footer a digest.
size_t block_size
Block size, 4 KB to 2 MB.
uint64_t decompressed_size
Source bytes the frame decodes to.
uint32_t dict_id
Dictionary the frame needs; 0 for none.
uint8_t has_seek_table
1 when a seek table precedes the footer.
Storage-agnostic reader interface for seekable archives.
uint64_t size
Total size of the compressed archive in bytes.
int64_t(* read_at)(void *ctx, void *dst, size_t len, uint64_t offset)
Reads exactly len bytes at offset into dst.
Where a container scan reads from: memory, or a positioned callback (the FILE* size query).
int zxc_read_block_header(const uint8_t *src, const size_t src_size, zxc_block_header_t *bh)
Parses and validates a block header from src.
void zxc_cctx_free(zxc_cctx_t *ctx)
Releases all resources owned by a compression context.
int zxc_scan_frame(const zxc_scan_src_t *src, const uint64_t end, uint64_t *start, zxc_frame_info_t *info, size_t *footer_len)
Measures the frame ending at end without decoding: its footer's compressed size gives where it starts...
int zxc_check_eof_header(const uint8_t *hdr)
Checks an EOF block header against its one valid form: type 255, every other field zero,...
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_cctx_attach_dict_huf(zxc_cctx_t *ctx, const uint8_t *lengths)
Attach the shared dictionary literal Huffman table to a context.
int zxc_check_seek_header(const uint8_t *hdr, const size_t avail, const uint64_t total_out, const size_t block_size, uint64_t *sek_bytes)
Checks the SEK block header the file header announced, on the avail bytes present.
int zxc_decompress_chunk_wrapper(const zxc_cctx_t *ctx, const uint8_t *src, const size_t src_sz, uint8_t *dst, const size_t dst_cap, const uint64_t block_index)
Public no-dict chunk decoder (decompression hot path).
Internal definitions, constants, SIMD helpers, and utility functions.
static uint64_t zxc_le64(const void *p)
Reads a little-endian 64-bit value from a possibly unaligned address.
static int zxc_ctz32(const uint32_t x)
Counts trailing zeros, returning 32 for a zero input.
static uint32_t zxc_le32(const void *p)
Reads a little-endian 32-bit value from a possibly unaligned address.
static uint64_t zxc_seek_block_count(const uint64_t total_decomp, const size_t block_size)
Blocks in total_decomp bytes of block_size: the seek table's entry count, which the SEK header's fiel...
static const zxc_seek_frame_t * zxc_seek_frame_of_block(const zxc_seekable *s, const uint64_t idx)
The frame holding archive-wide block idx, below the block count.
static uint32_t zxc_seek_decomp_size(const uint32_t block_size, const uint64_t total_decomp, const uint64_t idx)
Decompressed size of block idx (O(1)).
static int zxc_seek_load_spans(const zxc_seekable *s, const zxc_seek_frame_t *f, const uint64_t first, const uint32_t n, uint64_t *starts, uint32_t *sizes, uint8_t *raw)
Loads where each block of [first, first + n) starts and its on-disk size, both taken from the block's...
static void * zxc_seek_mt_worker(void *arg)
Worker thread entry point for multi-threaded seekable decompression.
void zxc_seekable_attach_owned_ctx(zxc_seekable *s, void *ctx)
Transfers ownership of a heap reader context to the handle.
static zxc_seek_source_t zxc_seek_source_of(const zxc_seekable *s)
The handle's archive as a read source, for the per-block readers.
static int zxc_seek_scan_read(const zxc_scan_src_t *scan, const uint64_t off, void *dst, const size_t len)
zxc_scan_src_t over a zxc_seek_source_t.
static int zxc_seek_read_block(const zxc_seekable *s, const uint64_t off, const uint32_t csz, const int has_cs, uint8_t *buf, const size_t buf_cap)
Reads a compressed block into buf from the memory buffer or reader.
#define ZXC_SEEK_TAIL_HDRS
EOF and SEK block headers, ahead of a frame's table: checked when it is read.
static int zxc_seek_parse_frame(zxc_seek_source_t *src, const uint64_t end, zxc_seek_frame_t *f)
Validates the seekable frame that ends at end, in two reads: header and footer as zxc_get_last_frame_...
static void zxc_seek_mt_fail_stripe(const zxc_seek_mt_shared_t *sh, const uint32_t first, const int code)
Marks every job of stripe first with code (setup-failure path).
static zxc_seekable * zxc_seekable_parse(const zxc_seek_source_t *source)
Walks the frames back from the end through zxc_seek_parse_frame, then numbers their bytes and blocks.
static uint64_t zxc_seek_block_of_offset(const zxc_seekable *s, const uint64_t offset)
Archive-wide index of the block holding decompressed offset, below the size.
#define ZXC_SEEK_MAX_FRAMES
Most frames a handle indexes: bounds the reads and memory of an open.
static size_t zxc_seek_spans_raw_max(const uint64_t first, const uint32_t n)
Scratch bound for zxc_seek_load_spans(): the groups blocks [first, first + n) touch.
static int zxc_seek_source_read(const zxc_seek_source_t *src, void *dst, const size_t len, const uint64_t off)
Reads a byte range from the archive, whatever backs it: the one bounds check every parsed offset goes...
static int zxc_seek_use_dict(const zxc_seekable *s, zxc_cctx_t *ctx, const zxc_seek_dict_t **held, const zxc_seek_frame_t *f)
Gives ctx frame f's dictionary, copied in unless *held, the one it holds, is already that one.
POSIX-shaped threading layer shared by the stream engine (zxc_driver.c) and the seekable reader (zxc_...
static int zxc_num_procs(void)
Returns the number of online logical processors.