1 // Copyright 2014 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 #include "quiche/spdy/core/hpack/hpack_output_stream.h"
6
7 #include <cstddef>
8 #include <cstdint>
9 #include <string>
10 #include <utility>
11
12 #include "absl/strings/string_view.h"
13 #include "quiche/common/platform/api/quiche_logging.h"
14 #include "quiche/spdy/core/hpack/hpack_constants.h"
15
16 namespace spdy {
17
HpackOutputStream()18 HpackOutputStream::HpackOutputStream() : bit_offset_(0) {}
19
20 HpackOutputStream::~HpackOutputStream() = default;
21
AppendBits(uint8_t bits,size_t bit_size)22 void HpackOutputStream::AppendBits(uint8_t bits, size_t bit_size) {
23 QUICHE_DCHECK_GT(bit_size, 0u);
24 QUICHE_DCHECK_LE(bit_size, 8u);
25 QUICHE_DCHECK_EQ(bits >> bit_size, 0);
26 size_t new_bit_offset = bit_offset_ + bit_size;
27 if (bit_offset_ == 0) {
28 // Buffer ends on a byte boundary.
29 QUICHE_DCHECK_LE(bit_size, 8u);
30 buffer_.append(1, bits << (8 - bit_size));
31 } else if (new_bit_offset <= 8) {
32 // Buffer does not end on a byte boundary but the given bits fit
33 // in the remainder of the last byte.
34 buffer_.back() |= bits << (8 - new_bit_offset);
35 } else {
36 // Buffer does not end on a byte boundary and the given bits do
37 // not fit in the remainder of the last byte.
38 buffer_.back() |= bits >> (new_bit_offset - 8);
39 buffer_.append(1, bits << (16 - new_bit_offset));
40 }
41 bit_offset_ = new_bit_offset % 8;
42 }
43
AppendPrefix(HpackPrefix prefix)44 void HpackOutputStream::AppendPrefix(HpackPrefix prefix) {
45 AppendBits(prefix.bits, prefix.bit_size);
46 }
47
AppendBytes(absl::string_view buffer)48 void HpackOutputStream::AppendBytes(absl::string_view buffer) {
49 QUICHE_DCHECK_EQ(bit_offset_, 0u);
50 buffer_.append(buffer.data(), buffer.size());
51 }
52
AppendUint32(uint32_t I)53 void HpackOutputStream::AppendUint32(uint32_t I) {
54 // The algorithm below is adapted from the pseudocode in 6.1.
55 size_t N = 8 - bit_offset_;
56 uint8_t max_first_byte = static_cast<uint8_t>((1 << N) - 1);
57 if (I < max_first_byte) {
58 AppendBits(static_cast<uint8_t>(I), N);
59 } else {
60 AppendBits(max_first_byte, N);
61 I -= max_first_byte;
62 while ((I & ~0x7f) != 0) {
63 buffer_.append(1, (I & 0x7f) | 0x80);
64 I >>= 7;
65 }
66 AppendBits(static_cast<uint8_t>(I), 8);
67 }
68 QUICHE_DCHECK_EQ(bit_offset_, 0u);
69 }
70
MutableString()71 std::string* HpackOutputStream::MutableString() {
72 QUICHE_DCHECK_EQ(bit_offset_, 0u);
73 return &buffer_;
74 }
75
TakeString()76 std::string HpackOutputStream::TakeString() {
77 // This must hold, since all public functions cause the buffer to
78 // end on a byte boundary.
79 QUICHE_DCHECK_EQ(bit_offset_, 0u);
80 std::string out = std::move(buffer_);
81 buffer_ = {};
82 bit_offset_ = 0;
83 return out;
84 }
85
BoundedTakeString(size_t max_size)86 std::string HpackOutputStream::BoundedTakeString(size_t max_size) {
87 if (buffer_.size() > max_size) {
88 // Save off overflow bytes to temporary string (causes a copy).
89 std::string overflow = buffer_.substr(max_size);
90
91 // Resize buffer down to the given limit.
92 buffer_.resize(max_size);
93
94 // Give buffer to output string.
95 std::string out = std::move(buffer_);
96
97 // Reset to contain overflow.
98 buffer_ = std::move(overflow);
99 return out;
100 } else {
101 return TakeString();
102 }
103 }
104
105 } // namespace spdy
106