• 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/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