diff --git a/libs/util/include/psemek/util/binary_stream.hpp b/libs/util/include/psemek/util/binary_stream.hpp new file mode 100644 index 00000000..9099987f --- /dev/null +++ b/libs/util/include/psemek/util/binary_stream.hpp @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +#include + +namespace psemek::util +{ + + namespace detail + { + + inline void unexpected_end() + { + throw std::runtime_error("Unexpected binary stream end"); + } + + template + struct read_helper + { + static_assert(std::is_trivially_copyable_v); + + static T read(std::string_view & data) + { + if (data.size() < sizeof(T)) + unexpected_end(); + + T value; + std::copy(data.data(), data.data() + sizeof(T), reinterpret_cast(std::addressof(value))); + data.remove_prefix(sizeof(T)); + return value; + } + }; + + template + struct read_helper> + { + static T read(std::string_view & data) + { + std::uint32_t size = read_helper::read(data); + + std::vector result; + + if constexpr (std::is_trivially_copyable_v) + { + if (data.size() < sizeof(T) * size) + unexpected_end(); + + result.resize(size); + std::copy(data.data(), data.data() + sizeof(T) * size, reinterpret_cast(result.data())); + } + else + { + for (std::uint32_t i = 0; i < size; ++i) + result.push_back(read_helper::read(data)); + } + + return result; + } + }; + + } + + struct binary_istream + { + std::string_view data; + + template + T read() + { + return detail::read_helper::read(data); + } + + char const * read_raw(std::size_t count) + { + if (data.size() < count) + detail::unexpected_end(); + + auto p = data.data(); + data.remove_prefix(count); + return p; + } + }; + +}