xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/spdy/core/hpack/hpack_output_stream.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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