1*288bf522SAndroid Build Coastguard Worker /*
2*288bf522SAndroid Build Coastguard Worker * Copyright (C) 2024 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker *
4*288bf522SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker *
8*288bf522SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker *
10*288bf522SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker * limitations under the License.
15*288bf522SAndroid Build Coastguard Worker */
16*288bf522SAndroid Build Coastguard Worker
17*288bf522SAndroid Build Coastguard Worker #include "ZstdUtil.h"
18*288bf522SAndroid Build Coastguard Worker
19*288bf522SAndroid Build Coastguard Worker #include <android-base/logging.h>
20*288bf522SAndroid Build Coastguard Worker #include <zstd.h>
21*288bf522SAndroid Build Coastguard Worker
22*288bf522SAndroid Build Coastguard Worker namespace simpleperf {
23*288bf522SAndroid Build Coastguard Worker
24*288bf522SAndroid Build Coastguard Worker namespace {
25*288bf522SAndroid Build Coastguard Worker
26*288bf522SAndroid Build Coastguard Worker class CompressionOutBuffer {
27*288bf522SAndroid Build Coastguard Worker public:
CompressionOutBuffer(size_t min_free_size)28*288bf522SAndroid Build Coastguard Worker CompressionOutBuffer(size_t min_free_size)
29*288bf522SAndroid Build Coastguard Worker : min_free_size_(min_free_size), buffer_(min_free_size) {}
30*288bf522SAndroid Build Coastguard Worker
DataStart() const31*288bf522SAndroid Build Coastguard Worker const char* DataStart() const { return buffer_.data() + data_pos_; }
DataSize() const32*288bf522SAndroid Build Coastguard Worker size_t DataSize() const { return data_size_; }
FreeStart()33*288bf522SAndroid Build Coastguard Worker char* FreeStart() { return buffer_.data() + data_pos_ + data_size_; }
FreeSize() const34*288bf522SAndroid Build Coastguard Worker size_t FreeSize() const { return buffer_.size() - data_pos_ - data_size_; }
35*288bf522SAndroid Build Coastguard Worker
PrepareForInput()36*288bf522SAndroid Build Coastguard Worker void PrepareForInput() {
37*288bf522SAndroid Build Coastguard Worker if (data_pos_ > 0) {
38*288bf522SAndroid Build Coastguard Worker if (data_size_ == 0) {
39*288bf522SAndroid Build Coastguard Worker data_pos_ = 0;
40*288bf522SAndroid Build Coastguard Worker } else {
41*288bf522SAndroid Build Coastguard Worker memmove(buffer_.data(), buffer_.data() + data_pos_, data_size_);
42*288bf522SAndroid Build Coastguard Worker data_pos_ = 0;
43*288bf522SAndroid Build Coastguard Worker }
44*288bf522SAndroid Build Coastguard Worker }
45*288bf522SAndroid Build Coastguard Worker if (FreeSize() < min_free_size_) {
46*288bf522SAndroid Build Coastguard Worker buffer_.resize(buffer_.size() * 2);
47*288bf522SAndroid Build Coastguard Worker }
48*288bf522SAndroid Build Coastguard Worker }
49*288bf522SAndroid Build Coastguard Worker
ProduceData(size_t size)50*288bf522SAndroid Build Coastguard Worker void ProduceData(size_t size) {
51*288bf522SAndroid Build Coastguard Worker data_size_ += size;
52*288bf522SAndroid Build Coastguard Worker CHECK_LE(data_pos_ + data_size_, buffer_.size());
53*288bf522SAndroid Build Coastguard Worker }
54*288bf522SAndroid Build Coastguard Worker
ConsumeData(size_t size)55*288bf522SAndroid Build Coastguard Worker void ConsumeData(size_t size) {
56*288bf522SAndroid Build Coastguard Worker CHECK_LE(size, data_size_);
57*288bf522SAndroid Build Coastguard Worker data_pos_ += size;
58*288bf522SAndroid Build Coastguard Worker data_size_ -= size;
59*288bf522SAndroid Build Coastguard Worker }
60*288bf522SAndroid Build Coastguard Worker
61*288bf522SAndroid Build Coastguard Worker private:
62*288bf522SAndroid Build Coastguard Worker const size_t min_free_size_;
63*288bf522SAndroid Build Coastguard Worker std::vector<char> buffer_;
64*288bf522SAndroid Build Coastguard Worker size_t data_pos_ = 0;
65*288bf522SAndroid Build Coastguard Worker size_t data_size_ = 0;
66*288bf522SAndroid Build Coastguard Worker };
67*288bf522SAndroid Build Coastguard Worker
68*288bf522SAndroid Build Coastguard Worker using ZSTD_CCtx_pointer = std::unique_ptr<ZSTD_CCtx, decltype(&ZSTD_freeCCtx)>;
69*288bf522SAndroid Build Coastguard Worker
70*288bf522SAndroid Build Coastguard Worker class ZstdCompressor : public Compressor {
71*288bf522SAndroid Build Coastguard Worker public:
ZstdCompressor(ZSTD_CCtx_pointer cctx)72*288bf522SAndroid Build Coastguard Worker ZstdCompressor(ZSTD_CCtx_pointer cctx)
73*288bf522SAndroid Build Coastguard Worker : cctx_(std::move(cctx)), out_buffer_(ZSTD_CStreamOutSize()) {}
74*288bf522SAndroid Build Coastguard Worker
AddInputData(const char * data,size_t size)75*288bf522SAndroid Build Coastguard Worker bool AddInputData(const char* data, size_t size) override {
76*288bf522SAndroid Build Coastguard Worker ZSTD_inBuffer input = {data, size, 0};
77*288bf522SAndroid Build Coastguard Worker while (input.pos < input.size) {
78*288bf522SAndroid Build Coastguard Worker out_buffer_.PrepareForInput();
79*288bf522SAndroid Build Coastguard Worker ZSTD_outBuffer output = {out_buffer_.FreeStart(), out_buffer_.FreeSize(), 0};
80*288bf522SAndroid Build Coastguard Worker size_t remaining = ZSTD_compressStream2(cctx_.get(), &output, &input, ZSTD_e_continue);
81*288bf522SAndroid Build Coastguard Worker if (ZSTD_isError(remaining)) {
82*288bf522SAndroid Build Coastguard Worker LOG(ERROR) << "ZSTD_compressStream2() failed: " << ZSTD_getErrorName(remaining);
83*288bf522SAndroid Build Coastguard Worker return false;
84*288bf522SAndroid Build Coastguard Worker }
85*288bf522SAndroid Build Coastguard Worker out_buffer_.ProduceData(output.pos);
86*288bf522SAndroid Build Coastguard Worker total_output_size_ += output.pos;
87*288bf522SAndroid Build Coastguard Worker }
88*288bf522SAndroid Build Coastguard Worker total_input_size_ += size;
89*288bf522SAndroid Build Coastguard Worker return true;
90*288bf522SAndroid Build Coastguard Worker }
91*288bf522SAndroid Build Coastguard Worker
FlushOutputData()92*288bf522SAndroid Build Coastguard Worker bool FlushOutputData() override {
93*288bf522SAndroid Build Coastguard Worker if (flushed_input_size_ == total_input_size_) {
94*288bf522SAndroid Build Coastguard Worker return true;
95*288bf522SAndroid Build Coastguard Worker }
96*288bf522SAndroid Build Coastguard Worker flushed_input_size_ = total_input_size_;
97*288bf522SAndroid Build Coastguard Worker ZSTD_inBuffer input = {nullptr, 0, 0};
98*288bf522SAndroid Build Coastguard Worker size_t remaining = 0;
99*288bf522SAndroid Build Coastguard Worker do {
100*288bf522SAndroid Build Coastguard Worker out_buffer_.PrepareForInput();
101*288bf522SAndroid Build Coastguard Worker ZSTD_outBuffer output = {out_buffer_.FreeStart(), out_buffer_.FreeSize(), 0};
102*288bf522SAndroid Build Coastguard Worker remaining = ZSTD_compressStream2(cctx_.get(), &output, &input, ZSTD_e_end);
103*288bf522SAndroid Build Coastguard Worker if (ZSTD_isError(remaining)) {
104*288bf522SAndroid Build Coastguard Worker LOG(ERROR) << "ZSTD_compressStream2() failed: " << ZSTD_getErrorName(remaining);
105*288bf522SAndroid Build Coastguard Worker return false;
106*288bf522SAndroid Build Coastguard Worker }
107*288bf522SAndroid Build Coastguard Worker out_buffer_.ProduceData(output.pos);
108*288bf522SAndroid Build Coastguard Worker total_output_size_ += output.pos;
109*288bf522SAndroid Build Coastguard Worker } while (remaining != 0);
110*288bf522SAndroid Build Coastguard Worker return true;
111*288bf522SAndroid Build Coastguard Worker }
112*288bf522SAndroid Build Coastguard Worker
GetOutputData()113*288bf522SAndroid Build Coastguard Worker std::string_view GetOutputData() override {
114*288bf522SAndroid Build Coastguard Worker return std::string_view(out_buffer_.DataStart(), out_buffer_.DataSize());
115*288bf522SAndroid Build Coastguard Worker }
116*288bf522SAndroid Build Coastguard Worker
ConsumeOutputData(size_t size)117*288bf522SAndroid Build Coastguard Worker void ConsumeOutputData(size_t size) override { out_buffer_.ConsumeData(size); }
118*288bf522SAndroid Build Coastguard Worker
119*288bf522SAndroid Build Coastguard Worker private:
120*288bf522SAndroid Build Coastguard Worker ZSTD_CCtx_pointer cctx_;
121*288bf522SAndroid Build Coastguard Worker CompressionOutBuffer out_buffer_;
122*288bf522SAndroid Build Coastguard Worker uint64_t flushed_input_size_ = 0;
123*288bf522SAndroid Build Coastguard Worker };
124*288bf522SAndroid Build Coastguard Worker
125*288bf522SAndroid Build Coastguard Worker using ZSTD_DCtx_pointer = std::unique_ptr<ZSTD_DCtx, decltype(&ZSTD_freeDCtx)>;
126*288bf522SAndroid Build Coastguard Worker
127*288bf522SAndroid Build Coastguard Worker class ZstdDecompressor : public Decompressor {
128*288bf522SAndroid Build Coastguard Worker public:
ZstdDecompressor(ZSTD_DCtx_pointer dctx)129*288bf522SAndroid Build Coastguard Worker ZstdDecompressor(ZSTD_DCtx_pointer dctx)
130*288bf522SAndroid Build Coastguard Worker : dctx_(std::move(dctx)), out_buffer_(ZSTD_DStreamOutSize()) {}
131*288bf522SAndroid Build Coastguard Worker
AddInputData(const char * data,size_t size)132*288bf522SAndroid Build Coastguard Worker bool AddInputData(const char* data, size_t size) override {
133*288bf522SAndroid Build Coastguard Worker ZSTD_inBuffer input = {data, size, 0};
134*288bf522SAndroid Build Coastguard Worker while (input.pos < input.size) {
135*288bf522SAndroid Build Coastguard Worker out_buffer_.PrepareForInput();
136*288bf522SAndroid Build Coastguard Worker ZSTD_outBuffer output = {out_buffer_.FreeStart(), out_buffer_.FreeSize(), 0};
137*288bf522SAndroid Build Coastguard Worker size_t remaining = ZSTD_decompressStream(dctx_.get(), &output, &input);
138*288bf522SAndroid Build Coastguard Worker if (ZSTD_isError(remaining)) {
139*288bf522SAndroid Build Coastguard Worker LOG(ERROR) << "ZSTD_decompressStream() failed: " << ZSTD_getErrorName(remaining);
140*288bf522SAndroid Build Coastguard Worker return false;
141*288bf522SAndroid Build Coastguard Worker }
142*288bf522SAndroid Build Coastguard Worker out_buffer_.ProduceData(output.pos);
143*288bf522SAndroid Build Coastguard Worker }
144*288bf522SAndroid Build Coastguard Worker return true;
145*288bf522SAndroid Build Coastguard Worker }
146*288bf522SAndroid Build Coastguard Worker
GetOutputData()147*288bf522SAndroid Build Coastguard Worker std::string_view GetOutputData() override {
148*288bf522SAndroid Build Coastguard Worker return std::string_view(out_buffer_.DataStart(), out_buffer_.DataSize());
149*288bf522SAndroid Build Coastguard Worker }
150*288bf522SAndroid Build Coastguard Worker
ConsumeOutputData(size_t size)151*288bf522SAndroid Build Coastguard Worker void ConsumeOutputData(size_t size) override { out_buffer_.ConsumeData(size); }
152*288bf522SAndroid Build Coastguard Worker
153*288bf522SAndroid Build Coastguard Worker private:
154*288bf522SAndroid Build Coastguard Worker ZSTD_DCtx_pointer dctx_;
155*288bf522SAndroid Build Coastguard Worker CompressionOutBuffer out_buffer_;
156*288bf522SAndroid Build Coastguard Worker };
157*288bf522SAndroid Build Coastguard Worker
158*288bf522SAndroid Build Coastguard Worker } // namespace
159*288bf522SAndroid Build Coastguard Worker
~Compressor()160*288bf522SAndroid Build Coastguard Worker Compressor::~Compressor() {}
161*288bf522SAndroid Build Coastguard Worker
~Decompressor()162*288bf522SAndroid Build Coastguard Worker Decompressor::~Decompressor() {}
163*288bf522SAndroid Build Coastguard Worker
CreateZstdCompressor(size_t compression_level)164*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Compressor> CreateZstdCompressor(size_t compression_level) {
165*288bf522SAndroid Build Coastguard Worker ZSTD_CCtx_pointer cctx(ZSTD_createCCtx(), ZSTD_freeCCtx);
166*288bf522SAndroid Build Coastguard Worker if (!cctx) {
167*288bf522SAndroid Build Coastguard Worker LOG(ERROR) << "ZSTD_createCCtx() failed";
168*288bf522SAndroid Build Coastguard Worker return nullptr;
169*288bf522SAndroid Build Coastguard Worker }
170*288bf522SAndroid Build Coastguard Worker size_t err = ZSTD_CCtx_setParameter(cctx.get(), ZSTD_c_compressionLevel, compression_level);
171*288bf522SAndroid Build Coastguard Worker if (ZSTD_isError(err)) {
172*288bf522SAndroid Build Coastguard Worker LOG(ERROR) << "failed to set compression level: " << ZSTD_getErrorName(err);
173*288bf522SAndroid Build Coastguard Worker return nullptr;
174*288bf522SAndroid Build Coastguard Worker }
175*288bf522SAndroid Build Coastguard Worker return std::unique_ptr<Compressor>(new ZstdCompressor(std::move(cctx)));
176*288bf522SAndroid Build Coastguard Worker }
177*288bf522SAndroid Build Coastguard Worker
CreateZstdDecompressor()178*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Decompressor> CreateZstdDecompressor() {
179*288bf522SAndroid Build Coastguard Worker ZSTD_DCtx_pointer dctx(ZSTD_createDCtx(), ZSTD_freeDCtx);
180*288bf522SAndroid Build Coastguard Worker if (!dctx) {
181*288bf522SAndroid Build Coastguard Worker LOG(ERROR) << "ZSTD_createDCtx() failed";
182*288bf522SAndroid Build Coastguard Worker return nullptr;
183*288bf522SAndroid Build Coastguard Worker }
184*288bf522SAndroid Build Coastguard Worker return std::unique_ptr<Decompressor>(new ZstdDecompressor(std::move(dctx)));
185*288bf522SAndroid Build Coastguard Worker }
ZstdCompress(const char * input_data,size_t input_size,std::string & output_data)186*288bf522SAndroid Build Coastguard Worker bool ZstdCompress(const char* input_data, size_t input_size, std::string& output_data) {
187*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Compressor> compressor = CreateZstdCompressor();
188*288bf522SAndroid Build Coastguard Worker CHECK(compressor != nullptr);
189*288bf522SAndroid Build Coastguard Worker if (!compressor->AddInputData(input_data, input_size)) {
190*288bf522SAndroid Build Coastguard Worker return false;
191*288bf522SAndroid Build Coastguard Worker }
192*288bf522SAndroid Build Coastguard Worker if (!compressor->FlushOutputData()) {
193*288bf522SAndroid Build Coastguard Worker return false;
194*288bf522SAndroid Build Coastguard Worker }
195*288bf522SAndroid Build Coastguard Worker std::string_view output = compressor->GetOutputData();
196*288bf522SAndroid Build Coastguard Worker output_data.clear();
197*288bf522SAndroid Build Coastguard Worker output_data.insert(0, output.data(), output.size());
198*288bf522SAndroid Build Coastguard Worker return true;
199*288bf522SAndroid Build Coastguard Worker }
200*288bf522SAndroid Build Coastguard Worker
ZstdDecompress(const char * input_data,size_t input_size,std::string & output_data)201*288bf522SAndroid Build Coastguard Worker bool ZstdDecompress(const char* input_data, size_t input_size, std::string& output_data) {
202*288bf522SAndroid Build Coastguard Worker std::unique_ptr<Decompressor> decompressor = CreateZstdDecompressor();
203*288bf522SAndroid Build Coastguard Worker CHECK(decompressor != nullptr);
204*288bf522SAndroid Build Coastguard Worker if (!decompressor->AddInputData(input_data, input_size)) {
205*288bf522SAndroid Build Coastguard Worker return false;
206*288bf522SAndroid Build Coastguard Worker }
207*288bf522SAndroid Build Coastguard Worker std::string_view output = decompressor->GetOutputData();
208*288bf522SAndroid Build Coastguard Worker output_data.clear();
209*288bf522SAndroid Build Coastguard Worker output_data.insert(0, output.data(), output.size());
210*288bf522SAndroid Build Coastguard Worker return true;
211*288bf522SAndroid Build Coastguard Worker }
212*288bf522SAndroid Build Coastguard Worker
213*288bf522SAndroid Build Coastguard Worker } // namespace simpleperf
214