Seed Core_IO from doc snapshot
This commit is contained in:
13
tjp/core/io/bin/Debug.h
Normal file
13
tjp/core/io/bin/Debug.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
//#define IO_BIN_USE_LOGGING
|
||||
|
||||
#ifdef IO_BIN_USE_LOGGING
|
||||
#define IO_BIN_LOG(...) sLogRelease("io_bin", x)
|
||||
#else
|
||||
#define IO_BIN_LOG(...)
|
||||
#endif
|
||||
357
tjp/core/io/bin/IO.h
Normal file
357
tjp/core/io/bin/IO.h
Normal file
@@ -0,0 +1,357 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../IO.h"
|
||||
#include "../IO_encoder.h"
|
||||
#include "../IO_dispatch.h"
|
||||
#include "../IO_.h"
|
||||
|
||||
#include "Debug.h"
|
||||
|
||||
#include "../Exception.h"
|
||||
#include <tjp/core/types/Types.h>
|
||||
#include <tjp/core/type_traits/is_mappish.hpp>
|
||||
#include <tjp/core/type_traits/is_string.hpp>
|
||||
#include <tjp/core/type_traits/is_iterable.hpp>
|
||||
#include <tjp/core/type_traits/always_false.hpp>
|
||||
#include <tjp/core/assert/debug_assert.h>
|
||||
#include <cmath>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory.h>
|
||||
#include <typeinfo>
|
||||
#include <optional>
|
||||
|
||||
|
||||
//#include <tjp/core/allocator/AllocatorPool.hpp>
|
||||
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
#include "dictionary.h"
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
using Size = u32;
|
||||
typedef const char *KeyType;
|
||||
|
||||
template <typename IO, typename T, typename = void>
|
||||
struct has_io_bin : std::false_type {};
|
||||
|
||||
template <typename IO, typename T>
|
||||
struct has_io_bin<IO, T,
|
||||
std::void_t<decltype(io_bin(std::declval<IO&>(),
|
||||
std::declval<T&>())) >> : std::true_type {};
|
||||
|
||||
|
||||
//template<typename IO, typename T>
|
||||
//void io_bin(IO &io, T &t)
|
||||
//{
|
||||
// io_(io, t);
|
||||
//}
|
||||
//
|
||||
template<typename IO, typename T>
|
||||
void io_bin_w_integer(IO &io, const T &t_)
|
||||
{
|
||||
constexpr u8 sevenBits = 0b01111111;
|
||||
constexpr u8 eighthBit = 0b10000000;
|
||||
|
||||
// // check
|
||||
// io.intrinsic(t_);
|
||||
|
||||
constexpr size_t maxBytes = sizeof(T) + sizeof(T)/8 + 1;
|
||||
u8 bytes[maxBytes];
|
||||
size_t count = 0;
|
||||
u8 *out = bytes;
|
||||
|
||||
const T zero = T(0);
|
||||
T t = t_;
|
||||
do
|
||||
{
|
||||
u8 byte = t & sevenBits;
|
||||
t >>= 7;
|
||||
if (t != zero)
|
||||
byte |= eighthBit;
|
||||
|
||||
*out++ = byte;
|
||||
count++;
|
||||
}
|
||||
while (t > 0);
|
||||
|
||||
debug_assert(0 < count && count <= maxBytes);
|
||||
|
||||
io.intrinsic((const char *)bytes, count);
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_r_integer(IO &io, T &t)
|
||||
{
|
||||
constexpr u8 sevenBits = 0b01111111;
|
||||
constexpr u8 eighthBit = 0b10000000;
|
||||
|
||||
// T check;
|
||||
// io.intrinsic(check);
|
||||
|
||||
u8 reverse[sizeof(T) + sizeof(T)/8 + 1];
|
||||
u8 *r = reverse;
|
||||
|
||||
t = 0;
|
||||
u8 byte;
|
||||
do
|
||||
{
|
||||
io.intrinsic(byte);
|
||||
*r = byte;
|
||||
++r;
|
||||
}
|
||||
while ((byte & eighthBit) != 0);
|
||||
|
||||
while (r != reverse)
|
||||
{
|
||||
--r;
|
||||
t <<= 7;
|
||||
t |= T(*r & sevenBits);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_w_integer_signed(IO &io, const T &t_)
|
||||
{
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
|
||||
UT t;
|
||||
if (t_ < 0)
|
||||
{
|
||||
t = -t_;
|
||||
t <<= 1;
|
||||
t |= UT(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
t = UT(t_) << 1;
|
||||
}
|
||||
|
||||
io_bin_w_integer(io, t);
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_r_integer_signed(IO &io, T &t_)
|
||||
{
|
||||
using UT = typename std::make_unsigned<T>::type;
|
||||
|
||||
UT t;
|
||||
io_bin_r_integer(io, t);
|
||||
|
||||
auto isNegative = t & 1;
|
||||
t_ = t >> 1;
|
||||
|
||||
if (isNegative)
|
||||
t_ = -t_;
|
||||
}
|
||||
|
||||
template<typename IO> void io_bin(IO &io, bool &t) { io.intrinsic((s8&)t); }
|
||||
template<typename IO> void io_bin(IO &io, u8 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_bin(IO &io, char &t) { io.intrinsic((s8&)t); }
|
||||
template<typename IO> void io_bin(IO &io, s8 &t) { io.intrinsic(t); }
|
||||
|
||||
/*
|
||||
template<typename IO> void io_bin(IO &io, u16 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_bin(IO &io, u32 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_bin(IO &io, u64 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_bin(IO &io, u128 &t) { io.intrinsic(t); }
|
||||
|
||||
template<typename IO> void io_bin(IO &io, s16 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_bin(IO &io, s32 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_bin(IO &io, s64 &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_bin(IO &io, s128 &t) { io.intrinsic(t); }
|
||||
*/
|
||||
|
||||
template<typename IO> void io_bin_w(IO &io, const u16 &t) { io_bin_w_integer(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const u32 &t) { io_bin_w_integer(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const u64 &t) { io_bin_w_integer(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const u128 &t) { io_bin_w_integer(io, t); }
|
||||
|
||||
template<typename IO> void io_bin_w(IO &io, const s16 &t) { io_bin_w_integer_signed(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const s32 &t) { io_bin_w_integer_signed(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const s64 &t) { io_bin_w_integer_signed(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const s128 &t) { io_bin_w_integer_signed(io, t); }
|
||||
|
||||
template<typename IO> void io_bin_r(IO &io, u16 &t) { io_bin_r_integer(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, u32 &t) { io_bin_r_integer(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, u64 &t) { io_bin_r_integer(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, u128 &t) { io_bin_r_integer(io, t); }
|
||||
|
||||
template<typename IO> void io_bin_r(IO &io, s16 &t) { io_bin_r_integer_signed(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, s32 &t) { io_bin_r_integer_signed(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, s64 &t) { io_bin_r_integer_signed(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, s128 &t) { io_bin_r_integer_signed(io, t); }
|
||||
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_w_real(IO &io, const T &t_)
|
||||
{
|
||||
bool downcast = false; // io.flags & IO_FLAGS_DOWNCAST_REAL;
|
||||
if (downcast)
|
||||
{
|
||||
io.intrinsic(static_cast<r32>(t_));
|
||||
}
|
||||
else
|
||||
{
|
||||
io.intrinsic(t_);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_r_real(IO &io, T &t)
|
||||
{
|
||||
bool downcast = false; // io.flags & IO_FLAGS_DOWNCAST_REAL;
|
||||
if (downcast)
|
||||
{
|
||||
r32 t_;
|
||||
io.intrinsic(t_);
|
||||
t = static_cast<T>(t_);
|
||||
}
|
||||
else
|
||||
{
|
||||
io.intrinsic(t);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IO> void io_bin_r(IO &io, r32 &t) { io_bin_r_real(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, r64 &t) { io_bin_r_real(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, r128 &t) { io_bin_r_real(io, t); }
|
||||
|
||||
template<typename IO> void io_bin_w(IO &io, const r32 &t) { io_bin_w_real(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const r64 &t) { io_bin_w_real(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const r128 &t) { io_bin_w_real(io, t); }
|
||||
|
||||
|
||||
template<typename IO> void io_bin(IO &io, std::string &t) { io.intrinsic_string(t); }
|
||||
|
||||
#if defined(SYS_MAC) || defined(SYS_IOS)
|
||||
/*
|
||||
template<typename IO> void io_bin(IO &io, unsigned long &t) { io.intrinsic(t); }
|
||||
template<typename IO> void io_bin(IO &io, signed long &t) { io.intrinsic(t); }
|
||||
*/
|
||||
|
||||
template<typename IO> void io_bin_w(IO &io, const unsigned long &t) { io_bin_w_integer(io, t); }
|
||||
template<typename IO> void io_bin_w(IO &io, const signed long &t) { io_bin_w_integer(io, t); }
|
||||
|
||||
template<typename IO> void io_bin_r(IO &io, unsigned long &t) { io_bin_r_integer(io, t); }
|
||||
template<typename IO> void io_bin_r(IO &io, signed long &t) { io_bin_r_integer(io, t); }
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
struct Optional
|
||||
{
|
||||
T &v;
|
||||
bool write;
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
Optional<T> optional(T &t, bool write=true) { return Optional<T> { t, write }; }
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin(IO &io, Optional<T> &t) {
|
||||
io.any(t.write);
|
||||
if (t.write)
|
||||
io.any(t.v);
|
||||
}
|
||||
|
||||
template<typename P, typename T>
|
||||
struct Pointer
|
||||
{
|
||||
P *p;
|
||||
bool write;
|
||||
} ;
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_ptr_allocate(IO &io, T *&t)
|
||||
{
|
||||
io.allocate(t);
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_ptr_allocate_default(IO &io, T *&t)
|
||||
{
|
||||
t = new T();
|
||||
}
|
||||
|
||||
template<typename IO, typename T> void io_bin(IO &io, T *&t)
|
||||
{
|
||||
auto w = io.template pointer<T>(t);
|
||||
io.any(w);
|
||||
}
|
||||
|
||||
template<typename IO, typename B, typename T>
|
||||
void io_bin_w(IO &io, const Pointer<B, T> &t)
|
||||
{
|
||||
io.any(t.write);
|
||||
|
||||
if (t.write)
|
||||
io.any(**t.p);
|
||||
}
|
||||
|
||||
template<typename IO, typename B, typename T>
|
||||
void io_bin_r(IO &io, Pointer<B, T> &t)
|
||||
{
|
||||
io.any(t.write);
|
||||
if (t.write)
|
||||
{
|
||||
io_ptr_allocate(io, *t.p);
|
||||
io.any(**t.p);
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::vector<char> Serialization;
|
||||
|
||||
template<typename T>
|
||||
struct Custom
|
||||
{
|
||||
T *p;
|
||||
} ;
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin(IO &io, Custom<T> &t)
|
||||
{
|
||||
io.template dispatch_custom<>(io, *t.p);
|
||||
}
|
||||
|
||||
template<typename IO, typename ...T>
|
||||
void io_bin(IO &io, std::tuple<T...> &t)
|
||||
{
|
||||
std::apply([&io](auto &...x){(io.any(x), ...);}, t);
|
||||
}
|
||||
|
||||
//#ifdef SYS_APPLE
|
||||
// std::vector<bool> fixes
|
||||
|
||||
template<typename IO>
|
||||
void io_bin_w(IO &io, const std::vector<bool>::const_iterator::reference t)
|
||||
{
|
||||
bool b = t;
|
||||
io.intrinsic(b);
|
||||
}
|
||||
|
||||
template<typename IO>
|
||||
void io_bin_r(IO &io, std::vector<bool>::iterator::reference &t)
|
||||
{
|
||||
bool b;
|
||||
io.intrinsic(b);
|
||||
t = b;
|
||||
}
|
||||
|
||||
//#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "in.h"
|
||||
#include "out.h"
|
||||
105
tjp/core/io/bin/_tests/ConstString+IO.cpp
Normal file
105
tjp/core/io/bin/_tests/ConstString+IO.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <tjp/core/string/Str.hpp>
|
||||
#include <tjp/core/string/Str+IO.h>
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
|
||||
#include <tjp/core/io/bin/IO.h>
|
||||
#include <tjp/core/io/json/in_sajson.h>
|
||||
#include <tjp/core/io/json/out.h>
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
|
||||
namespace tjp::core::str {
|
||||
namespace {
|
||||
|
||||
SCENARIO("core::string::str+io")
|
||||
{
|
||||
GIVEN("args")
|
||||
{
|
||||
core::from_string<core::Str>("1");
|
||||
}
|
||||
GIVEN("a const string")
|
||||
{
|
||||
Str one("1");
|
||||
Str one_again("1");
|
||||
Str two("2");
|
||||
|
||||
std::string v;
|
||||
for (auto i=0;i<256;++i)
|
||||
v += "a";
|
||||
|
||||
Str longString(v);
|
||||
|
||||
REQUIRE(one == one);
|
||||
REQUIRE(one == "1");
|
||||
REQUIRE(one != "2");
|
||||
REQUIRE(one != two);
|
||||
REQUIRE(one == one_again);
|
||||
REQUIRE(longString == longString);
|
||||
REQUIRE(longString != one);
|
||||
}
|
||||
|
||||
GIVEN("a const string")
|
||||
{
|
||||
Str a("Aweoiwefo wefoij wefioj wefoi jwefo ijwefijo w");
|
||||
Str b("Bweoiwefo wefoij wefioj wefoi jwefo ijwefijo w wefiojwefoi jwefio wefoij wefoij wefijo ewfioj wefjio wef");
|
||||
Str c("Cweo");
|
||||
|
||||
WHEN("bin")
|
||||
{
|
||||
auto s = io::bin::serialize(a);
|
||||
auto b = io::bin::deserialize<decltype(a)>(s);
|
||||
|
||||
REQUIRE(a == b);
|
||||
}
|
||||
|
||||
WHEN("json")
|
||||
{
|
||||
auto s = io::json::serialize(a);
|
||||
auto b = io::json::deserialize<decltype(a)>(s);
|
||||
|
||||
REQUIRE(a == b);
|
||||
}
|
||||
|
||||
WHEN("bin multi")
|
||||
{
|
||||
io::bin::Writer<io::bin::memory_out> o;
|
||||
o.object("a", a, "b", b, "c", c);
|
||||
auto s = o.serialization();
|
||||
|
||||
io::bin::Reader<io::bin::memory_in> i(io::bin::memory_in(s.data(), s.size()));
|
||||
|
||||
Str a_, b_, c_;
|
||||
i.object("a", a_, "b", b_, "c", c_);
|
||||
|
||||
REQUIRE(a == a_);
|
||||
REQUIRE(b == b_);
|
||||
REQUIRE(c == c_);
|
||||
}
|
||||
|
||||
WHEN("json multi")
|
||||
{
|
||||
io::json::Serialization s;
|
||||
omemstream stream(s);
|
||||
|
||||
io::json::Writer<io::json::json_out> o(stream);
|
||||
o.object("a", a, "b", b, "c", c);
|
||||
|
||||
io::json::Reader<io::json::json_in> i(std::move(s));
|
||||
|
||||
Str a_, b_, c_;
|
||||
i.object("a", a_, "b", b_, "c", c_);
|
||||
|
||||
REQUIRE(a == a_);
|
||||
REQUIRE(b == b_);
|
||||
REQUIRE(c == c_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
361
tjp/core/io/bin/_tests/IO_bin.cpp
Normal file
361
tjp/core/io/bin/_tests/IO_bin.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <tjp/core/io/bin/IO.h>
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
|
||||
namespace tjp::core {
|
||||
namespace {
|
||||
|
||||
struct VV : std::vector<int>
|
||||
{
|
||||
} ;
|
||||
|
||||
template<typename IO>
|
||||
void io_bin(IO &io, VV &v)
|
||||
{
|
||||
std::string b = "b";
|
||||
io.any(
|
||||
b
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
struct VS {
|
||||
int x;
|
||||
} ;
|
||||
|
||||
bool operator ==(const VS &l, const VS &r)
|
||||
{
|
||||
return l.x == r.x;
|
||||
}
|
||||
|
||||
std::ostream &operator <<(std::ostream &o, const VS &v)
|
||||
{
|
||||
return o << v.x;
|
||||
}
|
||||
|
||||
std::istream &operator >>(std::istream &i, VS &v)
|
||||
{
|
||||
return i >> v.x;
|
||||
}
|
||||
|
||||
struct A {
|
||||
bool w;
|
||||
int x;
|
||||
std::string s;
|
||||
|
||||
std::vector<int> y;
|
||||
std::vector<std::vector<int>> z;
|
||||
std::string empty;
|
||||
std::vector<int> v_b;
|
||||
VS v_s;
|
||||
|
||||
bool operator ==(const A &rhs) const
|
||||
{
|
||||
return
|
||||
w == rhs.w &&
|
||||
x == rhs.x &&
|
||||
y == rhs.y &&
|
||||
z == rhs.z &&
|
||||
v_b == rhs.v_b &&
|
||||
v_s == rhs.v_s;
|
||||
}
|
||||
} ;
|
||||
|
||||
struct B {
|
||||
int i;
|
||||
} ;
|
||||
|
||||
template<typename IO>
|
||||
void io_bin(IO &io, A &a)
|
||||
{
|
||||
io.object(
|
||||
"w", a.w, "x", a.x, "y", a.y, "z", a.z, "s", a.s, "e", a.empty,
|
||||
"v_b", makeBinaryEncoder(a.v_b),
|
||||
"v_s", makeStringEncoder(a.v_s)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename IO>
|
||||
void io_bin(IO &io, VS &v)
|
||||
{
|
||||
io.object(
|
||||
"x", v.x
|
||||
);
|
||||
}
|
||||
|
||||
SCENARIO("io bin serialization", "[core::io]" )
|
||||
{
|
||||
GIVEN("maliscous size")
|
||||
{
|
||||
io::bin::Size s;
|
||||
s = 10000;
|
||||
auto a = io::bin::serialize(s);
|
||||
REQUIRE_THROWS(io::bin::deserialize<String>(a));
|
||||
REQUIRE_THROWS(io::bin::deserialize<Vector<int>>(a));
|
||||
}
|
||||
|
||||
GIVEN("a custom array with serializer")
|
||||
{
|
||||
auto v = VV();
|
||||
auto vs = io::bin::serialize(v);
|
||||
std::string b = "b";
|
||||
auto vsi = io::bin::serialize(b);
|
||||
|
||||
REQUIRE(vs == vsi);
|
||||
|
||||
v = io::bin::deserialize<VV>(vs);
|
||||
REQUIRE(v.empty());
|
||||
}
|
||||
|
||||
GIVEN("interesting -1 s64")
|
||||
{
|
||||
s64 a = -1;
|
||||
|
||||
WHEN("serialize")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
|
||||
THEN("size is one byte")
|
||||
{
|
||||
REQUIRE(sa.size() == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("interesting u32s")
|
||||
{
|
||||
typedef std::vector<u32> Numbers;
|
||||
Numbers a { (u32)-1, (u32)0, (u32)1 };
|
||||
|
||||
WHEN("serialize and deserializee")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<Numbers>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("interesting s32s")
|
||||
{
|
||||
typedef s32 Number;
|
||||
typedef std::vector<Number> Numbers;
|
||||
Numbers a { (Number)-1, (Number)0, (Number)1 };
|
||||
|
||||
WHEN("serialize and deserializee")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<Numbers>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("vector of interesting s64 integers")
|
||||
{
|
||||
typedef std::vector<u64> Numbers;
|
||||
Numbers a { 0xF, 0xF, 0xFF, 0xFFF, 0xFFFF, 0xFFFFF, 0xFFFFFF, 0xFFFFFFF, 0xFFFFFFFF };
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<Numbers>(std::move(sa));
|
||||
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("a structure")
|
||||
{
|
||||
A a = {
|
||||
true, 13, std::string("hi"),
|
||||
{ 2, 3, 4 },
|
||||
{ { 1, 2, 3 }, { 4, 5, 6 } },
|
||||
.empty = {},
|
||||
.v_b = { 1, 2, 3, 4, 5, 6, 7 },
|
||||
.v_s = { 1 }
|
||||
};
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<A>(std::move(sa));
|
||||
|
||||
bool same = a == a_;
|
||||
|
||||
REQUIRE(same);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("an empty structure")
|
||||
{
|
||||
A a = { true, 13 };
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<A>(std::move(sa));
|
||||
|
||||
bool same = a == a_;
|
||||
|
||||
REQUIRE(same);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("a vector of bool")
|
||||
{
|
||||
typedef std::vector<bool> Bools;
|
||||
Bools a { true, false, true, true };
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<Bools>(std::move(sa));
|
||||
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("a vector of char")
|
||||
{
|
||||
typedef std::vector<char> Chars;
|
||||
Chars a { 'a', 'b', 'c', 'd' };
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<Chars>(std::move(sa));
|
||||
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("a vector of double including infinities")
|
||||
{
|
||||
typedef std::vector<double> Doubles;
|
||||
Doubles a { 1.0, 1.5, 2.0, 2.5, std::numeric_limits<double>::infinity(), -std::numeric_limits<double>::infinity() };
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<Doubles>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("NAN")
|
||||
{
|
||||
double a = std::numeric_limits<double>::quiet_NaN();
|
||||
|
||||
WHEN("serialize and deserialize, is same")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<double>(std::move(sa));
|
||||
|
||||
REQUIRE(std::isnan(a_));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GIVEN("an empty vector")
|
||||
{
|
||||
typedef std::vector<int> V;
|
||||
V a;
|
||||
|
||||
WHEN("serialize and deserialize")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<V>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("an empty string")
|
||||
{
|
||||
typedef std::string V;
|
||||
V a;
|
||||
|
||||
WHEN("serialize and deserialize")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<V>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("same string multiple times")
|
||||
{
|
||||
typedef std::vector<std::string> V;
|
||||
V a = { "a", "a", "a", "a", "b", "c", "c", "c", "b", "", "" };
|
||||
|
||||
WHEN("serialize and deserialize")
|
||||
{
|
||||
auto sa = io::bin::serialize(a);
|
||||
auto a_ = io::bin::deserialize<V>(std::move(sa));
|
||||
|
||||
THEN("is same")
|
||||
{
|
||||
REQUIRE(a_ == a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GIVEN("multiple embeds")
|
||||
{
|
||||
typedef std::vector<std::string> V;
|
||||
V a = { "a", "a", "a", "a", "b", "c", "c", "c", "b", "", "" };
|
||||
|
||||
WHEN("serialize and deserialize")
|
||||
{
|
||||
io::bin::Writer<io::bin::memory_out> e {};
|
||||
e.any(a);
|
||||
|
||||
io::bin::Writer<io::bin::memory_out> w {};
|
||||
w.embed(e);
|
||||
w.embed(e);
|
||||
w.embed(e);
|
||||
auto numExpected = 3;
|
||||
|
||||
auto sa = w.serialization();
|
||||
|
||||
io::bin::Reader<io::bin::memory_in> r(io::bin::memory_in(sa.data(), sa.size()));
|
||||
|
||||
int count = 0;
|
||||
while (r)
|
||||
{
|
||||
auto e = r.embed();
|
||||
auto a_ = io::bin::deserializeFrom<V>(e);
|
||||
|
||||
REQUIRE(a_ == a);
|
||||
count++;
|
||||
}
|
||||
|
||||
REQUIRE(count == numExpected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
179
tjp/core/io/bin/_tests/IO_bin_versioning.cpp
Normal file
179
tjp/core/io/bin/_tests/IO_bin_versioning.cpp
Normal file
@@ -0,0 +1,179 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <tjp/core/io/bin/IO.h>
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
|
||||
namespace tjp::core {
|
||||
namespace {
|
||||
|
||||
struct A_0 {
|
||||
char v;
|
||||
} ;
|
||||
|
||||
template<typename IO>
|
||||
void io_ (IO &io, A_0 &v)
|
||||
{
|
||||
io.object("v", v.v);
|
||||
}
|
||||
|
||||
struct A_1 {
|
||||
int v;
|
||||
};
|
||||
|
||||
template<typename IO>
|
||||
void io_ (IO &io, A_1 &v)
|
||||
{
|
||||
io.object("v", v.v);
|
||||
}
|
||||
|
||||
|
||||
struct A_2 {
|
||||
long v;
|
||||
} ;
|
||||
|
||||
template<typename IO>
|
||||
void io_ (IO &io, A_2 &v)
|
||||
{
|
||||
io.object("v", v.v);
|
||||
}
|
||||
|
||||
|
||||
struct A_3 {
|
||||
char v;
|
||||
} ;
|
||||
|
||||
template<typename IO>
|
||||
void io_ (IO &io, A_3 &v)
|
||||
{
|
||||
io.object("v", v.v);
|
||||
}
|
||||
|
||||
|
||||
A_1 convert(A_0 a)
|
||||
{
|
||||
return { (int)a.v + 1};
|
||||
}
|
||||
|
||||
A_2 convert(A_1 a)
|
||||
{
|
||||
return { (long)a.v + 1 };
|
||||
}
|
||||
|
||||
A_3 convert(A_2 a)
|
||||
{
|
||||
return { (char)(a.v + 1) };
|
||||
}
|
||||
|
||||
using VersionNumber = s8;
|
||||
|
||||
template<typename T>
|
||||
struct Version {
|
||||
static constexpr VersionNumber V = 0;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Version<A_3> {
|
||||
typedef A_2 Previous;
|
||||
static constexpr VersionNumber V = 3;
|
||||
} ;
|
||||
|
||||
template<>
|
||||
struct Version<A_2> {
|
||||
typedef A_1 Previous;
|
||||
static constexpr VersionNumber V = 2;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Version<A_1> {
|
||||
typedef A_0 Previous;
|
||||
static constexpr VersionNumber V = 1;
|
||||
} ;
|
||||
|
||||
template<typename T, typename IO>
|
||||
T deserialize_version(IO &io, VersionNumber v)
|
||||
{
|
||||
if (v == Version<T>::V)
|
||||
{
|
||||
T t;
|
||||
io.any(t);
|
||||
return t;
|
||||
}
|
||||
else
|
||||
{
|
||||
if constexpr(Version<T>::V == 0)
|
||||
{
|
||||
throw Exception { "Unknown version" };
|
||||
}
|
||||
else
|
||||
{
|
||||
return convert(deserialize_version<typename Version<T>::Previous>(io, v));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename S>
|
||||
T deserialize_version(const S &s)
|
||||
{
|
||||
io::bin::Reader<io::bin::memory_in> io(io::bin::memory_in(s.data(), s.size()));
|
||||
|
||||
s8 v;
|
||||
io.object("v", v);
|
||||
|
||||
return deserialize_version<T>(io, v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
auto serialize_version(const T &t)
|
||||
{
|
||||
io::bin::Writer<io::bin::memory_out> io;
|
||||
s8 v = Version<T>::V;
|
||||
io.object("v", v);
|
||||
|
||||
io.any(t);
|
||||
|
||||
return io.serialization();
|
||||
}
|
||||
|
||||
SCENARIO("io bin serialization with versioning", "[core::io]" )
|
||||
{
|
||||
GIVEN("versioned struct 0")
|
||||
{
|
||||
A_0 a;
|
||||
a.v = 0;
|
||||
|
||||
WHEN("serialize")
|
||||
{
|
||||
auto sa = serialize_version(a);
|
||||
|
||||
THEN("deserialize as a version A_0")
|
||||
{
|
||||
auto b = deserialize_version<A_0>(sa);
|
||||
REQUIRE(b.v == 0);
|
||||
}
|
||||
|
||||
THEN("deserialize as a version A_1")
|
||||
{
|
||||
auto b = deserialize_version<A_1>(sa);
|
||||
REQUIRE(b.v == 1);
|
||||
}
|
||||
|
||||
THEN("deserialize as a version A_2")
|
||||
{
|
||||
auto b = deserialize_version<A_2>(sa);
|
||||
REQUIRE(b.v == 2);
|
||||
}
|
||||
|
||||
THEN("deserialize as a version A_3")
|
||||
{
|
||||
auto b = deserialize_version<A_3>(sa);
|
||||
REQUIRE(b.v == 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
56
tjp/core/io/bin/_tests/Ptr+IO.cpp
Normal file
56
tjp/core/io/bin/_tests/Ptr+IO.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#include <tjp/core/ptr/Ptr.hpp>
|
||||
#include <tjp/core/ptr/Ptr+IO.h>
|
||||
#include <tjp/core/io/bin/IO.h>
|
||||
|
||||
#include <tjp/core/testing/catch.hpp>
|
||||
#include <tjp/core/testing/NO_WHEN.h>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
|
||||
namespace memory {
|
||||
namespace test_compile {
|
||||
|
||||
struct A
|
||||
{
|
||||
int i;
|
||||
} ;
|
||||
|
||||
template<typename IO>
|
||||
void io_bin(IO &io, A &a)
|
||||
{
|
||||
io.object("i", a.i);
|
||||
}
|
||||
|
||||
SCENARIO("core::ptr io")
|
||||
{
|
||||
GIVEN("a ptr")
|
||||
{
|
||||
StrongPtr<A> a = strong<A>();
|
||||
|
||||
WHEN("ptr is serialized")
|
||||
{
|
||||
auto result = io::bin::serialize(a);
|
||||
|
||||
WHEN("result is unserialized")
|
||||
{
|
||||
auto a2 = io::bin::deserialize<StrongPtr<A>>(result);
|
||||
|
||||
THEN("results are equal")
|
||||
{
|
||||
REQUIRE(a2->i == a->i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
86
tjp/core/io/bin/dictionary.h
Normal file
86
tjp/core/io/bin/dictionary.h
Normal file
@@ -0,0 +1,86 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory.h>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <tjp/core/string/Str.hpp>
|
||||
#include <tjp/core/allocator/AllocatorPool.hpp>
|
||||
#include <tjp/core/allocator/VectorPool.hpp>
|
||||
#include <tjp/core/exception/debug_throw.h>
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
using DictionaryIndex = u32;
|
||||
|
||||
template<typename T>
|
||||
struct DictionaryOut
|
||||
{
|
||||
typedef T K;
|
||||
typedef DictionaryIndex V;
|
||||
typedef std::pair<const T, V> KV;
|
||||
typedef core::AllocatorPool<KV> Allocator;
|
||||
|
||||
std::map<K, DictionaryIndex, std::less<K>, Allocator> values;
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
std::tuple<u32, bool> on(const T &t)
|
||||
{
|
||||
auto [ i, inserted ] = values.emplace(t, values.size());
|
||||
return { i->second, inserted };
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename T>
|
||||
struct DictionaryIn
|
||||
{
|
||||
std::vector<T, core::VectorPool<T>> values;
|
||||
|
||||
public:
|
||||
typedef T value_type;
|
||||
|
||||
bool has(const DictionaryIndex &i)
|
||||
{
|
||||
return i < values.size();
|
||||
}
|
||||
|
||||
const T &get(const DictionaryIndex &i)
|
||||
{
|
||||
if (i >= values.size())
|
||||
debug_throw(io::Exception("Dictionary failure"));
|
||||
|
||||
return values[i];
|
||||
}
|
||||
|
||||
T &set(const DictionaryIndex &i, const T &t)
|
||||
{
|
||||
debug_assert(i == values.size());
|
||||
values.push_back(t);
|
||||
return values.back();
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
T &emplace(const DictionaryIndex &i, Args&& ... args)
|
||||
{
|
||||
if (i != values.size())
|
||||
debug_throw(io::Exception("Dictionary failure"));
|
||||
|
||||
values.emplace_back(std::forward<Args>(args)...);
|
||||
return values.back();
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
using DictionaryOutStandard = DictionaryOut<Str>;
|
||||
using DictionaryInStandard = DictionaryIn<Str>;
|
||||
|
||||
} // namespace
|
||||
619
tjp/core/io/bin/in.h
Normal file
619
tjp/core/io/bin/in.h
Normal file
@@ -0,0 +1,619 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "in_fwd.h"
|
||||
#include "../IO+config.h"
|
||||
#include "Debug.h"
|
||||
|
||||
#include "../Exception.h"
|
||||
#include <tjp/core/algorithm/mem_copy.hpp>
|
||||
|
||||
#include <tjp/core/types/Types.h>
|
||||
#include <tjp/core/type_traits/is_mappish.hpp>
|
||||
#include <tjp/core/type_traits/is_string.hpp>
|
||||
#include <tjp/core/type_traits/is_iterable.hpp>
|
||||
#include <tjp/core/type_traits/always_false.hpp>
|
||||
#include <tjp/core/assert/debug_assert.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <memory.h>
|
||||
#include <typeinfo>
|
||||
|
||||
#include <tjp/core/ptr/Ptr.hpp>
|
||||
#include <tjp/core/system/System.h>
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_r(IO &io, T &t);
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_r_dispatch(IO &io, T &t);
|
||||
|
||||
inline
|
||||
void readFrom(std::istream &f, char *v, size_t size)
|
||||
{
|
||||
f.read(v, size);
|
||||
}
|
||||
|
||||
struct memory_in {
|
||||
const char *block;
|
||||
size_t size;
|
||||
size_t position;
|
||||
|
||||
memory_in(const Vector<char> &v) : block(v.data()), size(v.size()), position(0) {}
|
||||
|
||||
memory_in(const char *block_, size_t size_) : block(block_), size(size_), position(0) {}
|
||||
|
||||
bool eof() const {
|
||||
return position >= size;
|
||||
}
|
||||
|
||||
bool good() const {
|
||||
return !eof();
|
||||
}
|
||||
} ;
|
||||
|
||||
inline
|
||||
void validate_read_size(memory_in &f, size_t size)
|
||||
{
|
||||
if (size > f.size || f.position > f.size - size)
|
||||
{
|
||||
debug_throw(io::Exception ("Buffer underflow"));
|
||||
}
|
||||
}
|
||||
|
||||
inline
|
||||
auto skip(memory_in &f, size_t size)
|
||||
{
|
||||
validate_read_size(f, size);
|
||||
|
||||
auto *result = f.block + f.position;
|
||||
f.position += size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline
|
||||
void readFrom(memory_in &f, char *v, size_t size)
|
||||
{
|
||||
auto *start = skip(f, size);
|
||||
|
||||
core::mem_copy(v, start, size);
|
||||
}
|
||||
|
||||
struct ReaderEmptyBaseClass {
|
||||
template<typename IO, typename T>
|
||||
void dispatch_custom(IO &io, T &t) {}
|
||||
} ;
|
||||
|
||||
struct ReaderAllocatorDefault
|
||||
{
|
||||
template<typename IO, typename T>
|
||||
void allocate(IO &io, T &t)
|
||||
{
|
||||
io_ptr_allocate_default(io, t);
|
||||
}
|
||||
|
||||
typedef std::true_type is_strong_allocator;
|
||||
|
||||
template<typename T, typename ... Args>
|
||||
auto strong(Args && ...args)
|
||||
{
|
||||
return core::strong<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename Stream_, typename Base, typename Allocator_>
|
||||
class Reader : public Base
|
||||
{
|
||||
public:
|
||||
typedef Stream_ Stream;
|
||||
typedef Allocator_ Allocator;
|
||||
|
||||
Stream f;
|
||||
typedef DictionaryInStandard Dictionary;
|
||||
Dictionary dictionary;
|
||||
|
||||
#ifdef IO_BIN_USE_HEADER_CHECK
|
||||
u8 count=0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Allocator allocator;
|
||||
|
||||
template<typename T>
|
||||
void allocate(T &t)
|
||||
{
|
||||
allocator.allocate(*this, t);
|
||||
}
|
||||
|
||||
auto partial(const Serialization &s)
|
||||
{
|
||||
using ReaderT = Reader<memory_in, Base, Allocator>;
|
||||
return strong<ReaderT>(typename ReaderT::Stream(s.data(), s.size()), allocator);
|
||||
}
|
||||
|
||||
public:
|
||||
Reader (Stream &&f_, Allocator allocator) :
|
||||
f(std::move(f_)),
|
||||
allocator(std::move(allocator))
|
||||
{
|
||||
}
|
||||
|
||||
Reader (Stream &&f_) :
|
||||
f(std::move(f_))
|
||||
{
|
||||
}
|
||||
|
||||
Reader embed()
|
||||
{
|
||||
Size size;
|
||||
any(size);
|
||||
|
||||
auto *at = skip(f, size);
|
||||
return Reader(Stream(at, size), allocator);
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return f.good();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic(T &t)
|
||||
{
|
||||
read(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic_string(T &t)
|
||||
{
|
||||
read_string(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void any(T &t)
|
||||
{
|
||||
dispatch_any(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void any(T &&t)
|
||||
{
|
||||
dispatch_any(t);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void all(T&&... t)
|
||||
{
|
||||
begin();
|
||||
all_(t...);
|
||||
end();
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void object(T&&... t)
|
||||
{
|
||||
object_begin();
|
||||
object_(t...);
|
||||
object_end();
|
||||
}
|
||||
|
||||
void object_begin()
|
||||
{
|
||||
begin();
|
||||
}
|
||||
|
||||
void object_k(KeyType) {};
|
||||
|
||||
template<typename ... T>
|
||||
void object_kv(T&&... t)
|
||||
{
|
||||
object_(t...);
|
||||
}
|
||||
|
||||
void object_end()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void array(T&&... t)
|
||||
{
|
||||
begin();
|
||||
array_(t...);
|
||||
end();
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
void on_dispatch_any(T &t)
|
||||
{
|
||||
on_any(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_dispatch_custom(T &t)
|
||||
{
|
||||
Base::dispatch_custom(*this, t);
|
||||
}
|
||||
|
||||
protected:
|
||||
// ----- read
|
||||
|
||||
void read(char *data, size_t size)
|
||||
{
|
||||
readFrom(f, data, size);
|
||||
}
|
||||
|
||||
void validateRead (size_t size)
|
||||
{
|
||||
validate_read_size(f, size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void read(T &t)
|
||||
{
|
||||
read((char *)&t, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void read_string(T &t)
|
||||
{
|
||||
DictionaryIndex index;
|
||||
any(index);
|
||||
|
||||
if (dictionary.has(index))
|
||||
{
|
||||
t = T(dictionary.get(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
Size size;
|
||||
any(size);
|
||||
|
||||
validateRead(size);
|
||||
t = T(size, 0);
|
||||
read((char *)t.data(), size);
|
||||
|
||||
auto *begin = (char *)t.data();
|
||||
auto *end = begin + size;
|
||||
|
||||
dictionary.emplace(index, begin, end);
|
||||
}
|
||||
|
||||
IO_BIN_LOG("read " << t);
|
||||
}
|
||||
|
||||
// ----- dispatch out and in
|
||||
|
||||
template<typename T>
|
||||
void dispatch_any(T &t)
|
||||
{
|
||||
IO_BIN_LOG("one " << type_id<T>().name());
|
||||
|
||||
io_bin_r_dispatch(*this, t);
|
||||
}
|
||||
|
||||
void begin ()
|
||||
{
|
||||
#ifdef IO_BIN_USE_HEADER_CHECK
|
||||
u8 c;
|
||||
read(c);
|
||||
++count;
|
||||
debug_assert(c == count);
|
||||
#endif
|
||||
}
|
||||
|
||||
void end ()
|
||||
{
|
||||
#ifdef IO_BIN_USE_HEADER_CHECK
|
||||
u8 c;
|
||||
read(c);
|
||||
debug_assert(c == count);
|
||||
--count;
|
||||
#endif
|
||||
}
|
||||
|
||||
// -- handling different containers --
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
void any_no_ref(T v)
|
||||
{
|
||||
any(v);
|
||||
}
|
||||
|
||||
Size array_begin()
|
||||
{
|
||||
Size s;
|
||||
any(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void array_end()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_begin(T &t)
|
||||
{
|
||||
Size s = array_begin();
|
||||
t = std::move(T(s));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_end (T &t)
|
||||
{
|
||||
array_end();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_value(T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
auto map_begin()
|
||||
{
|
||||
Size s;
|
||||
any(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename K, typename F>
|
||||
void map_values(Size s, F &&f)
|
||||
{
|
||||
IO_BIN_LOG("map " << type_id<T>().name());
|
||||
|
||||
for (auto i=0; i<s; ++i)
|
||||
{
|
||||
K key;
|
||||
any(key);
|
||||
|
||||
f(key);
|
||||
}
|
||||
}
|
||||
|
||||
void map_end()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
void array_values(size_t size, F &&fun)
|
||||
{
|
||||
for (auto i=0;i<size;++i)
|
||||
fun();
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
void on_vector(T &t)
|
||||
{
|
||||
IO_BIN_LOG("many " << type_id<T>().name());
|
||||
on_vector_begin(t);
|
||||
|
||||
for (auto &v : t)
|
||||
on_vector_value(v);
|
||||
|
||||
on_vector_end(t);
|
||||
}
|
||||
|
||||
void on_vector(std::vector<bool> &t)
|
||||
{
|
||||
typedef std::vector<bool> T;
|
||||
IO_BIN_LOG("many " << type_id<T>().name());
|
||||
|
||||
on_vector_begin(t);
|
||||
|
||||
for (auto i = t.begin(); i!=t.end(); ++i)
|
||||
any_no_ref<typename T::iterator::reference>(*i);
|
||||
|
||||
on_vector_end(t);
|
||||
}
|
||||
|
||||
template<typename V, typename T>
|
||||
void on_set(T &t)
|
||||
{
|
||||
IO_BIN_LOG("set " << type_id<T>().name());
|
||||
|
||||
Size s;
|
||||
any(s);
|
||||
|
||||
for (auto i=0; i<s; ++i)
|
||||
{
|
||||
V v;
|
||||
any(v);
|
||||
|
||||
t.insert(std::move(v));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_map(T &t)
|
||||
{
|
||||
IO_BIN_LOG("map " << type_id<T>().name());
|
||||
|
||||
Size s;
|
||||
any(s);
|
||||
|
||||
for (auto i=0; i<s; ++i)
|
||||
{
|
||||
typename T::key_type key;
|
||||
any(key);
|
||||
|
||||
typename T::mapped_type value;
|
||||
any(value);
|
||||
|
||||
t.emplace(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_optional(T &t)
|
||||
{
|
||||
IO_BIN_LOG("map " << type_id<T>().name());
|
||||
|
||||
bool s;
|
||||
any(s);
|
||||
|
||||
if (s)
|
||||
{
|
||||
t.emplace();
|
||||
any(*t);
|
||||
}
|
||||
}
|
||||
|
||||
// -- dispatching different containers --
|
||||
|
||||
template<typename T, typename std::enable_if<!is_iterable<T>::value || is_string<T>::value, T>::type* = nullptr>
|
||||
void on_any(T &t)
|
||||
{
|
||||
io_dispatch_failed(*this, t);
|
||||
}
|
||||
|
||||
template<typename V, typename S>
|
||||
void on_any(std::set<V, S> &t)
|
||||
{
|
||||
on_set<V>(t);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value && !is_mappish<T>::value && !is_string<T>::value, T>::type* = nullptr>
|
||||
void on_any(T &t)
|
||||
{
|
||||
on_vector(t);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value && is_mappish<T>::value, T>::type* = nullptr>
|
||||
void on_any(T &t)
|
||||
{
|
||||
on_map(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_any(std::optional<T> &t)
|
||||
{
|
||||
on_optional(t);
|
||||
}
|
||||
|
||||
// void on_any(variant::Variant &t)
|
||||
// {
|
||||
// on_variant(t);
|
||||
// }
|
||||
//
|
||||
// -- handling all --
|
||||
|
||||
template<typename T>
|
||||
void all_(T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void all_(Args&& ...args)
|
||||
{
|
||||
call_expand_1(
|
||||
[&](auto && ...args) { any(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
// ---- handling object array ----
|
||||
|
||||
void object_()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void object__(KeyType, T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void object_(Args&& ...args)
|
||||
{
|
||||
call_expand_2(
|
||||
[&](auto && ...args) { object__(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void array__(T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void array_(Args&& ...args)
|
||||
{
|
||||
call_expand_1(
|
||||
[&](auto && ...args) { array__(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
public:
|
||||
bool null ()
|
||||
{
|
||||
return f.is_null();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Optional<T> optional(T &t, bool write=true)
|
||||
{
|
||||
return io::bin::optional(t, write);
|
||||
}
|
||||
|
||||
template<typename T, typename B>
|
||||
Pointer<B, T> pointer(B &b)
|
||||
{
|
||||
return io::bin::Pointer<B, T> { &b };
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Custom<T> custom(T &t)
|
||||
{
|
||||
return Custom<T> { &t };
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
// -----------------------
|
||||
|
||||
template<typename T, typename Reader>
|
||||
void deserializeFrom (Reader &reader, T &t);
|
||||
|
||||
template<typename T, typename Reader>
|
||||
T deserializeFrom (Reader &reader);
|
||||
|
||||
template<typename R, typename T, typename Allocator>
|
||||
T deserialize (const Serialization &data, Allocator);
|
||||
|
||||
template<typename R, typename T>
|
||||
T deserialize (const Serialization &data);
|
||||
|
||||
template<typename T>
|
||||
T deserialize (const Serialization &data);
|
||||
|
||||
template<typename T>
|
||||
void deserialize (const Serialization &data, T &t);
|
||||
|
||||
template<typename T>
|
||||
void fromFile (const std::string &fileName, T &t);
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "in.inl"
|
||||
86
tjp/core/io/bin/in.inl
Normal file
86
tjp/core/io/bin/in.inl
Normal file
@@ -0,0 +1,86 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IO.h"
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_r(IO &io_, T &t)
|
||||
{
|
||||
if constexpr (has_io_bin<IO, T>::value)
|
||||
{
|
||||
io_bin(io_, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
io_r(io_, t);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_r_dispatch(IO &io_, T &t)
|
||||
{
|
||||
io_bin_r(io_, t);
|
||||
}
|
||||
|
||||
template<typename T, typename Reader>
|
||||
void deserializeFrom (Reader &reader, T &t)
|
||||
{
|
||||
reader.any(t);
|
||||
}
|
||||
|
||||
template<typename T, typename Reader>
|
||||
T deserializeFrom (Reader &reader)
|
||||
{
|
||||
T t;
|
||||
deserializeFrom(reader, t);
|
||||
return t;
|
||||
}
|
||||
|
||||
template<typename R, typename T, typename Allocator>
|
||||
T deserialize (const Serialization &data, Allocator allocator)
|
||||
{
|
||||
R reader(typename R::Stream(data.data(), data.size()), allocator);
|
||||
return deserializeFrom<T>(reader);
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
T deserialize (const Serialization &data)
|
||||
{
|
||||
R reader(typename R::Stream(data.data(), data.size()));
|
||||
return deserializeFrom<T>(reader);
|
||||
}
|
||||
|
||||
template<typename R, typename T>
|
||||
void deserialize (const Serialization &data, T &t)
|
||||
{
|
||||
R reader(typename R::Stream(data.data(), data.size()));
|
||||
deserializeFrom<T>(reader, t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T deserialize (const Serialization &data)
|
||||
{
|
||||
return deserialize<Reader<memory_in>, T>(data);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T deserialize (const Serialization::value_type *begin, size_t size)
|
||||
{
|
||||
using R = Reader<memory_in>;
|
||||
R reader(typename R::Stream(begin, size));
|
||||
return deserializeFrom<T>(reader);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void deserialize (const Serialization &data, T &t)
|
||||
{
|
||||
deserialize<Reader<memory_in>, T>(data, t);
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
18
tjp/core/io/bin/in_file.hpp
Normal file
18
tjp/core/io/bin/in_file.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
template<typename T>
|
||||
void fromFile (const std::string &fileName, T &t)
|
||||
{
|
||||
Reader<std::ifstream> reader(fileName.c_str(), std::ios::binary);
|
||||
reader.any(t);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
16
tjp/core/io/bin/in_fwd.h
Normal file
16
tjp/core/io/bin/in_fwd.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
struct memory_in;
|
||||
struct ReaderEmptyBaseClass;
|
||||
struct ReaderAllocatorDefault;
|
||||
|
||||
template<typename Stream_, typename Base=ReaderEmptyBaseClass, typename Allocator_=ReaderAllocatorDefault>
|
||||
class Reader;
|
||||
|
||||
} // namespace
|
||||
26
tjp/core/io/bin/json.inl
Normal file
26
tjp/core/io/bin/json.inl
Normal file
@@ -0,0 +1,26 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace nlohmann {
|
||||
|
||||
template<typename IO>
|
||||
void io_bin_w(IO &io, const json &j)
|
||||
{
|
||||
auto cbor = json::to_cbor(j);
|
||||
io.all(cbor);
|
||||
}
|
||||
|
||||
template<typename IO>
|
||||
void io_bin_r(IO &io, json &j)
|
||||
{
|
||||
std::vector<unsigned char> v;
|
||||
io.all(v);
|
||||
j = json::from_cbor(v);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
543
tjp/core/io/bin/out.h
Normal file
543
tjp/core/io/bin/out.h
Normal file
@@ -0,0 +1,543 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "out_fwd.h"
|
||||
#include "../IO+config.h"
|
||||
|
||||
#include "Debug.h"
|
||||
#include <tjp/core/string/Str.hpp>
|
||||
#include <tjp/core/algorithm/mem_copy.hpp>
|
||||
#include <tjp/core/rtti/RTTI.hpp>
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_w(IO &io, const T &t);
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_w_dispatch(IO &io, const T &t);
|
||||
|
||||
inline
|
||||
void writeTo(std::ostream &f, const char *v, size_t size)
|
||||
{
|
||||
f.write(v, size);
|
||||
}
|
||||
|
||||
struct memory_out_ptr
|
||||
{
|
||||
using Block = std::vector<char>;
|
||||
Block *block;
|
||||
|
||||
memory_out_ptr(Block *block_) :
|
||||
block(block_)
|
||||
{
|
||||
}
|
||||
|
||||
Block &serialization ()
|
||||
{
|
||||
return *block;
|
||||
}
|
||||
} ;
|
||||
|
||||
template<size_t InitialSize>
|
||||
struct memory_out_sized
|
||||
{
|
||||
using Block = std::vector<char>;
|
||||
Block block;
|
||||
|
||||
memory_out_sized(Block &&block_) :
|
||||
block(block_)
|
||||
{
|
||||
}
|
||||
|
||||
memory_out_sized()
|
||||
{
|
||||
block.reserve(InitialSize);
|
||||
}
|
||||
|
||||
Block &serialization ()
|
||||
{
|
||||
return block;
|
||||
}
|
||||
} ;
|
||||
|
||||
using memory_out = memory_out_sized<4096>;
|
||||
|
||||
inline
|
||||
auto serialization_of(memory_out &m)
|
||||
{
|
||||
return m.serialization();
|
||||
}
|
||||
|
||||
inline
|
||||
auto serialization_of(memory_out_ptr &m)
|
||||
{
|
||||
return m.serialization();
|
||||
}
|
||||
|
||||
inline
|
||||
auto serialization_of(std::ostream &f) {}
|
||||
|
||||
inline
|
||||
void writeTo(memory_out_ptr &f, const char *v, size_t size)
|
||||
{
|
||||
auto then = f.block->size();
|
||||
f.block->resize(then + size);
|
||||
core::mem_copy(f.block->data() + then, v, size);
|
||||
}
|
||||
|
||||
inline
|
||||
void writeTo(memory_out &f, const char *v, size_t size)
|
||||
{
|
||||
auto then = f.block.size();
|
||||
f.block.resize(then + size);
|
||||
core::mem_copy(f.block.data() + then, v, size);
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
|
||||
struct EmptyWriterBaseClass {
|
||||
template<typename IO, typename T>
|
||||
void dispatch_custom(IO &io, T &t) {}
|
||||
} ;
|
||||
|
||||
struct WriterAllocatorBase {
|
||||
template<typename T>
|
||||
void allocate(T &t)
|
||||
{
|
||||
io_ptr_allocate_default(*this, t);
|
||||
}
|
||||
} ;
|
||||
|
||||
template<typename S, typename Base, typename Allocator>
|
||||
class Writer :
|
||||
public Base,
|
||||
public Allocator
|
||||
{
|
||||
public:
|
||||
S f;
|
||||
typedef DictionaryOutStandard Dictionary;
|
||||
Dictionary dictionary;
|
||||
|
||||
#ifdef IO_BIN_USE_HEADER_CHECK
|
||||
u8 count=0;
|
||||
#endif
|
||||
|
||||
public:
|
||||
auto partial()
|
||||
{
|
||||
return Writer<memory_out, Base>();
|
||||
}
|
||||
|
||||
auto serialization ()
|
||||
{
|
||||
return serialization_of(f);
|
||||
}
|
||||
|
||||
auto partial_serialization ()
|
||||
{
|
||||
return serialization();
|
||||
}
|
||||
|
||||
public:
|
||||
template<class... Args>
|
||||
Writer (Args&&... args) :
|
||||
f(args...)
|
||||
{
|
||||
}
|
||||
|
||||
void embed(Writer &writer)
|
||||
{
|
||||
any(writer.f.serialization());
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return f.good();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic(const T &t)
|
||||
{
|
||||
write(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void intrinsic_string(const T &t)
|
||||
{
|
||||
write_string(t);
|
||||
}
|
||||
|
||||
void intrinsic(const char *memory, size_t size)
|
||||
{
|
||||
write(memory, size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void any(const T &t)
|
||||
{
|
||||
dispatch_any(t);
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void all(T&&... t)
|
||||
{
|
||||
begin();
|
||||
all_(t...);
|
||||
end();
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void object(T&&... t)
|
||||
{
|
||||
object_begin();
|
||||
object_(t...);
|
||||
object_end();
|
||||
}
|
||||
|
||||
void object_begin()
|
||||
{
|
||||
begin();
|
||||
}
|
||||
|
||||
void object_k(KeyType) {};
|
||||
|
||||
template<typename ... T>
|
||||
void object_kv(T&&... t)
|
||||
{
|
||||
object_(t...);
|
||||
}
|
||||
|
||||
void object_end()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
auto map_begin(Size size)
|
||||
{
|
||||
begin();
|
||||
any(size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
template<typename K, typename F>
|
||||
void map_value_with(const K &key, F &&f)
|
||||
{
|
||||
any(key);
|
||||
f();
|
||||
}
|
||||
|
||||
template<typename K, typename V>
|
||||
void map_value(const K &key, const V &value)
|
||||
{
|
||||
any(key);
|
||||
any(value);
|
||||
}
|
||||
|
||||
void map_end()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
template<typename ... T>
|
||||
void array(T&&... t)
|
||||
{
|
||||
begin();
|
||||
array_(t...);
|
||||
end();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_dispatch_any(const T &t)
|
||||
{
|
||||
on_any(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_dispatch_custom(T &t)
|
||||
{
|
||||
Base::dispatch_custom(*this, t);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
void dispatch_any(const T &t)
|
||||
{
|
||||
IO_BIN_LOG("any " << type_id<T>().name());
|
||||
|
||||
io_bin_w_dispatch(*this, t);
|
||||
}
|
||||
|
||||
void begin()
|
||||
{
|
||||
#ifdef IO_BIN_USE_HEADER_CHECK
|
||||
write(++count);
|
||||
#endif
|
||||
}
|
||||
|
||||
void end()
|
||||
{
|
||||
#ifdef IO_BIN_USE_HEADER_CHECK
|
||||
write(count--);
|
||||
#endif
|
||||
}
|
||||
|
||||
void write(const char *data, size_t size)
|
||||
{
|
||||
writeTo(f, data, size);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(const T &t)
|
||||
{
|
||||
IO_BIN_LOG("write " << t);
|
||||
write((const char *)&t, sizeof(T));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void write_string(const T &t)
|
||||
{
|
||||
auto [index, should_write] = dictionary.on(t);
|
||||
any(index);
|
||||
|
||||
if (should_write)
|
||||
{
|
||||
any((Size)t.size());
|
||||
write(t.data(), t.size());
|
||||
}
|
||||
}
|
||||
|
||||
// -- handling different containers --
|
||||
|
||||
public:
|
||||
auto array_begin(Size s)
|
||||
{
|
||||
any(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void array_end()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_begin (const T &t)
|
||||
{
|
||||
array_begin((Size)t.size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_end (const T &t)
|
||||
{
|
||||
array_end();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_vector_value(const T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
void on_vector(const T &t)
|
||||
{
|
||||
IO_BIN_LOG("many " << type_id<T>().name());
|
||||
on_vector_begin(t);
|
||||
|
||||
// this fails for std::vector<bool> on some platforms, because vector bool
|
||||
// does some magic to compress to a bit field, and then the iterator
|
||||
// doesn't pass back a reference
|
||||
// for (const auto &v : t)
|
||||
// any(v);
|
||||
|
||||
for (auto i = t.begin(); i!=t.end(); ++i)
|
||||
on_vector_value(*i);
|
||||
|
||||
on_vector_end(t);
|
||||
}
|
||||
|
||||
void on_vector(const std::vector<u8> &t)
|
||||
{
|
||||
any((Size)t.size());
|
||||
intrinsic((char *)t.data(), t.size());
|
||||
}
|
||||
|
||||
void on_vector(const std::vector<s8> &t)
|
||||
{
|
||||
any((Size)t.size());
|
||||
intrinsic((char *)t.data(), t.size());
|
||||
}
|
||||
|
||||
void on_vector(const std::vector<char> &t)
|
||||
{
|
||||
any((Size)t.size());
|
||||
intrinsic((char *)t.data(), t.size());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_map(const T &t)
|
||||
{
|
||||
IO_BIN_LOG("map " << type_id<T>().name());
|
||||
any((Size)t.size());
|
||||
for (const auto &v : t)
|
||||
{
|
||||
any(v.first);
|
||||
any(v.second);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_optional(const T &t)
|
||||
{
|
||||
bool s = t.has_value();
|
||||
any(s);
|
||||
|
||||
if (s)
|
||||
{
|
||||
any(*t);
|
||||
}
|
||||
}
|
||||
|
||||
// -- dispatching different containers --
|
||||
|
||||
template<typename T, typename std::enable_if<!is_iterable<T>::value || is_string<T>::value, T>::type* = nullptr>
|
||||
void on_any(const T &t)
|
||||
{
|
||||
io_dispatch_failed(*this, t);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value && !is_mappish<T>::value && !is_string<T>::value, T>::type* = nullptr>
|
||||
void on_any(const T &t)
|
||||
{
|
||||
on_vector(t);
|
||||
}
|
||||
|
||||
template<typename T, typename std::enable_if<is_iterable<T>::value && is_mappish<T>::value, T>::type* = nullptr>
|
||||
void on_any(const T &t)
|
||||
{
|
||||
on_map(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void on_any(const std::optional<T> &t)
|
||||
{
|
||||
on_optional(t);
|
||||
}
|
||||
|
||||
// void on_any(const variant::Variant &t)
|
||||
// {
|
||||
// on_variant(t);
|
||||
// }
|
||||
//
|
||||
// ---- handling all ----
|
||||
|
||||
template<typename T>
|
||||
void all_(const T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
template<typename T, typename ... Rest>
|
||||
void all_(const T &t, Rest&&... rest)
|
||||
{
|
||||
any(t);
|
||||
all_(rest...);
|
||||
}
|
||||
|
||||
// ---- handling object array ----
|
||||
|
||||
void object_()
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void object__(KeyType, const T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void object_(Args&& ...args)
|
||||
{
|
||||
call_expand_2(
|
||||
[&](auto && ...args) { object__(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void array__(const T &t)
|
||||
{
|
||||
any(t);
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
void array_(Args&& ...args)
|
||||
{
|
||||
call_expand_1(
|
||||
[&](auto && ...args) { array__(std::forward<decltype(args)>(args)...); },
|
||||
std::forward<Args>(args)...
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
// ----- miscellaneous -----
|
||||
|
||||
template<typename T>
|
||||
Optional<T> optional(T &t, bool write=true)
|
||||
{
|
||||
return io::bin::optional(t, write);
|
||||
}
|
||||
|
||||
template<typename T, typename B, typename std::enable_if<!std::is_class<T>::value, T>::type* = nullptr>
|
||||
Pointer<B, T> pointer(B &b)
|
||||
{
|
||||
return io::bin::Pointer<B, T> {
|
||||
&b, b != nullptr
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T, typename B, typename std::enable_if<std::is_class<T>::value, T>::type* = nullptr>
|
||||
Pointer<B, T> pointer(B &b)
|
||||
{
|
||||
return io::bin::Pointer<B, T> {
|
||||
&b, dynamic_cast_ptr<T>(b) != nullptr
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
Custom<T> custom(const T &t)
|
||||
{
|
||||
return Custom<T> { const_cast<T*>(&t) };
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
// ----------
|
||||
|
||||
template<typename W, typename T>
|
||||
Serialization serialize (const T &t);
|
||||
|
||||
template<typename T>
|
||||
Serialization serialize (const T &t);
|
||||
|
||||
template<typename T>
|
||||
void serializeTo (Serialization &serialization, const T &t);
|
||||
|
||||
template<typename W, typename T>
|
||||
Serialization serializeTo(W &w, const T &t);
|
||||
|
||||
template<typename T>
|
||||
void toFile (const std::string &fileName, const T &t);
|
||||
|
||||
} // namespace
|
||||
|
||||
#include "out.inl"
|
||||
65
tjp/core/io/bin/out.inl
Normal file
65
tjp/core/io/bin/out.inl
Normal file
@@ -0,0 +1,65 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IO.h"
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_w(IO &io_, const T &t)
|
||||
{
|
||||
typedef typename std::remove_const<T>::type TC;
|
||||
|
||||
if constexpr (has_io_bin<IO, T>::value)
|
||||
{
|
||||
auto &t_ = *const_cast<TC *>(&t);
|
||||
io_bin(io_, t_);
|
||||
}
|
||||
else
|
||||
{
|
||||
io_w(io_, t);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename IO, typename T>
|
||||
void io_bin_w_dispatch(IO &io_, const T &t)
|
||||
{
|
||||
io_bin_w(io_, t);
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
Serialization serialize (const T &t)
|
||||
{
|
||||
W writer;
|
||||
writer.any(t);
|
||||
|
||||
return std::move(writer.serialization());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Serialization serialize (const T &t)
|
||||
{
|
||||
return serialize<Writer<memory_out>>(t);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void serializeTo (Serialization &serialization, const T &t)
|
||||
{
|
||||
memory_out bout { std::move(serialization) };
|
||||
Writer<memory_out> writer(bout);
|
||||
writer.any(t);
|
||||
|
||||
serialization = writer.serialization();
|
||||
}
|
||||
|
||||
template<typename W, typename T>
|
||||
Serialization serializeTo(W &w, const T &t)
|
||||
{
|
||||
w.any(t);
|
||||
return w.serialization();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
18
tjp/core/io/bin/out_file.hpp
Normal file
18
tjp/core/io/bin/out_file.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace tjp::core::io::json {
|
||||
|
||||
template<typename T>
|
||||
void toFile (const std::string &fileName, const T &t)
|
||||
{
|
||||
Writer<std::ofstream> writer(fileName.c_str(), std::ios::binary);
|
||||
writer.any(t);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
23
tjp/core/io/bin/out_fwd.h
Normal file
23
tjp/core/io/bin/out_fwd.h
Normal file
@@ -0,0 +1,23 @@
|
||||
// License: Modified MIT (NON-AI)
|
||||
// See the LICENSE file in the root directory for license information.
|
||||
// Copyright 2025 Timothy Prepscius
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace tjp::core::io::bin {
|
||||
|
||||
template<size_t InitialSize>
|
||||
struct memory_out_sized;
|
||||
|
||||
using memory_out = memory_out_sized<4096>;
|
||||
|
||||
|
||||
struct EmptyWriterBaseClass;
|
||||
struct WriterAllocatorBase;
|
||||
|
||||
template<typename S, typename Base=EmptyWriterBaseClass, typename Allocator=WriterAllocatorBase>
|
||||
class Writer;
|
||||
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user