1*07fb1d06SElliott Hughes // Copyright 2017 The ChromiumOS Authors
2*07fb1d06SElliott Hughes // Use of this source code is governed by a BSD-style license that can be
3*07fb1d06SElliott Hughes // found in the LICENSE file.
4*07fb1d06SElliott Hughes
5*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/huffer.h"
6*07fb1d06SElliott Hughes
7*07fb1d06SElliott Hughes #include <algorithm>
8*07fb1d06SElliott Hughes #include <memory>
9*07fb1d06SElliott Hughes #include <string>
10*07fb1d06SElliott Hughes #include <utility>
11*07fb1d06SElliott Hughes
12*07fb1d06SElliott Hughes #include "puffin/src/bit_writer.h"
13*07fb1d06SElliott Hughes #include "puffin/src/huffman_table.h"
14*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/common.h"
15*07fb1d06SElliott Hughes #include "puffin/src/include/puffin/stream.h"
16*07fb1d06SElliott Hughes #include "puffin/src/logging.h"
17*07fb1d06SElliott Hughes #include "puffin/src/puff_data.h"
18*07fb1d06SElliott Hughes #include "puffin/src/puff_reader.h"
19*07fb1d06SElliott Hughes
20*07fb1d06SElliott Hughes using std::string;
21*07fb1d06SElliott Hughes
22*07fb1d06SElliott Hughes namespace puffin {
23*07fb1d06SElliott Hughes
Huffer()24*07fb1d06SElliott Hughes Huffer::Huffer() : dyn_ht_(new HuffmanTable()), fix_ht_(new HuffmanTable()) {}
25*07fb1d06SElliott Hughes
~Huffer()26*07fb1d06SElliott Hughes Huffer::~Huffer() {}
27*07fb1d06SElliott Hughes
HuffDeflate(PuffReaderInterface * pr,BitWriterInterface * bw) const28*07fb1d06SElliott Hughes bool Huffer::HuffDeflate(PuffReaderInterface* pr,
29*07fb1d06SElliott Hughes BitWriterInterface* bw) const {
30*07fb1d06SElliott Hughes PuffData pd;
31*07fb1d06SElliott Hughes HuffmanTable* cur_ht = nullptr;
32*07fb1d06SElliott Hughes // If no bytes left for PuffReader to read, bail out.
33*07fb1d06SElliott Hughes while (pr->BytesLeft() != 0) {
34*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pr->GetNext(&pd));
35*07fb1d06SElliott Hughes
36*07fb1d06SElliott Hughes // The first data should be a metadata.
37*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pd.type == PuffData::Type::kBlockMetadata);
38*07fb1d06SElliott Hughes auto header = pd.block_metadata[0];
39*07fb1d06SElliott Hughes auto final_bit = (header & 0x80) >> 7;
40*07fb1d06SElliott Hughes auto type = (header & 0x60) >> 5;
41*07fb1d06SElliott Hughes auto skipped_bits = header & 0x1F;
42*07fb1d06SElliott Hughes DVLOG(2) << "Write block type: "
43*07fb1d06SElliott Hughes << BlockTypeToString(static_cast<BlockType>(type));
44*07fb1d06SElliott Hughes
45*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(1, final_bit));
46*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(2, type));
47*07fb1d06SElliott Hughes switch (static_cast<BlockType>(type)) {
48*07fb1d06SElliott Hughes case BlockType::kUncompressed:
49*07fb1d06SElliott Hughes bw->WriteBoundaryBits(skipped_bits);
50*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pr->GetNext(&pd));
51*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pd.type != PuffData::Type::kLiteral);
52*07fb1d06SElliott Hughes
53*07fb1d06SElliott Hughes if (pd.type == PuffData::Type::kLiterals) {
54*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(16, pd.length));
55*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(16, ~pd.length));
56*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBytes(pd.length, pd.read_fn));
57*07fb1d06SElliott Hughes // Reading end of block, but don't write anything.
58*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pr->GetNext(&pd));
59*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pd.type == PuffData::Type::kEndOfBlock);
60*07fb1d06SElliott Hughes } else if (pd.type == PuffData::Type::kEndOfBlock) {
61*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(16, 0));
62*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(16, ~0));
63*07fb1d06SElliott Hughes } else {
64*07fb1d06SElliott Hughes LOG(ERROR) << "Uncompressed block did not end properly!";
65*07fb1d06SElliott Hughes return false;
66*07fb1d06SElliott Hughes }
67*07fb1d06SElliott Hughes // We have to read a new block.
68*07fb1d06SElliott Hughes continue;
69*07fb1d06SElliott Hughes
70*07fb1d06SElliott Hughes case BlockType::kFixed:
71*07fb1d06SElliott Hughes fix_ht_->BuildFixedHuffmanTable();
72*07fb1d06SElliott Hughes cur_ht = fix_ht_.get();
73*07fb1d06SElliott Hughes break;
74*07fb1d06SElliott Hughes
75*07fb1d06SElliott Hughes case BlockType::kDynamic:
76*07fb1d06SElliott Hughes cur_ht = dyn_ht_.get();
77*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(dyn_ht_->BuildDynamicHuffmanTable(
78*07fb1d06SElliott Hughes &pd.block_metadata[1], pd.length - 1, bw));
79*07fb1d06SElliott Hughes break;
80*07fb1d06SElliott Hughes
81*07fb1d06SElliott Hughes default:
82*07fb1d06SElliott Hughes LOG(ERROR) << "Invalid block compression type: "
83*07fb1d06SElliott Hughes << static_cast<int>(type);
84*07fb1d06SElliott Hughes return false;
85*07fb1d06SElliott Hughes }
86*07fb1d06SElliott Hughes
87*07fb1d06SElliott Hughes // We read literal or distrance/lengths until and end of block or end of
88*07fb1d06SElliott Hughes // stream is reached.
89*07fb1d06SElliott Hughes bool block_ended = false;
90*07fb1d06SElliott Hughes while (!block_ended) {
91*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pr->GetNext(&pd));
92*07fb1d06SElliott Hughes switch (pd.type) {
93*07fb1d06SElliott Hughes case PuffData::Type::kLiteral:
94*07fb1d06SElliott Hughes case PuffData::Type::kLiterals: {
95*07fb1d06SElliott Hughes auto write_literal = [&cur_ht, &bw](uint8_t literal) {
96*07fb1d06SElliott Hughes uint16_t literal_huffman;
97*07fb1d06SElliott Hughes size_t nbits;
98*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(
99*07fb1d06SElliott Hughes cur_ht->LitLenHuffman(literal, &literal_huffman, &nbits));
100*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(nbits, literal_huffman));
101*07fb1d06SElliott Hughes return true;
102*07fb1d06SElliott Hughes };
103*07fb1d06SElliott Hughes
104*07fb1d06SElliott Hughes if (pd.type == PuffData::Type::kLiteral) {
105*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(write_literal(pd.byte));
106*07fb1d06SElliott Hughes } else {
107*07fb1d06SElliott Hughes auto len = pd.length;
108*07fb1d06SElliott Hughes while (len-- > 0) {
109*07fb1d06SElliott Hughes uint8_t literal;
110*07fb1d06SElliott Hughes pd.read_fn(&literal, 1);
111*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(write_literal(literal));
112*07fb1d06SElliott Hughes }
113*07fb1d06SElliott Hughes }
114*07fb1d06SElliott Hughes break;
115*07fb1d06SElliott Hughes }
116*07fb1d06SElliott Hughes case PuffData::Type::kLenDist: {
117*07fb1d06SElliott Hughes auto len = pd.length;
118*07fb1d06SElliott Hughes auto dist = pd.distance;
119*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(len >= 3 && len <= 258);
120*07fb1d06SElliott Hughes
121*07fb1d06SElliott Hughes // Using a binary search here instead of the linear search may be (but
122*07fb1d06SElliott Hughes // not necessarily) faster. Needs experiment to validate.
123*07fb1d06SElliott Hughes size_t index = 0;
124*07fb1d06SElliott Hughes while (len > kLengthBases[index]) {
125*07fb1d06SElliott Hughes index++;
126*07fb1d06SElliott Hughes }
127*07fb1d06SElliott Hughes if (len < kLengthBases[index]) {
128*07fb1d06SElliott Hughes index--;
129*07fb1d06SElliott Hughes }
130*07fb1d06SElliott Hughes auto extra_bits_len = kLengthExtraBits[index];
131*07fb1d06SElliott Hughes uint16_t length_huffman;
132*07fb1d06SElliott Hughes size_t nbits;
133*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(
134*07fb1d06SElliott Hughes cur_ht->LitLenHuffman(index + 257, &length_huffman, &nbits));
135*07fb1d06SElliott Hughes
136*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(nbits, length_huffman));
137*07fb1d06SElliott Hughes
138*07fb1d06SElliott Hughes if (extra_bits_len > 0) {
139*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(
140*07fb1d06SElliott Hughes bw->WriteBits(extra_bits_len, len - kLengthBases[index]));
141*07fb1d06SElliott Hughes }
142*07fb1d06SElliott Hughes
143*07fb1d06SElliott Hughes // Same as above (binary search).
144*07fb1d06SElliott Hughes index = 0;
145*07fb1d06SElliott Hughes while (dist > kDistanceBases[index]) {
146*07fb1d06SElliott Hughes index++;
147*07fb1d06SElliott Hughes }
148*07fb1d06SElliott Hughes if (dist < kDistanceBases[index]) {
149*07fb1d06SElliott Hughes index--;
150*07fb1d06SElliott Hughes }
151*07fb1d06SElliott Hughes extra_bits_len = kDistanceExtraBits[index];
152*07fb1d06SElliott Hughes uint16_t distance_huffman;
153*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(
154*07fb1d06SElliott Hughes cur_ht->DistanceHuffman(index, &distance_huffman, &nbits));
155*07fb1d06SElliott Hughes
156*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(nbits, distance_huffman));
157*07fb1d06SElliott Hughes if (extra_bits_len > 0) {
158*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(
159*07fb1d06SElliott Hughes bw->WriteBits(extra_bits_len, dist - kDistanceBases[index]));
160*07fb1d06SElliott Hughes }
161*07fb1d06SElliott Hughes break;
162*07fb1d06SElliott Hughes }
163*07fb1d06SElliott Hughes
164*07fb1d06SElliott Hughes case PuffData::Type::kEndOfBlock: {
165*07fb1d06SElliott Hughes uint16_t eos_huffman;
166*07fb1d06SElliott Hughes size_t nbits;
167*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(
168*07fb1d06SElliott Hughes cur_ht->LitLenHuffman(256, &eos_huffman, &nbits));
169*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->WriteBits(nbits, eos_huffman));
170*07fb1d06SElliott Hughes block_ended = true;
171*07fb1d06SElliott Hughes break;
172*07fb1d06SElliott Hughes }
173*07fb1d06SElliott Hughes case PuffData::Type::kBlockMetadata:
174*07fb1d06SElliott Hughes LOG(ERROR) << "Not expecing a metadata!";
175*07fb1d06SElliott Hughes return false;
176*07fb1d06SElliott Hughes
177*07fb1d06SElliott Hughes default:
178*07fb1d06SElliott Hughes LOG(ERROR) << "Invalid block data type!";
179*07fb1d06SElliott Hughes return false;
180*07fb1d06SElliott Hughes }
181*07fb1d06SElliott Hughes }
182*07fb1d06SElliott Hughes }
183*07fb1d06SElliott Hughes
184*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(bw->Flush());
185*07fb1d06SElliott Hughes return true;
186*07fb1d06SElliott Hughes }
187*07fb1d06SElliott Hughes
188*07fb1d06SElliott Hughes } // namespace puffin
189