Coverage Report

Created: 2026-07-17 22:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/block_compression.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "util/block_compression.h"
19
20
#include <bzlib.h>
21
#include <gen_cpp/parquet_types.h>
22
#include <gen_cpp/segment_v2.pb.h>
23
#include <glog/logging.h>
24
25
#include <exception>
26
// Only used on x86 or x86_64
27
#if defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || \
28
        defined(__i386) || defined(_M_IX86)
29
#include <libdeflate.h>
30
#endif
31
#include <brotli/decode.h>
32
#include <glog/log_severity.h>
33
#include <glog/logging.h>
34
#include <lz4/lz4.h>
35
#include <lz4/lz4frame.h>
36
#include <lz4/lz4hc.h>
37
#include <snappy/snappy-sinksource.h>
38
#include <snappy/snappy.h>
39
#include <zconf.h>
40
#include <zlib.h>
41
#include <zstd.h>
42
#include <zstd_errors.h>
43
44
#include <algorithm>
45
#include <cstdint>
46
#include <limits>
47
#include <mutex>
48
#include <orc/Exceptions.hh>
49
#include <ostream>
50
51
#include "absl/strings/substitute.h"
52
#include "common/config.h"
53
#include "common/factory_creator.h"
54
#include "exec/common/endian.h"
55
#include "runtime/thread_context.h"
56
#include "util/decompressor.h"
57
#include "util/defer_op.h"
58
#include "util/faststring.h"
59
60
namespace orc {
61
/**
62
 * Decompress the bytes in to the output buffer.
63
 * @param inputAddress the start of the input
64
 * @param inputLimit one past the last byte of the input
65
 * @param outputAddress the start of the output buffer
66
 * @param outputLimit one past the last byte of the output buffer
67
 * @result the number of bytes decompressed
68
 */
69
uint64_t lzoDecompress(const char* inputAddress, const char* inputLimit, char* outputAddress,
70
                       char* outputLimit);
71
} // namespace orc
72
73
namespace doris {
74
75
// exception safe
76
Status BlockCompressionCodec::compress(const std::vector<Slice>& inputs, size_t uncompressed_size,
77
441
                                       faststring* output) {
78
441
    faststring buf;
79
    // we compute total size to avoid more memory copy
80
441
    buf.reserve(uncompressed_size);
81
506
    for (auto& input : inputs) {
82
506
        buf.append(input.data, input.size);
83
506
    }
84
441
    return compress(buf, output);
85
441
}
86
87
21.6k
bool BlockCompressionCodec::exceed_max_compress_len(size_t uncompressed_size) {
88
21.6k
    return uncompressed_size > std::numeric_limits<int32_t>::max();
89
21.6k
}
90
91
class Lz4BlockCompression : public BlockCompressionCodec {
92
private:
93
    class Context {
94
        ENABLE_FACTORY_CREATOR(Context);
95
96
    public:
97
1
        Context() : ctx(nullptr) {
98
1
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
99
1
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
100
1
            buffer = std::make_unique<faststring>();
101
1
        }
102
        LZ4_stream_t* ctx;
103
        std::unique_ptr<faststring> buffer;
104
1
        ~Context() {
105
1
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
106
1
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
107
1
            if (ctx) {
108
1
                LZ4_freeStream(ctx);
109
1
            }
110
1
            buffer.reset();
111
1
        }
112
    };
113
114
public:
115
711
    static Lz4BlockCompression* instance() {
116
711
        static Lz4BlockCompression s_instance;
117
711
        return &s_instance;
118
711
    }
119
1
    ~Lz4BlockCompression() override {
120
1
        SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
121
1
                ExecEnv::GetInstance()->block_compression_mem_tracker());
122
1
        _ctx_pool.clear();
123
1
    }
124
125
492
    Status compress(const Slice& input, faststring* output) override {
126
492
        if (input.size > LZ4_MAX_INPUT_SIZE) {
127
0
            return Status::InvalidArgument(
128
0
                    "LZ4 not support those case(input.size>LZ4_MAX_INPUT_SIZE), maybe you should "
129
0
                    "change "
130
0
                    "fragment_transmission_compression_codec to snappy, input.size={}, "
131
0
                    "LZ4_MAX_INPUT_SIZE={}",
132
0
                    input.size, LZ4_MAX_INPUT_SIZE);
133
0
        }
134
135
492
        std::unique_ptr<Context> context;
136
492
        RETURN_IF_ERROR(_acquire_compression_ctx(context));
137
492
        bool compress_failed = false;
138
492
        Defer defer {[&] {
139
492
            if (!compress_failed) {
140
492
                _release_compression_ctx(std::move(context));
141
492
            }
142
492
        }};
143
144
492
        try {
145
492
            Slice compressed_buf;
146
492
            size_t max_len = max_compressed_len(input.size);
147
492
            if (max_len > MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
148
                // use output directly
149
0
                output->resize(max_len);
150
0
                compressed_buf.data = reinterpret_cast<char*>(output->data());
151
0
                compressed_buf.size = max_len;
152
492
            } else {
153
                // reuse context buffer if max_len <= MAX_COMPRESSION_BUFFER_FOR_REUSE
154
492
                {
155
                    // context->buffer is resuable between queries, should accouting to
156
                    // global tracker.
157
492
                    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
158
492
                            ExecEnv::GetInstance()->block_compression_mem_tracker());
159
492
                    context->buffer->resize(max_len);
160
492
                }
161
492
                compressed_buf.data = reinterpret_cast<char*>(context->buffer->data());
162
492
                compressed_buf.size = max_len;
163
492
            }
164
165
            // input.size is aready checked before;
166
            // compressed_buf.size is got from max_compressed_len, which is
167
            // the return value of LZ4_compressBound, so it is safe to cast to int
168
492
            size_t compressed_len = LZ4_compress_fast_continue(
169
492
                    context->ctx, input.data, compressed_buf.data, static_cast<int>(input.size),
170
492
                    static_cast<int>(compressed_buf.size), ACCELARATION);
171
492
            if (compressed_len == 0) {
172
0
                compress_failed = true;
173
0
                return Status::InvalidArgument("Output buffer's capacity is not enough, size={}",
174
0
                                               compressed_buf.size);
175
0
            }
176
492
            output->resize(compressed_len);
177
492
            if (max_len <= MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
178
492
                output->assign_copy(reinterpret_cast<uint8_t*>(compressed_buf.data),
179
492
                                    compressed_len);
180
492
            }
181
492
        } catch (...) {
182
            // Do not set compress_failed to release context
183
0
            DCHECK(!compress_failed);
184
0
            return Status::InternalError("Fail to do LZ4Block compress due to exception");
185
0
        }
186
492
        return Status::OK();
187
492
    }
188
189
145
    Status decompress(const Slice& input, Slice* output) override {
190
145
        auto decompressed_len = LZ4_decompress_safe(
191
145
                input.data, output->data, cast_set<int>(input.size), cast_set<int>(output->size));
192
145
        if (decompressed_len < 0) {
193
5
            return Status::InternalError("fail to do LZ4 decompress, error={}", decompressed_len);
194
5
        }
195
140
        output->size = decompressed_len;
196
140
        return Status::OK();
197
145
    }
198
199
492
    size_t max_compressed_len(size_t len) override { return LZ4_compressBound(cast_set<int>(len)); }
200
201
private:
202
    // reuse LZ4 compress stream
203
492
    Status _acquire_compression_ctx(std::unique_ptr<Context>& out) {
204
492
        std::lock_guard<std::mutex> l(_ctx_mutex);
205
492
        if (_ctx_pool.empty()) {
206
1
            std::unique_ptr<Context> localCtx = Context::create_unique();
207
1
            if (localCtx.get() == nullptr) {
208
0
                return Status::InvalidArgument("new LZ4 context error");
209
0
            }
210
1
            localCtx->ctx = LZ4_createStream();
211
1
            if (localCtx->ctx == nullptr) {
212
0
                return Status::InvalidArgument("LZ4_createStream error");
213
0
            }
214
1
            out = std::move(localCtx);
215
1
            return Status::OK();
216
1
        }
217
491
        out = std::move(_ctx_pool.back());
218
491
        _ctx_pool.pop_back();
219
491
        return Status::OK();
220
492
    }
221
492
    void _release_compression_ctx(std::unique_ptr<Context> context) {
222
492
        DCHECK(context);
223
492
        LZ4_resetStream(context->ctx);
224
492
        std::lock_guard<std::mutex> l(_ctx_mutex);
225
492
        _ctx_pool.push_back(std::move(context));
226
492
    }
227
228
private:
229
    mutable std::mutex _ctx_mutex;
230
    mutable std::vector<std::unique_ptr<Context>> _ctx_pool;
231
    static const int32_t ACCELARATION = 1;
