84 lines
2.1 KiB
C++
84 lines
2.1 KiB
C++
#pragma once
|
|
#include "helper.h"
|
|
#include <iostream>
|
|
#include <cstdint>
|
|
#include <tuple>
|
|
|
|
namespace io {
|
|
// This exists to allow for specialization and clearer errors
|
|
template <typename T>
|
|
void write(std::ostream&, T) {
|
|
static_assert(!std::is_same_v<T,T>, "No known specialization of io::write for type");
|
|
}
|
|
|
|
template <NumberByte T>
|
|
void write(std::ostream& os, T value) {
|
|
os.write(reinterpret_cast<const char*>(&value), sizeof(T));
|
|
}
|
|
|
|
namespace {
|
|
template <typename T>
|
|
void write_bytes(std::ostream& os, T value, size_t length) {
|
|
for (size_t i = length; i > 0; --i) {
|
|
uint8_t byte = (value >> (i-1)*8) & 0xFF;
|
|
write<uint8_t>(os, byte);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename T> requires std::is_integral_v<T>
|
|
void write(std::ostream& os, T value) {
|
|
write_bytes(os, value, sizeof(T));
|
|
}
|
|
|
|
// Special implementation for size_t (also uint64_t, so maybe this is not that smart)
|
|
template <>
|
|
void write(std::ostream& os, size_t value) {
|
|
// Check if we need more then one byte
|
|
if (value > 0b01111111) {
|
|
// Calculate how many bytes we need to store the number
|
|
uint8_t length = 0;
|
|
auto x = value;
|
|
while (x != 0) {
|
|
x >>= 8;
|
|
length++;
|
|
}
|
|
|
|
write<uint8_t>(os, length | 0b10000000);
|
|
write_bytes(os, value, length);
|
|
} else {
|
|
write<uint8_t>(os, value);
|
|
}
|
|
}
|
|
|
|
template <typename T> requires std::is_convertible_v<T, std::string_view>
|
|
void write(std::ostream& os, T value, bool store_length = true) {
|
|
std::string_view s = value;
|
|
if (store_length) {
|
|
write<size_t>(os, s.length());
|
|
}
|
|
os << s;
|
|
}
|
|
|
|
// @todo Merge this into one function with the proper default automatically
|
|
template <Container T>
|
|
void write(std::ostream& os, const T& value, bool store_length = true) {
|
|
if (store_length) {
|
|
write<size_t>(os, value.size());
|
|
}
|
|
for (const auto& entry : value) {
|
|
write<typename T::value_type>(os, entry);
|
|
}
|
|
}
|
|
|
|
template <Container T> requires requires (T a) { std::tuple_size<T>::value; }
|
|
void write(std::ostream& os, const T& value, bool store_length = false) {
|
|
if (store_length) {
|
|
write<size_t>(os, value.size());
|
|
}
|
|
for (const auto& entry : value) {
|
|
write<typename T::value_type>(os, entry);
|
|
}
|
|
}
|
|
}
|