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. 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 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 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 222 bool BufferPuffWriter::Flush(Error* error) { 223 TEST_AND_RETURN_FALSE(FlushLiterals(error)); 224 return true; 225 } 226 227 size_t BufferPuffWriter::Size() { 228 return index_; 229 } 230 231 } // namespace puffin 232