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. ReadByteArrayToUint16(const uint8_t * buffer)18inline uint16_t ReadByteArrayToUint16(const uint8_t* buffer) { 19 return (*buffer << 8) | *(buffer + 1); 20 } 21 } // namespace 22 GetNext(PuffData * data)23bool 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 BytesLeft() const112size_t BufferPuffReader::BytesLeft() const { 113 return puff_size_ - index_; 114 } 115 116 } // namespace puffin 117