blob: 0785f0e756e15832e121bb46d2e52df99d732999 [file] [log] [blame]
Matt Spinler113ad282019-07-09 14:44:13 -05001#pragma once
2
3#include <arpa/inet.h>
4#include <byteswap.h>
5
6#include <cassert>
7#include <cstring>
8#include <memory>
Andrew Geissler61efe322020-05-19 12:40:56 -05009#include <stdexcept>
Matt Spinler113ad282019-07-09 14:44:13 -050010#include <string>
11#include <vector>
12
13namespace openpower
14{
15namespace pels
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:
52 Stream() = delete;
53 ~Stream() = default;
54 Stream(const Stream&) = default;
55 Stream& operator=(const Stream&) = default;
56 Stream(Stream&&) = default;
57 Stream& operator=(Stream&&) = default;
58
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
68 /**
69 * @brief Constructor
70 *
71 * @param[in] data - the vector of data
72 * @param[in] offset - the starting offset
73 */
74 Stream(std::vector<uint8_t>& data, std::size_t offset) :
75 _data(data), _offset(offset)
76 {
77 if (_offset >= _data.size())
78 {
79 throw std::out_of_range("Offset out of range");
80 }
81 }
82
83 /**
84 * @brief Extraction operator for a uint8_t
85 *
86 * @param[out] value - filled in with the value
87 * @return Stream&
88 */
89 Stream& operator>>(uint8_t& value)
90 {
91 read(&value, 1);
92 return *this;
93 }
94
95 /**
96 * @brief Extraction operator for a char
97 *
98 * @param[out] value -filled in with the value
99 * @return Stream&
100 */
101 Stream& operator>>(char& value)
102 {
103 read(&value, 1);
104 return *this;
105 }
106
107 /**
108 * @brief Extraction operator for a uint16_t
109 *
110 * @param[out] value -filled in with the value
111 * @return Stream&
112 */
113 Stream& operator>>(uint16_t& value)
114 {
115 read(&value, 2);
116 value = htons(value);
117 return *this;
118 }
119
120 /**
121 * @brief Extraction operator for a uint32_t
122 *
123 * @param[out] value -filled in with the value
124 * @return Stream&
125 */
126 Stream& operator>>(uint32_t& value)
127 {
128 read(&value, 4);
129 value = htonl(value);
130 return *this;
131 }
132
133 /**
134 * @brief Extraction operator for a uint64_t
135 *
136 * @param[out] value -filled in with the value
137 * @return Stream&
138 */
139 Stream& operator>>(uint64_t& value)
140 {
141 read(&value, 8);
142 value = detail::htonll(value);
143 return *this;
144 }
145
146 /**
Matt Spinlerd3777932019-09-24 13:38:47 -0500147 * @brief Extraction operator for a std::vector<uint8_t>
148 *
149 * The vector's size is the amount extracted.
150 *
151 * @param[out] value - filled in with the value
152 * @return Stream&
153 */
154 Stream& operator>>(std::vector<uint8_t>& value)
155 {
156 if (!value.empty())
157 {
158 read(value.data(), value.size());
159 }
160 return *this;
161 }
162
163 /**
Matt Spinlerced1a212019-10-08 14:01:06 -0500164 * @brief Extraction operator for a std::vector<char>
165 *
166 * The vector's size is the amount extracted.
167 *
168 * @param[out] value - filled in with the value
169 * @return Stream&
170 */
171 Stream& operator>>(std::vector<char>& value)
172 {
173 if (!value.empty())
174 {
175 read(value.data(), value.size());
176 }
177 return *this;
178 }
179
180 /**
Matt Spinler113ad282019-07-09 14:44:13 -0500181 * @brief Insert operator for a uint8_t
182 *
183 * @param[in] value - the value to write to the stream
184 * @return Stream&
185 */
Matt Spinler237570d2019-10-11 15:15:19 -0500186 Stream& operator<<(uint8_t value)
Matt Spinler113ad282019-07-09 14:44:13 -0500187 {
188 write(&value, 1);
189 return *this;
190 }
191
192 /**
193 * @brief Insert operator for a char
194 *
195 * @param[in] value - the value to write to the stream
196 * @return Stream&
197 */
Matt Spinler237570d2019-10-11 15:15:19 -0500198 Stream& operator<<(char value)
Matt Spinler113ad282019-07-09 14:44:13 -0500199 {
200 write(&value, 1);
201 return *this;
202 }
203
204 /**
205 * @brief Insert operator for a uint16_t
206 *
207 * @param[in] value - the value to write to the stream
208 * @return Stream&
209 */
Matt Spinler237570d2019-10-11 15:15:19 -0500210 Stream& operator<<(uint16_t value)
Matt Spinler113ad282019-07-09 14:44:13 -0500211 {
212 uint16_t data = ntohs(value);
213 write(&data, 2);
214 return *this;
215 }
216
217 /**
218 * @brief Insert operator for a uint32_t
219 *
220 * @param[in] value - the value to write to the stream
221 * @return Stream&
222 */
Matt Spinler237570d2019-10-11 15:15:19 -0500223 Stream& operator<<(uint32_t value)
Matt Spinler113ad282019-07-09 14:44:13 -0500224 {
225 uint32_t data = ntohl(value);
226 write(&data, 4);
227 return *this;
228 }
229
230 /**
231 * @brief Insert operator for a uint64_t
232 *
233 * @param[in] value - the value to write to the stream
234 * @return Stream&
235 */
Matt Spinler237570d2019-10-11 15:15:19 -0500236 Stream& operator<<(uint64_t value)
Matt Spinler113ad282019-07-09 14:44:13 -0500237 {
238 uint64_t data = detail::ntohll(value);
239 write(&data, 8);
240 return *this;
241 }
242
243 /**
Matt Spinlerd3777932019-09-24 13:38:47 -0500244 * @brief Insert operator for a std::vector<uint8_t>
245 *
246 * The full vector is written to the stream.
247 *
248 * @param[in] value - the value to write to the stream
249 * @return Stream&
250 */
251 Stream& operator<<(const std::vector<uint8_t>& value)
252 {
253 if (!value.empty())
254 {
255 write(value.data(), value.size());
256 }
257 return *this;
258 }
259
260 /**
Matt Spinlerced1a212019-10-08 14:01:06 -0500261 * @brief Insert operator for a std::vector<char>
262 *
263 * The full vector is written to the stream.
264 *
265 * @param[in] value - the value to write to the stream
266 * @return Stream&
267 */
268 Stream& operator<<(const std::vector<char>& value)
269 {
270 if (!value.empty())
271 {
272 write(value.data(), value.size());
273 }
274 return *this;
275 }
276
277 /**
Matt Spinler113ad282019-07-09 14:44:13 -0500278 * @brief Sets the offset of the stream
279 *
280 * @param[in] newOffset - the new offset
281 */
282 void offset(std::size_t newOffset)
283 {
284 if (newOffset >= _data.size())
285 {
286 throw std::out_of_range("new offset out of range");
287 }
288
289 _offset = newOffset;
290 }
291
292 /**
293 * @brief Returns the current offset of the stream
294 *
295 * @return size_t - the offset
296 */
297 std::size_t offset() const
298 {
299 return _offset;
300 }
301
302 /**
303 * @brief Returns the remaining bytes left between the current offset
304 * and the data size.
305 *
306 * @return size_t - the remaining size
307 */
308 std::size_t remaining() const
309 {
310 assert(_data.size() >= _offset);
311 return _data.size() - _offset;
312 }
313
314 /**
315 * @brief Reads a specified number of bytes out of a stream
316 *
317 * @param[out] out - filled in with the data
318 * @param[in] size - the size to read
319 */
320 void read(void* out, std::size_t size)
321 {
322 rangeCheck(size);
323 memcpy(out, &_data[_offset], size);
324 _offset += size;
325 }
326
327 /**
328 * @brief Writes a specified number of bytes into the stream
329 *
330 * @param[in] in - the data to write
331 * @param[in] size - the size to write
332 */
Matt Spinlerd3777932019-09-24 13:38:47 -0500333 void write(const void* in, std::size_t size)
Matt Spinler113ad282019-07-09 14:44:13 -0500334 {
335 size_t newSize = _offset + size;
336 if (newSize > _data.size())
337 {
338 _data.resize(newSize, 0);
339 }
340 memcpy(&_data[_offset], in, size);
341 _offset += size;
342 }
343
344 private:
345 /**
346 * @brief Throws an exception if the size passed in plus the current
347 * offset is bigger than the current data size.
348 * @param[in] size - the size to check
349 */
350 void rangeCheck(std::size_t size)
351 {
352 if (_offset + size > _data.size())
353 {
354 std::string msg{"Attempted stream overflow: offset "};
355 msg += std::to_string(_offset) + " buffer size " +
356 std::to_string(_data.size()) + " op size " +
357 std::to_string(size);
358 throw std::out_of_range(msg.c_str());
359 }
360 }
361
362 /**
363 * @brief The data that the stream accesses.
364 */
365 std::vector<uint8_t>& _data;
366
367 /**
368 * @brief The current offset of the stream.
369 */
370 std::size_t _offset;
371};
372
373} // namespace pels
374} // namespace openpower