| Index: net/http2/hpack/decoder/hpack_string_decoder.h
 | 
| diff --git a/net/http2/hpack/decoder/hpack_string_decoder.h b/net/http2/hpack/decoder/hpack_string_decoder.h
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..baea422cebf91b0aac3b64c3a3524fa334a62c88
 | 
| --- /dev/null
 | 
| +++ b/net/http2/hpack/decoder/hpack_string_decoder.h
 | 
| @@ -0,0 +1,236 @@
 | 
| +// 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.
 | 
| +
 | 
| +#ifndef NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
 | 
| +#define NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
 | 
| +
 | 
| +// HpackStringDecoder decodes strings encoded per the HPACK spec; this does
 | 
| +// not mean decompressing Huffman encoded strings, just identifying the length,
 | 
| +// encoding and contents for a listener.
 | 
| +
 | 
| +#include <stddef.h>
 | 
| +
 | 
| +#include <algorithm>
 | 
| +#include <string>
 | 
| +
 | 
| +#include "base/logging.h"
 | 
| +#include "base/macros.h"
 | 
| +#include "net/base/net_export.h"
 | 
| +#include "net/http2/decoder/decode_buffer.h"
 | 
| +#include "net/http2/decoder/decode_status.h"
 | 
| +#include "net/http2/hpack/decoder/hpack_varint_decoder.h"
 | 
| +
 | 
