# clickhouse-compression.h Compressed-frame layout, CityHash128, codec vtable, and ready-made LZ4 & ZSTD adapters. Link `-llz4 -lzstd` by default; opt out of either adapter (and its `` / `` include) by defining `CHC_NO_LZ4` or `CHC_NO_ZSTD` before including. Custom codecs are wired by filling `chc_codec` directly. The TCP packet loop in [clickhouse-client.md](clickhouse-client.md) consumes this header. ## Frame layout Matches ClickHouse server / clickhouse-cpp `base/compressed.cpp`: ``` [ 16 B CityHash128 of the rest of the frame ] [ 1 B method (0x82 LZ4, 0x90 ZSTD, 0x02 none) ] [ 4 B LE: compressed_size_with_header (= 9 + payload bytes)] [ 4 B LE: original_size ] [ payload ] ``` CityHash128 covers method + size fields + payload — i.e. everything from offset 16 to end-of-frame. ## Compression mode & codec vtable ```c typedef enum chc_compression { CHC_COMP_NONE = 0, CHC_COMP_LZ4 = 1, CHC_COMP_ZSTD = 2, } chc_compression; typedef struct chc_codec { void *ud; int (*lz4_compress) (void *ud, const void *src, size_t src_len, void *dst, size_t dst_cap, size_t *dst_n, chc_err *err); int (*lz4_decompress)(void *ud, const void *src, size_t src_len, void *dst, size_t original_size, chc_err *err); int (*zstd_compress) (void *ud, const void *src, size_t src_len, void *dst, size_t dst_cap, size_t *dst_n, chc_err *err); int (*zstd_decompress)(void *ud, const void *src, size_t src_len, void *dst, size_t original_size, chc_err *err); size_t (*lz4_bound) (size_t src_len); /* may be NULL */ size_t (*zstd_bound)(size_t src_len); /* may be NULL */ } chc_codec; #define CHC_COMPRESS_MAX_CHUNK 65535u ``` `*_compress` returns the compressed size via `*dst_n`. Insufficient `dst_cap` returns `CHC_ERR_OOM`. `*_decompress` is given the known uncompressed length & must produce exactly that many bytes. Bound callbacks may be NULL; the frame writer falls back to the LZ4 classic `n + 256 + n/255` formula. Outgoing blocks are split into chunks of at most `CHC_COMPRESS_MAX_CHUNK` bytes (65 535) before each is wrapped in its own frame — matches clickhouse-cpp. ## Built-in LZ4 & ZSTD adapters ```c void chc_lz4_codec_init (chc_codec *out); /* fills lz4_* slots */ void chc_zstd_codec_init(chc_codec *out); /* fills zstd_* slots */ ``` Each helper only populates its own slots, so a caller wanting both codecs starts from a zero-initialised `chc_codec` & calls both inits in either order: ```c chc_codec codec = {0}; chc_lz4_codec_init (&codec); chc_zstd_codec_init(&codec); ``` Defining `CHC_NO_LZ4` (or `CHC_NO_ZSTD`) before including `clickhouse-compression.h` drops the corresponding adapter & its `` / `` include, so builds without those libraries present link cleanly. Custom codecs can still be wired by filling `chc_codec` manually. ### LZ4 errors * Source larger than `LZ4_MAX_INPUT_SIZE` returns `CHC_ERR_USAGE`. * `LZ4_compress_default` returning `<= 0` (output capacity too small) returns `CHC_ERR_OOM`. * `LZ4_decompress_safe` rc `< 0`, or producing a different size than the caller declared, returns `CHC_ERR_PROTOCOL`. ### ZSTD errors * `ZSTD_compress` reporting an error returns `CHC_ERR_OOM` with the zstd error name appended. * `ZSTD_decompress` reporting an error, or producing a different byte count than the caller declared, returns `CHC_ERR_PROTOCOL`. ## CityHash128 ```c void chc_cityhash128(const void *data, size_t len, uint64_t *out_lo, uint64_t *out_hi); ``` Frozen v1.0.3 variant ported from Google's `city.cc` (MIT). Returns the two 64-bit halves the wire format encodes (lo first, hi second). Exposed because the implementation already ships it; useful for callers driving the frame format themselves.