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