// 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 #include #include #include #include #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/logging.h" #include "puffin/src/puff_data.h" #include "puffin/src/puff_writer.h" using std::string; using std::vector; namespace puffin { Puffer::Puffer(bool exclude_bad_distance_caches) : dyn_ht_(new HuffmanTable()), fix_ht_(new HuffmanTable()), exclude_bad_distance_caches_(exclude_bad_distance_caches) {} Puffer::Puffer() : Puffer(false) {} Puffer::~Puffer() {} bool Puffer::PuffDeflate(BitReaderInterface* br, PuffWriterInterface* pw, vector* deflates) const { PuffData pd; HuffmanTable* cur_ht; bool end_loop = false; // 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 (!end_loop && br->CacheBits(8)) { auto start_bit_offset = br->OffsetInBits(); TEST_AND_RETURN_FALSE(br->CacheBits(3)); 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(type)); // If it is the final block and we are just looking for deflate locations, // we consider this the end of the search. if (deflates != nullptr && final_bit) { end_loop = true; } // 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(type)) { case BlockType::kUncompressed: { auto skipped_bits = br->ReadBoundaryBits(); br->SkipBoundaryBits(); TEST_AND_RETURN_FALSE(br->CacheBits(32)); 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 << ")"; 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)); // Insert all the raw literals. pd.type = PuffData::Type::kLiterals; pd.length = len; TEST_AND_RETURN_FALSE(br->GetByteReaderFn(pd.length, &pd.read_fn)); TEST_AND_RETURN_FALSE(pw->Insert(pd)); pd.type = PuffData::Type::kEndOfBlock; TEST_AND_RETURN_FALSE(pw->Insert(pd)); // There is no need to insert the location of uncompressed deflates // because we do not want the uncompressed blocks when trying to find // the bit-addressed location of deflates. They better be ignored. // 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)); 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)); pd.length += 1; // For the header. TEST_AND_RETURN_FALSE(pw->Insert(pd)); cur_ht = dyn_ht_.get(); break; default: LOG(ERROR) << "Invalid block compression type: " << static_cast(type); return false; } // If true and the list of output |deflates| is non-null, the current // deflate location will be added to that list. bool include_deflate = true; 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(cur_ht->EndOfBlockBitLength(&max_bits)); } TEST_AND_RETURN_FALSE(br->CacheBits(max_bits)); auto bits = br->ReadBits(max_bits); uint16_t lit_len_alphabet; size_t nbits; TEST_AND_RETURN_FALSE( cur_ht->LitLenAlphabet(bits, &lit_len_alphabet, &nbits)); 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)); } else if (256 == lit_len_alphabet) { pd.type = PuffData::Type::kEndOfBlock; TEST_AND_RETURN_FALSE(pw->Insert(pd)); if (deflates != nullptr && include_deflate) { deflates->emplace_back(start_bit_offset, br->OffsetInBits() - start_bit_offset); } break; // Breaks the loop. } else { TEST_AND_RETURN_FALSE(lit_len_alphabet <= 285); // 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(br->CacheBits(extra_bits_len)); extra_bits_value = br->ReadBits(extra_bits_len); br->DropBits(extra_bits_len); } auto length = kLengthBases[len_code_start] + extra_bits_value; auto bits_to_cache = cur_ht->DistanceMaxBits(); if (!br->CacheBits(bits_to_cache)) { // This is a corner case that is present in the older versions of the // puffin. So we need to catch it and correctly discard this kind of // deflate when we encounter it. See crbug.com/915559 for more info. bits_to_cache = br->BitsRemaining(); TEST_AND_RETURN_FALSE(br->CacheBits(bits_to_cache)); if (exclude_bad_distance_caches_) { include_deflate = false; } LOG(WARNING) << "A rare condition that older puffin clients fail to" << " recognize happened. Nothing to worry about." << " See crbug.com/915559"; } auto bits = br->ReadBits(bits_to_cache); uint16_t distance_alphabet; size_t nbits; TEST_AND_RETURN_FALSE( cur_ht->DistanceAlphabet(bits, &distance_alphabet, &nbits)); br->DropBits(nbits); // Reading distance. extra_bits_len = kDistanceExtraBits[distance_alphabet]; extra_bits_value = 0; if (extra_bits_len) { TEST_AND_RETURN_FALSE(br->CacheBits(extra_bits_len)); 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)); } } } TEST_AND_RETURN_FALSE(pw->Flush()); return true; } } // namespace puffin