blob: 6636363efa31f791f5b9a7733b604adbfbd59b46 [file] [log] [blame]
// Copyright 2020 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 "src/developer/forensics/feedback_data/system_log_recorder/encoding/lz4_decoder.h"
#include "src/developer/forensics/feedback_data/system_log_recorder/encoding/lz4_utils.h"
#include "src/lib/fxl/strings/join_strings.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace forensics {
namespace feedback_data {
namespace system_log_recorder {
const std::string kDecodingErrorStr = "!!! DECODING ERROR !!!\n";
const std::string kDecodingSizeErrorStr =
"!!! CANNOT DECODE %lu BYTES. THERE ARE ONLY %lu BYTES LEFT !!!\n";
Lz4Decoder::Lz4Decoder() : ring_(kDecoderRingBufferSize) { stream_ = LZ4_createStreamDecode(); }
Lz4Decoder::~Lz4Decoder() { LZ4_freeStreamDecode(stream_); }
bool Lz4Decoder::DecodeNextChunk(const char* block_end, const char** block_ptr,
std::string* decoded_chunk, std::string* err_msg) {
*decoded_chunk = "";
*err_msg = "";
if (*block_ptr >= block_end) {
return false;
}
const uint16_t encoded_bytes = DecodeSize(block_ptr);
// This indicates that the encoder reset its stream because it became invalid. If so, we reset
// the decoder too.
if (encoded_bytes == kEncodeSizeError) {
Reset();
return true;
}
// Check for size error.
if (*block_ptr + encoded_bytes > block_end) {
size_t bytes_left = block_end - *block_ptr;
*err_msg = fxl::StringPrintf(kDecodingSizeErrorStr.c_str(), encoded_bytes, bytes_left);
return false;
}
const int decoded_bytes = LZ4_decompress_safe_continue(stream_, *block_ptr, ring_.GetPtr(),
encoded_bytes, kMaxChunkSize);
// Check for decoding error.
if (decoded_bytes < 0) {
*err_msg = kDecodingErrorStr;
return false;
}
*decoded_chunk = std::string(ring_.GetPtr(), decoded_bytes);
// Update index variables.
*block_ptr += encoded_bytes;
ring_.Advance(decoded_bytes);
return true;
}
std::string Lz4Decoder::DecodeWithoutReset(const std::string& chunks) {
// initialize variables
std::vector<std::string> decoded_data = {};
const char* block_ptr = chunks.data();
const char* block_end = chunks.data() + chunks.size();
std::string decoded_chunk;
std::string err_msg;
// If err_msg is set, DecodeNextChunk() will return false and we will abort DecodeWithoutReset().
while (DecodeNextChunk(block_end, &block_ptr, &decoded_chunk, &err_msg)) {
decoded_data.push_back(decoded_chunk);
}
if (!err_msg.empty()) {
decoded_data.push_back(err_msg);
}
return fxl::JoinStrings(decoded_data);
}
std::string Lz4Decoder::Decode(const std::string& block) {
const std::string output = DecodeWithoutReset(block);
Reset();
return output;
}
void Lz4Decoder::Reset() {
LZ4_freeStreamDecode(stream_);
stream_ = LZ4_createStreamDecode();
ring_.Reset();
}
} // namespace system_log_recorder
} // namespace feedback_data
} // namespace forensics