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