1 #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 
13 namespace openpower
14 {
15 namespace pels
16 {
17 
18 namespace 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  */
26 inline 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  */
37 inline 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  */
49 class 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     /**
147      * @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     /**
164      * @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     /**
181      * @brief Insert operator for a uint8_t
182      *
183      * @param[in] value - the value to write to the stream
184      * @return Stream&
185      */
186     Stream& operator<<(uint8_t value)
187     {
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      */
198     Stream& operator<<(char value)
199     {
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      */
210     Stream& operator<<(uint16_t value)
211     {
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      */
223     Stream& operator<<(uint32_t value)
224     {
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      */
236     Stream& operator<<(uint64_t value)
237     {
238         uint64_t data = detail::ntohll(value);
239         write(&data, 8);
240         return *this;
241     }
242 
243     /**
244      * @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     /**
261      * @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     /**
278      * @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      */
333     void write(const void* in, std::size_t size)
334     {
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
375