Project import generated by Copybara.
PiperOrigin-RevId: 224614037
Change-Id: I14e53449d4aeccb328f86828c76b5f09dea0d4b8
diff --git a/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/http2/hpack/decoder/hpack_decoder_string_buffer.cc
new file mode 100644
index 0000000..b20c37a
--- /dev/null
+++ b/http2/hpack/decoder/hpack_decoder_string_buffer.cc
@@ -0,0 +1,235 @@
+// 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/decoder/hpack_decoder_string_buffer.h"
+
+#include <utility>
+
+#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_estimate_memory_usage.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
+
+namespace http2 {
+
+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";
+ }
+ // Since the value doesn't come over the wire, only a programming bug should
+ // result in reaching this point.
+ int unknown = static_cast<int>(v);
+ HTTP2_BUG << "Invalid HpackDecoderStringBuffer::State: " << unknown;
+ return out << "HpackDecoderStringBuffer::State(" << unknown << ")";
+}
+
+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";
+ }
+ // Since the value doesn't come over the wire, only a programming bug should
+ // result in reaching this point.
+ auto v2 = static_cast<int>(v);
+ HTTP2_BUG << "Invalid HpackDecoderStringBuffer::Backing: " << v2;
+ return out << "HpackDecoderStringBuffer::Backing(" << v2 << ")";
+}
+
+HpackDecoderStringBuffer::HpackDecoderStringBuffer()
+ : remaining_len_(0),
+ is_huffman_encoded_(false),
+ state_(State::RESET),
+ backing_(Backing::RESET) {}
+HpackDecoderStringBuffer::~HpackDecoderStringBuffer() = default;
+
+void HpackDecoderStringBuffer::Reset() {
+ DVLOG(3) << "HpackDecoderStringBuffer::Reset";
+ state_ = State::RESET;
+}
+
+void HpackDecoderStringBuffer::Set(Http2StringPiece value, bool is_static) {
+ DVLOG(2) << "HpackDecoderStringBuffer::Set";
+ DCHECK_EQ(state_, State::RESET);
+ value_ = value;
+ state_ = State::COMPLETE;
+ backing_ = is_static ? Backing::STATIC : Backing::UNBUFFERED;
+ // TODO(jamessynge): Determine which of these two fields must be set.
+ remaining_len_ = 0;
+ is_huffman_encoded_ = false;
+}
+
+void HpackDecoderStringBuffer::OnStart(bool huffman_encoded, size_t len) {
+ DVLOG(2) << "HpackDecoderStringBuffer::OnStart";
+ DCHECK_EQ(state_, State::RESET);
+
+ remaining_len_ = len;
+ is_huffman_encoded_ = huffman_encoded;
+ state_ = State::COLLECTING;
+
+ if (huffman_encoded) {
+ // We don't set, clear or use value_ for buffered strings until OnEnd.
+ decoder_.Reset();
+ buffer_.clear();
+ 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;
+ // OnData is not called for empty (zero length) strings, so make sure that
+ // value_ is cleared.
+ value_ = Http2StringPiece();
+ }
+}
+
+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);
+ return decoder_.Decode(Http2StringPiece(data, len), &buffer_);
+ }
+
+ if (backing_ == Backing::RESET) {
+ // This is the first call to OnData. 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_ = Http2StringPiece(data, len);
+ backing_ = Backing::UNBUFFERED;
+ return true;
+ }
+
+ // We need to buffer the string because it is split across input buffers.
+ // Reserve space in buffer_ for the entire string.
+ backing_ = Backing::BUFFERED;
+ buffer_.reserve(remaining_len_ + len);
+ 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);
+
+ // 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.
+ }
+ value_ = buffer_;
+ } else if (backing_ == Backing::BUFFERED) {
+ value_ = buffer_;
+ }
+ state_ = State::COMPLETE;
+ return true;
+}
+
+void HpackDecoderStringBuffer::BufferStringIfUnbuffered() {
+ DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
+ << state_ << ", backing=" << backing_;
+ if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) {
+ DVLOG(2) << "HpackDecoderStringBuffer buffering Http2String of length "
+ << value_.size();
+ buffer_.assign(value_.data(), value_.size());
+ if (state_ == State::COMPLETE) {
+ value_ = buffer_;
+ }
+ backing_ = Backing::BUFFERED;
+ }
+}
+
+bool HpackDecoderStringBuffer::IsBuffered() const {
+ DVLOG(3) << "HpackDecoderStringBuffer::IsBuffered";
+ return state_ != State::RESET && backing_ == Backing::BUFFERED;
+}
+
+size_t HpackDecoderStringBuffer::BufferedLength() const {
+ DVLOG(3) << "HpackDecoderStringBuffer::BufferedLength";
+ return IsBuffered() ? buffer_.size() : 0;
+}
+
+Http2StringPiece HpackDecoderStringBuffer::str() const {
+ DVLOG(3) << "HpackDecoderStringBuffer::str";
+ DCHECK_EQ(state_, State::COMPLETE);
+ return value_;
+}
+
+Http2String HpackDecoderStringBuffer::ReleaseString() {
+ DVLOG(3) << "HpackDecoderStringBuffer::ReleaseString";
+ DCHECK_EQ(state_, State::COMPLETE);
+ DCHECK_EQ(backing_, Backing::BUFFERED);
+ if (state_ == State::COMPLETE) {
+ state_ = State::RESET;
+ if (backing_ == Backing::BUFFERED) {
+ return std::move(buffer_);
+ } else {
+ return Http2String(value_);
+ }
+ }
+ return "";
+}
+
+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 << "}";
+}
+
+size_t HpackDecoderStringBuffer::EstimateMemoryUsage() const {
+ return Http2EstimateMemoryUsage(buffer_);
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackDecoderStringBuffer& v) {
+ v.OutputDebugStringTo(out);
+ return out;
+}
+
+} // namespace http2