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