| Index: net/http2/hpack/decoder/hpack_varint_decoder.h
|
| diff --git a/net/http2/hpack/decoder/hpack_varint_decoder.h b/net/http2/hpack/decoder/hpack_varint_decoder.h
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b110fad23a983b40b0e7974e56bd43ca9bbf56e1
|
| --- /dev/null
|
| +++ b/net/http2/hpack/decoder/hpack_varint_decoder.h
|
| @@ -0,0 +1,181 @@
|
| +// 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. 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.
|
| +//
|
| +// The caller will need to validate that the decoded value is in an acceptable
|
| +// range.
|
| +//
|
| +// In order to support naive encoders (i.e. which always output 5 extension
|
| +// bytes for a uint32 that is >= prefix_mask), the decoder supports an an
|
| +// encoding with up to 5 extension bytes, and a maximum value of 268,435,582
|
| +// (4 "full" extension bytes plus the maximum for a prefix, 127). It could be
|
| +// modified to support a lower maximum value (by requiring that extensions bytes
|
| +// be "empty"), or a larger value if valuable for some reason I can't see.
|
| +//
|
| +// For details of the encoding, see:
|
| +// http://httpwg.org/specs/rfc7541.html#integer.representation
|
| +//
|
| +// TODO(jamessynge): Consider dropping support for encodings of more than 4
|
| +// bytes, including the prefix byte, as in practice we only see at most 3 bytes,
|
| +// and 4 bytes would cover any desire to support large (but not ridiculously
|
| +// large) header values.
|
| +
|
| +#ifndef NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
|
| +#define NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
|
| +
|
| +#include <string>
|
| +
|
| +#include "base/logging.h"
|
| +#include "net/base/net_export.h"
|
| +#include "net/http2/decoder/decode_buffer.h"
|
| +#include "net/http2/decoder/decode_status.h"
|
| +
|
| +namespace net {
|
| +// 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 NET_EXPORT_PRIVATE HpackVarintDecoder {
|
| + public:
|
| + // |prefix_value| is the first byte of the encoded varint.
|
| + // |prefix_mask| is the mask of the valid bits, i.e. without the top 1 to 4
|
| + // high-bits set, as appropriate for the item being decoded; must be a
|
| + // contiguous sequence of set bits, starting with the low-order bits.
|
| + DecodeStatus Start(uint8_t prefix_value,
|
| + uint8_t prefix_mask,
|
| + DecodeBuffer* db) {
|
| + DCHECK_LE(15, prefix_mask) << std::hex << prefix_mask;
|
| + DCHECK_LE(prefix_mask, 127) << std::hex << prefix_mask;
|
| + // Confirm that |prefix_mask| is a contiguous sequence of bits.
|
| + DCHECK_EQ(0, (prefix_mask + 1) & prefix_mask) << std::hex << prefix_mask;
|
| +
|
| + // 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);
|
| + }
|
| +
|
| + // The caller has already determined that the encoding requires multiple
|
| + // bytes, i.e. that the 4 to 7 low-order bits (the number determined by the
|
| + // prefix length, a value not passed into this function) of the first byte are
|
| + // are all 1. The caller passes in |prefix_mask|, which is 2^prefix_length-1.
|
| + DecodeStatus StartExtended(uint8_t prefix_mask, DecodeBuffer* db) {
|
| + DCHECK_LE(15, prefix_mask) << std::hex << prefix_mask;
|
| + DCHECK_LE(prefix_mask, 127) << std::hex << prefix_mask;
|
| + // Confirm that |prefix_mask| is a contiguous sequence of bits.
|
| + DCHECK_EQ(0, prefix_mask & (prefix_mask + 1)) << std::hex << prefix_mask;
|
| +
|
| + value_ = prefix_mask;
|
| + offset_ = 0;
|
| + return Resume(db);
|
| + }
|
| +
|
| + // Resume decoding a variable length integer after an earlier
|
| + // call to Start or StartExtended returned kDecodeInProgress.
|
| + DecodeStatus Resume(DecodeBuffer* db) {
|
| + CheckNotDone();
|
| + do {
|
| + if (db->Empty()) {
|
| + return DecodeStatus::kDecodeInProgress;
|
| + }
|
| + uint8_t byte = db->DecodeUInt8();
|
| + value_ += (byte & 0x7f) << offset_;
|
| + if ((byte & 0x80) == 0) {
|
| + if (offset_ < MaxOffset() || byte == 0) {
|
| + MarkDone();
|
| + return DecodeStatus::kDecodeDone;
|
| + }
|
| + break;
|
| + }
|
| + offset_ += 7;
|
| + } while (offset_ <= MaxOffset());
|
| + DLOG(WARNING) << "Variable length int encoding is too large or too long. "
|
| + << DebugString();
|
| + MarkDone();
|
| + return DecodeStatus::kDecodeError;
|
| + }
|
| +
|
| + uint32_t value() const {
|
| + CheckDone();
|
| + return value_;
|
| + }
|
| +
|
| + // 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(uint32_t v) {
|
| + MarkDone();
|
| + value_ = 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_mask,
|
| + DecodeBuffer* db);
|
| + DecodeStatus StartExtendedForTest(uint8_t prefix_mask, DecodeBuffer* db);
|
| + DecodeStatus ResumeForTest(DecodeBuffer* db);
|
| +
|
| + static constexpr uint32_t MaxExtensionBytes() { return 5; }
|
| +
|
| + // Returns the highest value with the specified number of extension bytes and
|
| + // the specified prefix length (bits).
|
| + static uint64_t constexpr HiValueOfExtensionBytes(uint32_t extension_bytes,
|
| + uint32_t prefix_length) {
|
| + return (1 << prefix_length) - 2 +
|
| + (extension_bytes == 0 ? 0 : (1LLU << (extension_bytes * 7)));
|
| + }
|
| +
|
| + private:
|
| + // Protection in case Resume is called when it shouldn't be.
|
| + void MarkDone() {
|
| +#ifndef NDEBUG
|
| + // We support up to 5 extension bytes, so offset_ should never be > 28 when
|
| + // it makes sense to call Resume().
|
| + offset_ = MaxOffset() + 7;
|
| +#endif
|
| + }
|
| + void CheckNotDone() const {
|
| +#ifndef NDEBUG
|
| + DCHECK_LE(offset_, MaxOffset());
|
| +#endif
|
| + }
|
| + void CheckDone() const {
|
| +#ifndef NDEBUG
|
| + DCHECK_GT(offset_, MaxOffset());
|
| +#endif
|
| + }
|
| + static constexpr uint32_t MaxOffset() {
|
| + return 7 * (MaxExtensionBytes() - 1);
|
| + }
|
| +
|
| + // These fields are initialized just to keep ASAN happy about reading
|
| + // them from DebugString().
|
| + uint32_t value_ = 0;
|
| + uint32_t offset_ = 0;
|
| +};
|
| +
|
| +std::ostream& operator<<(std::ostream& out, const HpackVarintDecoder& v);
|
| +
|
| +} // namespace net
|
| +
|
| +#endif // NET_HTTP2_HPACK_DECODER_HPACK_VARINT_DECODER_H_
|
|
|