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