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