summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc
diff options
context:
space:
mode:
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.cc143
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