232
};
233
234
class HadoopLz4BlockCompression : public Lz4BlockCompression {
235
public:
236
0
    HadoopLz4BlockCompression() {
237
0
        Status st = Decompressor::create_decompressor(CompressType::LZ4BLOCK, &_decompressor);
238
0
        if (!st.ok()) {
239
0
            throw Exception(Status::FatalError(
240
0
                    "HadoopLz4BlockCompression construction failed. status = {}", st));
241
0
        }
242
0
    }
243
244
0
    ~HadoopLz4BlockCompression() override = default;
245
246
0
    static HadoopLz4BlockCompression* instance() {
247
0
        static HadoopLz4BlockCompression s_instance;
248
0
        return &s_instance;
249
0
    }
250
251
    // hadoop use block compression for lz4
252
    // https://github.com/apache/hadoop/blob/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/native/src/codec/Lz4Codec.cc
253
0
    Status compress(const Slice& input, faststring* output) override {
254
        // be same with hadop https://github.com/apache/hadoop/blob/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/compress/Lz4Codec.java
255
0
        size_t lz4_block_size = config::lz4_compression_block_size;
256
0
        size_t overhead = lz4_block_size / 255 + 16;
257
0
        size_t max_input_size = lz4_block_size - overhead;
258
259
0
        size_t data_len = input.size;
260
0
        char* data = input.data;
261
0
        std::vector<OwnedSlice> buffers;
262
0
        size_t out_len = 0;
263
264
0
        while (data_len > 0) {
265
0
            size_t input_size = std::min(data_len, max_input_size);
266
0
            Slice input_slice(data, input_size);
267
0
            faststring output_data;
268
0
            RETURN_IF_ERROR(Lz4BlockCompression::compress(input_slice, &output_data));
269
0
            out_len += output_data.size();
270
0
            buffers.push_back(output_data.build());
271
0
            data += input_size;
272
0
            data_len -= input_size;
273
0
        }
274
275
        // hadoop block compression: umcompressed_length | compressed_length1 | compressed_data1 | compressed_length2 | compressed_data2 | ...
276
0
        size_t total_output_len = 4 + 4 * buffers.size() + out_len;
277
0
        output->resize(total_output_len);
278
0
        char* output_buffer = (char*)output->data();
279
0
        BigEndian::Store32(output_buffer, cast_set<uint32_t>(input.get_size()));
280
0
        output_buffer += 4;
281
0
        for (const auto& buffer : buffers) {
282
0
            auto slice = buffer.slice();
283
0
            BigEndian::Store32(output_buffer, cast_set<uint32_t>(slice.get_size()));
284
0
            output_buffer += 4;
285
0
            memcpy(output_buffer, slice.get_data(), slice.get_size());
286
0
            output_buffer += slice.get_size();
287
0
        }
288
289
0
        DCHECK_EQ(output_buffer - (char*)output->data(), total_output_len);
290
291
0
        return Status::OK();
292
0
    }
293
294
0
    Status decompress(const Slice& input, Slice* output) override {
295
0
        size_t input_bytes_read = 0;
296
0
        size_t decompressed_len = 0;
297
0
        size_t more_input_bytes = 0;
298
0
        size_t more_output_bytes = 0;
299
0
        bool stream_end = false;
300
0
        auto st = _decompressor->decompress((uint8_t*)input.data, cast_set<uint32_t>(input.size),
301
0
                                            &input_bytes_read, (uint8_t*)output->data,
302
0
                                            cast_set<uint32_t>(output->size), &decompressed_len,
303
0
                                            &stream_end, &more_input_bytes, &more_output_bytes);
304
        //try decompress use hadoopLz4 ,if failed fall back lz4.
305
0
        return (st != Status::OK() || stream_end != true)
306
0
                       ? Lz4BlockCompression::decompress(input, output)
307
0
                       : Status::OK();
308
0
    }
309
310
private:
311
    std::unique_ptr<Decompressor> _decompressor;
312
};
313
// Used for LZ4 frame format, decompress speed is two times faster than LZ4.
314
class Lz4fBlockCompression : public BlockCompressionCodec {
315
private:
316
    class CContext {
317
        ENABLE_FACTORY_CREATOR(CContext);
318
319
    public:
320
1
        CContext() : ctx(nullptr) {
321
1
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
322
1
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
323
1
            buffer = std::make_unique<faststring>();
324
1
        }
325
        LZ4F_compressionContext_t ctx;
326
        std::unique_ptr<faststring> buffer;
327
1
        ~CContext() {
328
1
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
329
1
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
330
1
            if (ctx) {
331
1
                LZ4F_freeCompressionContext(ctx);
332
1
            }
333
1
            buffer.reset();
334
1
        }
335
    };
336
    class DContext {
337
        ENABLE_FACTORY_CREATOR(DContext);
338
339
    public:
340
6
        DContext() : ctx(nullptr) {}
341
        LZ4F_decompressionContext_t ctx;
342
6
        ~DContext() {
343
6
            if (ctx) {
344
6
                LZ4F_freeDecompressionContext(ctx);
345
6
            }
346
6
        }
347
    };
348
349
public:
350
22.4k
    static Lz4fBlockCompression* instance() {
351
22.4k
        static Lz4fBlockCompression s_instance;
352
22.4k
        return &s_instance;
353
22.4k
    }
354
1
    ~Lz4fBlockCompression() {
355
1
        SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
356
1
                ExecEnv::GetInstance()->block_compression_mem_tracker());
357
1
        _ctx_c_pool.clear();
358
1
        _ctx_d_pool.clear();
359
1
    }
360
361
5
    Status compress(const Slice& input, faststring* output) override {
362
5
        std::vector<Slice> inputs {input};
363
5
        return compress(inputs, input.size, output);
364
5
    }
365
366
    Status compress(const std::vector<Slice>& inputs, size_t uncompressed_size,
367
21.0k
                    faststring* output) override {
368
21.0k
        return _compress(inputs, uncompressed_size, output);
369
21.0k
    }
370
371
7.77k
    Status decompress(const Slice& input, Slice* output) override {
372
7.77k
        return _decompress(input, output);
373
7.77k
    }
374
375
21.0k
    size_t max_compressed_len(size_t len) override {
376
21.0k
        return std::max(LZ4F_compressBound(len, &_s_preferences),
377
21.0k
                        LZ4F_compressFrameBound(len, &_s_preferences));
378
21.0k
    }
379
380
private:
381
    Status _compress(const std::vector<Slice>& inputs, size_t uncompressed_size,
382
21.0k
                     faststring* output) {
383
21.0k
        std::unique_ptr<CContext> context;
384
21.0k
        RETURN_IF_ERROR(_acquire_compression_ctx(context));
385
21.0k
        bool compress_failed = false;
386
21.0k
        Defer defer {[&] {
387
21.0k
            if (!compress_failed) {
388
21.0k
                _release_compression_ctx(std::move(context));
389
21.0k
            }
390
21.0k
        }};
391
392
21.0k
        try {
393
21.0k
            Slice compressed_buf;
394
21.0k
            size_t max_len = max_compressed_len(uncompressed_size);
395
21.0k
            if (max_len > MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
396
                // use output directly
397
0
                output->resize(max_len);
398
0
                compressed_buf.data = reinterpret_cast<char*>(output->data());
399
0
                compressed_buf.size = max_len;
400
21.0k
            } else {
401
21.0k
                {
402
21.0k
                    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
403
21.0k
                            ExecEnv::GetInstance()->block_compression_mem_tracker());
404
                    // reuse context buffer if max_len <= MAX_COMPRESSION_BUFFER_FOR_REUSE
405
21.0k
                    context->buffer->resize(max_len);
406
21.0k
                }
407
21.0k
                compressed_buf.data = reinterpret_cast<char*>(context->buffer->data());
408
21.0k
                compressed_buf.size = max_len;
409
21.0k
            }
410
411
21.0k
            auto wbytes = LZ4F_compressBegin(context->ctx, compressed_buf.data, compressed_buf.size,
412
21.0k
                                             &_s_preferences);
413
21.0k
            if (LZ4F_isError(wbytes)) {
414
0
                compress_failed = true;
415
0
                return Status::InvalidArgument("Fail to do LZ4F compress begin, res={}",
416
0
                                               LZ4F_getErrorName(wbytes));
417
0
            }
418
21.0k
            size_t offset = wbytes;
419
21.6k
            for (auto input : inputs) {
420
21.6k
                wbytes = LZ4F_compressUpdate(context->ctx, compressed_buf.data + offset,
421
21.6k
                                             compressed_buf.size - offset, input.data, input.size,
422
21.6k
                                             nullptr);
423
21.6k
                if (LZ4F_isError(wbytes)) {
424
0
                    compress_failed = true;
425
0
                    return Status::InvalidArgument("Fail to do LZ4F compress update, res={}",
426
0
                                                   LZ4F_getErrorName(wbytes));
427
0
                }
428
21.6k
                offset += wbytes;
429
21.6k
            }
430
21.0k
            wbytes = LZ4F_compressEnd(context->ctx, compressed_buf.data + offset,
431
21.0k
                                      compressed_buf.size - offset, nullptr);
432
21.0k
            if (LZ4F_isError(wbytes)) {
433
0
                compress_failed = true;
434
0
                return Status::InvalidArgument("Fail to do LZ4F compress end, res={}",
435
0
                                               LZ4F_getErrorName(wbytes));
436
0
            }
437
21.0k
            offset += wbytes;
438
21.0k
            output->resize(offset);
439
21.0k
            if (max_len <= MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
440
21.0k
                output->assign_copy(reinterpret_cast<uint8_t*>(compressed_buf.data), offset);
441
21.0k
            }
442
21.0k
        } catch (...) {
443
            // Do not set compress_failed to release context
444
0
            DCHECK(!compress_failed);
445
0
            return Status::InternalError("Fail to do LZ4F compress due to exception");
446
0
        }
447
448
21.0k
        return Status::OK();
449
21.0k
    }
450
451
7.77k
    Status _decompress(const Slice& input, Slice* output) {
452
7.77k
        bool decompress_failed = false;
453
7.77k
        std::unique_ptr<DContext> context;
454
7.77k
        RETURN_IF_ERROR(_acquire_decompression_ctx(context));
455
7.77k
        Defer defer {[&] {
456
7.77k
            if (!decompress_failed) {
457
7.76k
                _release_decompression_ctx(std::move(context));
458
7.76k
            }
459
7.77k
        }};
460
7.77k
        size_t input_size = input.size;
461
7.77k
        auto lres = LZ4F_decompress(context->ctx, output->data, &output->size, input.data,
462
7.77k
                                    &input_size, nullptr);
463
7.77k
        if (LZ4F_isError(lres)) {
464
0
            decompress_failed = true;
465
0
            return Status::InternalError("Fail to do LZ4F decompress, res={}",
466
0
                                         LZ4F_getErrorName(lres));
467
7.77k
        } else if (input_size != input.size) {
468
0
            decompress_failed = true;
469
0
            return Status::InvalidArgument(
470
0
                    absl::Substitute("Fail to do LZ4F decompress: trailing data left in "
471
0
                                     "compressed data, read=$0 vs given=$1",
472
0
                                     input_size, input.size));
473
7.77k
        } else if (lres != 0) {
474
5
            decompress_failed = true;
475
5
            return Status::InvalidArgument(
476
5
                    "Fail to do LZ4F decompress: expect more compressed data, expect={}", lres);
477
5
        }
478
7.76k
        return Status::OK();
479
7.77k
    }
