Coverage Report

Created: 2026-07-23 18: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
422
                                       faststring* output) {
78
422
    faststring buf;
79
    // we compute total size to avoid more memory copy
80
422
    buf.reserve(uncompressed_size);
81
487
    for (auto& input : inputs) {
82
487
        buf.append(input.data, input.size);
83
487
    }
84
422
    return compress(buf, output);
85
422
}
86
87
393k
bool BlockCompressionCodec::exceed_max_compress_len(size_t uncompressed_size) {
88
393k
    return uncompressed_size > std::numeric_limits<int32_t>::max();
89
393k
}
90
91
class Lz4BlockCompression : public BlockCompressionCodec {
92
private:
93
    class Context {
94
        ENABLE_FACTORY_CREATOR(Context);
95
96
    public:
97
26
        Context() : ctx(nullptr) {
98
26
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
99
26
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
100
26
            buffer = std::make_unique<faststring>();
101
26
        }
102
        LZ4_stream_t* ctx;
103
        std::unique_ptr<faststring> buffer;
104
4
        ~Context() {
105
4
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
106
4
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
107
4
            if (ctx) {
108
4
                LZ4_freeStream(ctx);
109
4
            }
110
4
            buffer.reset();
111
4
        }
112
    };
113
114
public:
115
69.4k
    static Lz4BlockCompression* instance() {
116
69.4k
        static Lz4BlockCompression s_instance;
117
69.4k
        return &s_instance;
118
69.4k
    }
119
4
    ~Lz4BlockCompression() override {
120
4
        SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
121
4
                ExecEnv::GetInstance()->block_compression_mem_tracker());
122
4
        _ctx_pool.clear();
123
4
    }
124
125
34.5k
    Status compress(const Slice& input, faststring* output) override {
126
34.5k
        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
34.5k
        std::unique_ptr<Context> context;
136
34.5k
        RETURN_IF_ERROR(_acquire_compression_ctx(context));
137
34.5k
        bool compress_failed = false;
138
34.5k
        Defer defer {[&] {
139
34.5k
            if (!compress_failed) {
140
34.5k
                _release_compression_ctx(std::move(context));
141
34.5k
            }
142
34.5k
        }};
143
144
34.5k
        try {
145
34.5k
            Slice compressed_buf;
146
34.5k
            size_t max_len = max_compressed_len(input.size);
147
34.5k
            if (max_len > MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
148
                // use output directly
149
4
                output->resize(max_len);
150
4
                compressed_buf.data = reinterpret_cast<char*>(output->data());
151
4
                compressed_buf.size = max_len;
152
34.5k
            } else {
153
                // reuse context buffer if max_len <= MAX_COMPRESSION_BUFFER_FOR_REUSE
154
34.5k
                {
155
                    // context->buffer is resuable between queries, should accouting to
156
                    // global tracker.
157
34.5k
                    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
158
34.5k
                            ExecEnv::GetInstance()->block_compression_mem_tracker());
159
34.5k
                    context->buffer->resize(max_len);
160
34.5k
                }
161
34.5k
                compressed_buf.data = reinterpret_cast<char*>(context->buffer->data());
162
34.5k
                compressed_buf.size = max_len;
163
34.5k
            }
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
34.5k
            size_t compressed_len = LZ4_compress_fast_continue(
169
34.5k
                    context->ctx, input.data, compressed_buf.data, static_cast<int>(input.size),
170
34.5k
                    static_cast<int>(compressed_buf.size), ACCELARATION);
171
34.5k
            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
34.5k
            output->resize(compressed_len);
177
34.5k
            if (max_len <= MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
178
34.5k
                output->assign_copy(reinterpret_cast<uint8_t*>(compressed_buf.data),
179
34.5k
                                    compressed_len);
180
34.5k
            }
181
34.5k
        } 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
34.5k
        return Status::OK();
187
34.5k
    }
188
189
38.4k
    Status decompress(const Slice& input, Slice* output) override {
190
38.4k
        auto decompressed_len = LZ4_decompress_safe(
191
38.4k
                input.data, output->data, cast_set<int>(input.size), cast_set<int>(output->size));
192
38.4k
        if (decompressed_len < 0) {
193
5
            return Status::InternalError("fail to do LZ4 decompress, error={}", decompressed_len);
194
5
        }
195
38.3k
        output->size = decompressed_len;
196
38.3k
        return Status::OK();
197
38.4k
    }
198
199
34.5k
    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
