I'm trying to make a simple library for de/serialization in c++, but I know it can be tricky to implement, so I'd really like to have my code reviewed to see if there's anything that stands out and/or can be improved. [Here](https://gitlab.com/Nitr4m12/simple-binaryio) is the full repo containing all the code.
**binaryio/reader.h**:
```
#include <memory>
#include <span>
#include <stdexcept>
#include "binaryio/interface.h"
#include "binaryio/swap.h"
#ifndef BINARYIO_READER_H
#define BINARYIO_READER_H
namespace binaryio {
class BinaryReader : IBinaryIO {
public:
BinaryReader(void* const begin, void* const end)
: m_begin(reinterpret_cast<char*>(begin)),
m_end(reinterpret_cast<char*>(end)),
m_current(reinterpret_cast<char*>(begin)){};
BinaryReader(void* const begin, void* const end, const endian& endianness)
: m_begin{reinterpret_cast<char*>(begin)},
m_end{reinterpret_cast<char*>(end)},
m_current{reinterpret_cast<char*>(begin)}, m_endian{endianness} {};
BinaryReader(void* const begin, const std::ptrdiff_t& size)
: m_begin{reinterpret_cast<char*>(begin)}, m_end{m_begin + size},
m_current{reinterpret_cast<char*>(begin)} {};
BinaryReader(void* const begin, const std::ptrdiff_t& size,
const endian& endianness)
: m_begin{reinterpret_cast<char*>(begin)}, m_end{m_begin + size},
m_current{reinterpret_cast<char*>(begin)}, m_endian{endianness} {};
void seek(std::ptrdiff_t offset) override {
if (m_begin + offset > m_end)
throw std::out_of_range("out of bounds seek");
m_current = m_begin + offset;
}
size_t tell() const override {
size_t offset{static_cast<size_t>(m_current - m_begin)};
return offset;
}
template <typename T>
T read() {
if (m_current + sizeof(T) > m_end)
throw std::out_of_range("out of bounds read");
T val = *(T*)m_current;
swap_if_needed_in_place(val, m_endian);
m_current += sizeof(T);
return val;
}
std::string read_string(size_t max_len = 0) {
if (m_current + max_len > m_end || max_len == 0)
max_len = m_end - m_current;
return {m_current, strnlen(m_current, max_len)};
}
template <typename T>
std::span<T> read_many(int count) {
if (m_current + sizeof(T) * count > m_end)
throw std::out_of_range("out of bound read");
std::span<T> vals{{}, count};
for (int i{0}; i < count; ++i) {
vals[i] = *(T*)m_current;
swap_if_needed_in_place(vals[i], m_endian);
m_current += sizeof(T);
}
return vals;
}
endian endianness() { return m_endian; }
void set_endianness(endian new_endian) { m_endian = new_endian; }
void swap_endianness() {
if (m_endian == endian::big)
m_endian = endian::little;
else
m_endian = endian::big;
}
private:
char* m_begin;
char* m_end;
char* m_current;
endian m_endian{endian::native};
};
} // namespace binaryio
#endif
```
**binaryio/writer.h**:
```
#include <memory>
#include <span>
#include <vector>
#include "binaryio/align.h"
#include "binaryio/interface.h"
#include "binaryio/swap.h"
#ifndef BINARYIO_WRITER_H
#define BINARYIO_WRITER_H
namespace binaryio {
class BinaryWriter : IBinaryIO {
public:
// Based on
// https://github.com/zeldamods/oead/blob/master/src/include/oead/util/binary_reader.h
BinaryWriter() = default;
BinaryWriter(endian byte_order) : m_endian{byte_order} {};
std::vector<uint8_t> finalize() { return std::move(m_storage); }
void seek(std::ptrdiff_t offset) override { m_offset = offset; };
size_t tell() const override { return m_offset; }
void write_bytes(const uint8_t* data, size_t size) {
std::span<const uint8_t> bytes{data, size};
if (m_offset + bytes.size() > m_storage.size())
m_storage.resize(m_offset + bytes.size());
std::memcpy(&m_storage[m_offset], bytes.data(), bytes.size());
m_offset += bytes.size();
};
template <typename T,
typename std::enable_if_t<!std::is_pointer_v<T> &&
std::is_trivially_copyable_v<T>>* = nullptr>
void write(T value) {
swap_if_needed_in_place(value, m_endian);
write_bytes(reinterpret_cast<const uint8_t*>(&value), sizeof(value));
}
void write(std::string_view str) {
write_bytes(reinterpret_cast<const uint8_t*>(str.data()), str.size());
}
void write_null() { write<uint8_t>(0); }
void write_cstr(std::string_view str) {
write(str);
write_null();
}
void align_up(size_t n) { seek(AlignUp(tell(), n)); }
private:
std::vector<uint8_t> m_storage;
size_t m_offset{0};
endian m_endian{endian::native};
};
} // namespace binaryio
#endif
```