summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.cc
blob: ac85247f37094b27f48370a6b269feea3a842cd2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// 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 "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h"

#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"

namespace http2 {

DecodeStatus HpackVarintDecoder::Start(uint8_t prefix_value,
                                       uint8_t prefix_length,
                                       DecodeBuffer* db) {
  DCHECK_LE(3u, prefix_length);
  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) {
  DCHECK_LE(3u, prefix_length);
  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.
    DCHECK_LE(offset_, 56);
    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.
    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;
  }

  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.
  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;
}

Http2String HpackVarintDecoder::DebugString() const {
  return Http2StrCat("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