#include "foundation/binary_stream.h" #include namespace pp::foundation { namespace { [[nodiscard]] bool overlaps_backing_storage( const std::vector& backing, std::span bytes) noexcept { if (backing.empty() || bytes.empty()) { return false; } const auto backing_begin = reinterpret_cast(backing.data()); const auto backing_end = backing_begin + backing.size(); const auto bytes_begin = reinterpret_cast(bytes.data()); const auto bytes_end = bytes_begin + bytes.size(); return bytes_begin < backing_end && backing_begin < bytes_end; } } ByteReader::ByteReader(std::span bytes) noexcept : bytes_(bytes) { } std::size_t ByteReader::position() const noexcept { return position_; } std::size_t ByteReader::size() const noexcept { return bytes_.size(); } std::size_t ByteReader::remaining() const noexcept { return bytes_.size() - position_; } bool ByteReader::empty() const noexcept { return remaining() == 0; } Status ByteReader::seek(std::size_t position) noexcept { if (position > bytes_.size()) { return Status::out_of_range("seek position is outside the stream"); } position_ = position; return Status::success(); } Result ByteReader::read_u8() noexcept { const auto bytes = read_bytes(1); if (!bytes) { return Result::failure(bytes.status()); } return Result::success(static_cast(bytes.value()[0])); } Result ByteReader::read_u16_le() noexcept { const auto bytes = read_bytes(2); if (!bytes) { return Result::failure(bytes.status()); } const auto b0 = static_cast(bytes.value()[0]); const auto b1 = static_cast(bytes.value()[1]); return Result::success(static_cast(b0 | (b1 << 8U))); } Result ByteReader::read_u32_le() noexcept { const auto bytes = read_bytes(4); if (!bytes) { return Result::failure(bytes.status()); } const auto b0 = static_cast(bytes.value()[0]); const auto b1 = static_cast(bytes.value()[1]); const auto b2 = static_cast(bytes.value()[2]); const auto b3 = static_cast(bytes.value()[3]); return Result::success(b0 | (b1 << 8U) | (b2 << 16U) | (b3 << 24U)); } Result> ByteReader::read_bytes(std::size_t count) noexcept { if (count > remaining()) { return Result>::failure( Status::out_of_range("read would move beyond the end of the stream")); } const auto start = position_; position_ += count; return Result>::success(bytes_.subspan(start, count)); } ByteWriter::ByteWriter(std::vector& bytes) noexcept : bytes_(&bytes) { } std::size_t ByteWriter::size() const noexcept { return bytes_ == nullptr ? 0 : bytes_->size(); } Status ByteWriter::write_u8(std::uint8_t value) { if (bytes_ == nullptr) { return Status::invalid_argument("writer has no backing storage"); } bytes_->push_back(static_cast(value)); return Status::success(); } Status ByteWriter::write_u16_le(std::uint16_t value) { if (bytes_ == nullptr) { return Status::invalid_argument("writer has no backing storage"); } bytes_->push_back(static_cast(value & 0xffU)); bytes_->push_back(static_cast((value >> 8U) & 0xffU)); return Status::success(); } Status ByteWriter::write_u32_le(std::uint32_t value) { if (bytes_ == nullptr) { return Status::invalid_argument("writer has no backing storage"); } bytes_->push_back(static_cast(value & 0xffU)); bytes_->push_back(static_cast((value >> 8U) & 0xffU)); bytes_->push_back(static_cast((value >> 16U) & 0xffU)); bytes_->push_back(static_cast((value >> 24U) & 0xffU)); return Status::success(); } Status ByteWriter::write_bytes(std::span bytes) { if (bytes_ == nullptr) { return Status::invalid_argument("writer has no backing storage"); } if (overlaps_backing_storage(*bytes_, bytes)) { const std::vector copy(bytes.begin(), bytes.end()); bytes_->insert(bytes_->end(), copy.begin(), copy.end()); return Status::success(); } bytes_->insert(bytes_->end(), bytes.begin(), bytes.end()); return Status::success(); } }