34.5k
    Status _acquire_compression_ctx(std::unique_ptr<Context>& out) {
204
34.5k
        std::lock_guard<std::mutex> l(_ctx_mutex);
205
34.5k
        if (_ctx_pool.empty()) {
206
26
            std::unique_ptr<Context> localCtx = Context::create_unique();
207
26
            if (localCtx.get() == nullptr) {
208
0
                return Status::InvalidArgument("new LZ4 context error");
209
0
            }
210
26
            localCtx->ctx = LZ4_createStream();
211
26
            if (localCtx->ctx == nullptr) {
212
0
                return Status::InvalidArgument("LZ4_createStream error");
213
0
            }
214
26
            out = std::move(localCtx);
215
26
            return Status::OK();
216
26
        }
217
34.5k
        out = std::move(_ctx_pool.back());
218
34.5k
        _ctx_pool.pop_back();
219
34.5k
        return Status::OK();
220
34.5k
    }
221
34.5k
    void _release_compression_ctx(std::unique_ptr<Context> context) {
222
34.5k
        DCHECK(context);
223
34.5k
        LZ4_resetStream(context->ctx);
224
34.5k
        std::lock_guard<std::mutex> l(_ctx_mutex);
225
34.5k
        _ctx_pool.push_back(std::move(context));
226
34.5k
    }
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
3
    HadoopLz4BlockCompression() {
237
3
        Status st = Decompressor::create_decompressor(CompressType::LZ4BLOCK, &_decompressor);
238
3
        if (!st.ok()) {
239
0
            throw Exception(Status::FatalError(
240
0
                    "HadoopLz4BlockCompression construction failed. status = {}", st));
241
0
        }
242
3
    }
243
244
1
    ~HadoopLz4BlockCompression() override = default;
245
246
2.24k
    static HadoopLz4BlockCompression* instance() {
247
2.24k
        static HadoopLz4BlockCompression s_instance;
248
2.24k
        return &s_instance;
249
2.24k
    }
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
217
    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
217
        size_t lz4_block_size = config::lz4_compression_block_size;
256
217
        size_t overhead = lz4_block_size / 255 + 16;
257
217
        size_t max_input_size = lz4_block_size - overhead;
258
259
217
        size_t data_len = input.size;
260
217
        char* data = input.data;
261
217
        std::vector<OwnedSlice> buffers;
262
217
        size_t out_len = 0;
263
264
486
        while (data_len > 0) {
265
269
            size_t input_size = std::min(data_len, max_input_size);
266
269
            Slice input_slice(data, input_size);
267
269
            faststring output_data;
268
269
            RETURN_IF_ERROR(Lz4BlockCompression::compress(input_slice, &output_data));
269
269
            out_len += output_data.size();
270
269
            buffers.push_back(output_data.build());
271
269
            data += input_size;
272
269
            data_len -= input_size;
273
269
        }
274
275
        // hadoop block compression: umcompressed_length | compressed_length1 | compressed_data1 | compressed_length2 | compressed_data2 | ...
276
217
        size_t total_output_len = 4 + 4 * buffers.size() + out_len;
277
217
        output->resize(total_output_len);
278
217
        char* output_buffer = (char*)output->data();
279
217
        BigEndian::Store32(output_buffer, cast_set<uint32_t>(input.get_size()));
280
217
        output_buffer += 4;
281
269
        for (const auto& buffer : buffers) {
282
269
            auto slice = buffer.slice();
283
269
            BigEndian::Store32(output_buffer, cast_set<uint32_t>(slice.get_size()));
284
269
            output_buffer += 4;
285
269
            memcpy(output_buffer, slice.get_data(), slice.get_size());
286
269
            output_buffer += slice.get_size();
287
269
        }
288
289
217
        DCHECK_EQ(output_buffer - (char*)output->data(), total_output_len);
290
291
217
        return Status::OK();
292
217
    }
293
294
3.46k
    Status decompress(const Slice& input, Slice* output) override {
295
3.46k
        size_t input_bytes_read = 0;
296
3.46k
        size_t decompressed_len = 0;
297
3.46k
        size_t more_input_bytes = 0;
298
3.46k
        size_t more_output_bytes = 0;
299
3.46k
        bool stream_end = false;
300
3.46k
        auto st = _decompressor->decompress((uint8_t*)input.data, cast_set<uint32_t>(input.size),
301
3.46k
                                            &input_bytes_read, (uint8_t*)output->data,
302
3.46k
                                            cast_set<uint32_t>(output->size), &decompressed_len,
303
3.46k
                                            &stream_end, &more_input_bytes, &more_output_bytes);
304
        //try decompress use hadoopLz4 ,if failed fall back lz4.
305
3.46k
        return (st != Status::OK() || stream_end != true)
306
3.46k
                       ? Lz4BlockCompression::decompress(input, output)
307
3.46k
                       : Status::OK();
308
3.46k
    }
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
125k
    static SnappyBlockCompression* instance() {
756
125k
        static SnappyBlockCompression s_instance;
757
125k
        return &s_instance;
758
125k
    }