480
481
private:
482
    // acquire a compression ctx from pool, release while finish compress,
483
    // delete if compression failed
484
21.0k
    Status _acquire_compression_ctx(std::unique_ptr<CContext>& out) {
485
21.0k
        std::lock_guard<std::mutex> l(_ctx_c_mutex);
486
21.0k
        if (_ctx_c_pool.empty()) {
487
1
            std::unique_ptr<CContext> localCtx = CContext::create_unique();
488
1
            if (localCtx.get() == nullptr) {
489
0
                return Status::InvalidArgument("failed to new LZ4F CContext");
490
0
            }
491
1
            auto res = LZ4F_createCompressionContext(&localCtx->ctx, LZ4F_VERSION);
492
1
            if (LZ4F_isError(res) != 0) {
493
0
                return Status::InvalidArgument(absl::Substitute(
494
0
                        "LZ4F_createCompressionContext error, res=$0", LZ4F_getErrorName(res)));
495
0
            }
496
1
            out = std::move(localCtx);
497
1
            return Status::OK();
498
1
        }
499
21.0k
        out = std::move(_ctx_c_pool.back());
500
21.0k
        _ctx_c_pool.pop_back();
501
21.0k
        return Status::OK();
502
21.0k
    }
503
21.0k
    void _release_compression_ctx(std::unique_ptr<CContext> context) {
504
21.0k
        DCHECK(context);
505
21.0k
        std::lock_guard<std::mutex> l(_ctx_c_mutex);
506
21.0k
        _ctx_c_pool.push_back(std::move(context));
507
21.0k
    }
508
509
7.77k
    Status _acquire_decompression_ctx(std::unique_ptr<DContext>& out) {
510
7.77k
        std::lock_guard<std::mutex> l(_ctx_d_mutex);
511
7.77k
        if (_ctx_d_pool.empty()) {
512
6
            std::unique_ptr<DContext> localCtx = DContext::create_unique();
513
6
            if (localCtx.get() == nullptr) {
514
0
                return Status::InvalidArgument("failed to new LZ4F DContext");
515
0
            }
516
6
            auto res = LZ4F_createDecompressionContext(&localCtx->ctx, LZ4F_VERSION);
517
6
            if (LZ4F_isError(res) != 0) {
518
0
                return Status::InvalidArgument(absl::Substitute(
519
0
                        "LZ4F_createDeompressionContext error, res=$0", LZ4F_getErrorName(res)));
520
0
            }
521
6
            out = std::move(localCtx);
522
6
            return Status::OK();
523
6
        }
524
7.76k
        out = std::move(_ctx_d_pool.back());
525
7.76k
        _ctx_d_pool.pop_back();
526
7.76k
        return Status::OK();
527
7.77k
    }
528
7.76k
    void _release_decompression_ctx(std::unique_ptr<DContext> context) {
529
7.76k
        DCHECK(context);
530
        // reset decompression context to avoid ERROR_maxBlockSize_invalid
531
7.76k
        LZ4F_resetDecompressionContext(context->ctx);
532
7.76k
        std::lock_guard<std::mutex> l(_ctx_d_mutex);
533
7.76k
        _ctx_d_pool.push_back(std::move(context));
534
7.76k
    }
535
536
private:
537
    static LZ4F_preferences_t _s_preferences;
538
539
    std::mutex _ctx_c_mutex;
540
    // LZ4F_compressionContext_t is a pointer so no copy here
541
    std::vector<std::unique_ptr<CContext>> _ctx_c_pool;
542
543
    std::mutex _ctx_d_mutex;
544
    std::vector<std::unique_ptr<DContext>> _ctx_d_pool;
545
};
546
547
LZ4F_preferences_t Lz4fBlockCompression::_s_preferences = {
548
        {LZ4F_max256KB, LZ4F_blockLinked, LZ4F_noContentChecksum, LZ4F_frame, 0ULL, 0U,
549
         LZ4F_noBlockChecksum},
550
        0,
551
        0u,
552
        0u,
553
        {0u, 0u, 0u}};
554
555
class Lz4HCBlockCompression : public BlockCompressionCodec {
556
private:
557
    class Context {
558
        ENABLE_FACTORY_CREATOR(Context);
559
560
    public:
561
1
        Context() : ctx(nullptr) {
562
1
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
563
1
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
564
1
            buffer = std::make_unique<faststring>();
565
1
        }
566
        LZ4_streamHC_t* ctx;
567
        std::unique_ptr<faststring> buffer;
568
1
        ~Context() {
569
1
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
570
1
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
571
1
            if (ctx) {
572
1
                LZ4_freeStreamHC(ctx);
573
1
            }
574
1
            buffer.reset();
575
1
        }
576
    };
577
578
public:
579
2
    static Lz4HCBlockCompression* instance() {
580
2
        static Lz4HCBlockCompression s_instance;
581
2
        return &s_instance;
582
2
    }
583
1
    ~Lz4HCBlockCompression() {
584
1
        SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
585
1
                ExecEnv::GetInstance()->block_compression_mem_tracker());
586
1
        _ctx_pool.clear();
587
1
    }
588
589
6
    Status compress(const Slice& input, faststring* output) override {
590
6
        std::unique_ptr<Context> context;
591
6
        RETURN_IF_ERROR(_acquire_compression_ctx(context));
592
6
        bool compress_failed = false;
593
6
        Defer defer {[&] {
594
6
            if (!compress_failed) {
595
6
                _release_compression_ctx(std::move(context));
596
6
            }
597
6
        }};
598
599
6
        try {
600
6
            Slice compressed_buf;
601
6
            size_t max_len = max_compressed_len(input.size);
602
6
            if (max_len > MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
603
                // use output directly
604
0
                output->resize(max_len);
605
0
                compressed_buf.data = reinterpret_cast<char*>(output->data());
606
0
                compressed_buf.size = max_len;
607
6
            } else {
608
6
                {
609
6
                    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
610
6
                            ExecEnv::GetInstance()->block_compression_mem_tracker());
611
                    // reuse context buffer if max_len <= MAX_COMPRESSION_BUFFER_FOR_REUSE
612
6
                    context->buffer->resize(max_len);
613
6
                }
614
6
                compressed_buf.data = reinterpret_cast<char*>(context->buffer->data());
615
6
                compressed_buf.size = max_len;
616
6
            }
617
618
6
            size_t compressed_len = LZ4_compress_HC_continue(
619
6
                    context->ctx, input.data, compressed_buf.data, cast_set<int>(input.size),
620
6
                    static_cast<int>(compressed_buf.size));
621
6
            if (compressed_len == 0) {
622
0
                compress_failed = true;
623
0
                return Status::InvalidArgument("Output buffer's capacity is not enough, size={}",
624
0
                                               compressed_buf.size);
625
0
            }
626
6
            output->resize(compressed_len);
627
6
            if (max_len <= MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
628
6
                output->assign_copy(reinterpret_cast<uint8_t*>(compressed_buf.data),
629
6
                                    compressed_len);
630
6
            }
631
6
        } catch (...) {
632
            // Do not set compress_failed to release context
633
0
            DCHECK(!compress_failed);
634
0
            return Status::InternalError("Fail to do LZ4HC compress due to exception");
635
0
        }
636
6
        return Status::OK();
637
6
    }
638
639
11
    Status decompress(const Slice& input, Slice* output) override {
640
11
        auto decompressed_len = LZ4_decompress_safe(
641
11
                input.data, output->data, cast_set<int>(input.size), cast_set<int>(output->size));
642
11
        if (decompressed_len < 0) {
643
5
            return Status::InvalidArgument(
644
5
                    "destination buffer is not large enough or the source stream is detected "
645
5
                    "malformed, fail to do LZ4 decompress, error={}",
646
5
                    decompressed_len);
647
5
        }
648
6
        output->size = decompressed_len;
649
6
        return Status::OK();
650
11
    }
651
652
6
    size_t max_compressed_len(size_t len) override { return LZ4_compressBound(cast_set<int>(len)); }
653
654
private:
655
6
    Status _acquire_compression_ctx(std::unique_ptr<Context>& out) {
656
6
        std::lock_guard<std::mutex> l(_ctx_mutex);
657
6
        if (_ctx_pool.empty()) {
658
1
            std::unique_ptr<Context> localCtx = Context::create_unique();
659
1
            if (localCtx.get() == nullptr) {
660
0
                return Status::InvalidArgument("new LZ4HC context error");
661
0
            }
662
1
            localCtx->ctx = LZ4_createStreamHC();
663
1
            if (localCtx->ctx == nullptr) {
664
0
                return Status::InvalidArgument("LZ4_createStreamHC error");
665
0
            }
666
1
            out = std::move(localCtx);
667
1
            return Status::OK();
668
1
        }
669
5
        out = std::move(_ctx_pool.back());
670
5
        _ctx_pool.pop_back();
671
5
        return Status::OK();
672
6
    }
673
6
    void _release_compression_ctx(std::unique_ptr<Context> context) {
674
6
        DCHECK(context);
675
6
        LZ4_resetStreamHC_fast(context->ctx, static_cast<int>(_compression_level));
676
6
        std::lock_guard<std::mutex> l(_ctx_mutex);
677
6
        _ctx_pool.push_back(std::move(context));
678
6
    }
