| // 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 "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h" | |
| #include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h" | |
| namespace http2 { | |
| DecodeStatus HpackVarintDecoder::Start(uint8_t prefix_value, | |
| uint8_t prefix_length, | |
| DecodeBuffer* db) { | |
| DCHECK_LE(3u, prefix_length); | |
| 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) { | |
| DCHECK_LE(3u, prefix_length); | |
| 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. | |
| DCHECK_LE(offset_, 56); | |
| 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. | |
| 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; | |
| } | |
| 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 Http2StrCat("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 |