Moved write to header, added read, seperated test and started using flint
This commit is contained in:
parent
62343d06e0
commit
35cd2873cd
10
flint.yaml
Normal file
10
flint.yaml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
io:
|
||||||
|
type: lib
|
||||||
|
path: .
|
||||||
|
|
||||||
|
test:
|
||||||
|
type: exe
|
||||||
|
path: test
|
||||||
|
dependency:
|
||||||
|
- io
|
||||||
|
|
4
include/io.h
Normal file
4
include/io.h
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "io/write.h"
|
||||||
|
#include "io/read.h"
|
20
include/io/helper.h
Normal file
20
include/io/helper.h
Normal 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
82
include/io/read.h
Normal 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
71
include/io/write.h
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
106
src/write.cpp
106
src/write.cpp
|
@ -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';
|
|
||||||
}
|
|
||||||
|
|
61
test/src/test.cpp
Normal file
61
test/src/test.cpp
Normal 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';
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user