Seed Core_IO from doc snapshot

This commit is contained in:
Local Seed
2026-02-11 16:14:01 -05:00
commit 25a8076064
53 changed files with 11472 additions and 0 deletions

14
tjp/core/io/Exception.h Normal file
View File

@@ -0,0 +1,14 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <tjp/core/exception/Exception.hpp>
namespace tjp::core::io {
DECLARE_EXCEPTION(Exception);
} // namespace

56
tjp/core/io/IO+config.h Executable file
View 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
#pragma once
#define IO_USE_NON_RECURSIVE_APPLY
#ifdef IO_USE_NON_RECURSIVE_APPLY
#include "calls.hpp"
#endif
namespace tjp::core::io {
#ifdef IO_USE_NON_RECURSIVE_APPLY
template<typename F, typename ... Args>
void call_expand_1(
F &&f,
Args && ...args
)
{
call_f_1s(std::forward<F>(f), std::forward<Args>(args)...);
}
template<typename F, typename ... Args>
void call_expand_2(
F &&f,
Args && ...args
)
{
call_f_2s(std::forward<F>(f), std::forward<Args>(args)...);
}
#else
template<typename F, typename A, typename ... Args>
void call_expand_1(F &&f, A &&a Args&& ...args)
{
f(std::forward<A>(a));
call_expand_2(std::forward<F>(f), std::forward<Args>(args)...);
}
template<typename F, typename A, typename B, typename ... Args>
void call_expand_2(F &&f, A &&a, B &&b, Args&& ...args)
{
f(std::forward<A>(a), std::forward<B>(b));
call_expand_2(std::forward<F>(f), std::forward<Args>(args)...);
}
#endif
}

4
tjp/core/io/IO.cpp Normal file
View File

@@ -0,0 +1,4 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius

49
tjp/core/io/IO.h Executable file
View File

@@ -0,0 +1,49 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <tjp/core/type_traits/always_false.hpp>
namespace tjp::core {
//namespace variant { struct Variant; }
template<typename IO, typename T>
void io_dispatch_failed(IO &io, T &t)
{
static_assert(always_false<T>::value, "io_ or (io_w and io_r) must be defined for non trivial types");
}
template<typename IO, typename T>
void io_(IO &io, T &t)
{
io.on_dispatch_any(t);
}
template<typename IO, typename T>
void io_w(IO &io, const T &t)
{
io_(io, const_cast<T &>(t));
}
template<typename IO, typename T>
void io_w_(IO &io, const T &t)
{
io_w(io, t);
}
template<typename IO, typename T>
void io_r(IO &io, T &t)
{
io_(io, t);
}
template<typename IO, typename T>
void io_r_(IO &io, T &t)
{
io_r(io, t);
}
} // namespace

17
tjp/core/io/IO_.h Normal file
View File

@@ -0,0 +1,17 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
namespace tjp {
namespace core {
template<typename T>
void io_resize_vector(T &t, size_t size)
{
t.resize(size);
}
} //
} //

23
tjp/core/io/IO_dispatch.h Executable file
View 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 "IO.h"
namespace tjp::core {
template<typename T>
struct DispatchIO
{
T *t;
} ;
template<typename T>
DispatchIO<T> makeDispatchIO(T &t)
{
return DispatchIO<T>{&t};
}
} // namespace

112
tjp/core/io/IO_encoder.h Executable file
View File

@@ -0,0 +1,112 @@
// 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 <tjp/core/bases/Base64.h>
#include <tjp/core/string/String.h>
#include <type_traits>
#include <tjp/core/string/to_string.hpp>
#include <tjp/core/string/from_string.hpp>
namespace tjp::core {
template<typename T>
struct BinaryEncoder
{
T *t;
} ;
template<typename T>
BinaryEncoder<T> makeBinaryEncoder(T &t)
{
return BinaryEncoder<T>{&t};
}
template<typename T>
struct StringEncoder
{
T *t;
} ;
template<typename T>
StringEncoder<T> makeStringEncoder(T &t)
{
return StringEncoder<T>{&t};
}
template<typename IO, typename T>
void io_bin_r(IO &io, BinaryEncoder<T> &v)
{
io.any(*v.t);
}
template<typename IO, typename T>
void io_bin_w(IO &io, const BinaryEncoder<T> &v)
{
io.any(*v.t);
}
template<typename IO, typename T>
void io_bin_r(IO &io, StringEncoder<T> &v)
{
io.any(*v.t);
}
template<typename IO, typename T>
void io_bin_w(IO &io, const StringEncoder<T> &v)
{
io.any(*v.t);
}
template<typename IO, typename T>
void io_r(IO &io, BinaryEncoder<T> &v)
{
auto t = v.t;
using U = std::remove_reference_t<decltype(*t->data())>;
static_assert(std::is_fundamental_v<U> == true);
String s;
io.any(s);
auto size_byte = base64DecodeSize(s.size());
auto size = size_byte / sizeof(U);
t->resize(size);
fromBase64(s, (char *)t->data(), size_byte);
}
template<typename IO, typename T>
void io_w(IO &io, const BinaryEncoder<T> &v)
{
auto t = v.t;
using U = std::remove_reference_t<decltype(*t->data())>;
static_assert(std::is_fundamental_v<U> == true);
auto size_byte = sizeof(U) * t->size();
io.any(toBase64((const char *)t->data(), size_byte));
}
template<typename IO, typename T>
void io_r(IO &io, StringEncoder<T> &v)
{
String s;
io.any(s);
auto t = v.t;
core::from_string(*t, s);
}
template<typename IO, typename T>
void io_w(IO &io, const StringEncoder<T> &v)
{
auto t = v.t;
io.any(core::to_string(*t));
}
} // namespace

