// Copyright 2017 The Chromium OS Authors. All rights reserved.
|
// Use of this source code is governed by a BSD-style license that can be
|
// found in the LICENSE file.
|
|
#include "puffin/src/puff_reader.h"
|
|
#include <algorithm>
|
#include <memory>
|
#include <string>
|
#include <vector>
|
|
#include "puffin/src/logging.h"
|
|
namespace puffin {
|
|
namespace {
|
// Reads a value from the buffer in big-endian mode.
|
inline uint16_t ReadByteArrayToUint16(const uint8_t* buffer) {
|
return (*buffer << 8) | *(buffer + 1);
|
}
|
} // namespace
|
|
bool BufferPuffReader::GetNext(PuffData* data) {
|
PuffData& pd = *data;
|
size_t length = 0;
|
if (state_ == State::kReadingLenDist) {
|
// Boundary check
|
TEST_AND_RETURN_FALSE(index_ < puff_size_);
|
if (puff_buf_in_[index_] & 0x80) { // Reading length/distance.
|
if ((puff_buf_in_[index_] & 0x7F) < 127) {
|
length = puff_buf_in_[index_] & 0x7F;
|
} else {
|
index_++;
|
// Boundary check
|
TEST_AND_RETURN_FALSE(index_ < puff_size_);
|
length = puff_buf_in_[index_] + 127;
|
}
|
length += 3;
|
TEST_AND_RETURN_FALSE(length <= 259);
|
|
index_++;
|
|
// End of block. End of block is similar to length/distance but without
|
// distance value and length value set to 259.
|
if (length == 259) {
|
pd.type = PuffData::Type::kEndOfBlock;
|
state_ = State::kReadingBlockMetadata;
|
DVLOG(2) << "Read end of block";
|
return true;
|
}
|
|
// Boundary check
|
TEST_AND_RETURN_FALSE(index_ + 1 < puff_size_);
|
auto distance = ReadByteArrayToUint16(&puff_buf_in_[index_]);
|
// The distance in RFC is in the range [1..32768], but in the puff spec,
|
// we write zero-based distance in the puff stream.
|
TEST_AND_RETURN_FALSE(distance < (1 << 15));
|
distance++;
|
index_ += 2;
|
|
pd.type = PuffData::Type::kLenDist;
|
pd.length = length;
|
pd.distance = distance;
|
DVLOG(2) << "Read length: " << length << " distance: " << distance;
|
return true;
|
} else { // Reading literals.
|
// Boundary check
|
TEST_AND_RETURN_FALSE(index_ < puff_size_);
|
if ((puff_buf_in_[index_] & 0x7F) < 127) {
|
length = puff_buf_in_[index_] & 0x7F;
|
index_++;
|
} else {
|
index_++;
|
// Boundary check
|
TEST_AND_RETURN_FALSE(index_ + 1 < puff_size_);
|
length = ReadByteArrayToUint16(&puff_buf_in_[index_]) + 127;
|
index_ += 2;
|
}
|
length++;
|
DVLOG(2) << "Read literals length: " << length;
|
// Boundary check
|
TEST_AND_RETURN_FALSE(index_ + length <= puff_size_);
|
pd.type = PuffData::Type::kLiterals;
|
pd.length = length;
|
pd.read_fn = [this, length](uint8_t* buffer, size_t count) mutable {
|
TEST_AND_RETURN_FALSE(count <= length);
|
memcpy(buffer, &puff_buf_in_[index_], count);
|
index_ += count;
|
length -= count;
|
return true;
|
};
|
return true;
|
}
|
} else { // Block metadata
|
pd.type = PuffData::Type::kBlockMetadata;
|
// Boundary check
|
TEST_AND_RETURN_FALSE(index_ + 2 < puff_size_);
|
length = ReadByteArrayToUint16(&puff_buf_in_[index_]) + 1;
|
index_ += 2;
|
DVLOG(2) << "Read block metadata length: " << length;
|
// Boundary check
|
TEST_AND_RETURN_FALSE(index_ + length <= puff_size_);
|
TEST_AND_RETURN_FALSE(length <= sizeof(pd.block_metadata));
|
memcpy(pd.block_metadata, &puff_buf_in_[index_], length);
|
index_ += length;
|
pd.length = length;
|
state_ = State::kReadingLenDist;
|
}
|
return true;
|
}
|
|
size_t BufferPuffReader::BytesLeft() const {
|
return puff_size_ - index_;
|
}
|
|
} // namespace puffin
|