679
680
private:
681
    int64_t _compression_level = config::LZ4_HC_compression_level;
682
    mutable std::mutex _ctx_mutex;
683
    mutable std::vector<std::unique_ptr<Context>> _ctx_pool;
684
};
685
686
class SnappySlicesSource : public snappy::Source {
687
public:
688
    SnappySlicesSource(const std::vector<Slice>& slices)
689
5
            : _available(0), _cur_slice(0), _slice_off(0) {
690
9
        for (auto& slice : slices) {
691
            // We filter empty slice here to avoid complicated process
692
9
            if (slice.size == 0) {
693
1
                continue;
694
1
            }
695
8
            _available += slice.size;
696
8
            _slices.push_back(slice);
697
8
        }
698
5
    }
699
5
    ~SnappySlicesSource() override {}
700
701
    // Return the number of bytes left to read from the source
702
5
    size_t Available() const override { return _available; }
703
704
    // Peek at the next flat region of the source.  Does not reposition
705
    // the source.  The returned region is empty iff Available()==0.
706
    //
707
    // Returns a pointer to the beginning of the region and store its
708
    // length in *len.
709
    //
710
    // The returned region is valid until the next call to Skip() or
711
    // until this object is destroyed, whichever occurs first.
712
    //
713
    // The returned region may be larger than Available() (for example
714
    // if this ByteSource is a view on a substring of a larger source).
715
    // The caller is responsible for ensuring that it only reads the
716
    // Available() bytes.
717
23
    const char* Peek(size_t* len) override {
718
23
        if (_available == 0) {
719
0
            *len = 0;
720
0
            return nullptr;
721
0
        }
722
        // we should assure that *len is not 0
723
23
        *len = _slices[_cur_slice].size - _slice_off;
724
23
        DCHECK(*len != 0);
725
23
        return _slices[_cur_slice].data + _slice_off;
726
23
    }
727
728
    // Skip the next n bytes.  Invalidates any buffer returned by
729
    // a previous call to Peek().
730
    // REQUIRES: Available() >= n
731
24
    void Skip(size_t n) override {
732
24
        _available -= n;
733
32
        while (n > 0) {
734
23
            auto left = _slices[_cur_slice].size - _slice_off;
735
23
            if (left > n) {
736
                // n can be digest in current slice
737
15
                _slice_off += n;
738
15
                return;
739
15
            }
740
8
            _slice_off = 0;
741
8
            _cur_slice++;
742
8
            n -= left;
743
8
        }
744
24
    }
745
746
private:
747
    std::vector<Slice> _slices;
748
    size_t _available;
749
    size_t _cur_slice;
750
    size_t _slice_off;
751
};
752
753
class SnappyBlockCompression : public BlockCompressionCodec {
754
public:
755
183
    static SnappyBlockCompression* instance() {
756
183
        static SnappyBlockCompression s_instance;
757
183
        return &s_instance;
758
183
    }
759
1
    ~SnappyBlockCompression() override = default;
760
761
38
    Status compress(const Slice& input, faststring* output) override {
762
38
        size_t max_len = max_compressed_len(input.size);
763
38
        output->resize(max_len);
764
38
        Slice s(*output);
765
766
38
        snappy::RawCompress(input.data, input.size, s.data, &s.size);
767
38
        output->resize(s.size);
768
38
        return Status::OK();
769
38
    }
770
771
91
    Status decompress(const Slice& input, Slice* output) override {
772
91
        size_t uncompressed_size = 0;
773
91
        if (!snappy::GetUncompressedLength(input.data, input.size, &uncompressed_size)) {
774
0
            return Status::InvalidArgument("Fail to get Snappy uncompressed length");
775
0
        }
776
        // RawUncompress has no capacity argument, so reject an undersized destination first.
777
91
        if (uncompressed_size > output->size) {
778
1
            return Status::InvalidArgument("Snappy output size {} exceeds buffer capacity {}",
779
1
                                           uncompressed_size, output->size);
780
1
        }
781
90
        if (!snappy::RawUncompress(input.data, input.size, output->data)) {
782
0
            return Status::InvalidArgument("Fail to do Snappy decompress");
783
0
        }
784
90
        output->size = uncompressed_size;
785
90
        return Status::OK();
786
90
    }
787
788
    Status compress(const std::vector<Slice>& inputs, size_t uncompressed_size,
789
5
                    faststring* output) override {
790
5
        auto max_len = max_compressed_len(uncompressed_size);
791
5
        output->resize(max_len);
792
793
5
        SnappySlicesSource source(inputs);
794
5
        snappy::UncheckedByteArraySink sink(reinterpret_cast<char*>(output->data()));
795
5
        output->resize(snappy::Compress(&source, &sink));
796
5
        return Status::OK();
797
5
    }
798
799
43
    size_t max_compressed_len(size_t len) override { return snappy::MaxCompressedLength(len); }
800
};
801
802
class HadoopSnappyBlockCompression : public SnappyBlockCompression {
803
public:
804
0
    static HadoopSnappyBlockCompression* instance() {
805
0
        static HadoopSnappyBlockCompression s_instance;
806
0
        return &s_instance;
807
0
    }
808
0
    ~HadoopSnappyBlockCompression() override = default;
809
810
    // hadoop use block compression for snappy
811
    // https://github.com/apache/hadoop/blob/trunk/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/native/src/codec/SnappyCodec.cc
812
0
    Status compress(const Slice& input, faststring* output) override {
813
        // be same with hadop https://github.com/apache/hadoop/blob/trunk/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/compress/SnappyCodec.java
814
0
        size_t snappy_block_size = config::snappy_compression_block_size;
815
0
        size_t overhead = snappy_block_size / 6 + 32;
816
0
        size_t max_input_size = snappy_block_size - overhead;
817
818
0
        size_t data_len = input.size;
819
0
        char* data = input.data;
820
0
        std::vector<OwnedSlice> buffers;
821
0
        size_t out_len = 0;
822
823
0
        while (data_len > 0) {
824
0
            size_t input_size = std::min(data_len, max_input_size);
825
0
            Slice input_slice(data, input_size);
826
0
            faststring output_data;
827
0
            RETURN_IF_ERROR(SnappyBlockCompression::compress(input_slice, &output_data));
828
0
            out_len += output_data.size();
829
            // the OwnedSlice will be moved here
830
0
            buffers.push_back(output_data.build());
831
0
            data += input_size;
832
0
            data_len -= input_size;
833
0
        }
834
835
        // hadoop block compression: umcompressed_length | compressed_length1 | compressed_data1 | compressed_length2 | compressed_data2 | ...
836
0
        size_t total_output_len = 4 + 4 * buffers.size() + out_len;
837
0
        output->resize(total_output_len);
838
0
        char* output_buffer = (char*)output->data();
839
0
        BigEndian::Store32(output_buffer, cast_set<uint32_t>(input.get_size()));
840
0
        output_buffer += 4;
841
0
        for (const auto& buffer : buffers) {
842
0
            auto slice = buffer.slice();
843
0
            BigEndian::Store32(output_buffer, cast_set<uint32_t>(slice.get_size()));
844
0
            output_buffer += 4;
845
0
            memcpy(output_buffer, slice.get_data(), slice.get_size());
846
0
            output_buffer += slice.get_size();
847
0
        }
848
849
0
        DCHECK_EQ(output_buffer - (char*)output->data(), total_output_len);
850
851
0
        return Status::OK();
852
0
    }
853
854
0
    Status decompress(const Slice& input, Slice* output) override {
855
0
        return Status::InternalError("unimplement: SnappyHadoopBlockCompression::decompress");
856
0
    }
857
};
858
859
class ZlibBlockCompression : public BlockCompressionCodec {
860
public:
861
2
    static ZlibBlockCompression* instance() {
862
2
        static ZlibBlockCompression s_instance;
863
2
        return &s_instance;
864
2
    }
865
1
    ~ZlibBlockCompression() override = default;
866
867
5
    Status compress(const Slice& input, faststring* output) override {
868
5
        size_t max_len = max_compressed_len(input.size);
869
5
        output->resize(max_len);
870
5
        Slice s(*output);
871
872
5
        auto zres = ::compress((Bytef*)s.data, &s.size, (Bytef*)input.data, input.size);
873
5
        if (zres == Z_MEM_ERROR) {
874
0
            throw Exception(Status::MemoryLimitExceeded(fmt::format(
875
0
                    "ZLib compression failed due to memory allocationerror.error = {}, res = {} ",
876
0
                    zError(zres), zres)));
877
5
        } else if (zres != Z_OK) {
878
0
            return Status::InternalError("Fail to do Zlib compress, error={}", zError(zres));
879
0
        }
880
5
        output->resize(s.size);
881
5
        return Status::OK();
882
5
    }
883
884
    Status compress(const std::vector<Slice>& inputs, size_t uncompressed_size,
885
1
                    faststring* output) override {
886
1
        size_t max_len = max_compressed_len(uncompressed_size);
887
1
        output->resize(max_len);
888
889
1
        z_stream zstrm;
890
1
        zstrm.zalloc = Z_NULL;
891
1
        zstrm.zfree = Z_NULL;
892
1
        zstrm.opaque = Z_NULL;
893
1
        auto zres = deflateInit(&zstrm, Z_DEFAULT_COMPRESSION);
894
1
        if (zres == Z_MEM_ERROR) {
895
0
            throw Exception(Status::MemoryLimitExceeded(
896
0
                    "Fail to do ZLib stream compress, error={}, res={}", zError(zres), zres));
897
1
        } else if (zres != Z_OK) {
898
0
            return Status::InternalError("Fail to do ZLib stream compress, error={}, res={}",
899
0
                                         zError(zres), zres);
900
0
        }
901
        // we assume that output is e
902
1
        zstrm.next_out = (Bytef*)output->data();
903
1
        zstrm.avail_out = cast_set<decltype(zstrm.avail_out)>(output->size());
904
6
        for (int i = 0; i < inputs.size(); ++i) {
905
5
            if (inputs[i].size == 0) {
906
1
                continue;
907
1
            }
908
4
            zstrm.next_in = (Bytef*)inputs[i].data;
909
4
            zstrm.avail_in = cast_set<decltype(zstrm.avail_in)>(inputs[i].size);
910
4
            int flush = (i == (inputs.size() - 1)) ? Z_FINISH : Z_NO_FLUSH;
911
912
4
            zres = deflate(&zstrm, flush);
913
4
            if (zres != Z_OK && zres != Z_STREAM_END) {
914
0
                return Status::InternalError("Fail to do ZLib stream compress, error={}, res={}",
915
0
                                             zError(zres), zres);
916
0
            }
917
4
        }
918
919
1
        output->resize(zstrm.total_out);
920
1
        zres = deflateEnd(&zstrm);
921
1
        if (zres == Z_DATA_ERROR) {
922
0
            return Status::InvalidArgument("Fail to do deflateEnd, error={}, res={}", zError(zres),
923
0
                                           zres);
924
1
        } else if (zres != Z_OK) {
925
0
            return Status::InternalError("Fail to do deflateEnd on ZLib stream, error={}, res={}",
926
0
                                         zError(zres), zres);
927
0
        }
928
1
        return Status::OK();
929
1
    }
930
931
14
    Status decompress(const Slice& input, Slice* output) override {
932
14
        size_t input_size = input.size;
933
14
        auto zres =
934
14
                ::uncompress2((Bytef*)output->data, &output->size, (Bytef*)input.data, &input_size);
935
14
        if (zres == Z_DATA_ERROR) {
936
1
            return Status::InvalidArgument("Fail to do ZLib decompress, error={}", zError(zres));
937
13
        } else if (zres == Z_MEM_ERROR) {
938
0
            throw Exception(Status::MemoryLimitExceeded("Fail to do ZLib decompress, error={}",
939
0
                                                        zError(zres)));
940
13
        } else if (zres != Z_OK) {
941
7
            return Status::InternalError("Fail to do ZLib decompress, error={}", zError(zres));
942
7
        }
943
6
        return Status::OK();
944
14
    }
945
946
6
    size_t max_compressed_len(size_t len) override {
947
        // one-time overhead of six bytes for the entire stream plus five bytes per 16 KB block
948
6
        return len + 6 + 5 * ((len >> 14) + 1);
949
6
    }
950
};
951
952
class Bzip2BlockCompression : public BlockCompressionCodec {
953
public:
954
0
    static Bzip2BlockCompression* instance() {
955
0
        static Bzip2BlockCompression s_instance;
956
0
        return &s_instance;
957
0
    }
958
0
    ~Bzip2BlockCompression() override = default;
959
960
0
    Status compress(const Slice& input, faststring* output) override {
961
0
        size_t max_len = max_compressed_len(input.size);
962
0
        output->resize(max_len);
963
0
        auto size = cast_set<uint32_t>(output->size());
964
0
        auto bzres = BZ2_bzBuffToBuffCompress((char*)output->data(), &size, (char*)input.data,
965
0
                                              cast_set<uint32_t>(input.size), 9, 0, 0);
966
0
        if (bzres == BZ_MEM_ERROR) {
967
0
            throw Exception(
968
0
                    Status::MemoryLimitExceeded("Fail to do Bzip2 compress, ret={}", bzres));
969
0
        } else if (bzres == BZ_PARAM_ERROR) {
970
0
            return Status::InvalidArgument("Fail to do Bzip2 compress, ret={}", bzres);
971
0
        } else if (bzres != BZ_RUN_OK && bzres != BZ_FLUSH_OK && bzres != BZ_FINISH_OK &&
972
0
                   bzres != BZ_STREAM_END && bzres != BZ_OK) {
973
0
            return Status::InternalError("Failed to init bz2. status code: {}", bzres);
974
0
        }
975
0
        output->resize(size);
976
0
        return Status::OK();
977
0
    }
978
979
    Status compress(const std::vector<Slice>& inputs, size_t uncompressed_size,
980
0
                    faststring* output) override {
981
0
        size_t max_len = max_compressed_len(uncompressed_size);
982
0
        output->resize(max_len);
983
984
0
        bz_stream bzstrm;
985
0
        bzero(&bzstrm, sizeof(bzstrm));
986
0
        int bzres = BZ2_bzCompressInit(&bzstrm, 9, 0, 0);
987
0
        if (bzres == BZ_PARAM_ERROR) {
988
0
            return Status::InvalidArgument("Failed to init bz2. status code: {}", bzres);
989
0
        } else if (bzres == BZ_MEM_ERROR) {
990
0
            throw Exception(
991
0
                    Status::MemoryLimitExceeded("Failed to init bz2. status code: {}", bzres));
992
0
        } else if (bzres != BZ_OK) {
993
0
            return Status::InternalError("Failed to init bz2. status code: {}", bzres);
994
0
        }
995
        // we assume that output is e
996
0
        bzstrm.next_out = (char*)output->data();
997
0
        bzstrm.avail_out = cast_set<uint32_t>(output->size());
998
0
        for (int i = 0; i < inputs.size(); ++i) {
999
0
            if (inputs[i].size == 0) {
1000
0
                continue;
1001
0
            }
1002
0
            bzstrm.next_in = (char*)inputs[i].data;
1003
0
            bzstrm.avail_in = cast_set<uint32_t>(inputs[i].size);
1004
0
            int flush = (i == (inputs.size() - 1)) ? BZ_FINISH : BZ_RUN;
1005
1006
0
            bzres = BZ2_bzCompress(&bzstrm, flush);
1007
0
            if (bzres == BZ_PARAM_ERROR) {
1008
0
                return Status::InvalidArgument("Failed to init bz2. status code: {}", bzres);
1009
0
            } else if (bzres != BZ_RUN_OK && bzres != BZ_FLUSH_OK && bzres != BZ_FINISH_OK &&
1010
0
                       bzres != BZ_STREAM_END && bzres != BZ_OK) {
1011
0
                return Status::InternalError("Failed to init bz2. status code: {}", bzres);
1012
0
            }
1013
0
        }
1014
1015
0
        size_t total_out = (size_t)bzstrm.total_out_hi32 << 32 | (size_t)bzstrm.total_out_lo32;
1016
0
        output->resize(total_out);
1017
0
        bzres = BZ2_bzCompressEnd(&bzstrm);
1018
0
        if (bzres == BZ_PARAM_ERROR) {
1019
0
            return Status::InvalidArgument("Fail to do deflateEnd on bzip2 stream, res={}", bzres);
1020
0
        } else if (bzres != BZ_OK) {
1021
0
            return Status::InternalError("Fail to do deflateEnd on bzip2 stream, res={}", bzres);
1022
0
        }
1023
0
        return Status::OK();
1024
0
    }
1025
1026
0
    Status decompress(const Slice& input, Slice* output) override {
1027
0
        return Status::InternalError("unimplement: Bzip2BlockCompression::decompress");
1028
0
    }
1029
1030
0
    size_t max_compressed_len(size_t len) override {
1031
        // TODO: make sure the max_compressed_len for bzip2
1032
        // 50 is an estimate fix overhead for bzip2
1033
        // in case the input len is small and BZ2_bzBuffToBuffCompress will return
1034
        // BZ_OUTBUFF_FULL
1035
0
        return len * 2 + 50;
1036
0
    }
1037
};
1038
1039
// for ZSTD compression and decompression, with BOTH fast and high compression ratio
1040
class ZstdBlockCompression : public BlockCompressionCodec {
1041
private:
1042
    class CContext {
1043
        ENABLE_FACTORY_CREATOR(CContext);
1044
1045
    public:
1046
1
        CContext() : ctx(nullptr) {
1047
1
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
1048
1
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
1049
1
            buffer = std::make_unique<faststring>();
1050
1
        }
1051
        ZSTD_CCtx* ctx;
1052
        std::unique_ptr<faststring> buffer;
1053
1
        ~CContext() {
1054
1
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
1055
1
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
1056
1
            if (ctx) {
1057
1
                ZSTD_freeCCtx(ctx);
1058
1
            }
1059
1
            buffer.reset();
1060
1
        }
1061
    };
1062
    class DContext {
1063
        ENABLE_FACTORY_CREATOR(DContext);
1064
1065
    public:
1066
6
        DContext() : ctx(nullptr) {}
1067
        ZSTD_DCtx* ctx;
1068
6
        ~DContext() {
1069
6
            if (ctx) {
1070
6
                ZSTD_freeDCtx(ctx);
1071
6
            }
1072
6
        }
1073
    };
1074
1075
public:
1076
1.20k
    static ZstdBlockCompression* instance() {
1077
1.20k
        static ZstdBlockCompression s_instance;
1078
1.20k
        return &s_instance;
1079
1.20k
    }
1080
1
    ~ZstdBlockCompression() {
1081
1
        SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
1082
1
                ExecEnv::GetInstance()->block_compression_mem_tracker());
1083
1
        _ctx_c_pool.clear();
1084
1
        _ctx_d_pool.clear();
1085
1
    }
