diff options
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc')
-rw-r--r-- | chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc new file mode 100644 index 00000000000..33348073f44 --- /dev/null +++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc @@ -0,0 +1,143 @@ +// 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 "quiche/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 |