759
    ~SnappyBlockCompression() override = default;
760
761
30.1k
    Status compress(const Slice& input, faststring* output) override {
762
30.1k
        size_t max_len = max_compressed_len(input.size);
763
30.1k
        output->resize(max_len);
764
30.1k
        Slice s(*output);
765
766
30.1k
        snappy::RawCompress(input.data, input.size, s.data, &s.size);
767
30.1k
        output->resize(s.size);
768
30.1k
        return Status::OK();
769
30.1k
    }
770
771
100k
    Status decompress(const Slice& input, Slice* output) override {
772
100k
        size_t uncompressed_size = 0;
773
100k
        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
100k
        if (uncompressed_size > output->size) {
778
0
            return Status::InvalidArgument("Snappy output size {} exceeds buffer capacity {}",
779
0
                                           uncompressed_size, output->size);
780
0
        }
781
100k
        if (!snappy::RawUncompress(input.data, input.size, output->data)) {
782
0
            return Status::InvalidArgument("Fail to do Snappy decompress");
783
0
        }
784
100k
        output->size = uncompressed_size;
785
100k
        return Status::OK();
786
100k
    }
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
30.1k
    size_t max_compressed_len(size_t len) override { return snappy::MaxCompressedLength(len); }
800
};
801
802
class HadoopSnappyBlockCompression : public SnappyBlockCompression {
803
public:
804
150
    static HadoopSnappyBlockCompression* instance() {
805
150
        static HadoopSnappyBlockCompression s_instance;
806
150
        return &s_instance;
807
150
    }
808
    ~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
213
    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
213
        size_t snappy_block_size = config::snappy_compression_block_size;
815
213
        size_t overhead = snappy_block_size / 6 + 32;
816
213
        size_t max_input_size = snappy_block_size - overhead;
817
818
213
        size_t data_len = input.size;
819
213
        char* data = input.data;
820
213
        std::vector<OwnedSlice> buffers;
821
213
        size_t out_len = 0;
822
823
492
        while (data_len > 0) {
824
279
            size_t input_size = std::min(data_len, max_input_size);
825
279
            Slice input_slice(data, input_size);
826
279
            faststring output_data;
827
279
            RETURN_IF_ERROR(SnappyBlockCompression::compress(input_slice, &output_data));
828
279
            out_len += output_data.size();
829
            // the OwnedSlice will be moved here
830
279
            buffers.push_back(output_data.build());
831
279
            data += input_size;
832
279
            data_len -= input_size;
833
279
        }
834
835
        // hadoop block compression: umcompressed_length | compressed_length1 | compressed_data1 | compressed_length2 | compressed_data2 | ...
836
213
        size_t total_output_len = 4 + 4 * buffers.size() + out_len;
837
213
        output->resize(total_output_len);
838
213
        char* output_buffer = (char*)output->data();
839
213
        BigEndian::Store32(output_buffer, cast_set<uint32_t>(input.get_size()));
840
213
        output_buffer += 4;
841
279
        for (const auto& buffer : buffers) {
842
279
            auto slice = buffer.slice();
843
279
            BigEndian::Store32(output_buffer, cast_set<uint32_t>(slice.get_size()));
844
279
            output_buffer += 4;
845
279
            memcpy(output_buffer, slice.get_data(), slice.get_size());
846
279
            output_buffer += slice.get_size();
847
279
        }
848
849
213
        DCHECK_EQ(output_buffer - (char*)output->data(), total_output_len);
850
851
213
        return Status::OK();
852
213
    }
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
48
    static ZlibBlockCompression* instance() {
862
48
        static ZlibBlockCompression s_instance;
863
48
        return &s_instance;
864
48
    }
865
    ~ZlibBlockCompression() override = default;
866
867
21
    Status compress(const Slice& input, faststring* output) override {
868
21
        size_t max_len = max_compressed_len(input.size);
869
21
        output->resize(max_len);
870
21
        Slice s(*output);
871
872
21
        auto zres = ::compress((Bytef*)s.data, &s.size, (Bytef*)input.data, input.size);
873
21
        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
21
        } else if (zres != Z_OK) {
878
0
            return Status::InternalError("Fail to do Zlib compress, error={}", zError(zres));
879
0
        }
880
21
        output->resize(s.size);
881
21
        return Status::OK();