7
tjp/core/io/Notes.txt Normal file
View File

@@ -0,0 +1,7 @@
I want to put a checkSize when I read a size, to verify that the readSize is reasonable
Or - I want to check every read before I do it.
It should just be f.validateReadSize(s)
and be used everywhere.

13
tjp/core/io/bin/Debug.h Normal file
View 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
View 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"

View 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

View 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

View 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

View 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

View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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

View 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
View 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

38
tjp/core/io/calls.hpp Executable file
View File

@@ -0,0 +1,38 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#pragma once
#include <tuple>
#include <utility>
namespace tjp::core::io {
template<typename F, typename... Args>
void call_f_1s(F&& f, Args&&... args) {
(f(std::forward<Args>(args)), ...);
}
template<typename F, typename Tuple, std::size_t... I>
void call_f_2s_(F &&f, Tuple&& tup, std::index_sequence<I...>) {
(f(
std::forward<decltype(std::get<I * 2>(std::forward<Tuple>(tup)))>(
std::get<I * 2>(std::forward<Tuple>(tup))),
std::forward<decltype(std::get<I * 2 + 1>(std::forward<Tuple>(tup)))>(
std::get<I * 2 + 1>(std::forward<Tuple>(tup)))
), ...);
}
template<typename F, typename... Args>
void call_f_2s(F &&f, Args&&... args)
{
static_assert(sizeof...(Args) % 2 == 0, "Arguments must be in key-value pairs");
auto args_tuple = std::forward_as_tuple(std::forward<Args>(args)...);
constexpr std::size_t N = sizeof...(Args) / 2;
call_f_2s_(std::forward<F>(f), args_tuple, std::make_index_sequence<N>{});
}
} // namespace

227
tjp/core/io/json/IO.h Normal file
View File

