| Index: net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
|
| diff --git a/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..90d03365422bfd6c2a833eba2d5a4cbdb2076385
|
| --- /dev/null
|
| +++ b/net/http2/hpack/decoder/hpack_decoder_string_buffer.cc
|
| @@ -0,0 +1,215 @@
|
| +// 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/http2/hpack/decoder/hpack_decoder_string_buffer.h"
|
| +
|
| +#include "base/logging.h"
|
| +
|
| +using base::StringPiece;
|
| +
|
| +namespace net {
|
| +
|
| +std::ostream& operator<<(std::ostream& out,
|
| + const HpackDecoderStringBuffer::State v) {
|
| + switch (v) {
|
| + case HpackDecoderStringBuffer::State::RESET:
|
| + return out << "RESET";
|
| + case HpackDecoderStringBuffer::State::COLLECTING:
|
| + return out << "COLLECTING";
|
| + case HpackDecoderStringBuffer::State::COMPLETE:
|
| + return out << "COMPLETE";
|
| + default:
|
| + return out << "Unknown HpackDecoderStringBuffer::State!";
|
| + }
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& out,
|
| + const HpackDecoderStringBuffer::Backing v) {
|
| + switch (v) {
|
| + case HpackDecoderStringBuffer::Backing::RESET:
|
| + return out << "RESET";
|
| + case HpackDecoderStringBuffer::Backing::UNBUFFERED:
|
| + return out << "UNBUFFERED";
|
| + case HpackDecoderStringBuffer::Backing::BUFFERED:
|
| + return out << "BUFFERED";
|
| + case HpackDecoderStringBuffer::Backing::STATIC:
|
| + return out << "STATIC";
|
| + default:
|
| + return out << "Unknown HpackDecoderStringBuffer::Backing!";
|
| + }
|
| +}
|
| +
|
| +HpackDecoderStringBuffer::HpackDecoderStringBuffer() {
|
| + Reset();
|
| +}
|
| +HpackDecoderStringBuffer::~HpackDecoderStringBuffer() {}
|
| +
|
| +// TODO(jamessynge): Consider eliminating most of Reset (i.e. do less); in
|
| +// particular, if a variable won't be read again until after it is next set
|
| +// (e.g. is_huffman_encoded_ or remaining_len_), then it doesn't need to be
|
| +// cleared here. This will be easier when not supporting both HpackDecoder2
|
| +// (in net/spdy/hpack) and HpackWholeEntryDecoder, so we can eliminate
|
| +// the Set() and str() methods.
|
| +void HpackDecoderStringBuffer::Reset() {
|
| + DVLOG(3) << "HpackDecoderStringBuffer::Reset";
|
| + buffer_.clear();
|
| + value_.clear();
|
| + remaining_len_ = 0;
|
| + is_huffman_encoded_ = false;
|
| + state_ = State::RESET;
|
| + backing_ = Backing::RESET;
|
| +}
|
| +
|
| +void HpackDecoderStringBuffer::Set(StringPiece value, bool is_static) {
|
| + DVLOG(2) << "HpackDecoderStringBuffer::Set";
|
| + DCHECK_EQ(state_, State::RESET);
|
| + DCHECK_EQ(backing_, Backing::RESET);
|
| + value_ = value;
|
| + state_ = State::COMPLETE;
|
| + backing_ = is_static ? Backing::STATIC : Backing::UNBUFFERED;
|
| +}
|
| +
|
| +void HpackDecoderStringBuffer::OnStart(bool huffman_encoded, size_t len) {
|
| + DVLOG(2) << "HpackDecoderStringBuffer::OnStart";
|
| + DCHECK_EQ(state_, State::RESET);
|
| + DCHECK_EQ(backing_, Backing::RESET);
|
| + buffer_.clear();
|
| + value_.clear();
|
| +
|
| + remaining_len_ = len;
|
| + is_huffman_encoded_ = huffman_encoded;
|
| +
|
| + state_ = State::COLLECTING;
|
| +
|
| + if (huffman_encoded) {
|
| + decoder_.Reset();
|
| + backing_ = Backing::BUFFERED;
|
| +
|
| + // Reserve space in buffer_ for the uncompressed string, assuming the
|
| + // maximum expansion. The shortest Huffman codes in the RFC are 5 bits long,
|
| + // which then expand to 8 bits during decoding (i.e. each code is for one
|
| + // plain text octet, aka byte), so the maximum size is 60% longer than the
|
| + // encoded size.
|
| + len = len * 8 / 5;
|
| + if (buffer_.capacity() < len) {
|
| + buffer_.reserve(len);
|
| + }
|
| + } else {
|
| + // Assume for now that we won't need to use buffer_, so don't reserve space
|
| + // in it.
|
| + backing_ = Backing::RESET;
|
| + }
|
| +}
|
| +
|
| +bool HpackDecoderStringBuffer::OnData(const char* data, size_t len) {
|
| + DVLOG(2) << "HpackDecoderStringBuffer::OnData state=" << state_
|
| + << ", backing=" << backing_;
|
| + DCHECK_EQ(state_, State::COLLECTING);
|
| + DCHECK_LE(len, remaining_len_);
|
| + remaining_len_ -= len;
|
| +
|
| + if (is_huffman_encoded_) {
|
| + DCHECK_EQ(backing_, Backing::BUFFERED);
|
| + // We don't set value_ for buffered strings until OnEnd,
|
| + // so it should be empty.
|
| + DCHECK_EQ(0u, value_.size());
|
| + return decoder_.Decode(StringPiece(data, len), &buffer_);
|
| + }
|
| +
|
| + if (backing_ == Backing::RESET) {
|
| + // This is the first call to OnData.
|
| + DCHECK_EQ(0u, buffer_.size());
|
| + DCHECK_EQ(0u, value_.size());
|
| + // If data contains the entire string, don't copy the string. If we later
|
| + // find that the HPACK entry is split across input buffers, then we'll
|
| + // copy the string into buffer_.
|
| + if (remaining_len_ == 0) {
|
| + value_ = StringPiece(data, len);
|
| + backing_ = Backing::UNBUFFERED;
|
| + return true;
|
| + }
|
| +
|
| + // We need to buffer the string because it is split across input buffers.
|
| + backing_ = Backing::BUFFERED;
|
| + buffer_.assign(data, len);
|
| + return true;
|
| + }
|
| +
|
| + // This is not the first call to OnData for this string, so it should be
|
| + // buffered.
|
| + DCHECK_EQ(backing_, Backing::BUFFERED);
|
| + // We don't set value_ for buffered strings until OnEnd, so it should be
|
| + // empty.
|
| + DCHECK_EQ(0u, value_.size());
|
| +
|
| + // Append to the current contents of the buffer.
|
| + buffer_.append(data, len);
|
| + return true;
|
| +}
|
| +
|
| +bool HpackDecoderStringBuffer::OnEnd() {
|
| + DVLOG(2) << "HpackDecoderStringBuffer::OnEnd";
|
| + DCHECK_EQ(state_, State::COLLECTING);
|
| + DCHECK_EQ(0u, remaining_len_);
|
| +
|
| + if (is_huffman_encoded_) {
|
| + DCHECK_EQ(backing_, Backing::BUFFERED);
|
| + // Did the Huffman encoding of the string end properly?
|
| + if (!decoder_.InputProperlyTerminated()) {
|
| + return false; // No, it didn't.
|
| + }
|
| + }
|
| + state_ = State::COMPLETE;
|
| + if (backing_ == Backing::BUFFERED) {
|
| + value_ = buffer_;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +void HpackDecoderStringBuffer::BufferStringIfUnbuffered() {
|
| + DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
|
| + << state_ << ", backing=" << backing_;
|
| + if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) {
|
| + DVLOG(2) << "HpackDecoderStringBuffer buffering string of length "
|
| + << value_.size();
|
| + value_.CopyToString(&buffer_);
|
| + if (state_ == State::COMPLETE) {
|
| + value_ = buffer_;
|
| + }
|
| + backing_ = Backing::BUFFERED;
|
| + }
|
| +}
|
| +
|
| +size_t HpackDecoderStringBuffer::BufferedLength() const {
|
| + DVLOG(3) << "HpackDecoderStringBuffer::BufferedLength";
|
| + return backing_ == Backing::BUFFERED ? buffer_.size() : 0;
|
| +}
|
| +
|
| +StringPiece HpackDecoderStringBuffer::str() const {
|
| + DVLOG(3) << "HpackDecoderStringBuffer::str";
|
| + DCHECK_EQ(state_, State::COMPLETE);
|
| + return value_;
|
| +}
|
| +
|
| +void HpackDecoderStringBuffer::OutputDebugStringTo(std::ostream& out) const {
|
| + out << "{state=" << state_;
|
| + if (state_ != State::RESET) {
|
| + out << ", backing=" << backing_;
|
| + out << ", remaining_len=" << remaining_len_;
|
| + out << ", is_huffman_encoded=" << is_huffman_encoded_;
|
| + if (backing_ == Backing::BUFFERED) {
|
| + out << ", buffer: " << buffer_;
|
| + } else {
|
| + out << ", value: " << value_;
|
| + }
|
| + }
|
| + out << "}";
|
| +}
|
| +
|
| +std::ostream& operator<<(std::ostream& out, const HpackDecoderStringBuffer& v) {
|
| + v.OutputDebugStringTo(out);
|
| + return out;
|
| +}
|
| +
|
| +} // namespace net
|
|
|