882
21
    }
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
34
    Status decompress(const Slice& input, Slice* output) override {
932
34
        size_t input_size = input.size;
933
34
        auto zres =
934
34
                ::uncompress2((Bytef*)output->data, &output->size, (Bytef*)input.data, &input_size);
935
34
        if (zres == Z_DATA_ERROR) {
936
4
            return Status::InvalidArgument("Fail to do ZLib decompress, error={}", zError(zres));
937
30
        } else if (zres == Z_MEM_ERROR) {
938
0
            throw Exception(Status::MemoryLimitExceeded("Fail to do ZLib decompress, error={}",
939
0
                                                        zError(zres)));
940
30
        } else if (zres != Z_OK) {
941
7
            return Status::InternalError("Fail to do ZLib decompress, error={}", zError(zres));
942
7
        }
943
23
        return Status::OK();
944
34
    }
945
946
22
    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
22
        return len + 6 + 5 * ((len >> 14) + 1);
949
22
    }
950
};
951
952
class Bzip2BlockCompression : public BlockCompressionCodec {
953
public:
954
138
    static Bzip2BlockCompression* instance() {
955
138
        static Bzip2BlockCompression s_instance;
956
138
        return &s_instance;
957
138
    }
958
    ~Bzip2BlockCompression() override = default;
959
960
213
    Status compress(const Slice& input, faststring* output) override {
961
213
        size_t max_len = max_compressed_len(input.size);
962
213
        output->resize(max_len);
963
213
        auto size = cast_set<uint32_t>(output->size());
964
213
        auto bzres = BZ2_bzBuffToBuffCompress((char*)output->data(), &size, (char*)input.data,
965
213
                                              cast_set<uint32_t>(input.size), 9, 0, 0);
966
213
        if (bzres == BZ_MEM_ERROR) {
967
0
            throw Exception(
968
0
                    Status::MemoryLimitExceeded("Fail to do Bzip2 compress, ret={}", bzres));
969
213
        } else if (bzres == BZ_PARAM_ERROR) {
970
0
            return Status::InvalidArgument("Fail to do Bzip2 compress, ret={}", bzres);
971
213
        } else if (bzres != BZ_RUN_OK && bzres != BZ_FLUSH_OK && bzres != BZ_FINISH_OK &&
972
213
                   bzres != BZ_STREAM_END && bzres != BZ_OK) {
973
0
            return Status::InternalError("Failed to init bz2. status code: {}", bzres);
974
0
        }
975
213
        output->resize(size);
976
213
        return Status::OK();
977
213
    }
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
213
    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
213
        return len * 2 + 50;
1036
213
    }
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
30
        CContext() : ctx(nullptr) {
1047
30
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
1048
30
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
1049
30
            buffer = std::make_unique<faststring>();
1050
30
        }
1051
        ZSTD_CCtx* ctx;
1052
        std::unique_ptr<faststring> buffer;
1053
5
        ~CContext() {
1054
5
            SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
1055
5
                    ExecEnv::GetInstance()->block_compression_mem_tracker());
1056
5
            if (ctx) {
1057
5
                ZSTD_freeCCtx(ctx);
1058
5
            }
1059
5
            buffer.reset();
1060
5
        }
1061
    };
1062
    class DContext {
1063
        ENABLE_FACTORY_CREATOR(DContext);
1064
1065
    public:
1066
44
        DContext() : ctx(nullptr) {}
1067
        ZSTD_DCtx* ctx;
1068
21
        ~DContext() {
1069
21
            if (ctx) {
1070
21
                ZSTD_freeDCtx(ctx);
1071
21
            }
1072
21
        }
1073
    };
1074
1075
public:
1076
21.7M
    static ZstdBlockCompression* instance() {
1077
21.7M
        static ZstdBlockCompression s_instance;
1078
21.7M
        return &s_instance;
1079
21.7M
    }
1080
3
    ~ZstdBlockCompression() {
1081
3
        SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
1082
3
                ExecEnv::GetInstance()->block_compression_mem_tracker());
1083
3
        _ctx_c_pool.clear();
1084
3
        _ctx_d_pool.clear();
1085
3
    }
1086
1087
373k
    size_t max_compressed_len(size_t len) override { return ZSTD_compressBound(len); }