@@ -0,0 +1,227 @@
// 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_dispatch.h"
#include "../IO_.h"
#include "../IO+config.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/string/to_string.h>
#include <tjp/core/assert/debug_assert.h>
#include <tjp/core/string/to_string.hpp>
#include <tjp/core/string/from_string.hpp>
#include <tjp/core/system/System.h>
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include <list>
#include <optional>
//#ifdef DEBUG
//#define IO_JSON_LOG std::cerr
//#else
#define IO_JSON_LOG if(false) std::cout
//#endif
//#define IO_JSON_USE_LARGE_NUMBER_PROXY
namespace tjp::core::io::json {
using Size = u32;
typedef std::vector<char> Serialization;
// detects whether there is an appropriate intermediate
// io function
template <typename IO, typename T, typename = void>
struct has_io_json : std::false_type {};
template <typename IO, typename T>
struct has_io_json<IO, T,
std::void_t<decltype(io_json(std::declval<IO&>(),
std::declval<T&>())) >> : std::true_type {};
template<typename IO> void io_json(IO &io, bool &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, u8 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, u16 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, u32 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, u64 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, u128 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, s8 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, char &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, s16 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, s32 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, s64 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, s128 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, r32 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, r64 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, r128 &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, std::string &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, std::string_view &t) { io.intrinsic(t); }
#if defined(SYS_MAC) || defined(SYS_IOS)
template<typename IO> void io_json(IO &io, unsigned long &t) { io.intrinsic(t); }
template<typename IO> void io_json(IO &io, signed long &t) { io.intrinsic(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_json(IO &io, Optional<T> &t)
{
static_assert(always_false<T>::value, "this should not be dispatched to");
}
template<typename P, typename T>
struct Pointer
{
P *p;
bool write;
} ;
typedef const char *KeyType;
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 P, typename T>
void io_json(IO &io, Pointer<P, T> &t)
{
io.object("ptr", t);
}
template<typename IO, typename T>
void io_json(IO &io, T *&t)
{
auto p = io.template pointer<T>(t);
io.any(p);
}
template<typename T>
struct AsKeyType {
std::string value;
AsKeyType (const T &t)
{
value = core::to_string(t);
}
operator KeyType()
{
return value.c_str();
}
} ;
template<>
struct AsKeyType<std::string> {
const std::string &value;
AsKeyType (const std::string &value_) :
value(value_)
{
}
operator KeyType()
{
return value.c_str();
}
} ;
template<>
struct AsKeyType<KeyType> {
KeyType value;
AsKeyType (KeyType value_) :
value(value_)
{
}
operator KeyType()
{
return value;
}
} ;
template<typename T>
AsKeyType<T> asKeyType(const T &t)
{
return AsKeyType<T>(t);
}
template<typename T>
struct Custom
{
T *p;
} ;
template<typename IO, typename T>
void io_json(IO &io, Custom<T> &t)
{
io.template dispatch_custom<>(io, *t.p);
}
template<typename IO, typename ...T>
void io_json(IO &io, std::tuple<T...> &t)
{
io.tuple(t);
}
//#ifdef SYS_APPLE
// std::vector<bool> fixes
template<typename IO>
void io_json_w(IO &io, const std::vector<bool>::const_iterator::reference t)
{
bool b = t;
io.intrinsic(b);
}
template<typename IO>
void io_json_r(IO &io, std::vector<bool>::iterator::reference &t)
{
bool b;
io.intrinsic(b);
t = b;
}
//#endif
} // namespace

View File

@@ -0,0 +1,415 @@
// License: Modified MIT (NON-AI)
// See the LICENSE file in the root directory for license information.
// Copyright 2025 Timothy Prepscius
#include <tjp/core/io/json/IO.h>
#include <tjp/core/io/json/in_sajson.h>
#include <tjp/core/io/json/out.h>
#include <tjp/core/io/IO_encoder.h>
#include <tjp/core/testing/catch.hpp>
namespace tjp::core::io_json_test {
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 VV : std::vector<int>
{
} ;
template<typename IO>
void io_json(IO &io, VV &v)
{
std::string b = "b";
io.object(
"a",b
);
};
struct A {
bool w;
int x;
std::string s;
u64 _u64;
s64 _s64;
u128 _u128;
s128 _s128;
std::vector<int> y;
std::vector<std::vector<int>> z;
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 &&
s == rhs.s &&
v_b == rhs.v_b &&
v_s == rhs.v_s &&
_u64 == rhs._u64 &&
_s64 == rhs._s64 &&
_u128 == rhs._u128 &&
_s128 == rhs._s128
;
}
} ;
struct B {
int i;
} ;
struct C {
s32 i32;
u32 u32;
s64 i64;
u64 u64;
s128 i128;
u128 u128;
bool operator ==(const C &rhs) const
{
return
i32 == rhs.i32 &&
u32 == rhs.u32 &&
i64 == rhs.i64 &&
u64 == rhs.u64 &&
i128 == rhs.i128 &&
u128 == rhs.u128
;
}
} ;
template<typename IO>
void io_json(IO &io, C &a)
{
io.object(
"i32", a.i32,
"u32", a.u32,
"i64", a.i64,
"u64", a.u64,
"i128", a.i128,
"u128", a.u128
);
};
template<typename IO>
void io_json(IO &io, A &a)
{
io.object(
"w", a.w,
"x", a.x,
"y", a.y,
"z", a.z,
"s", a.s,
"_u64", a._u64,
"_s64", a._s64,
"_u128", a._u128,
"_s128", a._s128,
"v_b", makeBinaryEncoder(a.v_b),
"v_s", makeStringEncoder(a.v_s)
);
}
template<typename T>
T make_number()
{
T t;
char *b = (char *)&t;
auto *e = b + sizeof(T);
int i=1;
for (char *v=b; v!=e; ++v)
*v = i++;
return t;
}
SCENARIO("io json serialization", "[core::io]" )
{
GIVEN("a custom array with serializer")
{
auto v = VV();
auto vs = io::json::serialize(v);
auto vss = String(vs.begin(), vs.end());
REQUIRE(vss == "{\"a\":\"b\"}");
auto vs0 = io::json::deserialize<VV>(vs);
REQUIRE(vs0.empty());
}
GIVEN("a simple structure")
{
auto check_each = [](C &c) {
auto vi32_ = io::json::deserialize<decltype(c.i32)>(io::json::serialize(c.i32));
auto vi64_ = io::json::deserialize<decltype(c.i64)>(io::json::serialize(c.i64));
auto vi128_ = io::json::deserialize<decltype(c.i128)>(io::json::serialize(c.i128));
auto vu32_ = io::json::deserialize<decltype(c.u32)>(io::json::serialize(c.u32));
auto vu64_ = io::json::deserialize<decltype(c.u64)>(io::json::serialize(c.u64));
auto vu128_ = io::json::deserialize<decltype(c.u128)>(io::json::serialize(c.u128));
REQUIRE(vi32_ == c.i32);
REQUIRE(vi64_ == c.i64);
REQUIRE(vi128_ == c.i128);
REQUIRE(vu32_ == c.u32);
REQUIRE(vu64_ == c.u64);
REQUIRE(vu128_ == c.u128);
} ;
WHEN("interesting numbers 1")
{
C c = {
.i32 = 0,
.u32 = 0,
.i64 = 0,
.u64 = 0,
.i128 = 0,
.u128 = 0
};
auto sc = io::json::serialize(c);
auto c_ = io::json::deserialize<C>(std::move(sc));
bool same = c == c_;
REQUIRE(same);
check_each(c);
}
WHEN("interesting numbers max")
{
C c = {
.i32 = std::numeric_limits<s32>::max(),
.u32 = std::numeric_limits<u32>::max(),
.i64 = std::numeric_limits<s64>::max(),
.u64 = std::numeric_limits<u64>::max(),
.i128 = std::numeric_limits<s128>::max(),
.u128 = std::numeric_limits<u128>::max()
};
auto sc = io::json::serialize(c);
auto c_ = io::json::deserialize<C>(std::move(sc));
bool same = c == c_;
REQUIRE(same);
check_each(c);
}
WHEN("interesting numbers min")
{
C c = {
.i32 = std::numeric_limits<s32>::min(),
.u32 = std::numeric_limits<u32>::min(),
.i64 = std::numeric_limits<s64>::min(),
.u64 = std::numeric_limits<u64>::min(),
.i128 = std::numeric_limits<s128>::min(),
.u128 = std::numeric_limits<u128>::min()
};
auto sc = io::json::serialize(c);
auto sc_ = std::string(sc.begin(), sc.end());
auto c_ = io::json::deserialize<C>(std::move(sc));
bool same = c == c_;
REQUIRE(same);
check_each(c);
}
WHEN("interesting numbers max-1")
{
C c = {
.i32 = std::numeric_limits<s32>::max()-1,
.u32 = std::numeric_limits<u32>::max()-1,
.i64 = std::numeric_limits<s64>::max()-1,
.u64 = std::numeric_limits<u64>::max()-1,
.i128 = std::numeric_limits<s128>::max()-1,
.u128 = std::numeric_limits<u128>::max()-1
};
auto sc = io::json::serialize(c);
auto c_ = io::json::deserialize<C>(std::move(sc));
bool same = c == c_;
REQUIRE(same);
check_each(c);
}
WHEN("interesting numbers min+1")
{
C c = {
.i32 = std::numeric_limits<s32>::min()+1,
.u32 = std::numeric_limits<u32>::min()+1,
.i64 = std::numeric_limits<s64>::min()+1,
.u64 = std::numeric_limits<u64>::min()+1,
.i128 = std::numeric_limits<s128>::min()+1,
.u128 = std::numeric_limits<u128>::min()+1
};
auto sc = io::json::serialize(c);
auto c_ = io::json::deserialize<C>(std::move(sc));
bool same = c == c_;
REQUIRE(same);
check_each(c);
}
WHEN("interesting numbers max+1")
{
C c = {
.i32 = std::numeric_limits<s16>::max()+1,
.u32 = std::numeric_limits<u16>::max()+1,
.i64 = s64(std::numeric_limits<s32>::max())+1,
.u64 = u64(std::numeric_limits<u32>::max())+1,
.i128 = s128(std::numeric_limits<s64>::max())+1,
.u128 = u128(std::numeric_limits<u64>::max())+1
};
auto sc = io::json::serialize(c);
auto c_ = io::json::deserialize<C>(std::move(sc));
bool same = c == c_;
REQUIRE(same);
check_each(c);
}
}
GIVEN("a structure")
{
A a = {
true, 13,
std::string("hi"),
make_number<u64>(), make_number<s64>(),
make_number<u128>(), make_number<s128>(),
{ 2, 3, 4 },
{ { 1, 2, 3 }, { 4, 5, 6 }},
{ 1, 2, 3, 4, 5, 6, 7, 8 },
{ 1 }
};
WHEN("base64 works")
{
A b;
fromBase64(toBase64((char *)&a._s128, sizeof(a._s128)), (char *)&b._s128, sizeof(b._s128));
fromBase64(toBase64((char *)&a._u128, sizeof(a._u128)), (char *)&b._u128, sizeof(b._u128));
fromBase64(toBase64((char *)&a._s64, sizeof(a._s64)), (char *)&b._s64, sizeof(b._s64));
fromBase64(toBase64((char *)&a._u64, sizeof(a._u64)), (char *)&b._u64, sizeof(b._u64));
REQUIRE(a._s128 == b._s128);
REQUIRE(a._u128 == b._u128);
REQUIRE(a._s64 == b._s64);
REQUIRE(a._u64 == b._u64);
}
WHEN("serialize and deserialize, is same")
{
auto sa = io::json::serialize(a);
auto saX = std::string(sa.data(), sa.size());
auto a_ = io::json::deserialize<A>(std::move(sa));
bool same = a == a_;
REQUIRE(same);
auto sa2 = io::json::serialize(a_);
auto sa2X = std::string(sa2.data(), sa2.size());
REQUIRE(saX == sa2X);
}
}
GIVEN("a structure empty")
{
A a = { true, 13 };
WHEN("serialize and deserialize, is same")
{
auto sa = io::json::serialize(a);
auto a_ = io::json::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")
{
auto sa = io::json::serialize(a);
auto a_ = io::json::deserialize<Bools>(std::move(sa));
THEN("is same")
{
REQUIRE(a_ == a);
}
}
}
GIVEN("an empty vector")
{
typedef std::vector<int> V;
V a;
WHEN("serialize and deserialize")
{
auto sa = io::json::serialize(a);
auto a_ = io::json::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::json::serialize(a);
auto a_ = io::json::deserialize<V>(std::move(sa));
THEN("is same")
{
REQUIRE(a_ == a);
}
}
}
}
} // namespace

View File

@@ -0,0 +1,39 @@
// 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>
bool fromFile (const std::string &fileName, T &t)
{
std::ifstream inFile;
inFile.open(fileName);
if (!inFile)
return false;
fromStream(inFile, t);
return true;
}
template<typename T>
T fromFile (const std::string &fileName)
{
std::ifstream inFile;
inFile.open(fileName);
if (!inFile)
throw Exception { "File could not be opened" };
T t;
fromStream(inFile, t);
return t;
}
} // namespace

View File

@@ -0,0 +1,831 @@
// 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 <iostream>
#include "../../json/sajson.h"
#include <set>
#include <tjp/core/string/from_string.h>
#include <tjp/core/io/memstream.h>
#include <tjp/core/ptr/Ptr.hpp>
#include <tjp/core/debug/Debug.h>
#include <tjp/core/rtti/RTTI.hpp>
#include "../Exception.h"
#include <tjp/core/system/System.h>
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
#include <tjp/core/bases/Base64.h>
#endif
//#ifdef DEBUG
//#define IO_JSON_LOG std::cerr
//#else
#define IO_JSON_LOG if(false) std::cout
//#endif
namespace tjp::core::io::json {
template<typename IO, typename T>
void io_json_r(IO &io, T &t);
template<typename IO, typename T>
void io_json_r_dispatch(IO &io, T &t);
typedef const char *KeyType;
struct json_in {
Serialization underlying;
typedef const sajson::document Document;
Document document;
typedef const sajson::value Node;
struct Current {
Node node;
Current(const Node &node_) :
node(node_)
{
}
int keyIndex = 0;
bool objectKeysFound = false;
std::vector<std::string> objectKeys;
size_t array_size () {
return node.get_length();
}
const std::vector<std::string> &object_keys ()
{
if (!objectKeysFound)
{
auto length = node.get_length();
for (auto i = 0; i<length; ++i)
{
auto key = node.get_object_key(i);
objectKeys.push_back(key.as_string());
}
objectKeysFound = true;
}
return objectKeys;
}
bool isValid()
{
return node.get_type() != sajson::TYPE_NULL;
}
bool is_null ()
{
return node.get_type() == sajson::TYPE_NULL;
}
Node operator[] (int i)
{
if (i >= node.get_length())
throw Exception { "Index out of bounds" };
return node.get_array_element(i);
}
Node operator[] (const std::string &i)
{
auto key = sajson::string(i.c_str(), i.length());
auto element = node.find_object_key(key);
if (element >= node.get_length())
throw Exception { "Key does not exist" };
return node.get_object_value(element);
}
template<typename T>
T get_int()
{
T t;
if (!node.get_integer_value(t))
{
debug_assert(false);
throw Exception { "Value not convertible" };
}
return t;
}
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
template<typename T>
T get_proxy()
{
T t;
fromBase64(
node.as_cstring(),
node.get_string_length(),
(char *)&t,
sizeof(t)
);
return t;
}
#endif
// s64 get_int64()
// {
// s64 v = -1;
// if (!node.get_int53_value(&v))
// {
// debug_assert(false);
// // throw something
// }
// return v;
// }
//
// s64 get_int128()
// {
// s64 v = -1;
// if (!node.get_int53_value(&v))
// {
// debug_assert(false);
// // throw something
// }
// return v;
// }
double get_double()
{
return node.get_number_value();
}
std::string_view get_utf8()
{
return node.as_string();
}
bool get_bool ()
{
return node.get_type() == sajson::TYPE_TRUE;
}
} ;
std::list<Current> current;
json_in(Serialization &&serialization) :
underlying(std::move(serialization)),
document(sajson::parse(
sajson::dynamic_allocation(),
sajson::mutable_string_view(underlying.size(), (char *)underlying.data())
))
{
current.emplace_back(document.get_root());
}
size_t array_size()
{
auto &doc = current.back();
return doc.array_size();
}
std::vector<std::string> object_keys()
{
auto &doc = current.back();
return doc.object_keys();
}
void key(const KeyType &key)
{
current.emplace_back(current.back()[key]);
}
// bool hasKey(const KeyType &key_)
// {
// std::string key(key_);
// auto &keys = current.back().object_keys();
// return std::find(keys.begin(), keys.end(), key) != keys.end();
// }
bool hasKeyValue(const KeyType &key_)
{
std::string key(key_);
auto &keys = current.back().object_keys();
auto i= std::find(keys.begin(), keys.end(), key);
if (i == keys.end())
return false;
return current.back()[*i].get_type() != sajson::TYPE_NULL;
}
void next_array_key()
{
auto index = current.back().keyIndex++;
current.emplace_back(current.back()[index]);
}
void endKey()
{
current.pop_back();
}
void begin_element ()
{
}
void begin_object()
{
begin_element();
}
void end_element()
{
// current.pop_back();
}
void end_object()
{
end_element();
}
void begin_array()
{
begin_element();
}
void end_array()
{
end_element();
}
void clearKey ()
{
}
template<typename V>
void v_int(V &v)
{
v = current.back().get_int<V>();
}
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
template<typename V>
void v_proxy(V &v)
{
v = current.back().get_proxy<V>();
}
template<>
void v_int(s64 &v)
{
return v_proxy(v);
}
template<>
void v_int(u64 &v)
{
return v_proxy(v);
}
template<>
void v_int(s128 &v)
{
return v_proxy(v);
}
template<>
void v_int(u128 &v)
{
return v_proxy(v);
}
#endif
template<typename V>
void v_string(V &v)
{
v = current.back().get_utf8();
}
template<typename V>
void v_double(V &v)
{
v = current.back().get_double();
}
template<typename V>
void v_bool(V &v)
{
v = current.back().get_bool();
}
void v(u8 &v) { v_int(v); }
void v(u16 &v) { v_int(v); }
void v(u32 &v) { v_int(v); }
void v(u64 &v) { v_int(v); }
void v(u128 &v) { v_int(v); }
void v(s8 &v) { v_int(v); }
void v(char &v) { v_int(v); }
void v(s16 &v) { v_int(v); }
void v(s32 &v) { v_int(v); }
void v(s64 &v) { v_int(v); }
void v(s128 &v) { v_int(v); }
void v(r32 &v) { v_double(v); }
void v(r64 &v) { v_double(v); }
void v(r128 &v) { v_double(v); }
void v(bool &v) { v_bool(v); }
void v(std::string &v) { v_string(v); }
#if defined(SYS_MAC) || defined(SYS_IOS)
void v(unsigned long &v) { v_int(v); }
void v(signed long &v) { v_int(v); }
#endif
bool is_null()
{
return current.back().is_null();
}
} ;
// ------------------------
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);
}
} ;
template<typename Stream_, typename Base=ReaderEmptyBaseClass, typename Allocator=ReaderAllocatorDefault>
class Reader : public Base
{
public:
typedef Stream_ Stream;
Stream f;
u8 count = 0;
public:
Allocator allocator;
template<typename T>
void allocate(T &t)
{
allocator.allocate(*this, t);
}
auto partial(const Serialization &s)
{
using ReaderT = Reader<json_in, Base>;
return strong<ReaderT>(std::move(Serialization(s)));
}
public:
template<class... Args>
Reader (Args&&... args) :
f(std::forward<Args>(args)...)
{
}
operator bool() const
{
return f.good();
}
template<typename T>
void intrinsic(T &t)
{
read(t);
}
template<typename T>
void intrinsic_string(T &t)
{
std::string s;
read(s);
t = s;
}
template<typename T>
void intrinsic_string(std::string &t)
{
read(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 object(T&&... t)
{
object_begin();
object_(t...);
object_end();
}
void object_begin()
{
f.begin_object();
}
template<typename F>
void object_kf(KeyType k, F &&q)
{
f.key(k);
q();
f.endKey();
};
template<typename ... T>
void object_kv(T&&... t)
{
object_(t...);
}
void object_end()
{
f.end_object();
}
template<typename ... T>
void array(T&&... t)
{
f.begin_array();
array_(t...);
f.end_array();
}
template<typename ...T>
void tuple(std::tuple<T...> &t)
{
f.begin_array();
std::apply([this](auto &...x){(av(x), ...);}, t);
f.end_array();
}
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
template<typename T>
void read(T &t)
{
f.v(t);
}
// ----- dispatch out and in
template<typename T>
void dispatch_any(T &t)
{
IO_JSON_LOG << "dispatch_any " << type_id<T>().name() << std::endl;
io_json_r_dispatch(*this, t);
}
// kv & av -----------
template<typename V>
void kv(KeyType k, V &v)
{
f.key(k);
any(v);
f.endKey();
}
template<typename T>
void kv(KeyType k, Optional<T> &t)
{
if (f.hasKeyValue(k))
{
kv(k, t.v);
}
else
{
}
}
template<typename T>
void kv(KeyType k, std::optional<T> &t)
{
if (f.hasKeyValue(k))
{
t.emplace();
kv(k, *t);
}
else
{
}
}
template<typename B, typename T>
void kv(KeyType k, Pointer<B, T> &t)
{
if (f.hasKeyValue(k))
{
allocator.allocate(*this, *t.p);
kv(k, **t.p);
}
}
template<typename T>
void av(T &v)
{
f.next_array_key();
any(v);
f.endKey();
}
template<typename T>
void av_no_ref(T v)
{
f.next_array_key();
any(v);
f.endKey();
}
// -- handling different containers --
public:
size_t array_begin()
{
f.begin_array();
return f.array_size();
}
void array_end()
{
f.end_array();
}
template<typename T>
void on_vector_begin (T &t)
{
auto count = array_begin();
io_resize_vector(t, count);
}
template<typename T>
void on_vector_end (T &t)
{
array_end();
}
template<typename T>
void on_vector_value(T &t)
{
av(t);
}
template<typename F>
void array_values(size_t size, F &&fun)
{
for (auto i=0;i<size;++i)
fun();
}
template<typename K, typename F>
void object_values(F &&fun)
{
f.begin_object();
auto keys = f.object_keys();
for (auto &k: keys)
{
typedef K Key;
Key key = core::from_string<Key>(k);
f.key(k);
fun(k);
f.endKey();
}
f.end_object();
}
protected:
template<typename T>
void on_vector(T &t)
{
IO_JSON_LOG << "many " << type_id<T>().name() << std::endl;
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_JSON_LOG << "many " << type_id<T>().name() << std::endl;
on_vector_begin(t);
for (auto i = t.begin(); i!=t.end(); ++i)
av_no_ref<typename T::iterator::reference>(*i);
on_vector_end(t);
}
template<typename T>
void on_map(T &t)
{
IO_JSON_LOG << "map " << type_id<T>().name() << std::endl;
f.begin_object();
auto keys = f.object_keys();
for (auto &k: keys)
{
typedef typename T::key_type Key;
Key key = core::from_string<Key>(k);
typename T::mapped_type value;
kv(asKeyType(k), value);
t.emplace(key, value);
}
f.end_object();
}
template<typename V, typename T>
void on_set(T &t)
{
IO_JSON_LOG << "many " << type_id<T>().name() << std::endl;
f.begin_array();
auto count = f.array_size();
for (auto i=0; i<count; ++i)
{
V v;
av(v);
t.insert(v);
}
f.end_array();
}
// void on_any(variant::Variant &t)
// {
// on_variant(t);
// }
//
// -- dispatching different containers --
public:
template<typename T, typename std::enable_if<!is_iterable<T>::value || is_stringish<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_stringish<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);
}
protected:
// ---- handling object & array ----
void object_()
{
}
template<typename T>
void object__(KeyType key, T &t)
{
IO_JSON_LOG << "key " << key << std::endl;
kv(key, 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)
{
av(t);
}
template<typename ...Args>
void array_(Args&& ...args)
{
call_expand_1(
[&](auto && ...args) { array__(std::forward<decltype(args)>(args)...); },
std::forward<Args>(args)...
);
}
template<typename ...T>
void tuple_(std::tuple<T...> &t)
{
std::apply([this](auto &...x){(av(x), ...);}, t);
}
// ----- miscellaneous -----
public:
bool null ()
{
return f.is_null();
}
template<typename T>
Optional<T> optional(T &t, bool write=true)
{
return io::json::optional(t, write);
}
template<typename T, typename B>
Pointer<B, T> pointer(B &b)
{
return io::json::Pointer<B, T> { &b };
}
template<typename K>
AsKeyType<K> asKeyType(const K &k)
{
return io::json::asKeyType(k);
}
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>
T deserialize (Serialization &&data);
template<typename T>
T deserialize (Serialization &&data);
template<typename T>
T deserialize (const Serialization &data);
} // namespace
#include "in_sajson.inl"

View File

@@ -0,0 +1,76 @@
// 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::json {
template<typename IO, typename T>
void io_json_r(IO &io_, T &t)
{
if constexpr (has_io_json<IO, T>::value)
{
io_json(io_, t);
}
else
{
io_r(io_, t);
}
}
template<typename IO, typename T>
void io_json_r_dispatch(IO &io_, T &t)
{
io_json_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>
T deserialize (Serialization &&data)
{
R reader(std::move(data));
return deserializeFrom<T>(reader);
}
template<typename T>
T deserialize (Serialization &&data)
{
return deserialize<Reader<json_in>, T>(std::move(data));
}
template<typename T>
T deserialize (const Serialization &data)
{
return deserialize<Reader<json_in>, T>(Serialization(data));
}
template<typename T>
void fromStream (const std::istream &stream, T &t)
{
std::vector<char> v;
omemstream s(v);
s << stream.rdbuf(); //read the file
Reader<json_in> reader(std::move(v));
reader.any(t);
}
} // namespace

26
tjp/core/io/json/json.inl Normal file
View 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_json_w(IO &io, const json &j)
{
auto s = to_string(j);
io.any(s);
}
template<typename IO>
void io_json_r(IO &io, json &j)
{
std::string s;
io.any(s);
j = json(s);
}
} // namespace

