/* * Copyright 2015 - 2025 gary@drinkingtea.net * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include "platspecs.hpp" namespace ox { template class Preloader; template class Preloader: public ModelHandlerBase, OpType::Reflect> { private: using PtrType = typename PlatSpec::PtrType; static constexpr auto PtrSize = sizeof(PtrType); class UnionIdxTracker { private: int m_unionIdx = -1; int m_it = 0; public: constexpr UnionIdxTracker() noexcept = default; constexpr explicit UnionIdxTracker(int idx) noexcept: m_unionIdx(idx) {} constexpr auto checkAndIterate() noexcept { return m_unionIdx == -1 || m_it++ == m_unionIdx; } }; ox::Buffer m_buff; ox::BufferWriter m_writer; // list of all the places where ptrs were written to buffer struct PtrPair { std::size_t loc = 0; typename PlatSpec::PtrType value = 0; constexpr PtrPair() noexcept = default; constexpr PtrPair(std::size_t pLoc, typename PlatSpec::PtrType pValue) noexcept: loc(pLoc), value(pValue) {} }; ox::Vector m_ptrs; ox::Vector m_unionIdx = {{}}; class AllocStackItem { public: PtrType restore = 0; ox::ios_base::seekdir seekdir = ox::ios_base::end; constexpr AllocStackItem(PtrType pRestore, ox::ios_base::seekdir pSeekdir = ox::ios_base::end) noexcept: restore(pRestore), seekdir(pSeekdir) {} }; ox::Vector m_allocStack; ox::Vector m_allocStart{}; constexpr Preloader() noexcept: m_writer(&m_buff) {} public: Preloader(const Preloader &src) = delete; Preloader(Preloader &&src) = delete; const Preloader &operator=(const Preloader &src) = delete; const Preloader &operator=(Preloader &&src) = delete; constexpr static ox::Result> make(ox::ios_base::seekdir anchor = ox::ios_base::cur, std::size_t sz = 0) noexcept; template constexpr ox::Error setTypeInfo( const char* = T::TypeName, int = T::TypeVersion) noexcept { return {}; } template constexpr ox::Error setTypeInfo( const char*, int, const Vector&, std::size_t) noexcept { return {}; } template constexpr ox::Error field(StringViewCR, ox::UnionView val) noexcept; template constexpr ox::Error field(StringViewCR, const T *val) noexcept; template constexpr ox::Error field(StringViewCR, const ox::BasicString *val) noexcept; template constexpr ox::Error field(StringViewCR, const ox::Array *valArray) noexcept; template constexpr ox::Error field(StringViewCR, const T **val, std::size_t cnt) noexcept; constexpr ox::Result startAlloc(size_t sz, size_t align) noexcept; constexpr ox::Result startAlloc(size_t sz, size_t align, std::size_t restore) noexcept; constexpr ox::Error endAlloc() noexcept; constexpr ox::Error offsetPtrs(std::size_t offset) noexcept; [[nodiscard]] constexpr auto &buff() const noexcept { return m_buff; } template constexpr ox::Error pad(const T*) noexcept; private: constexpr ox::Error fieldVector(StringViewCR name, const ox::ModelValueVector *val) noexcept; template constexpr ox::Error fieldVector(StringViewCR, const ox::Vector *val) noexcept; constexpr ox::Error fieldVector(StringViewCR, const auto *val, ox::VectorMemMap vecVal) noexcept; constexpr ox::Error fieldArray(StringViewCR name, ox::ModelValueArray const*val) noexcept; constexpr bool unionCheckAndIt() noexcept; [[nodiscard]] constexpr size_t calcPadding(size_t align) const noexcept; }; template constexpr ox::Result>> Preloader::make(ox::ios_base::seekdir anchor, std::size_t sz) noexcept { auto p = ox::UniquePtr(new Preloader); if (const auto err = p->m_writer.seekp(0, anchor)) { return {std::move(p), err}; } if (const auto err = p->m_writer.write(nullptr, sz)) { return {std::move(p), err}; } if (const auto err = p->m_writer.seekp(p->m_writer.tellp() - sz)) { return {std::move(p), err}; } return p; } template template constexpr ox::Error Preloader::field(StringViewCR, const ox::UnionView val) noexcept { if (!unionCheckAndIt()) { return {}; } OX_RETURN_ERROR(pad(val.get())); m_unionIdx.emplace_back(val.idx()); const auto err = preload(this, val.get()); m_unionIdx.pop_back(); return err; } template template constexpr ox::Error Preloader::field(StringViewCR name, const T *val) noexcept { if (!unionCheckAndIt()) { return {}; } OX_RETURN_ERROR(pad(val)); if constexpr(ox::is_integral_v) { return ox::serialize(m_writer, PlatSpec::correctEndianness(*val)); } else if constexpr(ox::is_pointer_v) { const PtrType a = startAlloc(sizeOf(val), alignOf(*val), m_writer.tellp()) + PlatSpec::RomStart; OX_RETURN_ERROR(field(name, *val)); OX_RETURN_ERROR(endAlloc()); return ox::serialize(m_writer, PlatSpec::correctEndianness(a)); } else if constexpr(ox::isVector_v) { return fieldVector(name, val); } else if constexpr(ox::is_same_v) { val->types(); return fieldVector(name, val); } else if constexpr(ox::is_same_v) { return fieldArray(name, val); } else { m_unionIdx.emplace_back(-1); const auto out = preload(this, val); m_unionIdx.pop_back(); return out; } } template template constexpr ox::Error Preloader::field(StringViewCR, const ox::BasicString *val) noexcept { if (!unionCheckAndIt()) { return {}; } using VecMap = ox::VectorMemMap; const auto sz = val->bytes(); VecMap vecVal{ .smallVecSize = SmallStringSize, .size = PlatSpec::correctEndianness(static_cast(sz)), .cap = PlatSpec::correctEndianness(static_cast(sz)), }; OX_RETURN_ERROR(pad(&vecVal)); const auto restore = m_writer.tellp(); std::size_t a = 0; if (sz && sz >= SmallStringSize) { OX_RETURN_ERROR(ox::allocate(m_writer, sz).moveTo(a)); } else { a = restore; } vecVal.items = PlatSpec::correctEndianness(static_cast(a) + PlatSpec::RomStart); OX_RETURN_ERROR(m_writer.seekp(a)); OX_RETURN_ERROR(m_writer.write(val->data(), sz)); OX_RETURN_ERROR(m_writer.seekp(restore)); OX_RETURN_ERROR(serialize(m_writer, vecVal)); m_ptrs.emplace_back(restore + offsetof(VecMap, items), vecVal.items); return {}; } template template constexpr ox::Error Preloader::field(StringViewCR name, const ox::Array *val) noexcept { if (!unionCheckAndIt()) { return {}; } OX_RETURN_ERROR(pad(&(*val)[0])); // serialize the Array elements if constexpr(sz) { m_unionIdx.emplace_back(-1); for (std::size_t i = 0; i < val->size(); ++i) { OX_RETURN_ERROR(this->interface()->field(name, &(*val)[i])); } m_unionIdx.pop_back(); } return {}; } template template constexpr ox::Error Preloader::field(StringViewCR, const T **val, std::size_t cnt) noexcept { if (!unionCheckAndIt()) { return {}; } if (cnt) { OX_RETURN_ERROR(pad(*val)); // serialize the array m_unionIdx.emplace_back(-1); for (std::size_t i = 0; i < cnt; ++i) { OX_RETURN_ERROR(this->interface()->field(nullptr, &val[i])); } m_unionIdx.pop_back(); } return {}; } template constexpr ox::Result Preloader::startAlloc(size_t sz, size_t align) noexcept { m_allocStack.emplace_back(static_cast(m_writer.tellp())); OX_RETURN_ERROR(m_writer.seekp(0, ox::ios_base::end)); auto const padding = calcPadding(align); OX_REQUIRE_M(a, ox::allocate(m_writer, sz + padding)); a += padding; OX_RETURN_ERROR(m_writer.seekp(a)); m_allocStart.push_back(a); return a; } template constexpr ox::Result Preloader::startAlloc( std::size_t sz, size_t align, std::size_t restore) noexcept { m_allocStack.emplace_back(restore, ox::ios_base::beg); OX_RETURN_ERROR(m_writer.seekp(0, ox::ios_base::end)); auto const padding = calcPadding(align); OX_REQUIRE_M(a, ox::allocate(m_writer, sz + padding)); a += padding; OX_RETURN_ERROR(m_writer.seekp(a)); m_allocStart.push_back(a); return a; } template constexpr ox::Error Preloader::endAlloc() noexcept { if (m_allocStack.empty()) { return m_writer.seekp(0, ox::ios_base::end); } const auto &si = *m_allocStack.back().unwrap(); OX_RETURN_ERROR(m_writer.seekp(static_cast(si.restore), si.seekdir)); m_allocStack.pop_back(); m_allocStart.pop_back(); return {}; } template constexpr ox::Error Preloader::offsetPtrs(std::size_t offset) noexcept { for (const auto &p : m_ptrs) { OX_RETURN_ERROR(m_writer.seekp(p.loc)); const auto val = PlatSpec::template correctEndianness( static_cast(p.value + offset)); OX_RETURN_ERROR(ox::serialize(m_writer, val)); } OX_RETURN_ERROR(m_writer.seekp(0, ox::ios_base::end)); return {}; } template template constexpr ox::Error Preloader::pad(const T *v) noexcept { const auto a = alignOf(*v); const auto excess = m_writer.tellp() % a; if (excess) { return m_writer.write(nullptr, a - excess); } else { return {}; } } template constexpr ox::Error Preloader::fieldVector( StringViewCR name, const ox::ModelValueVector *val) noexcept { // serialize the Vector ox::VectorMemMap vecVal{ .size = PlatSpec::correctEndianness(static_cast(val->size())), .cap = PlatSpec::correctEndianness(static_cast(val->size())), }; return fieldVector(name, val, vecVal); } template template constexpr ox::Error Preloader::fieldVector( StringViewCR name, const ox::Vector *val) noexcept { // serialize the Vector ox::VectorMemMap vecVal{ .smallVecSize = SmallVectorSize * sizeOf(static_cast(nullptr)), .size = PlatSpec::correctEndianness( static_cast(val->size())), .cap = PlatSpec::correctEndianness( static_cast(val->size())), }; return fieldVector(name, val, vecVal); } template constexpr ox::Error Preloader::fieldVector( StringViewCR, const auto *val, ox::VectorMemMap vecVal) noexcept { OX_RETURN_ERROR(pad(&vecVal)); const auto vecValPt = m_writer.tellp(); // serialize the Vector elements if (val->size()) { const auto sz = sizeOf(&(*val)[0]) * val->size(); const auto align = alignOf((*val)[0]); OX_RETURN_ERROR(m_writer.seekp(0, ox::ios_base::end)); auto const padding = calcPadding(align); OX_REQUIRE_M(p, ox::allocate(m_writer, sz + padding)); p += padding; OX_RETURN_ERROR(m_writer.seekp(p)); m_unionIdx.emplace_back(-1); for (std::size_t i = 0; i < val->size(); ++i) { OX_RETURN_ERROR(this->interface()->field(nullptr, &val->operator[](i))); } m_unionIdx.pop_back(); vecVal.items = PlatSpec::correctEndianness( static_cast(p + PlatSpec::RomStart)); OX_RETURN_ERROR(m_writer.seekp(vecValPt)); } else { vecVal.items = 0; } // serialize the Vector OX_RETURN_ERROR(serialize(m_writer, vecVal)); m_ptrs.emplace_back(m_writer.tellp() - PtrSize, vecVal.items); return {}; } template constexpr ox::Error Preloader::fieldArray(StringViewCR, ox::ModelValueArray const*val) noexcept { OX_RETURN_ERROR(pad(&(*val)[0])); for (auto const&v : *val) { OX_RETURN_ERROR(this->interface()->field({}, &v)); } return {}; } template constexpr bool Preloader::unionCheckAndIt() noexcept { auto &u = *m_unionIdx.back().unwrap(); return u.checkAndIterate(); } template constexpr size_t Preloader::calcPadding(size_t align) const noexcept { auto const excess = m_writer.tellp() % align; return (align * (excess != 0)) - excess; } template constexpr ox::Error preload(Preloader *pl, ox::CommonPtrWith auto *obj) noexcept { OX_RETURN_ERROR(model(pl->interface(), obj)); return pl->pad(obj); } extern template class Preloader; }