Moved write to header, added read, seperated test and started using flint

This commit is contained in:
Dreaded_X 2019-12-13 03:11:43 +01:00
parent 62343d06e0
commit 35cd2873cd
8 changed files with 250 additions and 104 deletions

10
flint.yaml Normal file
View File

@ -0,0 +1,10 @@
io:
type: lib
path: .
test:
type: exe
path: test
dependency:
- io

4
include/io.h Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include "io/write.h"
#include "io/read.h"

20
include/io/helper.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include <string_view>
namespace io {
template <typename T>
concept NumberByte = std::is_integral_v<T> && sizeof(T) == 1;
template <typename T>
concept Container = requires (T a) {
typename T::value_type;
typename T::size_type;
typename T::iterator;
typename T::const_iterator;
a.size();
a.begin();
a.end();
a.cbegin();
a.cend();
} && !std::is_convertible_v<T, std::string_view>;
}

82
include/io/read.h Normal file
View File

@ -0,0 +1,82 @@
#pragma once
#include "helper.h"
#include <iostream>
#include <vector>
namespace io {
// This exists to allow for specialization and clearer errors
template <typename T>
T read(std::istream&) {
static_assert(!std::is_same_v<T,T>, "No known specialization of io::read for type");
}
template <NumberByte T>
T read(std::istream& is) {
T value = 0;
is.read(reinterpret_cast<char*>(&value), sizeof(T));
return value;
}
namespace {
template <typename T>
T read_bytes(std::istream& is, size_t length) {
T value = 0;
for (size_t i = 0; i < length; ++i) {
value <<= 8;
value |= read<uint8_t>(is);
}
return value;
}
}
template <typename T> requires std::is_integral_v<T>
T read(std::istream& is) {
T value = read_bytes<T>(is, sizeof(T));
return value;
}
template<>
size_t read(std::istream& is) {
size_t value = 0;
uint8_t length = io::read<uint8_t>(is);
if (length & 0b10000000) {
value = read_bytes<size_t>(is, length & 0b01111111);
} else {
value = length;
}
return value;
}
template <typename T> requires std::is_convertible_v<std::string, T>
T read(std::istream& is, size_t length) {
std::vector<char> t(length);
is.read(t.data(), length);
return std::string(t.data(), length);
}
// @todo Make this require that read<T>(is, length) exist so we can use that as a generic entrypoint
template <typename T> requires std::is_convertible_v<std::string, T>
T read(std::istream& is) {
size_t length = io::read<size_t>(is);
return read<T>(is, length);
}
// @todo We need to support std::array
// Extra concept that checks if the size is static
template <Container T>
T read(std::istream& is, size_t length) {
// @todo Properly constuct the container
T v(length);
for (size_t i = 0; i < length; ++i) {
v[i] = io::read<typename T::value_type>(is);
}
return v;
}
template <Container T>
T read(std::istream& is) {
size_t length = io::read<size_t>(is);
return read<T>(is, length);
}
}

71
include/io/write.h Normal file
View File

@ -0,0 +1,71 @@
#pragma once
#include "helper.h"
#include <iostream>
#include <cstdint>
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;
}
template <Container T>
void write(std::ostream& os, 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);
}
}
}

View File

@ -1,105 +1,3 @@
#include <iostream> #include "io/write.h"
#include <cstdint>
namespace io { // We do not implement anything without templates
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 <typename T>
concept NumberByte = std::is_integral_v<T> && sizeof(T) == 1;
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 (auto 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) {
std::string_view s = value;
write<size_t>(os, s.length());
os << s;
}
template <typename T>
concept Container = requires (T a) {
typename T::value_type;
typename T::size_type;
typename T::iterator;
typename T::const_iterator;
a.size();
a.begin();
a.end();
a.cbegin();
a.cend();
} && !std::is_convertible_v<T, std::string_view>;
template <Container T>
void write(std::ostream& os, T value) {
write<size_t>(os, value.size());
for (const auto& entry : value) {
write<typename T::value_type>(os, entry);
}
}
}
#include <vector>
#include <array>
struct A {
int b;
};
namespace io {
template <>
void write(std::ostream& os, A value) {
write(os, value.b);
}
}
int main() {
std::cout << "TEST\n";
io::write<size_t>(std::cout, 50);
std::array<char, 2> a = {'a', 'b'};
io::write(std::cout, a);
io::write(std::cout, "test");
io::write(std::cout, A{4});
std::cout << '\n';
}

BIN
test

Binary file not shown.

61
test/src/test.cpp Normal file
View File

@ -0,0 +1,61 @@
#include <io.h>
#include <vector>
#include <array>
#include <fstream>
struct A {
int b;
};
namespace io {
template <>
void write(std::ostream& os, A value) {
write<int>(os, value.b);
}
template <>
A read(std::istream& is) {
return A{read<int>(is)};
}
}
int main() {
{
std::ofstream f("test.bin");
io::write(f, 80085);
io::write<size_t>(f, 123456);
io::write(f, "test");
std::array<char, 2> a = {'a', 'b'};
io::write(f, a);
std::array<std::string, 2> b = {"Hello", "World"};
io::write(f, b);
io::write(f, A{4});
}
{
std::ifstream f("test.bin");
std::cout << io::read<int>(f) << '\n';
std::cout << io::read<size_t>(f) << '\n';
std::cout << io::read<std::string>(f) << '\n';
// auto v = io::read<std::array<char, 2>>(f);
auto a = io::read<std::vector<char>>(f);
for (auto& v : a) {
std::cout << v << ' ';
}
std::cout << '\n';
auto b = io::read<std::vector<std::string>>(f);
for (auto& v : b) {
std::cout << v << ' ';
}
std::cout << '\n';
std::cout << io::read<A>(f).b << '\n';
}
}