Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(138)

Side by Side Diff: net/http2/hpack/decoder/hpack_string_decoder.h

Issue 2554683003: Revert of Add new HTTP/2 and HPACK decoder in net/http2/. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
6 #define NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
7
8 // HpackStringDecoder decodes strings encoded per the HPACK spec; this does
9 // not mean decompressing Huffman encoded strings, just identifying the length,
10 // encoding and contents for a listener.
11
12 #include <stddef.h>
13
14 #include <algorithm>
15 #include <string>
16
17 #include "base/logging.h"
18 #include "base/macros.h"
19 #include "net/base/net_export.h"
20 #include "net/http2/decoder/decode_buffer.h"
21 #include "net/http2/decoder/decode_status.h"
22 #include "net/http2/hpack/decoder/hpack_varint_decoder.h"
23
24 namespace net {
25
26 // Decodes a single string in an HPACK header entry. The high order bit of
27 // the first byte of the length is the H (Huffman) bit indicating whether
28 // the value is Huffman encoded, and the remainder of the byte is the first
29 // 7 bits of an HPACK varint.
30 //
31 // Call Start() to begin decoding; if it returns kDecodeInProgress, then call
32 // Resume() when more input is available, repeating until kDecodeInProgress is
33 // not returned. If kDecodeDone or kDecodeError is returned, then Resume() must
34 // not be called until Start() has been called to start decoding a new string.
35 //
36 // There are 3 variants of Start in this class, participants in a performance
37 // experiment. Perflab experiments show it is generally fastest to call
38 // StartSpecialCaseShort rather than StartOnly (~9% slower) or
39 // StartAndDecodeLength (~10% slower).
40 class NET_EXPORT_PRIVATE HpackStringDecoder {
41 public:
42 enum StringDecoderState {
43 kStartDecodingLength,
44 kDecodingString,
45 kResumeDecodingLength,
46 };
47
48 // TODO(jamessynge): Get rid of all but one of the Start and Resume methods
49 // after all of the HPACK decoder is checked in and has been perf tested.
50 template <class Listener>
51 DecodeStatus Start(DecodeBuffer* db, Listener* cb) {
52 return StartSpecialCaseShort(db, cb);
53 }
54
55 template <class Listener>
56 DecodeStatus StartOnly(DecodeBuffer* db, Listener* cb) {
57 state_ = kStartDecodingLength;
58 return Resume(db, cb);
59 }
60
61 template <class Listener>
62 DecodeStatus StartAndDecodeLength(DecodeBuffer* db, Listener* cb) {
63 DecodeStatus status;
64 if (StartDecodingLength(db, cb, &status)) {
65 state_ = kDecodingString;
66 return DecodeString(db, cb);
67 }
68 return status;
69 }
70
71 template <class Listener>
72 DecodeStatus StartSpecialCaseShort(DecodeBuffer* db, Listener* cb) {
73 // Fast decode path is used if the string is under 127 bytes and the
74 // entire length of the string is in the decode buffer. More than 83% of
75 // string lengths are encoded in just one byte.
76 if (db->HasData() && (*db->cursor() & 0x7f) != 0x7f) {
77 // The string is short.
78 uint8_t h_and_prefix = db->DecodeUInt8();
79 uint8_t length = h_and_prefix & 0x7f;
80 bool huffman_encoded = (h_and_prefix & 0x80) == 0x80;
81 cb->OnStringStart(huffman_encoded, length);
82 if (length <= db->Remaining()) {
83 // Yeah, we've got the whole thing in the decode buffer.
84 // Ideally this will be the common case. Note that we don't
85 // update any of the member variables in this path.
86 cb->OnStringData(db->cursor(), length);
87 db->AdvanceCursor(length);
88 cb->OnStringEnd();
89 return DecodeStatus::kDecodeDone;
90 }
91 // Not all in the buffer.
92 huffman_encoded_ = huffman_encoded;
93 remaining_ = length;
94 // Call Resume to decode the string body, which is only partially
95 // in the decode buffer (or not at all).
96 state_ = kDecodingString;
97 return Resume(db, cb);
98 }
99 // Call Resume to decode the string length, which is either not in
100 // the decode buffer, or spans multiple bytes.
101 state_ = kStartDecodingLength;
102 return Resume(db, cb);
103 }
104
105 template <class Listener>
106 DecodeStatus Resume(DecodeBuffer* db, Listener* cb) {
107 DecodeStatus status;
108 while (true) {
109 switch (state_) {
110 case kStartDecodingLength:
111 DVLOG(2) << "kStartDecodingLength: db->Remaining=" << db->Remaining();
112 if (!StartDecodingLength(db, cb, &status)) {
113 // The length is split across decode buffers.
114 return status;
115 }
116 // We've finished decoding the length, which spanned one or more
117 // bytes. Approximately 17% of strings have a length that is greater
118 // than 126 bytes, and thus the length is encoded in more than one
119 // byte, and so doesn't get the benefit of the optimization in
120 // Start() for single byte lengths. But, we still expect that most
121 // of such strings will be contained entirely in a single decode
122 // buffer, and hence this fall through skips another trip through the
123 // switch above and more importantly skips setting the state_ variable
124 // again in those cases where we don't need it.
125
126 // FALLTHROUGH_INTENDED
127
128 case kDecodingString:
129 DVLOG(2) << "kDecodingString: db->Remaining=" << db->Remaining()
130 << " remaining_=" << remaining_;
131 return DecodeString(db, cb);
132
133 case kResumeDecodingLength:
134 DVLOG(2) << "kResumeDecodingLength: db->Remaining="
135 << db->Remaining();
136 if (!ResumeDecodingLength(db, cb, &status)) {
137 return status;
138 }
139 }
140 }
141 }
142
143 std::string DebugString() const;
144
145 private:
146 static std::string StateToString(StringDecoderState v);
147
148 // Returns true if the length is fully decoded and the listener wants the
149 // decoding to continue, false otherwise; status is set to the status from
150 // the varint decoder.
151 // If the length is not fully decoded, case state_ is set appropriately
152 // for the next call to Resume.
153 template <class Listener>
154 bool StartDecodingLength(DecodeBuffer* db,
155 Listener* cb,
156 DecodeStatus* status) {
157 if (db->Empty()) {
158 *status = DecodeStatus::kDecodeInProgress;
159 state_ = kStartDecodingLength;
160 return false;
161 }
162 uint8_t h_and_prefix = db->DecodeUInt8();
163 huffman_encoded_ = (h_and_prefix & 0x80) == 0x80;
164 *status = length_decoder_.Start(h_and_prefix, 0x7f, db);
165 if (*status == DecodeStatus::kDecodeDone) {
166 OnStringStart(cb, status);
167 return true;
168 }
169 // Set the state to cover the DecodeStatus::kDecodeInProgress case.
170 // Won't be needed if the status is kDecodeError.
171 state_ = kResumeDecodingLength;
172 return false;
173 }
174
175 // Returns true if the length is fully decoded and the listener wants the
176 // decoding to continue, false otherwise; status is set to the status from
177 // the varint decoder; state_ is updated when fully decoded.
178 // If the length is not fully decoded, case state_ is set appropriately
179 // for the next call to Resume.
180 template <class Listener>
181 bool ResumeDecodingLength(DecodeBuffer* db,
182 Listener* cb,
183 DecodeStatus* status) {
184 DCHECK_EQ(state_, kResumeDecodingLength);
185 *status = length_decoder_.Resume(db);
186 if (*status == DecodeStatus::kDecodeDone) {
187 state_ = kDecodingString;
188 OnStringStart(cb, status);
189 return true;
190 }
191 return false;
192 }
193
194 // Returns true if the listener wants the decoding to continue, and
195 // false otherwise, in which case status set.
196 template <class Listener>
197 void OnStringStart(Listener* cb, DecodeStatus* status) {
198 remaining_ = length_decoder_.value();
199 // Make callback so consumer knows what is coming.
200 cb->OnStringStart(huffman_encoded_, remaining_);
201 return;
202 }
203
204 // Passes the available portion of the string to the listener, and signals
205 // the end of the string when it is reached. Returns kDecodeDone or
206 // kDecodeInProgress as appropriate.
207 template <class Listener>
208 DecodeStatus DecodeString(DecodeBuffer* db, Listener* cb) {
209 size_t len = std::min(remaining_, db->Remaining());
210 if (len > 0) {
211 cb->OnStringData(db->cursor(), len);
212 db->AdvanceCursor(len);
213 remaining_ -= len;
214 }
215 if (remaining_ == 0) {
216 cb->OnStringEnd();
217 return DecodeStatus::kDecodeDone;
218 }
219 state_ = kDecodingString;
220 return DecodeStatus::kDecodeInProgress;
221 }
222
223 HpackVarintDecoder length_decoder_;
224
225 // These fields are initialized just to keep ASAN happy about reading
226 // them from DebugString().
227 size_t remaining_ = 0;
228 StringDecoderState state_ = kStartDecodingLength;
229 bool huffman_encoded_ = false;
230 };
231
232 NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
233 const HpackStringDecoder& v);
234
235 } // namespace net
236 #endif // NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
OLDNEW
« no previous file with comments | « net/http2/hpack/decoder/hpack_string_collector.cc ('k') | net/http2/hpack/decoder/hpack_string_decoder.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698