// Copyright 2017 The Chromium OS 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 "puffin/src/include/puffin/puffer.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "puffin/src/bit_reader.h"
#include "puffin/src/huffman_table.h"
#include "puffin/src/include/puffin/common.h"
#include "puffin/src/include/puffin/stream.h"
#include "puffin/src/puff_data.h"
#include "puffin/src/puff_writer.h"
#include "puffin/src/set_errors.h"
namespace puffin {
using std::vector;
using std::string;
Puffer::Puffer() : dyn_ht_(new HuffmanTable()), fix_ht_(new HuffmanTable()) {}
Puffer::~Puffer() {}
bool Puffer::PuffDeflate(BitReaderInterface* br,
PuffWriterInterface* pw,
vector<BitExtent>* deflates,
Error* error) const {
*error = Error::kSuccess;
PuffData pd;
HuffmanTable* cur_ht;
// No bits left to read, return. We try to cache at least eight bits because
// the minimum length of a deflate bit stream is 8: (fixed huffman table) 3
// bits header + 5 bits just one len/dist symbol.
while (br->CacheBits(8)) {
auto start_bit_offset = br->OffsetInBits();
TEST_AND_RETURN_FALSE_SET_ERROR(br->CacheBits(3),
Error::kInsufficientInput);
uint8_t final_bit = br->ReadBits(1); // BFINAL
br->DropBits(1);
uint8_t type = br->ReadBits(2); // BTYPE
br->DropBits(2);
DVLOG(2) << "Read block type: "
<< BlockTypeToString(static_cast<BlockType>(type));
// Header structure
// +-+-+-+-+-+-+-+-+
// |F| TP| SKIP |
// +-+-+-+-+-+-+-+-+
// F -> final_bit
// TP -> type
// SKIP -> skipped_bits (only in kUncompressed type)
auto block_header = (final_bit << 7) | (type << 5);
switch (static_cast<BlockType>(type)) {
case BlockType::kUncompressed: {
auto skipped_bits = br->ReadBoundaryBits();
br->SkipBoundaryBits();
TEST_AND_RETURN_FALSE_SET_ERROR(br->CacheBits(32),
Error::kInsufficientInput);
auto len = br->ReadBits(16); // LEN
br->DropBits(16);
auto nlen = br->ReadBits(16); // NLEN
br->DropBits(16);
if ((len ^ nlen) != 0xFFFF) {
LOG(ERROR) << "Length of uncompressed data is invalid;"
<< " LEN(" << len << ") NLEN(" << nlen << ")";
*error = Error::kInvalidInput;
return false;
}
// Put skipped bits into header.
block_header |= skipped_bits;
// Insert block header.
pd.type = PuffData::Type::kBlockMetadata;
pd.block_metadata[0] = block_header;
pd.length = 1;
TEST_AND_RETURN_FALSE(pw->Insert(pd, error));
// Insert all the raw literals.
pd.type = PuffData::Type::kLiterals;
pd.length = len;
TEST_AND_RETURN_FALSE_SET_ERROR(
br->GetByteReaderFn(pd.length, &pd.read_fn),
Error::kInsufficientInput);
TEST_AND_RETURN_FALSE(pw->Insert(pd, error));
pd.type = PuffData::Type::kEndOfBlock;
TEST_AND_RETURN_FALSE(pw->Insert(pd, error));
if (deflates != nullptr) {
deflates->emplace_back(start_bit_offset,
br->OffsetInBits() - start_bit_offset);
}
// continue the loop. Do not read any literal/length/distance.
continue;
}
case BlockType::kFixed:
fix_ht_->BuildFixedHuffmanTable();
cur_ht = fix_ht_.get();
pd.type = PuffData::Type::kBlockMetadata;
pd.block_metadata[0] = block_header;
pd.length = 1;
TEST_AND_RETURN_FALSE(pw->Insert(pd, error));
break;
case BlockType::kDynamic:
pd.type = PuffData::Type::kBlockMetadata;
pd.block_metadata[0] = block_header;
pd.length = sizeof(pd.block_metadata) - 1;
TEST_AND_RETURN_FALSE(dyn_ht_->BuildDynamicHuffmanTable(
br, &pd.block_metadata[1], &pd.length, error));
pd.length += 1; // For the header.
TEST_AND_RETURN_FALSE(pw->Insert(pd, error));
cur_ht = dyn_ht_.get();
break;
default:
LOG(ERROR) << "Invalid block compression type: "
<< static_cast<int>(type);
*error = Error::kInvalidInput;
return false;
}
while (true) { // Breaks when the end of block is reached.
auto max_bits = cur_ht->LitLenMaxBits();
if (!br->CacheBits(max_bits)) {
// It could be the end of buffer and the bit length of the end_of_block
// symbol has less than maximum bit length of current Huffman table. So
// only asking for the size of end of block symbol (256).
TEST_AND_RETURN_FALSE_SET_ERROR(cur_ht->EndOfBlockBitLength(&max_bits),
Error::kInvalidInput);
}
TEST_AND_RETURN_FALSE_SET_ERROR(br->CacheBits(max_bits),
Error::kInsufficientInput);
auto bits = br->ReadBits(max_bits);
uint16_t lit_len_alphabet;
size_t nbits;
TEST_AND_RETURN_FALSE_SET_ERROR(
cur_ht->LitLenAlphabet(bits, &lit_len_alphabet, &nbits),
Error::kInvalidInput);
br->DropBits(nbits);
if (lit_len_alphabet < 256) {
pd.type = PuffData::Type::kLiteral;
pd.byte = lit_len_alphabet;
TEST_AND_RETURN_FALSE(pw->Insert(pd, error));
} else if (256 == lit_len_alphabet) {
pd.type = PuffData::Type::kEndOfBlock;
TEST_AND_RETURN_FALSE(pw->Insert(pd, error));
if (deflates != nullptr) {
deflates->emplace_back(start_bit_offset,
br->OffsetInBits() - start_bit_offset);
}
break; // Breaks the loop.
} else {
TEST_AND_RETURN_FALSE_SET_ERROR(lit_len_alphabet <= 285,
Error::kInvalidInput);
// Reading length.
auto len_code_start = lit_len_alphabet - 257;
auto extra_bits_len = kLengthExtraBits[len_code_start];
uint16_t extra_bits_value = 0;
if (extra_bits_len) {
TEST_AND_RETURN_FALSE_SET_ERROR(br->CacheBits(extra_bits_len),
Error::kInsufficientInput);
extra_bits_value = br->ReadBits(extra_bits_len);
br->DropBits(extra_bits_len);
}
auto length = kLengthBases[len_code_start] + extra_bits_value;
TEST_AND_RETURN_FALSE_SET_ERROR(
br->CacheBits(cur_ht->DistanceMaxBits()),
Error::kInsufficientInput);
auto bits = br->ReadBits(cur_ht->DistanceMaxBits());
uint16_t distance_alphabet;
size_t nbits;
TEST_AND_RETURN_FALSE_SET_ERROR(
cur_ht->DistanceAlphabet(bits, &distance_alphabet, &nbits),
Error::kInvalidInput);
br->DropBits(nbits);
// Reading distance.
extra_bits_len = kDistanceExtraBits[distance_alphabet];
extra_bits_value = 0;
if (extra_bits_len) {
TEST_AND_RETURN_FALSE_SET_ERROR(br->CacheBits(extra_bits_len),
Error::kInsufficientInput);
extra_bits_value = br->ReadBits(extra_bits_len);
br->DropBits(extra_bits_len);
}
pd.type = PuffData::Type::kLenDist;
pd.length = length;
pd.distance = kDistanceBases[distance_alphabet] + extra_bits_value;
TEST_AND_RETURN_FALSE(pw->Insert(pd, error));
}
}
}
TEST_AND_RETURN_FALSE(pw->Flush(error));
return true;
}
} // namespace puffin