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/puff_writer.h"
6*07fb1d06SElliott Hughes
7*07fb1d06SElliott Hughes #include <algorithm>
8*07fb1d06SElliott Hughes #include <memory>
9*07fb1d06SElliott Hughes #include <string>
10*07fb1d06SElliott Hughes #include <vector>
11*07fb1d06SElliott Hughes
12*07fb1d06SElliott Hughes #include "puffin/src/logging.h"
13*07fb1d06SElliott Hughes
14*07fb1d06SElliott Hughes namespace puffin {
15*07fb1d06SElliott Hughes
16*07fb1d06SElliott Hughes namespace {
17*07fb1d06SElliott Hughes // Writes a value to the buffer in big-endian mode. Experience showed that
18*07fb1d06SElliott Hughes // big-endian creates smaller payloads.
WriteUint16ToByteArray(uint16_t value,uint8_t * buffer)19*07fb1d06SElliott Hughes inline void WriteUint16ToByteArray(uint16_t value, uint8_t* buffer) {
20*07fb1d06SElliott Hughes *buffer = value >> 8;
21*07fb1d06SElliott Hughes *(buffer + 1) = value & 0x00FF;
22*07fb1d06SElliott Hughes }
23*07fb1d06SElliott Hughes
24*07fb1d06SElliott Hughes constexpr size_t kLiteralsMaxLength = (1 << 16) + 127; // 65663
25*07fb1d06SElliott Hughes } // namespace
26*07fb1d06SElliott Hughes
Insert(const PuffData & pd)27*07fb1d06SElliott Hughes bool BufferPuffWriter::Insert(const PuffData& pd) {
28*07fb1d06SElliott Hughes switch (pd.type) {
29*07fb1d06SElliott Hughes case PuffData::Type::kLiterals:
30*07fb1d06SElliott Hughes if (pd.length == 0) {
31*07fb1d06SElliott Hughes return true;
32*07fb1d06SElliott Hughes }
33*07fb1d06SElliott Hughes FALLTHROUGH_INTENDED;
34*07fb1d06SElliott Hughes case PuffData::Type::kLiteral: {
35*07fb1d06SElliott Hughes DVLOG(2) << "Write literals length: " << pd.length;
36*07fb1d06SElliott Hughes size_t length = pd.type == PuffData::Type::kLiteral ? 1 : pd.length;
37*07fb1d06SElliott Hughes if (state_ == State::kWritingNonLiteral) {
38*07fb1d06SElliott Hughes len_index_ = index_;
39*07fb1d06SElliott Hughes index_++;
40*07fb1d06SElliott Hughes state_ = State::kWritingSmallLiteral;
41*07fb1d06SElliott Hughes }
42*07fb1d06SElliott Hughes if (state_ == State::kWritingSmallLiteral) {
43*07fb1d06SElliott Hughes if ((cur_literals_length_ + length) > 127) {
44*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
45*07fb1d06SElliott Hughes // Boundary check
46*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(index_ + 2 <= puff_size_);
47*07fb1d06SElliott Hughes
48*07fb1d06SElliott Hughes // Shift two bytes forward to open space for length value.
49*07fb1d06SElliott Hughes memmove(&puff_buf_out_[len_index_ + 3],
50*07fb1d06SElliott Hughes &puff_buf_out_[len_index_ + 1], cur_literals_length_);
51*07fb1d06SElliott Hughes }
52*07fb1d06SElliott Hughes index_ += 2;
53*07fb1d06SElliott Hughes state_ = State::kWritingLargeLiteral;
54*07fb1d06SElliott Hughes }
55*07fb1d06SElliott Hughes }
56*07fb1d06SElliott Hughes
57*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
58*07fb1d06SElliott Hughes // Boundary check
59*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(index_ + length <= puff_size_);
60*07fb1d06SElliott Hughes if (pd.type == PuffData::Type::kLiteral) {
61*07fb1d06SElliott Hughes puff_buf_out_[index_] = pd.byte;
62*07fb1d06SElliott Hughes } else {
63*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pd.read_fn(&puff_buf_out_[index_], length));
64*07fb1d06SElliott Hughes }
65*07fb1d06SElliott Hughes } else if (pd.type == PuffData::Type::kLiterals) {
66*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pd.read_fn(nullptr, length));
67*07fb1d06SElliott Hughes }
68*07fb1d06SElliott Hughes
69*07fb1d06SElliott Hughes index_ += length;
70*07fb1d06SElliott Hughes cur_literals_length_ += length;
71*07fb1d06SElliott Hughes
72*07fb1d06SElliott Hughes // Technically with the current structure of the puff stream, we cannot
73*07fb1d06SElliott Hughes // have total length of more than 65663 bytes for a series of literals. So
74*07fb1d06SElliott Hughes // we have to cap it at 65663 and continue afterwards.
75*07fb1d06SElliott Hughes if (cur_literals_length_ == kLiteralsMaxLength) {
76*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(FlushLiterals());
77*07fb1d06SElliott Hughes }
78*07fb1d06SElliott Hughes break;
79*07fb1d06SElliott Hughes }
80*07fb1d06SElliott Hughes case PuffData::Type::kLenDist:
81*07fb1d06SElliott Hughes DVLOG(2) << "Write length: " << pd.length << " distance: " << pd.distance;
82*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(FlushLiterals());
83*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pd.length <= 258 && pd.length >= 3);
84*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pd.distance <= 32768 && pd.distance >= 1);
85*07fb1d06SElliott Hughes if (pd.length < 130) {
86*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
87*07fb1d06SElliott Hughes // Boundary check
88*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(index_ + 3 <= puff_size_);
89*07fb1d06SElliott Hughes
90*07fb1d06SElliott Hughes puff_buf_out_[index_++] =
91*07fb1d06SElliott Hughes kLenDistHeader | static_cast<uint8_t>(pd.length - 3);
92*07fb1d06SElliott Hughes } else {
93*07fb1d06SElliott Hughes index_++;
94*07fb1d06SElliott Hughes }
95*07fb1d06SElliott Hughes } else {
96*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
97*07fb1d06SElliott Hughes // Boundary check
98*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(index_ + 4 <= puff_size_);
99*07fb1d06SElliott Hughes
100*07fb1d06SElliott Hughes puff_buf_out_[index_++] = kLenDistHeader | 127;
101*07fb1d06SElliott Hughes puff_buf_out_[index_++] = static_cast<uint8_t>(pd.length - 3 - 127);
102*07fb1d06SElliott Hughes } else {
103*07fb1d06SElliott Hughes index_ += 2;
104*07fb1d06SElliott Hughes }
105*07fb1d06SElliott Hughes }
106*07fb1d06SElliott Hughes
107*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
108*07fb1d06SElliott Hughes // Write the distance in the range [1..32768] zero-based.
109*07fb1d06SElliott Hughes WriteUint16ToByteArray(pd.distance - 1, &puff_buf_out_[index_]);
110*07fb1d06SElliott Hughes }
111*07fb1d06SElliott Hughes index_ += 2;
112*07fb1d06SElliott Hughes len_index_ = index_;
113*07fb1d06SElliott Hughes state_ = State::kWritingNonLiteral;
114*07fb1d06SElliott Hughes break;
115*07fb1d06SElliott Hughes
116*07fb1d06SElliott Hughes case PuffData::Type::kBlockMetadata:
117*07fb1d06SElliott Hughes DVLOG(2) << "Write block metadata length: " << pd.length;
118*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(FlushLiterals());
119*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(pd.length <= sizeof(pd.block_metadata) &&
120*07fb1d06SElliott Hughes pd.length > 0);
121*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
122*07fb1d06SElliott Hughes // Boundary check
123*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(index_ + pd.length + 2 <= puff_size_);
124*07fb1d06SElliott Hughes
125*07fb1d06SElliott Hughes WriteUint16ToByteArray(pd.length - 1, &puff_buf_out_[index_]);
126*07fb1d06SElliott Hughes }
127*07fb1d06SElliott Hughes index_ += 2;
128*07fb1d06SElliott Hughes
129*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
130*07fb1d06SElliott Hughes memcpy(&puff_buf_out_[index_], pd.block_metadata, pd.length);
131*07fb1d06SElliott Hughes }
132*07fb1d06SElliott Hughes index_ += pd.length;
133*07fb1d06SElliott Hughes len_index_ = index_;
134*07fb1d06SElliott Hughes state_ = State::kWritingNonLiteral;
135*07fb1d06SElliott Hughes break;
136*07fb1d06SElliott Hughes
137*07fb1d06SElliott Hughes case PuffData::Type::kEndOfBlock:
138*07fb1d06SElliott Hughes DVLOG(2) << "Write end of block";
139*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(FlushLiterals());
140*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
141*07fb1d06SElliott Hughes // Boundary check
142*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(index_ + 2 <= puff_size_);
143*07fb1d06SElliott Hughes
144*07fb1d06SElliott Hughes puff_buf_out_[index_++] = kLenDistHeader | 127;
145*07fb1d06SElliott Hughes puff_buf_out_[index_++] = static_cast<uint8_t>(259 - 3 - 127);
146*07fb1d06SElliott Hughes } else {
147*07fb1d06SElliott Hughes index_ += 2;
148*07fb1d06SElliott Hughes }
149*07fb1d06SElliott Hughes
150*07fb1d06SElliott Hughes len_index_ = index_;
151*07fb1d06SElliott Hughes state_ = State::kWritingNonLiteral;
152*07fb1d06SElliott Hughes break;
153*07fb1d06SElliott Hughes
154*07fb1d06SElliott Hughes default:
155*07fb1d06SElliott Hughes LOG(ERROR) << "Invalid PuffData::Type";
156*07fb1d06SElliott Hughes return false;
157*07fb1d06SElliott Hughes }
158*07fb1d06SElliott Hughes return true;
159*07fb1d06SElliott Hughes }
160*07fb1d06SElliott Hughes
FlushLiterals()161*07fb1d06SElliott Hughes bool BufferPuffWriter::FlushLiterals() {
162*07fb1d06SElliott Hughes if (cur_literals_length_ == 0) {
163*07fb1d06SElliott Hughes return true;
164*07fb1d06SElliott Hughes }
165*07fb1d06SElliott Hughes switch (state_) {
166*07fb1d06SElliott Hughes case State::kWritingSmallLiteral:
167*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(cur_literals_length_ == (index_ - len_index_ - 1));
168*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
169*07fb1d06SElliott Hughes puff_buf_out_[len_index_] =
170*07fb1d06SElliott Hughes kLiteralsHeader | static_cast<uint8_t>(cur_literals_length_ - 1);
171*07fb1d06SElliott Hughes }
172*07fb1d06SElliott Hughes len_index_ = index_;
173*07fb1d06SElliott Hughes state_ = State::kWritingNonLiteral;
174*07fb1d06SElliott Hughes DVLOG(2) << "Write small literals length: " << cur_literals_length_;
175*07fb1d06SElliott Hughes break;
176*07fb1d06SElliott Hughes
177*07fb1d06SElliott Hughes case State::kWritingLargeLiteral:
178*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(cur_literals_length_ == (index_ - len_index_ - 3));
179*07fb1d06SElliott Hughes if (puff_buf_out_ != nullptr) {
180*07fb1d06SElliott Hughes puff_buf_out_[len_index_++] = kLiteralsHeader | 127;
181*07fb1d06SElliott Hughes WriteUint16ToByteArray(
182*07fb1d06SElliott Hughes static_cast<uint16_t>(cur_literals_length_ - 127 - 1),
183*07fb1d06SElliott Hughes &puff_buf_out_[len_index_]);
184*07fb1d06SElliott Hughes }
185*07fb1d06SElliott Hughes
186*07fb1d06SElliott Hughes len_index_ = index_;
187*07fb1d06SElliott Hughes state_ = State::kWritingNonLiteral;
188*07fb1d06SElliott Hughes DVLOG(2) << "Write large literals length: " << cur_literals_length_;
189*07fb1d06SElliott Hughes break;
190*07fb1d06SElliott Hughes
191*07fb1d06SElliott Hughes case State::kWritingNonLiteral:
192*07fb1d06SElliott Hughes // Do nothing.
193*07fb1d06SElliott Hughes break;
194*07fb1d06SElliott Hughes
195*07fb1d06SElliott Hughes default:
196*07fb1d06SElliott Hughes LOG(ERROR) << "Invalid State";
197*07fb1d06SElliott Hughes return false;
198*07fb1d06SElliott Hughes }
199*07fb1d06SElliott Hughes cur_literals_length_ = 0;
200*07fb1d06SElliott Hughes return true;
201*07fb1d06SElliott Hughes }
202*07fb1d06SElliott Hughes
Flush()203*07fb1d06SElliott Hughes bool BufferPuffWriter::Flush() {
204*07fb1d06SElliott Hughes TEST_AND_RETURN_FALSE(FlushLiterals());
205*07fb1d06SElliott Hughes return true;
206*07fb1d06SElliott Hughes }
207*07fb1d06SElliott Hughes
Size()208*07fb1d06SElliott Hughes size_t BufferPuffWriter::Size() {
209*07fb1d06SElliott Hughes return index_;
210*07fb1d06SElliott Hughes }
211*07fb1d06SElliott Hughes
212*07fb1d06SElliott Hughes } // namespace puffin
213