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 * @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 pels 372 } // namespace openpower 373