1086
1087
712
    size_t max_compressed_len(size_t len) override { return ZSTD_compressBound(len); }
1088
1089
554
    Status compress(const Slice& input, faststring* output) override {
1090
554
        std::vector<Slice> inputs {input};
1091
554
        return compress(inputs, input.size, output);
1092
554
    }
1093
1094
    // follow ZSTD official example
1095
    //  https://github.com/facebook/zstd/blob/dev/examples/streaming_compression.c
1096
    Status compress(const std::vector<Slice>& inputs, size_t uncompressed_size,
1097
712
                    faststring* output) override {
1098
712
        std::unique_ptr<CContext> context;
1099
712
        RETURN_IF_ERROR(_acquire_compression_ctx(context));
1100
712
        bool compress_failed = false;
1101
712
        Defer defer {[&] {
1102
712
            if (!compress_failed) {
1103
712
                _release_compression_ctx(std::move(context));
1104
712
            }
1105
712
        }};
1106
1107
712
        try {
1108
712
            size_t max_len = max_compressed_len(uncompressed_size);
1109
712
            Slice compressed_buf;
1110
712
            if (max_len > MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
1111
                // use output directly
1112
4
                output->resize(max_len);
1113
4
                compressed_buf.data = reinterpret_cast<char*>(output->data());
1114
4
                compressed_buf.size = max_len;
1115
708
            } else {
1116
708
                {
1117
708
                    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
1118
708
                            ExecEnv::GetInstance()->block_compression_mem_tracker());
1119
                    // reuse context buffer if max_len <= MAX_COMPRESSION_BUFFER_FOR_REUSE
1120
708
                    context->buffer->resize(max_len);
1121
708
                }
1122
708
                compressed_buf.data = reinterpret_cast<char*>(context->buffer->data());
1123
708
                compressed_buf.size = max_len;
1124
708
            }
1125
1126
            // set compression level to default 3
1127
712
            auto ret = ZSTD_CCtx_setParameter(context->ctx, ZSTD_c_compressionLevel,
1128
712
                                              ZSTD_CLEVEL_DEFAULT);
1129
712
            if (ZSTD_isError(ret)) {
1130
0
                return Status::InvalidArgument("ZSTD_CCtx_setParameter compression level error: {}",
1131
0
                                               ZSTD_getErrorString(ZSTD_getErrorCode(ret)));
1132
0
            }
1133
            // set checksum flag to 1
1134
712
            ret = ZSTD_CCtx_setParameter(context->ctx, ZSTD_c_checksumFlag, 1);
1135
712
            if (ZSTD_isError(ret)) {
1136
0
                return Status::InvalidArgument("ZSTD_CCtx_setParameter checksumFlag error: {}",
1137
0
                                               ZSTD_getErrorString(ZSTD_getErrorCode(ret)));
1138
0
            }
1139
1140
712
            ZSTD_outBuffer out_buf = {compressed_buf.data, compressed_buf.size, 0};
1141
1142
1.42k
            for (size_t i = 0; i < inputs.size(); i++) {
1143
716
                ZSTD_inBuffer in_buf = {inputs[i].data, inputs[i].size, 0};
1144
1145
716
                bool last_input = (i == inputs.size() - 1);
1146
716
                auto mode = last_input ? ZSTD_e_end : ZSTD_e_continue;
1147
1148
716
                bool finished = false;
1149
716
                do {
1150
                    // do compress
1151
716
                    ret = ZSTD_compressStream2(context->ctx, &out_buf, &in_buf, mode);
1152
1153
716
                    if (ZSTD_isError(ret)) {
1154
0
                        compress_failed = true;
1155
0
                        return Status::InternalError("ZSTD_compressStream2 error: {}",
1156
0
                                                     ZSTD_getErrorString(ZSTD_getErrorCode(ret)));
1157
0
                    }
1158
1159
                    // ret is ZSTD hint for needed output buffer size
1160
716
                    if (ret > 0 && out_buf.pos == out_buf.size) {
1161
0
                        compress_failed = true;
1162
0
                        return Status::InternalError("ZSTD_compressStream2 output buffer full");
1163
0
                    }
1164
1165
716
                    finished = last_input ? (ret == 0) : (in_buf.pos == inputs[i].size);
1166
716
                } while (!finished);
1167
716
            }
1168
1169
            // set compressed size for caller
1170
712
            output->resize(out_buf.pos);
1171
712
            if (max_len <= MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
1172
708
                output->assign_copy(reinterpret_cast<uint8_t*>(compressed_buf.data), out_buf.pos);
1173
708
            }
1174
712
        } catch (std::exception& e) {
1175
0
            return Status::InternalError("Fail to do ZSTD compress due to exception {}", e.what());
1176
0
        } catch (...) {
1177
            // Do not set compress_failed to release context
1178
0
            DCHECK(!compress_failed);
1179
0
            return Status::InternalError("Fail to do ZSTD compress due to exception");
1180
0
        }
