blob: 4922bc745e4f814c4b62661addcc3e60cc7c0446 [file] [log] [blame]
#pragma once
#include <arpa/inet.h>
#include <byteswap.h>
#include <cassert>
#include <cstring>
#include <memory>
#include <string>
#include <vector>
namespace openpower
{
namespace pels
{
namespace detail
{
/**
* @brief A host-to-network implementation for uint64_t
*
* @param[in] value - the value to convert to
* @return uint64_t - the byteswapped value
*/
inline uint64_t htonll(uint64_t value)
{
return bswap_64(value);
}
/**
* @brief A network-to-host implementation for uint64_t
*
* @param[in] value - the value to convert to
* @return uint64_t - the byteswapped value
*/
inline uint64_t ntohll(uint64_t value)
{
return bswap_64(value);
}
} // namespace detail
/**
* @class Stream
*
* This class is used for getting data types into and out of a vector<uint8_t>
* that contains data in network byte (big endian) ordering.
*/
class Stream
{
public:
Stream() = delete;
~Stream() = default;
Stream(const Stream&) = default;
Stream& operator=(const Stream&) = default;
Stream(Stream&&) = default;
Stream& operator=(Stream&&) = default;
/**
* @brief Constructor
*
* @param[in] data - the vector of data
*/
explicit Stream(std::vector<uint8_t>& data) : _data(data), _offset(0)
{
}
/**
* @brief Constructor
*
* @param[in] data - the vector of data
* @param[in] offset - the starting offset
*/
Stream(std::vector<uint8_t>& data, std::size_t offset) :
_data(data), _offset(offset)
{
if (_offset >= _data.size())
{
throw std::out_of_range("Offset out of range");
}
}
/**
* @brief Extraction operator for a uint8_t
*
* @param[out] value - filled in with the value
* @return Stream&
*/
Stream& operator>>(uint8_t& value)
{
read(&value, 1);
return *this;
}
/**
* @brief Extraction operator for a char
*
* @param[out] value -filled in with the value
* @return Stream&
*/
Stream& operator>>(char& value)
{
read(&value, 1);
return *this;
}
/**
* @brief Extraction operator for a uint16_t
*
* @param[out] value -filled in with the value
* @return Stream&
*/
Stream& operator>>(uint16_t& value)
{
read(&value, 2);
value = htons(value);
return *this;
}
/**
* @brief Extraction operator for a uint32_t
*
* @param[out] value -filled in with the value
* @return Stream&
*/
Stream& operator>>(uint32_t& value)
{
read(&value, 4);
value = htonl(value);
return *this;
}
/**
* @brief Extraction operator for a uint64_t
*
* @param[out] value -filled in with the value
* @return Stream&
*/
Stream& operator>>(uint64_t& value)
{
read(&value, 8);
value = detail::htonll(value);
return *this;
}
/**
* @brief Extraction operator for a std::vector<uint8_t>
*
* The vector's size is the amount extracted.
*
* @param[out] value - filled in with the value
* @return Stream&
*/
Stream& operator>>(std::vector<uint8_t>& value)
{
if (!value.empty())
{
read(value.data(), value.size());
}
return *this;
}
/**
* @brief Extraction operator for a std::vector<char>
*
* The vector's size is the amount extracted.
*
* @param[out] value - filled in with the value
* @return Stream&
*/
Stream& operator>>(std::vector<char>& value)
{
if (!value.empty())
{
read(value.data(), value.size());
}
return *this;
}
/**
* @brief Insert operator for a uint8_t
*
* @param[in] value - the value to write to the stream
* @return Stream&
*/
Stream& operator<<(uint8_t& value)
{
write(&value, 1);
return *this;
}
/**
* @brief Insert operator for a char
*
* @param[in] value - the value to write to the stream
* @return Stream&
*/
Stream& operator<<(char& value)
{
write(&value, 1);
return *this;
}
/**
* @brief Insert operator for a uint16_t
*
* @param[in] value - the value to write to the stream
* @return Stream&
*/
Stream& operator<<(uint16_t& value)
{
uint16_t data = ntohs(value);
write(&data, 2);
return *this;
}
/**
* @brief Insert operator for a uint32_t
*
* @param[in] value - the value to write to the stream
* @return Stream&
*/
Stream& operator<<(uint32_t& value)
{
uint32_t data = ntohl(value);
write(&data, 4);
return *this;
}
/**
* @brief Insert operator for a uint64_t
*
* @param[in] value - the value to write to the stream
* @return Stream&
*/
Stream& operator<<(uint64_t& value)
{
uint64_t data = detail::ntohll(value);
write(&data, 8);
return *this;
}
/**
* @brief Insert operator for a std::vector<uint8_t>
*
* The full vector is written to the stream.
*
* @param[in] value - the value to write to the stream
* @return Stream&
*/
Stream& operator<<(const std::vector<uint8_t>& value)
{
if (!value.empty())
{
write(value.data(), value.size());
}
return *this;
}
/**
* @brief Insert operator for a std::vector<char>
*
* The full vector is written to the stream.
*
* @param[in] value - the value to write to the stream
* @return Stream&
*/
Stream& operator<<(const std::vector<char>& value)
{
if (!value.empty())
{
write(value.data(), value.size());
}
return *this;
}
/**
* @brief Sets the offset of the stream
*
* @param[in] newOffset - the new offset
*/
void offset(std::size_t newOffset)
{
if (newOffset >= _data.size())
{
throw std::out_of_range("new offset out of range");
}
_offset = newOffset;
}
/**
* @brief Returns the current offset of the stream
*
* @return size_t - the offset
*/
std::size_t offset() const
{
return _offset;
}
/**
* @brief Returns the remaining bytes left between the current offset
* and the data size.
*
* @return size_t - the remaining size
*/
std::size_t remaining() const
{
assert(_data.size() >= _offset);
return _data.size() - _offset;
}
/**
* @brief Reads a specified number of bytes out of a stream
*
* @param[out] out - filled in with the data
* @param[in] size - the size to read
*/
void read(void* out, std::size_t size)
{
rangeCheck(size);
memcpy(out, &_data[_offset], size);
_offset += size;
}
/**
* @brief Writes a specified number of bytes into the stream
*
* @param[in] in - the data to write
* @param[in] size - the size to write
*/
void write(const void* in, std::size_t size)
{
size_t newSize = _offset + size;
if (newSize > _data.size())
{
_data.resize(newSize, 0);
}
memcpy(&_data[_offset], in, size);
_offset += size;
}
private:
/**
* @brief Throws an exception if the size passed in plus the current
* offset is bigger than the current data size.
* @param[in] size - the size to check
*/
void rangeCheck(std::size_t size)
{
if (_offset + size > _data.size())
{
std::string msg{"Attempted stream overflow: offset "};
msg += std::to_string(_offset) + " buffer size " +
std::to_string(_data.size()) + " op size " +
std::to_string(size);
throw std::out_of_range(msg.c_str());
}
}
/**
* @brief The data that the stream accesses.
*/
std::vector<uint8_t>& _data;
/**
* @brief The current offset of the stream.
*/
std::size_t _offset;
};
} // namespace pels
} // namespace openpower