summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2022-05-17 17:24:03 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-06-22 07:51:41 +0000
commit774f54339e5db91f785733232d3950366db65d07 (patch)
tree068e1b47bd1af94d77094ed12b604a6b83d9c22a /chromium/net/third_party/quiche/src/quiche/http2/hpack/varint
parentf7eaed5286974984ba5f9e3189d8f49d03e99f81 (diff)
downloadqtwebengine-chromium-774f54339e5db91f785733232d3950366db65d07.tar.gz
BASELINE: Update Chromium to 102.0.5005.57
Change-Id: I885f714bb40ee724c28f94ca6bd8dbdb39915158 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/http2/hpack/varint')
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc143
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.h128
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder_test.cc309
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc47
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.h29
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder_test.cc161
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_round_trip_test.cc416
7 files changed, 1233 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
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.h
new file mode 100644
index 00000000000..d2c8feb3abb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.h
@@ -0,0 +1,128 @@
+// 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.
+
+// HpackVarintDecoder decodes HPACK variable length unsigned integers. In HPACK,
+// these integers are used to identify static or dynamic table index entries, to
+// specify string lengths, and to update the size limit of the dynamic table.
+// In QPACK, in addition to these uses, these integers also identify streams.
+//
+// The caller will need to validate that the decoded value is in an acceptable
+// range.
+//
+// For details of the encoding, see:
+// http://httpwg.org/specs/rfc7541.html#integer.representation
+//
+// HpackVarintDecoder supports decoding any integer that can be represented on
+// uint64_t, thereby exceeding the requirements for QPACK: "QPACK
+// implementations MUST be able to decode integers up to 62 bits long." See
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.1.1
+//
+// This decoder supports at most 10 extension bytes (bytes following the prefix,
+// also called continuation bytes). An encoder is allowed to zero pad the
+// encoded integer on the left, thereby increasing the number of extension
+// bytes. If an encoder uses so much padding that the number of extension bytes
+// exceeds the limit, then this decoder signals an error.
+
+#ifndef QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_
+#define QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_
+
+#include <cstdint>
+#include <limits>
+#include <string>
+
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+// Sentinel value for |HpackVarintDecoder::offset_| to signify that decoding is
+// completed. Only used in debug builds.
+#ifndef NDEBUG
+const uint8_t kHpackVarintDecoderOffsetDone =
+ std::numeric_limits<uint8_t>::max();
+#endif
+
+// Decodes an HPACK variable length unsigned integer, in a resumable fashion
+// so it can handle running out of input in the DecodeBuffer. Call Start or
+// StartExtended the first time (when decoding the byte that contains the
+// prefix), then call Resume later if it is necessary to resume. When done,
+// call value() to retrieve the decoded value.
+//
+// No constructor or destructor. Holds no resources, so destruction isn't
+// needed. Start and StartExtended handles the initialization of member
+// variables. This is necessary in order for HpackVarintDecoder to be part
+// of a union.
+class QUICHE_EXPORT_PRIVATE HpackVarintDecoder {
+ public:
+ // |prefix_value| is the first byte of the encoded varint.
+ // |prefix_length| is number of bits in the first byte that are used for
+ // encoding the integer. |db| is the rest of the buffer, that is, not
+ // including the first byte.
+ DecodeStatus Start(uint8_t prefix_value, uint8_t prefix_length,
+ DecodeBuffer* db);
+
+ // The caller has already determined that the encoding requires multiple
+ // bytes, i.e. that the 3 to 8 low-order bits (the number determined by
+ // |prefix_length|) of the first byte are are all 1. |db| is the rest of the
+ // buffer, that is, not including the first byte.
+ DecodeStatus StartExtended(uint8_t prefix_length, DecodeBuffer* db);
+
+ // Resume decoding a variable length integer after an earlier
+ // call to Start or StartExtended returned kDecodeInProgress.
+ DecodeStatus Resume(DecodeBuffer* db);
+
+ uint64_t value() const;
+
+ // This supports optimizations for the case of a varint with zero extension
+ // bytes, where the handling of the prefix is done by the caller.
+ void set_value(uint64_t v);
+
+ // All the public methods below are for supporting assertions and tests.
+
+ std::string DebugString() const;
+
+ // For benchmarking, these methods ensure the decoder
+ // is NOT inlined into the caller.
+ DecodeStatus StartForTest(uint8_t prefix_value, uint8_t prefix_length,
+ DecodeBuffer* db);
+ DecodeStatus StartExtendedForTest(uint8_t prefix_length, DecodeBuffer* db);
+ DecodeStatus ResumeForTest(DecodeBuffer* db);
+
+ private:
+ // Protection in case Resume is called when it shouldn't be.
+ void MarkDone() {
+#ifndef NDEBUG
+ offset_ = kHpackVarintDecoderOffsetDone;
+#endif
+ }
+ void CheckNotDone() const {
+#ifndef NDEBUG
+ QUICHE_DCHECK_NE(kHpackVarintDecoderOffsetDone, offset_);
+#endif
+ }
+ void CheckDone() const {
+#ifndef NDEBUG
+ QUICHE_DCHECK_EQ(kHpackVarintDecoderOffsetDone, offset_);
+#endif
+ }
+
+ // These fields are initialized just to keep ASAN happy about reading
+ // them from DebugString().
+
+ // The encoded integer is being accumulated in |value_|. When decoding is
+ // complete, |value_| holds the result.
+ uint64_t value_ = 0;
+
+ // Each extension byte encodes in its lowest 7 bits a segment of the integer.
+ // |offset_| is the number of places this segment has to be shifted to the
+ // left for decoding. It is zero for the first extension byte, and increases
+ // by 7 for each subsequent extension byte.
+ uint8_t offset_ = 0;
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder_test.cc
new file mode 100644
index 00000000000..020ca0f7a6a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder_test.cc
@@ -0,0 +1,309 @@
+// Copyright 2018 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"
+
+// Test HpackVarintDecoder against hardcoded data.
+
+#include <stddef.h>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+using ::testing::AssertionSuccess;
+
+namespace http2 {
+namespace test {
+namespace {
+
+class HpackVarintDecoderTest
+ : public RandomDecoderTest,
+ public ::testing::WithParamInterface<std::tuple<uint8_t, const char*>> {
+ protected:
+ HpackVarintDecoderTest()
+ : high_bits_(std::get<0>(GetParam())),
+ suffix_(absl::HexStringToBytes(std::get<1>(GetParam()))),
+ prefix_length_(0) {}
+
+ void DecodeExpectSuccess(absl::string_view data, uint32_t prefix_length,
+ uint64_t expected_value) {
+ Validator validator = [expected_value, this](
+ const DecodeBuffer& /*db*/,
+ DecodeStatus /*status*/) -> AssertionResult {
+ VERIFY_EQ(expected_value, decoder_.value())
+ << "Value doesn't match expected: " << decoder_.value()
+ << " != " << expected_value;
+ return AssertionSuccess();
+ };
+
+ // First validate that decoding is done and that we've advanced the cursor
+ // the expected amount.
+ validator = ValidateDoneAndOffset(/* offset = */ data.size(), validator);
+
+ EXPECT_TRUE(Decode(data, prefix_length, validator));
+
+ EXPECT_EQ(expected_value, decoder_.value());
+ }
+
+ void DecodeExpectError(absl::string_view data, uint32_t prefix_length) {
+ Validator validator = [](const DecodeBuffer& /*db*/,
+ DecodeStatus status) -> AssertionResult {
+ VERIFY_EQ(DecodeStatus::kDecodeError, status);
+ return AssertionSuccess();
+ };
+
+ EXPECT_TRUE(Decode(data, prefix_length, validator));
+ }
+
+ private:
+ AssertionResult Decode(absl::string_view data, uint32_t prefix_length,
+ const Validator validator) {
+ prefix_length_ = prefix_length;
+
+ // Copy |data| so that it can be modified.
+ std::string data_copy(data);
+
+ // Bits of the first byte not part of the prefix should be ignored.
+ uint8_t high_bits_mask = 0b11111111 << prefix_length_;
+ data_copy[0] |= (high_bits_mask & high_bits_);
+
+ // Extra bytes appended to the input should be ignored.
+ data_copy.append(suffix_);
+
+ DecodeBuffer b(data_copy);
+
+ // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+ // can call Start with the prefix byte.
+ bool return_non_zero_on_first = true;
+
+ return DecodeAndValidateSeveralWays(&b, return_non_zero_on_first,
+ validator);
+ }
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ QUICHE_CHECK_LT(0u, b->Remaining());
+ uint8_t prefix = b->DecodeUInt8();
+ return decoder_.Start(prefix, prefix_length_, b);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ return decoder_.Resume(b);
+ }
+
+ // Bits of the first byte not part of the prefix.
+ const uint8_t high_bits_;
+ // Extra bytes appended to the input.
+ const std::string suffix_;
+
+ HpackVarintDecoder decoder_;
+ uint8_t prefix_length_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ HpackVarintDecoderTest, HpackVarintDecoderTest,
+ ::testing::Combine(
+ // Bits of the first byte not part of the prefix should be ignored.
+ ::testing::Values(0b00000000, 0b11111111, 0b10101010),
+ // Extra bytes appended to the input should be ignored.
+ ::testing::Values("", "00", "666f6f")));
+
+struct {
+ const char* data;
+ uint32_t prefix_length;
+ uint64_t expected_value;
+} kSuccessTestData[] = {
+ // Zero value with different prefix lengths.
+ {"00", 3, 0},
+ {"00", 4, 0},
+ {"00", 5, 0},
+ {"00", 6, 0},
+ {"00", 7, 0},
+ {"00", 8, 0},
+ // Small values that fit in the prefix.
+ {"06", 3, 6},
+ {"0d", 4, 13},
+ {"10", 5, 16},
+ {"29", 6, 41},
+ {"56", 7, 86},
+ {"bf", 8, 191},
+ // Values of 2^n-1, which have an all-zero extension byte.
+ {"0700", 3, 7},
+ {"0f00", 4, 15},
+ {"1f00", 5, 31},
+ {"3f00", 6, 63},
+ {"7f00", 7, 127},
+ {"ff00", 8, 255},
+ // Values of 2^n-1, plus one extra byte of padding.
+ {"078000", 3, 7},
+ {"0f8000", 4, 15},
+ {"1f8000", 5, 31},
+ {"3f8000", 6, 63},
+ {"7f8000", 7, 127},
+ {"ff8000", 8, 255},
+ // Values requiring one extension byte.
+ {"0760", 3, 103},
+ {"0f2a", 4, 57},
+ {"1f7f", 5, 158},
+ {"3f02", 6, 65},
+ {"7f49", 7, 200},
+ {"ff6f", 8, 366},
+ // Values requiring one extension byte, plus one byte of padding.
+ {"07e000", 3, 103},
+ {"0faa00", 4, 57},
+ {"1fff00", 5, 158},
+ {"3f8200", 6, 65},
+ {"7fc900", 7, 200},
+ {"ffef00", 8, 366},
+ // Values requiring one extension byte, plus two bytes of padding.
+ {"07e08000", 3, 103},
+ {"0faa8000", 4, 57},
+ {"1fff8000", 5, 158},
+ {"3f828000", 6, 65},
+ {"7fc98000", 7, 200},
+ {"ffef8000", 8, 366},
+ // Values requiring one extension byte, plus the maximum amount of padding.
+ {"07e0808080808080808000", 3, 103},
+ {"0faa808080808080808000", 4, 57},
+ {"1fff808080808080808000", 5, 158},
+ {"3f82808080808080808000", 6, 65},
+ {"7fc9808080808080808000", 7, 200},
+ {"ffef808080808080808000", 8, 366},
+ // Values requiring two extension bytes.
+ {"07b260", 3, 12345},
+ {"0f8a2a", 4, 5401},
+ {"1fa87f", 5, 16327},
+ {"3fd002", 6, 399},
+ {"7fff49", 7, 9598},
+ {"ffe32f", 8, 6370},
+ // Values requiring two extension bytes, plus one byte of padding.
+ {"07b2e000", 3, 12345},
+ {"0f8aaa00", 4, 5401},
+ {"1fa8ff00", 5, 16327},
+ {"3fd08200", 6, 399},
+ {"7fffc900", 7, 9598},
+ {"ffe3af00", 8, 6370},
+ // Values requiring two extension bytes, plus the maximum amount of padding.
+ {"07b2e080808080808000", 3, 12345},
+ {"0f8aaa80808080808000", 4, 5401},
+ {"1fa8ff80808080808000", 5, 16327},
+ {"3fd08280808080808000", 6, 399},
+ {"7fffc980808080808000", 7, 9598},
+ {"ffe3af80808080808000", 8, 6370},
+ // Values requiring three extension bytes.
+ {"078ab260", 3, 1579281},
+ {"0fc18a2a", 4, 689488},
+ {"1fada87f", 5, 2085964},
+ {"3fa0d002", 6, 43103},
+ {"7ffeff49", 7, 1212541},
+ {"ff93de23", 8, 585746},
+ // Values requiring three extension bytes, plus one byte of padding.
+ {"078ab2e000", 3, 1579281},
+ {"0fc18aaa00", 4, 689488},
+ {"1fada8ff00", 5, 2085964},
+ {"3fa0d08200", 6, 43103},
+ {"7ffeffc900", 7, 1212541},
+ {"ff93dea300", 8, 585746},
+ // Values requiring four extension bytes.
+ {"079f8ab260", 3, 202147110},
+ {"0fa2c18a2a", 4, 88252593},
+ {"1fd0ada87f", 5, 266999535},
+ {"3ff9a0d002", 6, 5509304},
+ {"7f9efeff49", 7, 155189149},
+ {"ffaa82f404", 8, 10289705},
+ // Values requiring four extension bytes, plus one byte of padding.
+ {"079f8ab2e000", 3, 202147110},
+ {"0fa2c18aaa00", 4, 88252593},
+ {"1fd0ada8ff00", 5, 266999535},
+ {"3ff9a0d08200", 6, 5509304},
+ {"7f9efeffc900", 7, 155189149},
+ {"ffaa82f48400", 8, 10289705},
+ // Values requiring six extension bytes.
+ {"0783aa9f8ab260", 3, 3311978140938},
+ {"0ff0b0a2c18a2a", 4, 1445930244223},
+ {"1fda84d0ada87f", 5, 4374519874169},
+ {"3fb5fbf9a0d002", 6, 90263420404},
+ {"7fcff19efeff49", 7, 2542616951118},
+ {"ff9fa486bbc327", 8, 1358138807070},
+ // Values requiring eight extension bytes.
+ {"07f19883aa9f8ab260", 3, 54263449861016696},
+ {"0f84fdf0b0a2c18a2a", 4, 23690121121119891},
+ {"1fa0dfda84d0ada87f", 5, 71672133617889215},
+ {"3f9ff0b5fbf9a0d002", 6, 1478875878881374},
+ {"7ffbc1cff19efeff49", 7, 41658236125045114},
+ {"ff91b6fb85af99c342", 8, 37450237664484368},
+ // Values requiring ten extension bytes.
+ {"0794f1f19883aa9f8ab201", 3, 12832019021693745307u},
+ {"0fa08f84fdf0b0a2c18a01", 4, 9980690937382242223u},
+ {"1fbfdda0dfda84d0ada801", 5, 12131360551794650846u},
+ {"3f9dc79ff0b5fbf9a0d001", 6, 15006530362736632796u},
+ {"7f8790fbc1cff19efeff01", 7, 18445754019193211014u},
+ {"fffba8c5b8d3fe9f8c8401", 8, 9518498503615141242u},
+ // Maximum value: 2^64-1.
+ {"07f8ffffffffffffffff01", 3, 18446744073709551615u},
+ {"0ff0ffffffffffffffff01", 4, 18446744073709551615u},
+ {"1fe0ffffffffffffffff01", 5, 18446744073709551615u},
+ {"3fc0ffffffffffffffff01", 6, 18446744073709551615u},
+ {"7f80ffffffffffffffff01", 7, 18446744073709551615u},
+ {"ff80feffffffffffffff01", 8, 18446744073709551615u},
+ // Examples from RFC7541 C.1.
+ {"0a", 5, 10},
+ {"1f9a0a", 5, 1337},
+};
+
+TEST_P(HpackVarintDecoderTest, Success) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kSuccessTestData); ++i) {
+ DecodeExpectSuccess(absl::HexStringToBytes(kSuccessTestData[i].data),
+ kSuccessTestData[i].prefix_length,
+ kSuccessTestData[i].expected_value);
+ }
+}
+
+struct {
+ const char* data;
+ uint32_t prefix_length;
+} kErrorTestData[] = {
+ // Too many extension bytes, all 0s (except for extension bit in each byte).
+ {"0780808080808080808080", 3},
+ {"0f80808080808080808080", 4},
+ {"1f80808080808080808080", 5},
+ {"3f80808080808080808080", 6},
+ {"7f80808080808080808080", 7},
+ {"ff80808080808080808080", 8},
+ // Too many extension bytes, all 1s.
+ {"07ffffffffffffffffffff", 3},
+ {"0fffffffffffffffffffff", 4},
+ {"1fffffffffffffffffffff", 5},
+ {"3fffffffffffffffffffff", 6},
+ {"7fffffffffffffffffffff", 7},
+ {"ffffffffffffffffffffff", 8},
+ // Value of 2^64, one higher than maximum of 2^64-1.
+ {"07f9ffffffffffffffff01", 3},
+ {"0ff1ffffffffffffffff01", 4},
+ {"1fe1ffffffffffffffff01", 5},
+ {"3fc1ffffffffffffffff01", 6},
+ {"7f81ffffffffffffffff01", 7},
+ {"ff81feffffffffffffff01", 8},
+ // Maximum value: 2^64-1, with one byte of padding.
+ {"07f8ffffffffffffffff8100", 3},
+ {"0ff0ffffffffffffffff8100", 4},
+ {"1fe0ffffffffffffffff8100", 5},
+ {"3fc0ffffffffffffffff8100", 6},
+ {"7f80ffffffffffffffff8100", 7},
+ {"ff80feffffffffffffff8100", 8}};
+
+TEST_P(HpackVarintDecoderTest, Error) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kErrorTestData); ++i) {
+ DecodeExpectError(absl::HexStringToBytes(kErrorTestData[i].data),
+ kErrorTestData[i].prefix_length);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc
new file mode 100644
index 00000000000..89beb4ae00b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc
@@ -0,0 +1,47 @@
+// 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_encoder.h"
+
+#include <limits>
+
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+
+// static
+void HpackVarintEncoder::Encode(uint8_t high_bits, uint8_t prefix_length,
+ uint64_t varint, std::string* output) {
+ QUICHE_DCHECK_LE(1u, 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;
+ QUICHE_DCHECK_EQ(0, high_bits & prefix_mask);
+
+ if (varint < prefix_mask) {
+ // The integer fits into the prefix in its entirety.
+ unsigned char first_byte = high_bits | static_cast<unsigned char>(varint);
+ output->push_back(first_byte);
+ return;
+ }
+
+ // Extension bytes are needed.
+ unsigned char first_byte = high_bits | prefix_mask;
+ output->push_back(first_byte);
+
+ varint -= prefix_mask;
+ while (varint >= 128) {
+ // Encode the next seven bits, with continuation bit set to one.
+ output->push_back(0b10000000 | (varint % 128));
+ varint >>= 7;
+ }
+
+ // Encode final seven bits, with continuation bit set to zero.
+ output->push_back(varint);
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.h
new file mode 100644
index 00000000000..69acc161a67
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.h
@@ -0,0 +1,29 @@
+// 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.
+
+#ifndef QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_ENCODER_H_
+#define QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_ENCODER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+// HPACK integer encoder class with single static method implementing variable
+// length integer representation defined in RFC7541, Section 5.1:
+// https://httpwg.org/specs/rfc7541.html#integer.representation
+class QUICHE_EXPORT_PRIVATE HpackVarintEncoder {
+ public:
+ // Encode |varint|, appending encoded data to |*output|.
+ // Appends between 1 and 11 bytes in total.
+ static void Encode(uint8_t high_bits, uint8_t prefix_length, uint64_t varint,
+ std::string* output);
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder_test.cc
new file mode 100644
index 00000000000..bd51606b079
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder_test.cc
@@ -0,0 +1,161 @@
+// 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_encoder.h"
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+struct {
+ uint8_t high_bits;
+ uint8_t prefix_length;
+ uint64_t value;
+ uint8_t expected_encoding;
+} kShortTestData[] = {{0b10110010, 1, 0, 0b10110010},
+ {0b10101100, 2, 2, 0b10101110},
+ {0b10100000, 3, 6, 0b10100110},
+ {0b10110000, 4, 13, 0b10111101},
+ {0b10100000, 5, 8, 0b10101000},
+ {0b11000000, 6, 48, 0b11110000},
+ {0b10000000, 7, 99, 0b11100011},
+ // Example from RFC7541 C.1.
+ {0b00000000, 5, 10, 0b00001010}};
+
+// Encode integers that fit in the prefix.
+TEST(HpackVarintEncoderTest, Short) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kShortTestData); ++i) {
+ std::string output;
+ HpackVarintEncoder::Encode(kShortTestData[i].high_bits,
+ kShortTestData[i].prefix_length,
+ kShortTestData[i].value, &output);
+ ASSERT_EQ(1u, output.size());
+ EXPECT_EQ(kShortTestData[i].expected_encoding,
+ static_cast<uint8_t>(output[0]));
+ }
+}
+
+struct {
+ uint8_t high_bits;
+ uint8_t prefix_length;
+ uint64_t value;
+ const char* expected_encoding;
+} kLongTestData[] = {
+ // One extension byte.
+ {0b10011000, 3, 103, "9f60"},
+ {0b10010000, 4, 57, "9f2a"},
+ {0b11000000, 5, 158, "df7f"},
+ {0b01000000, 6, 65, "7f02"},
+ {0b00000000, 7, 200, "7f49"},
+ // Two extension bytes.
+ {0b10011000, 3, 12345, "9fb260"},
+ {0b10010000, 4, 5401, "9f8a2a"},
+ {0b11000000, 5, 16327, "dfa87f"},
+ {0b01000000, 6, 399, "7fd002"},
+ {0b00000000, 7, 9598, "7fff49"},
+ // Three extension bytes.
+ {0b10011000, 3, 1579281, "9f8ab260"},
+ {0b10010000, 4, 689488, "9fc18a2a"},
+ {0b11000000, 5, 2085964, "dfada87f"},
+ {0b01000000, 6, 43103, "7fa0d002"},
+ {0b00000000, 7, 1212541, "7ffeff49"},
+ // Four extension bytes.
+ {0b10011000, 3, 202147110, "9f9f8ab260"},
+ {0b10010000, 4, 88252593, "9fa2c18a2a"},
+ {0b11000000, 5, 266999535, "dfd0ada87f"},
+ {0b01000000, 6, 5509304, "7ff9a0d002"},
+ {0b00000000, 7, 155189149, "7f9efeff49"},
+ // Six extension bytes.
+ {0b10011000, 3, 3311978140938, "9f83aa9f8ab260"},
+ {0b10010000, 4, 1445930244223, "9ff0b0a2c18a2a"},
+ {0b11000000, 5, 4374519874169, "dfda84d0ada87f"},
+ {0b01000000, 6, 90263420404, "7fb5fbf9a0d002"},
+ {0b00000000, 7, 2542616951118, "7fcff19efeff49"},
+ // Eight extension bytes.
+ {0b10011000, 3, 54263449861016696, "9ff19883aa9f8ab260"},
+ {0b10010000, 4, 23690121121119891, "9f84fdf0b0a2c18a2a"},
+ {0b11000000, 5, 71672133617889215, "dfa0dfda84d0ada87f"},
+ {0b01000000, 6, 1478875878881374, "7f9ff0b5fbf9a0d002"},
+ {0b00000000, 7, 41658236125045114, "7ffbc1cff19efeff49"},
+ // Ten extension bytes.
+ {0b10011000, 3, 12832019021693745307u, "9f94f1f19883aa9f8ab201"},
+ {0b10010000, 4, 9980690937382242223u, "9fa08f84fdf0b0a2c18a01"},
+ {0b11000000, 5, 12131360551794650846u, "dfbfdda0dfda84d0ada801"},
+ {0b01000000, 6, 15006530362736632796u, "7f9dc79ff0b5fbf9a0d001"},
+ {0b00000000, 7, 18445754019193211014u, "7f8790fbc1cff19efeff01"},
+ // Maximum value: 2^64-1.
+ {0b10011000, 3, 18446744073709551615u, "9ff8ffffffffffffffff01"},
+ {0b10010000, 4, 18446744073709551615u, "9ff0ffffffffffffffff01"},
+ {0b11000000, 5, 18446744073709551615u, "dfe0ffffffffffffffff01"},
+ {0b01000000, 6, 18446744073709551615u, "7fc0ffffffffffffffff01"},
+ {0b00000000, 7, 18446744073709551615u, "7f80ffffffffffffffff01"},
+ // Example from RFC7541 C.1.
+ {0b00000000, 5, 1337, "1f9a0a"},
+};
+
+// Encode integers that do not fit in the prefix.
+TEST(HpackVarintEncoderTest, Long) {
+ // Test encoding byte by byte, also test encoding in
+ // a single ResumeEncoding() call.
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kLongTestData); ++i) {
+ std::string expected_encoding =
+ absl::HexStringToBytes(kLongTestData[i].expected_encoding);
+
+ std::string output;
+ HpackVarintEncoder::Encode(kLongTestData[i].high_bits,
+ kLongTestData[i].prefix_length,
+ kLongTestData[i].value, &output);
+
+ EXPECT_EQ(expected_encoding, output);
+ }
+}
+
+struct {
+ uint8_t high_bits;
+ uint8_t prefix_length;
+ uint64_t value;
+ uint8_t expected_encoding_first_byte;
+} kLastByteIsZeroTestData[] = {
+ {0b10110010, 1, 1, 0b10110011}, {0b10101100, 2, 3, 0b10101111},
+ {0b10101000, 3, 7, 0b10101111}, {0b10110000, 4, 15, 0b10111111},
+ {0b10100000, 5, 31, 0b10111111}, {0b11000000, 6, 63, 0b11111111},
+ {0b10000000, 7, 127, 0b11111111}, {0b00000000, 8, 255, 0b11111111}};
+
+// Make sure that the encoder outputs the last byte even when it is zero. This
+// happens exactly when encoding the value 2^prefix_length - 1.
+TEST(HpackVarintEncoderTest, LastByteIsZero) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kLastByteIsZeroTestData); ++i) {
+ std::string output;
+ HpackVarintEncoder::Encode(kLastByteIsZeroTestData[i].high_bits,
+ kLastByteIsZeroTestData[i].prefix_length,
+ kLastByteIsZeroTestData[i].value, &output);
+ ASSERT_EQ(2u, output.size());
+ EXPECT_EQ(kLastByteIsZeroTestData[i].expected_encoding_first_byte,
+ static_cast<uint8_t>(output[0]));
+ EXPECT_EQ(0b00000000, output[1]);
+ }
+}
+
+// Test that encoder appends correctly to non-empty string.
+TEST(HpackVarintEncoderTest, Append) {
+ std::string output("foo");
+ EXPECT_EQ(absl::HexStringToBytes("666f6f"), output);
+
+ HpackVarintEncoder::Encode(0b10011000, 3, 103, &output);
+ EXPECT_EQ(absl::HexStringToBytes("666f6f9f60"), output);
+
+ HpackVarintEncoder::Encode(0b10100000, 5, 8, &output);
+ EXPECT_EQ(absl::HexStringToBytes("666f6f9f60a8"), output);
+
+ HpackVarintEncoder::Encode(0b10011000, 3, 202147110, &output);
+ EXPECT_EQ(absl::HexStringToBytes("666f6f9f60a89f9f8ab260"), output);
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_round_trip_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_round_trip_test.cc
new file mode 100644
index 00000000000..ba5164961aa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_round_trip_test.cc
@@ -0,0 +1,416 @@
+// 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"
+
+// Test HpackVarintDecoder against data encoded via HpackBlockBuilder,
+// which uses HpackVarintEncoder under the hood.
+
+#include <stddef.h>
+
+#include <iterator>
+#include <set>
+#include <vector>
+
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/quiche_text_utils.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionSuccess;
+
+namespace http2 {
+namespace test {
+namespace {
+
+// Returns the highest value with the specified number of extension bytes
+// and the specified prefix length (bits).
+uint64_t HiValueOfExtensionBytes(uint32_t extension_bytes,
+ uint32_t prefix_length) {
+ return (1 << prefix_length) - 2 +
+ (extension_bytes == 0 ? 0 : (1LLU << (extension_bytes * 7)));
+}
+
+class HpackVarintRoundTripTest : public RandomDecoderTest {
+ protected:
+ HpackVarintRoundTripTest() : prefix_length_(0) {}
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ QUICHE_CHECK_LT(0u, b->Remaining());
+ uint8_t prefix = b->DecodeUInt8();
+ return decoder_.Start(prefix, prefix_length_, b);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ return decoder_.Resume(b);
+ }
+
+ void DecodeSeveralWays(uint64_t expected_value, uint32_t expected_offset) {
+ // The validator is called after each of the several times that the input
+ // DecodeBuffer is decoded, each with a different segmentation of the input.
+ // Validate that decoder_.value() matches the expected value.
+ Validator validator = [expected_value, this](
+ const DecodeBuffer& /*db*/,
+ DecodeStatus /*status*/) -> AssertionResult {
+ if (decoder_.value() != expected_value) {
+ return AssertionFailure()
+ << "Value doesn't match expected: " << decoder_.value()
+ << " != " << expected_value;
+ }
+ return AssertionSuccess();
+ };
+
+ // First validate that decoding is done and that we've advanced the cursor
+ // the expected amount.
+ validator = ValidateDoneAndOffset(expected_offset, validator);
+
+ // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+ // can call Start with the prefix byte.
+ bool return_non_zero_on_first = true;
+
+ DecodeBuffer b(buffer_);
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, return_non_zero_on_first, validator));
+
+ EXPECT_EQ(expected_value, decoder_.value());
+ EXPECT_EQ(expected_offset, b.Offset());
+ }
+
+ void EncodeNoRandom(uint64_t value, uint8_t prefix_length) {
+ QUICHE_DCHECK_LE(3, prefix_length);
+ QUICHE_DCHECK_LE(prefix_length, 8);
+ prefix_length_ = prefix_length;
+
+ HpackBlockBuilder bb;
+ bb.AppendHighBitsAndVarint(0, prefix_length_, value);
+ buffer_ = bb.buffer();
+ ASSERT_LT(0u, buffer_.size());
+
+ const uint8_t prefix_mask = (1 << prefix_length_) - 1;
+ ASSERT_EQ(static_cast<uint8_t>(buffer_[0]),
+ static_cast<uint8_t>(buffer_[0]) & prefix_mask);
+ }
+
+ void Encode(uint64_t value, uint8_t prefix_length) {
+ EncodeNoRandom(value, prefix_length);
+ // Add some random bits to the prefix (the first byte) above the mask.
+ uint8_t prefix = buffer_[0];
+ buffer_[0] = prefix | (Random().Rand8() << prefix_length);
+ const uint8_t prefix_mask = (1 << prefix_length_) - 1;
+ ASSERT_EQ(prefix, buffer_[0] & prefix_mask);
+ }
+
+ // This is really a test of HpackBlockBuilder, making sure that the input to
+ // HpackVarintDecoder is as expected, which also acts as confirmation that
+ // my thinking about the encodings being used by the tests, i.e. cover the
+ // range desired.
+ void ValidateEncoding(uint64_t value, uint64_t minimum, uint64_t maximum,
+ size_t expected_bytes) {
+ ASSERT_EQ(expected_bytes, buffer_.size());
+ if (expected_bytes > 1) {
+ const uint8_t prefix_mask = (1 << prefix_length_) - 1;
+ EXPECT_EQ(prefix_mask, buffer_[0] & prefix_mask);
+ size_t last = expected_bytes - 1;
+ for (size_t ndx = 1; ndx < last; ++ndx) {
+ // Before the last extension byte, we expect the high-bit set.
+ uint8_t byte = buffer_[ndx];
+ if (value == minimum) {
+ EXPECT_EQ(0x80, byte) << "ndx=" << ndx;
+ } else if (value == maximum) {
+ if (expected_bytes < 11) {
+ EXPECT_EQ(0xff, byte) << "ndx=" << ndx;
+ }
+ } else {
+ EXPECT_EQ(0x80, byte & 0x80) << "ndx=" << ndx;
+ }
+ }
+ // The last extension byte should not have the high-bit set.
+ uint8_t byte = buffer_[last];
+ if (value == minimum) {
+ if (expected_bytes == 2) {
+ EXPECT_EQ(0x00, byte);
+ } else {
+ EXPECT_EQ(0x01, byte);
+ }
+ } else if (value == maximum) {
+ if (expected_bytes < 11) {
+ EXPECT_EQ(0x7f, byte);
+ }
+ } else {
+ EXPECT_EQ(0x00, byte & 0x80);
+ }
+ } else {
+ const uint8_t prefix_mask = (1 << prefix_length_) - 1;
+ EXPECT_EQ(value, static_cast<uint32_t>(buffer_[0] & prefix_mask));
+ EXPECT_LT(value, prefix_mask);
+ }
+ }
+
+ void EncodeAndDecodeValues(const std::set<uint64_t>& values,
+ uint8_t prefix_length, size_t expected_bytes) {
+ QUICHE_CHECK(!values.empty());
+ const uint64_t minimum = *values.begin();
+ const uint64_t maximum = *values.rbegin();
+ for (const uint64_t value : values) {
+ Encode(value, prefix_length); // Sets buffer_.
+
+ std::string msg = absl::StrCat("value=", value, " (0x", absl::Hex(value),
+ "), prefix_length=", prefix_length,
+ ", expected_bytes=", expected_bytes, "\n",
+ quiche::QuicheTextUtils::HexDump(buffer_));
+
+ if (value == minimum) {
+ HTTP2_LOG(INFO) << "Checking minimum; " << msg;
+ } else if (value == maximum) {
+ HTTP2_LOG(INFO) << "Checking maximum; " << msg;
+ }
+
+ SCOPED_TRACE(msg);
+ ValidateEncoding(value, minimum, maximum, expected_bytes);
+ DecodeSeveralWays(value, expected_bytes);
+
+ // Append some random data to the end of buffer_ and repeat. That random
+ // data should be ignored.
+ buffer_.append(Random().RandString(1 + Random().Uniform(10)));
+ DecodeSeveralWays(value, expected_bytes);
+
+ // If possible, add extension bytes that don't change the value.
+ if (1 < expected_bytes) {
+ buffer_.resize(expected_bytes);
+ for (uint8_t total_bytes = expected_bytes + 1; total_bytes <= 6;
+ ++total_bytes) {
+ // Mark the current last byte as not being the last one.
+ EXPECT_EQ(0x00, 0x80 & buffer_.back());
+ buffer_.back() |= 0x80;
+ buffer_.push_back('\0');
+ DecodeSeveralWays(value, total_bytes);
+ }
+ }
+ }
+ }
+
+ // Encode values (all or some of it) in [start, start+range). Check
+ // that |start| is the smallest value and |start+range-1| is the largest value
+ // corresponding to |expected_bytes|, except if |expected_bytes| is maximal.
+ void EncodeAndDecodeValuesInRange(uint64_t start, uint64_t range,
+ uint8_t prefix_length,
+ size_t expected_bytes) {
+ const uint8_t prefix_mask = (1 << prefix_length) - 1;
+ const uint64_t beyond = start + range;
+
+ HTTP2_LOG(INFO)
+ << "############################################################";
+ HTTP2_LOG(INFO) << "prefix_length=" << static_cast<int>(prefix_length);
+ HTTP2_LOG(INFO) << "prefix_mask=" << std::hex
+ << static_cast<int>(prefix_mask);
+ HTTP2_LOG(INFO) << "start=" << start << " (" << std::hex << start << ")";
+ HTTP2_LOG(INFO) << "range=" << range << " (" << std::hex << range << ")";
+ HTTP2_LOG(INFO) << "beyond=" << beyond << " (" << std::hex << beyond << ")";
+ HTTP2_LOG(INFO) << "expected_bytes=" << expected_bytes;
+
+ if (expected_bytes < 11) {
+ // Confirm the claim that beyond requires more bytes.
+ Encode(beyond, prefix_length);
+ EXPECT_EQ(expected_bytes + 1, buffer_.size())
+ << quiche::QuicheTextUtils::HexDump(buffer_);
+ }
+
+ std::set<uint64_t> values;
+ if (range < 200) {
+ // Select all values in the range.
+ for (uint64_t offset = 0; offset < range; ++offset) {
+ values.insert(start + offset);
+ }
+ } else {
+ // Select some values in this range, including the minimum and maximum
+ // values that require exactly |expected_bytes| extension bytes.
+ values.insert({start, start + 1, beyond - 2, beyond - 1});
+ while (values.size() < 100) {
+ values.insert(Random().UniformInRange(start, beyond - 1));
+ }
+ }
+
+ EncodeAndDecodeValues(values, prefix_length, expected_bytes);
+ }
+
+ HpackVarintDecoder decoder_;
+ std::string buffer_;
+ uint8_t prefix_length_;
+};
+
+// To help me and future debuggers of varint encodings, this HTTP2_LOGs out the
+// transition points where a new extension byte is added.
+TEST_F(HpackVarintRoundTripTest, Encode) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t a = HiValueOfExtensionBytes(0, prefix_length);
+ const uint64_t b = HiValueOfExtensionBytes(1, prefix_length);
+ const uint64_t c = HiValueOfExtensionBytes(2, prefix_length);
+ const uint64_t d = HiValueOfExtensionBytes(3, prefix_length);
+ const uint64_t e = HiValueOfExtensionBytes(4, prefix_length);
+ const uint64_t f = HiValueOfExtensionBytes(5, prefix_length);
+ const uint64_t g = HiValueOfExtensionBytes(6, prefix_length);
+ const uint64_t h = HiValueOfExtensionBytes(7, prefix_length);
+ const uint64_t i = HiValueOfExtensionBytes(8, prefix_length);
+ const uint64_t j = HiValueOfExtensionBytes(9, prefix_length);
+
+ HTTP2_LOG(INFO)
+ << "############################################################";
+ HTTP2_LOG(INFO) << "prefix_length=" << prefix_length << " a=" << a
+ << " b=" << b << " c=" << c << " d=" << d
+ << " e=" << e << " f=" << f << " g=" << g
+ << " h=" << h << " i=" << i << " j=" << j;
+
+ std::vector<uint64_t> values = {
+ 0, 1, // Force line break.
+ a - 1, a, a + 1, a + 2, a + 3, // Force line break.
+ b - 1, b, b + 1, b + 2, b + 3, // Force line break.
+ c - 1, c, c + 1, c + 2, c + 3, // Force line break.
+ d - 1, d, d + 1, d + 2, d + 3, // Force line break.
+ e - 1, e, e + 1, e + 2, e + 3, // Force line break.
+ f - 1, f, f + 1, f + 2, f + 3, // Force line break.
+ g - 1, g, g + 1, g + 2, g + 3, // Force line break.
+ h - 1, h, h + 1, h + 2, h + 3, // Force line break.
+ i - 1, i, i + 1, i + 2, i + 3, // Force line break.
+ j - 1, j, j + 1, j + 2, j + 3, // Force line break.
+ };
+
+ for (uint64_t value : values) {
+ EncodeNoRandom(value, prefix_length);
+ std::string dump = quiche::QuicheTextUtils::HexDump(buffer_);
+ HTTP2_LOG(INFO) << absl::StrFormat("%10llu %0#18x ", value, value)
+ << quiche::QuicheTextUtils::HexDump(buffer_).substr(7);
+ }
+ }
+}
+
+TEST_F(HpackVarintRoundTripTest, FromSpec1337) {
+ DecodeBuffer b(absl::string_view("\x1f\x9a\x0a"));
+ uint32_t prefix_length = 5;
+ uint8_t p = b.DecodeUInt8();
+ EXPECT_EQ(1u, b.Offset());
+ EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_.Start(p, prefix_length, &b));
+ EXPECT_EQ(3u, b.Offset());
+ EXPECT_EQ(1337u, decoder_.value());
+
+ EncodeNoRandom(1337, prefix_length);
+ EXPECT_EQ(3u, buffer_.size());
+ EXPECT_EQ('\x1f', buffer_[0]);
+ EXPECT_EQ('\x9a', buffer_[1]);
+ EXPECT_EQ('\x0a', buffer_[2]);
+}
+
+// Test all the values that fit into the prefix (one less than the mask).
+TEST_F(HpackVarintRoundTripTest, ValidatePrefixOnly) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint8_t prefix_mask = (1 << prefix_length) - 1;
+ EncodeAndDecodeValuesInRange(0, prefix_mask, prefix_length, 1);
+ }
+}
+
+// Test all values that require exactly 1 extension byte.
+TEST_F(HpackVarintRoundTripTest, ValidateOneExtensionByte) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(0, prefix_length) + 1;
+ EncodeAndDecodeValuesInRange(start, 128, prefix_length, 2);
+ }
+}
+
+// Test *some* values that require exactly 2 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateTwoExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(1, prefix_length) + 1;
+ const uint64_t range = 127 << 7;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 3);
+ }
+}
+
+// Test *some* values that require 3 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateThreeExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(2, prefix_length) + 1;
+ const uint64_t range = 127 << 14;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 4);
+ }
+}
+
+// Test *some* values that require 4 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateFourExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(3, prefix_length) + 1;
+ const uint64_t range = 127 << 21;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 5);
+ }
+}
+
+// Test *some* values that require 5 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateFiveExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(4, prefix_length) + 1;
+ const uint64_t range = 127llu << 28;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 6);
+ }
+}
+
+// Test *some* values that require 6 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateSixExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(5, prefix_length) + 1;
+ const uint64_t range = 127llu << 35;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 7);
+ }
+}
+
+// Test *some* values that require 7 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateSevenExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(6, prefix_length) + 1;
+ const uint64_t range = 127llu << 42;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 8);
+ }
+}
+
+// Test *some* values that require 8 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateEightExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(7, prefix_length) + 1;
+ const uint64_t range = 127llu << 49;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 9);
+ }
+}
+
+// Test *some* values that require 9 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateNineExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(8, prefix_length) + 1;
+ const uint64_t range = 127llu << 56;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 10);
+ }
+}
+
+// Test *some* values that require 10 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateTenExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(9, prefix_length) + 1;
+ const uint64_t range = std::numeric_limits<uint64_t>::max() - start;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 11);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2