1181
1182
712
        return Status::OK();
1183
712
    }
1184
1185
584
    Status decompress(const Slice& input, Slice* output) override {
1186
584
        std::unique_ptr<DContext> context;
1187
584
        bool decompress_failed = false;
1188
584
        RETURN_IF_ERROR(_acquire_decompression_ctx(context));
1189
584
        Defer defer {[&] {
1190
584
            if (!decompress_failed) {
1191
579
                _release_decompression_ctx(std::move(context));
1192
579
            }
1193
584
        }};
1194
1195
584
        size_t ret = ZSTD_decompressDCtx(context->ctx, output->data, output->size, input.data,
1196
584
                                         input.size);
1197
584
        if (ZSTD_isError(ret)) {
1198
5
            decompress_failed = true;
1199
5
            return Status::InternalError("ZSTD_decompressDCtx error: {}",
1200
5
                                         ZSTD_getErrorString(ZSTD_getErrorCode(ret)));
1201
5
        }
1202
1203
        // set decompressed size for caller
1204
579
        output->size = ret;
1205
1206
579
        return Status::OK();
1207
584
    }
1208
1209
private:
1210
712
    Status _acquire_compression_ctx(std::unique_ptr<CContext>& out) {
1211
712
        std::lock_guard<std::mutex> l(_ctx_c_mutex);
1212
712
        if (_ctx_c_pool.empty()) {
1213
1
            std::unique_ptr<CContext> localCtx = CContext::create_unique();
1214
1
            if (localCtx.get() == nullptr) {
1215
0
                return Status::InvalidArgument("failed to new ZSTD CContext");
1216
0
            }
1217
            //typedef LZ4F_cctx* LZ4F_compressionContext_t;
1218
1
            localCtx->ctx = ZSTD_createCCtx();
1219
1
            if (localCtx->ctx == nullptr) {
1220
0
                return Status::InvalidArgument("Failed to create ZSTD compress ctx");
1221
0
            }
1222
1
            out = std::move(localCtx);
1223
1
            return Status::OK();
1224
1
        }
1225
711
        out = std::move(_ctx_c_pool.back());
1226
711
        _ctx_c_pool.pop_back();
1227
711
        return Status::OK();
1228
712
    }
1229
712
    void _release_compression_ctx(std::unique_ptr<CContext> context) {
1230
712
        DCHECK(context);
1231
712
        auto ret = ZSTD_CCtx_reset(context->ctx, ZSTD_reset_session_only);
1232
712
        DCHECK(!ZSTD_isError(ret));
1233
712
        std::lock_guard<std::mutex> l(_ctx_c_mutex);
1234
712
        _ctx_c_pool.push_back(std::move(context));
1235
712
    }
1236
1237
584
    Status _acquire_decompression_ctx(std::unique_ptr<DContext>& out) {
1238
584
        std::lock_guard<std::mutex> l(_ctx_d_mutex);
1239
584
        if (_ctx_d_pool.empty()) {
1240
6
            std::unique_ptr<DContext> localCtx = DContext::create_unique();
1241
6
            if (localCtx.get() == nullptr) {
1242
0
                return Status::InvalidArgument("failed to new ZSTD DContext");
1243
0
            }
1244
6
            localCtx->ctx = ZSTD_createDCtx();
1245
6
            if (localCtx->ctx == nullptr) {
1246
0
                return Status::InvalidArgument("Fail to init ZSTD decompress context");
1247
0
            }
1248
6
            out = std::move(localCtx);
1249
6
            return Status::OK();
1250
6
        }
1251
578
        out = std::move(_ctx_d_pool.back());
1252
578
        _ctx_d_pool.pop_back();
1253
578
        return Status::OK();
1254
584
    }
1255
579
    void _release_decompression_ctx(std::unique_ptr<DContext> context) {
1256
579
        DCHECK(context);
1257
        // reset ctx to start a new decompress session
1258
579
        auto ret = ZSTD_DCtx_reset(context->ctx, ZSTD_reset_session_only);
1259
579
        DCHECK(!ZSTD_isError(ret));
1260
579
        std::lock_guard<std::mutex> l(_ctx_d_mutex);
1261
579
        _ctx_d_pool.push_back(std::move(context));
1262
579
    }
1263
1264
private:
1265
    mutable std::mutex _ctx_c_mutex;
1266
    mutable std::vector<std::unique_ptr<CContext>> _ctx_c_pool;
1267
1268
    mutable std::mutex _ctx_d_mutex;
1269
    mutable std::vector<std::unique_ptr<DContext>> _ctx_d_pool;