1088
1089
787
    Status compress(const Slice& input, faststring* output) override {
1090
787
        std::vector<Slice> inputs {input};
1091
787
        return compress(inputs, input.size, output);
1092
787
    }
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
372k
                    faststring* output) override {
1098
372k
        std::unique_ptr<CContext> context;
1099
372k
        RETURN_IF_ERROR(_acquire_compression_ctx(context));
1100
372k
        bool compress_failed = false;
1101
373k
        Defer defer {[&] {
1102
373k
            if (!compress_failed) {
1103
373k
                _release_compression_ctx(std::move(context));
1104
373k
            }
1105
373k
        }};
1106
1107
372k
        try {
1108
372k
            size_t max_len = max_compressed_len(uncompressed_size);
1109
372k
            Slice compressed_buf;
1110
372k
            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
372k
            } else {
1116
372k
                {
1117
372k
                    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
1118
372k
                            ExecEnv::GetInstance()->block_compression_mem_tracker());
1119
                    // reuse context buffer if max_len <= MAX_COMPRESSION_BUFFER_FOR_REUSE
1120
372k
                    context->buffer->resize(max_len);
1121
372k
                }
1122
372k
                compressed_buf.data = reinterpret_cast<char*>(context->buffer->data());
1123
372k
                compressed_buf.size = max_len;
1124
372k
            }
1125
1126
            // set compression level to default 3
1127
372k
            auto ret = ZSTD_CCtx_setParameter(context->ctx, ZSTD_c_compressionLevel,
1128
372k
                                              ZSTD_CLEVEL_DEFAULT);
1129
372k
            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
372k
            ret = ZSTD_CCtx_setParameter(context->ctx, ZSTD_c_checksumFlag, 1);
1135
372k
            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
372k
            ZSTD_outBuffer out_buf = {compressed_buf.data, compressed_buf.size, 0};
1141
1142
785k
            for (size_t i = 0; i < inputs.size(); i++) {
1143
412k
                ZSTD_inBuffer in_buf = {inputs[i].data, inputs[i].size, 0};
1144
1145
412k
                bool last_input = (i == inputs.size() - 1);
1146
412k
                auto mode = last_input ? ZSTD_e_end : ZSTD_e_continue;
1147
1148
412k
                bool finished = false;
1149
412k
                do {
1150
                    // do compress
1151
412k
                    ret = ZSTD_compressStream2(context->ctx, &out_buf, &in_buf, mode);
1152
1153
412k
                    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
412k
                    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
412k
                    finished = last_input ? (ret == 0) : (in_buf.pos == inputs[i].size);
1166
412k
                } while (!finished);
1167
412k
            }
1168
1169
            // set compressed size for caller
1170
372k
            output->resize(out_buf.pos);
1171
373k
            if (max_len <= MAX_COMPRESSION_BUFFER_SIZE_FOR_REUSE) {
1172
373k
                output->assign_copy(reinterpret_cast<uint8_t*>(compressed_buf.data), out_buf.pos);
1173
373k
            }
1174
372k
        } 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
373k
        return Status::OK();
1183
372k
    }
1184
1185
157k
    Status decompress(const Slice& input, Slice* output) override {
1186
157k
        std::unique_ptr<DContext> context;
1187
157k
        bool decompress_failed = false;
1188
157k
        RETURN_IF_ERROR(_acquire_decompression_ctx(context));
1189
157k
        Defer defer {[&] {
1190
157k
            if (!decompress_failed) {
1191
157k
                _release_decompression_ctx(std::move(context));
1192
157k
            }
1193
157k
        }};
1194
1195
157k
        size_t ret = ZSTD_decompressDCtx(context->ctx, output->data, output->size, input.data,
1196
157k
                                         input.size);
1197
157k
        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
157k
        output->size = ret;
1205
1206
157k
        return Status::OK();
1207
157k
    }
1208
1209
private:
1210
372k
    Status _acquire_compression_ctx(std::unique_ptr<CContext>& out) {
1211
372k
        std::lock_guard<std::mutex> l(_ctx_c_mutex);
1212
372k
        if (_ctx_c_pool.empty()) {
1213
30
            std::unique_ptr<CContext> localCtx = CContext::create_unique();
1214
30
            if (localCtx.get() == nullptr) {
1215
0
                return Status::InvalidArgument("failed to new ZSTD CContext");
1216
0
            }
1217
            //typedef LZ4F_cctx* LZ4F_compressionContext_t;
1218
30
            localCtx->ctx = ZSTD_createCCtx();
1219
30
            if (localCtx->ctx == nullptr) {
1220
0
                return Status::InvalidArgument("Failed to create ZSTD compress ctx");
1221
0
            }
1222
30
            out = std::move(localCtx);
1223
30
            return Status::OK();
1224
30
        }
1225
372k
        out = std::move(_ctx_c_pool.back());
1226
372k
        _ctx_c_pool.pop_back();
1227
372k
        return Status::OK();
1228
372k
    }
