#pragma once #include "FileStream.hpp" #include "cplt_fwd.hpp" #include #include #include #include class BaseDataStream { private: std::endian mEndian = std::endian::big; public: std::endian GetEndianness() const; void SetEndianness(std::endian endianness); }; class InputDataStream : public BaseDataStream { private: InputFileStream mBackend; public: static constexpr bool IsSerializer() { return false; } InputDataStream(InputFileStream stream); void ReadBytes(size_t byteCount, std::byte* buffer); void ReadBytes(size_t byteCount, char* buffer); void ReadBytes(size_t byteCount, signed char* buffer); void ReadBytes(size_t byteCount, unsigned char* buffer); template void ReadBytes(size_t byteCount, TInserter&& inserter) { for (size_t i = 0; i < byteCount; ++i) { uint8_t byte; Read(byte); inserter = byte; } } void Read(int8_t& n); void Read(int16_t& n); void Read(int32_t& n); void Read(int64_t& n); void Read(uint8_t& n); void Read(uint16_t& n); void Read(uint32_t& n); void Read(uint64_t& n); void Read(float& n); void Read(double& n); template requires std::is_enum_v void ReadEnum(TEnum& e) { std::underlying_type_t n; Read(n); e = static_cast(e); } template void ReadObject(TObject& obj) { if constexpr (requires(TObject t) { t.ReadFromDataStream(std::declval()); }) { obj.ReadFromDataStream(*this); } else if constexpr (requires(TObject t) { t.OperateIOProxy(std::declval()); }) { obj.OperateIOProxy(*this); } else { static_assert(false && sizeof(TObject), "This type does not have integration with InputDataStream."); } } /// Helper to invoke either Read() or ReadObject(). Note that this function doesn't account for types that needs and adapter. /// This is intended for writing IO adapters, users that's writing IO logic shouldn't using this - it increases compile time while reducing readability. template void ReadGeneric(T& t) { if constexpr (requires(T tt, InputDataStream ss) { ss.Read(tt); }) { Read(t); } else if constexpr (requires(T tt, InputDataStream ss) { ss.ReadEnum(tt); }) { ReadEnum(t); } else if constexpr (requires(T tt, InputDataStream ss) { ss.ReadObject(tt); }) { ReadObject(t); } else { static_assert(false && sizeof(T), "This type is neither a 'value' nor an 'object'."); } } template void ReadObjectAdapted(TObject& obj) { TAdapter::ReadFromDataStream(*this, obj); } public: // Proxy functions for OperateIOProxy template void Bytes(size_t byteCount, T* buffer) { ReadBytes(byteCount, buffer); } template void Value(T& t) { Read(t); } template void Object(T& obj) { ReadObject(obj); } template void ObjectAdapted(TObject& obj) { ReadObjectAdapted(obj); } }; class OutputDataStream : public BaseDataStream { private: OutputFileStream mBackend; public: static constexpr bool IsSerializer() { return true; } OutputDataStream(OutputFileStream stream); 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 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); void Write(int64_t n); void Write(uint8_t n); void Write(uint16_t n); void Write(uint32_t n); void Write(uint64_t n); void Write(float n); void Write(double n); template requires std::is_enum_v void WriteEnum(TEnum e) { auto n = static_cast>(e); Write(n); } template void WriteObject(TObject& obj) { if constexpr (requires(TObject t) { t.WriteToDataStream(std::declval()); }) { obj.WriteToDataStream(*this); } else if constexpr (requires(TObject t) { t.OperateIOProxy(std::declval()); }) { obj.OperateIOProxy(*this); } else { static_assert(false && sizeof(TObject), "This type does not have integration with OutputDataStream."); } } /// Helper to invoke either Write() or WriteObject(). Note that this function doesn't account for types that needs and adapter. /// This is intended for writing IO adapters, users that's writing IO logic shouldn't using this - it increases compile time while reducing readability. template void WriteGeneric(T& t) { if constexpr (requires(T tt, OutputDataStream ss) { ss.Write(tt); }) { Write(t); } else if constexpr (requires(T tt, OutputDataStream ss) { ss.WriteEnum(tt); }) { WriteEnum(t); } else if constexpr (requires(T tt, OutputDataStream ss) { ss.WriteObject(tt); }) { WriteObject(t); } else { static_assert(false && sizeof(T), "This type is neither a 'value' nor an 'object'."); } } template void WriteObjectAdapted(TObject& obj) { TAdapter::WriteToDataStream(*this, obj); } public: // Proxy functions for OperateIOProxy template void Bytes(size_t byteCount, T* buffer) { WriteBytes(byteCount, buffer); } template void Value(T t) { Write(t); } template void Object(T& obj) { WriteObject(obj); } template void ObjectAdapted(TObject& obj) { WriteObjectAdapted(obj); } };