blob: 08a79f532e2311445069376ed88e566a247b2fd8 [file] [log] [blame]
// Copyright 2022 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This program compresses a file into a separate .lz4 file comprised of a
// single LZ4 Frame.
// It's written for a specific purpose: to produce LZ4 compressed versions of
// the Cobalt Shared Library that can be tested and released. As such, the
// encoding must remain compatible with what the client-side decompression code
// in the Loader Application expects. Ad hoc users wanting more flexibility
// should use the official LZ4 CLI instead.
#include <stdio.h>
#include <cstdint>
#include <iostream>
#include <vector>
#include "third_party/lz4_lib/lz4frame.h"
#include "third_party/lz4_lib/lz4hc.h"
bool ReadFile(const char* file_path, std::vector<char>& buffer) {
FILE* file;
if ((file = fopen(file_path, "rb")) == NULL) {
std::cerr << "Failed to open " << file_path << "\n";
return false;
}
if (fseek(file, 0L, SEEK_END) != 0) {
std::cerr << "Failed to seek in " << file_path << "\n";
fclose(file);
return false;
}
std::int64_t file_size;
if ((file_size = ftell(file)) == -1L) {
std::cerr << "Failed to get size of " << file_path << "\n";
fclose(file);
return false;
}
rewind(file);
buffer.resize(file_size);
size_t result;
if ((result = fread(buffer.data(), sizeof(char), file_size, file)) !=
file_size) {
std::cerr << "Expected to read " << file_size << " bytes from " << file_path
<< " but only read " << result << "\n";
fclose(file);
return false;
}
fclose(file);
return true;
}
bool Compress(std::vector<char>& src_buffer, std::vector<char>& dst_buffer) {
// Based on experimentation these preferences yield a high compression ratio
// and fast decompression speed for a diverse set of devices.
LZ4F_preferences_t prefs = {
{LZ4F_max256KB, LZ4F_blockIndependent, LZ4F_noContentChecksum, LZ4F_frame,
src_buffer.size(), 0 /* no dictID */, LZ4F_noBlockChecksum},
LZ4HC_CLEVEL_MAX,
0, /* autoflush */
0, /* favor decompression speed */
{0, 0, 0}, /* reserved, must be set to 0 */
};
size_t dst_capacity = LZ4F_compressFrameBound(src_buffer.size(), &prefs);
dst_buffer.resize(dst_capacity);
size_t encoded_size =
LZ4F_compressFrame(dst_buffer.data(), dst_capacity, src_buffer.data(),
src_buffer.size(), &prefs);
if (LZ4F_isError(encoded_size)) {
std::cerr << LZ4F_getErrorName(encoded_size) << "\n";
return false;
}
dst_buffer.resize(encoded_size);
return true;
}
bool WriteFile(const char* file_path, std::vector<char>& buffer) {
FILE* file;
if ((file = fopen(file_path, "wb")) == NULL) {
std::cerr << "Failed to open " << file_path << "\n";
return false;
}
size_t result;
if ((result = fwrite(buffer.data(), sizeof(char), buffer.size(), file)) !=
buffer.size()) {
std::cerr << "Expected to write " << buffer.size() << " bytes to "
<< file_path << " but only wrote " << result << "\n";
fclose(file);
return false;
}
fclose(file);
return true;
}
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cerr << "Usage: lz4_compress <source> <destination>"
<< "\n";
return 1;
}
std::vector<char> src_buffer;
if (!ReadFile(argv[1], src_buffer)) {
std::cerr << "Failed to read the src file"
<< "\n";
return 1;
}
std::vector<char> dst_buffer;
if (!Compress(src_buffer, dst_buffer)) {
std::cerr << "Failed to compress"
<< "\n";
return 1;
}
if (!WriteFile(argv[2], dst_buffer)) {
std::cerr << "Failed to write the dst file"
<< "\n";
return 1;
}
return 0;
}