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