1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 */
8
9 #pragma once
10
11 #include <folly/Optional.h>
12 #include <folly/io/Cursor.h>
13 #include <folly/lang/Bits.h>
14 #include <quic/QuicException.h>
15 #include <quic/common/BufUtil.h>
16
17 namespace quic {
18
19 constexpr uint64_t kOneByteLimit = 0x3F;
20 constexpr uint64_t kTwoByteLimit = 0x3FFF;
21 constexpr uint64_t kFourByteLimit = 0x3FFFFFFF;
22 constexpr uint64_t kEightByteLimit = 0x3FFFFFFFFFFFFFFF;
23
24 namespace {
25 template <typename BufOp>
encodeOneByte(BufOp bufop,uint64_t value)26 inline uint8_t encodeOneByte(BufOp bufop, uint64_t value) {
27 auto modified = static_cast<uint8_t>(value);
28 bufop(modified);
29 return sizeof(modified);
30 }
31
32 template <typename BufOp>
encodeTwoBytes(BufOp bufop,uint64_t value)33 inline uint16_t encodeTwoBytes(BufOp bufop, uint64_t value) {
34 auto reduced = static_cast<uint16_t>(value);
35 uint16_t modified = reduced | 0x4000;
36 bufop(modified);
37 return sizeof(modified);
38 }
39
40 template <typename BufOp>
encodeFourBytes(BufOp bufop,uint64_t value)41 inline uint32_t encodeFourBytes(BufOp bufop, uint64_t value) {
42 auto reduced = static_cast<uint32_t>(value);
43 uint32_t modified = reduced | 0x80000000;
44 bufop(modified);
45 return sizeof(modified);
46 }
47
48 template <typename BufOp>
encodeEightBytes(BufOp bufop,uint64_t value)49 inline uint64_t encodeEightBytes(BufOp bufop, uint64_t value) {
50 uint64_t modified = value | 0xC000000000000000;
51 bufop(modified);
52 return sizeof(modified);
53 }
54 } // namespace
55
56 /**
57 * Encodes the integer and writes it out to appender. Returns the number of
58 * bytes written, or an error if value is too large to be represented with the
59 * variable length encoding.
60 */
61 template <typename BufOp>
encodeQuicInteger(uint64_t value,BufOp bufop)62 folly::Expected<size_t, TransportErrorCode> encodeQuicInteger(
63 uint64_t value,
64 BufOp bufop) {
65 if (value <= kOneByteLimit) {
66 return encodeOneByte(std::move(bufop), value);
67 } else if (value <= kTwoByteLimit) {
68 return encodeTwoBytes(std::move(bufop), value);
69 } else if (value <= kFourByteLimit) {
70 return encodeFourBytes(std::move(bufop), value);
71 } else if (value <= kEightByteLimit) {
72 return encodeEightBytes(std::move(bufop), value);
73 }
74 return folly::makeUnexpected(TransportErrorCode::INTERNAL_ERROR);
75 }
76
77 template <typename BufOp>
78 folly::Expected<size_t, TransportErrorCode>
encodeQuicInteger(uint64_t value,BufOp bufop,int outputSize)79 encodeQuicInteger(uint64_t value, BufOp bufop, int outputSize) {
80 switch (outputSize) {
81 case 1:
82 CHECK(value <= kOneByteLimit);
83 return encodeOneByte(std::move(bufop), value);
84 case 2:
85 CHECK(value <= kTwoByteLimit);
86 return encodeTwoBytes(std::move(bufop), value);
87 case 4:
88 CHECK(value <= kFourByteLimit);
89 return encodeFourBytes(std::move(bufop), value);
90 case 8:
91 CHECK(value <= kEightByteLimit);
92 return encodeEightBytes(std::move(bufop), value);
93 }
94 return folly::makeUnexpected(TransportErrorCode::INTERNAL_ERROR);
95 }
96
97 /**
98 * Reads an integer out of the cursor and returns a pair with the integer and
99 * the numbers of bytes read, or folly::none if there are not enough bytes to
100 * read the int. It only advances the cursor in case of success.
101 */
102 folly::Optional<std::pair<uint64_t, size_t>> decodeQuicInteger(
103 folly::io::Cursor& cursor,
104 uint64_t atMost = sizeof(uint64_t));
105
106 /**
107 * Returns the length of a quic integer given the first byte
108 */
109 uint8_t decodeQuicIntegerLength(uint8_t firstByte);
110
111 /**
112 * Returns number of bytes needed to encode value as a QUIC integer, or an error
113 * if value is too large to be represented with the variable
114 * length encoding
115 */
116 folly::Expected<size_t, TransportErrorCode> getQuicIntegerSize(uint64_t value);
117
118 /**
119 * Returns number of bytes needed to encode value as a QUIC integer, or throws
120 * an exception if value is too large to be represented with the variable
121 * length encoding
122 */
123 size_t getQuicIntegerSizeThrows(uint64_t value);
124
125 /**
126 * A better API for dealing with QUIC integers for encoding.
127 */
128 class QuicInteger {
129 public:
130 explicit QuicInteger(uint64_t value);
131
132 /**
133 * Encodes a QUIC integer to the appender.
134 */
135 template <typename BufOp>
encode(BufOp appender)136 size_t encode(BufOp appender) const {
137 auto size = encodeQuicInteger(value_, std::move(appender));
138 if (size.hasError()) {
139 LOG(ERROR) << "Value too large value=" << value_;
140 throw QuicTransportException(
141 folly::to<std::string>("Value too large ", value_), size.error());
142 }
143 return size.value();
144 }
145
146 template <typename BufOp>
encode(BufOp appender,int outputSize)147 size_t encode(BufOp appender, int outputSize) const {
148 auto size = encodeQuicInteger(value_, std::move(appender), outputSize);
149 if (size.hasError()) {
150 LOG(ERROR) << "Value too large value=" << value_;
151 throw QuicTransportException(
152 folly::to<std::string>("Value too large ", value_), size.error());
153 }
154 return size.value();
155 }
156
157 /**
158 * Returns the number of bytes needed to represent the QUIC integer in
159 * its encoded form.
160 **/
161 size_t getSize() const;
162
163 /**
164 * Returns the real value of the QUIC integer that it was instantiated with.
165 * This should normally never be used.
166 */
167 uint64_t getValue() const;
168
169 private:
170 uint64_t value_;
171 };
172 } // namespace quic
173