Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a652877b authored by David Anderson's avatar David Anderson
Browse files

libsnapshot: Add support for brotli compression.

Bug: 162274240
Test: cow_api_test
Change-Id: I0b0ceec3c3041a6aea4b1e6c4d01ed0a8860d7e8
parent ebd07cc5
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -152,6 +152,7 @@ cc_library_static {
        "liblog",
    ],
    static_libs: [
        "libbrotli",
        "libz",
    ],
    ramdisk_available: true,
@@ -362,6 +363,7 @@ cc_defaults {

    static_libs: [
        "libbase",
        "libbrotli",
        "liblog",
        "libdm",
        "libz",
@@ -403,6 +405,7 @@ cc_test {
        "libz",
    ],
    static_libs: [
        "libbrotli",
        "libgtest",
        "libsnapshot_cow",
    ],
@@ -494,11 +497,12 @@ cc_test {
    shared_libs: [
        "libbase",
        "liblog",
        "libz",
    ],
    static_libs: [
        "libbrotli",
        "libgtest",
        "libsnapshot_cow",
        "libz",
    ],
    header_libs: [
        "libstorage_literals_headers",
+8 −4
Original line number Diff line number Diff line
@@ -30,12 +30,12 @@ namespace snapshot {

class CowTest : public ::testing::Test {
  protected:
    void SetUp() override {
    virtual void SetUp() override {
        cow_ = std::make_unique<TemporaryFile>();
        ASSERT_GE(cow_->fd, 0) << strerror(errno);
    }

    void TearDown() override { cow_ = nullptr; }
    virtual void TearDown() override { cow_ = nullptr; }

    std::unique_ptr<TemporaryFile> cow_;
};
@@ -211,9 +211,11 @@ class HorribleStringSink : public StringSink {
    void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
};

TEST_F(CowTest, HorribleSink) {
class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};

TEST_P(CompressionTest, HorribleSink) {
    CowOptions options;
    options.compression = "gz";
    options.compression = GetParam();
    CowWriter writer(options);

    ASSERT_TRUE(writer.Initialize(cow_->fd));
@@ -239,6 +241,8 @@ TEST_F(CowTest, HorribleSink) {
    ASSERT_EQ(sink.stream(), data);
}

INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));

TEST_F(CowTest, GetSize) {
    CowOptions options;
    CowWriter writer(options);
+53 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <utility>

#include <android-base/logging.h>
#include <brotli/decode.h>
#include <zlib.h>

namespace android {
@@ -207,5 +208,57 @@ std::unique_ptr<IDecompressor> IDecompressor::Gz() {
    return std::unique_ptr<IDecompressor>(new GzDecompressor());
}

class BrotliDecompressor final : public StreamDecompressor {
  public:
    ~BrotliDecompressor();

    bool Init() override;
    bool DecompressInput(const uint8_t* data, size_t length) override;
    bool Done() override { return BrotliDecoderIsFinished(decoder_); }

  private:
    BrotliDecoderState* decoder_ = nullptr;
};

bool BrotliDecompressor::Init() {
    decoder_ = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr);
    return true;
}

BrotliDecompressor::~BrotliDecompressor() {
    if (decoder_) {
        BrotliDecoderDestroyInstance(decoder_);
    }
}

bool BrotliDecompressor::DecompressInput(const uint8_t* data, size_t length) {
    size_t available_in = length;
    const uint8_t* next_in = data;

    bool needs_more_output = false;
    while (available_in || needs_more_output) {
        if (!output_buffer_remaining_ && !GetFreshBuffer()) {
            return false;
        }

        auto output_buffer = output_buffer_;
        auto r = BrotliDecoderDecompressStream(decoder_, &available_in, &next_in,
                                               &output_buffer_remaining_, &output_buffer_, nullptr);
        if (r == BROTLI_DECODER_RESULT_ERROR) {
            LOG(ERROR) << "brotli decode failed";
            return false;
        }
        if (!sink_->ReturnData(output_buffer, output_buffer_ - output_buffer)) {
            return false;
        }
        needs_more_output = (r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT);
    }
    return true;
}

std::unique_ptr<IDecompressor> IDecompressor::Brotli() {
    return std::unique_ptr<IDecompressor>(new BrotliDecompressor());
}

}  // namespace snapshot
}  // namespace android
+1 −0
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ class IDecompressor {
    // Factory methods for decompression methods.
    static std::unique_ptr<IDecompressor> Uncompressed();
    static std::unique_ptr<IDecompressor> Gz();
    static std::unique_ptr<IDecompressor> Brotli();

    // |output_bytes| is the expected total number of bytes to sink.
    virtual bool Decompress(size_t output_bytes) = 0;
+3 −0
Original line number Diff line number Diff line
@@ -233,6 +233,9 @@ bool CowReader::ReadData(const CowOperation& op, IByteSink* sink) {
        case kCowCompressGz:
            decompressor = IDecompressor::Gz();
            break;
        case kCowCompressBrotli:
            decompressor = IDecompressor::Brotli();
            break;
        default:
            LOG(ERROR) << "Unknown compression type: " << op.compression;
            return false;
Loading