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