1 | #include <cstring> |
2 | |
3 | #include <Compression/CompressionCodecT64.h> |
4 | #include <Compression/CompressionFactory.h> |
5 | #include <common/unaligned.h> |
6 | #include <Parsers/IAST.h> |
7 | #include <Parsers/ASTLiteral.h> |
8 | #include <IO/WriteHelpers.h> |
9 | |
10 | |
11 | namespace DB |
12 | { |
13 | |
14 | namespace ErrorCodes |
15 | { |
16 | extern const int CANNOT_COMPRESS; |
17 | extern const int CANNOT_DECOMPRESS; |
18 | extern const int ILLEGAL_SYNTAX_FOR_CODEC_TYPE; |
19 | extern const int ILLEGAL_CODEC_PARAMETER; |
20 | extern const int LOGICAL_ERROR; |
21 | } |
22 | |
23 | namespace |
24 | { |
25 | |
26 | UInt8 codecId() |
27 | { |
28 | return static_cast<UInt8>(CompressionMethodByte::T64); |
29 | } |
30 | |
31 | TypeIndex baseType(TypeIndex type_idx) |
32 | { |
33 | switch (type_idx) |
34 | { |
35 | case TypeIndex::Int8: |
36 | return TypeIndex::Int8; |
37 | case TypeIndex::Int16: |
38 | return TypeIndex::Int16; |
39 | case TypeIndex::Int32: |
40 | case TypeIndex::Decimal32: |
41 | return TypeIndex::Int32; |
42 | case TypeIndex::Int64: |
43 | case TypeIndex::Decimal64: |
44 | return TypeIndex::Int64; |
45 | case TypeIndex::UInt8: |
46 | case TypeIndex::Enum8: |
47 | return TypeIndex::UInt8; |
48 | case TypeIndex::UInt16: |
49 | case TypeIndex::Enum16: |
50 | case TypeIndex::Date: |
51 | return TypeIndex::UInt16; |
52 | case TypeIndex::UInt32: |
53 | case TypeIndex::DateTime: |
54 | return TypeIndex::UInt32; |
55 | case TypeIndex::UInt64: |
56 | return TypeIndex::UInt64; |
57 | default: |
58 | break; |
59 | } |
60 | |
61 | return TypeIndex::Nothing; |
62 | } |
63 | |
64 | TypeIndex typeIdx(const DataTypePtr & data_type) |
65 | { |
66 | if (!data_type) |
67 | return TypeIndex::Nothing; |
68 | |
69 | WhichDataType which(*data_type); |
70 | switch (which.idx) |
71 | { |
72 | case TypeIndex::Int8: |
73 | case TypeIndex::UInt8: |
74 | case TypeIndex::Enum8: |
75 | case TypeIndex::Int16: |
76 | case TypeIndex::UInt16: |
77 | case TypeIndex::Enum16: |
78 | case TypeIndex::Date: |
79 | case TypeIndex::Int32: |
80 | case TypeIndex::UInt32: |
81 | case TypeIndex::DateTime: |
82 | case TypeIndex::Decimal32: |
83 | case TypeIndex::Int64: |
84 | case TypeIndex::UInt64: |
85 | case TypeIndex::Decimal64: |
86 | return which.idx; |
87 | default: |
88 | break; |
89 | } |
90 | |
91 | return TypeIndex::Nothing; |
92 | } |
93 | |
94 | void transpose64x8(UInt64 * src_dst) |
95 | { |
96 | auto * src8 = reinterpret_cast<const UInt8 *>(src_dst); |
97 | UInt64 dst[8] = {}; |
98 | |
99 | for (UInt32 i = 0; i < 64; ++i) |
100 | { |
101 | UInt64 value = src8[i]; |
102 | dst[0] |= (value & 0x1) << i; |
103 | dst[1] |= ((value >> 1) & 0x1) << i; |
104 | dst[2] |= ((value >> 2) & 0x1) << i; |
105 | dst[3] |= ((value >> 3) & 0x1) << i; |
106 | dst[4] |= ((value >> 4) & 0x1) << i; |
107 | dst[5] |= ((value >> 5) & 0x1) << i; |
108 | dst[6] |= ((value >> 6) & 0x1) << i; |
109 | dst[7] |= ((value >> 7) & 0x1) << i; |
110 | } |
111 | |
112 | memcpy(src_dst, dst, 8 * sizeof(UInt64)); |
113 | } |
114 | |
115 | void reverseTranspose64x8(UInt64 * src_dst) |
116 | { |
117 | UInt8 dst8[64]; |
118 | |
119 | for (UInt32 i = 0; i < 64; ++i) |
120 | { |
121 | dst8[i] = ((src_dst[0] >> i) & 0x1) |
122 | | (((src_dst[1] >> i) & 0x1) << 1) |
123 | | (((src_dst[2] >> i) & 0x1) << 2) |
124 | | (((src_dst[3] >> i) & 0x1) << 3) |
125 | | (((src_dst[4] >> i) & 0x1) << 4) |
126 | | (((src_dst[5] >> i) & 0x1) << 5) |
127 | | (((src_dst[6] >> i) & 0x1) << 6) |
128 | | (((src_dst[7] >> i) & 0x1) << 7); |
129 | } |
130 | |
131 | memcpy(src_dst, dst8, 8 * sizeof(UInt64)); |
132 | } |
133 | |
134 | template <typename T> |
135 | void transposeBytes(T value, UInt64 * matrix, UInt32 col) |
136 | { |
137 | UInt8 * matrix8 = reinterpret_cast<UInt8 *>(matrix); |
138 | const UInt8 * value8 = reinterpret_cast<const UInt8 *>(&value); |
139 | |
140 | if constexpr (sizeof(T) > 4) |
141 | { |
142 | matrix8[64 * 7 + col] = value8[7]; |
143 | matrix8[64 * 6 + col] = value8[6]; |
144 | matrix8[64 * 5 + col] = value8[5]; |
145 | matrix8[64 * 4 + col] = value8[4]; |
146 | } |
147 | |
148 | if constexpr (sizeof(T) > 2) |
149 | { |
150 | matrix8[64 * 3 + col] = value8[3]; |
151 | matrix8[64 * 2 + col] = value8[2]; |
152 | } |
153 | |
154 | if constexpr (sizeof(T) > 1) |
155 | matrix8[64 * 1 + col] = value8[1]; |
156 | |
157 | matrix8[64 * 0 + col] = value8[0]; |
158 | } |
159 | |
160 | template <typename T> |
161 | void reverseTransposeBytes(const UInt64 * matrix, UInt32 col, T & value) |
162 | { |
163 | auto * matrix8 = reinterpret_cast<const UInt8 *>(matrix); |
164 | |
165 | if constexpr (sizeof(T) > 4) |
166 | { |
167 | value |= UInt64(matrix8[64 * 7 + col]) << (8 * 7); |
168 | value |= UInt64(matrix8[64 * 6 + col]) << (8 * 6); |
169 | value |= UInt64(matrix8[64 * 5 + col]) << (8 * 5); |
170 | value |= UInt64(matrix8[64 * 4 + col]) << (8 * 4); |
171 | } |
172 | |
173 | if constexpr (sizeof(T) > 2) |
174 | { |
175 | value |= UInt32(matrix8[64 * 3 + col]) << (8 * 3); |
176 | value |= UInt32(matrix8[64 * 2 + col]) << (8 * 2); |
177 | } |
178 | |
179 | if constexpr (sizeof(T) > 1) |
180 | value |= UInt32(matrix8[64 * 1 + col]) << (8 * 1); |
181 | |
182 | value |= UInt32(matrix8[col]); |
183 | } |
184 | |
185 | |
186 | template <typename T> |
187 | void load(const char * src, T * buf, UInt32 tail = 64) |
188 | { |
189 | memcpy(buf, src, tail * sizeof(T)); |
190 | } |
191 | |
192 | template <typename T> |
193 | void store(const T * buf, char * dst, UInt32 tail = 64) |
194 | { |
195 | memcpy(dst, buf, tail * sizeof(T)); |
196 | } |
197 | |
198 | template <typename T> |
199 | void clear(T * buf) |
200 | { |
201 | for (UInt32 i = 0; i < 64; ++i) |
202 | buf[i] = 0; |
203 | } |
204 | |
205 | |
206 | /// UIntX[64] -> UInt64[N] transposed matrix, N <= X |
207 | template <typename T, bool full = false> |
208 | void transpose(const T * src, char * dst, UInt32 num_bits, UInt32 tail = 64) |
209 | { |
210 | UInt32 full_bytes = num_bits / 8; |
211 | UInt32 part_bits = num_bits % 8; |
212 | |
213 | UInt64 matrix[64] = {}; |
214 | for (UInt32 col = 0; col < tail; ++col) |
215 | transposeBytes(src[col], matrix, col); |
216 | |
217 | if constexpr (full) |
218 | { |
219 | UInt64 * matrix_line = matrix; |
220 | for (UInt32 byte = 0; byte < full_bytes; ++byte, matrix_line += 8) |
221 | transpose64x8(matrix_line); |
222 | } |
223 | |
224 | UInt32 full_size = sizeof(UInt64) * (num_bits - part_bits); |
225 | memcpy(dst, matrix, full_size); |
226 | dst += full_size; |
227 | |
228 | /// transpose only partially filled last byte |
229 | if (part_bits) |
230 | { |
231 | UInt64 * matrix_line = &matrix[full_bytes * 8]; |
232 | transpose64x8(matrix_line); |
233 | memcpy(dst, matrix_line, part_bits * sizeof(UInt64)); |
234 | } |
235 | } |
236 | |
237 | /// UInt64[N] transposed matrix -> UIntX[64] |
238 | template <typename T, bool full = false> |
239 | void reverseTranspose(const char * src, T * buf, UInt32 num_bits, UInt32 tail = 64) |
240 | { |
241 | UInt64 matrix[64] = {}; |
242 | memcpy(matrix, src, num_bits * sizeof(UInt64)); |
243 | |
244 | UInt32 full_bytes = num_bits / 8; |
245 | UInt32 part_bits = num_bits % 8; |
246 | |
247 | if constexpr (full) |
248 | { |
249 | UInt64 * matrix_line = matrix; |
250 | for (UInt32 byte = 0; byte < full_bytes; ++byte, matrix_line += 8) |
251 | reverseTranspose64x8(matrix_line); |
252 | } |
253 | |
254 | if (part_bits) |
255 | { |
256 | UInt64 * matrix_line = &matrix[full_bytes * 8]; |
257 | reverseTranspose64x8(matrix_line); |
258 | } |
259 | |
260 | clear(buf); |
261 | for (UInt32 col = 0; col < tail; ++col) |
262 | reverseTransposeBytes(matrix, col, buf[col]); |
263 | } |
264 | |
265 | template <typename T, typename MinMaxT = std::conditional_t<is_signed_v<T>, Int64, UInt64>> |
266 | void restoreUpperBits(T * buf, T upper_min, T upper_max [[maybe_unused]], T sign_bit [[maybe_unused]], UInt32 tail = 64) |
267 | { |
268 | if constexpr (is_signed_v<T>) |
269 | { |
270 | /// Restore some data as negatives and others as positives |
271 | if (sign_bit) |
272 | { |
273 | for (UInt32 col = 0; col < tail; ++col) |
274 | { |
275 | T & value = buf[col]; |
276 | |
277 | if (value & sign_bit) |
278 | value |= upper_min; |
279 | else |
280 | value |= upper_max; |
281 | } |
282 | |
283 | return; |
284 | } |
285 | } |
286 | |
287 | for (UInt32 col = 0; col < tail; ++col) |
288 | buf[col] |= upper_min; |
289 | } |
290 | |
291 | |
292 | UInt32 getValuableBitsNumber(UInt64 min, UInt64 max) |
293 | { |
294 | UInt64 diff_bits = min ^ max; |
295 | if (diff_bits) |
296 | return 64 - __builtin_clzll(diff_bits); |
297 | return 0; |
298 | } |
299 | |
300 | UInt32 getValuableBitsNumber(Int64 min, Int64 max) |
301 | { |
302 | if (min < 0 && max >= 0) |
303 | { |
304 | if (min + max >= 0) |
305 | return getValuableBitsNumber(0ull, UInt64(max)) + 1; |
306 | else |
307 | return getValuableBitsNumber(0ull, UInt64(~min)) + 1; |
308 | } |
309 | else |
310 | return getValuableBitsNumber(UInt64(min), UInt64(max)); |
311 | } |
312 | |
313 | |
314 | template <typename T> |
315 | void findMinMax(const char * src, UInt32 src_size, T & min, T & max) |
316 | { |
317 | min = unalignedLoad<T>(src); |
318 | max = unalignedLoad<T>(src); |
319 | |
320 | const char * end = src + src_size; |
321 | for (; src < end; src += sizeof(T)) |
322 | { |
323 | auto current = unalignedLoad<T>(src); |
324 | if (current < min) |
325 | min = current; |
326 | if (current > max) |
327 | max = current; |
328 | } |
329 | } |
330 | |
331 | |
332 | using Variant = CompressionCodecT64::Variant; |
333 | |
334 | template <typename T, bool full> |
335 | UInt32 compressData(const char * src, UInt32 bytes_size, char * dst) |
336 | { |
337 | using MinMaxType = std::conditional_t<is_signed_v<T>, Int64, UInt64>; |
338 | |
339 | static constexpr const UInt32 matrix_size = 64; |
340 | static constexpr const UInt32 = 2 * sizeof(UInt64); |
341 | |
342 | if (bytes_size % sizeof(T)) |
343 | throw Exception("Cannot compress, data size " + toString(bytes_size) + " is not multiplier of " + toString(sizeof(T)), |
344 | ErrorCodes::CANNOT_COMPRESS); |
345 | |
346 | UInt32 src_size = bytes_size / sizeof(T); |
347 | UInt32 num_full = src_size / matrix_size; |
348 | UInt32 tail = src_size % matrix_size; |
349 | |
350 | T min, max; |
351 | findMinMax<T>(src, bytes_size, min, max); |
352 | MinMaxType min64 = min; |
353 | MinMaxType max64 = max; |
354 | |
355 | /// Write header |
356 | { |
357 | memcpy(dst, &min64, sizeof(MinMaxType)); |
358 | memcpy(dst + 8, &max64, sizeof(MinMaxType)); |
359 | dst += header_size; |
360 | } |
361 | |
362 | UInt32 num_bits = getValuableBitsNumber(min64, max64); |
363 | if (!num_bits) |
364 | return header_size; |
365 | |
366 | T buf[matrix_size]; |
367 | UInt32 src_shift = sizeof(T) * matrix_size; |
368 | UInt32 dst_shift = sizeof(UInt64) * num_bits; |
369 | for (UInt32 i = 0; i < num_full; ++i) |
370 | { |
371 | load<T>(src, buf, matrix_size); |
372 | transpose<T, full>(buf, dst, num_bits); |
373 | src += src_shift; |
374 | dst += dst_shift; |
375 | } |
376 | |
377 | UInt32 dst_bytes = num_full * dst_shift; |
378 | |
379 | if (tail) |
380 | { |
381 | load<T>(src, buf, tail); |
382 | transpose<T, full>(buf, dst, num_bits, tail); |
383 | dst_bytes += dst_shift; |
384 | } |
385 | |
386 | return header_size + dst_bytes; |
387 | } |
388 | |
389 | template <typename T, bool full> |
390 | void decompressData(const char * src, UInt32 bytes_size, char * dst, UInt32 uncompressed_size) |
391 | { |
392 | using MinMaxType = std::conditional_t<is_signed_v<T>, Int64, UInt64>; |
393 | |
394 | static constexpr const UInt32 matrix_size = 64; |
395 | static constexpr const UInt32 = 2 * sizeof(UInt64); |
396 | |
397 | if (bytes_size < header_size) |
398 | throw Exception("Cannot decompress, data size " + toString(bytes_size) + " is less then T64 header" , |
399 | ErrorCodes::CANNOT_DECOMPRESS); |
400 | |
401 | if (uncompressed_size % sizeof(T)) |
402 | throw Exception("Cannot decompress, unexpected uncompressed size " + toString(uncompressed_size), |
403 | ErrorCodes::CANNOT_DECOMPRESS); |
404 | |
405 | UInt64 num_elements = uncompressed_size / sizeof(T); |
406 | MinMaxType min; |
407 | MinMaxType max; |
408 | |
409 | /// Read header |
410 | { |
411 | memcpy(&min, src, sizeof(MinMaxType)); |
412 | memcpy(&max, src + 8, sizeof(MinMaxType)); |
413 | src += header_size; |
414 | bytes_size -= header_size; |
415 | } |
416 | |
417 | UInt32 num_bits = getValuableBitsNumber(min, max); |
418 | if (!num_bits) |
419 | { |
420 | T min_value = min; |
421 | for (UInt32 i = 0; i < num_elements; ++i, dst += sizeof(T)) |
422 | unalignedStore<T>(dst, min_value); |
423 | return; |
424 | } |
425 | |
426 | UInt32 src_shift = sizeof(UInt64) * num_bits; |
427 | UInt32 dst_shift = sizeof(T) * matrix_size; |
428 | |
429 | if (!bytes_size || bytes_size % src_shift) |
430 | throw Exception("Cannot decompress, data size " + toString(bytes_size) + " is not multiplier of " + toString(src_shift), |
431 | ErrorCodes::CANNOT_DECOMPRESS); |
432 | |
433 | UInt32 num_full = bytes_size / src_shift; |
434 | UInt32 tail = num_elements % matrix_size; |
435 | if (tail) |
436 | --num_full; |
437 | |
438 | T upper_min = 0; |
439 | T upper_max [[maybe_unused]] = 0; |
440 | T sign_bit [[maybe_unused]] = 0; |
441 | if (num_bits < 64) |
442 | upper_min = UInt64(min) >> num_bits << num_bits; |
443 | |
444 | if constexpr (is_signed_v<T>) |
445 | { |
446 | if (min < 0 && max >= 0 && num_bits < 64) |
447 | { |
448 | sign_bit = 1ull << (num_bits - 1); |
449 | upper_max = UInt64(max) >> num_bits << num_bits; |
450 | } |
451 | } |
452 | |
453 | T buf[matrix_size]; |
454 | for (UInt32 i = 0; i < num_full; ++i) |
455 | { |
456 | reverseTranspose<T, full>(src, buf, num_bits); |
457 | restoreUpperBits(buf, upper_min, upper_max, sign_bit); |
458 | store<T>(buf, dst, matrix_size); |
459 | src += src_shift; |
460 | dst += dst_shift; |
461 | } |
462 | |
463 | if (tail) |
464 | { |
465 | reverseTranspose<T, full>(src, buf, num_bits, tail); |
466 | restoreUpperBits(buf, upper_min, upper_max, sign_bit, tail); |
467 | store<T>(buf, dst, tail); |
468 | } |
469 | } |
470 | |
471 | template <typename T> |
472 | UInt32 compressData(const char * src, UInt32 src_size, char * dst, Variant variant) |
473 | { |
474 | if (variant == Variant::Bit) |
475 | return compressData<T, true>(src, src_size, dst); |
476 | return compressData<T, false>(src, src_size, dst); |
477 | } |
478 | |
479 | template <typename T> |
480 | void decompressData(const char * src, UInt32 src_size, char * dst, UInt32 uncompressed_size, Variant variant) |
481 | { |
482 | if (variant == Variant::Bit) |
483 | decompressData<T, true>(src, src_size, dst, uncompressed_size); |
484 | else |
485 | decompressData<T, false>(src, src_size, dst, uncompressed_size); |
486 | } |
487 | |
488 | } |
489 | |
490 | |
491 | UInt32 CompressionCodecT64::doCompressData(const char * src, UInt32 src_size, char * dst) const |
492 | { |
493 | UInt8 cookie = static_cast<UInt8>(type_idx) | (static_cast<UInt8>(variant) << 7); |
494 | memcpy(dst, &cookie, 1); |
495 | dst += 1; |
496 | |
497 | switch (baseType(type_idx)) |
498 | { |
499 | case TypeIndex::Int8: |
500 | return 1 + compressData<Int8>(src, src_size, dst, variant); |
501 | case TypeIndex::Int16: |
502 | return 1 + compressData<Int16>(src, src_size, dst, variant); |
503 | case TypeIndex::Int32: |
504 | return 1 + compressData<Int32>(src, src_size, dst, variant); |
505 | case TypeIndex::Int64: |
506 | return 1 + compressData<Int64>(src, src_size, dst, variant); |
507 | case TypeIndex::UInt8: |
508 | return 1 + compressData<UInt8>(src, src_size, dst, variant); |
509 | case TypeIndex::UInt16: |
510 | return 1 + compressData<UInt16>(src, src_size, dst, variant); |
511 | case TypeIndex::UInt32: |
512 | return 1 + compressData<UInt32>(src, src_size, dst, variant); |
513 | case TypeIndex::UInt64: |
514 | return 1 + compressData<UInt64>(src, src_size, dst, variant); |
515 | default: |
516 | break; |
517 | } |
518 | |
519 | throw Exception("Connot compress with T64" , ErrorCodes::CANNOT_COMPRESS); |
520 | } |
521 | |
522 | void CompressionCodecT64::doDecompressData(const char * src, UInt32 src_size, char * dst, UInt32 uncompressed_size) const |
523 | { |
524 | if (!src_size) |
525 | throw Exception("Connot decompress with T64" , ErrorCodes::CANNOT_DECOMPRESS); |
526 | |
527 | UInt8 cookie = unalignedLoad<UInt8>(src); |
528 | src += 1; |
529 | src_size -= 1; |
530 | |
531 | auto saved_variant = static_cast<Variant>(cookie >> 7); |
532 | auto saved_type_id = static_cast<TypeIndex>(cookie & 0x7F); |
533 | |
534 | switch (baseType(saved_type_id)) |
535 | { |
536 | case TypeIndex::Int8: |
537 | return decompressData<Int8>(src, src_size, dst, uncompressed_size, saved_variant); |
538 | case TypeIndex::Int16: |
539 | return decompressData<Int16>(src, src_size, dst, uncompressed_size, saved_variant); |
540 | case TypeIndex::Int32: |
541 | return decompressData<Int32>(src, src_size, dst, uncompressed_size, saved_variant); |
542 | case TypeIndex::Int64: |
543 | return decompressData<Int64>(src, src_size, dst, uncompressed_size, saved_variant); |
544 | case TypeIndex::UInt8: |
545 | return decompressData<UInt8>(src, src_size, dst, uncompressed_size, saved_variant); |
546 | case TypeIndex::UInt16: |
547 | return decompressData<UInt16>(src, src_size, dst, uncompressed_size, saved_variant); |
548 | case TypeIndex::UInt32: |
549 | return decompressData<UInt32>(src, src_size, dst, uncompressed_size, saved_variant); |
550 | case TypeIndex::UInt64: |
551 | return decompressData<UInt64>(src, src_size, dst, uncompressed_size, saved_variant); |
552 | default: |
553 | break; |
554 | } |
555 | |
556 | throw Exception("Connot decompress with T64" , ErrorCodes::CANNOT_DECOMPRESS); |
557 | } |
558 | |
559 | void CompressionCodecT64::useInfoAboutType(DataTypePtr data_type) |
560 | { |
561 | if (data_type) |
562 | { |
563 | type_idx = typeIdx(data_type); |
564 | if (type_idx == TypeIndex::Nothing) |
565 | throw Exception("T64 codec is not supported for specified type" , ErrorCodes::ILLEGAL_SYNTAX_FOR_CODEC_TYPE); |
566 | } |
567 | } |
568 | |
569 | UInt8 CompressionCodecT64::getMethodByte() const |
570 | { |
571 | return codecId(); |
572 | } |
573 | |
574 | void registerCodecT64(CompressionCodecFactory & factory) |
575 | { |
576 | auto reg_func = [&](const ASTPtr & arguments, DataTypePtr type) -> CompressionCodecPtr |
577 | { |
578 | Variant variant = Variant::Byte; |
579 | |
580 | if (arguments && !arguments->children.empty()) |
581 | { |
582 | if (arguments->children.size() > 1) |
583 | throw Exception("T64 support zero or one parameter, given " + std::to_string(arguments->children.size()), |
584 | ErrorCodes::ILLEGAL_SYNTAX_FOR_CODEC_TYPE); |
585 | |
586 | const auto children = arguments->children; |
587 | const auto * literal = children[0]->as<ASTLiteral>(); |
588 | if (!literal) |
589 | throw Exception("Wrong modification for T64. Expected: 'bit', 'byte')" , |
590 | ErrorCodes::ILLEGAL_CODEC_PARAMETER); |
591 | String name = literal->value.safeGet<String>(); |
592 | |
593 | if (name == "byte" ) |
594 | variant = Variant::Byte; |
595 | else if (name == "bit" ) |
596 | variant = Variant::Bit; |
597 | else |
598 | throw Exception("Wrong modification for T64: " + name, ErrorCodes::ILLEGAL_CODEC_PARAMETER); |
599 | } |
600 | |
601 | auto type_idx = typeIdx(type); |
602 | if (type && type_idx == TypeIndex::Nothing) |
603 | throw Exception("T64 codec is not supported for specified type" , ErrorCodes::ILLEGAL_SYNTAX_FOR_CODEC_TYPE); |
604 | return std::make_shared<CompressionCodecT64>(type_idx, variant); |
605 | }; |
606 | |
607 | factory.registerCompressionCodecWithType("T64" , codecId(), reg_func); |
608 | } |
609 | } |
610 | |