aboutsummaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
Diffstat (limited to 'core/src')
-rw-r--r--core/src/Utils/IO/DataStream.cpp278
-rw-r--r--core/src/Utils/IO/DataStream.hpp184
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);
+}