io/include/io/write.h

95 lines
2.3 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));
}
template <>
void write(std::ostream& os, float value) {
union {
uint32_t a;
float b;
} u;
u.b = value;
write_bytes(os, u.a, sizeof(uint32_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);
}
}
}