blob: 7de72baf1acdf1c693fde42770bdece399bd5c7a [file] [log] [blame]
// 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 "http2/decoder/decode_buffer.h"
#include "http2/decoder/decode_status.h"
#include "http2/platform/api/http2_logging.h"
#include "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_