diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-05-17 17:24:03 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2022-06-22 07:51:41 +0000 |
commit | 774f54339e5db91f785733232d3950366db65d07 (patch) | |
tree | 068e1b47bd1af94d77094ed12b604a6b83d9c22a /chromium/net/third_party/quiche/src/quiche/http2/hpack/varint | |
parent | f7eaed5286974984ba5f9e3189d8f49d03e99f81 (diff) | |
download | qtwebengine-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')
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 |