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
14namespace puffin {
15
16namespace {
17// Reads a value from the buffer in big-endian mode.
18inline uint16_t ReadByteArrayToUint16(const uint8_t* buffer) {
19  return (*buffer << 8) | *(buffer + 1);
20}
21}  // namespace
22
23bool 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
121size_t BufferPuffReader::BytesLeft() const {
122  return puff_size_ - index_;
123}
124
125}  // namespace puffin
126