Add basic binary serialization library with tests
This commit is contained in:
parent
9a47155284
commit
d4f299f277
13 changed files with 1440 additions and 0 deletions
8
libs/sir/CMakeLists.txt
Normal file
8
libs/sir/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
file(GLOB_RECURSE PSEMEK_SIR_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/*.hpp")
|
||||
file(GLOB_RECURSE PSEMEK_SIR_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "source/*.cpp")
|
||||
|
||||
psemek_add_library(psemek-sir ${PSEMEK_SIR_HEADERS} ${PSEMEK_SIR_SOURCES})
|
||||
target_include_directories(psemek-sir PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(psemek-sir PUBLIC psemek-io)
|
||||
|
||||
psemek_glob_tests(psemek-sir tests)
|
||||
197
libs/sir/include/psemek/sir/container.hpp
Normal file
197
libs/sir/include/psemek/sir/container.hpp
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/sir/platform.hpp>
|
||||
#include <psemek/sir/stream.hpp>
|
||||
#include <psemek/sir/trivial.hpp>
|
||||
#include <psemek/sir/struct.hpp>
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace psemek::sir
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
using std::size;
|
||||
using std::data;
|
||||
|
||||
template <typename Container>
|
||||
constexpr bool is_container_helper = !is_struct_v<Container> && requires (std::remove_cv_t<Container> c)
|
||||
{
|
||||
begin(c);
|
||||
end(c);
|
||||
size(c);
|
||||
requires requires (typename Container::value_type const & x) { c.insert(end(c), x); };
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
constexpr bool is_contiguous_container_helper = is_container_helper<Container> && requires (Container & c)
|
||||
{
|
||||
data(c);
|
||||
};
|
||||
|
||||
template <typename Container>
|
||||
constexpr bool is_associative_container_helper = is_container_helper<Container> && requires
|
||||
{
|
||||
typename Container::key_type;
|
||||
typename Container::mapped_type;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
using size_type = std::uint32_t;
|
||||
|
||||
template <typename Container>
|
||||
struct is_container
|
||||
: std::bool_constant<detail::is_container_helper<Container>>
|
||||
{};
|
||||
|
||||
template <typename Container>
|
||||
constexpr bool is_container_v = is_container<Container>::value;
|
||||
|
||||
template <typename Container>
|
||||
struct is_contiguous_container
|
||||
: std::bool_constant<detail::is_contiguous_container_helper<Container>>
|
||||
{};
|
||||
|
||||
template <typename Container>
|
||||
constexpr bool is_contiguous_container_v = is_contiguous_container<Container>::value;
|
||||
|
||||
template <typename Container>
|
||||
struct is_associative_container
|
||||
: std::bool_constant<detail::is_associative_container_helper<Container>>
|
||||
{};
|
||||
|
||||
template <typename Container>
|
||||
constexpr bool is_associative_container_v = is_associative_container<Container>::value;
|
||||
|
||||
template <typename Container>
|
||||
struct has_static_size
|
||||
: std::false_type
|
||||
{};
|
||||
|
||||
template <typename Container>
|
||||
constexpr bool has_static_size_v = has_static_size<Container>::value;
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct has_static_size<T[N]>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct has_static_size<std::array<T, N>>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template <typename Stream, typename Container>
|
||||
requires is_ostream_v<Stream>
|
||||
void write_size(Stream & s, Container const & c)
|
||||
{
|
||||
write(s, static_cast<size_type>(std::size(c)));
|
||||
}
|
||||
|
||||
template <typename Stream, typename Container>
|
||||
requires is_istream_v<Stream>
|
||||
void read_size(Stream & s, Container & c)
|
||||
{
|
||||
size_type size;
|
||||
read(s, size);
|
||||
c.resize(size);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_ostream_v<Stream> && is_trivial_v<T>)
|
||||
void write_contiguous(Stream & s, T const * begin, std::size_t size)
|
||||
{
|
||||
write_padding<T>(s);
|
||||
s.write(reinterpret_cast<char const *>(begin), sizeof(T) * size);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_istream_v<Stream> && is_trivial_v<T>)
|
||||
void read_contiguous(Stream & s, T * begin, std::size_t size)
|
||||
{
|
||||
read_padding<T>(s);
|
||||
s.read(reinterpret_cast<char *>(begin), sizeof(T) * size);
|
||||
}
|
||||
|
||||
template <typename Stream, typename Container>
|
||||
requires is_ostream_v<Stream>
|
||||
void write_container(Stream & s, Container const & c)
|
||||
{
|
||||
if constexpr (!has_static_size_v<Container>)
|
||||
write_size(s, c);
|
||||
|
||||
using std::begin;
|
||||
using std::end;
|
||||
using std::data;
|
||||
using std::size;
|
||||
|
||||
if constexpr (is_contiguous_container_v<Container> && is_trivial_v<std::decay_t<decltype(*begin(c))>>)
|
||||
write_contiguous(s, data(c), size(c));
|
||||
else
|
||||
write_sequence(s, begin(c), end(c));
|
||||
}
|
||||
|
||||
template <typename Stream, typename Container>
|
||||
requires is_istream_v<Stream>
|
||||
void read_container(Stream & s, Container & c)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
using std::data;
|
||||
using std::size;
|
||||
|
||||
using value_type = std::decay_t<decltype(*begin(c))>;
|
||||
|
||||
if constexpr (has_static_size_v<Container>)
|
||||
read_sequence(s, begin(c), end(c));
|
||||
else if constexpr (is_contiguous_container_v<Container> && is_trivial_v<value_type>)
|
||||
{
|
||||
read_size(s, c);
|
||||
read_contiguous(s, data(c), size(c));
|
||||
}
|
||||
else if constexpr (is_associative_container_v<Container>)
|
||||
{
|
||||
size_type size;
|
||||
read(s, size);
|
||||
while (size --> 0)
|
||||
{
|
||||
typename Container::key_type key;
|
||||
typename Container::mapped_type value;
|
||||
read(s, key);
|
||||
read(s, value);
|
||||
c.insert(end(c), {std::move(key), std::move(value)});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_type size;
|
||||
read(s, size);
|
||||
while (size --> 0)
|
||||
{
|
||||
value_type x;
|
||||
read(s, x);
|
||||
c.insert(end(c), std::move(x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_ostream_v<Stream> && !is_custom_v<T> && !is_empty_v<T> && !is_trivial_v<T> && !is_struct_v<T> && is_container_v<T>)
|
||||
void write(Stream & s, T const & x)
|
||||
{
|
||||
write_container(s, x);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_istream_v<Stream> && !is_custom_v<T> && !is_empty_v<T> && !is_trivial_v<T> && !is_struct_v<T> && is_container_v<T>)
|
||||
void read(Stream & s, T & x)
|
||||
{
|
||||
read_container(s, x);
|
||||
}
|
||||
|
||||
}
|
||||
295
libs/sir/include/psemek/sir/memory.hpp
Normal file
295
libs/sir/include/psemek/sir/memory.hpp
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/sir/stream.hpp>
|
||||
#include <psemek/sir/trivial.hpp>
|
||||
#include <psemek/sir/container.hpp>
|
||||
#include <psemek/io/memory_stream.hpp>
|
||||
|
||||
namespace psemek::sir
|
||||
{
|
||||
|
||||
struct memory_istream
|
||||
: istream
|
||||
{
|
||||
memory_istream(io::memory_istream & s, std::size_t offset = 0)
|
||||
: istream(s, offset)
|
||||
{}
|
||||
|
||||
char const * data() const
|
||||
{
|
||||
return static_cast<io::memory_istream const &>(stream()).data();
|
||||
}
|
||||
|
||||
void advance(std::size_t n)
|
||||
{
|
||||
static_cast<io::memory_istream &>(stream()).advance(n);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string_view data_;
|
||||
std::size_t offset_;
|
||||
};
|
||||
|
||||
static_assert(is_istream_v<memory_istream>);
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
struct vector
|
||||
{
|
||||
vector() = default;
|
||||
|
||||
vector(T const * begin, T const * end)
|
||||
: begin_(begin)
|
||||
, end_(end)
|
||||
{}
|
||||
|
||||
vector(vector && v)
|
||||
: begin_(v.begin_)
|
||||
, end_(v.end_)
|
||||
{
|
||||
v.begin_ = nullptr;
|
||||
v.end_ = nullptr;
|
||||
}
|
||||
|
||||
vector(vector const &) = default;
|
||||
|
||||
vector & operator = (vector const &) = default;
|
||||
|
||||
vector & operator = (vector && v)
|
||||
{
|
||||
if (&v == this)
|
||||
return *this;
|
||||
|
||||
begin_ = v.begin_;
|
||||
end_ = v.end_;
|
||||
|
||||
v.begin_ = nullptr;
|
||||
v.end_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T const * begin() const
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
|
||||
T const * end() const
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return end_ - begin_;
|
||||
}
|
||||
|
||||
T const & operator[] (std::size_t i) const
|
||||
{
|
||||
return begin_[i];
|
||||
}
|
||||
|
||||
T const & at(std::size_t i) const
|
||||
{
|
||||
if (i >= size())
|
||||
throw std::out_of_range("psemek::sir::vector::at");
|
||||
return begin_[i];
|
||||
}
|
||||
|
||||
private:
|
||||
T const * begin_ = nullptr;
|
||||
T const * end_ = nullptr;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
requires is_trivial_v<T>
|
||||
void read(memory_istream & s, vector<T> & x)
|
||||
{
|
||||
size_type size;
|
||||
read(s, size);
|
||||
read_padding<T>(s);
|
||||
auto begin = reinterpret_cast<T const *>(s.data());
|
||||
s.advance(size * sizeof(T));
|
||||
auto end = reinterpret_cast<T const *>(s.data());
|
||||
x = vector<T>(begin, end);
|
||||
}
|
||||
|
||||
template <typename T, typename Compare = std::less<void>>
|
||||
struct set
|
||||
{
|
||||
set() = default;
|
||||
|
||||
set(T const * begin, T const * end)
|
||||
: begin_(begin)
|
||||
, end_(end)
|
||||
{}
|
||||
|
||||
set(set && s)
|
||||
: begin_(s.begin_)
|
||||
, end_(s.end_)
|
||||
{
|
||||
s.begin_ = nullptr;
|
||||
s.end_ = nullptr;
|
||||
}
|
||||
|
||||
set(set const &) = default;
|
||||
|
||||
set & operator = (set const &) = default;
|
||||
|
||||
set & operator = (set && s)
|
||||
{
|
||||
if (&s == this)
|
||||
return *this;
|
||||
|
||||
begin_ = s.begin_;
|
||||
end_ = s.end_;
|
||||
|
||||
s.begin_ = nullptr;
|
||||
s.end_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
T const * begin() const
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
|
||||
T const * end() const
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return end_ - begin_;
|
||||
}
|
||||
|
||||
template <typename H>
|
||||
T const * find(H const & x) const
|
||||
{
|
||||
Compare comp;
|
||||
auto it = std::lower_bound(begin_, end_, x, comp);
|
||||
if (it != end_ && comp(x, *it))
|
||||
return end_;
|
||||
return it;
|
||||
}
|
||||
|
||||
private:
|
||||
T const * begin_ = nullptr;
|
||||
T const * end_ = nullptr;
|
||||
};
|
||||
|
||||
template <typename T, typename Compare>
|
||||
requires is_trivial_v<T>
|
||||
void read(memory_istream & s, set<T, Compare> & x)
|
||||
{
|
||||
size_type size;
|
||||
read(s, size);
|
||||
read_padding<T>(s);
|
||||
auto begin = reinterpret_cast<T const *>(s.data());
|
||||
s.advance(size * sizeof(T));
|
||||
auto end = reinterpret_cast<T const *>(s.data());
|
||||
x = set<T, Compare>(begin, end);
|
||||
}
|
||||
|
||||
template <typename K, typename V, typename Compare = std::less<void>>
|
||||
struct map
|
||||
{
|
||||
struct pair
|
||||
{
|
||||
K first;
|
||||
V second;
|
||||
};
|
||||
|
||||
map() = default;
|
||||
|
||||
map(pair const * begin, pair const * end)
|
||||
: begin_(begin)
|
||||
, end_(end)
|
||||
{}
|
||||
|
||||
map(map && m)
|
||||
: begin_(m.begin_)
|
||||
, end_(m.end_)
|
||||
{
|
||||
m.begin_ = nullptr;
|
||||
m.end_ = nullptr;
|
||||
}
|
||||
|
||||
map(map const &) = default;
|
||||
|
||||
map & operator = (map const &) = default;
|
||||
|
||||
map & operator = (map && m)
|
||||
{
|
||||
if (&m == this)
|
||||
return *this;
|
||||
|
||||
begin_ = m.begin_;
|
||||
end_ = m.end_;
|
||||
|
||||
m.begin_ = nullptr;
|
||||
m.end_ = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
pair const * begin() const
|
||||
{
|
||||
return begin_;
|
||||
}
|
||||
|
||||
pair const * end() const
|
||||
{
|
||||
return end_;
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return end_ - begin_;
|
||||
}
|
||||
|
||||
template <typename H>
|
||||
pair const * find(H const & x) const
|
||||
{
|
||||
Compare comp;
|
||||
auto it = std::lower_bound(begin_, end_, x, [&](auto const & p, auto const & x){ return comp(p.first, x); });
|
||||
if (it != end_ && comp(x, it->first))
|
||||
return end_;
|
||||
return it;
|
||||
}
|
||||
|
||||
V const & at(K const & key) const
|
||||
{
|
||||
auto p = find(key);
|
||||
if (p == end_)
|
||||
throw std::out_of_range("psemek::sir::map::at");
|
||||
return p->second;
|
||||
}
|
||||
|
||||
private:
|
||||
pair const * begin_ = nullptr;
|
||||
pair const * end_ = nullptr;
|
||||
};
|
||||
|
||||
template <typename K, typename V, typename Compare>
|
||||
requires is_trivial_v<typename map<K, V, Compare>::pair>
|
||||
void read(memory_istream & s, map<K, V, Compare> & x)
|
||||
{
|
||||
using T = typename map<K, V, Compare>::pair;
|
||||
size_type size;
|
||||
read(s, size);
|
||||
read_padding<T>(s);
|
||||
auto begin = reinterpret_cast<T const *>(s.data());
|
||||
s.advance(size * sizeof(T));
|
||||
auto end = reinterpret_cast<T const *>(s.data());
|
||||
x = map<K, V, Compare>(begin, end);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using detail::vector;
|
||||
using detail::set;
|
||||
using detail::map;
|
||||
|
||||
}
|
||||
32
libs/sir/include/psemek/sir/platform.hpp
Normal file
32
libs/sir/include/psemek/sir/platform.hpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <bit>
|
||||
#include <cstdint>
|
||||
|
||||
namespace psemek::sir
|
||||
{
|
||||
|
||||
// If the target platform is big endian or mixed endian, the library needs tweaking
|
||||
static_assert(std::endian::native == std::endian::little);
|
||||
|
||||
// If the target platform has different sizes or alignments for basic types, the library needs tweaking
|
||||
static_assert(sizeof(char) == 1);
|
||||
static_assert(alignof(char) == 1);
|
||||
static_assert(sizeof(char8_t) == 1);
|
||||
static_assert(alignof(char8_t) == 1);
|
||||
static_assert(sizeof(char32_t) == 4);
|
||||
static_assert(alignof(char32_t) == 4);
|
||||
static_assert(alignof(std::uint8_t) == 1);
|
||||
static_assert(alignof(std::uint16_t) == 2);
|
||||
static_assert(alignof(std::uint32_t) == 4);
|
||||
static_assert(alignof(std::uint64_t) == 8);
|
||||
static_assert(alignof(std::int8_t) == 1);
|
||||
static_assert(alignof(std::int16_t) == 2);
|
||||
static_assert(alignof(std::int32_t) == 4);
|
||||
static_assert(alignof(std::int64_t) == 8);
|
||||
static_assert(sizeof(float) == 4);
|
||||
static_assert(alignof(float) == 4);
|
||||
static_assert(sizeof(double) == 8);
|
||||
static_assert(alignof(double) == 8);
|
||||
|
||||
}
|
||||
128
libs/sir/include/psemek/sir/stream.hpp
Normal file
128
libs/sir/include/psemek/sir/stream.hpp
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/io/stream.hpp>
|
||||
|
||||
namespace psemek::sir
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_istream_helper = requires (T & x, T const & cx, char * p, std::size_t n)
|
||||
{
|
||||
x.read(p, n);
|
||||
{ cx.offset() } -> std::same_as<std::size_t>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_ostream_helper = requires (T & x, T const & cx, char const * p, std::size_t n)
|
||||
{
|
||||
x.write(p, n);
|
||||
{ cx.offset() } -> std::same_as<std::size_t>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_istream
|
||||
: std::bool_constant<detail::is_istream_helper<T>>
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_istream_v = is_istream<T>::value;
|
||||
|
||||
template <typename T>
|
||||
struct is_ostream
|
||||
: std::bool_constant<detail::is_ostream_helper<T>>
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_ostream_v = is_ostream<T>::value;
|
||||
|
||||
struct istream
|
||||
{
|
||||
istream(io::istream & s, std::size_t offset = 0)
|
||||
: s_(s)
|
||||
, offset_(offset)
|
||||
{}
|
||||
|
||||
void read(char * p, std::size_t size)
|
||||
{
|
||||
s_.read_all(p, size);
|
||||
offset_ += size;
|
||||
}
|
||||
|
||||
io::istream & stream() { return s_; }
|
||||
io::istream const & stream() const { return s_; }
|
||||
|
||||
std::size_t offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
io::istream & s_;
|
||||
std::size_t offset_ = 0;
|
||||
};
|
||||
|
||||
struct ostream
|
||||
{
|
||||
ostream(io::ostream & s, std::size_t offset = 0)
|
||||
: s_(s)
|
||||
, offset_(offset)
|
||||
{}
|
||||
|
||||
void write(char const * p, std::size_t size)
|
||||
{
|
||||
s_.write_all(p, size);
|
||||
offset_ += size;
|
||||
}
|
||||
|
||||
io::ostream & stream() { return s_; }
|
||||
io::ostream const & stream() const { return s_; }
|
||||
|
||||
std::size_t offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
io::ostream & s_;
|
||||
std::size_t offset_ = 0;
|
||||
};
|
||||
|
||||
struct null_istream
|
||||
{
|
||||
null_istream(std::size_t offset = 0)
|
||||
: offset_(offset)
|
||||
{}
|
||||
|
||||
void read(char *, std::size_t size)
|
||||
{
|
||||
offset_ += size;
|
||||
}
|
||||
|
||||
std::size_t offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
std::size_t offset_ = 0;
|
||||
};
|
||||
|
||||
struct null_ostream
|
||||
{
|
||||
null_ostream(std::size_t offset = 0)
|
||||
: offset_(offset)
|
||||
{}
|
||||
|
||||
void write(char const *, std::size_t size)
|
||||
{
|
||||
offset_ += size;
|
||||
}
|
||||
|
||||
std::size_t offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
std::size_t offset_ = 0;
|
||||
};
|
||||
|
||||
static_assert(is_istream_v<istream>);
|
||||
static_assert(is_ostream_v<ostream>);
|
||||
static_assert(is_istream_v<null_istream>);
|
||||
static_assert(is_ostream_v<null_ostream>);
|
||||
|
||||
}
|
||||
89
libs/sir/include/psemek/sir/struct.hpp
Normal file
89
libs/sir/include/psemek/sir/struct.hpp
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/sir/trivial.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace psemek::sir
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_struct_helper = requires (T x)
|
||||
{
|
||||
std::get<0>(x);
|
||||
std::tuple_size_v<T>;
|
||||
};
|
||||
|
||||
template <typename T, std::size_t ... Is>
|
||||
void write_struct_impl(ostream & s, T const & x, std::index_sequence<Is...>)
|
||||
{
|
||||
(write(s, std::get<Is>(x)), ...);
|
||||
}
|
||||
|
||||
template <typename T, std::size_t ... Is>
|
||||
void read_struct_impl(istream & s, T & x, std::index_sequence<Is...>)
|
||||
{
|
||||
(read(s, std::get<Is>(x)), ...);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_struct
|
||||
: std::bool_constant<detail::is_struct_helper<T>>
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_struct_v = is_struct<T>::value;
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
struct is_struct<T[N]>
|
||||
: std::true_type
|
||||
{};
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires is_ostream_v<Stream>
|
||||
void write_struct(Stream & s, T const & x)
|
||||
{
|
||||
detail::write_struct_impl(s, x, std::make_index_sequence<std::tuple_size_v<T>>{});
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires is_istream_v<Stream>
|
||||
void read_struct(Stream & s, T & x)
|
||||
{
|
||||
detail::read_struct_impl(s, x, std::make_index_sequence<std::tuple_size_v<T>>{});
|
||||
}
|
||||
|
||||
template <typename Stream, typename T, std::size_t N>
|
||||
requires is_ostream_v<Stream>
|
||||
void write_struct(ostream & s, T const (&x)[N])
|
||||
{
|
||||
write_sequence(s, x, x + N);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T, std::size_t N>
|
||||
requires is_istream_v<Stream>
|
||||
void read_struct(Stream & s, T (&x)[N])
|
||||
{
|
||||
read_sequence(s, x, x + N);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_ostream_v<Stream> && !is_custom_v<T> && !is_empty_v<T> && !is_trivial_v<T> && is_struct_v<T>)
|
||||
void write(Stream & s, T const & x)
|
||||
{
|
||||
write_struct(s, x);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_istream_v<Stream> && !is_custom_v<T> && !is_empty_v<T> && !is_trivial_v<T> && is_struct_v<T>)
|
||||
void read(Stream & s, T & x)
|
||||
{
|
||||
read_struct(s, x);
|
||||
}
|
||||
|
||||
}
|
||||
124
libs/sir/include/psemek/sir/trivial.hpp
Normal file
124
libs/sir/include/psemek/sir/trivial.hpp
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/sir/platform.hpp>
|
||||
#include <psemek/sir/stream.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace psemek::sir
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
struct fake_istream
|
||||
{
|
||||
void read(char *, std::size_t){}
|
||||
|
||||
std::size_t offset() const { return 0; }
|
||||
};
|
||||
|
||||
struct fake_ostream
|
||||
{
|
||||
void write(char const *, std::size_t){}
|
||||
|
||||
std::size_t offset() const { return 0; }
|
||||
};
|
||||
|
||||
static_assert(is_istream_v<fake_istream>);
|
||||
static_assert(is_ostream_v<fake_ostream>);
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_custom_helper = requires (T & x, fake_istream & is, fake_ostream & os)
|
||||
{
|
||||
write(os, x);
|
||||
read(is, x);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
struct is_custom
|
||||
: std::bool_constant<detail::is_custom_helper<T>>
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_custom_v = is_custom<T>::value;
|
||||
|
||||
template <typename T>
|
||||
struct is_empty
|
||||
: std::bool_constant<std::is_empty_v<T>>
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_empty_v = is_empty<T>::value;
|
||||
|
||||
template <typename T>
|
||||
struct is_trivial
|
||||
: std::bool_constant<std::is_trivial_v<T> && std::is_standard_layout_v<T>>
|
||||
{};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_trivial_v = is_trivial<T>::value;
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_ostream_v<Stream> && !is_custom_v<T> && is_empty_v<T>)
|
||||
void write(Stream &, T const &)
|
||||
{}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_istream_v<Stream> && !is_custom_v<T> && is_empty_v<T>)
|
||||
void read(Stream &, T &)
|
||||
{}
|
||||
|
||||
template <typename T, typename Stream>
|
||||
requires is_ostream_v<Stream>
|
||||
void write_padding(Stream & s)
|
||||
{
|
||||
char const zeros[alignof(T)] = {};
|
||||
std::size_t const padding = (alignof(T) - s.offset()) % alignof(T);
|
||||
if (padding != 0)
|
||||
s.write(zeros, padding);
|
||||
}
|
||||
|
||||
template <typename T, typename Stream>
|
||||
requires is_istream_v<Stream>
|
||||
void read_padding(Stream & s)
|
||||
{
|
||||
char zeros[alignof(T)] = {};
|
||||
std::size_t const padding = (alignof(T) - s.offset()) % alignof(T);
|
||||
if (padding != 0)
|
||||
s.read(zeros, padding);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_ostream_v<Stream> && !is_custom_v<T> && !is_empty_v<T> && is_trivial_v<T>)
|
||||
void write(Stream & s, T const & x)
|
||||
{
|
||||
write_padding<T>(s);
|
||||
s.write(reinterpret_cast<char const *>(std::addressof(x)), sizeof(x));
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
requires (is_istream_v<Stream> && !is_custom_v<T> && !is_empty_v<T> && is_trivial_v<T>)
|
||||
void read(Stream & s, T & x)
|
||||
{
|
||||
read_padding<T>(s);
|
||||
s.read(reinterpret_cast<char *>(std::addressof(x)), sizeof(x));
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
void write_sequence(ostream & s, Iterator begin, Iterator end)
|
||||
{
|
||||
for (; begin != end; ++begin)
|
||||
write(s, *begin);
|
||||
}
|
||||
|
||||
template <typename Iterator>
|
||||
void read_sequence(istream & s, Iterator begin, Iterator end)
|
||||
{
|
||||
for (; begin != end; ++begin)
|
||||
read(s, *begin);
|
||||
}
|
||||
|
||||
}
|
||||
180
libs/sir/tests/container.cpp
Normal file
180
libs/sir/tests/container.cpp
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
#include <psemek/test/test.hpp>
|
||||
|
||||
#include <psemek/sir/container.hpp>
|
||||
#include <psemek/io/memory_stream.hpp>
|
||||
|
||||
#include "test_type.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename Container>
|
||||
void test_container(Container const & c, bool ordered = true)
|
||||
{
|
||||
using std::begin;
|
||||
using std::end;
|
||||
using std::cbegin;
|
||||
using std::cend;
|
||||
using std::size;
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
{
|
||||
io::memory_ostream s{buffer, buffer + size(buffer)};
|
||||
sir::ostream os{s};
|
||||
write(os, c);
|
||||
}
|
||||
|
||||
Container c2;
|
||||
|
||||
{
|
||||
io::memory_istream s{buffer, buffer + size(buffer)};
|
||||
sir::istream is{s};
|
||||
read(is, c2);
|
||||
}
|
||||
|
||||
expect_equal(std::size(c), std::size(c2));
|
||||
|
||||
if (ordered)
|
||||
{
|
||||
for (auto i1 = cbegin(c), i2 = cbegin(c2); i1 != cend(c); ++i1, ++i2)
|
||||
expect_equal(*i1, *i2);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto i1 = begin(c); i1 != end(c); ++i1)
|
||||
expect(std::find(begin(c2), end(c2), *i1) != end(c2));
|
||||
|
||||
for (auto i2 = begin(c2); i2 != end(c2); ++i2)
|
||||
expect(std::find(begin(c), end(c), *i2) != end(c));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
test_case(sir_container_c__array_trivial)
|
||||
{
|
||||
std::uint32_t c[13] { 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_c__array_custom)
|
||||
{
|
||||
test_type c[3] { {15u}, {65535u}, {1234567u} };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_std__array_trivial)
|
||||
{
|
||||
std::array<std::uint32_t, 13> c { 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_std__array_custom)
|
||||
{
|
||||
std::array<test_type, 3> c {{ {15u}, {65535u}, {1234567u} }};
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_vector_trivial)
|
||||
{
|
||||
std::vector<std::uint32_t> c { 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_vector_custom)
|
||||
{
|
||||
std::vector<test_type> c { {15u}, {65535u}, {1234567u} };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_list_trivial)
|
||||
{
|
||||
std::list<std::uint32_t> c { 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_list_custom)
|
||||
{
|
||||
std::list<test_type> c { {15u}, {65535u}, {1234567u} };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_deque_trivial)
|
||||
{
|
||||
std::deque<std::uint32_t> c { 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_deque_custom)
|
||||
{
|
||||
std::deque<test_type> c { {15u}, {65535u}, {1234567u} };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_set_trivial)
|
||||
{
|
||||
std::set<std::uint32_t> c { 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_set_custom)
|
||||
{
|
||||
std::set<test_type> c { {15u}, {65535u}, {1234567u} };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_unordered__set_trivial)
|
||||
{
|
||||
std::unordered_set<std::uint32_t> c { 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
test_container(c, false);
|
||||
}
|
||||
|
||||
test_case(sir_container_unordered__set_custom)
|
||||
{
|
||||
std::unordered_set<test_type> c { {15u}, {65535u}, {1234567u} };
|
||||
test_container(c, false);
|
||||
}
|
||||
|
||||
test_case(sir_container_map_trivial)
|
||||
{
|
||||
std::map<std::uint32_t, float> c { {10, 3.1415f}, {15, 6.7123f}, {1, -24.444f} };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_map_custom__key)
|
||||
{
|
||||
std::map<test_type, float> c { {{15u}, 3.1415f}, {{65535u}, 23.451f}, {{1234567u}, -29.231f} };
|
||||
test_container(c);
|
||||
}
|
||||
|
||||
test_case(sir_container_unordered__map_trivial)
|
||||
{
|
||||
std::unordered_map<std::uint32_t, float> c { {10, 3.1415f}, {15, 6.7123f}, {1, -24.444f} };
|
||||
test_container(c, false);
|
||||
}
|
||||
|
||||
test_case(sir_container_unordered__map_custom__key)
|
||||
{
|
||||
std::unordered_map<test_type, float> c { {{15u}, 3.1415f}, {{65535u}, 23.451f}, {{1234567u}, -29.231f} };
|
||||
test_container(c, false);
|
||||
}
|
||||
|
||||
test_case(sir_container_string)
|
||||
{
|
||||
std::string c = "Hello, world!";
|
||||
test_container(c);
|
||||
}
|
||||
32
libs/sir/tests/custom.cpp
Normal file
32
libs/sir/tests/custom.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
#include <psemek/test/test.hpp>
|
||||
|
||||
#include <psemek/sir/container.hpp>
|
||||
#include <psemek/io/memory_stream.hpp>
|
||||
|
||||
#include "test_type.hpp"
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
test_case(sir_custom)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
test_type x;
|
||||
x.value = 42;
|
||||
|
||||
{
|
||||
io::memory_ostream s{buffer, buffer + std::size(buffer)};
|
||||
sir::ostream os{s};
|
||||
write(os, x);
|
||||
}
|
||||
|
||||
test_type x2;
|
||||
|
||||
{
|
||||
io::memory_istream s{buffer, buffer + std::size(buffer)};
|
||||
sir::istream is{s};
|
||||
read(is, x2);
|
||||
}
|
||||
|
||||
expect_equal(x, x2);
|
||||
}
|
||||
83
libs/sir/tests/memory.cpp
Normal file
83
libs/sir/tests/memory.cpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
#include <psemek/test/test.hpp>
|
||||
|
||||
#include <psemek/sir/memory.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <map>
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename C1, typename C2>
|
||||
void read_write(C1 const & c1, C2 & c2)
|
||||
{
|
||||
using std::size;
|
||||
|
||||
char buffer[1024];
|
||||
|
||||
{
|
||||
io::memory_ostream s{buffer, buffer + size(buffer)};
|
||||
sir::ostream os{s};
|
||||
write(os, c1);
|
||||
}
|
||||
|
||||
{
|
||||
io::memory_istream s{buffer, buffer + size(buffer)};
|
||||
sir::memory_istream is{s};
|
||||
read(is, c2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
test_case(sir_memory_vector)
|
||||
{
|
||||
std::vector<std::uint32_t> c1{ 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
sir::vector<std::uint32_t> c2;
|
||||
|
||||
read_write(c1, c2);
|
||||
|
||||
expect_equal(c1.size(), c2.size());
|
||||
|
||||
for (std::size_t i = 0; i < c1.size(); ++i)
|
||||
expect_equal(c1[i], c2[i]);
|
||||
}
|
||||
|
||||
test_case(sir_memory_set)
|
||||
{
|
||||
std::set<std::uint32_t> c1{ 15, 16, 18, 2, 65535, 14, 5, 8, 42, 555, 18, 729, 76 };
|
||||
sir::set<std::uint32_t> c2;
|
||||
|
||||
read_write(c1, c2);
|
||||
|
||||
expect_equal(c1.size(), c2.size());
|
||||
expect(std::is_sorted(c2.begin(), c2.end()));
|
||||
|
||||
auto i1 = c1.begin();
|
||||
auto i2 = c2.begin();
|
||||
|
||||
for (; i1 != c1.end(); ++i1, ++i2)
|
||||
expect_equal(*i1, *i2);
|
||||
}
|
||||
|
||||
test_case(sir_memory_map)
|
||||
{
|
||||
std::map<std::uint32_t, float> c1 { {10, 3.1415f}, {15, 6.7123f}, {1, -24.444f} };
|
||||
sir::map<std::uint32_t, float> c2;
|
||||
|
||||
read_write(c1, c2);
|
||||
|
||||
expect_equal(c1.size(), c2.size());
|
||||
|
||||
auto i1 = c1.begin();
|
||||
auto i2 = c2.begin();
|
||||
|
||||
for (; i1 != c1.end(); ++i1, ++i2)
|
||||
{
|
||||
expect_equal(i1->first, i2->first);
|
||||
expect_equal(i1->second, i2->second);
|
||||
}
|
||||
}
|
||||
62
libs/sir/tests/struct.cpp
Normal file
62
libs/sir/tests/struct.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
#include <psemek/test/test.hpp>
|
||||
|
||||
#include <psemek/sir/struct.hpp>
|
||||
#include <psemek/io/memory_stream.hpp>
|
||||
|
||||
#include "test_type.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
void test_value(T const & x)
|
||||
{
|
||||
char buffer[1024];
|
||||
|
||||
{
|
||||
io::memory_ostream s{buffer, buffer + std::size(buffer)};
|
||||
sir::ostream os{s};
|
||||
write(os, x);
|
||||
}
|
||||
|
||||
T x2;
|
||||
|
||||
{
|
||||
io::memory_istream s{buffer, buffer + std::size(buffer)};
|
||||
sir::istream is{s};
|
||||
read(is, x2);
|
||||
}
|
||||
|
||||
expect_equal(x, x2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
test_case(sir_struct_pair_trivial)
|
||||
{
|
||||
std::pair<char, std::uint32_t> p{'a', 1000000u};
|
||||
test_value(p);
|
||||
}
|
||||
|
||||
test_case(sir_struct_pair_custom)
|
||||
{
|
||||
std::pair<char, test_type> p{'a', {1000000u}};
|
||||
test_value(p);
|
||||
}
|
||||
|
||||
test_case(sir_struct_tuple_trivial)
|
||||
{
|
||||
std::tuple<char, std::uint32_t, float> p{'a', 1000000u, 3.1415f};
|
||||
test_value(p);
|
||||
}
|
||||
|
||||
test_case(sir_struct_tuple_custom)
|
||||
{
|
||||
std::tuple<char, test_type, float> p{'a', {1000000u}, 3.1415f};
|
||||
test_value(p);
|
||||
}
|
||||
49
libs/sir/tests/test_type.hpp
Normal file
49
libs/sir/tests/test_type.hpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/test/test.hpp>
|
||||
#include <psemek/sir/container.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
struct test_type
|
||||
{
|
||||
std::uint32_t value;
|
||||
|
||||
friend auto operator <=> (test_type const &, test_type const &) = default;
|
||||
};
|
||||
|
||||
template <typename Stream>
|
||||
void write(Stream & s, test_type const & x)
|
||||
{
|
||||
write(s, x.value);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
void read(Stream & s, test_type & x)
|
||||
{
|
||||
read(s, x.value);
|
||||
}
|
||||
|
||||
static_assert(sir::is_custom_v<test_type>);
|
||||
|
||||
inline std::ostream & operator << (std::ostream & s, test_type const & x)
|
||||
{
|
||||
return s << '{' << x.value << '}';
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <>
|
||||
struct hash<test_type>
|
||||
{
|
||||
std::size_t operator()(test_type const & x) const
|
||||
{
|
||||
return x.value;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
161
libs/sir/tests/trivial.cpp
Normal file
161
libs/sir/tests/trivial.cpp
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
#include <psemek/test/test.hpp>
|
||||
|
||||
#include <psemek/sir/trivial.hpp>
|
||||
#include <psemek/io/memory_stream.hpp>
|
||||
|
||||
#include <numeric>
|
||||
#include <array>
|
||||
|
||||
using namespace psemek;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
void test_primitive_write(T value)
|
||||
{
|
||||
T result;
|
||||
io::memory_ostream s{reinterpret_cast<char *>(&result), reinterpret_cast<char *>(&result) + sizeof(result)};
|
||||
sir::ostream os{s};
|
||||
sir::write(os, value);
|
||||
|
||||
expect_equal(result, value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void test_primitive_read(T value)
|
||||
{
|
||||
T result;
|
||||
io::memory_istream s{reinterpret_cast<char *>(&value), reinterpret_cast<char *>(&value) + sizeof(value)};
|
||||
sir::istream is{s};
|
||||
sir::read(is, result);
|
||||
|
||||
expect_equal(result, value);
|
||||
}
|
||||
|
||||
struct trivial_type
|
||||
{
|
||||
char c;
|
||||
std::int64_t i;
|
||||
std::uint32_t u;
|
||||
float f;
|
||||
|
||||
friend bool operator == (trivial_type const &, trivial_type const &) = default;
|
||||
|
||||
friend std::ostream & operator << (std::ostream & os, trivial_type const &)
|
||||
{
|
||||
return os << "trivial_type{...}";
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
test_case(sir_primitive_write)
|
||||
{
|
||||
test_primitive_write<char>('a');
|
||||
test_primitive_write<std::uint8_t>(42);
|
||||
test_primitive_write<std::uint16_t>(42042);
|
||||
test_primitive_write<std::uint32_t>(1000000000u);
|
||||
test_primitive_write<std::uint64_t>(1000000000ull);
|
||||
test_primitive_write<std::int8_t>(-42);
|
||||
test_primitive_write<std::int16_t>(-10042);
|
||||
test_primitive_write<std::int32_t>(-1000000000u);
|
||||
test_primitive_write<std::int64_t>(-1000000000ull);
|
||||
test_primitive_write<float>(3.14159265f);
|
||||
test_primitive_write<double>(2.718281828459045);
|
||||
}
|
||||
|
||||
test_case(sir_primitive_read)
|
||||
{
|
||||
test_primitive_read<char>('a');
|
||||
test_primitive_read<std::uint8_t>(42);
|
||||
test_primitive_read<std::uint16_t>(42042);
|
||||
test_primitive_read<std::uint32_t>(1000000000u);
|
||||
test_primitive_read<std::uint64_t>(1000000000ull);
|
||||
test_primitive_read<std::int8_t>(-42);
|
||||
test_primitive_read<std::int16_t>(-10042);
|
||||
test_primitive_read<std::int32_t>(-1000000000u);
|
||||
test_primitive_read<std::int64_t>(-1000000000ull);
|
||||
test_primitive_read<float>(3.14159265f);
|
||||
test_primitive_read<double>(2.718281828459045);
|
||||
}
|
||||
|
||||
test_case(sir_padding_write)
|
||||
{
|
||||
char buffer[256];
|
||||
io::memory_ostream s{buffer, buffer + std::size(buffer)};
|
||||
sir::ostream os{s};
|
||||
|
||||
sir::write(os, char('a'));
|
||||
expect_equal(os.offset(), 1);
|
||||
sir::write(os, std::uint32_t(42));
|
||||
expect_equal(os.offset(), 8);
|
||||
sir::write(os, char('b'));
|
||||
expect_equal(os.offset(), 9);
|
||||
sir::write(os, std::int32_t(-42));
|
||||
expect_equal(os.offset(), 16);
|
||||
}
|
||||
|
||||
test_case(sir_padding_read)
|
||||
{
|
||||
char buffer[256] = {0};
|
||||
io::memory_istream s{buffer, buffer + std::size(buffer)};
|
||||
sir::istream is{s};
|
||||
|
||||
char c;
|
||||
std::uint32_t ui;
|
||||
sir::read(is, c);
|
||||
expect_equal(is.offset(), 1);
|
||||
sir::read(is, ui);
|
||||
expect_equal(is.offset(), 8);
|
||||
sir::read(is, c);
|
||||
expect_equal(is.offset(), 9);
|
||||
sir::read(is, ui);
|
||||
expect_equal(is.offset(), 16);
|
||||
}
|
||||
|
||||
test_case(sir_trivial)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
trivial_type value{'a', 42, 12345, 3.1415f};
|
||||
|
||||
{
|
||||
io::memory_ostream s{buffer, buffer + std::size(buffer)};
|
||||
sir::ostream os{s};
|
||||
sir::write(os, char('x'));
|
||||
sir::write(os, value);
|
||||
|
||||
expect_equal(os.offset(), sizeof(trivial_type) + alignof(trivial_type));
|
||||
}
|
||||
|
||||
{
|
||||
io::memory_istream s{buffer, buffer + std::size(buffer)};
|
||||
sir::istream is{s};
|
||||
|
||||
char c;
|
||||
trivial_type read_value;
|
||||
sir::read(is, c);
|
||||
sir::read(is, read_value);
|
||||
|
||||
expect_equal(c, 'x');
|
||||
expect_equal(is.offset(), sizeof(trivial_type) + alignof(trivial_type));
|
||||
expect_equal(read_value, value);
|
||||
}
|
||||
}
|
||||
|
||||
test_case(sir_trivial__array)
|
||||
{
|
||||
char buffer[256];
|
||||
|
||||
{
|
||||
std::array<std::uint32_t, 7> array;
|
||||
std::iota(std::begin(array), std::end(array), 0);
|
||||
|
||||
io::memory_ostream s{buffer, buffer + std::size(buffer)};
|
||||
sir::ostream os{s};
|
||||
sir::write(os, array);
|
||||
|
||||
expect_equal(os.offset(), std::size(array) * sizeof(array[0]));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue