431a9c00a5
On Linux and macOS, using Clang to compile OpenStarbound produces about 400 MB worth of warnings during the build, making the compiler output unreadable and slowing the build down considerably. 99% of the warnings were unqualified uses of std::move and std::forward, which are now all properly qualified. Fixed a few other minor warnings about non-virtual destructors and some uses of std::move preventing copy elision on temporary objects. Most remaining warnings are now unused parameters.
394 lines
12 KiB
C++
394 lines
12 KiB
C++
#ifndef STAR_DATA_STREAM_HPP
|
|
#define STAR_DATA_STREAM_HPP
|
|
|
|
#include "StarString.hpp"
|
|
|
|
namespace Star {
|
|
|
|
STAR_EXCEPTION(DataStreamException, IOException);
|
|
|
|
// Writes complex types to bytes in a portable big-endian fashion.
|
|
class DataStream {
|
|
public:
|
|
DataStream();
|
|
virtual ~DataStream() = default;
|
|
|
|
static unsigned const CurrentStreamVersion = 1;
|
|
|
|
// DataStream defaults to big-endian order for all primitive types
|
|
ByteOrder byteOrder() const;
|
|
void setByteOrder(ByteOrder byteOrder);
|
|
|
|
// DataStream can optionally write strings as null terminated rather than
|
|
// length prefixed
|
|
bool nullTerminatedStrings() const;
|
|
void setNullTerminatedStrings(bool nullTerminatedStrings);
|
|
|
|
// streamCompatibilityVersion defaults to CurrentStreamVersion, but can be
|
|
// changed for compatibility with older versions of DataStream serialization.
|
|
unsigned streamCompatibilityVersion() const;
|
|
void setStreamCompatibilityVersion(unsigned streamCompatibilityVersion);
|
|
|
|
// Do direct reads and writes
|
|
virtual void readData(char* data, size_t len) = 0;
|
|
virtual void writeData(char const* data, size_t len) = 0;
|
|
|
|
// These do not read / write sizes, they simply read / write directly.
|
|
ByteArray readBytes(size_t len);
|
|
void writeBytes(ByteArray const& ba);
|
|
|
|
DataStream& operator<<(bool d);
|
|
DataStream& operator<<(char c);
|
|
DataStream& operator<<(int8_t d);
|
|
DataStream& operator<<(uint8_t d);
|
|
DataStream& operator<<(int16_t d);
|
|
DataStream& operator<<(uint16_t d);
|
|
DataStream& operator<<(int32_t d);
|
|
DataStream& operator<<(uint32_t d);
|
|
DataStream& operator<<(int64_t d);
|
|
DataStream& operator<<(uint64_t d);
|
|
DataStream& operator<<(float d);
|
|
DataStream& operator<<(double d);
|
|
|
|
DataStream& operator>>(bool& d);
|
|
DataStream& operator>>(char& c);
|
|
DataStream& operator>>(int8_t& d);
|
|
DataStream& operator>>(uint8_t& d);
|
|
DataStream& operator>>(int16_t& d);
|
|
DataStream& operator>>(uint16_t& d);
|
|
DataStream& operator>>(int32_t& d);
|
|
DataStream& operator>>(uint32_t& d);
|
|
DataStream& operator>>(int64_t& d);
|
|
DataStream& operator>>(uint64_t& d);
|
|
DataStream& operator>>(float& d);
|
|
DataStream& operator>>(double& d);
|
|
|
|
// Writes and reads a VLQ encoded integer. Can write / read anywhere from 1
|
|
// to 10 bytes of data, with integers of smaller (absolute) value taking up
|
|
// fewer bytes. size_t version can be used to portably write a size_t type,
|
|
// and portably and efficiently handles the case of NPos.
|
|
|
|
size_t writeVlqU(uint64_t i);
|
|
size_t writeVlqI(int64_t i);
|
|
size_t writeVlqS(size_t i);
|
|
|
|
size_t readVlqU(uint64_t& i);
|
|
size_t readVlqI(int64_t& i);
|
|
size_t readVlqS(size_t& i);
|
|
|
|
uint64_t readVlqU();
|
|
int64_t readVlqI();
|
|
size_t readVlqS();
|
|
|
|
// The following functions write / read data with length and then content
|
|
// following, but note that the length is encoded as an unsigned VLQ integer.
|
|
// String objects are encoded in utf8, and can optionally be written as null
|
|
// terminated rather than length then content.
|
|
|
|
DataStream& operator<<(const char* s);
|
|
DataStream& operator<<(std::string const& d);
|
|
DataStream& operator<<(ByteArray const& d);
|
|
DataStream& operator<<(String const& s);
|
|
|
|
DataStream& operator>>(std::string& d);
|
|
DataStream& operator>>(ByteArray& d);
|
|
DataStream& operator>>(String& s);
|
|
|
|
// All enum types are automatically serializable
|
|
|
|
template <typename EnumType, typename = typename std::enable_if<std::is_enum<EnumType>::value>::type>
|
|
DataStream& operator<<(EnumType const& e);
|
|
|
|
template <typename EnumType, typename = typename std::enable_if<std::is_enum<EnumType>::value>::type>
|
|
DataStream& operator>>(EnumType& e);
|
|
|
|
// Convenience method to avoid temporary.
|
|
template <typename T>
|
|
T read();
|
|
|
|
// Convenient argument style reading / writing
|
|
|
|
template <typename Data>
|
|
void read(Data& data);
|
|
|
|
template <typename Data>
|
|
void write(Data const& data);
|
|
|
|
// Argument style reading / writing with casting.
|
|
|
|
template <typename ReadType, typename Data>
|
|
void cread(Data& data);
|
|
|
|
template <typename WriteType, typename Data>
|
|
void cwrite(Data const& data);
|
|
|
|
// Argument style reading / writing of variable length integers. Arguments
|
|
// are explicitly casted, so things like enums are allowed.
|
|
|
|
template <typename IntegralType>
|
|
void vuread(IntegralType& data);
|
|
|
|
template <typename IntegralType>
|
|
void viread(IntegralType& data);
|
|
|
|
template <typename IntegralType>
|
|
void vsread(IntegralType& data);
|
|
|
|
template <typename IntegralType>
|
|
void vuwrite(IntegralType const& data);
|
|
|
|
template <typename IntegralType>
|
|
void viwrite(IntegralType const& data);
|
|
|
|
template <typename IntegralType>
|
|
void vswrite(IntegralType const& data);
|
|
|
|
// Store a fixed point number as a variable length integer
|
|
|
|
template <typename FloatType>
|
|
void vfread(FloatType& data, FloatType base);
|
|
|
|
template <typename FloatType>
|
|
void vfwrite(FloatType const& data, FloatType base);
|
|
|
|
// Read a shared / unique ptr, and store whether the pointer is initialized.
|
|
|
|
template <typename PointerType, typename ReadFunction>
|
|
void pread(PointerType& pointer, ReadFunction readFunction);
|
|
|
|
template <typename PointerType, typename WriteFunction>
|
|
void pwrite(PointerType const& pointer, WriteFunction writeFunction);
|
|
|
|
template <typename PointerType>
|
|
void pread(PointerType& pointer);
|
|
|
|
template <typename PointerType>
|
|
void pwrite(PointerType const& pointer);
|
|
|
|
// WriteFunction should be void (DataStream& ds, Element const& e)
|
|
template <typename Container, typename WriteFunction>
|
|
void writeContainer(Container const& container, WriteFunction function);
|
|
|
|
// ReadFunction should be void (DataStream& ds, Element& e)
|
|
template <typename Container, typename ReadFunction>
|
|
void readContainer(Container& container, ReadFunction function);
|
|
|
|
template <typename Container, typename WriteFunction>
|
|
void writeMapContainer(Container& map, WriteFunction function);
|
|
|
|
// Specialization of readContainer for map types (whose elements are a pair
|
|
// with the key type marked const)
|
|
template <typename Container, typename ReadFunction>
|
|
void readMapContainer(Container& map, ReadFunction function);
|
|
|
|
template <typename Container>
|
|
void writeContainer(Container const& container);
|
|
|
|
template <typename Container>
|
|
void readContainer(Container& container);
|
|
|
|
template <typename Container>
|
|
void writeMapContainer(Container const& container);
|
|
|
|
template <typename Container>
|
|
void readMapContainer(Container& container);
|
|
|
|
private:
|
|
void writeStringData(char const* data, size_t len);
|
|
|
|
ByteOrder m_byteOrder;
|
|
bool m_nullTerminatedStrings;
|
|
unsigned m_streamCompatibilityVersion;
|
|
};
|
|
|
|
template <typename EnumType, typename>
|
|
DataStream& DataStream::operator<<(EnumType const& e) {
|
|
*this << (typename std::underlying_type<EnumType>::type)e;
|
|
return *this;
|
|
}
|
|
|
|
template <typename EnumType, typename>
|
|
DataStream& DataStream::operator>>(EnumType& e) {
|
|
typename std::underlying_type<EnumType>::type i;
|
|
*this >> i;
|
|
e = (EnumType)i;
|
|
return *this;
|
|
}
|
|
|
|
template <typename T>
|
|
T DataStream::read() {
|
|
T t;
|
|
*this >> t;
|
|
return t;
|
|
}
|
|
|
|
template <typename Data>
|
|
void DataStream::read(Data& data) {
|
|
*this >> data;
|
|
}
|
|
|
|
template <typename Data>
|
|
void DataStream::write(Data const& data) {
|
|
*this << data;
|
|
}
|
|
|
|
template <typename ReadType, typename Data>
|
|
void DataStream::cread(Data& data) {
|
|
ReadType v;
|
|
*this >> v;
|
|
data = (Data)v;
|
|
}
|
|
|
|
template <typename WriteType, typename Data>
|
|
void DataStream::cwrite(Data const& data) {
|
|
WriteType v = (WriteType)data;
|
|
*this << v;
|
|
}
|
|
|
|
template <typename IntegralType>
|
|
void DataStream::vuread(IntegralType& data) {
|
|
uint64_t i = readVlqU();
|
|
data = (IntegralType)i;
|
|
}
|
|
|
|
template <typename IntegralType>
|
|
void DataStream::viread(IntegralType& data) {
|
|
int64_t i = readVlqI();
|
|
data = (IntegralType)i;
|
|
}
|
|
|
|
template <typename IntegralType>
|
|
void DataStream::vsread(IntegralType& data) {
|
|
size_t s = readVlqS();
|
|
data = (IntegralType)s;
|
|
}
|
|
|
|
template <typename IntegralType>
|
|
void DataStream::vuwrite(IntegralType const& data) {
|
|
writeVlqU((uint64_t)data);
|
|
}
|
|
|
|
template <typename IntegralType>
|
|
void DataStream::viwrite(IntegralType const& data) {
|
|
writeVlqI((int64_t)data);
|
|
}
|
|
|
|
template <typename IntegralType>
|
|
void DataStream::vswrite(IntegralType const& data) {
|
|
writeVlqS((size_t)data);
|
|
}
|
|
|
|
template <typename FloatType>
|
|
void DataStream::vfread(FloatType& data, FloatType base) {
|
|
int64_t i = readVlqI();
|
|
data = (FloatType)i * base;
|
|
}
|
|
|
|
template <typename FloatType>
|
|
void DataStream::vfwrite(FloatType const& data, FloatType base) {
|
|
writeVlqI((int64_t)round(data / base));
|
|
}
|
|
|
|
template <typename PointerType, typename ReadFunction>
|
|
void DataStream::pread(PointerType& pointer, ReadFunction readFunction) {
|
|
bool initialized = read<bool>();
|
|
if (initialized) {
|
|
auto element = make_unique<typename std::decay<typename PointerType::element_type>::type>();
|
|
readFunction(*this, *element);
|
|
pointer.reset(element.release());
|
|
} else {
|
|
pointer.reset();
|
|
}
|
|
}
|
|
|
|
template <typename PointerType, typename WriteFunction>
|
|
void DataStream::pwrite(PointerType const& pointer, WriteFunction writeFunction) {
|
|
if (pointer) {
|
|
write(true);
|
|
writeFunction(*this, *pointer);
|
|
} else {
|
|
write(false);
|
|
}
|
|
}
|
|
|
|
template <typename PointerType>
|
|
void DataStream::pread(PointerType& pointer) {
|
|
return pread(pointer, [](DataStream& ds, typename std::decay<typename PointerType::element_type>::type& value) {
|
|
ds.read(value);
|
|
});
|
|
}
|
|
|
|
template <typename PointerType>
|
|
void DataStream::pwrite(PointerType const& pointer) {
|
|
return pwrite(pointer, [](DataStream& ds, typename std::decay<typename PointerType::element_type>::type const& value) {
|
|
ds.write(value);
|
|
});
|
|
}
|
|
|
|
template <typename Container, typename WriteFunction>
|
|
void DataStream::writeContainer(Container const& container, WriteFunction function) {
|
|
writeVlqU(container.size());
|
|
for (auto const& elem : container)
|
|
function(*this, elem);
|
|
}
|
|
|
|
template <typename Container, typename ReadFunction>
|
|
void DataStream::readContainer(Container& container, ReadFunction function) {
|
|
container.clear();
|
|
size_t size = readVlqU();
|
|
for (size_t i = 0; i < size; ++i) {
|
|
typename Container::value_type elem;
|
|
function(*this, elem);
|
|
container.insert(container.end(), elem);
|
|
}
|
|
}
|
|
|
|
template <typename Container, typename WriteFunction>
|
|
void DataStream::writeMapContainer(Container& map, WriteFunction function) {
|
|
writeVlqU(map.size());
|
|
for (auto const& elem : map)
|
|
function(*this, elem.first, elem.second);
|
|
}
|
|
|
|
template <typename Container, typename ReadFunction>
|
|
void DataStream::readMapContainer(Container& map, ReadFunction function) {
|
|
map.clear();
|
|
size_t size = readVlqU();
|
|
for (size_t i = 0; i < size; ++i) {
|
|
typename Container::key_type key;
|
|
typename Container::mapped_type mapped;
|
|
function(*this, key, mapped);
|
|
map.insert(make_pair(std::move(key), std::move(mapped)));
|
|
}
|
|
}
|
|
|
|
template <typename Container>
|
|
void DataStream::writeContainer(Container const& container) {
|
|
writeContainer(container, [](DataStream& ds, typename Container::value_type const& element) { ds << element; });
|
|
}
|
|
|
|
template <typename Container>
|
|
void DataStream::readContainer(Container& container) {
|
|
readContainer(container, [](DataStream& ds, typename Container::value_type& element) { ds >> element; });
|
|
}
|
|
|
|
template <typename Container>
|
|
void DataStream::writeMapContainer(Container const& container) {
|
|
writeMapContainer(container, [](DataStream& ds, typename Container::key_type const& key, typename Container::mapped_type const& mapped) {
|
|
ds << key;
|
|
ds << mapped;
|
|
});
|
|
}
|
|
|
|
template <typename Container>
|
|
void DataStream::readMapContainer(Container& container) {
|
|
readMapContainer(container, [](DataStream& ds, typename Container::key_type& key, typename Container::mapped_type& mapped) {
|
|
ds >> key;
|
|
ds >> mapped;
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|