blob: 5c03adf1d0c562c575eda2a9b55ba39ef32d4d1d [file] [log] [blame]
Ben Tynerf5210bb2021-01-05 12:58:10 -06001#pragma once
2
3#include <arpa/inet.h>
4#include <byteswap.h>
5
6#include <cassert>
7#include <cstring>
8#include <memory>
9#include <stdexcept>
10#include <string>
11#include <vector>
12
13namespace attn
14{
15namespace pel
16{
17
18namespace detail
19{
20/**
21 * @brief A host-to-network implementation for uint64_t
22 *
23 * @param[in] value - the value to convert to
24 * @return uint64_t - the byteswapped value
25 */
26inline uint64_t htonll(uint64_t value)
27{
28 return bswap_64(value);
29}
30
31/**
32 * @brief A network-to-host implementation for uint64_t
33 *
34 * @param[in] value - the value to convert to
35 * @return uint64_t - the byteswapped value
36 */
37inline uint64_t ntohll(uint64_t value)
38{
39 return bswap_64(value);
40}
41} // namespace detail
42
43/**
44 * @class Stream
45 *
46 * This class is used for getting data types into and out of a vector<uint8_t>
47 * that contains data in network byte (big endian) ordering.
48 */
49class Stream
50{
51 public:
Patrick Williams27dd6362023-05-10 07:51:20 -050052 Stream() = delete;
53 ~Stream() = default;
54 Stream(const Stream&) = default;
Ben Tynerf5210bb2021-01-05 12:58:10 -060055 Stream& operator=(const Stream&) = default;
Patrick Williams27dd6362023-05-10 07:51:20 -050056 Stream(Stream&&) = default;
57 Stream& operator=(Stream&&) = default;
Ben Tynerf5210bb2021-01-05 12:58:10 -060058
59 /**
60 * @brief Constructor
61 *
62 * @param[in] data - the vector of data
63 */
64 explicit Stream(std::vector<uint8_t>& data) : _data(data), _offset(0) {}
65
66 /**
67 * @brief Constructor
68 *
69 * @param[in] data - the vector of data
70 * @param[in] offset - the starting offset
71 */
72 Stream(std::vector<uint8_t>& data, std::size_t offset) :
73 _data(data), _offset(offset)
74 {
75 if (_offset >= _data.size())
76 {
77 throw std::out_of_range("Offset out of range");
78 }
79 }
80
81 /**
82 * @brief Extraction operator for a uint8_t
83 *
84 * @param[out] value - filled in with the value
85 * @return Stream&
86 */
87 Stream& operator>>(uint8_t& value)
88 {
89 read(&value, 1);
90 return *this;
91 }
92
93 /**
94 * @brief Extraction operator for a char
95 *
96 * @param[out] value -filled in with the value
97 * @return Stream&
98 */
99 Stream& operator>>(char& value)
100 {
101 read(&value, 1);
102 return *this;
103 }
104
105 /**
106 * @brief Extraction operator for a uint16_t
107 *
108 * @param[out] value -filled in with the value
109 * @return Stream&
110 */
111 Stream& operator>>(uint16_t& value)
112 {
113 read(&value, 2);
114 value = htons(value);
115 return *this;
116 }
117
118 /**
119 * @brief Extraction operator for a uint32_t
120 *
121 * @param[out] value -filled in with the value
122 * @return Stream&
123 */
124 Stream& operator>>(uint32_t& value)
125 {
126 read(&value, 4);
127 value = htonl(value);
128 return *this;
129 }
130
131 /**
132 * @brief Extraction operator for a uint64_t
133 *
134 * @param[out] value -filled in with the value
135 * @return Stream&
136 */
137 Stream& operator>>(uint64_t& value)
138 {
139 read(&value, 8);
140 value = detail::htonll(value);
141 return *this;
142 }
143
144 /**
145 * @brief Extraction operator for a std::vector<uint8_t>
146 *
147 * The vector's size is the amount extracted.
148 *
149 * @param[out] value - filled in with the value
150 * @return Stream&
151 */
152 Stream& operator>>(std::vector<uint8_t>& value)
153 {
154 if (!value.empty())
155 {
156 read(value.data(), value.size());
157 }
158 return *this;
159 }
160
161 /**
162 * @brief Extraction operator for a std::vector<char>
163 *
164 * The vector's size is the amount extracted.
165 *
166 * @param[out] value - filled in with the value
167 * @return Stream&
168 */
169 Stream& operator>>(std::vector<char>& value)
170 {
171 if (!value.empty())
172 {
173 read(value.data(), value.size());
174 }
175 return *this;
176 }
177
178 /**
179 * @brief Insert operator for a uint8_t
180 *
181 * @param[in] value - the value to write to the stream
182 * @return Stream&
183 */
184 Stream& operator<<(uint8_t value)
185 {
186 write(&value, 1);
187 return *this;
188 }
189
190 /**
191 * @brief Insert operator for a char
192 *
193 * @param[in] value - the value to write to the stream
194 * @return Stream&
195 */
196 Stream& operator<<(char value)
197 {
198 write(&value, 1);
199 return *this;
200 }
201
202 /**
203 * @brief Insert operator for a uint16_t
204 *
205 * @param[in] value - the value to write to the stream
206 * @return Stream&
207 */
208 Stream& operator<<(uint16_t value)
209 {
210 uint16_t data = ntohs(value);
211 write(&data, 2);
212 return *this;
213 }
214
215 /**
216 * @brief Insert operator for a uint32_t
217 *
218 * @param[in] value - the value to write to the stream
219 * @return Stream&
220 */
221 Stream& operator<<(uint32_t value)
222 {
223 uint32_t data = ntohl(value);
224 write(&data, 4);
225 return *this;
226 }
227
228 /**
229 * @brief Insert operator for a uint64_t
230 *
231 * @param[in] value - the value to write to the stream
232 * @return Stream&
233 */
234 Stream& operator<<(uint64_t value)
235 {
236 uint64_t data = detail::ntohll(value);
237 write(&data, 8);
238 return *this;
239 }
240
241 /**
242 * @brief Insert operator for a std::vector<uint8_t>
243 *
244 * The full vector is written to the stream.
245 *
246 * @param[in] value - the value to write to the stream
247 * @return Stream&
248 */
249 Stream& operator<<(const std::vector<uint8_t>& value)
250 {
251 if (!value.empty())
252 {
253 write(value.data(), value.size());
254 }
255 return *this;
256 }
257
258 /**
259 * @brief Insert operator for a std::vector<char>
260 *
261 * The full vector is written to the stream.
262 *
263 * @param[in] value - the value to write to the stream
264 * @return Stream&
265 */
266 Stream& operator<<(const std::vector<char>& value)
267 {
268 if (!value.empty())
269 {
270 write(value.data(), value.size());
271 }
272 return *this;
273 }
274
275 /**
276 * @brief Sets the offset of the stream
277 *
278 * @param[in] newOffset - the new offset
279 */
280 void offset(std::size_t newOffset)
281 {
282 if (newOffset >= _data.size())
283 {
284 throw std::out_of_range("new offset out of range");
285 }
286
287 _offset = newOffset;
288 }
289
290 /**
291 * @brief Returns the current offset of the stream
292 *
293 * @return size_t - the offset
294 */
295 std::size_t offset() const
296 {
297 return _offset;
298 }
299
300 /**
301 * @brief Returns the remaining bytes left between the current offset
302 * and the data size.
303 *
304 * @return size_t - the remaining size
305 */
306 std::size_t remaining() const
307 {
308 assert(_data.size() >= _offset);
309 return _data.size() - _offset;
310 }
311
312 /**
313 * @brief Reads a specified number of bytes out of a stream
314 *
315 * @param[out] out - filled in with the data
316 * @param[in] size - the size to read
317 */
318 void read(void* out, std::size_t size)
319 {
320 rangeCheck(size);
321 memcpy(out, &_data[_offset], size);
322 _offset += size;
323 }
324
325 /**
326 * @brief Writes a specified number of bytes into the stream
327 *
328 * @param[in] in - the data to write
329 * @param[in] size - the size to write
330 */
331 void write(const void* in, std::size_t size)
332 {
333 size_t newSize = _offset + size;
334 if (newSize > _data.size())
335 {
336 _data.resize(newSize, 0);
337 }
338 memcpy(&_data[_offset], in, size);
339 _offset += size;
340 }
341
342 private:
343 /**
344 * @brief Throws an exception if the size passed in plus the current
345 * offset is bigger than the current data size.
346 * @param[in] size - the size to check
347 */
348 void rangeCheck(std::size_t size)
349 {
350 if (_offset + size > _data.size())
351 {
352 std::string msg{"Attempted stream overflow: offset "};
353 msg += std::to_string(_offset) + " buffer size " +
354 std::to_string(_data.size()) + " op size " +
355 std::to_string(size);
356 throw std::out_of_range(msg.c_str());
357 }
358 }
359
360 /**
361 * @brief The data that the stream accesses.
362 */
363 std::vector<uint8_t>& _data;
364
365 /**
366 * @brief The current offset of the stream.
367 */
368 std::size_t _offset;
369};
370
371} // namespace pel
372} // namespace attn