650
tjp/core/io/json/out.h Normal file
View File

@@ -0,0 +1,650 @@
// 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 <tjp/core/io/memstream.h>
#include <tjp/core/types/Types+IO.h>
#include <tjp/core/rtti/RTTI.hpp>
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
#include <tjp/core/containers/InPlaceArray.hpp>
#include <tjp/core/bases/Base64.h>
#endif
namespace tjp::core::io::json {
template<typename IO, typename T>
void io_json_w(IO &io, const T &t);
template<typename IO, typename T>
void io_json_w_dispatch(IO &io, const T &t);
struct json_out {
std::ostream *out;
char Q = '"';
char C = ':';
char SEP = ',';
bool firstValue=true;
int level = 0;
json_out(std::ostream &out_) :
out(&out_)
{
}
json_out()
{
}
~json_out()
{
}
void prettyPrintX ()
{
(*out) << std::endl;
for (auto i=0; i<level; ++i)
(*out) << " ";
}
void prettyPrint ()
{
}
void valueSeparator()
{
if (!firstValue)
{
(*out) << SEP;
}
}
void endValue ()
{
firstValue = false;
}
void beginValue ()
{
firstValue = true;
}
void key(const KeyType &key)
{
valueSeparator();
beginValue();
prettyPrint();
(*out) << Q << key << Q << C;
}
void begin_object()
{
valueSeparator();
beginValue();
(*out) << '{';
level++;
}
void end_object()
{
level--;
prettyPrint();
(*out) << '}';
endValue();
}
void begin_array()
{
valueSeparator();
beginValue();
(*out) << '[';
level++;
}
void end_array()
{
level--;
(*out) << ']';
endValue();
}
template<typename V>
void v_value(const V &v_)
{
valueSeparator();
(*out) << v_;
endValue();
}
template<typename T>
void v_quoted(const T &v_)
{
valueSeparator();
(*out) << Q;
auto &oss = *out;
for (const char c : v_) {
switch (c) {
case '\"': oss << "\\\""; break;
case '\\': oss << "\\\\"; break;
case '\b': oss << "\\b"; break;
case '\f': oss << "\\f"; break;
case '\n': oss << "\\n"; break;
case '\r': oss << "\\r"; break;
case '\t': oss << "\\t"; break;
default:
if (static_cast<unsigned char>(c) < 0x20)
{
char buffer[7];
std::snprintf(buffer, sizeof(buffer), "\\u%04X", static_cast<unsigned char>(c));
oss << buffer;
}
else
{
oss << c;
}
}
}
(*out) << Q;
endValue();
}
#ifdef IO_JSON_USE_LARGE_NUMBER_PROXY
template<typename V>
void v_value_proxy(const V &v_)
{
constexpr size_t b64Size = sizeof(V) * 4;
InPlaceArray<char, b64Size> b64;
b64.resize(b64Size);
auto size = toBase64((char *)&v_, sizeof(V), b64.data(), b64.size());
b64.resize(size);
v_quoted(b64);
}
#else
template<typename V>
void v_value_proxy(const V &v_)
{
v_value(v_);
}
#endif
template<typename V>
void v(const V &v_)
{
v_value(v_);
}
void v(const std::string &v_)
{
v_quoted(v_);
}
void v(const std::string_view &v_)
{
v_quoted(v_);
}
void v(const u8 &v_)
{
v_value((u32)v_);
}
void v(const s8 &v_)
{
v_value((s32)v_);
}
void v(const char &v_)
{
v_value((s32)v_);
}
void v(const s64 &v_)
{
v_value_proxy(v_);
}
void v(const u64 &v_)
{
v_value_proxy(v_);
}
void v(const s128 &v_)
{
v_value_proxy(v_);
}
void v(const u128 &v_)
{
v_value_proxy(v_);
}
void v(const bool &v_)
{
v_value(v_ ? "true" : "false");
}
void null()
{
v_value("null");
}
} ;
struct json_out_mem : json_out
{
Serialization block;
omemstream s;
json_out_mem() :
s(block)
{
s.precision(17);
out = &s;
}
} ;
inline
auto serialization_of(json_out &m)
{
}
inline
auto serialization_of(json_out_mem &m)
{
return m.block;
}
// ------------------------
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=EmptyWriterBaseClass, typename Allocator=WriterAllocatorBase>
class Writer :
public Base,
public Allocator
{
public:
S f;
u8 count=0;
public:
auto partial()
{
return Writer<json_out_mem, Base>();
}
auto serialization ()
{
return serialization_of(f);
}
auto partial_serialization ()
{
return serialization();
}
public:
template<class... Args>
Writer (Args&&... args) :
f(args...)
{
}
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(std::string_view(t));
}
template<typename T>
void any(const T &t)
{
dispatch_any(t);
}
template<typename ... T>
void object(T&&... t)
{
object_begin();
object_(t...);
object_end();
}
void object_begin()
{
f.begin_object();
}
template<typename F>
void object_kf(KeyType k, F &&q)
{
f.key(k);
q();
};
template<typename ... T>
void object_kv(T&&... t)
{
object_(t...);
}
void object_end()
{
f.end_object();
}
template<typename ... T>
void array(T&&... t)
{
f.begin_array();
array_(t...);
f.end_array();
}
template<typename ...T>
void tuple(std::tuple<T...> &t)
{
f.begin_array();
std::apply([this](auto &...x){(av(x), ...);}, t);
f.end_array();
}
template<typename T>
void on_dispatch_any(const T &t)
{
on_any(t);
}
void null()
{
f.null();
}
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_JSON_LOG << "any " << type_id<T>().name() << std::endl;
io_json_w_dispatch(*this, t);
}
template<typename T>
void write(const T &t)
{
f.v(t);
}
// kv & av -----------
template<typename V>
void kv(KeyType k, const V &v)
{
f.key(k);
any(v);
}
template<typename T>
void kv(KeyType k, const Optional<T> &t)
{
if (t.write)
kv(k, t.v);
}
template<typename T>
void kv(KeyType k, const std::optional<T> &t)
{
if (t.has_value())
kv(k, *t);
}
template<typename B, typename T>
void kv(KeyType k, const Pointer<B, T> &t)
{
if (t.write)
kv(k, *(T *)*t.p);
}
template<typename V>
void av(const V &v)
{
any(v);
}
// -- handling different containers --
public:
void array_begin (Size size)
{
f.begin_array();
}
void array_end()
{
f.end_array();
}
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)
{
av(t);
}
protected:
template<typename T>
void on_vector(const T &t)
{
IO_JSON_LOG << "many " << type_id<T>().name() << std::endl;
on_vector_begin(t);
// this fails for std::vector<bool>, 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);
}
template<typename T>
void on_map(const T &t)
{
IO_JSON_LOG << "map " << type_id<T>().name() << std::endl;
f.begin_object();
for (auto &v : t)
kv(asKeyType(v.first), v.second);
f.end_object();
}
// -- dispatching different containers --
template<typename T, typename std::enable_if<!is_iterable<T>::value || is_stringish<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_stringish<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);
}
// void on_any(const variant::Variant &t)
// {
// on_variant(t);
// }
//
// ---- handling object & array ----
void object_()
{
}
template<typename T>
void object__(KeyType key, const T &t)
{
kv(key, 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)...
);
}
template<typename ...T>
void tuple_(std::tuple<T...> &t)
{
std::apply([this](auto &...x){(av(x), ...);}, t);
}
public:
// ----- miscellaneous -----
template<typename T>
Optional<T> optional(T &t, bool write=true)
{
return io::json::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::json::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::json::Pointer<B, T> {
&b, dynamic_cast_ptr<T>(b) != nullptr
};
}
template<typename K>
AsKeyType<K> asKeyType(const K &k)
{
return io::json::asKeyType(k);
}
template<typename T>
Custom<T> custom(T &t)
{
return Custom<T> { &t };
}
} ;
// -------------------
template<typename W, typename ... X>
Serialization serialize (X&&... x);
template<typename ... X>
Serialization serialize (X&&... x);
template<typename T>
void serializeTo (Serialization &serialization, const T &t);
template<typename W, typename T>
Serialization serializeTo(W &w, const T &t);
template<typename ... X>
void toStream (std::ostream &file, X&& ... x);
template<typename ... X>
bool toFile (const std::string &fileName, X&& ... x);
} // namespace
#include "out.inl"

