blob: 46b53969fdf9795fadd5f3f8a76f179526553642 [file] [log] [blame] [edit]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <fbl/unique_fd.h>
#include "blobfs-compression.h"
#include "src/lib/chunked-compression/chunked-compressor.h"
#include "src/lib/chunked-compression/status.h"
namespace blobfs_compress {
namespace {
using chunked_compression::ChunkedCompressor;
using chunked_compression::CompressionParams;
constexpr const char kAnsiUpLine[] = "\33[A";
constexpr const char kAnsiClearLine[] = "\33[2K\r";
// TODO (fxbug.dev/66779): Use blobfs compression level directly instead of hardcoding.
constexpr int kDefaultBlobfsCompressionLevel = 14;
constexpr size_t kTargetFrameSize = 32 * 1024;
} // namespace
// ProgressWriter writes live a progress indicator to stdout. Updates are written in-place
// (using ANSI control codes to rewrite the current line).
class ProgressWriter {
public:
explicit ProgressWriter(int refresh_hz = 60) : refresh_hz_(refresh_hz) {
last_report_ = std::chrono::steady_clock::time_point::min();
printf("\n");
}
void Update(const char* fmt, ...) {
auto now = std::chrono::steady_clock::now();
if (now < last_report_ + refresh_duration()) {
return;
}
last_report_ = now;
printf("%s%s", kAnsiUpLine, kAnsiClearLine);
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
fflush(stdout);
}
void Final(const char* fmt, ...) {
printf("%s%s", kAnsiUpLine, kAnsiClearLine);
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
fflush(stdout);
}
std::chrono::steady_clock::duration refresh_duration() const {
return std::chrono::seconds(1) / refresh_hz_;
}
private:
std::chrono::steady_clock::time_point last_report_;
int refresh_hz_;
};
chunked_compression::CompressionParams ComputeDefaultBlobfsCompressionParams(size_t sz) {
chunked_compression::CompressionParams params;
// Use default param values, which are opaque to sdk users.
// This allows us to fine tune these and keep them in sync with blobfs chunked compression
// algoriithm.
params.frame_checksum = false;
params.compression_level = kDefaultBlobfsCompressionLevel;
params.chunk_size =
chunked_compression::CompressionParams::ChunkSizeForInputSize(sz, kTargetFrameSize);
return params;
}
// Reads |sz| bytes from |src| and compresses it, writing the output to |dst_file|.
int BlobfsCompress(const uint8_t* src, size_t src_sz, uint8_t* dest_write_buf,
size_t* out_compressed_size, CompressionParams params) {
ChunkedCompressor compressor(params);
ProgressWriter progress;
compressor.SetProgressCallback([&](size_t bytes_read, size_t bytes_total, size_t bytes_written) {
progress.Update("%2.0f%% (%lu bytes written)\n",
static_cast<double>(bytes_read) / static_cast<double>(bytes_total) * 100,
bytes_written);
});
size_t compressed_size;
size_t output_limit = params.ComputeOutputSizeLimit(src_sz);
if (compressor.Compress(src, src_sz, dest_write_buf, output_limit, &compressed_size) !=
chunked_compression::kStatusOk) {
return 1;
}
progress.Final("Wrote %lu bytes (%2.0f%% compression)\n", compressed_size,
static_cast<double>(compressed_size) / static_cast<double>(src_sz) * 100);
*out_compressed_size = compressed_size;
return 0;
}
} // namespace blobfs_compress