• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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