1229
373k
    void _release_compression_ctx(std::unique_ptr<CContext> context) {
1230
373k
        DCHECK(context);
1231
373k
        auto ret = ZSTD_CCtx_reset(context->ctx, ZSTD_reset_session_only);
1232
373k
        DCHECK(!ZSTD_isError(ret));
1233
373k
        std::lock_guard<std::mutex> l(_ctx_c_mutex);
1234
373k
        _ctx_c_pool.push_back(std::move(context));
1235
373k
    }
1236
1237
157k
    Status _acquire_decompression_ctx(std::unique_ptr<DContext>& out) {
1238
157k
        std::lock_guard<std::mutex> l(_ctx_d_mutex);
1239
157k
        if (_ctx_d_pool.empty()) {
1240
44
            std::unique_ptr<DContext> localCtx = DContext::create_unique();
1241
44
            if (localCtx.get() == nullptr) {
1242
0
                return Status::InvalidArgument("failed to new ZSTD DContext");
1243
0
            }
1244
44
            localCtx->ctx = ZSTD_createDCtx();
1245
44
            if (localCtx->ctx == nullptr) {
1246
0
                return Status::InvalidArgument("Fail to init ZSTD decompress context");
1247
0
            }
1248
44
            out = std::move(localCtx);
1249
44
            return Status::OK();
1250
44
        }
1251
157k
        out = std::move(_ctx_d_pool.back());
1252
157k
        _ctx_d_pool.pop_back();
1253
157k
        return Status::OK();
1254
157k
    }
1255
157k
    void _release_decompression_ctx(std::unique_ptr<DContext> context) {
1256
157k
        DCHECK(context);
1257
        // reset ctx to start a new decompress session
1258
157k
        auto ret = ZSTD_DCtx_reset(context->ctx, ZSTD_reset_session_only);
1259
157k
        DCHECK(!ZSTD_isError(ret));
1260
157k
        std::lock_guard<std::mutex> l(_ctx_d_mutex);
1261
157k
        _ctx_d_pool.push_back(std::move(context));
1262
157k
    }
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
143
    static GzipBlockCompression* instance() {
1275
143
        static GzipBlockCompression s_instance;
1276
143
        return &s_instance;
1277
143
    }
1278
    ~GzipBlockCompression() override = default;
1279
1280
234
    Status compress(const Slice& input, faststring* output) override {
1281
234
        size_t max_len = max_compressed_len(input.size);
1282
234
        output->resize(max_len);
1283
1284
234
        z_stream z_strm = {};
1285
234
        z_strm.zalloc = Z_NULL;
1286
234
        z_strm.zfree = Z_NULL;
1287
234
        z_strm.opaque = Z_NULL;
1288
1289
234
        int zres = deflateInit2(&z_strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + GZIP_CODEC,
1290
234
                                8, Z_DEFAULT_STRATEGY);
1291
1292
234
        if (zres == Z_MEM_ERROR) {
1293
0
            throw Exception(Status::MemoryLimitExceeded(
1294
0
                    "Fail to init ZLib compress, error={}, res={}", zError(zres), zres));
1295
234
        } 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
234
        z_strm.next_in = (Bytef*)input.get_data();
1301
234
        z_strm.avail_in = cast_set<decltype(z_strm.avail_in)>(input.get_size());
1302
234
        z_strm.next_out = (Bytef*)output->data();
1303
234
        z_strm.avail_out = cast_set<decltype(z_strm.avail_out)>(output->size());
1304
1305
234
        zres = deflate(&z_strm, Z_FINISH);
1306
234
        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
234
        output->resize(z_strm.total_out);
1312
234
        zres = deflateEnd(&z_strm);
1313
234
        if (zres == Z_DATA_ERROR) {
1314
0
            return Status::InvalidArgument("Fail to end zlib compress");
1315
234
        } else if (zres != Z_OK) {
1316
0
            return Status::InternalError("Fail to end zlib compress");
1317
0
        }
1318
234
        return Status::OK();
1319
234
    }
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
234
    size_t max_compressed_len(size_t len) override {
1410
234
        z_stream zstrm;
1411
234
        zstrm.zalloc = Z_NULL;
1412
234
        zstrm.zfree = Z_NULL;
1413
234
        zstrm.opaque = Z_NULL;
1414
234
        auto zres = deflateInit2(&zstrm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, MAX_WBITS + GZIP_CODEC,
1415
234
                                 MEM_LEVEL, Z_DEFAULT_STRATEGY);
1416
234
        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
234
        } else {
1423
234
            zres = deflateEnd(&zstrm);
1424
234
            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
234
            auto upper_bound = deflateBound(&zstrm, len) + 32;
1434
234
            return upper_bound;
1435
234
        }
