adb: add interfaces for Encoder/Decoder.
More groundwork to support more compression algorithms.
Bug: https://issuetracker.google.com/150827486
Test: python3 -m unittest test_device.FileOperationsTest{Uncompressed,Brotli}
Change-Id: I638493083b83e3f6c6854b631471e9d6b50bd79f
diff --git a/adb/compression_utils.h b/adb/compression_utils.h
index c445095..f349697 100644
--- a/adb/compression_utils.h
+++ b/adb/compression_utils.h
@@ -16,8 +16,12 @@
#pragma once
+#include <algorithm>
+#include <memory>
#include <span>
+#include <android-base/logging.h>
+
#include <brotli/decode.h>
#include <brotli/encode.h>
@@ -37,15 +41,103 @@
MoreOutput,
};
-struct BrotliDecoder {
+struct Decoder {
+ void Append(Block&& block) { input_buffer_.append(std::move(block)); }
+ bool Finish() {
+ bool old = std::exchange(finished_, true);
+ if (old) {
+ LOG(FATAL) << "Decoder::Finish called while already finished?";
+ return false;
+ }
+ return true;
+ }
+
+ virtual DecodeResult Decode(std::span<char>* output) = 0;
+
+ protected:
+ Decoder(std::span<char> output_buffer) : output_buffer_(output_buffer) {}
+ ~Decoder() = default;
+
+ bool finished_ = false;
+ IOVector input_buffer_;
+ std::span<char> output_buffer_;
+};
+
+struct Encoder {
+ void Append(Block input) { input_buffer_.append(std::move(input)); }
+ bool Finish() {
+ bool old = std::exchange(finished_, true);
+ if (old) {
+ LOG(FATAL) << "Decoder::Finish called while already finished?";
+ return false;
+ }
+ return true;
+ }
+
+ virtual EncodeResult Encode(Block* output) = 0;
+
+ protected:
+ explicit Encoder(size_t output_block_size) : output_block_size_(output_block_size) {}
+ ~Encoder() = default;
+
+ const size_t output_block_size_;
+ bool finished_ = false;
+ IOVector input_buffer_;
+};
+
+struct NullDecoder final : public Decoder {
+ explicit NullDecoder(std::span<char> output_buffer) : Decoder(output_buffer) {}
+
+ DecodeResult Decode(std::span<char>* output) final {
+ size_t available_out = output_buffer_.size();
+ void* p = output_buffer_.data();
+ while (available_out > 0 && !input_buffer_.empty()) {
+ size_t len = std::min(available_out, input_buffer_.front_size());
+ p = mempcpy(p, input_buffer_.front_data(), len);
+ available_out -= len;
+ input_buffer_.drop_front(len);
+ }
+ *output = std::span(output_buffer_.data(), static_cast<char*>(p));
+ if (input_buffer_.empty()) {
+ return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
+ }
+ return DecodeResult::MoreOutput;
+ }
+};
+
+struct NullEncoder final : public Encoder {
+ explicit NullEncoder(size_t output_block_size) : Encoder(output_block_size) {}
+
+ EncodeResult Encode(Block* output) final {
+ output->clear();
+ output->resize(output_block_size_);
+
+ size_t available_out = output->size();
+ void* p = output->data();
+
+ while (available_out > 0 && !input_buffer_.empty()) {
+ size_t len = std::min(available_out, input_buffer_.front_size());
+ p = mempcpy(p, input_buffer_.front_data(), len);
+ available_out -= len;
+ input_buffer_.drop_front(len);
+ }
+
+ output->resize(output->size() - available_out);
+
+ if (input_buffer_.empty()) {
+ return finished_ ? EncodeResult::Done : EncodeResult::NeedInput;
+ }
+ return EncodeResult::MoreOutput;
+ }
+};
+
+struct BrotliDecoder final : public Decoder {
explicit BrotliDecoder(std::span<char> output_buffer)
- : output_buffer_(output_buffer),
+ : Decoder(output_buffer),
decoder_(BrotliDecoderCreateInstance(nullptr, nullptr, nullptr),
BrotliDecoderDestroyInstance) {}
- void Append(Block&& block) { input_buffer_.append(std::move(block)); }
-
- DecodeResult Decode(std::span<char>* output) {
+ DecodeResult Decode(std::span<char>* output) final {
size_t available_in = input_buffer_.front_size();
const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
@@ -63,7 +155,8 @@
switch (r) {
case BROTLI_DECODER_RESULT_SUCCESS:
- return DecodeResult::Done;
+ // We need to wait for ID_DONE from the other end.
+ return finished_ ? DecodeResult::Done : DecodeResult::NeedInput;
case BROTLI_DECODER_RESULT_ERROR:
return DecodeResult::Error;
case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT:
@@ -77,33 +170,29 @@
}
private:
- IOVector input_buffer_;
- std::span<char> output_buffer_;
std::unique_ptr<BrotliDecoderState, void (*)(BrotliDecoderState*)> decoder_;
};
-template <size_t OutputBlockSize>
-struct BrotliEncoder {
- explicit BrotliEncoder()
- : output_block_(OutputBlockSize),
- output_bytes_left_(OutputBlockSize),
+struct BrotliEncoder final : public Encoder {
+ explicit BrotliEncoder(size_t output_block_size)
+ : Encoder(output_block_size),
+ output_block_(output_block_size_),
+ output_bytes_left_(output_block_size_),
encoder_(BrotliEncoderCreateInstance(nullptr, nullptr, nullptr),
BrotliEncoderDestroyInstance) {
BrotliEncoderSetParameter(encoder_.get(), BROTLI_PARAM_QUALITY, 1);
}
- void Append(Block input) { input_buffer_.append(std::move(input)); }
- void Finish() { finished_ = true; }
-
- EncodeResult Encode(Block* output) {
+ EncodeResult Encode(Block* output) final {
output->clear();
+
while (true) {
size_t available_in = input_buffer_.front_size();
const uint8_t* next_in = reinterpret_cast<const uint8_t*>(input_buffer_.front_data());
size_t available_out = output_bytes_left_;
- uint8_t* next_out = reinterpret_cast<uint8_t*>(output_block_.data() +
- (OutputBlockSize - output_bytes_left_));
+ uint8_t* next_out = reinterpret_cast<uint8_t*>(
+ output_block_.data() + (output_block_size_ - output_bytes_left_));
BrotliEncoderOperation op = BROTLI_OPERATION_PROCESS;
if (finished_) {
@@ -121,13 +210,13 @@
output_bytes_left_ = available_out;
if (BrotliEncoderIsFinished(encoder_.get())) {
- output_block_.resize(OutputBlockSize - output_bytes_left_);
+ output_block_.resize(output_block_size_ - output_bytes_left_);
*output = std::move(output_block_);
return EncodeResult::Done;
} else if (output_bytes_left_ == 0) {
*output = std::move(output_block_);
- output_block_.resize(OutputBlockSize);
- output_bytes_left_ = OutputBlockSize;
+ output_block_.resize(output_block_size_);
+ output_bytes_left_ = output_block_size_;
return EncodeResult::MoreOutput;
} else if (input_buffer_.empty()) {
return EncodeResult::NeedInput;
@@ -136,8 +225,6 @@
}
private:
- bool finished_ = false;
- IOVector input_buffer_;
Block output_block_;
size_t output_bytes_left_;
std::unique_ptr<BrotliEncoderState, void (*)(BrotliEncoderState*)> encoder_;