xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/common/quiche_data_writer.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2012 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/common/quiche_data_writer.h"
6 
7 #include <algorithm>
8 #include <limits>
9 
10 #include "absl/strings/str_cat.h"
11 #include "absl/strings/string_view.h"
12 #include "quiche/common/platform/api/quiche_bug_tracker.h"
13 #include "quiche/common/quiche_endian.h"
14 
15 namespace quiche {
16 
QuicheDataWriter(size_t size,char * buffer)17 QuicheDataWriter::QuicheDataWriter(size_t size, char* buffer)
18     : QuicheDataWriter(size, buffer, quiche::NETWORK_BYTE_ORDER) {}
19 
QuicheDataWriter(size_t size,char * buffer,quiche::Endianness endianness)20 QuicheDataWriter::QuicheDataWriter(size_t size, char* buffer,
21                                    quiche::Endianness endianness)
22     : buffer_(buffer), capacity_(size), length_(0), endianness_(endianness) {}
23 
~QuicheDataWriter()24 QuicheDataWriter::~QuicheDataWriter() {}
25 
data()26 char* QuicheDataWriter::data() { return buffer_; }
27 
WriteUInt8(uint8_t value)28 bool QuicheDataWriter::WriteUInt8(uint8_t value) {
29   return WriteBytes(&value, sizeof(value));
30 }
31 
WriteUInt16(uint16_t value)32 bool QuicheDataWriter::WriteUInt16(uint16_t value) {
33   if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
34     value = quiche::QuicheEndian::HostToNet16(value);
35   }
36   return WriteBytes(&value, sizeof(value));
37 }
38 
WriteUInt32(uint32_t value)39 bool QuicheDataWriter::WriteUInt32(uint32_t value) {
40   if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
41     value = quiche::QuicheEndian::HostToNet32(value);
42   }
43   return WriteBytes(&value, sizeof(value));
44 }
45 
WriteUInt64(uint64_t value)46 bool QuicheDataWriter::WriteUInt64(uint64_t value) {
47   if (endianness_ == quiche::NETWORK_BYTE_ORDER) {
48     value = quiche::QuicheEndian::HostToNet64(value);
49   }
50   return WriteBytes(&value, sizeof(value));
51 }
52 
WriteBytesToUInt64(size_t num_bytes,uint64_t value)53 bool QuicheDataWriter::WriteBytesToUInt64(size_t num_bytes, uint64_t value) {
54   if (num_bytes > sizeof(value)) {
55     return false;
56   }
57   if (endianness_ == quiche::HOST_BYTE_ORDER) {
58     return WriteBytes(&value, num_bytes);
59   }
60 
61   value = quiche::QuicheEndian::HostToNet64(value);
62   return WriteBytes(reinterpret_cast<char*>(&value) + sizeof(value) - num_bytes,
63                     num_bytes);
64 }
65 
WriteStringPiece16(absl::string_view val)66 bool QuicheDataWriter::WriteStringPiece16(absl::string_view val) {
67   if (val.size() > std::numeric_limits<uint16_t>::max()) {
68     return false;
69   }
70   if (!WriteUInt16(static_cast<uint16_t>(val.size()))) {
71     return false;
72   }
73   return WriteBytes(val.data(), val.size());
74 }
75 
WriteStringPiece(absl::string_view val)76 bool QuicheDataWriter::WriteStringPiece(absl::string_view val) {
77   return WriteBytes(val.data(), val.size());
78 }
79 
BeginWrite(size_t length)80 char* QuicheDataWriter::BeginWrite(size_t length) {
81   if (length_ > capacity_) {
82     return nullptr;
83   }
84 
85   if (capacity_ - length_ < length) {
86     return nullptr;
87   }
88 
89 #ifdef ARCH_CPU_64_BITS
90   QUICHE_DCHECK_LE(length, std::numeric_limits<uint32_t>::max());
91 #endif
92 
93   return buffer_ + length_;
94 }
95 
WriteBytes(const void * data,size_t data_len)96 bool QuicheDataWriter::WriteBytes(const void* data, size_t data_len) {
97   char* dest = BeginWrite(data_len);
98   if (!dest) {
99     return false;
100   }
101 
102   std::copy(static_cast<const char*>(data),
103             static_cast<const char*>(data) + data_len, dest);
104 
105   length_ += data_len;
106   return true;
107 }
108 
WriteRepeatedByte(uint8_t byte,size_t count)109 bool QuicheDataWriter::WriteRepeatedByte(uint8_t byte, size_t count) {
110   char* dest = BeginWrite(count);
111   if (!dest) {
112     return false;
113   }
114 
115   std::fill(dest, dest + count, byte);
116 
117   length_ += count;
118   return true;
119 }
120 
WritePadding()121 void QuicheDataWriter::WritePadding() {
122   QUICHE_DCHECK_LE(length_, capacity_);
123   if (length_ > capacity_) {
124     return;
125   }
126   std::fill(buffer_ + length_, buffer_ + capacity_, 0x00);
127   length_ = capacity_;
128 }
129 
WritePaddingBytes(size_t count)130 bool QuicheDataWriter::WritePaddingBytes(size_t count) {
131   return WriteRepeatedByte(0x00, count);
132 }
133 
WriteTag(uint32_t tag)134 bool QuicheDataWriter::WriteTag(uint32_t tag) {
135   return WriteBytes(&tag, sizeof(tag));
136 }
137 
138 // Converts a uint64_t into a 62-bit RFC 9000 Variable Length Integer.
139 //
140 // Performance notes
141 //
142 // Measurements and experiments showed that unrolling the four cases
143 // like this and dereferencing next_ as we do (*(next_+n)) gains about
144 // 10% over making a loop and dereferencing it as *(next_++)
145 //
146 // Using a register for next didn't help.
147 //
148 // Branches are ordered to increase the likelihood of the first being
149 // taken.
150 //
151 // Low-level optimization is useful here because this function will be
152 // called frequently, leading to outsize benefits.
WriteVarInt62(uint64_t value)153 bool QuicheDataWriter::WriteVarInt62(uint64_t value) {
154   QUICHE_DCHECK_EQ(endianness(), quiche::NETWORK_BYTE_ORDER);
155 
156   size_t remaining_bytes = remaining();
157   char* next = buffer() + length();
158 
159   if ((value & kVarInt62ErrorMask) == 0) {
160     // We know the high 2 bits are 0 so |value| is legal.
161     // We can do the encoding.
162     if ((value & kVarInt62Mask8Bytes) != 0) {
163       // Someplace in the high-4 bytes is a 1-bit. Do an 8-byte
164       // encoding.
165       if (remaining_bytes >= 8) {
166         *(next + 0) = ((value >> 56) & 0x3f) + 0xc0;
167         *(next + 1) = (value >> 48) & 0xff;
168         *(next + 2) = (value >> 40) & 0xff;
169         *(next + 3) = (value >> 32) & 0xff;
170         *(next + 4) = (value >> 24) & 0xff;
171         *(next + 5) = (value >> 16) & 0xff;
172         *(next + 6) = (value >> 8) & 0xff;
173         *(next + 7) = value & 0xff;
174         IncreaseLength(8);
175         return true;
176       }
177       return false;
178     }
179     // The high-order-4 bytes are all 0, check for a 1, 2, or 4-byte
180     // encoding
181     if ((value & kVarInt62Mask4Bytes) != 0) {
182       // The encoding will not fit into 2 bytes, Do a 4-byte
183       // encoding.
184       if (remaining_bytes >= 4) {
185         *(next + 0) = ((value >> 24) & 0x3f) + 0x80;
186         *(next + 1) = (value >> 16) & 0xff;
187         *(next + 2) = (value >> 8) & 0xff;
188         *(next + 3) = value & 0xff;
189         IncreaseLength(4);
190         return true;
191       }
192       return false;
193     }
194     // The high-order bits are all 0. Check to see if the number
195     // can be encoded as one or two bytes. One byte encoding has
196     // only 6 significant bits (bits 0xffffffff ffffffc0 are all 0).
197     // Two byte encoding has more than 6, but 14 or less significant
198     // bits (bits 0xffffffff ffffc000 are 0 and 0x00000000 00003fc0
199     // are not 0)
200     if ((value & kVarInt62Mask2Bytes) != 0) {
201       // Do 2-byte encoding
202       if (remaining_bytes >= 2) {
203         *(next + 0) = ((value >> 8) & 0x3f) + 0x40;
204         *(next + 1) = (value)&0xff;
205         IncreaseLength(2);
206         return true;
207       }
208       return false;
209     }
210     if (remaining_bytes >= 1) {
211       // Do 1-byte encoding
212       *next = (value & 0x3f);
213       IncreaseLength(1);
214       return true;
215     }
216     return false;
217   }
218   // Can not encode, high 2 bits not 0
219   return false;
220 }
221 
WriteStringPieceVarInt62(const absl::string_view & string_piece)222 bool QuicheDataWriter::WriteStringPieceVarInt62(
223     const absl::string_view& string_piece) {
224   if (!WriteVarInt62(string_piece.size())) {
225     return false;
226   }
227   if (!string_piece.empty()) {
228     if (!WriteBytes(string_piece.data(), string_piece.size())) {
229       return false;
230     }
231   }
232   return true;
233 }
234 
235 // static
GetVarInt62Len(uint64_t value)236 QuicheVariableLengthIntegerLength QuicheDataWriter::GetVarInt62Len(
237     uint64_t value) {
238   if ((value & kVarInt62ErrorMask) != 0) {
239     QUICHE_BUG(invalid_varint) << "Attempted to encode a value, " << value
240                                << ", that is too big for VarInt62";
241     return VARIABLE_LENGTH_INTEGER_LENGTH_0;
242   }
243   if ((value & kVarInt62Mask8Bytes) != 0) {
244     return VARIABLE_LENGTH_INTEGER_LENGTH_8;
245   }
246   if ((value & kVarInt62Mask4Bytes) != 0) {
247     return VARIABLE_LENGTH_INTEGER_LENGTH_4;
248   }
249   if ((value & kVarInt62Mask2Bytes) != 0) {
250     return VARIABLE_LENGTH_INTEGER_LENGTH_2;
251   }
252   return VARIABLE_LENGTH_INTEGER_LENGTH_1;
253 }
254 
WriteVarInt62WithForcedLength(uint64_t value,QuicheVariableLengthIntegerLength write_length)255 bool QuicheDataWriter::WriteVarInt62WithForcedLength(
256     uint64_t value, QuicheVariableLengthIntegerLength write_length) {
257   QUICHE_DCHECK_EQ(endianness(), NETWORK_BYTE_ORDER);
258 
259   size_t remaining_bytes = remaining();
260   if (remaining_bytes < write_length) {
261     return false;
262   }
263 
264   const QuicheVariableLengthIntegerLength min_length = GetVarInt62Len(value);
265   if (write_length < min_length) {
266     QUICHE_BUG(invalid_varint_forced) << "Cannot write value " << value
267                                       << " with write_length " << write_length;
268     return false;
269   }
270   if (write_length == min_length) {
271     return WriteVarInt62(value);
272   }
273 
274   if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_2) {
275     return WriteUInt8(0b01000000) && WriteUInt8(value);
276   }
277   if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_4) {
278     return WriteUInt8(0b10000000) && WriteUInt8(0) && WriteUInt16(value);
279   }
280   if (write_length == VARIABLE_LENGTH_INTEGER_LENGTH_8) {
281     return WriteUInt8(0b11000000) && WriteUInt8(0) && WriteUInt16(0) &&
282            WriteUInt32(value);
283   }
284 
285   QUICHE_BUG(invalid_write_length)
286       << "Invalid write_length " << static_cast<int>(write_length);
287   return false;
288 }
289 
Seek(size_t length)290 bool QuicheDataWriter::Seek(size_t length) {
291   if (!BeginWrite(length)) {
292     return false;
293   }
294   length_ += length;
295   return true;
296 }
297 
DebugString() const298 std::string QuicheDataWriter::DebugString() const {
299   return absl::StrCat(" { capacity: ", capacity_, ", length: ", length_, " }");
300 }
301 
302 }  // namespace quiche
303