72
tjp/core/io/json/out.inl Normal file
View File

@@ -0,0 +1,72 @@
// 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::json {
template<typename IO, typename T>
void io_json_w(IO &io_, const T &t)
{
typedef typename std::remove_const<T>::type TC;
if constexpr (has_io_json<IO, T>::value)
{
auto &t_ = *const_cast<TC *>(&t);
io_json(io_, t_);
}
else
{
io_w(io_, t);
}
}
template<typename IO, typename T>
void io_json_w_dispatch(IO &io_, const T &t)
{
io_json_w(io_, t);
}
template<typename W, typename T>
void serialize_many (W &writer, const T &t)
{
writer.any(t);
}
template<typename W, typename T, typename ... X>
void serialize_many (W &writer, const T &t, X&& ... x)
{
writer.any(t);
serialize_many(writer, x...);
}
template<typename W, typename ... X>
Serialization serialize (X&&... x)
{
Serialization v;
omemstream s(v);
s.precision(17);
W writer(s);
serialize_many(writer, x...);
return v;
};
template<typename ... X>
Serialization serialize (X&&... x)
{
return serialize<Writer<json_out>>(x...);
}
template<typename ... X>
void toStream (std::ostream &file, X&& ... x)
{
Writer<json_out> writer { file };
serialize_many(writer, x...);
}
} // namespace

View File

@@ -0,0 +1,21 @@
// 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 ... X>
bool toFile (const std::string &fileName, X&& ... x)
{
std::ofstream stream(fileName);
stream.precision(17);
toStream(stream, x...);
return true;
}
} // namespace