57 virtual void Seek(std::size_t offset) = 0;
62 virtual std::size_t
Tell() = 0;
67 virtual std::size_t
Len() = 0;
91 template <
typename T,
typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
106 template <
typename T>
108 static_assert(std::is_trivially_copyable_v<T>);
109 std::vector<T> result;
111 result.resize(num_elements);
113 ReadData(result.data(), num_elements *
sizeof(T));
118 virtual ~Reader()
override =
default;
126 virtual void ReadData(
void* dst, std::size_t size) = 0;
156 template <
typename T>
159 auto Get()
const {
return Ref<T>(*
this); }
161 template <
typename M,
typename A = T, std::enable_if_t<std::is_
class_v<A>,
int> = 0>
162 auto Get(M A::*member)
const {
163 return this->Get().Get(member);
183 template <
typename M,
typename A = T, std::enable_if_t<std::is_
class_v<A>,
int> = 0>
185 return this->Get(member);
198 auto operator[](std::size_t index)
const {
return *((*this) + index); }
205 template <
typename Int>
206 explicit operator Int() const noexcept {
207 static_assert(std::is_integral_v<Int>);
208 return static_cast<Int
>(offset_);
211 bool operator==(
const Offset& rhs)
const =
delete;
213 bool operator!=(
const Offset& rhs)
const =
delete;
216 offset_ +=
sizeof(T);
220 Offset operator++(
int) {
226 Offset& operator+=(std::size_t value) {
227 offset_ +=
sizeof(T) * value;
231 Offset operator+(std::size_t value)
const {
232 Offset result = *
this;
237 Offset& operator--() {
238 offset_ -=
sizeof(T);
242 Offset operator--(
int) {
248 Offset& operator-=(std::size_t value) {
return *
this -= value; }
250 Offset operator-(std::size_t value)
const {
251 Offset result = *
this;
256 friend Offset operator+(std::size_t value,
const Offset& offset) {
257 return Offset{offset.writer_, offset.offset_ + value};
260 friend Offset operator-(std::size_t value,
const Offset& offset) {
261 return Offset{offset.writer_, offset.offset_ - value};
265 Offset(Writer& writer, std::size_t offset) : writer_(writer), offset_(offset) {}
283 template <
typename T>
286 template <
typename A = T>
287 std::enable_if_t<!std::is_array_v<A>, T> Get()
const {
288 auto prev_offset = off_.writer_.Tell();
289 off_.writer_.Seek(off_.offset_);
290 auto value =
dynamic_cast<Reader&
>(off_.writer_).ReadLE<T>();
291 off_.writer_.
Seek(prev_offset);
295 void Set(
const T& value) {
296 auto prev_offset = off_.writer_.Tell();
297 off_.writer_.Seek(off_.offset_);
298 off_.writer_.WriteData(&value,
sizeof(T));
299 off_.writer_.Seek(prev_offset);
302 template <
typename M,
typename A = T, std::enable_if_t<std::is_
class_v<A>,
int> = 0>
303 Ref<M> Get(M A::*member)
const {
304 std::size_t member_offset =
reinterpret_cast<std::size_t
>(&(((T*)
nullptr)->*member));
305 return Ref<M>({off_.writer_, off_.offset_ + member_offset});
308 template <
typename A = T,
typename M = std::remove_all_extents_t<A>,
309 std::enable_if_t<std::is_array_v<A> && std::rank_v<A> == 1,
int> = 0>
310 Ref<M> Get(std::size_t index)
const {
311 return Ref<M>({off_.writer_, off_.offset_ +
sizeof(M) * index});
314 template <
typename A = T,
typename M = std::remove_extent_t<A>,
315 std::enable_if_t<std::is_array_v<A> && std::rank_v<A> != 1,
int> = 0>
316 Ref<M> Get(std::size_t index)
const {
317 return Ref<M>({off_.writer_, off_.offset_ +
sizeof(M) * index});
326 template <
typename M,
typename A = T, std::enable_if_t<std::is_
class_v<A>,
int> = 0>
336 template <
typename A = T,
typename M = std::remove_all_extents_t<A>,
337 std::enable_if_t<std::is_array_v<A> && std::rank_v<A> == 1,
int> = 0>
348 template <
typename A = T,
typename M = std::remove_extent_t<A>,
349 std::enable_if_t<std::is_array_v<A> && std::rank_v<A> != 1,
int> = 0>
358 template <
typename A = T, std::enable_if_t<!std::is_array_v<A>,
int> = 0>
360 static_assert(std::is_convertible_v<T, A>);
371 bool operator==(
const Ref& other)
const {
return this->Get() == other.Get(); }
373 bool operator!=(
const Ref& other)
const {
return !(*
this == other); }
375 bool operator==(
const T& other)
const {
return this->Get() == other; }
377 bool operator!=(
const T& other)
const {
return !(*
this == other); }
380 Ref(Offset<T> offset) : off_(offset) {}
392 template <
typename T,
typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
414 template <
typename T, std::enable_if_t<std::is_trivially_copyable_v<T>,
int> = 0>
430 template <
typename T>
432 static_assert(std::is_trivially_copyable_v<T>);
434 auto prev_position =
Tell();
436 WriteData(container.data(), container.size() *
sizeof(T));
446 void AlignData(std::size_t multiple,
unsigned char value = 0) {
447 auto cur_pos =
Tell();
449 for (std::size_t i = 0; i < num_bytes; i++)
WriteLE(value);
452 virtual ~Writer()
override =
default;
460 virtual void WriteData(
const void* src, std::size_t size) = 0;
475 FileReader(
const std::filesystem::path& path) : path_(path) {
476 file_stream_.open(path, std::ios::ate | std::ios::in | std::ios::binary);
478 if (!file_stream_.is_open()) {
479 NNL_THROW(nnl::IOError(NNL_SRCTAG(std::strerror(errno)), utl::filesys::u8string(path)));
482 size_ = file_stream_.tellg();
483 file_stream_.seekg(0);
485 void Seek(std::size_t offset)
override { file_stream_.seekg(offset, std::ios::beg); }
487 std::size_t
Tell()
override {
return file_stream_.tellg(); }
489 std::size_t
Len()
override {
return size_; }
498 file_stream_.close();
503 void ReadData(
void* dst, std::size_t size)
override {
504 file_stream_.read(
static_cast<char*
>(dst), size);
511 std::filesystem::path path_;
512 std::ifstream file_stream_;
513 std::size_t size_ = 0;
530 FileRW(
const std::filesystem::path& path,
bool truncate) : path_(path) {
531 auto open_mode = std::ios::binary | std::ios::out | std::ios::in;
534 open_mode |= std::ios::trunc;
537 file_stream_.open(path, open_mode);
539 if (!file_stream_.is_open()) {
540 NNL_THROW(nnl::IOError(NNL_SRCTAG(std::strerror(errno)), utl::filesys::u8string(path)));
543 void Seek(std::size_t offset)
override { file_stream_.seekp(offset, std::ios::beg); }
545 std::size_t
Tell()
override {
return file_stream_.tellp(); }
547 std::size_t
Len()
override {
548 auto cur_pos = file_stream_.tellp();
549 file_stream_.seekp(0, std::ios::end);
550 std::size_t size = file_stream_.tellp();
551 file_stream_.seekp(cur_pos);
561 void Close() { file_stream_.close(); }
564 void WriteData(
const void* src, std::size_t size)
override {
565 file_stream_.write(
static_cast<const char*
>(src), size);
571 void ReadData(
void* dst, std::size_t size)
override {
572 file_stream_.read(
static_cast<char*
>(dst), size);
579 std::filesystem::path path_;
580 std::fstream file_stream_;
600 using value_type =
u8;
601 using iterator =
const value_type*;
602 using const_iterator =
const value_type*;
603 using reverse_iterator = std::reverse_iterator<iterator>;
604 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
606 BufferView()
noexcept {}
614 : buffer_ptr_(
reinterpret_cast<const u8*
>(buffer)), buffer_size_(size) {
623 template <
typename TContainer,
624 typename = std::enable_if_t<std::is_trivially_copyable_v<typename TContainer::value_type>>>
626 : buffer_ptr_(
reinterpret_cast<const u8*
>(buffer.data())),
627 buffer_size_(buffer.size() *
sizeof(
typename TContainer::value_type)) {}
629 void Seek(std::size_t offset)
override { position_ = offset; }
631 std::size_t
Tell()
override {
return position_; }
633 std::size_t
Len()
override {
return buffer_size_; }
641 [[nodiscard]] BufferView
SubView(std::size_t offset, std::size_t size)
const {
643 return BufferView(buffer_ptr_ + offset, size);
647 std::vector<u8> copied_buf(buffer_size_);
649 if (buffer_ptr_ !=
nullptr && buffer_size_ != 0) std::memcpy(copied_buf.data(), buffer_ptr_, buffer_size_);
654 const u8& operator[](std::size_t index)
const {
656 return buffer_ptr_[index];
659 friend bool operator==(
const BufferView& lhs,
const BufferView& rhs) {
660 return (lhs.buffer_size_ == 0 && rhs.buffer_size_ == 0) ||
661 (lhs.buffer_size_ == rhs.buffer_size_ &&
662 std::memcmp(lhs.buffer_ptr_, rhs.buffer_ptr_, lhs.buffer_size_) == 0);
665 friend bool operator!=(
const BufferView& lhs,
const BufferView& rhs) {
return !(lhs == rhs); }
667 std::size_t size() const noexcept {
return buffer_size_; }
669 [[nodiscard]] std::size_t empty() const noexcept {
return buffer_size_ == 0; }
675 const u8*
data() const noexcept {
return buffer_ptr_; }
677 const u8& at(std::size_t index)
const {
679 return buffer_ptr_[index];
682 const u8& front()
const {
687 const u8& back()
const {
692 const_iterator begin() const noexcept {
return buffer_ptr_; }
694 const_iterator end() const noexcept {
return buffer_ptr_ + buffer_size_; }
696 const_reverse_iterator rbegin() const noexcept {
return std::make_reverse_iterator(end()); }
698 const_reverse_iterator rend() const noexcept {
return std::make_reverse_iterator(begin()); }
701 void ReadData(
void* dst, std::size_t size)
override {
702 if (position_ + size > this->buffer_size_) {
705 std::memcpy(dst, buffer_ptr_ + position_, size);
710 const u8* buffer_ptr_ =
nullptr;
711 std::size_t buffer_size_ = 0;
712 std::size_t position_ = 0;
727 using value_type =
u8;
728 using iterator = value_type*;
729 using reverse_iterator = std::reverse_iterator<iterator>;
731 BufferSpan()
noexcept {}
737 BufferSpan(
void* buffer, std::size_t size) noexcept : buffer_ptr_(
reinterpret_cast<u8*
>(buffer)), buffer_size_(size) {
746 template <
typename TContainer,
747 typename = std::enable_if_t<std::is_trivially_copyable_v<typename TContainer::value_type>>>
749 : buffer_ptr_(
reinterpret_cast<u8*
>(buffer.data())),
750 buffer_size_(buffer.size() *
sizeof(
typename TContainer::value_type)) {}
752 void Seek(std::size_t offset)
override { position_ = offset; }
754 std::size_t
Tell()
override {
return position_; }
756 std::size_t
Len()
override {
return buffer_size_; }
763 if (buffer_ptr_ !=
nullptr) std::memset(buffer_ptr_, val, buffer_size_);
775 return BufferView(buffer_ptr_ + offset, size);
785 [[nodiscard]] BufferSpan
SubSpan(std::size_t offset, std::size_t size) {
788 return BufferSpan(buffer_ptr_ + offset, size);
792 std::vector<u8> copy(buffer_size_);
793 if (buffer_ptr_ !=
nullptr && buffer_size_ != 0) std::memcpy(copy.data(), buffer_ptr_, buffer_size_);
802 u8& operator[](std::size_t index) {
804 return buffer_ptr_[index];
807 friend bool operator==(
const BufferSpan& lhs,
const BufferSpan& rhs) {
808 return (lhs.buffer_size_ == 0 && rhs.buffer_size_ == 0) ||
809 (lhs.buffer_size_ == rhs.buffer_size_ &&
810 std::memcmp(lhs.buffer_ptr_, rhs.buffer_ptr_, lhs.buffer_size_) == 0);
813 friend bool operator!=(
const BufferSpan& lhs,
const BufferSpan& rhs) {
return !(lhs == rhs); }
815 std::size_t size() const noexcept {
return buffer_size_; }
817 [[nodiscard]] std::size_t empty() const noexcept {
return buffer_size_ == 0; }
822 u8*
data() noexcept {
return buffer_ptr_; }
824 u8& at(std::size_t index) {
826 return buffer_ptr_[index];
839 iterator begin() noexcept {
return buffer_ptr_; }
841 iterator end() noexcept {
return buffer_ptr_ + buffer_size_; }
843 reverse_iterator rbegin() noexcept {
return std::make_reverse_iterator(end()); }
845 reverse_iterator rend() noexcept {
return std::make_reverse_iterator(begin()); }
848 void ReadData(
void* dst, std::size_t size)
override {
849 if (position_ + size > this->buffer_size_) {
852 std::memcpy(dst, buffer_ptr_ + position_, size);
856 void WriteData(
const void* src, std::size_t size)
override {
857 if (position_ + size > this->buffer_size_) {
860 std::memcpy(buffer_ptr_ + position_, src, size);
865 u8* buffer_ptr_ =
nullptr;
866 std::size_t buffer_size_ = 0;
867 std::size_t position_ = 0;
882 BufferRW(
const BufferRW& buf) : buffer_(buf.buffer_), position_(buf.position_) {}
884 BufferRW(BufferRW&& buf) noexcept : buffer_(std::move(buf.buffer_)), position_(buf.position_) { buf.position_ = 0; }
886 BufferRW(
const Buffer& buf) : buffer_(buf) {}
888 BufferRW(
Buffer&& buf) noexcept : buffer_(std::move(buf)) {}
890 BufferRW& operator=(
const BufferRW& buf) {
892 buffer_ = buf.buffer_;
893 position_ = buf.position_;
898 BufferRW& operator=(BufferRW&& buf)
noexcept {
900 buffer_ = std::move(buf.buffer_);
901 position_ = buf.position_;
907 void Seek(std::size_t offset)
override { position_ = offset; }
909 std::size_t
Tell()
override {
return position_; }
911 std::size_t
Len()
override {
return buffer_.size(); }
926 return std::move(buffer_);
929 operator Buffer()
const {
return buffer_; }
941 return std::move(buffer_);
945 void WriteData(
const void* src, std::size_t size)
override {
946 if (position_ + size > buffer_.size()) buffer_.resize(position_ + size);
948 std::memcpy(buffer_.data() + position_, src, size);
952 void ReadData(
void* dst, std::size_t size)
override {
953 if (position_ + size > this->buffer_.size()) {
956 std::memcpy(dst, buffer_.data() + position_, size);
962 std::size_t position_ = 0;
Pointer-like object for storing stream positions.
Definition io.hpp:157
auto operator[](std::size_t index) const
Gets a reference to the element at the index relative to the current offset.
Definition io.hpp:198
auto operator->*(M A::*member) const
Gets a reference to a member of T (->)
Definition io.hpp:184
auto operator*() const
Gets a reference to T.
Definition io.hpp:173
Reference-like object for writing and reading data at memorized stream positions.
Definition io.hpp:284
Ref< M > operator[](std::size_t index) const
Creates an Ref to an element of the referenced C array.
Definition io.hpp:338
Ref< M > operator->*(M A::*member) const
Creates an Ref to a member of the referenced struct.
Definition io.hpp:327
void operator=(const T &value)
Assignment operator for writing the entire object.
Definition io.hpp:369
Defines the library-wide exception hierarchy and error handling macros.
Provides utility functions for filesystem operations.
Contains macros and definitions for fixed-width types.
#define NNL_EXPECTS_DBG(precondition)
Debug-only precondition check.
Definition contract.hpp:59
#define NNL_EXPECTS(precondition)
A precondition check.
Definition contract.hpp:45
Exception thrown for I/O like operations.
Definition exception.hpp:132
#define NNL_THROW(object)
Throws an exception or terminates the program if exceptions are disabled.
Definition exception.hpp:46
std::string u8string(const std::filesystem::path &path)
Converts a filesystem path to a UTF-8 string.
std::uint8_t u8
8-bit unsigned integer
Definition fixed_type.hpp:64
T RoundNum(T number, std::size_t multiple)
Rounds number up to the nearest multiple.
Definition math.hpp:219
Provides various math utility functions.
Definition exception.hpp:56