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