1436
234
    }
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
3
    GzipBlockCompressionByLibdeflate() : GzipBlockCompression() {}
1452
10.0k
    static GzipBlockCompressionByLibdeflate* instance() {
1453
10.0k
        static GzipBlockCompressionByLibdeflate s_instance;
1454
10.0k
        return &s_instance;
1455
10.0k
    }
1456
    ~GzipBlockCompressionByLibdeflate() override = default;
1457
1458
7.90k
    Status decompress(const Slice& input, Slice* output) override {
1459
7.90k
        if (input.empty()) {
1460
0
            output->size = 0;
1461
0
            return Status::OK();
1462
0
        }
1463
7.90k
        thread_local std::unique_ptr<libdeflate_decompressor, void (*)(libdeflate_decompressor*)>
1464
7.90k
                decompressor {libdeflate_alloc_decompressor(), libdeflate_free_decompressor};
1465
7.90k
        if (!decompressor) {
1466
0
            return Status::InternalError("libdeflate_alloc_decompressor error.");
1467
0
        }
1468
7.90k
        std::size_t out_len;
1469
7.90k
        auto result = libdeflate_gzip_decompress(decompressor.get(), input.data, input.size,
1470
7.90k
                                                 output->data, output->size, &out_len);
1471
7.90k
        if (result != LIBDEFLATE_SUCCESS) {
1472
0
            return Status::InternalError("libdeflate_gzip_decompress error, res={}", result);
1473
0
        }
1474
7.90k
        return Status::OK();
1475
7.90k
    }