| +namespace net {
 | 
| +
 | 
| +// Decodes a single string in an HPACK header entry. The high order bit of
 | 
| +// the first byte of the length is the H (Huffman) bit indicating whether
 | 
| +// the value is Huffman encoded, and the remainder of the byte is the first
 | 
| +// 7 bits of an HPACK varint.
 | 
| +//
 | 
| +// Call Start() to begin decoding; if it returns kDecodeInProgress, then call
 | 
| +// Resume() when more input is available, repeating until kDecodeInProgress is
 | 
| +// not returned. If kDecodeDone or kDecodeError is returned, then Resume() must
 | 
| +// not be called until Start() has been called to start decoding a new string.
 | 
| +//
 | 
| +// There are 3 variants of Start in this class, participants in a performance
 | 
| +// experiment. Perflab experiments show it is generally fastest to call
 | 
| +// StartSpecialCaseShort rather than StartOnly (~9% slower) or
 | 
| +// StartAndDecodeLength (~10% slower).
 | 
| +class NET_EXPORT_PRIVATE HpackStringDecoder {
 | 
| + public:
 | 
| +  enum StringDecoderState {
 | 
| +    kStartDecodingLength,
 | 
| +    kDecodingString,
 | 
| +    kResumeDecodingLength,
 | 
| +  };
 | 
| +
 | 
| +  // TODO(jamessynge): Get rid of all but one of the Start and Resume methods
 | 
| +  // after all of the HPACK decoder is checked in and has been perf tested.
 | 
| +  template <class Listener>
 | 
| +  DecodeStatus Start(DecodeBuffer* db, Listener* cb) {
 | 
| +    return StartSpecialCaseShort(db, cb);
 | 
| +  }
 | 
| +
 | 
| +  template <class Listener>
 | 
| +  DecodeStatus StartOnly(DecodeBuffer* db, Listener* cb) {
 | 
| +    state_ = kStartDecodingLength;
 | 
| +    return Resume(db, cb);
 | 
| +  }
 | 
| +
 | 
| +  template <class Listener>
 | 
| +  DecodeStatus StartAndDecodeLength(DecodeBuffer* db, Listener* cb) {
 | 
| +    DecodeStatus status;
 | 
| +    if (StartDecodingLength(db, cb, &status)) {
 | 
| +      state_ = kDecodingString;
 | 
| +      return DecodeString(db, cb);
 | 
| +    }
 | 
| +    return status;
 | 
| +  }
 | 
| +
 | 
| +  template <class Listener>
 | 
| +  DecodeStatus StartSpecialCaseShort(DecodeBuffer* db, Listener* cb) {
 | 
| +    // Fast decode path is used if the string is under 127 bytes and the
 | 
| +    // entire length of the string is in the decode buffer. More than 83% of
 | 
| +    // string lengths are encoded in just one byte.
 | 
| +    if (db->HasData() && (*db->cursor() & 0x7f) != 0x7f) {
 | 
| +      // The string is short.
 | 
| +      uint8_t h_and_prefix = db->DecodeUInt8();
 | 
| +      uint8_t length = h_and_prefix & 0x7f;
 | 
| +      bool huffman_encoded = (h_and_prefix & 0x80) == 0x80;
 | 
| +      cb->OnStringStart(huffman_encoded, length);
 | 
| +      if (length <= db->Remaining()) {
 | 
| +        // Yeah, we've got the whole thing in the decode buffer.
 | 
| +        // Ideally this will be the common case. Note that we don't
 | 
| +        // update any of the member variables in this path.
 | 
| +        cb->OnStringData(db->cursor(), length);
 | 
| +        db->AdvanceCursor(length);
 | 
| +        cb->OnStringEnd();
 | 
| +        return DecodeStatus::kDecodeDone;
 | 
| +      }
 | 
| +      // Not all in the buffer.
 | 
| +      huffman_encoded_ = huffman_encoded;
 | 
| +      remaining_ = length;
 | 
| +      // Call Resume to decode the string body, which is only partially
 | 
| +      // in the decode buffer (or not at all).
 | 
| +      state_ = kDecodingString;
 | 
| +      return Resume(db, cb);
 | 
| +    }
 | 
| +    // Call Resume to decode the string length, which is either not in
 | 
| +    // the decode buffer, or spans multiple bytes.
 | 
| +    state_ = kStartDecodingLength;
 | 
| +    return Resume(db, cb);
 | 
| +  }
 | 
| +
 | 
| +  template <class Listener>
 | 
| +  DecodeStatus Resume(DecodeBuffer* db, Listener* cb) {
 | 
| +    DecodeStatus status;
 | 
| +    while (true) {
 | 
| +      switch (state_) {
 | 
| +        case kStartDecodingLength:
 | 
| +          DVLOG(2) << "kStartDecodingLength: db->Remaining=" << db->Remaining();
 | 
| +          if (!StartDecodingLength(db, cb, &status)) {
 | 
| +            // The length is split across decode buffers.
 | 
| +            return status;
 | 
| +          }
 | 
| +        // We've finished decoding the length, which spanned one or more
 | 
| +        // bytes. Approximately 17% of strings have a length that is greater
 | 
| +        // than 126 bytes, and thus the length is encoded in more than one
 | 
| +        // byte, and so doesn't get the benefit of the optimization in
 | 
| +        // Start() for single byte lengths. But, we still expect that most
 | 
| +        // of such strings will be contained entirely in a single decode
 | 
| +        // buffer, and hence this fall through skips another trip through the
 | 
| +        // switch above and more importantly skips setting the state_ variable
 | 
| +        // again in those cases where we don't need it.
 | 
| +
 | 
| +        // FALLTHROUGH_INTENDED
 | 
| +
 | 
| +        case kDecodingString:
 | 
| +          DVLOG(2) << "kDecodingString: db->Remaining=" << db->Remaining()
 | 
| +                   << "    remaining_=" << remaining_;
 | 
| +          return DecodeString(db, cb);
 | 
| +
 | 
| +        case kResumeDecodingLength:
 | 
| +          DVLOG(2) << "kResumeDecodingLength: db->Remaining="
 | 
| +                   << db->Remaining();
 | 
| +          if (!ResumeDecodingLength(db, cb, &status)) {
 | 
| +            return status;
 | 
| +          }
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  std::string DebugString() const;
 | 
| +
 | 
| + private:
 | 
| +  static std::string StateToString(StringDecoderState v);
 | 
| +
 | 
| +  // Returns true if the length is fully decoded and the listener wants the
 | 
| +  // decoding to continue, false otherwise; status is set to the status from
 | 
| +  // the varint decoder.
 | 
| +  // If the length is not fully decoded, case state_ is set appropriately
 | 
| +  // for the next call to Resume.
 | 
| +  template <class Listener>
 | 
| +  bool StartDecodingLength(DecodeBuffer* db,
 | 
| +                           Listener* cb,
 | 
| +                           DecodeStatus* status) {
 | 
| +    if (db->Empty()) {
 | 
| +      *status = DecodeStatus::kDecodeInProgress;
 | 
| +      state_ = kStartDecodingLength;
 | 
| +      return false;
 | 
| +    }
 | 
| +    uint8_t h_and_prefix = db->DecodeUInt8();
 | 
| +    huffman_encoded_ = (h_and_prefix & 0x80) == 0x80;
 | 
| +    *status = length_decoder_.Start(h_and_prefix, 0x7f, db);
 | 
| +    if (*status == DecodeStatus::kDecodeDone) {
 | 
| +      OnStringStart(cb, status);
 | 
| +      return true;
 | 
| +    }
 | 
| +    // Set the state to cover the DecodeStatus::kDecodeInProgress case.
 | 
| +    // Won't be needed if the status is kDecodeError.
 | 
| +    state_ = kResumeDecodingLength;
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  // Returns true if the length is fully decoded and the listener wants the
 | 
| +  // decoding to continue, false otherwise; status is set to the status from
 | 
| +  // the varint decoder; state_ is updated when fully decoded.
 | 
| +  // If the length is not fully decoded, case state_ is set appropriately
 | 
| +  // for the next call to Resume.
 | 
| +  template <class Listener>
 | 
| +  bool ResumeDecodingLength(DecodeBuffer* db,
 | 
| +                            Listener* cb,
 | 
| +                            DecodeStatus* status) {
 | 
| +    DCHECK_EQ(state_, kResumeDecodingLength);
 | 
| +    *status = length_decoder_.Resume(db);
 | 
| +    if (*status == DecodeStatus::kDecodeDone) {
 | 
| +      state_ = kDecodingString;
 | 
| +      OnStringStart(cb, status);
 | 
| +      return true;
 | 
| +    }
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  // Returns true if the listener wants the decoding to continue, and
 | 
| +  // false otherwise, in which case status set.
 | 
| +  template <class Listener>
 | 
| +  void OnStringStart(Listener* cb, DecodeStatus* status) {
 | 
| +    remaining_ = length_decoder_.value();
 | 
| +    // Make callback so consumer knows what is coming.
 | 
| +    cb->OnStringStart(huffman_encoded_, remaining_);
 | 
| +    return;
 | 
| +  }
 | 
| +
 | 
| +  // Passes the available portion of the string to the listener, and signals
 | 
| +  // the end of the string when it is reached. Returns kDecodeDone or
 | 
| +  // kDecodeInProgress as appropriate.
 | 
| +  template <class Listener>
 | 
| +  DecodeStatus DecodeString(DecodeBuffer* db, Listener* cb) {
 | 
| +    size_t len = std::min(remaining_, db->Remaining());
 | 
| +    if (len > 0) {
 | 
| +      cb->OnStringData(db->cursor(), len);
 | 
| +      db->AdvanceCursor(len);
 | 
| +      remaining_ -= len;
 | 
| +    }
 | 
| +    if (remaining_ == 0) {
 | 
| +      cb->OnStringEnd();
 | 
| +      return DecodeStatus::kDecodeDone;
 | 
| +    }
 | 
| +    state_ = kDecodingString;
 | 
| +    return DecodeStatus::kDecodeInProgress;
 | 
| +  }
 | 
| +
 | 
| +  HpackVarintDecoder length_decoder_;
 | 
| +
 | 
| +  // These fields are initialized just to keep ASAN happy about reading
 | 
| +  // them from DebugString().
 | 
| +  size_t remaining_ = 0;
 | 
| +  StringDecoderState state_ = kStartDecodingLength;
 | 
| +  bool huffman_encoded_ = false;
 | 
| +};
 | 
| +
 | 
| +NET_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
 | 
| +                                            const HpackStringDecoder& v);
 | 
| +
 | 
| +}  // namespace net
 | 
| +#endif  // NET_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
 | 
| 
 |