1270
};
1271
1272
class GzipBlockCompression : public ZlibBlockCompression {
1273
public:
1274
0
    static GzipBlockCompression* instance() {
1275
0
        static GzipBlockCompression s_instance;
1276
0
        return &s_instance;
1277
0
    }
1278
0
    ~GzipBlockCompression() override = default;
1279
1280
0
    Status compress(const Slice& input, faststring* output) override {
1281
0
        size_t max_len = max_compressed_len(input.size);
1282
0
        output->resize(max_len);
1283
1284
0
        z_stream z_strm = {};
1285
0
        z_strm.zalloc = Z_NULL;
1286
0
        z_strm.zfree = Z_NULL;
1287
0
        z_strm.opaque = Z_NULL;
1288
1289
0
        int zres = deflateInit2(&z_strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + GZIP_CODEC,
1290
0
                                8, Z_DEFAULT_STRATEGY);
1291
1292
0
        if (zres == Z_MEM_ERROR) {
1293
0
            throw Exception(Status::MemoryLimitExceeded(
1294
0
                    "Fail to init ZLib compress, error={}, res={}", zError(zres), zres));
1295
0
        } else if (zres != Z_OK) {
1296
0
            return Status::InternalError("Fail to init ZLib compress, error={}, res={}",
1297
0
                                         zError(zres), zres);
1298
0
        }
1299
1300
0
        z_strm.next_in = (Bytef*)input.get_data();
1301
0
        z_strm.avail_in = cast_set<decltype(z_strm.avail_in)>(input.get_size());
1302
0
        z_strm.next_out = (Bytef*)output->data();
1303
0
        z_strm.avail_out = cast_set<decltype(z_strm.avail_out)>(output->size());
1304
1305
0
        zres = deflate(&z_strm, Z_FINISH);
1306
0
        if (zres != Z_OK && zres != Z_STREAM_END) {
1307
0
            return Status::InternalError("Fail to do ZLib stream compress, error={}, res={}",
1308
0
                                         zError(zres), zres);
1309
0
        }
1310
1311
0
        output->resize(z_strm.total_out);
1312
0
        zres = deflateEnd(&z_strm);
1313
0
        if (zres == Z_DATA_ERROR) {
1314
0
            return Status::InvalidArgument("Fail to end zlib compress");
1315
0
        } else if (zres != Z_OK) {
1316
0
            return Status::InternalError("Fail to end zlib compress");
1317
0
        }
1318
0
        return Status::OK();
1319
0
    }
1320
1321
    Status compress(const std::vector<Slice>& inputs, size_t uncompressed_size,
1322
0
                    faststring* output) override {
1323
0
        size_t max_len = max_compressed_len(uncompressed_size);
1324
0
        output->resize(max_len);
1325
1326
0
        z_stream zstrm;
1327
0
        zstrm.zalloc = Z_NULL;
1328
0
        zstrm.zfree = Z_NULL;
1329
0
        zstrm.opaque = Z_NULL;
1330
0
        auto zres = deflateInit2(&zstrm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + GZIP_CODEC,
1331
0
                                 8, Z_DEFAULT_STRATEGY);
1332
0
        if (zres == Z_MEM_ERROR) {
1333
0
            throw Exception(Status::MemoryLimitExceeded(
1334
0
                    "Fail to init ZLib stream compress, error={}, res={}", zError(zres), zres));
1335
0
        } else if (zres != Z_OK) {
1336
0
            return Status::InternalError("Fail to init ZLib stream compress, error={}, res={}",
1337
0
                                         zError(zres), zres);
1338
0
        }
1339
1340
        // we assume that output is e
1341
0
        zstrm.next_out = (Bytef*)output->data();
1342
0
        zstrm.avail_out = cast_set<decltype(zstrm.avail_out)>(output->size());
1343
0
        for (int i = 0; i < inputs.size(); ++i) {
1344
0
            if (inputs[i].size == 0) {
1345
0
                continue;
1346
0
            }
1347
0
            zstrm.next_in = (Bytef*)inputs[i].data;
1348
0
            zstrm.avail_in = cast_set<decltype(zstrm.avail_in)>(inputs[i].size);
1349
0
            int flush = (i == (inputs.size() - 1)) ? Z_FINISH : Z_NO_FLUSH;
1350
1351
0
            zres = deflate(&zstrm, flush);
1352
0
            if (zres != Z_OK && zres != Z_STREAM_END) {
1353
0
                return Status::InternalError("Fail to do ZLib stream compress, error={}, res={}",
1354
0
                                             zError(zres), zres);
1355
0
            }
1356
0
        }
1357
1358
0
        output->resize(zstrm.total_out);
1359
0
        zres = deflateEnd(&zstrm);
1360
0
        if (zres == Z_DATA_ERROR) {
1361
0
            return Status::InvalidArgument("Fail to do deflateEnd on ZLib stream, error={}, res={}",
1362
0
                                           zError(zres), zres);
1363
0
        } else if (zres != Z_OK) {
1364
0
            return Status::InternalError("Fail to do deflateEnd on ZLib stream, error={}, res={}",
1365
0
                                         zError(zres), zres);
1366
0
        }
1367
0
        return Status::OK();
1368
0
    }
1369
1370
0
    Status decompress(const Slice& input, Slice* output) override {
1371
0
        z_stream z_strm = {};
1372
0
        z_strm.zalloc = Z_NULL;
1373
0
        z_strm.zfree = Z_NULL;
1374
0
        z_strm.opaque = Z_NULL;
1375
1376
0
        int ret = inflateInit2(&z_strm, MAX_WBITS + GZIP_CODEC);
1377
0
        if (ret != Z_OK) {
1378
0
            return Status::InternalError("Fail to init ZLib decompress, error={}, res={}",
1379
0
                                         zError(ret), ret);
1380
0
        }
1381
1382
        // 1. set input and output
1383
0
        z_strm.next_in = reinterpret_cast<Bytef*>(input.data);
1384
0
        z_strm.avail_in = cast_set<decltype(z_strm.avail_in)>(input.size);
1385
0
        z_strm.next_out = reinterpret_cast<Bytef*>(output->data);
1386
0
        z_strm.avail_out = cast_set<decltype(z_strm.avail_out)>(output->size);
1387
1388
0
        if (z_strm.avail_out > 0) {
1389
            // We only support non-streaming use case  for block decompressor
1390
0
            ret = inflate(&z_strm, Z_FINISH);
1391
0
            if (ret != Z_OK && ret != Z_STREAM_END) {
1392
0
                (void)inflateEnd(&z_strm);
1393
0
                if (ret == Z_MEM_ERROR) {
1394
0
                    throw Exception(Status::MemoryLimitExceeded(
1395
0
                            "Fail to do ZLib stream compress, error={}, res={}", zError(ret), ret));
1396
0
                } else if (ret == Z_DATA_ERROR) {
1397
0
                    return Status::InvalidArgument(
1398
0
                            "Fail to do ZLib stream compress, error={}, res={}", zError(ret), ret);
1399
0
                }
1400
0
                return Status::InternalError("Fail to do ZLib stream compress, error={}, res={}",
1401
0
                                             zError(ret), ret);
1402
0
            }
1403
0
        }
1404
0
        (void)inflateEnd(&z_strm);
1405
1406
0
        return Status::OK();
1407
0
    }
1408
1409
0
    size_t max_compressed_len(size_t len) override {
1410
0
        z_stream zstrm;
1411
0
        zstrm.zalloc = Z_NULL;
1412
0
        zstrm.zfree = Z_NULL;
1413
0
        zstrm.opaque = Z_NULL;
1414
0
        auto zres = deflateInit2(&zstrm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + GZIP_CODEC,
1415
0
                                 MEM_LEVEL, Z_DEFAULT_STRATEGY);
1416
0
        if (zres != Z_OK) {
1417
            // Fall back to zlib estimate logic for deflate, notice this may
1418
            // cause decompress error
1419
0
            LOG(WARNING) << "Fail to do ZLib stream compress, error=" << zError(zres)
1420
0
                         << ", res=" << zres;
1421
0
            return ZlibBlockCompression::max_compressed_len(len);
1422
0
        } else {
1423
0
            zres = deflateEnd(&zstrm);
1424
0
            if (zres != Z_OK) {
1425
0
                LOG(WARNING) << "Fail to do deflateEnd on ZLib stream, error=" << zError(zres)
1426
0
                             << ", res=" << zres;
1427
0
            }
1428
            // Mark, maintainer of zlib, has stated that 12 needs to be added to
1429
            // result for gzip
1430
            // http://compgroups.net/comp.unix.programmer/gzip-compressing-an-in-memory-string-usi/54854
1431
            // To have a safe upper bound for "wrapper variations", we add 32 to
1432
            // estimate
1433
0
            auto upper_bound = deflateBound(&zstrm, len) + 32;
1434
0
            return upper_bound;
1435
0
        }
1436
0
    }
1437
1438
private:
1439
    // Magic number for zlib, see https://zlib.net/manual.html for more details.
1440
    const static int GZIP_CODEC = 16; // gzip
1441
    // The memLevel parameter specifies how much memory should be allocated for
1442
    // the internal compression state.
1443
    const static int MEM_LEVEL = 8;
1444
};
1445
1446
// Only used on x86 or x86_64
1447
#if defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || \
1448
        defined(__i386) || defined(_M_IX86)
