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_reader.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 // Reads a value from the buffer in big-endian mode.
     18 inline uint16_t ReadByteArrayToUint16(const uint8_t* buffer) {
     19   return (*buffer << 8) | *(buffer + 1);
     20 }
     21 }  // namespace
     22 
     23 bool BufferPuffReader::GetNext(PuffData* data, Error* error) {
     24   PuffData& pd = *data;
     25   size_t length = 0;
     26   if (state_ == State::kReadingLenDist) {
     27     // Boundary check
     28     TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_,
     29                                     Error::kInsufficientInput);
     30     if (puff_buf_in_[index_] & 0x80) {  // Reading length/distance.
     31       if ((puff_buf_in_[index_] & 0x7F) < 127) {
     32         length = puff_buf_in_[index_] & 0x7F;
     33       } else {
     34         index_++;
     35         // Boundary check
     36         TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_,
     37                                         Error::kInsufficientInput);
     38         length = puff_buf_in_[index_] + 127;
     39       }
     40       length += 3;
     41       TEST_AND_RETURN_FALSE(length <= 259);
     42 
     43       index_++;
     44 
     45       // End of block. End of block is similar to length/distance but without
     46       // distance value and length value set to 259.
     47       if (length == 259) {
     48         pd.type = PuffData::Type::kEndOfBlock;
     49         state_ = State::kReadingBlockMetadata;
     50         DVLOG(2) << "Read end of block";
     51         return true;
     52       }
     53 
     54       // Boundary check
     55       TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 1 < puff_size_,
     56                                       Error::kInsufficientInput);
     57       auto distance = ReadByteArrayToUint16(&puff_buf_in_[index_]);
     58       // The distance in RFC is in the range [1..32768], but in the puff spec,
     59       // we write zero-based distance in the puff stream.
     60       TEST_AND_RETURN_FALSE_SET_ERROR(distance < (1 << 15),
     61                                       Error::kInsufficientInput);
     62       distance++;
     63       index_ += 2;
     64 
     65       pd.type = PuffData::Type::kLenDist;
     66       pd.length = length;
     67       pd.distance = distance;
     68       DVLOG(2) << "Read length: " << length << " distance: " << distance;
     69       return true;
     70     } else {  // Reading literals.
     71       // Boundary check
     72       TEST_AND_RETURN_FALSE_SET_ERROR(index_ < puff_size_,
     73                                       Error::kInsufficientInput);
     74       if ((puff_buf_in_[index_] & 0x7F) < 127) {
     75         length = puff_buf_in_[index_] & 0x7F;
     76         index_++;
     77       } else {
     78         index_++;
     79         // Boundary check
     80         TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 1 < puff_size_,
     81                                         Error::kInsufficientInput);
     82         length = ReadByteArrayToUint16(&puff_buf_in_[index_]) + 127;
     83         index_ += 2;
     84       }
     85       length++;
     86       DVLOG(2) << "Read literals length: " << length;
     87       // Boundary check
     88       TEST_AND_RETURN_FALSE_SET_ERROR(index_ + length <= puff_size_,
     89                                       Error::kInsufficientInput);
     90       pd.type = PuffData::Type::kLiterals;
     91       pd.length = length;
     92       pd.read_fn = [this, length](uint8_t* buffer, size_t count) mutable {
     93         TEST_AND_RETURN_FALSE(count <= length);
     94         memcpy(buffer, &puff_buf_in_[index_], count);
     95         index_ += count;
     96         length -= count;
     97         return true;
     98       };
     99       return true;
    100     }
    101   } else {  // Block metadata
    102     pd.type = PuffData::Type::kBlockMetadata;
    103     // Boundary check
    104     TEST_AND_RETURN_FALSE_SET_ERROR(index_ + 2 < puff_size_,
    105                                     Error::kInsufficientInput);
    106     length = ReadByteArrayToUint16(&puff_buf_in_[index_]) + 1;
    107     index_ += 2;
    108     DVLOG(2) << "Read block metadata length: " << length;
    109     // Boundary check
    110     TEST_AND_RETURN_FALSE_SET_ERROR(index_ + length <= puff_size_,
    111                                     Error::kInsufficientInput);
    112     TEST_AND_RETURN_FALSE(length <= sizeof(pd.block_metadata));
    113     memcpy(pd.block_metadata, &puff_buf_in_[index_], length);
    114     index_ += length;
    115     pd.length = length;
    116     state_ = State::kReadingLenDist;
    117   }
    118   return true;
    119 }
    120 
    121 size_t BufferPuffReader::BytesLeft() const {
    122   return puff_size_ - index_;
    123 }
    124 
    125 }  // namespace puffin
    126