1#ifndef INTNS_IO_BINARY_READER_HPP
2#define INTNS_IO_BINARY_READER_HPP
39template <Endianness E = Endianness::kLittle>
50 : data_(buffer.data()), size_(buffer.
size()), position_(
position) {
52 throw std::out_of_range(
"Initial position " + std::to_string(
position) +
53 " exceeds buffer size " + std::to_string(size_));
65 : data_(buffer.data()), size_(buffer.
size()), position_(
position) {
67 throw std::out_of_range(
"Initial position " + std::to_string(
position) +
68 " exceeds buffer size " + std::to_string(size_));
77 [[nodiscard]]
size_t size() const noexcept {
return size_; }
84 [[nodiscard]]
size_t position() const noexcept {
return position_; }
91 [[nodiscard]]
size_t remaining() const noexcept {
return size_ - position_; }
103 if (pos > size_) pos = size_;
116 void skip(
size_t bytes)
noexcept {
117 position_ = std::min(position_ + bytes, size_);
127 if (position_ >= size_) {
128 throw std::out_of_range(
"Cannot read uint8_t: position " +
129 std::to_string(position_) +
" >= size " +
130 std::to_string(size_));
132 return data_[position_++];
142 if (position_ + 2 > size_) {
143 throw std::out_of_range(
"Cannot read uint16_t: need 2 bytes, only " +
144 std::to_string(
remaining()) +
" available");
147 std::memcpy(&value, data_ + position_, 2);
149 if constexpr (E != native_endian()) value = bswap_16(value);
160 if (position_ + 4 > size_) {
161 throw std::out_of_range(
"Cannot read uint32_t: need 4 bytes, only " +
162 std::to_string(
remaining()) +
" available");
165 std::memcpy(&value, data_ + position_, 4);
167 if constexpr (E != native_endian()) value = bswap_32(value);
178 if (position_ + 8 > size_) {
179 throw std::out_of_range(
"Cannot read uint64_t: need 8 bytes, only " +
180 std::to_string(
remaining()) +
" available");
183 std::memcpy(&value, data_ + position_, 8);
185 if constexpr (E != native_endian()) value = bswap_64(value);
247 throw std::invalid_argument(
"Destination pointer cannot be null");
249 if (position_ + bytes > size_) {
250 throw std::out_of_range(
"Cannot read " + std::to_string(bytes) +
251 " bytes: only " + std::to_string(
remaining()) +
254 std::memcpy(dest, data_ + position_, bytes);
269 throw std::invalid_argument(
"Array pointer cannot be null");
272 if constexpr (E != native_endian()) {
273 for (
size_t i = 0; i < count; ++i) {
274 array[i] = bswap_16(array[i]);
290 throw std::invalid_argument(
"Array pointer cannot be null");
293 if constexpr (E != native_endian()) {
294 for (
size_t i = 0; i < count; ++i) {
295 array[i] = bswap_32(array[i]);
308 if (position_ + length > size_) {
309 throw std::out_of_range(
"Cannot read string of length " +
310 std::to_string(length) +
": only " +
311 std::to_string(
remaining()) +
" bytes available");
313 std::string result(
reinterpret_cast<const char*
>(data_ + position_),
329 const uint8_t* start = data_ + position_;
331 static_cast<const uint8_t*
>(std::memchr(start, 0,
remaining()));
338 size_t length = end - start;
339 std::string result(
reinterpret_cast<const char*
>(start), length);
340 position_ += length + (end < data_ + size_ ? 1 : 0);
351 if (position_ >= size_) {
352 throw std::out_of_range(
"Cannot peek uint8_t: position " +
353 std::to_string(position_) +
" >= size " +
354 std::to_string(size_));
356 return data_[position_];
366 if (position_ + 2 > size_) {
367 throw std::out_of_range(
"Cannot peek uint16_t: need 2 bytes, only " +
368 std::to_string(size_ - position_) +
" available");
371 std::memcpy(&value, data_ + position_, 2);
372 if constexpr (E != native_endian()) value = bswap_16(value);
382 static constexpr Endianness native_endian() noexcept {
383 return std::endian::native == std::endian::little ? Endianness::kLittle
387 const uint8_t* data_;
414template <Endianness E = Endianness::kLittle>
425 explicit FileReader(
const std::string& filename,
size_t buffer_size = 8192)
426 : buffer_(buffer_size) {
427 if (buffer_size == 0) {
428 throw std::invalid_argument(
"Buffer size cannot be zero");
431 file_.open(filename, std::ios::binary);
433 throw std::runtime_error(
"Failed to open file: " + filename);
436 file_.seekg(0, std::ios::end);
437 file_size_ = file_.tellg();
448 [[nodiscard]]
size_t size() const noexcept {
return file_size_; }
456 return file_pos_ - buffer_remaining();
476 if (pos > file_size_) pos = file_size_;
479 throw std::runtime_error(
"Failed to seek to position " +
480 std::to_string(pos));
502 return buffer_[buffer_pos_++];
514 std::memcpy(&value, &buffer_[buffer_pos_], 2);
516 if constexpr (E != native_endian()) value = bswap_16(value);
529 std::memcpy(&value, &buffer_[buffer_pos_], 4);
531 if constexpr (E != native_endian()) value = bswap_32(value);
544 std::memcpy(&value, &buffer_[buffer_pos_], 8);
546 if constexpr (E != native_endian()) value = bswap_64(value);
608 throw std::invalid_argument(
"Destination pointer cannot be null");
611 uint8_t* out =
static_cast<uint8_t*
>(dest);
614 size_t chunk = std::min(bytes, buffer_remaining());
615 std::memcpy(out, &buffer_[buffer_pos_], chunk);
616 buffer_pos_ += chunk;
630 std::string result(length,
'\0');
645 while ((ch =
read_u8()) != 0) {
646 result.push_back(
static_cast<char>(ch));
657 [[nodiscard]]
size_t buffer_remaining() const noexcept {
658 return buffer_end_ - buffer_pos_;
667 file_.read(
reinterpret_cast<char*
>(buffer_.data()), buffer_.size());
668 buffer_end_ = file_.gcount();
670 file_pos_ = file_.tellg();
673 throw std::runtime_error(
"Failed to read from file");
684 void ensure_available(
size_t bytes) {
685 if (buffer_remaining() < bytes) {
687 if (buffer_remaining() > 0) {
688 std::memmove(buffer_.data(), &buffer_[buffer_pos_], buffer_remaining());
690 buffer_end_ = buffer_remaining();
694 file_.read(
reinterpret_cast<char*
>(&buffer_[buffer_end_]),
695 buffer_.size() - buffer_end_);
696 buffer_end_ += file_.gcount();
697 file_pos_ = file_.tellg();
700 throw std::runtime_error(
"Failed to read from file");
704 if (buffer_remaining() < bytes) {
705 throw std::out_of_range(
"Cannot read " + std::to_string(bytes) +
706 " bytes: reached end of file");
715 static constexpr Endianness native_endian() noexcept {
716 return std::endian::native == std::endian::little ? Endianness::kLittle
721 std::vector<uint8_t> buffer_;
722 size_t file_size_ = 0;
723 size_t file_pos_ = 0;
724 size_t buffer_pos_ = 0;
725 size_t buffer_end_ = 0;
731using LEMemoryReader = MemoryReader<Endianness::kLittle>;
736using BEMemoryReader = MemoryReader<Endianness::kBig>;
741using LEFileReader = FileReader<Endianness::kLittle>;
746using BEFileReader = FileReader<Endianness::kBig>;
A file-based binary reader with buffering and configurable endianness.
Definition BinaryReader.hpp:415
int8_t read_s8()
Reads a signed 8-bit integer.
Definition BinaryReader.hpp:556
int16_t read_s16()
Reads a signed 16-bit integer with endianness conversion.
Definition BinaryReader.hpp:564
FileReader(const std::string &filename, size_t buffer_size=8192)
Constructs a FileReader and opens the specified file.
Definition BinaryReader.hpp:425
uint32_t read_u32()
Reads an unsigned 32-bit integer with endianness conversion.
Definition BinaryReader.hpp:526
std::string read_string(size_t length)
Reads a fixed-length string from the file.
Definition BinaryReader.hpp:629
std::string read_cstring()
Reads a null-terminated C-style string from the file.
Definition BinaryReader.hpp:642
double read_f64()
Reads a 64-bit floating-point value with endianness conversion.
Definition BinaryReader.hpp:596
void skip(size_t bytes)
Advances the read position by the specified number of bytes.
Definition BinaryReader.hpp:492
float read_f32()
Reads a 32-bit floating-point value with endianness conversion.
Definition BinaryReader.hpp:588
int32_t read_s32()
Reads a signed 32-bit integer with endianness conversion.
Definition BinaryReader.hpp:572
void set_position(size_t pos)
Sets the read position within the file.
Definition BinaryReader.hpp:475
uint64_t read_u64()
Reads an unsigned 64-bit integer with endianness conversion.
Definition BinaryReader.hpp:541
uint16_t read_u16()
Reads an unsigned 16-bit integer with endianness conversion.
Definition BinaryReader.hpp:511
size_t size() const noexcept
Returns the total size of the file.
Definition BinaryReader.hpp:448
void read_bytes(void *dest, size_t bytes)
Reads raw bytes into a destination buffer.
Definition BinaryReader.hpp:606
size_t remaining() const noexcept
Returns the number of bytes remaining to be read.
Definition BinaryReader.hpp:464
size_t position() const noexcept
Returns the current read position in the file.
Definition BinaryReader.hpp:455
uint8_t read_u8()
Reads an unsigned 8-bit integer.
Definition BinaryReader.hpp:500
int64_t read_s64()
Reads a signed 64-bit integer with endianness conversion.
Definition BinaryReader.hpp:580
A memory-based binary reader with configurable endianness.
Definition BinaryReader.hpp:40
double read_f64()
Reads a 64-bit floating-point value with endianness conversion.
Definition BinaryReader.hpp:235
uint16_t peek_u16() const
Peeks at an unsigned 16-bit integer without advancing position.
Definition BinaryReader.hpp:365
MemoryReader(std::span< const uint8_t > buffer, size_t position=0)
Constructs a MemoryReader from a span buffer.
Definition BinaryReader.hpp:64
int16_t read_s16()
Reads a signed 16-bit integer with endianness conversion.
Definition BinaryReader.hpp:203
MemoryReader(const std::vector< uint8_t > &buffer, size_t position=0)
Constructs a MemoryReader from a vector buffer.
Definition BinaryReader.hpp:49
void read_u32_array(uint32_t *array, size_t count)
Reads an array of unsigned 32-bit integers with endianness conversion.
Definition BinaryReader.hpp:288
uint64_t read_u64()
Reads an unsigned 64-bit integer with endianness conversion.
Definition BinaryReader.hpp:177
void read_u16_array(uint16_t *array, size_t count)
Reads an array of unsigned 16-bit integers with endianness conversion.
Definition BinaryReader.hpp:267
void skip(size_t bytes) noexcept
Advances the read position by the specified number of bytes.
Definition BinaryReader.hpp:116
uint32_t read_u32()
Reads an unsigned 32-bit integer with endianness conversion.
Definition BinaryReader.hpp:159
float read_f32()
Reads a 32-bit floating-point value with endianness conversion.
Definition BinaryReader.hpp:227
int64_t read_s64()
Reads a signed 64-bit integer with endianness conversion.
Definition BinaryReader.hpp:219
size_t position() const noexcept
Returns the current read position.
Definition BinaryReader.hpp:84
size_t size() const noexcept
Returns the total size of the buffer.
Definition BinaryReader.hpp:77
int32_t read_s32()
Reads a signed 32-bit integer with endianness conversion.
Definition BinaryReader.hpp:211
int8_t read_s8()
Reads a signed 8-bit integer.
Definition BinaryReader.hpp:195
uint16_t read_u16()
Reads an unsigned 16-bit integer with endianness conversion.
Definition BinaryReader.hpp:141
void read_bytes(void *dest, size_t bytes)
Reads raw bytes into a destination buffer.
Definition BinaryReader.hpp:245
uint8_t peek_u8() const
Peeks at an unsigned 8-bit integer without advancing position.
Definition BinaryReader.hpp:350
std::string read_cstring()
Reads a null-terminated C-style string from the buffer.
Definition BinaryReader.hpp:328
size_t remaining() const noexcept
Returns the number of bytes remaining to be read.
Definition BinaryReader.hpp:91
std::string read_string(size_t length)
Reads a fixed-length string from the buffer.
Definition BinaryReader.hpp:307
void set_position(size_t pos) noexcept
Sets the read position within the buffer.
Definition BinaryReader.hpp:102
uint8_t read_u8()
Reads an unsigned 8-bit integer.
Definition BinaryReader.hpp:126