text: // 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
  