| // 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_THIRD_PARTY_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_ |
| #define NET_THIRD_PARTY_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_ |
| |
| #include <cstdint> |
| |
| #include "base/logging.h" |
| #include "net/third_party/http2/decoder/decode_buffer.h" |
| #include "net/third_party/http2/decoder/decode_status.h" |
| #include "net/third_party/http2/platform/api/http2_export.h" |
| #include "net/third_party/http2/platform/api/http2_string.h" |
| |
| namespace http2 { |
| // 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 HTTP2_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 7 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); |
| |
| uint32_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(uint32_t v); |
| |
| // All the public methods below are for supporting assertions and tests. |
| |
| Http2String 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); |
| |
| static constexpr uint32_t MaxExtensionBytes() { return 5; } |
| |
| 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; |
| }; |
| |
| } // namespace http2 |
| |
| #endif // NET_THIRD_PARTY_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_ |