• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <algorithm>
20 #include <memory>
21 #include <span>
22 
23 #include <android-base/logging.h>
24 
25 #include <brotli/decode.h>
26 #include <brotli/encode.h>
27 #include <lz4frame.h>
28 #include <zstd.h>
29 
30 #include "types.h"
31 
32 enum class DecodeResult {
33     Error,
34     Done,
35     NeedInput,
36     MoreOutput,
37 };
38 
39 enum class EncodeResult {
40     Error,
41     Done,
42     NeedInput,
43     MoreOutput,
44 };
45 
46 struct Decoder {
AppendDecoder47     void Append(Block&& block) { input_buffer_.append(std::move(block)); }
FinishDecoder48     bool Finish() {
49         bool old = std::exchange(finished_, true);
50         if (old) {
51             LOG(FATAL) << "Decoder::Finish called while already finished?";
52             return false;
53         }
54         return true;
55     }
56 
57     virtual DecodeResult Decode(std::span<char>* output) = 0;
58 
59   protected:
DecoderDecoder60     Decoder(std::span<char> output_buffer) : output_buffer_(output_buffer) {}
61     ~Decoder() = default;
62 
63     bool finished_ = false;
64     IOVector input_buffer_;
65     std::span<char> output_buffer_;
66 };
67 
68 struct Encoder {
AppendEncoder69     void Append(Block input) { input_buffer_.append(std::move(input)); }
FinishEncoder70     bool Finish() {
71         bool old = std::exchange(finished_, true);
72         if (old) {
73             LOG(FATAL) << "Decoder::Finish called while already finished?";
74             return false;
75         }
76         return true;
77     }
78 
79     virtual EncodeResult Encode(Block* output) = 0;
80 
81   protected:
EncoderEncoder82     explicit Encoder(size_t output_block_size) : output_block_size_(output_block_size) {}
83     ~Encoder() = default;
84 
85     const size_t output_block_size_;
86     bool finished_ = false;
87     IOVector input_buffer_;
88 };
89 
90 struct NullDecoder final : public Decoder {
NullDecoderfinal91     explicit NullDecoder(std::span<char> output_buffer) : Decoder(output_buffer) {}
92 
Decodefinal93     DecodeResult Decode(std::span<char>* output) final {
94         size_t available_out = output_buffer_.size();
95         void* p = output_buffer_.data();
96         while (available_out > 0 && !input_buffer_.empty()) {
97             size_t len = std::min(available_out, input_buffer_.front_size());
98             p = mempcpy(p, input_buffer_.front_data(), len);
99             available_out -= len;
100             input_buffer_.drop_front(len);
101         }
102         *output = std::span(output_buffer_.data(), static_cast<char*>(p));
103         if (input_buffer_.empty()) {
104             return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
105         }
106         return DecodeResult::MoreOutput;
107     }
108 };
109 
110 struct NullEncoder final : public Encoder {
NullEncoderfinal111     explicit NullEncoder(size_t output_block_size) : Encoder(output_block_size) {}
112 
Encodefinal113     EncodeResult Encode(Block* output) final {
114         output->clear();
115         output->resize(output_block_size_);
116 
117         size_t available_out = output->size();
118         void* p = output->data();
119 
120         while (available_out > 0 && !input_buffer_.empty()) {
121             size_t len = std::min(available_out, input_buffer_.front_size());
122             p = mempcpy(p, input_buffer_.front_data(), len);
123             available_out -= len;
124             input_buffer_.drop_front(len);
125         }
126 
127         output->resize(output->size() - available_out);
128 
129         if (input_buffer_.empty()) {
130             return finished_ ? EncodeResult::Done : EncodeResult::NeedInput;
131         }
132         return EncodeResult::MoreOutput;
133     }
134 };
135 
136 struct BrotliDecoder final : public Decoder {
BrotliDecoderfinal137     explicit BrotliDecoder(std::span<char> output_buffer)
138         : Decoder(output_buffer),
139           decoder_(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr),
140                    BrotliDecoderDestroyInstance) {}
141 
Decodefinal142     DecodeResult Decode(std::span<char>* output) final {
143         size_t available_in = input_buffer_.front_size();
144         const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
145 
146         size_t available_out = output_buffer_.size();
147         uint8_t* next_out = reinterpret_cast<uint8_t*>(output_buffer_.data());
148 
149         BrotliDecoderResult r = BrotliDecoderDecompressStream(
150                 decoder_.get(), &available_in, &next_in, &available_out, &next_out, nullptr);
151 
152         size_t bytes_consumed = input_buffer_.front_size() - available_in;
153         input_buffer_.drop_front(bytes_consumed);
154 
155         size_t bytes_emitted = output_buffer_.size() - available_out;
156         *output = std::span<char>(output_buffer_.data(), bytes_emitted);
157 
158         switch (r) {
159             case BROTLI_DECODER_RESULT_SUCCESS:
160                 // We need to wait for ID_DONE from the other end.
161                 return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
162             case BROTLI_DECODER_RESULT_ERROR:
163                 return DecodeResult::Error;
164             case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
165                 // Brotli guarantees as one of its invariants that if it returns NEEDS_MORE_INPUT,
166                 // it will consume the entire input buffer passed in, so we don't have to worry
167                 // about bytes left over in the front block with more input remaining.
168                 return DecodeResult::NeedInput;
169             case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT:
170                 return DecodeResult::MoreOutput;
171         }
172     }
173 
174   private:
175     std::unique_ptr<BrotliDecoderState, void (*)(BrotliDecoderState*)> decoder_;
176 };
177 
178 struct BrotliEncoder final : public Encoder {
BrotliEncoderfinal179     explicit BrotliEncoder(size_t output_block_size)
180         : Encoder(output_block_size),
181           output_block_(output_block_size_),
182           output_bytes_left_(output_block_size_),
183           encoder_(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr),
184                    BrotliEncoderDestroyInstance) {
185         BrotliEncoderSetParameter(encoder_.get(), BROTLI_PARAM_QUALITY, 1);
186     }
187 
Encodefinal188     EncodeResult Encode(Block* output) final {
189         output->clear();
190 
191         while (true) {
192             size_t available_in = input_buffer_.front_size();
193             const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
194 
195             size_t available_out = output_bytes_left_;
196             uint8_t* next_out = reinterpret_cast<uint8_t*>(
197                     output_block_.data() + (output_block_size_ - output_bytes_left_));
198 
199             BrotliEncoderOperation op = BROTLI_OPERATION_PROCESS;
200             if (finished_) {
201                 op = BROTLI_OPERATION_FINISH;
202             }
203 
204             if (!BrotliEncoderCompressStream(encoder_.get(), op, &available_in, &next_in,
205                                              &available_out, &next_out, nullptr)) {
206                 return EncodeResult::Error;
207             }
208 
209             size_t bytes_consumed = input_buffer_.front_size() - available_in;
210             input_buffer_.drop_front(bytes_consumed);
211 
212             output_bytes_left_ = available_out;
213 
214             if (BrotliEncoderIsFinished(encoder_.get())) {
215                 output_block_.resize(output_block_size_ - output_bytes_left_);
216                 *output = std::move(output_block_);
217                 return EncodeResult::Done;
218             } else if (output_bytes_left_ == 0) {
219                 *output = std::move(output_block_);
220                 output_block_.resize(output_block_size_);
221                 output_bytes_left_ = output_block_size_;
222                 return EncodeResult::MoreOutput;
223             } else if (input_buffer_.empty()) {
224                 return EncodeResult::NeedInput;
225             }
226         }
227     }
228 
229   private:
230     Block output_block_;
231     size_t output_bytes_left_;
232     std::unique_ptr<BrotliEncoderState, void (*)(BrotliEncoderState*)> encoder_;
233 };
234 
235 struct LZ4Decoder final : public Decoder {
LZ4Decoderfinal236     explicit LZ4Decoder(std::span<char> output_buffer)
237         : Decoder(output_buffer), decoder_(nullptr, nullptr) {
238         LZ4F_dctx* dctx;
239         if (LZ4F_createDecompressionContext(&dctx, LZ4F_VERSION) != 0) {
240             LOG(FATAL) << "failed to initialize LZ4 decompression context";
241         }
242         decoder_ = std::unique_ptr<LZ4F_dctx, decltype(&LZ4F_freeDecompressionContext)>(
243                 dctx, LZ4F_freeDecompressionContext);
244     }
245 
Decodefinal246     DecodeResult Decode(std::span<char>* output) final {
247         size_t available_in = input_buffer_.front_size();
248         const char* next_in = input_buffer_.front_data();
249 
250         size_t available_out = output_buffer_.size();
251         char* next_out = output_buffer_.data();
252 
253         size_t rc = LZ4F_decompress(decoder_.get(), next_out, &available_out, next_in,
254                                     &available_in, nullptr);
255         if (LZ4F_isError(rc)) {
256             LOG(ERROR) << "LZ4F_decompress failed: " << LZ4F_getErrorName(rc);
257             return DecodeResult::Error;
258         }
259 
260         input_buffer_.drop_front(available_in);
261 
262         if (rc == 0) {
263             if (!input_buffer_.empty()) {
264                 LOG(ERROR) << "LZ4 stream hit end before reading all data";
265                 return DecodeResult::Error;
266             }
267             lz4_done_ = true;
268         }
269 
270         *output = std::span<char>(output_buffer_.data(), available_out);
271 
272         if (finished_) {
273             return input_buffer_.empty() && lz4_done_ ? DecodeResult::Done
274                                                       : DecodeResult::MoreOutput;
275         }
276 
277         return DecodeResult::NeedInput;
278     }
279 
280   private:
281     bool lz4_done_ = false;
282     std::unique_ptr<LZ4F_dctx, LZ4F_errorCode_t (*)(LZ4F_dctx*)> decoder_;
283 };
284 
285 struct LZ4Encoder final : public Encoder {
LZ4Encoderfinal286     explicit LZ4Encoder(size_t output_block_size)
287         : Encoder(output_block_size), encoder_(nullptr, nullptr) {
288         LZ4F_cctx* cctx;
289         if (LZ4F_createCompressionContext(&cctx, LZ4F_VERSION) != 0) {
290             LOG(FATAL) << "failed to initialize LZ4 compression context";
291         }
292         encoder_ = std::unique_ptr<LZ4F_cctx, decltype(&LZ4F_freeCompressionContext)>(
293                 cctx, LZ4F_freeCompressionContext);
294         Block header(LZ4F_HEADER_SIZE_MAX);
295         size_t rc = LZ4F_compressBegin(encoder_.get(), header.data(), header.size(), nullptr);
296         if (LZ4F_isError(rc)) {
297             LOG(FATAL) << "LZ4F_compressBegin failed: %s", LZ4F_getErrorName(rc);
298         }
299         header.resize(rc);
300         output_buffer_.append(std::move(header));
301     }
302 
303     // As an optimization, only emit a block if we have an entire output block ready, or we're done.
OutputReadyfinal304     bool OutputReady() const {
305         return output_buffer_.size() >= output_block_size_ || lz4_finalized_;
306     }
307 
308     // TODO: Switch the output type to IOVector to remove a copy?
Encodefinal309     EncodeResult Encode(Block* output) final {
310         size_t available_in = input_buffer_.front_size();
311         const char* next_in = input_buffer_.front_data();
312 
313         // LZ4 makes no guarantees about being able to recover from trying to compress with an
314         // insufficiently large output buffer. LZ4F_compressBound tells us how much buffer we
315         // need to compress a given number of bytes, but the smallest value seems to be bigger
316         // than SYNC_DATA_MAX, so we need to buffer ourselves.
317 
318         // Input size chosen to be a local maximum for LZ4F_compressBound (i.e. the block size).
319         constexpr size_t max_input_size = 65536;
320         const size_t encode_block_size = LZ4F_compressBound(max_input_size, nullptr);
321 
322         if (available_in != 0) {
323             if (lz4_finalized_) {
324                 LOG(ERROR) << "LZ4Encoder received data after Finish?";
325                 return EncodeResult::Error;
326             }
327 
328             available_in = std::min(available_in, max_input_size);
329 
330             Block encode_block(encode_block_size);
331             size_t available_out = encode_block.capacity();
332             char* next_out = encode_block.data();
333 
334             size_t rc = LZ4F_compressUpdate(encoder_.get(), next_out, available_out, next_in,
335                                             available_in, nullptr);
336             if (LZ4F_isError(rc)) {
337                 LOG(ERROR) << "LZ4F_compressUpdate failed: " << LZ4F_getErrorName(rc);
338                 return EncodeResult::Error;
339             }
340 
341             input_buffer_.drop_front(available_in);
342 
343             available_out -= rc;
344             next_out += rc;
345 
346             encode_block.resize(encode_block_size - available_out);
347             output_buffer_.append(std::move(encode_block));
348         }
349 
350         if (finished_ && !lz4_finalized_) {
351             lz4_finalized_ = true;
352 
353             Block final_block(encode_block_size + 4);
354             size_t rc = LZ4F_compressEnd(encoder_.get(), final_block.data(), final_block.size(),
355                                          nullptr);
356             if (LZ4F_isError(rc)) {
357                 LOG(ERROR) << "LZ4F_compressEnd failed: " << LZ4F_getErrorName(rc);
358                 return EncodeResult::Error;
359             }
360 
361             final_block.resize(rc);
362             output_buffer_.append(std::move(final_block));
363         }
364 
365         if (OutputReady()) {
366             size_t len = std::min(output_block_size_, output_buffer_.size());
367             *output = output_buffer_.take_front(len).coalesce();
368         } else {
369             output->clear();
370         }
371 
372         if (lz4_finalized_ && output_buffer_.empty()) {
373             return EncodeResult::Done;
374         } else if (OutputReady()) {
375             return EncodeResult::MoreOutput;
376         }
377         return EncodeResult::NeedInput;
378     }
379 
380   private:
381     bool lz4_finalized_ = false;
382     std::unique_ptr<LZ4F_cctx, LZ4F_errorCode_t (*)(LZ4F_cctx*)> encoder_;
383     IOVector output_buffer_;
384 };
385 
386 struct ZstdDecoder final : public Decoder {
ZstdDecoderfinal387     explicit ZstdDecoder(std::span<char> output_buffer)
388         : Decoder(output_buffer), decoder_(ZSTD_createDStream(), ZSTD_freeDStream) {
389         if (!decoder_) {
390             LOG(FATAL) << "failed to initialize Zstd decompression context";
391         }
392     }
393 
Decodefinal394     DecodeResult Decode(std::span<char>* output) final {
395         ZSTD_inBuffer in;
396         in.src = input_buffer_.front_data();
397         in.size = input_buffer_.front_size();
398         in.pos = 0;
399 
400         ZSTD_outBuffer out;
401         out.dst = output_buffer_.data();
402         // The standard specifies size() as returning size_t, but our current version of
403         // libc++ returns a signed value instead.
404         out.size = static_cast<size_t>(output_buffer_.size());
405         out.pos = 0;
406 
407         size_t rc = ZSTD_decompressStream(decoder_.get(), &out, &in);
408         if (ZSTD_isError(rc)) {
409             LOG(ERROR) << "ZSTD_decompressStream failed: " << ZSTD_getErrorName(rc);
410             return DecodeResult::Error;
411         }
412 
413         input_buffer_.drop_front(in.pos);
414         if (rc == 0) {
415             if (!input_buffer_.empty()) {
416                 LOG(ERROR) << "Zstd stream hit end before reading all data";
417                 return DecodeResult::Error;
418             }
419             zstd_done_ = true;
420         }
421 
422         *output = std::span<char>(output_buffer_.data(), out.pos);
423 
424         if (finished_) {
425             return input_buffer_.empty() && zstd_done_ ? DecodeResult::Done
426                                                        : DecodeResult::MoreOutput;
427         }
428         return DecodeResult::NeedInput;
429     }
430 
431   private:
432     bool zstd_done_ = false;
433     std::unique_ptr<ZSTD_DStream, size_t (*)(ZSTD_DStream*)> decoder_;
434 };
435 
436 struct ZstdEncoder final : public Encoder {
ZstdEncoderfinal437     explicit ZstdEncoder(size_t output_block_size)
438         : Encoder(output_block_size), encoder_(ZSTD_createCStream(), ZSTD_freeCStream) {
439         if (!encoder_) {
440             LOG(FATAL) << "failed to initialize Zstd compression context";
441         }
442         ZSTD_CCtx_setParameter(encoder_.get(), ZSTD_c_compressionLevel, 1);
443     }
444 
Encodefinal445     EncodeResult Encode(Block* output) final {
446         ZSTD_inBuffer in;
447         in.src = input_buffer_.front_data();
448         in.size = input_buffer_.front_size();
449         in.pos = 0;
450 
451         output->resize(output_block_size_);
452 
453         ZSTD_outBuffer out;
454         out.dst = output->data();
455         out.size = static_cast<size_t>(output->size());
456         out.pos = 0;
457 
458         ZSTD_EndDirective end_directive = finished_ ? ZSTD_e_end : ZSTD_e_continue;
459         size_t rc = ZSTD_compressStream2(encoder_.get(), &out, &in, end_directive);
460         if (ZSTD_isError(rc)) {
461             LOG(ERROR) << "ZSTD_compressStream2 failed: " << ZSTD_getErrorName(rc);
462             return EncodeResult::Error;
463         }
464 
465         input_buffer_.drop_front(in.pos);
466         output->resize(out.pos);
467 
468         if (rc == 0) {
469             // Zstd finished flushing its data.
470             if (finished_) {
471                 if (!input_buffer_.empty()) {
472                     LOG(ERROR) << "ZSTD_compressStream2 finished early";
473                     return EncodeResult::Error;
474                 }
475                 return EncodeResult::Done;
476             } else {
477                 return input_buffer_.empty() ? EncodeResult::NeedInput : EncodeResult::MoreOutput;
478             }
479         } else {
480             return EncodeResult::MoreOutput;
481         }
482     }
483 
484   private:
485     std::unique_ptr<ZSTD_CStream, size_t (*)(ZSTD_CStream*)> encoder_;
486 };
487