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 Insert operator for a uint8_t
147      *
148      * @param[in] value - the value to write to the stream
149      * @return Stream&
150      */
151     Stream& operator<<(uint8_t& value)
152     {
153         write(&value, 1);
154         return *this;
155     }
156 
157     /**
158      * @brief Insert operator for a char
159      *
160      * @param[in] value - the value to write to the stream
161      * @return Stream&
162      */
163     Stream& operator<<(char& value)
164     {
165         write(&value, 1);
166         return *this;
167     }
168 
169     /**
170      * @brief Insert operator for a uint16_t
171      *
172      * @param[in] value - the value to write to the stream
173      * @return Stream&
174      */
175     Stream& operator<<(uint16_t& value)
176     {
177         uint16_t data = ntohs(value);
178         write(&data, 2);
179         return *this;
180     }
181 
182     /**
183      * @brief Insert operator for a uint32_t
184      *
185      * @param[in] value - the value to write to the stream
186      * @return Stream&
187      */
188     Stream& operator<<(uint32_t& value)
189     {
190         uint32_t data = ntohl(value);
191         write(&data, 4);
192         return *this;
193     }
194 
195     /**
196      * @brief Insert operator for a uint64_t
197      *
198      * @param[in] value - the value to write to the stream
199      * @return Stream&
200      */
201     Stream& operator<<(uint64_t& value)
202     {
203         uint64_t data = detail::ntohll(value);
204         write(&data, 8);
205         return *this;
206     }
207 
208     /**
209      * @brief Sets the offset of the stream
210      *
211      * @param[in] newOffset - the new offset
212      */
213     void offset(std::size_t newOffset)
214     {
215         if (newOffset >= _data.size())
216         {
217             throw std::out_of_range("new offset out of range");
218         }
219 
220         _offset = newOffset;
221     }
222 
223     /**
224      * @brief Returns the current offset of the stream
225      *
226      * @return size_t - the offset
227      */
228     std::size_t offset() const
229     {
230         return _offset;
231     }
232 
233     /**
234      * @brief Returns the remaining bytes left between the current offset
235      *        and the data size.
236      *
237      * @return size_t - the remaining size
238      */
239     std::size_t remaining() const
240     {
241         assert(_data.size() >= _offset);
242         return _data.size() - _offset;
243     }
244 
245     /**
246      * @brief Reads a specified number of bytes out of a stream
247      *
248      * @param[out] out - filled in with the data
249      * @param[in] size - the size to read
250      */
251     void read(void* out, std::size_t size)
252     {
253         rangeCheck(size);
254         memcpy(out, &_data[_offset], size);
255         _offset += size;
256     }
257 
258     /**
259      * @brief Writes a specified number of bytes into the stream
260      *
261      * @param[in] in - the data to write
262      * @param[in] size - the size to write
263      */
264     void write(void* in, std::size_t size)
265     {
266         size_t newSize = _offset + size;
267         if (newSize > _data.size())
268         {
269             _data.resize(newSize, 0);
270         }
271         memcpy(&_data[_offset], in, size);
272         _offset += size;
273     }
274 
275   private:
276     /**
277      * @brief Throws an exception if the size passed in plus the current
278      *        offset is bigger than the current data size.
279      * @param[in] size - the size to check
280      */
281     void rangeCheck(std::size_t size)
282     {
283         if (_offset + size > _data.size())
284         {
285             std::string msg{"Attempted stream overflow: offset "};
286             msg += std::to_string(_offset) + " buffer size " +
287                    std::to_string(_data.size()) + " op size " +
288                    std::to_string(size);
289             throw std::out_of_range(msg.c_str());
290         }
291     }
292 
293     /**
294      * @brief The data that the stream accesses.
295      */
296     std::vector<uint8_t>& _data;
297 
298     /**
299      * @brief The current offset of the stream.
300      */
301     std::size_t _offset;
302 };
303 
304 } // namespace pels
305 } // namespace openpower
306