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 <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  */
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  */
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&) = 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     /**
146      * @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     /**
163      * @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     /**
180      * @brief Insert operator for a uint8_t
181      *
182      * @param[in] value - the value to write to the stream
183      * @return Stream&
184      */
185     Stream& operator<<(uint8_t value)
186     {
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      */
197     Stream& operator<<(char value)
198     {
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      */
209     Stream& operator<<(uint16_t value)
210     {
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      */
222     Stream& operator<<(uint32_t value)
223     {
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      */
235     Stream& operator<<(uint64_t value)
236     {
237         uint64_t data = detail::ntohll(value);
238         write(&data, 8);
239         return *this;
240     }
241 
242     /**
243      * @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     /**
260      * @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     /**
277      * @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      */
332     void write(const void* in, std::size_t size)
333     {
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
374