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