blob: 5c813b26aaef72f2d1883bac064ea91043f1e61b [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 Spinler113ad282019-07-09 14:44:13 -0500163 * @brief Insert operator for a uint8_t
164 *
165 * @param[in] value - the value to write to the stream
166 * @return Stream&
167 */
168 Stream& operator<<(uint8_t& value)
169 {
170 write(&value, 1);
171 return *this;
172 }
173
174 /**
175 * @brief Insert operator for a char
176 *
177 * @param[in] value - the value to write to the stream
178 * @return Stream&
179 */
180 Stream& operator<<(char& value)
181 {
182 write(&value, 1);
183 return *this;
184 }
185
186 /**
187 * @brief Insert operator for a uint16_t
188 *
189 * @param[in] value - the value to write to the stream
190 * @return Stream&
191 */
192 Stream& operator<<(uint16_t& value)
193 {
194 uint16_t data = ntohs(value);
195 write(&data, 2);
196 return *this;
197 }
198
199 /**
200 * @brief Insert operator for a uint32_t
201 *
202 * @param[in] value - the value to write to the stream
203 * @return Stream&
204 */
205 Stream& operator<<(uint32_t& value)
206 {
207 uint32_t data = ntohl(value);
208 write(&data, 4);
209 return *this;
210 }
211
212 /**
213 * @brief Insert operator for a uint64_t
214 *
215 * @param[in] value - the value to write to the stream
216 * @return Stream&
217 */
218 Stream& operator<<(uint64_t& value)
219 {
220 uint64_t data = detail::ntohll(value);
221 write(&data, 8);
222 return *this;
223 }
224
225 /**
Matt Spinlerd3777932019-09-24 13:38:47 -0500226 * @brief Insert operator for a std::vector<uint8_t>
227 *
228 * The full vector is written to the stream.
229 *
230 * @param[in] value - the value to write to the stream
231 * @return Stream&
232 */
233 Stream& operator<<(const std::vector<uint8_t>& value)
234 {
235 if (!value.empty())
236 {
237 write(value.data(), value.size());
238 }
239 return *this;
240 }
241
242 /**
Matt Spinler113ad282019-07-09 14:44:13 -0500243 * @brief Sets the offset of the stream
244 *
245 * @param[in] newOffset - the new offset
246 */
247 void offset(std::size_t newOffset)
248 {
249 if (newOffset >= _data.size())
250 {
251 throw std::out_of_range("new offset out of range");
252 }
253
254 _offset = newOffset;
255 }
256
257 /**
258 * @brief Returns the current offset of the stream
259 *
260 * @return size_t - the offset
261 */
262 std::size_t offset() const
263 {
264 return _offset;
265 }
266
267 /**
268 * @brief Returns the remaining bytes left between the current offset
269 * and the data size.
270 *
271 * @return size_t - the remaining size
272 */
273 std::size_t remaining() const
274 {
275 assert(_data.size() >= _offset);
276 return _data.size() - _offset;
277 }
278
279 /**
280 * @brief Reads a specified number of bytes out of a stream
281 *
282 * @param[out] out - filled in with the data
283 * @param[in] size - the size to read
284 */
285 void read(void* out, std::size_t size)
286 {
287 rangeCheck(size);
288 memcpy(out, &_data[_offset], size);
289 _offset += size;
290 }
291
292 /**
293 * @brief Writes a specified number of bytes into the stream
294 *
295 * @param[in] in - the data to write
296 * @param[in] size - the size to write
297 */
Matt Spinlerd3777932019-09-24 13:38:47 -0500298 void write(const void* in, std::size_t size)
Matt Spinler113ad282019-07-09 14:44:13 -0500299 {
300 size_t newSize = _offset + size;
301 if (newSize > _data.size())
302 {
303 _data.resize(newSize, 0);
304 }
305 memcpy(&_data[_offset], in, size);
306 _offset += size;
307 }
308
309 private:
310 /**
311 * @brief Throws an exception if the size passed in plus the current
312 * offset is bigger than the current data size.
313 * @param[in] size - the size to check
314 */
315 void rangeCheck(std::size_t size)
316 {
317 if (_offset + size > _data.size())
318 {
319 std::string msg{"Attempted stream overflow: offset "};
320 msg += std::to_string(_offset) + " buffer size " +
321 std::to_string(_data.size()) + " op size " +
322 std::to_string(size);
323 throw std::out_of_range(msg.c_str());
324 }
325 }
326
327 /**
328 * @brief The data that the stream accesses.
329 */
330 std::vector<uint8_t>& _data;
331
332 /**
333 * @brief The current offset of the stream.
334 */
335 std::size_t _offset;
336};
337
338} // namespace pels
339} // namespace openpower