blob: a9e6e74cb071d3e1d9e0abd7f7af25f7e8ca98cd [file] [log] [blame]
// Copyright 2016 The Chromium 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 "http2/hpack/varint/hpack_varint_decoder.h"
#include "absl/strings/str_cat.h"
namespace http2 {
DecodeStatus HpackVarintDecoder::Start(uint8_t prefix_value,
uint8_t prefix_length,
DecodeBuffer* db) {
QUICHE_DCHECK_LE(3u, prefix_length);
QUICHE_DCHECK_LE(prefix_length, 8u);
// |prefix_mask| defines the sequence of low-order bits of the first byte
// that encode the prefix of the value. It is also the marker in those bits
// of the first byte indicating that at least one extension byte is needed.
const uint8_t prefix_mask = (1 << prefix_length) - 1;
// Ignore the bits that aren't a part of the prefix of the varint.
value_ = prefix_value & prefix_mask;
if (value_ < prefix_mask) {
MarkDone();
return DecodeStatus::kDecodeDone;
}
offset_ = 0;
return Resume(db);
}
DecodeStatus HpackVarintDecoder::StartExtended(uint8_t prefix_length,
DecodeBuffer* db) {
QUICHE_DCHECK_LE(3u, prefix_length);
QUICHE_DCHECK_LE(prefix_length, 8u);
value_ = (1 << prefix_length) - 1;
offset_ = 0;
return Resume(db);
}
DecodeStatus HpackVarintDecoder::Resume(DecodeBuffer* db) {
// There can be at most 10 continuation bytes. Offset is zero for the
// first one and increases by 7 for each subsequent one.
const uint8_t kMaxOffset = 63;
CheckNotDone();
// Process most extension bytes without the need for overflow checking.
while (offset_ < kMaxOffset) {
if (db->Empty()) {
return DecodeStatus::kDecodeInProgress;
}
uint8_t byte = db->DecodeUInt8();
uint64_t summand = byte & 0x7f;
// Shifting a 7 bit value to the left by at most 56 places can never
// overflow on uint64_t.
QUICHE_DCHECK_LE(offset_, 56);
QUICHE_DCHECK_LE(summand, std::numeric_limits<uint64_t>::max() >> offset_);
summand <<= offset_;
// At this point,
// |value_| is at most (2^prefix_length - 1) + (2^49 - 1), and
// |summand| is at most 255 << 56 (which is smaller than 2^63),
// so adding them can never overflow on uint64_t.
QUICHE_DCHECK_LE(value_, std::numeric_limits<uint64_t>::max() - summand);
value_ += summand;
// Decoding ends if continuation flag is not set.
if ((byte & 0x80) == 0) {
MarkDone();
return DecodeStatus::kDecodeDone;
}
offset_ += 7;
}
if (db->Empty()) {
return DecodeStatus::kDecodeInProgress;
}
QUICHE_DCHECK_EQ(kMaxOffset, offset_);
uint8_t byte = db->DecodeUInt8();
// No more extension bytes are allowed after this.
if ((byte & 0x80) == 0) {
uint64_t summand = byte & 0x7f;
// Check for overflow in left shift.
if (summand <= std::numeric_limits<uint64_t>::max() >> offset_) {
summand <<= offset_;
// Check for overflow in addition.
if (value_ <= std::numeric_limits<uint64_t>::max() - summand) {
value_ += summand;
MarkDone();
return DecodeStatus::kDecodeDone;
}
}
}
// Signal error if value is too large or there are too many extension bytes.
HTTP2_DLOG(WARNING)
<< "Variable length int encoding is too large or too long. "
<< DebugString();
MarkDone();
return DecodeStatus::kDecodeError;
}
uint64_t HpackVarintDecoder::value() const {
CheckDone();
return value_;
}
void HpackVarintDecoder::set_value(uint64_t v) {
MarkDone();
value_ = v;
}
std::string HpackVarintDecoder::DebugString() const {
return absl::StrCat("HpackVarintDecoder(value=", value_, ", offset=", offset_,
")");
}
DecodeStatus HpackVarintDecoder::StartForTest(uint8_t prefix_value,
uint8_t prefix_length,
DecodeBuffer* db) {
return Start(prefix_value, prefix_length, db);
}
DecodeStatus HpackVarintDecoder::StartExtendedForTest(uint8_t prefix_length,
DecodeBuffer* db) {
return StartExtended(prefix_length, db);
}
DecodeStatus HpackVarintDecoder::ResumeForTest(DecodeBuffer* db) {
return Resume(db);
}
} // namespace http2