xref: /aosp_15_r20/system/extras/simpleperf/ZstdUtil.cpp (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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