1476
};
1477
#endif
1478
1479
class LzoBlockCompression final : public BlockCompressionCodec {
1480
public:
1481
418
    static LzoBlockCompression* instance() {
1482
418
        static LzoBlockCompression s_instance;
1483
418
        return &s_instance;
1484
418
    }
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
92
    Status decompress(const Slice& input, Slice* output) override {
1491
92
        auto* input_ptr = input.data;
1492
92
        auto remain_input_size = input.size;
1493
92
        auto* output_ptr = output->data;
1494
92
        auto remain_output_size = output->size;
1495
92
        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
92
        try {
1512
184
            while (remain_input_size > 0) {
1513
92
                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
92
                uint32_t large_block_uncompressed_len = BigEndian::Load32(input_ptr);
1519
92
                input_ptr += 4;
1520
92
                remain_input_size -= 4;
1521
1522
92
                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
184
                while (large_block_uncompressed_len > 0) {
1528
92
                    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
92
                    uint32_t small_block_compressed_len = BigEndian::Load32(input_ptr);
1534
92
                    input_ptr += 4;
1535
92
                    remain_input_size -= 4;
1536
1537
92
                    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
92
                    auto small_block_uncompressed_len =
1543
92
                            orc::lzoDecompress(input_ptr, input_ptr + small_block_compressed_len,
1544
92
                                               output_ptr, output_limit);
1545
1546
92
                    input_ptr += small_block_compressed_len;
1547
92
                    remain_input_size -= small_block_compressed_len;
1548
1549
92
                    output_ptr += small_block_uncompressed_len;
1550
92
                    large_block_uncompressed_len -= small_block_uncompressed_len;
1551
92
                    remain_output_size -= small_block_uncompressed_len;
1552
92
                }
1553
92
            }
1554
92
        } 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
92
        return Status::OK();
1559
92
    }
1560
};
1561
1562
class BrotliBlockCompression final : public BlockCompressionCodec {
1563
public:
1564
32
    static BrotliBlockCompression* instance() {
1565
32
        static BrotliBlockCompression s_instance;
1566
32
        return &s_instance;
1567
32
    }
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
64
    Status decompress(const Slice& input, Slice* output) override {
1576
        // The size of output buffer is always equal to the umcompressed length.
1577
64
        BrotliDecoderResult result = BrotliDecoderDecompress(
1578
64
                input.get_size(), reinterpret_cast<const uint8_t*>(input.get_data()), &output->size,
1579
64
                reinterpret_cast<uint8_t*>(output->data));
1580
64
        if (result != BROTLI_DECODER_RESULT_SUCCESS) {
1581
0
            return Status::InternalError("Brotli decompression failed, result={}", result);
1582
0
        }
1583
64
        return Status::OK();
1584
64
    }
1585
};
1586
1587
Status get_block_compression_codec(segment_v2::CompressionTypePB type,
1588
33.4M
                                   BlockCompressionCodec** codec) {
1589
33.4M
    switch (type) {
1590
11.5M
    case segment_v2::CompressionTypePB::NO_COMPRESSION:
1591
11.5M
        *codec = nullptr;
1592
11.5M
        break;
1593
61.5k
    case segment_v2::CompressionTypePB::SNAPPY:
1594
61.5k
        *codec = SnappyBlockCompression::instance();
1595
61.5k
        break;
1596
69.4k
    case segment_v2::CompressionTypePB::LZ4:
1597
69.4k
        *codec = Lz4BlockCompression::instance();
1598
69.4k
        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
48
    case segment_v2::CompressionTypePB::ZLIB:
1606
48
        *codec = ZlibBlockCompression::instance();
1607
48
        break;
1608
21.7M
    case segment_v2::CompressionTypePB::ZSTD:
1609
21.7M
        *codec = ZstdBlockCompression::instance();
1610
21.7M
        break;
1611
0
    default:
1612
0
        return Status::InternalError("unknown compression type({})", type);
1613
33.4M
    }
1614
1615
33.4M
    return Status::OK();
1616
33.4M
}
1617
1618
// this can only be used in hive text write
1619
1.06k
Status get_block_compression_codec(TFileCompressType::type type, BlockCompressionCodec** codec) {
1620
1.06k
    switch (type) {
1621
332
    case TFileCompressType::PLAIN:
1622
332
        *codec = nullptr;
1623
332
        break;
1624
0
    case TFileCompressType::ZLIB:
1625
0
        *codec = ZlibBlockCompression::instance();
1626
0
        break;
1627
143
    case TFileCompressType::GZ:
1628
143
        *codec = GzipBlockCompression::instance();
1629
143
        break;
1630
138
    case TFileCompressType::BZ2:
1631
138
        *codec = Bzip2BlockCompression::instance();
1632
138
        break;
1633
152
    case TFileCompressType::LZ4BLOCK:
1634
152
        *codec = HadoopLz4BlockCompression::instance();
1635
152
        break;
1636
150
    case TFileCompressType::SNAPPYBLOCK:
1637
150
        *codec = HadoopSnappyBlockCompression::instance();
1638
150
        break;
1639
152
    case TFileCompressType::ZSTD:
1640
152
        *codec = ZstdBlockCompression::instance();
1641
152
        break;
1642
0
    default:
1643
0
        return Status::InternalError("unsupport compression type({}) int hive text", type);
1644
1.06k
    }
1645
1646
1.06k
    return Status::OK();
1647
1.06k
}
1648
1649
Status get_block_compression_codec(tparquet::CompressionCodec::type parquet_codec,
1650
165k
                                   BlockCompressionCodec** codec) {
1651
165k
    switch (parquet_codec) {
1652
18.8k
    case tparquet::CompressionCodec::UNCOMPRESSED:
1653
18.8k
        *codec = nullptr;
1654
18.8k
        break;
1655
64.0k
    case tparquet::CompressionCodec::SNAPPY:
1656
64.0k
        *codec = SnappyBlockCompression::instance();
1657
64.0k
        break;
1658
1.78k
    case tparquet::CompressionCodec::LZ4_RAW: // we can use LZ4 compression algorithm parse LZ4_RAW
1659
2.09k
    case tparquet::CompressionCodec::LZ4:
1660
2.09k
        *codec = HadoopLz4BlockCompression::instance();
1661
2.09k
        break;
1662
69.9k
    case tparquet::CompressionCodec::ZSTD:
1663
69.9k
        *codec = ZstdBlockCompression::instance();
1664
69.9k
        break;
1665
10.0k
    case tparquet::CompressionCodec::GZIP:
1666
// Only used on x86 or x86_64
1667
10.0k
#if defined(__x86_64__) || defined(_M_X64) || defined(i386) || defined(__i386__) || \
1668
10.0k
        defined(__i386) || defined(_M_IX86)
1669
10.0k
        *codec = GzipBlockCompressionByLibdeflate::instance();
1670
#else
1671
        *codec = GzipBlockCompression::instance();
1672
#endif
1673
10.0k
        break;
1674
418
    case tparquet::CompressionCodec::LZO:
1675
418
        *codec = LzoBlockCompression::instance();
1676
418
        break;
1677
32
    case tparquet::CompressionCodec::BROTLI:
1678
32
        *codec = BrotliBlockCompression::instance();
1679
32
        break;
1680
0
    default:
1681
0
        return Status::InternalError("unknown compression type({})", parquet_codec);
1682
165k
    }
1683
1684
165k
    return Status::OK();
1685
165k
}
1686
1687
} // namespace doris