1449
class GzipBlockCompressionByLibdeflate final : public GzipBlockCompression {
1450
public:
1451
0
    GzipBlockCompressionByLibdeflate() : GzipBlockCompression() {}
1452
0
    static GzipBlockCompressionByLibdeflate* instance() {
1453
0
        static GzipBlockCompressionByLibdeflate s_instance;
1454
0
        return &s_instance;
1455
0
    }
1456
0
    ~GzipBlockCompressionByLibdeflate() override = default;
1457
1458
0
    Status decompress(const Slice& input, Slice* output) override {
1459
0
        if (input.empty()) {
1460
0
            output->size = 0;
1461
0
            return Status::OK();
1462
0
        }
1463
0
        thread_local std::unique_ptr<libdeflate_decompressor, void (*)(libdeflate_decompressor*)>
1464
0
                decompressor {libdeflate_alloc_decompressor(), libdeflate_free_decompressor};
1465
0
        if (!decompressor) {
1466
0
            return Status::InternalError("libdeflate_alloc_decompressor error.");
1467
0
        }
1468
0
        std::size_t out_len;
1469
0
        auto result = libdeflate_gzip_decompress(decompressor.get(), input.data, input.size,
1470
0
                                                 output->data, output->size, &out_len);
1471
0
        if (result != LIBDEFLATE_SUCCESS) {
1472
0
            return Status::InternalError("libdeflate_gzip_decompress error, res={}", result);
1473
0
        }
1474
0
        return Status::OK();
1475
0
    }
1476
};
1477
#endif
1478
1479
class LzoBlockCompression final : public BlockCompressionCodec {
1480
public:
1481
0
    static LzoBlockCompression* instance() {
1482
0
        static LzoBlockCompression s_instance;
1483
0
        return &s_instance;
1484
0
    }
1485
1486
0
    Status compress(const Slice& input, faststring* output) override {
1487
0
        return Status::InvalidArgument("not impl lzo compress.");
1488
0
    }
1489
0
    size_t max_compressed_len(size_t len) override { return 0; };
1490
0
    Status decompress(const Slice& input, Slice* output) override {
1491
0
        auto* input_ptr = input.data;
1492
0
        auto remain_input_size = input.size;
1493
0
        auto* output_ptr = output->data;
1494
0
        auto remain_output_size = output->size;
1495
0
        auto* output_limit = output->data + output->size;
1496
1497
        // Example:
1498
        // OriginData(The original data will be divided into several large data block.) :
1499
        //      large data block1 | large data block2 | large data block3 | ....
1500
        // The large data block will be divided into several small data block.
1501
        // Suppose a large data block is divided into three small blocks:
1502
        // large data block1:            | small block1 | small block2 | small block3 |
1503
        // CompressData:   <A [B1 compress(small block1) ] [B2 compress(small block1) ] [B3 compress(small block1)]>
1504
        //
1505
        // A : original length of the current block of large data block.
1506
        // sizeof(A) = 4 bytes.
1507
        // A = length(small block1) + length(small block2) + length(small block3)
1508
        // Bx : length of  small data block bx.
1509
        // sizeof(Bx) = 4 bytes.
1510
        // Bx = length(compress(small blockx))
1511
0
        try {
1512
0
            while (remain_input_size > 0) {
1513
0
                if (remain_input_size < 4) {
1514
0
                    return Status::InvalidArgument(
1515
0
                            "Need more input buffer to get large_block_uncompressed_len.");
1516
0
                }
1517
1518
0
                uint32_t large_block_uncompressed_len = BigEndian::Load32(input_ptr);
1519
0
                input_ptr += 4;
1520
0
                remain_input_size -= 4;
1521
1522
0
                if (remain_output_size < large_block_uncompressed_len) {
1523
0
                    return Status::InvalidArgument(
1524
0
                            "Need more output buffer to get uncompressed data.");
1525
0
                }
1526
1527
0
                while (large_block_uncompressed_len > 0) {
1528
0
                    if (remain_input_size < 4) {
1529
0
                        return Status::InvalidArgument(
1530
0
                                "Need more input buffer to get small_block_compressed_len.");
1531
0
                    }
1532
1533
0
                    uint32_t small_block_compressed_len = BigEndian::Load32(input_ptr);
1534
0
                    input_ptr += 4;
1535
0
                    remain_input_size -= 4;
1536
1537
0
                    if (remain_input_size < small_block_compressed_len) {
1538
0
                        return Status::InvalidArgument(
1539
0
                                "Need more input buffer to decompress small block.");
1540
0
                    }
1541
1542
0
                    auto small_block_uncompressed_len =
1543
0
                            orc::lzoDecompress(input_ptr, input_ptr + small_block_compressed_len,
1544
0
                                               output_ptr, output_limit);
1545
1546
0
                    input_ptr += small_block_compressed_len;
1547
0
                    remain_input_size -= small_block_compressed_len;
1548
1549
0
                    output_ptr += small_block_uncompressed_len;
1550
0
                    large_block_uncompressed_len -= small_block_uncompressed_len;
1551
0
                    remain_output_size -= small_block_uncompressed_len;
1552
0
                }
1553
0
            }
1554
0
        } catch (const orc::ParseError& e) {
1555
            //Prevent be from hanging due to orc::lzoDecompress throw exception
1556
0
            return Status::InternalError("Fail to do LZO decompress, error={}", e.what());
1557
0
        }
1558
0
        return Status::OK();
1559
0
    }
1560
};
1561
1562
class BrotliBlockCompression final : public BlockCompressionCodec {
1563
public:
1564
0
    static BrotliBlockCompression* instance() {
1565
0
        static BrotliBlockCompression s_instance;
1566
0
        return &s_instance;
1567
0
    }
1568
1569
0
    Status compress(const Slice& input, faststring* output) override {
1570
0
        return Status::InvalidArgument("not impl brotli compress.");
1571
0
    }
1572
1573
0
    size_t max_compressed_len(size_t len) override { return 0; };
1574
1575
0
    Status decompress(const Slice& input, Slice* output) override {
1576
        // The size of output buffer is always equal to the umcompressed length.
1577
0
        BrotliDecoderResult result = BrotliDecoderDecompress(
1578
0
                input.get_size(), reinterpret_cast<const uint8_t*>(input.get_data()), &output->size,
1579
0
                reinterpret_cast<uint8_t*>(output->data));
1580
0
        if (result != BROTLI_DECODER_RESULT_SUCCESS) {
1581
0
            return Status::InternalError("Brotli decompression failed, result={}", result);
1582
0
        }
1583
0
        return Status::OK();
1584
0
    }
1585
};
1586
1587
Status get_block_compression_codec(segment_v2::CompressionTypePB type,
1588
24.7k
                                   BlockCompressionCodec** codec) {
1589
24.7k
    switch (type) {
1590
259
    case segment_v2::CompressionTypePB::NO_COMPRESSION:
1591
259
        *codec = nullptr;
1592
259
        break;
1593
57
    case segment_v2::CompressionTypePB::SNAPPY:
1594
57
        *codec = SnappyBlockCompression::instance();
1595
57
        break;
1596
711
    case segment_v2::CompressionTypePB::LZ4:
1597
711
        *codec = Lz4BlockCompression::instance();
1598
711
        break;
1599
22.4k
    case segment_v2::CompressionTypePB::LZ4F:
1600
22.4k
        *codec = Lz4fBlockCompression::instance();
1601
22.4k
        break;
1602
2
    case segment_v2::CompressionTypePB::LZ4HC:
1603
2
        *codec = Lz4HCBlockCompression::instance();
1604
2
        break;
1605
2
    case segment_v2::CompressionTypePB::ZLIB:
1606
2
        *codec = ZlibBlockCompression::instance();
1607
2
        break;
1608
1.17k
    case segment_v2::CompressionTypePB::ZSTD:
1609
1.17k
        *codec = ZstdBlockCompression::instance();
1610
1.17k
        break;
1611
0
    default:
1612
0
        return Status::InternalError("unknown compression type({})", type);
1613
24.7k
    }
1614
1615
24.7k
    return Status::OK();
1616
24.7k
}
1617
1618
// this can only be used in hive text write
1619
0
Status get_block_compression_codec(TFileCompressType::type type, BlockCompressionCodec** codec) {
1620
0
    switch (type) {
1621
0
    case TFileCompressType::PLAIN:
1622
0
        *codec = nullptr;
1623
0
        break;
1624
0
    case TFileCompressType::ZLIB:
1625
0
        *codec = ZlibBlockCompression::instance();
1626
0
        break;
1627
0
    case TFileCompressType::GZ:
1628
0
        *codec = GzipBlockCompression::instance();
1629
0
        break;
1630
0
    case TFileCompressType::BZ2:
1631
0
        *codec = Bzip2BlockCompression::instance();
1632
0
        break;
1633
0
    case TFileCompressType::LZ4BLOCK:
1634
0
        *codec = HadoopLz4BlockCompression::instance();
1635
0
        break;
1636
0
    case TFileCompressType::SNAPPYBLOCK:
1637
0
        *codec = HadoopSnappyBlockCompression::instance();
1638
0
        break;
1639
0
    case TFileCompressType::ZSTD:
1640
0
        *codec = ZstdBlockCompression::instance();
1641
0
        break;
1642
0
    default:
1643
0
        return Status::InternalError("unsupport compression type({}) int hive text", type);
1644
0
    }
1645
1646
0
    return Status::OK();
1647
0
}
1648
1649
Status get_block_compression_codec(tparquet::CompressionCodec::type parquet_codec,
1650
448
                                   BlockCompressionCodec** codec) {
1651
448
    switch (parquet_codec) {
1652
291
    case tparquet::CompressionCodec::UNCOMPRESSED:
1653
291
        *codec = nullptr;
1654
291
        break;
1655
126
    case tparquet::CompressionCodec::SNAPPY:
1656
126
        *codec = SnappyBlockCompression::instance();
1657
126
        break;
1658
0
    case tparquet::CompressionCodec::LZ4_RAW: // we can use LZ4 compression algorithm parse LZ4_RAW
1659
0
    case tparquet::CompressionCodec::LZ4:
1660
0
        *codec = HadoopLz4BlockCompression::instance();
1661
0
        break;
1662
31
    case tparquet::CompressionCodec::ZSTD:
1663
31
        *codec = ZstdBlockCompression::instance();
1664
31
        break;
1665
0
    case tparquet::CompressionCodec::GZIP:
1666
// Only used on x86 or x86_64
1667
0
#if defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || \
1668
0
        defined(__i386) || defined(_M_IX86)
1669
0
        *codec = GzipBlockCompressionByLibdeflate::instance();
1670
#else
1671
        *codec = GzipBlockCompression::instance();
1672
#endif
1673
0
        break;
1674
0
    case tparquet::CompressionCodec::LZO:
1675
0
        *codec = LzoBlockCompression::instance();
1676
0
        break;
1677
0
    case tparquet::CompressionCodec::BROTLI:
1678
0
        *codec = BrotliBlockCompression::instance();
1679
0
        break;
1680
0
    default:
1681
0
        return Status::InternalError("unknown compression type({})", parquet_codec);
1682
448
    }
1683
1684
448
    return Status::OK();
1685
448
}
1686
1687
} // namespace doris