Home | History | Annotate | Download | only in src
      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