ch_connect 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,634 @@
1
+ /*
2
+ * clickhouse-compression.h -- compressed-frame layout, CityHash128, codec
3
+ * vtable, and ready-made LZ4 / ZSTD chc_codec wrappers.
4
+ *
5
+ * Exactly one TU must `#define CHC_IMPLEMENTATION` before including;
6
+ * the implementation uses static helpers from clickhouse.h.
7
+ *
8
+ * LZ4 wrapper (chc_lz4_codec_init) is compiled in by default & pulls
9
+ * <lz4.h>; link -llz4. Define CHC_NO_LZ4 before including to opt out
10
+ * (drops the include & the wrapper).
11
+ *
12
+ * ZSTD wrapper (chc_zstd_codec_init) is compiled in by default & pulls
13
+ * <zstd.h>; link -lzstd. Define CHC_NO_ZSTD before including to opt out.
14
+ *
15
+ * Frame layout (matches ClickHouse server / clickhouse-cpp
16
+ * base/compressed.cpp):
17
+ *
18
+ * [ 16 B CityHash128 of the rest of the frame ]
19
+ * [ 1 B method (0x82 LZ4, 0x90 ZSTD, 0x02 none) ]
20
+ * [ 4 B LE: compressed_size_with_header (= 9 + payload bytes)]
21
+ * [ 4 B LE: original_size ]
22
+ * [ payload ]
23
+ *
24
+ * The CityHash128 covers the method byte, the two size fields, and the
25
+ * payload bytes -- i.e. everything starting at offset 16.
26
+ */
27
+
28
+ #ifndef CLICKHOUSE_COMPRESSION_H
29
+ #define CLICKHOUSE_COMPRESSION_H
30
+
31
+ #include <stdbool.h>
32
+ #include <stddef.h>
33
+ #include <stdint.h>
34
+
35
+ #include "clickhouse.h"
36
+
37
+ #ifdef __cplusplus
38
+ extern "C" {
39
+ #endif
40
+
41
+ /* Compression mode for client Data packets. Shared between
42
+ * clickhouse-client.h and clickhouse-compression.h so consumers can
43
+ * include the latter in isolation if driving frame format directly. */
44
+ typedef enum chc_compression {
45
+ CHC_COMP_NONE = 0,
46
+ CHC_COMP_LZ4 = 1,
47
+ CHC_COMP_ZSTD = 2,
48
+ } chc_compression;
49
+
50
+ typedef struct chc_codec chc_codec;
51
+
52
+ struct chc_codec {
53
+ void *ud;
54
+
55
+ /* Compress src[0..src_len] into dst[0..dst_cap]. On success returns
56
+ * CHC_OK and writes compressed-size into *dst_n. On insufficient
57
+ * destination capacity returns CHC_ERR_OOM. */
58
+ int (*lz4_compress) (void *ud, const void *src, size_t src_len,
59
+ void *dst, size_t dst_cap, size_t *dst_n,
60
+ chc_err *err);
61
+ /* original_size is the known uncompressed payload length. */
62
+ int (*lz4_decompress)(void *ud, const void *src, size_t src_len,
63
+ void *dst, size_t original_size,
64
+ chc_err *err);
65
+
66
+ int (*zstd_compress) (void *ud, const void *src, size_t src_len,
67
+ void *dst, size_t dst_cap, size_t *dst_n,
68
+ chc_err *err);
69
+ int (*zstd_decompress)(void *ud, const void *src, size_t src_len,
70
+ void *dst, size_t original_size,
71
+ chc_err *err);
72
+
73
+ /* Worst-case compressed size for a payload of src_len bytes. May be
74
+ * NULL; chc__compress_emit_frame() falls back to src_len + 256 +
75
+ * src_len/255 in that case (LZ4's classic bound formula). */
76
+ size_t (*lz4_bound) (size_t src_len);
77
+ size_t (*zstd_bound)(size_t src_len);
78
+ };
79
+
80
+ /* Default chunk size for outgoing frames (matches clickhouse-cpp). */
81
+ #define CHC_COMPRESS_MAX_CHUNK 65535u
82
+
83
+ /* Internal-but-exported: 128-bit CityHash. Returned as the two 64-bit
84
+ * halves the wire-format encodes (lo first, hi second). Frozen variant
85
+ * matching CH server / clickhouse-cpp. */
86
+ void chc_cityhash128(const void *data, size_t len,
87
+ uint64_t *out_lo, uint64_t *out_hi);
88
+
89
+ #ifndef CHC_NO_LZ4
90
+ /* Fill lz4_* slots of `out` with wrappers around <lz4.h>. Leaves zstd_*
91
+ * untouched, so callers wanting both codecs can call both init helpers
92
+ * on the same struct in either order. Caller links -llz4. */
93
+ void chc_lz4_codec_init(chc_codec *out);
94
+ #endif
95
+
96
+ #ifndef CHC_NO_ZSTD
97
+ /* Fill zstd_* slots of `out` with wrappers around <zstd.h>. Leaves lz4_*
98
+ * untouched. Caller links -lzstd. */
99
+ void chc_zstd_codec_init(chc_codec *out);
100
+ #endif
101
+
102
+ #ifdef CHC_IMPLEMENTATION
103
+
104
+ #include <limits.h>
105
+ #include <string.h>
106
+
107
+ /* ============================================================
108
+ * CityHash128 (frozen v1.0.3 variant, ported from city.cc).
109
+ * Original: Copyright (c) 2011 Google, Inc. (MIT licence).
110
+ * Short-string helpers live in clickhouse.h; this block layers
111
+ * the 128-bit driver on top.
112
+ * ============================================================ */
113
+
114
+ static uint64_t chc__city_rotate(uint64_t v, int s)
115
+ {
116
+ return s == 0 ? v : ((v >> s) | (v << (64 - s)));
117
+ }
118
+
119
+ typedef struct { uint64_t a, b; } chc__u128;
120
+
121
+ static chc__u128
122
+ chc__city_weak_seeds(uint64_t w, uint64_t x, uint64_t y, uint64_t z,
123
+ uint64_t a, uint64_t b)
124
+ {
125
+ a += w;
126
+ b = chc__city_rotate(b + a + z, 21);
127
+ uint64_t c = a;
128
+ a += x;
129
+ a += y;
130
+ b += chc__city_rotate(a, 44);
131
+ chc__u128 r = { a + z, b + c };
132
+ return r;
133
+ }
134
+
135
+ static chc__u128 chc__city_weak_s(const char *s, uint64_t a, uint64_t b)
136
+ {
137
+ return chc__city_weak_seeds(chc__city_fetch64(s),
138
+ chc__city_fetch64(s + 8),
139
+ chc__city_fetch64(s + 16),
140
+ chc__city_fetch64(s + 24),
141
+ a, b);
142
+ }
143
+
144
+ static chc__u128
145
+ chc__city_murmur(const char *s, size_t len, chc__u128 seed)
146
+ {
147
+ uint64_t a = seed.a;
148
+ uint64_t b = seed.b;
149
+ uint64_t c = 0;
150
+ uint64_t d = 0;
151
+ if (len <= 16) {
152
+ a = chc__city_shift_mix(a * chc__city_k1) * chc__city_k1;
153
+ c = b * chc__city_k1 + chc__city_hash_len_0_to_16(s, len);
154
+ d = chc__city_shift_mix(a + (len >= 8 ? chc__city_fetch64(s) : c));
155
+ } else {
156
+ c = chc__city_hash_len_16(chc__city_fetch64(s + len - 8) + chc__city_k1, a);
157
+ d = chc__city_hash_len_16(b + len, c + chc__city_fetch64(s + len - 16));
158
+ a += d;
159
+ do {
160
+ a ^= chc__city_shift_mix(chc__city_fetch64(s) * chc__city_k1) * chc__city_k1;
161
+ a *= chc__city_k1;
162
+ b ^= a;
163
+ c ^= chc__city_shift_mix(chc__city_fetch64(s + 8) * chc__city_k1) * chc__city_k1;
164
+ c *= chc__city_k1;
165
+ d ^= c;
166
+ s += 16;
167
+ len -= 16;
168
+ } while (len > 16);
169
+ }
170
+ a = chc__city_hash_len_16(a, c);
171
+ b = chc__city_hash_len_16(d, b);
172
+ chc__u128 r = { a ^ b, chc__city_hash_len_16(b, a) };
173
+ return r;
174
+ }
175
+
176
+ static chc__u128
177
+ chc__city_hash128_with_seed(const char *s, size_t len, chc__u128 seed)
178
+ {
179
+ if (len < 128) return chc__city_murmur(s, len, seed);
180
+
181
+ chc__u128 v = {}, w = {};
182
+ uint64_t x = seed.a, y = seed.b;
183
+ uint64_t z = len * chc__city_k1;
184
+ v.a = chc__city_rotate(y ^ chc__city_k1, 49) * chc__city_k1 + chc__city_fetch64(s);
185
+ v.b = chc__city_rotate(v.a, 42) * chc__city_k1 + chc__city_fetch64(s + 8);
186
+ w.a = chc__city_rotate(y + z, 35) * chc__city_k1 + x;
187
+ w.b = chc__city_rotate(x + chc__city_fetch64(s + 88), 53) * chc__city_k1;
188
+
189
+ do {
190
+ x = chc__city_rotate(x + y + v.a + chc__city_fetch64(s + 16), 37) * chc__city_k1;
191
+ y = chc__city_rotate(y + v.b + chc__city_fetch64(s + 48), 42) * chc__city_k1;
192
+ x ^= w.b; y ^= v.a;
193
+ z = chc__city_rotate(z ^ w.a, 33);
194
+ v = chc__city_weak_s(s, v.b * chc__city_k1, x + w.a);
195
+ w = chc__city_weak_s(s + 32, z + w.b, y);
196
+ { uint64_t tmp = z; z = x; x = tmp; }
197
+ s += 64;
198
+ x = chc__city_rotate(x + y + v.a + chc__city_fetch64(s + 16), 37) * chc__city_k1;
199
+ y = chc__city_rotate(y + v.b + chc__city_fetch64(s + 48), 42) * chc__city_k1;
200
+ x ^= w.b; y ^= v.a;
201
+ z = chc__city_rotate(z ^ w.a, 33);
202
+ v = chc__city_weak_s(s, v.b * chc__city_k1, x + w.a);
203
+ w = chc__city_weak_s(s + 32, z + w.b, y);
204
+ { uint64_t tmp = z; z = x; x = tmp; }
205
+ s += 64;
206
+ len -= 128;
207
+ } while (len >= 128);
208
+
209
+ y += chc__city_rotate(w.a, 37) * chc__city_k0 + z;
210
+ x += chc__city_rotate(v.a + z, 49) * chc__city_k0;
211
+ for (size_t td = 0; td < len; ) {
212
+ td += 32;
213
+ y = chc__city_rotate(y - x, 42) * chc__city_k0 + v.b;
214
+ w.a += chc__city_fetch64(s + len - td + 16);
215
+ x = chc__city_rotate(x, 49) * chc__city_k0 + w.a;
216
+ w.a += v.a;
217
+ v = chc__city_weak_s(s + len - td, v.a, v.b);
218
+ }
219
+ x = chc__city_hash_len_16(x, v.a);
220
+ y = chc__city_hash_len_16(y, w.a);
221
+ chc__u128 r = { chc__city_hash_len_16(x + v.b, w.b) + y,
222
+ chc__city_hash_len_16(x + w.b, y + v.b) };
223
+ return r;
224
+ }
225
+
226
+ static chc__u128 chc__city_hash128_impl(const char *s, size_t len)
227
+ {
228
+ chc__u128 seed;
229
+ if (len >= 16) {
230
+ seed.a = chc__city_fetch64(s) ^ chc__city_k3;
231
+ seed.b = chc__city_fetch64(s + 8);
232
+ return chc__city_hash128_with_seed(s + 16, len - 16, seed);
233
+ } else if (len >= 8) {
234
+ seed.a = chc__city_fetch64(s) ^ (len * chc__city_k0);
235
+ seed.b = chc__city_fetch64(s + len - 8) ^ chc__city_k1;
236
+ return chc__city_hash128_with_seed(NULL, 0, seed);
237
+ } else {
238
+ seed.a = chc__city_k0;
239
+ seed.b = chc__city_k1;
240
+ return chc__city_hash128_with_seed(s, len, seed);
241
+ }
242
+ }
243
+
244
+ void
245
+ chc_cityhash128(const void *data, size_t len,
246
+ uint64_t *out_lo, uint64_t *out_hi)
247
+ {
248
+ chc__u128 r = chc__city_hash128_impl((const char *) data, len);
249
+ *out_lo = r.a;
250
+ *out_hi = r.b;
251
+ }
252
+
253
+ /* ============================================================
254
+ * Frame I/O
255
+ * ============================================================ */
256
+
257
+ #define CHC__COMP_LZ4 0x82u
258
+ #define CHC__COMP_ZSTD 0x90u
259
+ #define CHC__COMP_HEADER_BYTES 9u
260
+
261
+ /* lz4-style fallback bound. */
262
+ static size_t chc__comp_default_bound(size_t n) { return n + 256 + n / 255; }
263
+
264
+ /* Write one compressed frame to io. Caller has already supplied the
265
+ * codec for the chosen method. */
266
+ static int
267
+ chc__comp_emit_frame(chc_io *io, const chc_codec *codec, chc_compression m,
268
+ const void *src, size_t src_len,
269
+ const chc_alloc *al, chc_err *err)
270
+ {
271
+ int rc;
272
+ uint8_t method;
273
+ size_t bound;
274
+ switch (m) {
275
+ case CHC_COMP_LZ4:
276
+ method = CHC__COMP_LZ4;
277
+ bound = codec->lz4_bound ? codec->lz4_bound(src_len)
278
+ : chc__comp_default_bound(src_len);
279
+ break;
280
+ case CHC_COMP_ZSTD:
281
+ method = CHC__COMP_ZSTD;
282
+ bound = codec->zstd_bound ? codec->zstd_bound(src_len)
283
+ : chc__comp_default_bound(src_len);
284
+ break;
285
+ default:
286
+ return chc__err_set(err, CHC_ERR_USAGE,
287
+ "chc__comp_emit_frame: unsupported method");
288
+ }
289
+ size_t buf_cap = CHC__COMP_HEADER_BYTES + bound;
290
+ uint8_t *buf = chc__alloc(al, buf_cap, err);
291
+ if (!buf) return CHC_ERR_OOM;
292
+
293
+ size_t comp_sz = 0;
294
+ if (m == CHC_COMP_LZ4) {
295
+ rc = codec->lz4_compress(codec->ud, src, src_len,
296
+ buf + CHC__COMP_HEADER_BYTES,
297
+ buf_cap - CHC__COMP_HEADER_BYTES,
298
+ &comp_sz, err);
299
+ } else {
300
+ rc = codec->zstd_compress(codec->ud, src, src_len,
301
+ buf + CHC__COMP_HEADER_BYTES,
302
+ buf_cap - CHC__COMP_HEADER_BYTES,
303
+ &comp_sz, err);
304
+ }
305
+ if (rc != CHC_OK) { al->free(al->ud, buf, buf_cap); return rc; }
306
+
307
+ uint32_t comp_with_hdr = (uint32_t) (comp_sz + CHC__COMP_HEADER_BYTES);
308
+ uint32_t orig = (uint32_t) src_len;
309
+ uint32_t comp_le = chc__bswap32(comp_with_hdr);
310
+ uint32_t orig_le = chc__bswap32(orig);
311
+ buf[0] = method;
312
+ memcpy(buf + 1, &comp_le, sizeof comp_le);
313
+ memcpy(buf + 5, &orig_le, sizeof orig_le);
314
+
315
+ uint64_t lo, hi;
316
+ chc_cityhash128(buf, CHC__COMP_HEADER_BYTES + comp_sz, &lo, &hi);
317
+
318
+ rc = chc__write_u64_le(io, lo, err);
319
+ if (rc == CHC_OK) rc = chc__write_u64_le(io, hi, err);
320
+ if (rc == CHC_OK) rc = chc__write_bytes(io, buf,
321
+ CHC__COMP_HEADER_BYTES + comp_sz, err);
322
+ al->free(al->ud, buf, buf_cap);
323
+ return rc;
324
+ }
325
+
326
+ /* Mem sink chc_io. Used to capture a block's bytes before compressing. */
327
+ typedef struct chc__mem_sink {
328
+ uint8_t *buf;
329
+ size_t cap;
330
+ size_t len;
331
+ const chc_alloc *al;
332
+ int oom;
333
+ } chc__mem_sink;
334
+
335
+ static int
336
+ chc__mem_sink_write(void *ud, const void *p, size_t n, chc_err *err)
337
+ {
338
+ chc__mem_sink *s = ud;
339
+ if (s->oom) return chc__err_set(err, CHC_ERR_OOM, "mem sink oom");
340
+ if (n > SIZE_MAX - s->len) {
341
+ s->oom = 1;
342
+ return chc__err_set(err, CHC_ERR_OOM, "mem sink size overflow");
343
+ }
344
+ size_t need = s->len + n;
345
+ if (need > s->cap) {
346
+ size_t new_cap = s->cap ? s->cap : 4096;
347
+ while (new_cap < need) {
348
+ if (new_cap > SIZE_MAX / 2) { new_cap = need; break; }
349
+ new_cap *= 2;
350
+ }
351
+ uint8_t *nb = chc__realloc(s->al, s->buf, s->cap, new_cap, err);
352
+ if (!nb) { s->oom = 1; return CHC_ERR_OOM; }
353
+ s->buf = nb; s->cap = new_cap;
354
+ }
355
+ memcpy(s->buf + s->len, p, n);
356
+ s->len += n;
357
+ return CHC_OK;
358
+ }
359
+
360
+ CHC_MAYBE_UNUSED static void
361
+ chc__mem_sink_init(chc__mem_sink *s, chc_io *io, const chc_alloc *al)
362
+ {
363
+ *s = (chc__mem_sink) { .al = al };
364
+ *io = (chc_io) { .ud = s, .write = chc__mem_sink_write };
365
+ }
366
+
367
+ CHC_MAYBE_UNUSED static void
368
+ chc__mem_sink_free(chc__mem_sink *s)
369
+ {
370
+ s->al->free(s->al->ud, s->buf, s->cap);
371
+ s->buf = NULL;
372
+ s->cap = s->len = 0;
373
+ }
374
+
375
+ /* Emit a buffered block as one or more compressed frames to io. */
376
+ static int
377
+ chc__comp_emit_chunks(chc_io *io, const chc_codec *codec, chc_compression m,
378
+ const void *src, size_t src_len,
379
+ const chc_alloc *al, chc_err *err)
380
+ {
381
+ const uint8_t *p = src;
382
+ size_t left = src_len;
383
+ while (left) {
384
+ size_t take = left > CHC_COMPRESS_MAX_CHUNK
385
+ ? CHC_COMPRESS_MAX_CHUNK : left;
386
+ int rc = chc__comp_emit_frame(io, codec, m, p, take, al, err);
387
+ if (rc != CHC_OK) return rc;
388
+ p += take;
389
+ left -= take;
390
+ }
391
+ return CHC_OK;
392
+ }
393
+
394
+ /* ---------- decompression side ---------- */
395
+
396
+ /* Adapter chc_io that produces decompressed bytes from a compressed
397
+ * frame stream sourced from a chc_in. Each "frame" is read on demand;
398
+ * we hold one frame's uncompressed bytes in scratch. */
399
+ typedef struct chc__decomp_src {
400
+ chc_in *raw;
401
+ const chc_codec *codec;
402
+ const chc_alloc *al;
403
+ uint8_t *frame_buf;
404
+ size_t frame_cap;
405
+ size_t frame_pos;
406
+ size_t frame_fill;
407
+ uint8_t *comp_buf;
408
+ size_t comp_cap;
409
+ } chc__decomp_src;
410
+
411
+ static int
412
+ chc__decomp_read_frame(chc__decomp_src *s, chc_err *err)
413
+ {
414
+ int rc;
415
+ uint64_t lo, hi;
416
+ rc = chc__read_u64_le(s->raw, &lo, err); if (rc) return rc;
417
+ rc = chc__read_u64_le(s->raw, &hi, err); if (rc) return rc;
418
+ uint8_t header[CHC__COMP_HEADER_BYTES];
419
+ rc = chc__read_bytes(s->raw, header, CHC__COMP_HEADER_BYTES, err);
420
+ if (rc) return rc;
421
+ uint8_t method = header[0];
422
+ uint32_t comp_with_hdr, orig;
423
+ memcpy(&comp_with_hdr, header + 1, sizeof comp_with_hdr);
424
+ memcpy(&orig, header + 5, sizeof orig);
425
+ comp_with_hdr = chc__bswap32(comp_with_hdr);
426
+ orig = chc__bswap32(orig);
427
+ if (comp_with_hdr < CHC__COMP_HEADER_BYTES)
428
+ return chc__err_set(err, CHC_ERR_PROTOCOL,
429
+ "compressed frame too short: %u", comp_with_hdr);
430
+ if (comp_with_hdr > 0x40000000u)
431
+ return chc__err_set(err, CHC_ERR_PROTOCOL,
432
+ "compressed frame oversized: %u", comp_with_hdr);
433
+ /* LZ4 API sizes are int; reject dst outside domain before (int)
434
+ * narrowing in codec adapter. src side bounded by frame cap above */
435
+ if (method == CHC__COMP_LZ4 && orig > (uint32_t) INT_MAX)
436
+ return chc__err_set(err, CHC_ERR_PROTOCOL,
437
+ "LZ4 original size oversized: %u", orig);
438
+
439
+ size_t payload = comp_with_hdr - CHC__COMP_HEADER_BYTES;
440
+ /* Capture the bytes hashed: header (9B) + payload. */
441
+ if (comp_with_hdr > s->comp_cap) {
442
+ uint8_t *nb = chc__realloc(s->al, s->comp_buf, s->comp_cap,
443
+ comp_with_hdr, err);
444
+ if (!nb) return CHC_ERR_OOM;
445
+ s->comp_buf = nb; s->comp_cap = comp_with_hdr;
446
+ }
447
+ memcpy(s->comp_buf, header, CHC__COMP_HEADER_BYTES);
448
+ rc = chc__read_bytes(s->raw, s->comp_buf + CHC__COMP_HEADER_BYTES,
449
+ payload, err);
450
+ if (rc) return rc;
451
+
452
+ uint64_t got_lo, got_hi;
453
+ chc_cityhash128(s->comp_buf, comp_with_hdr, &got_lo, &got_hi);
454
+ if (got_lo != lo || got_hi != hi)
455
+ return chc__err_set(err, CHC_ERR_PROTOCOL,
456
+ "compressed frame hash mismatch");
457
+
458
+ if (orig > s->frame_cap) {
459
+ uint8_t *nb = chc__realloc(s->al, s->frame_buf, s->frame_cap,
460
+ orig, err);
461
+ if (!nb) return CHC_ERR_OOM;
462
+ s->frame_buf = nb; s->frame_cap = orig;
463
+ }
464
+ switch (method) {
465
+ case CHC__COMP_LZ4:
466
+ if (!s->codec || !s->codec->lz4_decompress)
467
+ return chc__err_set(err, CHC_ERR_USAGE,
468
+ "LZ4 frame received but no codec configured");
469
+ rc = s->codec->lz4_decompress(s->codec->ud,
470
+ s->comp_buf + CHC__COMP_HEADER_BYTES, payload,
471
+ s->frame_buf, orig, err);
472
+ if (rc) return rc;
473
+ break;
474
+ case CHC__COMP_ZSTD:
475
+ if (!s->codec || !s->codec->zstd_decompress)
476
+ return chc__err_set(err, CHC_ERR_USAGE,
477
+ "ZSTD frame received but no codec configured");
478
+ rc = s->codec->zstd_decompress(s->codec->ud,
479
+ s->comp_buf + CHC__COMP_HEADER_BYTES, payload,
480
+ s->frame_buf, orig, err);
481
+ if (rc) return rc;
482
+ break;
483
+ default:
484
+ return chc__err_set(err, CHC_ERR_PROTOCOL,
485
+ "unknown compression method 0x%02x", method);
486
+ }
487
+ s->frame_pos = 0;
488
+ s->frame_fill = orig;
489
+ return CHC_OK;
490
+ }
491
+
492
+ static int
493
+ chc__decomp_io_read(void *ud, void *buf, size_t len, size_t *out_n,
494
+ chc_err *err)
495
+ {
496
+ chc__decomp_src *s = ud;
497
+ if (s->frame_pos == s->frame_fill) {
498
+ int rc = chc__decomp_read_frame(s, err);
499
+ if (rc) { *out_n = 0; return rc; }
500
+ }
501
+ size_t avail = s->frame_fill - s->frame_pos;
502
+ size_t take = len < avail ? len : avail;
503
+ memcpy(buf, s->frame_buf + s->frame_pos, take);
504
+ s->frame_pos += take;
505
+ *out_n = take;
506
+ return CHC_OK;
507
+ }
508
+
509
+ static void
510
+ chc__decomp_src_init(chc__decomp_src *s, chc_in *raw, const chc_codec *codec,
511
+ const chc_alloc *al, chc_io *out_io)
512
+ {
513
+ *s = (chc__decomp_src) { .raw = raw, .codec = codec, .al = al };
514
+ *out_io = (chc_io) { .ud = s, .read = chc__decomp_io_read };
515
+ }
516
+
517
+ static void
518
+ chc__decomp_src_free(chc__decomp_src *s)
519
+ {
520
+ s->al->free(s->al->ud, s->frame_buf, s->frame_cap);
521
+ s->al->free(s->al->ud, s->comp_buf, s->comp_cap);
522
+ s->frame_buf = s->comp_buf = NULL;
523
+ s->frame_cap = s->comp_cap = 0;
524
+ }
525
+
526
+ /* ============================================================
527
+ * LZ4 adapter (opt out with CHC_NO_LZ4 before including).
528
+ * ============================================================ */
529
+
530
+ #ifndef CHC_NO_LZ4
531
+
532
+ #include <lz4.h>
533
+
534
+ static int
535
+ chc__lz4_compress(void *ud, const void *src, size_t src_len,
536
+ void *dst, size_t dst_cap, size_t *dst_n, chc_err *err)
537
+ {
538
+ (void) ud;
539
+ if (src_len > (size_t) LZ4_MAX_INPUT_SIZE)
540
+ return chc__err_set(err, CHC_ERR_USAGE,
541
+ "LZ4 input too big: %zu", src_len);
542
+ int n = LZ4_compress_default((const char *) src, (char *) dst,
543
+ (int) src_len, (int) dst_cap);
544
+ if (n <= 0)
545
+ return chc__err_set(err, CHC_ERR_OOM,
546
+ "LZ4_compress_default failed (rc=%d)", n);
547
+ *dst_n = (size_t) n;
548
+ return CHC_OK;
549
+ }
550
+
551
+ static int
552
+ chc__lz4_decompress(void *ud, const void *src, size_t src_len,
553
+ void *dst, size_t original, chc_err *err)
554
+ {
555
+ (void) ud;
556
+ int n = LZ4_decompress_safe((const char *) src, (char *) dst,
557
+ (int) src_len, (int) original);
558
+ if (n < 0 || (size_t) n != original)
559
+ return chc__err_set(err, CHC_ERR_PROTOCOL,
560
+ "LZ4_decompress_safe rc=%d, expected %zu",
561
+ n, original);
562
+ return CHC_OK;
563
+ }
564
+
565
+ static size_t chc__lz4_bound(size_t n) { return (size_t) LZ4_compressBound((int) n); }
566
+
567
+ void
568
+ chc_lz4_codec_init(chc_codec *out)
569
+ {
570
+ out->ud = NULL;
571
+ out->lz4_compress = chc__lz4_compress;
572
+ out->lz4_decompress = chc__lz4_decompress;
573
+ out->lz4_bound = chc__lz4_bound;
574
+ }
575
+
576
+ #endif /* !CHC_NO_LZ4 */
577
+
578
+ /* ============================================================
579
+ * ZSTD adapter (opt out with CHC_NO_ZSTD before including).
580
+ * ============================================================ */
581
+
582
+ #ifndef CHC_NO_ZSTD
583
+
584
+ #include <zstd.h>
585
+
586
+ static int
587
+ chc__zstd_compress(void *ud, const void *src, size_t src_len,
588
+ void *dst, size_t dst_cap, size_t *dst_n, chc_err *err)
589
+ {
590
+ (void) ud;
591
+ size_t n = ZSTD_compress(dst, dst_cap, src, src_len, ZSTD_CLEVEL_DEFAULT);
592
+ if (ZSTD_isError(n))
593
+ return chc__err_set(err, CHC_ERR_OOM,
594
+ "ZSTD_compress failed: %s", ZSTD_getErrorName(n));
595
+ *dst_n = n;
596
+ return CHC_OK;
597
+ }
598
+
599
+ static int
600
+ chc__zstd_decompress(void *ud, const void *src, size_t src_len,
601
+ void *dst, size_t original, chc_err *err)
602
+ {
603
+ (void) ud;
604
+ size_t n = ZSTD_decompress(dst, original, src, src_len);
605
+ if (ZSTD_isError(n))
606
+ return chc__err_set(err, CHC_ERR_PROTOCOL,
607
+ "ZSTD_decompress failed: %s", ZSTD_getErrorName(n));
608
+ if (n != original)
609
+ return chc__err_set(err, CHC_ERR_PROTOCOL,
610
+ "ZSTD_decompress produced %zu bytes, expected %zu",
611
+ n, original);
612
+ return CHC_OK;
613
+ }
614
+
615
+ static size_t chc__zstd_bound(size_t n) { return ZSTD_compressBound(n); }
616
+
617
+ void
618
+ chc_zstd_codec_init(chc_codec *out)
619
+ {
620
+ out->ud = NULL;
621
+ out->zstd_compress = chc__zstd_compress;
622
+ out->zstd_decompress = chc__zstd_decompress;
623
+ out->zstd_bound = chc__zstd_bound;
624
+ }
625
+
626
+ #endif /* !CHC_NO_ZSTD */
627
+
628
+ #endif /* CHC_IMPLEMENTATION */
629
+
630
+ #ifdef __cplusplus
631
+ }
632
+ #endif
633
+
634
+ #endif /* CLICKHOUSE_COMPRESSION_H */