diff options
Diffstat (limited to 'core/src/Utils')
-rw-r--r-- | core/src/Utils/IO/DataStream.cpp | 278 | ||||
-rw-r--r-- | core/src/Utils/IO/DataStream.hpp | 184 |
2 files changed, 369 insertions, 93 deletions
diff --git a/core/src/Utils/IO/DataStream.cpp b/core/src/Utils/IO/DataStream.cpp index 0e0a37b..95e0b09 100644 --- a/core/src/Utils/IO/DataStream.cpp +++ b/core/src/Utils/IO/DataStream.cpp @@ -1,6 +1,280 @@ #include "DataStream.hpp" -DataStream::DataStream() - : mEndian{ LittleEndian } +#include <bit> +#include <limits> + +static_assert(std::numeric_limits<float>::is_iec559, "Non IEE754/IEC559 'float' is not supported."); +static_assert(std::numeric_limits<double>::is_iec559, "Non IEE754/IEC559 'double' is not supported."); + +static_assert(std::endian::native == std::endian::little || std::endian::native == std::endian::big, "Mixed endian is not supported."); + +DataStream::DataStream(std::fstream stream) + : mBackend{ std::move(stream) } + , mEndian{ std::endian::big } +{ +} + +DataStream::~DataStream() = default; + +std::endian DataStream::GetEndianness() const +{ + return mEndian; +} + +void DataStream::SetEndianness(std::endian endianness) +{ + mEndian = endianness; +} + +void DataStream::ReadBytes(size_t byteCount, std::byte* buffer) +{ + mBackend.read(reinterpret_cast<char*>(buffer), static_cast<std::streamsize>(byteCount)); +} + +void DataStream::ReadBytes(size_t byteCount, char* buffer) +{ + mBackend.read(reinterpret_cast<char*>(buffer), static_cast<std::streamsize>(byteCount)); +} + +void DataStream::ReadBytes(size_t byteCount, signed char* buffer) +{ + mBackend.read(reinterpret_cast<char*>(buffer), static_cast<std::streamsize>(byteCount)); +} + +void DataStream::ReadBytes(size_t byteCount, unsigned char* buffer) +{ + mBackend.read(reinterpret_cast<char*>(buffer), static_cast<std::streamsize>(byteCount)); +} + +void DataStream::WriteBytes(size_t byteCount, const std::byte* buffer) +{ + mBackend.write(reinterpret_cast<const char*>(buffer), static_cast<std::streamsize>(byteCount)); +} + +void DataStream::WriteBytes(size_t byteCount, const char* buffer) +{ + mBackend.write(reinterpret_cast<const char*>(buffer), static_cast<std::streamsize>(byteCount)); +} + +void DataStream::WriteBytes(size_t byteCount, const signed char* buffer) +{ + mBackend.write(reinterpret_cast<const char*>(buffer), static_cast<std::streamsize>(byteCount)); +} + +void DataStream::WriteBytes(size_t byteCount, const unsigned char* buffer) +{ + mBackend.write(reinterpret_cast<const char*>(buffer), static_cast<std::streamsize>(byteCount)); +} + +// Reading/writing signed integer byte-by-byte is fine, since the representation got fixed to 2's complements in C++20 + +uint16_t ByteSwap(uint16_t n) +{ + auto bytes = reinterpret_cast<std::byte*>(n); + std::swap(bytes[0], bytes[1]); + return n; +} + +uint32_t ByteSwap(uint32_t n) +{ +#ifdef _MSC_VER + // TODO +#else + return __builtin_bswap32(n); +#endif +} + +uint64_t ByteSwap(uint64_t n) +{ +#ifdef _MSC_VER + // TODO +#else + return __builtin_bswap64(n); +#endif +} + +template <class TSigned> +TSigned ByteSwap(TSigned n) +{ + using Unsigned = std::make_unsigned_t<TSigned>; + + auto swapped = ::ByteSwap(std::bit_cast<Unsigned>(n)); + return std::bit_cast<TSigned>(swapped); +} + +void DataStream::Write(int8_t n) +{ + mBackend.write(reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void DataStream::Write(int16_t n) +{ + if (mEndian != std::endian::native) { + n = ::ByteSwap(n); + } + mBackend.write(reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void DataStream::Write(int32_t n) +{ + if (mEndian != std::endian::native) { + n = ::ByteSwap(n); + } + mBackend.write(reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void DataStream::Write(int64_t n) +{ + if (mEndian != std::endian::native) { + n = ::ByteSwap(n); + } + mBackend.write(reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void DataStream::Read(int8_t& n) +{ + // sizeof() of a reference type yields the size of the reference + mBackend.read(reinterpret_cast<char*>(&n), sizeof(n)); +} + +void DataStream::Read(int16_t& n) +{ + int16_t tmp; + mBackend.read(reinterpret_cast<char*>(&tmp), sizeof(tmp)); + if (mEndian != std::endian::native) { + n = ::ByteSwap(tmp); + } else { + n = tmp; + } +} + +void DataStream::Read(int32_t& n) +{ + int32_t tmp; + mBackend.read(reinterpret_cast<char*>(&tmp), sizeof(tmp)); + if (mEndian != std::endian::native) { + n = ::ByteSwap(tmp); + } else { + n = tmp; + } +} + +void DataStream::Read(int64_t& n) +{ + int64_t tmp; + mBackend.read(reinterpret_cast<char*>(&tmp), sizeof(tmp)); + if (mEndian != std::endian::native) { + n = ::ByteSwap(tmp); + } else { + n = tmp; + } +} + +void DataStream::Write(uint8_t n) +{ + mBackend.write(reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void DataStream::Write(uint16_t n) +{ + if (mEndian != std::endian::native) { + n = ::ByteSwap(n); + } + mBackend.write(reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void DataStream::Write(uint32_t n) +{ + if (mEndian != std::endian::native) { + n = ::ByteSwap(n); + } + mBackend.write(reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void DataStream::Write(uint64_t n) { + if (mEndian != std::endian::native) { + n = ::ByteSwap(n); + } + mBackend.write(reinterpret_cast<const char*>(&n), sizeof(n)); +} + +void DataStream::Read(uint8_t& n) +{ + mBackend.read(reinterpret_cast<char*>(&n), sizeof(n)); +} + +void DataStream::Read(uint16_t& n) +{ + uint16_t tmp; + mBackend.read(reinterpret_cast<char*>(&tmp), sizeof(tmp)); + if (mEndian != std::endian::native) { + n = ::ByteSwap(tmp); + } else { + n = tmp; + } +} + +void DataStream::Read(uint32_t& n) +{ + uint32_t tmp; + mBackend.read(reinterpret_cast<char*>(&tmp), sizeof(tmp)); + if (mEndian != std::endian::native) { + n = ::ByteSwap(tmp); + } else { + n = tmp; + } +} + +void DataStream::Read(uint64_t& n) +{ + uint64_t tmp; + mBackend.read(reinterpret_cast<char*>(&tmp), sizeof(tmp)); + if (mEndian != std::endian::native) { + n = ::ByteSwap(tmp); + } else { + n = tmp; + } +} + +void DataStream::Write(float n) +{ + auto buffer = std::bit_cast<uint32_t>(n); + if (mEndian != std::endian::native) { + buffer = ::ByteSwap(buffer); + } + mBackend.read(reinterpret_cast<char*>(&buffer), sizeof(buffer)); +} + +void DataStream::Write(double n) +{ + auto buffer = std::bit_cast<uint64_t>(n); + if (mEndian != std::endian::native) { + buffer = ::ByteSwap(buffer); + } + mBackend.read(reinterpret_cast<char*>(&buffer), sizeof(buffer)); +} + +void DataStream::Read(float& n) +{ + uint32_t buffer; + Read(buffer); + + if (mEndian != std::endian::native) { + buffer = ::ByteSwap(buffer); + } + + n = std::bit_cast<float>(buffer); +} + +void DataStream::Read(double& n) +{ + uint64_t buffer; + Read(buffer); + + if (mEndian != std::endian::native) { + buffer = ::ByteSwap(buffer); + } + + n = std::bit_cast<double>(buffer); } diff --git a/core/src/Utils/IO/DataStream.hpp b/core/src/Utils/IO/DataStream.hpp index a88fe07..b965263 100644 --- a/core/src/Utils/IO/DataStream.hpp +++ b/core/src/Utils/IO/DataStream.hpp @@ -2,8 +2,10 @@ #include "cplt_fwd.hpp" +#include <bit> #include <cstddef> #include <cstdint> +#include <fstream> #include <span> class SerializationStreamProxy @@ -18,28 +20,16 @@ public: DataStream* Stream; template <class T> - void Bytes(size_t byteCount, T* buffer) const - { - Stream->WriteBytes(byteCount, buffer); - } + void Bytes(size_t byteCount, T* buffer) const; template <class T> - void Value(T t) const - { - Stream->Write(t); - } + void Value(T t) const; template <class T> - void Object(T& obj) const - { - Stream->WriteObject(obj); - } + void Object(T& obj) const; template <class TObject, class TAdapter> - void ObjectAdapted(TObject& obj, TAdapter adapter) const - { - Stream->WriteObjectAdapted(obj, adapter); - } + void ObjectAdapted(TObject& obj, TAdapter&& adapter) const; }; class DeserializationStreamProxy @@ -54,66 +44,46 @@ public: DataStream* Stream; template <class T> - void Bytes(size_t byteCount, T* buffer) const - { - Stream->WriteBytes(byteCount, buffer); - } + void Bytes(size_t byteCount, T* buffer) const; template <class T> - void Value(T& t) const - { - Stream->Read(t); - } + void Value(T& t) const; template <class T> - void Object(T& obj) const - { - Stream->ReadObject(obj); - } + void Object(T& obj) const; template <class TObject, class TAdapter> - void ObjectAdapted(TObject& obj, TAdapter& adapter) const - { - Stream->ReadObjectAdapted(obj, adapter); - } + void ObjectAdapted(TObject& obj, TAdapter&& adapter) const; }; -namespace DataStreamTraits { - -template <class T> -concept HasMember = requires(T t) -{ - t.ReadFromDataStream(std::declval<DataStream>()); - t.WriteToDataStream(std::declval<DataStream>()); -}; - -template <class T> -concept HasIOAdapterMember = requires(T t) -{ - t.OperateIOProxy(std::declval<SerializationStreamProxy>()); - t.OperateIOProxy(std::declval<DeserializationStreamProxy>()); -}; - -} // namespace DataStreamTraits - class DataStream { -public: - enum Endianness - { - BigEndian, - LittleEndian, - }; - private: - Endianness mEndian; + // TODO customizable storage lik QIODevice + // TODO move read/write into separate classes, removing need of IOAdapters? + std::fstream mBackend; + std::endian mEndian; public: - DataStream(); + DataStream(std::fstream stream); ~DataStream(); - Endianness GetEndianness() const; - void SetEndianness(Endianness endianness); + std::endian GetEndianness() const; + void SetEndianness(std::endian endianness); + + void WriteBytes(size_t byteCount, const std::byte* buffer); + void WriteBytes(size_t byteCount, const char* buffer); + void WriteBytes(size_t byteCount, const signed char* buffer); + void WriteBytes(size_t byteCount, const unsigned char* buffer); + + template <class TIterator> + void WriteBytes(TIterator&& begin, TIterator&& end) + { + for (; begin != end; ++begin) { + uint8_t byte = *begin; + Write(byte); + } + } void ReadBytes(size_t byteCount, std::byte* buffer); void ReadBytes(size_t byteCount, char* buffer); @@ -131,20 +101,6 @@ public: } } - void WriteBytes(size_t byteCount, const std::byte* buffer); - void WriteBytes(size_t byteCount, const char* buffer); - void WriteBytes(size_t byteCount, const signed char* buffer); - void WriteBytes(size_t byteCount, const unsigned char* buffer); - - template <class TIterator> - void WriteBytes(TIterator&& begin, TIterator&& end) - { - for (; begin != end; ++begin) { - uint8_t byte = *begin; - Write(byte); - } - } - void Write(int8_t n); void Write(int16_t n); void Write(int32_t n); @@ -172,13 +128,12 @@ public: void Read(double& n); template <class TObject> - void ReadObject(TObject& obj) + void WriteObject(TObject& obj) { - using namespace DataStreamTraits; - if constexpr (requires(TObject t) { t.ReadFromDataStream(std::declval<DataStream>()); }) { - obj.ReadFromDataStream(*this); - } else if constexpr (requires(TObject t) { t.OperateIOProxy(std::declval<DeserializationStreamProxy>()); }) { - DeserializationStreamProxy adapter{ this }; + if constexpr (requires(TObject t) { t.WriteToDataStream(std::declval<DataStream>()); }) { + obj.WriteToDataStream(*this); + } else if constexpr (requires(TObject t) { t.OperateIOProxy(std::declval<SerializationStreamProxy>()); }) { + SerializationStreamProxy adapter{ this }; obj.OperateIOProxy(adapter); } else { static_assert(false && sizeof(TObject), "This type does not have integration with DataStream."); @@ -186,19 +141,18 @@ public: } template <class TObject, class TAdapter> - void ReadObjectAdapted(TObject& obj, TAdapter& adapter) + void WriteObjectAdapted(TObject& obj, TAdapter&& adapter) { - adapter.ReadFromDataStream(*this, obj); + adapter.WriteToDataStream(*this, obj); } template <class TObject> - void WriteObject(TObject& obj) + void ReadObject(TObject& obj) { - using namespace DataStreamTraits; - if constexpr (requires(TObject t) { t.WriteToDataStream(std::declval<DataStream>()); }) { - obj.WriteToDataStream(*this); - } else if constexpr (requires(TObject t) { t.OperateIOProxy(std::declval<SerializationStreamProxy>()); }) { - SerializationStreamProxy adapter{ this }; + if constexpr (requires(TObject t) { t.ReadFromDataStream(std::declval<DataStream>()); }) { + obj.ReadFromDataStream(*this); + } else if constexpr (requires(TObject t) { t.OperateIOProxy(std::declval<DeserializationStreamProxy>()); }) { + DeserializationStreamProxy adapter{ this }; obj.OperateIOProxy(adapter); } else { static_assert(false && sizeof(TObject), "This type does not have integration with DataStream."); @@ -206,8 +160,56 @@ public: } template <class TObject, class TAdapter> - void WriteObjectAdapted(TObject& obj, TAdapter& adapter) + void ReadObjectAdapted(TObject& obj, TAdapter&& adapter) { - adapter.WriteToDataStream(*this, obj); + adapter.ReadFromDataStream(*this, obj); } }; + +template <class T> +void SerializationStreamProxy::Bytes(size_t byteCount, T* buffer) const +{ + Stream->WriteBytes(byteCount, buffer); +} + +template <class T> +void SerializationStreamProxy::Value(T t) const +{ + Stream->Write(t); +} + +template <class T> +void SerializationStreamProxy::Object(T& obj) const +{ + Stream->WriteObject(obj); +} + +template <class TObject, class TAdapter> +void SerializationStreamProxy::ObjectAdapted(TObject& obj, TAdapter&& adapter) const +{ + Stream->WriteObjectAdapted(obj, adapter); +} + +template <class T> +void DeserializationStreamProxy::Bytes(size_t byteCount, T* buffer) const +{ + Stream->WriteBytes(byteCount, buffer); +} + +template <class T> +void DeserializationStreamProxy::Value(T& t) const +{ + Stream->Read(t); +} + +template <class T> +void DeserializationStreamProxy::Object(T& obj) const +{ + Stream->ReadObject(obj); +} + +template <class TObject, class TAdapter> +void DeserializationStreamProxy::ObjectAdapted(TObject& obj, TAdapter&& adapter) const +{ + Stream->ReadObjectAdapted(obj, adapter); +} |