text:
/////////////////////////////////////////////////////////////////////////
///
///  \file          Serialization_UnitTest.hpp
///  \brief         Unit tests for Serialization.h.
///
///  \author        David Brownell <db@DavidBrownell.com>
///  \date          2019-03-29 14:44:39
///
///  \note
///
///  \bug
///
/////////////////////////////////////////////////////////////////////////
///
///  \attention
///  Copyright David Brownell 2019-21
///  Distributed under the Boost Software License, Version 1.0. See
///  accompanying file LICENSE_1_0.txt or copy at
///  http://www.boost.org/LICENSE_1_0.txt.
///
/////////////////////////////////////////////////////////////////////////
#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#define CATCH_CONFIG_CONSOLE_WIDTH 200
#include "../Serialization.h"
#include <catch.hpp>

#include <CommonHelpers/Compare.h>
#include <CommonHelpers/Copy.h>
#include <CommonHelpers/Constructor.h>
#include <CommonHelpers/Move.h>
#include <CommonHelpers/Stl.h>

#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <boost/serialization/vector.hpp>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>

#include "../Serialization.suffix.h"

template <typename T>
using ComparisonFunc = void (*)(T const &, T const &);

// ----------------------------------------------------------------------
template <typename OArchiveT, typename IArchiveT, typename T>
void TestImplArchive(T const &value, ComparisonFunc<T> comparison_func=nullptr) {
    std::ostringstream                      out;

    value.template Serialize<OArchiveT>(out);
    out.flush();

    std::string                             result(out.str());

    UNSCOPED_INFO(result);

    std::istringstream                      in(result);
    T const                                 other(T::template Deserialize<IArchiveT>(in));

    CHECK(CommonHelpers::Compare(other, value) == 0);
    if(comparison_func)
        comparison_func(other, value);
}

template <typename T>
void TestImpl(T const &value, ComparisonFunc<T> comparison_func=nullptr) {
    TestImplArchive<boost::archive::text_oarchive, boost::archive::text_iarchive>(value, comparison_func);
    TestImplArchive<boost::archive::xml_oarchive, boost::archive::xml_iarchive>(value, comparison_func);
}

// ----------------------------------------------------------------------
template <typename BaseT, typename OArchiveT, typename IArchiveT, typename T>
void PtrTestImplArchive(T const &value, ComparisonFunc<T> comparison_func=nullptr) {
    std::ostringstream                      out;

    value.template SerializePtr<OArchiveT>(out);
    out.flush();

    std::string                             result(out.str());

    UNSCOPED_INFO(result);

    std::istringstream                      in(result);
    std::unique_ptr<BaseT> const            other(T::template DeserializePtr<IArchiveT>(in));

    CHECK(CommonHelpers::Compare(static_cast<T const &>(*other), value) == 0);
    if(comparison_func)
        comparison_func(static_cast<T const &>(*other), value);
}

template <typename BaseT, typename T>
void PtrTestImpl(T const &value, ComparisonFunc<T> comparison_func=nullptr) {
    PtrTestImplArchive<BaseT, boost::archive::text_oarchive, boost::archive::text_iarchive>(value, comparison_func);
    PtrTestImplArchive<BaseT, boost::archive::xml_oarchive, boost::archive::xml_iarchive>(value, comparison_func);
}

// ----------------------------------------------------------------------
template <typename OArchiveT, typename IArchiveT, typename T>
void StandardTestImplArchive(T const &value, ComparisonFunc<T> comparison_func=nullptr) {
    std::ostringstream                      out;

    OArchiveT(out) << boost::serialization::make_nvp("obj", value);
    out.flush();

    std::string                             result(out.str());

    UNSCOPED_INFO(result);

    std::istringstream                      in(result);
    T                                       other;

    IArchiveT(in) >> boost::serialization::make_nvp("obj", other);

    CHECK(CommonHelpers::Compare(other, value) == 0);
    if(comparison_func)
        comparison_func(other, value);
}

template <typename T>
void StandardTestImpl(T const &value, ComparisonFunc<T> comparison_func=nullptr) {
    StandardTestImplArchive<boost::archive::text_oarchive, boost::archive::text_iarchive>(value, comparison_func);
    StandardTestImplArchive<boost::archive::xml_oarchive, boost::archive::xml_iarchive>(value, comparison_func);
}

// ----------------------------------------------------------------------
struct EmptyObj {
    CONSTRUCTOR(EmptyObj, FLAGS(CONSTRUCTOR_BASE_NUM_ARGS(0)));
    NON_COPYABLE(EmptyObj);
    MOVE(EmptyObj);
    COMPARE(EmptyObj);
    SERIALIZATION(EmptyObj);
};

TEST_CASE("EmptyObj") {
    TestImpl(EmptyObj());
}

struct SingleMemberObj {
    int const a;

    CONSTRUCTOR(SingleMemberObj, a);
    NON_COPYABLE(SingleMemberObj);
    MOVE(SingleMemberObj, a);
    COMPARE(SingleMemberObj, a);
    SERIALIZATION(SingleMemberObj, a);
};

TEST_CASE("SingleMemberObj") {
    TestImpl(SingleMemberObj(10));
}

struct SingleBaseObj : public SingleMemberObj {
    CONSTRUCTOR(SingleBaseObj, BASES(SingleMemberObj));
    NON_COPYABLE(SingleBaseObj);
    MOVE(SingleBaseObj, BASES(SingleMemberObj));
    COMPARE(SingleBaseObj, BASES(SingleMemberObj));
    SERIALIZATION(SingleBaseObj, BASES(SingleMemberObj));
};

TEST_CASE("SingleBaseObj") {
    TestImpl(SingleBaseObj(10));
}

struct SingleMemberSingleBaseObj : public SingleMemberObj {
    bool const b;

    CONSTRUCTOR(SingleMemberSingleBaseObj, MEMBERS(b), BASES(SingleMemberObj), FLAGS(CONSTRUCTOR_BASES_BEFORE_MEMBERS));
    NON_COPYABLE(SingleMemberSingleBaseObj);
    MOVE(SingleMemberSingleBaseObj, MEMBERS(b), BASES(SingleMemberObj));
    COMPARE(SingleMemberSingleBaseObj, MEMBERS(b), BASES(SingleMemberObj));
    SERIALIZATION(SingleMemberSingleBaseObj, MEMBERS(b), BASES(SingleMemberObj));
};

TEST_CASE("SingleMemberSingleBaseObj") {
    TestImpl(SingleMemberSingleBaseObj(10, true));
}

struct MultiMemberObj {
    bool const b;
    char const c;

    CONSTRUCTOR(MultiMemberObj, MEMBERS(b, c));
    NON_COPYABLE(MultiMemberObj);
    MOVE(MultiMemberObj, MEMBERS(b, c));
    COMPARE(MultiMemberObj, MEMBERS(b, c));
    SERIALIZATION(MultiMemberObj, MEMBERS(b, c));
};

TEST_CASE("MultiMemberObj") {
    TestImpl(MultiMemberObj(true, 'c'));
}

struct MultiBaseObj : public SingleMemberObj, public MultiMemberObj {
    CONSTRUCTOR(MultiBaseObj, BASES(SingleMemberObj, MultiMemberObj), FLAGS(CONSTRUCTOR_BASE_ARGS_1(2)));
    NON_COPYABLE(MultiBaseObj);
    MOVE(MultiBaseObj, BASES(SingleMemberObj, MultiMemberObj));
    COMPARE(MultiBaseObj, BASES(SingleMemberObj, MultiMemberObj));
    SERIALIZATION(MultiBaseObj, BASES(SingleMemberObj, MultiMemberObj));
};

TEST_CASE("MultiBaseObj") {
    TestImpl(MultiBaseObj(10, true, 'c'));
}

struct MultiMemberMultiBaseObj : public SingleMemberObj, public MultiMemberObj {
    double const d;
    float const f;
    std::unique_ptr<MultiMemberObj> const pMultiMember;

    CONSTRUCTOR(MultiMemberMultiBaseObj, MEMBERS(d, f, pMultiMember), BASES(SingleMemberObj, MultiMemberObj), FLAGS(CONSTRUCTOR_BASE_ARGS_1(2), CONSTRUCTOR_BASES_BEFORE_MEMBERS));
    NON_COPYABLE(MultiMemberMultiBaseObj);
    MOVE(MultiMemberMultiBaseObj, MEMBERS(d, f, pMultiMember), BASES(SingleMemberObj, MultiMemberObj));
    COMPARE(MultiMemberMultiBaseObj, MEMBERS(d, f, pMultiMember), BASES(SingleMemberObj, MultiMemberObj));
    SERIALIZATION(MultiMemberMultiBaseObj, MEMBERS(d, f, pMultiMember), BASES(SingleMemberObj, MultiMemberObj));
};

TEST_CASE("MultiMemberMultiBaseObj") {
    TestImpl(MultiMemberMultiBaseObj(10, true, 'c', 1.0, 2.0f, std::make_unique<MultiMemberObj>(true, 'q')));
}

TEST_CASE("std::unique_ptr") {
    StandardTestImpl(std::make_unique<MultiMemberMultiBaseObj>(10, true, 'c', 1.0, 2.0f, std::make_unique<MultiMemberObj>(true, 'q')));
}

TEST_CASE("std::shared_ptr") {
    StandardTestImpl(std::make_shared<MultiMemberMultiBaseObj>(10, true, 'c', 1.0, 2.0f, std::make_unique<MultiMemberObj>(true, 'q')));
}

TEST_CASE("std::vector") {
    StandardTestImpl(std::vector<int>{ 1, 2, 3 });

    StandardTestImpl(
        CommonHelpers::Stl::CreateVector<MultiMemberMultiBaseObj>(
            MultiMemberMultiBaseObj(10, true, 'c', 1.0, 2.0f, std::make_unique<MultiMemberObj>(true, 'q')),
            MultiMemberMultiBaseObj(20, true, 'z', 3.0, 4.0f, std::make_unique<MultiMemberObj>(true, 'x'))
        )
    );
    StandardTestImpl(
        CommonHelpers::Stl::CreateVector<std::shared_ptr<MultiMemberMultiBaseObj>>(
            std::make_shared<MultiMemberMultiBaseObj>(10, true, 'c', 1.0, 2.0f, std::make_unique<MultiMemberObj>(true, 'q'))
        )
    );

    auto ptr(std::make_shared<MultiMemberMultiBaseObj>(10, true, 'c', 1.0, 2.0f, std::make_unique<MultiMemberObj>(true, 'q')));

    // ----------------------------------------------------------------------
    struct Internal {
        static void Compare(std::vector<std::shared_ptr<MultiMemberMultiBaseObj>> const &new_value, std::vector<std::shared_ptr<MultiMemberMultiBaseObj>> const &value) {
            CHECK(value[0].get() == value[1].get());
            CHECK(value[1].get() == value[2].get());

            CHECK(new_value[0].get() == new_value[1].get());
            CHECK(new_value[1].get() == new_value[2].get());
        }
    };

    StandardTestImpl(std::vector<std::shared_ptr<MultiMemberMultiBaseObj>>{ ptr, ptr, ptr }, Internal::Compare);
}

struct BaseObj {
    int const a;

    CONSTRUCTOR(BaseObj, MEMBERS(a));
    COMPARE(BaseObj, MEMBERS(a));
    SERIALIZATION(BaseObj, MEMBERS(a), FLAGS(SERIALIZATION_ABSTRACT));

    virtual ~BaseObj(void) = default;
    virtual void Method1(void) const = 0;
};

SERIALIZATION_POLYMORPHIC_DECLARE(BaseObj);
SERIALIZATION_POLYMORPHIC_DEFINE(BaseObj);

struct AbstractObj : public BaseObj {
    bool const b;

    CONSTRUCTOR(AbstractObj, MEMBERS(b), BASES(BaseObj), FLAGS(CONSTRUCTOR_BASES_BEFORE_MEMBERS));
    NON_COPYABLE(AbstractObj);
    NON_MOVABLE(AbstractObj);
    COMPARE(AbstractObj, MEMBERS(b), BASES(BaseObj));
    SERIALIZATION(AbstractObj, MEMBERS(b), BASES(BaseObj), FLAGS(SERIALIZATION_DATA_ONLY));

    virtual ~AbstractObj(void) = default;
    virtual void Method2(void) const = 0;
};

struct Derived1Obj : public AbstractObj {
    char const c;

    CONSTRUCTOR(Derived1Obj, MEMBERS(c), BASES(AbstractObj), FLAGS(CONSTRUCTOR_BASES_BEFORE_MEMBERS, CONSTRUCTOR_BASE_ARGS_0(2)));
    COMPARE(Derived1Obj, MEMBERS(c), BASES(AbstractObj));
    SERIALIZATION(Derived1Obj, MEMBERS(c), BASES(AbstractObj), FLAGS(SERIALIZATION_POLYMORPHIC(BaseObj)));

    ~Derived1Obj(void) override = default;

    void Method1(void) const override {}
    void Method2(void) const override {}
};

SERIALIZATION_POLYMORPHIC_DECLARE(Derived1Obj);
SERIALIZATION_POLYMORPHIC_DEFINE(Derived1Obj);

struct Derived2Obj : public AbstractObj {
    double const d;

    CONSTRUCTOR(Derived2Obj, MEMBERS(d), BASES(AbstractObj), FLAGS(CONSTRUCTOR_BASES_BEFORE_MEMBERS, CONSTRUCTOR_BASE_ARGS_0(2)));
    COMPARE(Derived2Obj, MEMBERS(d), BASES(AbstractObj));
    SERIALIZATION(Derived2Obj, MEMBERS(d), BASES(AbstractObj), FLAGS(SERIALIZATION_POLYMORPHIC(BaseObj)));

    ~Derived2Obj(void) override = default;

    void Method1(void) const override {}
    void Method2(void) const override {}
};

SERIALIZATION_POLYMORPHIC_DECLARE_AND_DEFINE(Derived2Obj);

TEST_CASE("Polymorphic") {
    PtrTestImpl<BaseObj>(Derived1Obj(10, true, 'c'));
    PtrTestImpl<BaseObj>(Derived2Obj(10, true, 1.0));

    // ----------------------------------------------------------------------
    using BasePtr                           = std::shared_ptr<BaseObj>;
    using Container                         = std::vector<BasePtr>;

    struct Internal {
        static void Compare(Container const &new_value, Container const &value) {
            REQUIRE(std::dynamic_pointer_cast<Derived1Obj>(value[0]));
            REQUIRE(std::dynamic_pointer_cast<Derived2Obj>(value[1]));
            REQUIRE(std::dynamic_pointer_cast<Derived1Obj>(new_value[0]));
            REQUIRE(std::dynamic_pointer_cast<Derived2Obj>(new_value[1]));

            CHECK(CommonHelpers::Compare(std::static_pointer_cast<Derived1Obj>(new_value[0]), std::static_pointer_cast<Derived1Obj>(value[0])) == 0);
            CHECK(CommonHelpers::Compare(std::static_pointer_cast<Derived2Obj>(new_value[1]), std::static_pointer_cast<Derived2Obj>(value[1])) == 0);
        }
    };
    // ----------------------------------------------------------------------

    StandardTestImpl(BasePtr(std::make_shared<Derived1Obj>(10, true, 'c')));
    StandardTestImpl(Container{ BasePtr(std::make_shared<Derived1Obj>(10, true, 'c')), BasePtr(std::make_shared<Derived2Obj>(20, false, 2.0)) }, Internal::Compare);

    // Empty objects
    StandardTestImpl(std::unique_ptr<BaseObj>());
    StandardTestImpl(std::shared_ptr<BaseObj>());
    StandardTestImpl(Container{ BasePtr() });
}

struct DataCustomConstructorObj {
    struct Value {
        int const a;

        CONSTRUCTOR(Value, a);
        NON_COPYABLE(Value);
        MOVE(Value, a);
        COMPARE(Value, a);

        // Note that we can't use SERIALIZATION macro here, as its local data value will actually be a SerializationPOD object.
        template <typename ArchiveT>
        void serialize(ArchiveT &ar, const unsigned int) {
            ar & boost::serialization::make_nvp("a", make_mutable(a));
        }
    };

    Value const value;

    CONSTRUCTOR(DataCustomConstructorObj, value);
    NON_COPYABLE(DataCustomConstructorObj);
    MOVE(DataCustomConstructorObj, value);
    COMPARE(DataCustomConstructorObj, value);
    SERIALIZATION(DataCustomConstructorObj, MEMBERS(value), FLAGS(SERIALIZATION_DATA_CUSTOM_CONSTRUCTOR));
};

DataCustomConstructorObj::SerializationPOD::DeserializeLocalData::DeserializeLocalData(void)
    : value(-1) {
}

TEST_CASE("DataCustomConstructorObj") {
    TestImpl(DataCustomConstructorObj(DataCustomConstructorObj::Value(10)));
}

class CustomTypesObj {
private:
    // In the following code, the 2 nibbles are serialized as a single byte
    struct SerializeLocalData {
        unsigned char a;

        SerializeLocalData(CustomTypesObj const &obj);
        NON_COPYABLE(SerializeLocalData);
        MOVE(SerializeLocalData, a);

        template <typename ArchiveT>
        void Execute(ArchiveT &ar) const {
            ar << boost::serialization::make_nvp("a", a);
        }
    };

    struct DeserializeLocalData {
        unsigned char a;

        DeserializeLocalData(void) = default;
        NON_COPYABLE(DeserializeLocalData);
        MOVE(DeserializeLocalData, a);

        template <typename ArchiveT>
        void Execute(ArchiveT &ar) {
            ar >> boost::serialization::make_nvp("a", a);
        }
    };

public:
    unsigned char const nibble1: 4;
    unsigned char const nibble2: 4;

    CONSTRUCTOR(CustomTypesObj, nibble1, nibble2);
    COMPARE(CustomTypesObj, nibble1, nibble2);
    SERIALIZATION(CustomTypesObj, FLAGS(SERIALIZATION_DATA_CUSTOM_CONSTRUCTOR, SERIALIZATION_DATA_CUSTOM_TYPES));
};

CustomTypesObj::SerializeLocalData::SerializeLocalData(CustomTypesObj const &obj)
    : a(static_cast<unsigned char>(obj.nibble1 | (obj.nibble2 << 4))) {
}

CustomTypesObj::CustomTypesObj(SerializationPOD::DeserializeData && data)
    : nibble1(data.local.a & 0xFF)
    , nibble2(data.local.a >> 4) {
}

TEST_CASE("CustomTypesObj") {
    TestImpl(CustomTypesObj(static_cast<unsigned char>(10), static_cast<unsigned char>(20)));
}

class EventObj {
public:
    int const a;
    bool const b;

    unsigned long ulConstructCtr = 0;
    unsigned long ulDeserializeConstructCtr = 0;

    CONSTRUCTOR(EventObj, a, b);
    COMPARE(EventObj, a, b);
    SERIALIZATION(EventObj, a, b);

private:
    friend class CommonHelpers::TypeTraits::Access;

    void DeserializeFinalConstruct(void) {
        ++ulDeserializeConstructCtr;
    }

    void FinalConstruct(void) {
        ++ulConstructCtr;
    }
};

TEST_CASE("EventObj") {
    // ----------------------------------------------------------------------
    struct Internal {
        static void Compare(EventObj const &new_value, EventObj const &value) {
            CHECK(value.ulConstructCtr == 1);
            CHECK(value.ulDeserializeConstructCtr == 0);
            CHECK(new_value.ulConstructCtr == 1);
            CHECK(new_value.ulDeserializeConstructCtr == 1);
        }
    };
    // ----------------------------------------------------------------------

    TestImpl(EventObj(10, true), Internal::Compare);
}

TEST_CASE("GetSerializedSize") {
    EventObj const                          obj(10, true);
    size_t const                            value1(obj.GetSerializedSize<boost::archive::text_oarchive>());
    size_t const                            value2(obj.GetSerializedSize<boost::archive::xml_oarchive>());

    CHECK(value1 != 0);
    CHECK(value2 != 0);
    CHECK(value1 != value2);
}

TEST_CASE("GetSerializedPtrSize") {
    Derived1Obj const                       obj(10, true, 'c');
    size_t const                            value1(obj.GetSerializedPtrSize<boost::archive::text_oarchive>());
    size_t const                            value2(obj.GetSerializedPtrSize<boost::archive::xml_oarchive>());

    CHECK(value1 != 0);
    CHECK(value2 != 0);
    CHECK(value1 != value2);
}

struct AdditionalVoidCastBase {
public:
    int const                               a;

    CONSTRUCTOR(AdditionalVoidCastBase, a);
    virtual ~AdditionalVoidCastBase(void) = default;

    COMPARE(AdditionalVoidCastBase, a);
    SERIALIZATION(AdditionalVoidCastBase, MEMBERS(a), FLAGS(SERIALIZATION_POLYMORPHIC_BASE));
};

struct AdditionalVoidCastDerivedData : public AdditionalVoidCastBase {
    bool const                              b;

    CONSTRUCTOR(AdditionalVoidCastDerivedData, MEMBERS(b), BASES(AdditionalVoidCastBase));
    ~AdditionalVoidCastDerivedData(void) override = default;

    COMPARE(AdditionalVoidCastDerivedData, MEMBERS(b), BASES(AdditionalVoidCastBase));
    SERIALIZATION(AdditionalVoidCastDerivedData, MEMBERS(b), BASES(AdditionalVoidCastBase), FLAGS(SERIALIZATION_DATA_ONLY));

    virtual void Method(void) = 0;
};

struct AdditionalVoidCastDerived : public AdditionalVoidCastDerivedData {
    char const                              c;

    CONSTRUCTOR(AdditionalVoidCastDerived, MEMBERS(c), BASES(AdditionalVoidCastDerivedData), FLAGS(CONSTRUCTOR_BASE_ARGS_0(2)));
    ~AdditionalVoidCastDerived(void) override = default;

    COMPARE(AdditionalVoidCastDerived, MEMBERS(c), BASES(AdditionalVoidCastDerivedData));
    SERIALIZATION(AdditionalVoidCastDerived, MEMBERS(c), BASES(AdditionalVoidCastDerivedData), FLAGS(SERIALIZATION_POLYMORPHIC(AdditionalVoidCastBase)));

    // The serialization using 'AdditionalVoidCaseDerivedData' will fail at runtime
    // without this macro.
    SERIALIZATION_POLYMORPHIC_ADDITIONAL_VOID_CASTS(AdditionalVoidCastDerived, AdditionalVoidCastDerivedData);

    void Method(void) override {}
};

SERIALIZATION_POLYMORPHIC_DECLARE_AND_DEFINE(AdditionalVoidCastBase);
SERIALIZATION_POLYMORPHIC_DECLARE_AND_DEFINE(AdditionalVoidCastDerived);

TEST_CASE("AdditionalVoidCast") {
    SECTION("AdditionalVoidCastBase") {
        std::shared_ptr<AdditionalVoidCastBase> const   ptr(std::make_shared<AdditionalVoidCastDerived>('a', true, 10));

        std::ostringstream                  out;

        boost::archive::text_oarchive(out) << ptr;
        out.flush();

        std::string                         result(out.str());

        UNSCOPED_INFO(result);

        std::istringstream                              in(result);
        std::shared_ptr<AdditionalVoidCastBase>         other;

        boost::archive::text_iarchive(in) >> other;

        REQUIRE(dynamic_cast<AdditionalVoidCastDerived *>(other.get()));
        CHECK(
            CommonHelpers::Compare(
                static_cast<AdditionalVoidCastDerived const &>(*ptr),
                static_cast<AdditionalVoidCastDerived const &>(*other)
            ) == 0
        );
    }

    SECTION("AdditionalVoidCastDerivedData") {
        std::shared_ptr<AdditionalVoidCastDerivedData> const                ptr(std::make_shared<AdditionalVoidCastDerived>('a', true, 10));

        std::ostringstream                  out;

        boost::archive::text_oarchive(out) << ptr;
        out.flush();

        std::string                         result(out.str());

        UNSCOPED_INFO(result);

        std::istringstream                                                  in(result);
        std::shared_ptr<AdditionalVoidCastDerivedData>                      other;

        boost::archive::text_iarchive(in) >> other;

        REQUIRE(dynamic_cast<AdditionalVoidCastDerived *>(other.get()));
        CHECK(
            CommonHelpers::Compare(
                static_cast<AdditionalVoidCastDerived const &>(*ptr),
                static_cast<AdditionalVoidCastDerived const &>(*other)
            ) == 0
        );
    }
}


text:
//
// Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BEAST_TEST_FAIL_STREAM_HPP
#define BEAST_TEST_FAIL_STREAM_HPP

#include <beast/core/async_result.hpp>
#include <beast/core/bind_handler.hpp>
#include <beast/core/error.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <beast/websocket/teardown.hpp>
#include <beast/test/fail_counter.hpp>
#include <boost/optional.hpp>

namespace beast {
namespace test {

/** A stream wrapper that fails.

    On the Nth operation, the stream will fail with the specified
    error code, or the default error code of invalid_argument.
*/
template<class NextLayer>
class fail_stream
{
    boost::optional<fail_counter> fc_;
    fail_counter* pfc_;
    NextLayer next_layer_;

public:
    using next_layer_type =
        typename std::remove_reference<NextLayer>::type;

    using lowest_layer_type =
        typename get_lowest_layer<next_layer_type>::type;

    fail_stream(fail_stream&&) = delete;
    fail_stream(fail_stream const&) = delete;
    fail_stream& operator=(fail_stream&&) = delete;
    fail_stream& operator=(fail_stream const&) = delete;

    template<class... Args>
    explicit
    fail_stream(std::size_t n, Args&&... args)
        : fc_(n)
        , pfc_(&*fc_)
        , next_layer_(std::forward<Args>(args)...)
    {
    }

    template<class... Args>
    explicit
    fail_stream(fail_counter& fc, Args&&... args)
        : pfc_(&fc)
        , next_layer_(std::forward<Args>(args)...)
    {
    }

    next_layer_type&
    next_layer()
    {
        return next_layer_;
    }

    lowest_layer_type&
    lowest_layer()
    {
        return next_layer_.lowest_layer();
    }

    lowest_layer_type const&
    lowest_layer() const
    {
        return next_layer_.lowest_layer();
    }

    boost::asio::io_service&
    get_io_service()
    {
        return next_layer_.get_io_service();
    }

    template<class MutableBufferSequence>
    std::size_t
    read_some(MutableBufferSequence const& buffers)
    {
        pfc_->fail();
        return next_layer_.read_some(buffers);
    }

    template<class MutableBufferSequence>
    std::size_t
    read_some(MutableBufferSequence const& buffers, error_code& ec)
    {
        if(pfc_->fail(ec))
            return 0;
        return next_layer_.read_some(buffers, ec);
    }

    template<class MutableBufferSequence, class ReadHandler>
    async_return_type<
        ReadHandler, void(error_code, std::size_t)>
    async_read_some(MutableBufferSequence const& buffers,
        ReadHandler&& handler)
    {
        error_code ec;
        if(pfc_->fail(ec))
        {
            async_completion<ReadHandler,
                void(error_code, std::size_t)> init{handler};
            next_layer_.get_io_service().post(
                bind_handler(init.completion_handler, ec, 0));
            return init.result.get();
        }
        return next_layer_.async_read_some(buffers,
            std::forward<ReadHandler>(handler));
    }

    template<class ConstBufferSequence>
    std::size_t
    write_some(ConstBufferSequence const& buffers)
    {
        pfc_->fail();
        return next_layer_.write_some(buffers);
    }

    template<class ConstBufferSequence>
    std::size_t
    write_some(ConstBufferSequence const& buffers, error_code& ec)
    {
        if(pfc_->fail(ec))
            return 0;
        return next_layer_.write_some(buffers, ec);
    }

    template<class ConstBufferSequence, class WriteHandler>
    async_return_type<
        WriteHandler, void(error_code, std::size_t)>
    async_write_some(ConstBufferSequence const& buffers,
        WriteHandler&& handler)
    {
        error_code ec;
        if(pfc_->fail(ec))
        {
            async_completion<WriteHandler,
                void(error_code, std::size_t)> init{handler};
            next_layer_.get_io_service().post(
                bind_handler(init.completion_handler, ec, 0));
            return init.result.get();
        }
        return next_layer_.async_write_some(buffers,
            std::forward<WriteHandler>(handler));
    }

    friend
    void
    teardown(websocket::teardown_tag,
        fail_stream<NextLayer>& stream,
            boost::system::error_code& ec)
    {
        if(stream.pfc_->fail(ec))
            return;
        beast::websocket_helpers::call_teardown(stream.next_layer(), ec);
    }

    template<class TeardownHandler>
    friend
    void
    async_teardown(websocket::teardown_tag,
        fail_stream<NextLayer>& stream,
            TeardownHandler&& handler)
    {
        error_code ec;
        if(stream.pfc_->fail(ec))
        {
            stream.get_io_service().post(
                bind_handler(std::move(handler), ec));
            return;
        }
        beast::websocket_helpers::call_async_teardown(
            stream.next_layer(), std::forward<TeardownHandler>(handler));
    }
};

} // test
} // beast

#endif

ext: hpp

max_issues_repo_name: madilraza/rippled

max_forks_repo_name: madilraza/rippled


text:
/**
 * Copyright (C) 2019-2021 Xilinx, Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You may
 * not use this file except in compliance with the License. A copy of the
 * License is located at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */

#include "device_linux.h"

#include "core/common/message.h"
#include "core/common/query_requests.h"
#include "core/common/system.h"
#include "core/common/utils.h"
#include "core/include/xcl_app_debug.h"
#include "core/pcie/driver/linux/include/mgmt-ioctl.h"

#include "xrt.h"
#include "scan.h"

#include <array>
#include <fstream>
#include <functional>
#include <iostream>
#include <map>
#include <string>
#include <boost/format.hpp>
#include <boost/tokenizer.hpp>
#include <boost/filesystem.hpp>

namespace {

namespace query = xrt_core::query;
using pdev = std::shared_ptr<pcidev::pci_device>;
using key_type = query::key_type;
const static int LEGACY_COUNT = 4;

static int
get_render_value(const std::string dir)
{
  static const std::string render_name = "renderD";
  int instance_num = INVALID_ID;
  std::string sub;

  boost::filesystem::path render_dirs(dir);
  if (!boost::filesystem::is_directory(render_dirs))
    return instance_num;

  boost::filesystem::recursive_directory_iterator end_iter;
    for(boost::filesystem::recursive_directory_iterator iter(render_dirs); iter != end_iter; ++iter) {
      if (iter->path().filename().string().compare(0,render_name.size(),render_name)== 0) {
        sub = iter->path().filename().string().substr(render_name.size());
        instance_num = std::stoi(sub);
        break;
      }
    }
    return instance_num;
}

inline pdev
get_pcidev(const xrt_core::device* device)
{
  return pcidev::get_dev(device->get_device_id(), device->is_userpf());
}

static std::vector<uint64_t>
get_counter_status_from_sysfs(const std::string &mon_name_address,
                              const std::string &sysfs_file_name,
                              size_t size,
                              const xrt_core::device* device)
{
  auto pdev = get_pcidev(device);

  /* Get full path to "name" sysfs file.
   * Then use that path to form full path to "counters"/"status" sysfs file
   * which contains counter/status data for the monitor
   */
  std::string name_path = pdev->get_sysfs_path(mon_name_address, "name");

  std::size_t pos = name_path.find_last_of('/');
  if (std::string::npos == pos) {
    std::string msg = "Invalid path for name sysfs node for " + mon_name_address;
    throw xrt_core::query::sysfs_error(msg);
  }

  std::string path = name_path.substr(0, pos+1);
  path += sysfs_file_name;

  std::vector<uint64_t> val_buf(size);

  std::ifstream ifs(path);

  try {
    // Enable exception on reading error
    ifs.exceptions(std::ifstream::failbit);
    for (std::size_t idx = 0; idx < size; ++idx)
      ifs >> val_buf[idx];
  } catch (const std::ios_base::failure& fail) {
    std::string msg = "Incomplete counter data read from " + path + " due to " + fail.what()
                         + ".\n Using 0 as default value in results.";
    xrt_core::message::send(xrt_core::message::severity_level::info, "XRT", msg);
  }

  return val_buf;
}

struct bdf
{
  using result_type = query::pcie_bdf::result_type;

  static result_type
  get(const xrt_core::device* device, key_type)
  {
    auto pdev = get_pcidev(device);
    return std::make_tuple(pdev->domain, pdev->bus, pdev->dev, pdev->func);
  }
};

struct kds_cu_stat
{
  using result_type = query::kds_cu_stat::result_type;
  using data_type = query::kds_cu_stat::data_type;

  static result_type
  get(const xrt_core::device* device, key_type)
  {
    auto pdev = get_pcidev(device);

    using tokenizer = boost::tokenizer< boost::char_separator<char> >;
    std::vector<std::string> stats;
    std::string errmsg;

    // The kds_custat_raw is printing in formatted string of each line
    // Format: "%d,%s:%s,0x%lx,0x%x,%lu"
    // Using comma as separator.
    pdev->sysfs_get("", "kds_custat_raw", errmsg, stats);
    if (!errmsg.empty())
      throw xrt_core::query::sysfs_error(errmsg);

    result_type cuStats;
    for (auto& line : stats) {
      boost::char_separator<char> sep(",");
      tokenizer tokens(line, sep);

      if (std::distance(tokens.begin(), tokens.end()) != 5)
        throw xrt_core::query::sysfs_error("CU statistic sysfs node corrupted");

      data_type data;
      const int radix = 16;
      tokenizer::iterator tok_it = tokens.begin();
      data.index     = std::stoi(std::string(*tok_it++));
      data.name      = std::string(*tok_it++);
      data.base_addr = std::stoull(std::string(*tok_it++), nullptr, radix);
      data.status    = std::stoul(std::string(*tok_it++), nullptr, radix);
      data.usages    = std::stoul(std::string(*tok_it++));

      cuStats.push_back(data);
    }

    return cuStats;
  }
};

struct instance
{
  using result_type = query::instance::result_type;

  static result_type
  get(const xrt_core::device* device, key_type)
  {
    static const std::string  dev_root = "/sys/bus/pci/devices/";
    std::string errmsg;
    auto pdev = get_pcidev(device);

    auto sysfsname = boost::str( boost::format("%04x:%02x:%02x.%x") % pdev->domain % pdev->bus % pdev->dev %  pdev->func);
    if(device->is_userpf())
      pdev->instance = get_render_value(dev_root + sysfsname + "/drm");
    else
      pdev->sysfs_get("", "instance", errmsg, pdev->instance,static_cast<uint32_t>(INVALID_ID));

    return pdev->instance;
  }

};

struct kds_scu_stat
{
  using result_type = query::kds_scu_stat::result_type;
  using data_type = query::kds_scu_stat::data_type;
  static constexpr uint32_t scu_domain = 0x10000;

  static result_type
  get(const xrt_core::device* device, key_type)
  {
    auto pdev = get_pcidev(device);

    using tokenizer = boost::tokenizer< boost::char_separator<char> >;
    std::vector<std::string> stats;
    std::string errmsg;

    // The kds_scustat_raw is printing in formatted string of each line
    // Format: "%d,%s:%s,0x%x,%lu"
    // Using comma as separator.
    pdev->sysfs_get("", "kds_scustat_raw", errmsg, stats);
    if (!errmsg.empty())
      throw xrt_core::query::sysfs_error(errmsg);

    result_type cu_stats;
    std::map<std::string, unsigned int> name2idx; // kernel name -> instance index
    for (auto& line : stats) {
      boost::char_separator<char> sep(",");
      tokenizer tokens(line, sep);

      if (std::distance(tokens.begin(), tokens.end()) != 4)
        throw xrt_core::query::sysfs_error("PS kernel statistic sysfs node corrupted");

      data_type data;
      const int radix = 16;
      tokenizer::iterator tok_it = tokens.begin();
      data.index  = std::stoi(std::string(*tok_it++));
      data.name   = std::string(*tok_it++);  // kernel name
      data.status = std::stoul(std::string(*tok_it++), nullptr, radix);
      data.usages = std::stoul(std::string(*tok_it++));

      // TODO: Let's avoid this special handling for PS kernel name
      // Modify instance name to match up with xclbin convention where
      // instance names are numbered 0..n as in kernel_name:[0..n] for
      // kernel_name.  It is important that the instance name matches
      // up with the expectations of core XRT, which is based off of
      // the instance names in the IP_LAYOUT.  By using a knm -> idx
      // map the driver can assign internal indeces in any order it
      // likes and sysfs node does not have to list all kernel
      // instances of a kernel before instances of another kernel.
      auto& kidx = name2idx[data.name];  // integral values are zero-initialized
      data.name += ":scu_" + std::to_string(kidx++);

      cu_stats.push_back(data);
    }

    return cu_stats;
  }
};

struct kds_cu_info
{
  using result_type = query::kds_cu_info::result_type;

  static result_type
  get(const xrt_core::device* device, key_type)
  {
    auto pdev = get_pcidev(device);

    std::vector<std::string> stats;
    std::string errmsg;
    pdev->sysfs_get("mb_scheduler", "kds_custat", errmsg, stats);
    if (!errmsg.empty())
      throw xrt_core::query::sysfs_error(errmsg);

    result_type cuStats;
    for (auto& line : stats) {
	uint32_t base_addr = 0;
	uint32_t usage = 0;
	uint32_t status = 0;
	sscanf(line.c_str(), "CU[@0x%x] : %d status : %d", &base_addr, &usage, &status);
	cuStats.push_back(std::make_tuple(base_addr, usage, status));
    }

    return cuStats;
  }
};

/**
 * qspi write protection status
 * byte 0:
 *   '0': status not available, '1': status available
 * byte 1 primary qspi(if status available):
 *   '1': write protect enable, '2': write protect disable
 * byte 2 recovery qspi(if status available):
 *   '1': write protect enable, '2': write protect disable
 */
struct qspi_status
{
  using result_type = query::xmc_qspi_status::result_type;

  static result_type
  get(const xrt_core::device* device, key_type)
  {
    auto pdev = get_pcidev(device);

    std::string status_str, errmsg;
    pdev->sysfs_get("xmc", "xmc_qspi_status", errmsg, status_str);
    if (!errmsg.empty())
      throw xrt_core::query::sysfs_error(errmsg);

    std::string primary, recovery;
    for (auto status_byte : status_str) {
      if(status_byte == '0')
        return std::pair<std::string, std::string>("N/A", "N/A");

      if(primary.empty())
        primary = status_byte == '1' ? "Enabled": status_byte == '2' ? "Disabled" : "Invalid";
      else
        recovery = status_byte == '1' ? "Enabled": status_byte == '2' ? "Disabled" : "Invalid";
    }
    return std::pair<std::string, std::string>(primary, recovery);
  }
};

struct mac_addr_list
{
  using result_type = query::mac_addr_list::result_type;

  static result_type
  get(const xrt_core::device* device, key_type)
  {
    std::vector<std::string> list;
    auto pdev = get_pcidev(device);

    //legacy code exposes only 4 mac addr sysfs nodes (0-3)
    for (int i=0; i < LEGACY_COUNT; i++) {
      std::string addr, errmsg;
      pdev->sysfs_get("xmc", "mac_addr"+std::to_string(i), errmsg, addr);
      list.push_back(addr);
    }

    return list;
  }
};

/* AIM counter values
 * In PCIe Linux, access the sysfs file for AIM to retrieve the AIM counter values
 */
struct aim_counter
{
  using result_type = query::aim_counter::result_type;

  static result_type
  get(const xrt_core::device* device, key_type key, const boost::any& dbg_ip_dt)
  {
    const auto dbg_ip_data = boost::any_cast<query::aim_counter::debug_ip_data_type>(dbg_ip_dt);

    std::string aim_name("aximm_mon_");
    aim_name += std::to_string(dbg_ip_data->m_base_address);

    result_type retval_buf(XAIM_DEBUG_SAMPLE_COUNTERS_PER_SLOT, 0);

    result_type val_buf = get_counter_status_from_sysfs(aim_name, "counters", XAIM_TOTAL_DEBUG_SAMPLE_COUNTERS_PER_SLOT, device);

    /* Note that required return values are NOT in contiguous sequential order
     * in AIM subdevice file. So, need to read only a few isolated indices in val_buf.
     */
    retval_buf[XAIM_WRITE_BYTES_INDEX]        = val_buf[XAIM_IOCTL_WRITE_BYTES_INDEX];
    retval_buf[XAIM_WRITE_TRANX_INDEX]        = val_buf[XAIM_IOCTL_WRITE_TRANX_INDEX];
    retval_buf[XAIM_READ_BYTES_INDEX]         = val_buf[XAIM_IOCTL_READ_BYTES_INDEX];
    retval_buf[XAIM_READ_TRANX_INDEX]         = val_buf[XAIM_IOCTL_READ_TRANX_INDEX];
    retval_buf[XAIM_OUTSTANDING_COUNT_INDEX]  = val_buf[XAIM_IOCTL_OUTSTANDING_COUNT_INDEX];
    retval_buf[XAIM_WRITE_LAST_ADDRESS_INDEX] = val_buf[XAIM_IOCTL_WRITE_LAST_ADDRESS_INDEX];
    retval_buf[XAIM_WRITE_LAST_DATA_INDEX]    = val_buf[XAIM_IOCTL_WRITE_LAST_DATA_INDEX];
    retval_buf[XAIM_READ_LAST_ADDRESS_INDEX]  = val_buf[XAIM_IOCTL_READ_LAST_ADDRESS_INDEX];
    retval_buf[XAIM_READ_LAST_DATA_INDEX]     = val_buf[XAIM_IOCTL_READ_LAST_DATA_INDEX];

    return retval_buf;
  }

};

/* AM counter values
 * In PCIe Linux, access the sysfs file for AM to retrieve the AM counter values
 */
struct am_counter
{
  using result_type = query::am_counter::result_type;

  static result_type
  get(const xrt_core::device* device, key_type key, const boost::any& dbg_ip_dt)
  {
    const auto dbg_ip_data = boost::any_cast<query::am_counter::debug_ip_data_type>(dbg_ip_dt);

    std::string am_name("accel_mon_");
    am_name += std::to_string(dbg_ip_data->m_base_address);

    result_type val_buf = get_counter_status_from_sysfs(am_name, "counters", XAM_TOTAL_DEBUG_COUNTERS_PER_SLOT, device);

    return val_buf;
  }

};

/* ASM counter values
 * In PCIe Linux, access the sysfs file for ASM to retrieve the ASM counter values
 */
struct asm_counter
{
  using result_type = query::asm_counter::result_type;

  static result_type
  get(const xrt_core::device* device, key_type key, const boost::any& dbg_ip_dt)
  {
    const auto dbg_ip_data = boost::any_cast<query::asm_counter::debug_ip_data_type>(dbg_ip_dt);

    std::string asm_name("axistream_mon_");
    asm_name += std::to_string(dbg_ip_data->m_base_address);

    result_type val_buf = get_counter_status_from_sysfs(asm_name, "counters", XASM_DEBUG_SAMPLE_COUNTERS_PER_SLOT, device);

    return val_buf;
  }
};

/* LAPC status
 * In PCIe Linux, access the sysfs file for LAPC to retrieve the LAPC status
 */
struct lapc_status
{
  using result_type = query::lapc_status::result_type;

  static result_type
  get(const xrt_core::device* device, key_type key, const boost::any& dbg_ip_dt)
  {
    const auto dbg_ip_data = boost::any_cast<query::lapc_status::debug_ip_data_type>(dbg_ip_dt);

    std::string lapc_name("lapc_");
    lapc_name += std::to_string(dbg_ip_data->m_base_address);

    std::vector<uint64_t> val_buf = get_counter_status_from_sysfs(lapc_name, "status", XLAPC_STATUS_PER_SLOT, device);

    result_type ret_val;
    for(auto& e: val_buf) {
      ret_val.push_back(static_cast<uint32_t>(e));
    }

    return ret_val;
  }
};

/* SPC status
 * In PCIe Linux, access the sysfs file for SPC to retrieve the SPC status
 */
struct spc_status
{
  using result_type = query::spc_status::result_type;

  static result_type
  get(const xrt_core::device* device, key_type key, const boost::any& dbg_ip_dt)
  {
    const auto dbg_ip_data = boost::any_cast<query::spc_status::debug_ip_data_type>(dbg_ip_dt);

    std::string spc_name("spc_");
    spc_name += std::to_string(dbg_ip_data->m_base_address);

    std::vector<uint64_t> val_buf = get_counter_status_from_sysfs(spc_name, "status", XSPC_STATUS_PER_SLOT, device);

    result_type ret_val;
    for(auto& e: val_buf) {
      ret_val.push_back(static_cast<uint32_t>(e));
    }

    return ret_val;
  }
};

/* Accelerator Deadlock Detector status
 * In PCIe Linux, access the sysfs file for Accelerator Deadlock Detector to retrieve the deadlock status
 */
struct accel_deadlock_status
{
  using result_type = query::accel_deadlock_status::result_type;

  static result_type
  get(const xrt_core::device* device, key_type key, const boost::any& dbg_ip_dt)
  {
    const auto dbg_ip_data = boost::any_cast<query::accel_deadlock_status::debug_ip_data_type>(dbg_ip_dt);

    std::string mon_name("accel_deadlock_");
    mon_name += std::to_string(dbg_ip_data->m_base_address);

    std::vector<uint64_t> val_buf = get_counter_status_from_sysfs(mon_name, "status", 1, device);

    result_type ret_val = val_buf[0];

    return ret_val;
  }
};

// Specialize for other value types.
template <typename ValueType>
struct sysfs_fcn
{
  static ValueType
  get(const pdev& dev, const char* subdev, const char* entry)
  {
    std::string err;
    ValueType value;
    dev->sysfs_get(subdev, entry, err, value, static_cast<ValueType>(-1));
    if (!err.empty())
      throw xrt_core::query::sysfs_error(err);

    return value;
  }

  static void
  put(const pdev& dev, const char* subdev, const char* entry, ValueType value)
  {
    std::string err;
    dev->sysfs_put(subdev, entry, err, value);
    if (!err.empty())
      throw xrt_core::query::sysfs_error(err);
  }
};

template <>
struct sysfs_fcn<std::string>
{
  using ValueType = std::string;

  static ValueType
  get(const pdev& dev, const char* subdev, const char* entry)
  {
    std::string err;
    ValueType value;
    dev->sysfs_get(subdev, entry, err, value);
    if (!err.empty())
      throw xrt_core::query::sysfs_error(err);

    return value;
  }

  static void
  put(const pdev& dev, const char* subdev, const char* entry, const ValueType& value)
  {
    std::string err;
    dev->sysfs_put(subdev, entry, err, value);
    if (!err.empty())
      throw xrt_core::query::sysfs_error(err);
  }
};

template <typename VectorValueType>
struct sysfs_fcn<std::vector<VectorValueType>>
{
  //using ValueType = std::vector<std::string>;
  using ValueType = std::vector<VectorValueType>;

  static ValueType
  get(const pdev& dev, const char* subdev, const char* entry)
  {
    std::string err;
    ValueType value;
    dev->sysfs_get(subdev, entry, err, value);
    if (!err.empty())
      throw xrt_core::query::sysfs_error(err);

    return value;
  }

  static void
  put(const pdev& dev, const char* subdev, const char* entry, const ValueType& value)
  {
    std::string err;
    dev->sysfs_put(subdev, entry, err, value);
    if (!err.empty())
      throw xrt_core::query::sysfs_error(err);
  }
};

template <typename QueryRequestType>
struct sysfs_get : virtual QueryRequestType
{
  const char* subdev;
  const char* entry;

  sysfs_get(const char* s, const char* e)
    : subdev(s), entry(e)
  {}

  boost::any
  get(const xrt_core::device* device) const
  {
    return sysfs_fcn<typename QueryRequestType::result_type>
      ::get(get_pcidev(device), subdev, entry);
  }

  boost::any
  get(const xrt_core::device* device, query::request::modifier m, const std::string& v) const
  {
    auto ms = (m == query::request::modifier::subdev) ? v.c_str() : subdev;
    auto me = (m == query::request::modifier::entry) ? v.c_str() : entry;
    return sysfs_fcn<typename QueryRequestType::result_type>
      ::get(get_pcidev(device), ms, me);
  }
};

template <typename QueryRequestType>
struct sysfs_put : virtual QueryRequestType
{
  const char* subdev;
  const char* entry;

  sysfs_put(const char* s, const char* e)
    : subdev(s), entry(e)
  {}

  void
  put(const xrt_core::device* device, const boost::any& any) const
  {
    auto value = boost::any_cast<typename QueryRequestType::value_type>(any);
    sysfs_fcn<typename QueryRequestType::value_type>
      ::put(get_pcidev(device), this->subdev, this->entry, value);
  }
};

template <typename QueryRequestType>
struct sysfs_getput : sysfs_get<QueryRequestType>, sysfs_put<QueryRequestType>
{
  sysfs_getput(const char* s, const char* e)
    : sysfs_get<QueryRequestType>(s, e), sysfs_put<QueryRequestType>(s, e)
  {}
};

template <typename QueryRequestType, typename Getter>
struct function0_get : virtual QueryRequestType
{
  boost::any
  get(const xrt_core::device* device) const
  {
    auto k = QueryRequestType::key;
    return Getter::get(device, k);
  }
};

template <typename QueryRequestType, typename Getter>
struct function4_get : virtual QueryRequestType
{
  boost::any
  get(const xrt_core::device* device, const boost::any& arg1) const
  {
    auto k = QueryRequestType::key;
    return Getter::get(device, k, arg1);
  }
};

static std::map<xrt_core::query::key_type, std::unique_ptr<query::request>> query_tbl;

template <typename QueryRequestType>
static void
emplace_sysfs_get(const char* subdev, const char* entry)
{
  auto x = QueryRequestType::key;
  query_tbl.emplace(x, std::make_unique<sysfs_get<QueryRequestType>>(subdev, entry));
}

template <typename QueryRequestType, typename Getter>
static void
emplace_func0_request()
{
  auto k = QueryRequestType::key;
  query_tbl.emplace(k, std::make_unique<function0_get<QueryRequestType, Getter>>());
}

template <typename QueryRequestType, typename Getter>
static void
emplace_func4_request()
{
  auto k = QueryRequestType::key;
  query_tbl.emplace(k, std::make_unique<function4_get<QueryRequestType, Getter>>());
}

template <typename QueryRequestType>
static void
emplace_sysfs_put(const char* subdev, const char* entry)
{
  auto x = QueryRequestType::key;
  query_tbl.emplace(x, std::make_unique<sysfs_put<QueryRequestType>>(subdev, entry));
}

template <typename QueryRequestType>
static void
emplace_sysfs_getput(const char* subdev, const char* entry)
{
  auto x = QueryRequestType::key;
  query_tbl.emplace(x, std::make_unique<sysfs_getput<QueryRequestType>>(subdev, entry));
}

static void
initialize_query_table()
{
  emplace_sysfs_get<query::pcie_vendor>                        ("", "vendor");
  emplace_sysfs_get<query::pcie_device>                        ("", "device");
  emplace_sysfs_get<query::pcie_subsystem_vendor>              ("", "subsystem_vendor");
  emplace_sysfs_get<query::pcie_subsystem_id>                  ("", "subsystem_device");
  emplace_sysfs_get<query::pcie_link_speed>                    ("", "link_speed");
  emplace_sysfs_get<query::pcie_link_speed_max>                ("", "link_speed_max");
  emplace_sysfs_get<query::pcie_express_lane_width>            ("", "link_width");
  emplace_sysfs_get<query::pcie_express_lane_width_max>        ("", "link_width_max");
  emplace_sysfs_get<query::dma_threads_raw>                    ("dma", "channel_stat_raw");
  emplace_sysfs_get<query::rom_vbnv>                           ("rom", "VBNV");
  emplace_sysfs_get<query::rom_ddr_bank_size_gb>               ("rom", "ddr_bank_size");
  emplace_sysfs_get<query::rom_ddr_bank_count_max>             ("rom", "ddr_bank_count_max");
  emplace_sysfs_get<query::rom_fpga_name>                      ("rom", "FPGA");
  emplace_sysfs_get<query::rom_raw>                            ("rom", "raw");
  emplace_sysfs_get<query::rom_uuid>                           ("rom", "uuid");
  emplace_sysfs_get<query::rom_time_since_epoch>               ("rom", "timestamp");
  emplace_sysfs_get<query::xclbin_uuid>                        ("", "xclbinuuid");
  emplace_sysfs_getput<query::ic_enable>                       ("icap_controller", "enable");
  emplace_sysfs_getput<query::ic_load_flash_address>           ("icap_controller", "load_flash_addr");
  emplace_sysfs_get<query::memstat>                            ("", "memstat");
  emplace_sysfs_get<query::memstat_raw>                        ("", "memstat_raw");
  emplace_sysfs_get<query::mem_topology_raw>                   ("icap", "mem_topology");
  emplace_sysfs_get<query::dma_stream>                         ("dma", "");
  emplace_sysfs_get<query::group_topology>                     ("icap", "group_topology");
  emplace_sysfs_get<query::ip_layout_raw>                      ("icap", "ip_layout");
  emplace_sysfs_get<query::debug_ip_layout_raw>                ("icap", "debug_ip_layout");
  emplace_sysfs_get<query::clock_freq_topology_raw>            ("icap", "clock_freq_topology");
  emplace_sysfs_get<query::clock_freqs_mhz>                    ("icap", "clock_freqs");
  emplace_sysfs_get<query::idcode>                             ("icap", "idcode");
  emplace_sysfs_getput<query::data_retention>                  ("icap", "data_retention");
  emplace_sysfs_getput<query::sec_level>                       ("icap", "sec_level");
  emplace_sysfs_get<query::max_shared_host_mem_aperture_bytes> ("icap", "max_host_mem_aperture");
  emplace_sysfs_get<query::status_mig_calibrated>              ("", "mig_calibration");
  emplace_sysfs_getput<query::mig_cache_update>                ("", "mig_cache_update");
  emplace_sysfs_get<query::temp_by_mem_topology>               ("xmc", "temp_by_mem_topology");
  emplace_sysfs_get<query::xmc_version>                        ("xmc", "version");
  emplace_sysfs_get<query::xmc_board_name>                     ("xmc", "bd_name");
  emplace_sysfs_get<query::xmc_serial_num>                     ("xmc", "serial_num");
  emplace_sysfs_get<query::max_power_level>                    ("xmc", "max_power");
  emplace_sysfs_get<query::xmc_sc_presence>                    ("xmc", "sc_presence");
  emplace_sysfs_get<query::is_sc_fixed>                        ("xmc", "sc_is_fixed");
  emplace_sysfs_get<query::xmc_sc_version>                     ("xmc", "bmc_ver");
  emplace_sysfs_get<query::expected_sc_version>                ("xmc", "exp_bmc_ver");
  emplace_sysfs_get<query::xmc_status>                         ("xmc", "status");
  emplace_sysfs_get<query::xmc_reg_base>                       ("xmc", "reg_base");
  emplace_sysfs_get<query::xmc_scaling_support>                ("xmc", "scaling_support");
  emplace_sysfs_getput<query::xmc_scaling_enabled>             ("xmc", "scaling_enabled");
  emplace_sysfs_get<query::xmc_scaling_critical_pow_threshold> ("xmc", "scaling_critical_power_threshold");
  emplace_sysfs_get<query::xmc_scaling_critical_temp_threshold> ("xmc", "scaling_critical_temp_threshold");
  emplace_sysfs_get<query::xmc_scaling_threshold_power_limit>  ("xmc", "scaling_threshold_power_limit");
  emplace_sysfs_get<query::xmc_scaling_threshold_temp_limit>   ("xmc", "scaling_threshold_temp_limit");
  emplace_sysfs_get<query::xmc_scaling_power_override_enable>  ("xmc", "scaling_threshold_power_override_en");
  emplace_sysfs_get<query::xmc_scaling_temp_override_enable>   ("xmc", "scaling_threshold_temp_override_en");
  emplace_sysfs_getput<query::xmc_scaling_power_override>      ("xmc", "scaling_threshold_power_override");
  emplace_sysfs_getput<query::xmc_scaling_temp_override>       ("xmc", "scaling_threshold_temp_override");
  emplace_sysfs_put<query::xmc_scaling_reset>                  ("xmc", "scaling_reset");
  emplace_sysfs_get<query::m2m>                                ("m2m", "");
  emplace_sysfs_get<query::nodma>                              ("", "nodma");
  emplace_sysfs_get<query::dna_serial_num>                     ("dna", "dna");
  emplace_sysfs_get<query::p2p_config>                         ("p2p", "config");
  emplace_sysfs_get<query::temp_card_top_front>                ("xmc", "xmc_se98_temp0");
  emplace_sysfs_get<query::temp_card_top_rear>                 ("xmc", "xmc_se98_temp1");
  emplace_sysfs_get<query::temp_card_bottom_front>             ("xmc", "xmc_se98_temp2");
  emplace_sysfs_get<query::temp_fpga>                          ("xmc", "xmc_fpga_temp");
  emplace_sysfs_get<query::fan_trigger_critical_temp>          ("xmc", "xmc_fan_temp");
  emplace_sysfs_get<query::fan_fan_presence>                   ("xmc", "fan_presence");
  emplace_sysfs_get<query::fan_speed_rpm>                      ("xmc", "xmc_fan_rpm");
  emplace_sysfs_get<query::ddr_temp_0>                         ("xmc", "xmc_ddr_temp0");
  emplace_sysfs_get<query::ddr_temp_1>                         ("xmc", "xmc_ddr_temp1");
  emplace_sysfs_get<query::ddr_temp_2>                         ("xmc", "xmc_ddr_temp2");
  emplace_sysfs_get<query::ddr_temp_3>                         ("xmc", "xmc_ddr_temp3");
  emplace_sysfs_get<query::hbm_temp>                           ("xmc", "xmc_hbm_temp");
  emplace_sysfs_get<query::cage_temp_0>                        ("xmc", "xmc_cage_temp0");
  emplace_sysfs_get<query::cage_temp_1>                        ("xmc", "xmc_cage_temp1");
  emplace_sysfs_get<query::cage_temp_2>                        ("xmc", "xmc_cage_temp2");
  emplace_sysfs_get<query::cage_temp_3>                        ("xmc", "xmc_cage_temp3");
  emplace_sysfs_get<query::v12v_pex_millivolts>                ("xmc", "xmc_12v_pex_vol");
  emplace_sysfs_get<query::v12v_pex_milliamps>                 ("xmc", "xmc_12v_pex_curr");
  emplace_sysfs_get<query::v12v_aux_millivolts>                ("xmc", "xmc_12v_aux_vol");
  emplace_sysfs_get<query::v12v_aux_milliamps>                 ("xmc", "xmc_12v_aux_curr");
  emplace_sysfs_get<query::v3v3_pex_millivolts>                ("xmc", "xmc_3v3_pex_vol");
  emplace_sysfs_get<query::v3v3_aux_millivolts>                ("xmc", "xmc_3v3_aux_vol");
  emplace_sysfs_get<query::v3v3_aux_milliamps>                 ("xmc", "xmc_3v3_aux_cur");
  emplace_sysfs_get<query::ddr_vpp_bottom_millivolts>          ("xmc", "xmc_ddr_vpp_btm");
  emplace_sysfs_get<query::ddr_vpp_top_millivolts>             ("xmc", "xmc_ddr_vpp_top");

  emplace_sysfs_get<query::v5v5_system_millivolts>             ("xmc", "xmc_sys_5v5");
  emplace_sysfs_get<query::v1v2_vcc_top_millivolts>            ("xmc", "xmc_1v2_top");
  emplace_sysfs_get<query::v1v2_vcc_bottom_millivolts>         ("xmc", "xmc_vcc1v2_btm");
  emplace_sysfs_get<query::v1v8_millivolts>                    ("xmc", "xmc_1v8");
  emplace_sysfs_get<query::v0v85_millivolts>                   ("xmc", "xmc_0v85");
  emplace_sysfs_get<query::v0v9_vcc_millivolts>                ("xmc", "xmc_mgt0v9avcc");
  emplace_sysfs_get<query::v12v_sw_millivolts>                 ("xmc", "xmc_12v_sw");
  emplace_sysfs_get<query::mgt_vtt_millivolts>                 ("xmc", "xmc_mgtavtt");
  emplace_sysfs_get<query::int_vcc_millivolts>                 ("xmc", "xmc_vccint_vol");
  emplace_sysfs_get<query::int_vcc_milliamps>                  ("xmc", "xmc_vccint_curr");
  emplace_sysfs_get<query::int_vcc_temp>                       ("xmc", "xmc_vccint_temp");

  emplace_sysfs_get<query::v12_aux1_millivolts>                ("xmc", "xmc_12v_aux1");
  emplace_sysfs_get<query::vcc1v2_i_milliamps>                 ("xmc", "xmc_vcc1v2_i");
  emplace_sysfs_get<query::v12_in_i_milliamps>                 ("xmc", "xmc_v12_in_i");
  emplace_sysfs_get<query::v12_in_aux0_i_milliamps>            ("xmc", "xmc_v12_in_aux0_i");
  emplace_sysfs_get<query::v12_in_aux1_i_milliamps>            ("xmc", "xmc_v12_in_aux1_i");
  emplace_sysfs_get<query::vcc_aux_millivolts>                 ("xmc", "xmc_vccaux");
  emplace_sysfs_get<query::vcc_aux_pmc_millivolts>             ("xmc", "xmc_vccaux_pmc");
  emplace_sysfs_get<query::vcc_ram_millivolts>                 ("xmc", "xmc_vccram");

  emplace_sysfs_get<query::v3v3_pex_milliamps>                 ("xmc", "xmc_3v3_pex_curr");
  emplace_sysfs_get<query::v3v3_aux_milliamps>                 ("xmc", "xmc_3v3_aux_cur");
  emplace_sysfs_get<query::int_vcc_io_milliamps>               ("xmc", "xmc_0v85_curr");
  emplace_sysfs_get<query::v3v3_vcc_millivolts>                ("xmc", "xmc_3v3_vcc_vol");
  emplace_sysfs_get<query::hbm_1v2_millivolts>                 ("xmc", "xmc_hbm_1v2_vol");
  emplace_sysfs_get<query::v2v5_vpp_millivolts>                ("xmc", "xmc_vpp2v5_vol");
  emplace_sysfs_get<query::int_vcc_io_millivolts>              ("xmc", "xmc_vccint_bram_vol");
  emplace_sysfs_get<query::v0v9_int_vcc_vcu_millivolts>        ("xmc", "xmc_vccint_vcu_0v9");
  emplace_sysfs_get<query::mac_contiguous_num>                 ("xmc", "mac_contiguous_num");
  emplace_sysfs_get<query::mac_addr_first>                     ("xmc", "mac_addr_first");
  emplace_sysfs_get<query::oem_id>                             ("xmc", "xmc_oem_id");
  emplace_sysfs_get<query::heartbeat_count>                    ("xmc", "xmc_heartbeat_count");
  emplace_sysfs_get<query::heartbeat_err_code>                 ("xmc", "xmc_heartbeat_err_code");
  emplace_sysfs_get<query::heartbeat_err_time>                 ("xmc", "xmc_heartbeat_err_time");
  emplace_sysfs_get<query::heartbeat_stall>                    ("xmc", "xmc_heartbeat_stall");
  emplace_func0_request<query::xmc_qspi_status,                qspi_status>();
  emplace_func0_request<query::mac_addr_list,                  mac_addr_list>();

  emplace_sysfs_get<query::firewall_detect_level>             ("firewall", "detected_level");
  emplace_sysfs_get<query::firewall_detect_level_name>        ("firewall", "detected_level_name");
  emplace_sysfs_get<query::firewall_status>                   ("firewall", "detected_status");
  emplace_sysfs_get<query::firewall_time_sec>                 ("firewall", "detected_time");

  emplace_sysfs_get<query::power_microwatts>                  ("xmc", "xmc_power");
  emplace_sysfs_get<query::power_warning>                     ("xmc", "xmc_power_warn");
  emplace_sysfs_get<query::host_mem_size>                     ("address_translator", "host_mem_size");
  emplace_sysfs_get<query::kds_numcdmas>                      ("mb_scheduler", "kds_numcdmas");

  //emplace_sysfs_get<query::mig_ecc_enabled>                ("mig", "ecc_enabled");
  emplace_sysfs_get<query::mig_ecc_status>                   ("mig", "ecc_status");
  emplace_sysfs_get<query::mig_ecc_ce_cnt>                   ("mig", "ecc_ce_cnt");
  emplace_sysfs_get<query::mig_ecc_ue_cnt>                   ("mig", "ecc_ue_cnt");
  emplace_sysfs_get<query::mig_ecc_ce_ffa>                   ("mig", "ecc_ce_ffa");
  emplace_sysfs_get<query::mig_ecc_ue_ffa>                   ("mig", "ecc_ue_ffa");
  emplace_sysfs_get<query::flash_bar_offset>                 ("flash", "bar_off");
  emplace_sysfs_get<query::is_mfg>                           ("", "mfg");
  emplace_sysfs_get<query::mfg_ver>                          ("", "mfg_ver");
  emplace_sysfs_get<query::is_recovery>                      ("", "recovery");
  emplace_sysfs_get<query::is_ready>                         ("", "ready");
  emplace_sysfs_get<query::is_offline>                       ("", "dev_offline");
  emplace_sysfs_get<query::f_flash_type>                     ("flash", "flash_type");
  emplace_sysfs_get<query::flash_type>                       ("", "flash_type");
  emplace_sysfs_get<query::board_name>                       ("", "board_name");
  emplace_sysfs_get<query::logic_uuids>                      ("", "logic_uuids");
  emplace_sysfs_get<query::interface_uuids>                  ("", "interface_uuids");
  emplace_sysfs_getput<query::rp_program_status>             ("", "rp_program");
  emplace_sysfs_get<query::shared_host_mem>                  ("", "host_mem_size");
  emplace_sysfs_get<query::enabled_host_mem>                  ("address_translator", "host_mem_size");
  emplace_sysfs_get<query::cpu_affinity>                     ("", "local_cpulist");
  emplace_sysfs_get<query::mailbox_metrics>                  ("mailbox", "recv_metrics");
  emplace_sysfs_get<query::clock_timestamp>                  ("ert_user", "clock_timestamp");
  emplace_sysfs_getput<query::ert_sleep>                     ("ert_user", "mb_sleep");
  emplace_sysfs_get<query::ert_cq_read>                      ("ert_user", "cq_read_cnt");
  emplace_sysfs_get<query::ert_cq_write>                     ("ert_user", "cq_write_cnt");
  emplace_sysfs_get<query::ert_cu_read>                      ("ert_user", "cu_read_cnt");
  emplace_sysfs_get<query::ert_cu_write>                     ("ert_user", "cu_write_cnt");
  emplace_sysfs_get<query::ert_data_integrity>               ("ert_user", "data_integrity");
  emplace_sysfs_getput<query::config_mailbox_channel_disable> ("", "config_mailbox_channel_disable");
  emplace_sysfs_getput<query::config_mailbox_channel_switch> ("", "config_mailbox_channel_switch");
  emplace_sysfs_getput<query::config_xclbin_change>          ("", "config_xclbin_change");
  emplace_sysfs_getput<query::cache_xclbin>                  ("", "cache_xclbin");

  emplace_sysfs_get<query::kds_mode>                         ("", "kds_mode");
  emplace_func0_request<query::kds_cu_stat,                  kds_cu_stat>();
  emplace_func0_request<query::kds_scu_stat,                 kds_scu_stat>();
  emplace_sysfs_get<query::ps_kernel>                        ("icap", "ps_kernel");
  emplace_sysfs_get<query::xocl_errors>                       ("", "xocl_errors");

  emplace_func0_request<query::pcie_bdf,                     bdf>();
  emplace_func0_request<query::kds_cu_info,                  kds_cu_info>();
  emplace_func0_request<query::instance,                     instance>();

  emplace_func4_request<query::aim_counter,                  aim_counter>();
  emplace_func4_request<query::am_counter,                   am_counter>();
  emplace_func4_request<query::asm_counter,                  asm_counter>();
  emplace_func4_request<query::lapc_status,                  lapc_status>();
  emplace_func4_request<query::spc_status,                   spc_status>();
  emplace_func4_request<query::accel_deadlock_status,        accel_deadlock_status>();
}

struct X { X() { initialize_query_table(); }};
static X x;

}

namespace xrt_core {

const query::request&
device_linux::
lookup_query(query::key_type query_key) const
{
  auto it = query_tbl.find(query_key);

  if (it == query_tbl.end())
    throw query::no_such_key(query_key);

  return *(it->second);
}

device_linux::
device_linux(handle_type device_handle, id_type device_id, bool user)
  : shim<device_pcie>(device_handle, device_id, user)
{
}

void
device_linux::
read_dma_stats(boost::property_tree::ptree& pt) const
{
  auto handle = get_device_handle();

  xclDeviceUsage devstat = { 0 };
  xclGetUsageInfo(handle, &devstat);

  boost::property_tree::ptree pt_channels;
  for (unsigned int idx = 0; idx < XCL_DEVICE_USAGE_COUNT; ++idx) {
    boost::property_tree::ptree pt_dma;
    pt_dma.put( "id", std::to_string(get_device_id()));
    pt_dma.put( "h2c", xrt_core::utils::unit_convert(devstat.h2c[idx]) );
    pt_dma.put( "c2h", xrt_core::utils::unit_convert(devstat.c2h[idx]) );

    // Create our array of data
    pt_channels.push_back(std::make_pair("", pt_dma));
  }

  pt.add_child( "transfer_metrics.channels", pt_channels);
}

void
device_linux::
read(uint64_t offset, void* buf, uint64_t len) const
{
  if (auto err = pcidev::get_dev(get_device_id(), false)->pcieBarRead(offset, buf, len))
    throw error(err, "read failed");
}

void
device_linux::
write(uint64_t offset, const void* buf, uint64_t len) const
{
  if (auto err = pcidev::get_dev(get_device_id(), false)->pcieBarWrite(offset, buf, len))
    throw error(err, "write failed");
}

void
device_linux::
reset(query::reset_type& key) const
{
  std::string err;
  pcidev::get_dev(get_device_id(), false)->sysfs_put(key.get_subdev(), key.get_entry(), err, key.get_value());
  if (!err.empty())
    throw error("reset failed");
}

int
device_linux::
open(const std::string& subdev, int flag) const
{
  return pcidev::get_dev(get_device_id(), false)->open(subdev, flag);
}

void
device_linux::
close(int dev_handle) const
{
  pcidev::get_dev(get_device_id(), false)->close(dev_handle);
}

void
device_linux::
xclmgmt_load_xclbin(const char* buffer) const {
  //resolves to xclbin2
  const char xclbin_magic_str[] = { 0x78, 0x63, 0x6c, 0x62, 0x69, 0x6e, 0x32 };
  if (sizeof(buffer) < sizeof(xclbin_magic_str))
    throw xrt_core::error("Xclbin is smaller than expected");
  if (std::memcmp(buffer, xclbin_magic_str, sizeof(xclbin_magic_str)) != 0)
    throw xrt_core::error(boost::str(boost::format("Bad binary version '%s'") % xclbin_magic_str));
  int ret = 0;
  try {
    xrt_core::scope_value_guard<int, std::function<void()>> fd = file_open("", O_RDWR);
    xclmgmt_ioc_bitstream_axlf obj = { reinterpret_cast<axlf *>( const_cast<char*>(buffer) ) };
    ret = pcidev::get_dev(get_device_id(), false)->ioctl(fd.get(), XCLMGMT_IOCICAPDOWNLOAD_AXLF, &obj);
  } catch (const std::exception& e) {
    xrt_core::send_exception_message(e.what(), "Failed to open device");
  }

  if(ret != 0) {
    throw error(ret, "Failed to download xclbin");
  }
}

} // xrt_core

ext: cpp

max_stars_repo_name: subeeshm-xilinx/XRT

max_issues_repo_name: subeeshm-xilinx/XRT

max_forks_repo_name: subeeshm-xilinx/XRT


text:
#pragma once

#include <boost/numeric/ublas/lu.hpp>
#include <immintrin.h>
#include <omp.h>
#include <tuple>

#include "matrix.hpp"

using namespace std;
namespace ub = boost::numeric::ublas;

void lu_factorize(Matrix &m, omp_sched_t sched_type, size_t chunk_size);
void lu_factorize_sequential(Matrix &m);
void lu_factorize_sequential_vectorized(Matrix &m);
void lu_factorize_parallel(Matrix &m, omp_sched_t sched_type,
                           size_t chunk_size);
void lu_factorize_parallel_vectorized(Matrix &m, omp_sched_t sched_type,
                           size_t chunk_size);
bool check_lu_correctness(ub::matrix<double> input, Matrix lu);


text:
#include "shift/render/vk/pass_warp.hpp"
#include <shift/render/vk/renderer.hpp>
#include "shift/render/vk/layer1/shader_module.hpp"
#include "shift/render/vk/layer1/device.hpp"
#include <shift/resource_db/repository.hpp>
#include <shift/resource_db/shader.hpp>
#include <shift/core/exception.hpp>
#include <shift/core/algorithm.hpp>
#include <filesystem>

namespace shift::render::vk
{
pass_warp::pass_warp(vk::layer1::device& device) : pass(device)
{
}

void pass_warp::create_resource_descriptions()
{
}

void pass_warp::destroy_resource_descriptions()
{
}

void pass_warp::create_pipeline(vk::layer1::pipeline_cache& pipeline_cache)
{
  auto& repository = resource_db::repository::singleton_instance();

  // Create descriptor set layout and pipeline layout.
  const auto warp_layout_bindings = core::make_array(
    vk::descriptor_set_layout_binding(
      /* binding */ 0,
      /* descriptor_type */ vk::descriptor_type::uniform_buffer,
      /* descriptor_count */ 1,
      /* stage_flags */ vk::shader_stage_flag::vertex_bit,
      /* immutable_samplers */ nullptr),
    vk::descriptor_set_layout_binding(
      /* binding */ 1,
      /* descriptor_type */ vk::descriptor_type::combined_image_sampler,
      /* descriptor_count */ 1,
      /* stage_flags */ vk::shader_stage_flag::fragment_bit,
      /* immutable_samplers */ nullptr));

  _descriptor_set_layout.create(
    *_device, vk::descriptor_set_layout_create_info(
                /* next */ nullptr,
                /* flags */ vk::descriptor_set_layout_create_flag::none,
                /* binding_count */ warp_layout_bindings.size(),
                /* bindings */ warp_layout_bindings.data()));
  _pipeline_layout.create(*_device,
                          vk::pipeline_layout_create_info(
                            /* next */ nullptr,
                            /* flags */ vk::pipeline_layout_create_flag::none,
                            /* set_layout_count */ 1,
                            /* set_layouts */ &_descriptor_set_layout->handle(),
                            /* push_constant_range_count */ 0,
                            /* push_constant_ranges */ nullptr));

  // Create render pass.
  const auto attachment_descriptions =
    core::make_array(vk::attachment_description(
      /* flags */ vk::attachment_description_flag::none,
      /* format */ vk::format::b8_g8_r8_a8_srgb,
      /* samples */ vk::sample_count_flag::_1_bit,
      /* load_op */ vk::attachment_load_op::clear,
      /* store_op */ vk::attachment_store_op::store,
      /* stencil_load_op */ vk::attachment_load_op::dont_care,
      /* stencil_store_op */ vk::attachment_store_op::dont_care,
      /* initial_layout */ vk::image_layout::color_attachment_optimal,
      /* final_layout */ vk::image_layout::color_attachment_optimal));

  const auto color_references = core::make_array(vk::attachment_reference(
    /* attachment */ 0,
    /* layout */ vk::image_layout::color_attachment_optimal));

  const auto subpass_descriptions = core::make_array(vk::subpass_description(
    /* flags */ vk::subpass_description_flag::none,
    /* pipeline_bind_point */ vk::pipeline_bind_point::graphics,
    /* input_attachment_count */ 0,
    /* input_attachments */ nullptr,
    /* color_attachment_count */ color_references.size(),
    /* color_attachments */ color_references.data(),
    /* resolve_attachments */ nullptr,
    /* depth_stencil_attachment */ nullptr,
    /* preserve_attachment_count */ 0,
    /* preserve_attachments */ nullptr));

  _render_pass = vk::make_framed_shared<vk::layer1::render_pass>(
    *_device, vk::render_pass_create_info(
                /* next */ nullptr,
                /* flags */ vk::render_pass_create_flag::none,
                /* attachment_count */ attachment_descriptions.size(),
                /* attachments */ attachment_descriptions.data(),
                /* subpass_count */ subpass_descriptions.size(),
                /* subpasses */ subpass_descriptions.data(),
                /* dependency_count */ 0,
                /* dependencies */ nullptr));

  // Pass viewport and scissor rects dynamically to command buffer.
  const auto dynamic_states =
    core::make_array(vk::dynamic_state::viewport, vk::dynamic_state::scissor);
  const auto dynamic_state = vk::pipeline_dynamic_state_create_info(
    /* next */ nullptr,
    /* flags */ vk::pipeline_dynamic_state_create_flag::none,
    /* dynamic_state_count */ dynamic_states.size(),
    /* dynamic_states */ dynamic_states.data());

  auto vertex_shader_resource =
    repository.load<resource_db::shader>("resources/shaders/warp.vert.spv");
  auto tess_ctrl_shader_resource =
    repository.load<resource_db::shader>("resources/shaders/warp.tesc.spv");
  auto tess_eval_shader_resource =
    repository.load<resource_db::shader>("resources/shaders/warp.tese.spv");
  auto geometry_shader_resource =
    repository.load<resource_db::shader>("resources/shaders/warp.geom.spv");
  auto fragment_shader_resource =
    repository.load<resource_db::shader>("resources/shaders/warp.frag.spv");
  if (!vertex_shader_resource || !tess_ctrl_shader_resource ||
      !tess_eval_shader_resource || !geometry_shader_resource ||
      !fragment_shader_resource)
  {
    BOOST_THROW_EXCEPTION(
      shift::core::runtime_error()
      << shift::core::context_info("Missing shader resource"));
  }

  core::stack_ptr<vk::layer1::shader_module> vertex_shader;
  vertex_shader.create(
    *_device, vk::shader_module_create_info(
                /* next */ nullptr,
                /* flags */ {},
                /* code_size */ vertex_shader_resource->storage.size() * 4,
                /* code */ vertex_shader_resource->storage.data()));
  core::stack_ptr<vk::layer1::shader_module> tessellation_control_shader;
  tessellation_control_shader.create(
    *_device, vk::shader_module_create_info(
                /* next */ nullptr,
                /* flags */ {},
                /* code_size */ tess_ctrl_shader_resource->storage.size() * 4,
                /* code */ tess_ctrl_shader_resource->storage.data()));
  core::stack_ptr<vk::layer1::shader_module> tessellation_evaluation_shader;
  tessellation_evaluation_shader.create(
    *_device, vk::shader_module_create_info(
                /* next */ nullptr,
                /* flags */ {},
                /* code_size */ tess_eval_shader_resource->storage.size() * 4,
                /* code */ tess_eval_shader_resource->storage.data()));
  core::stack_ptr<vk::layer1::shader_module> geometry_shader;
  geometry_shader.create(
    *_device, vk::shader_module_create_info(
                /* next */ nullptr,
                /* flags */ {},
                /* code_size */ geometry_shader_resource->storage.size() * 4,
                /* code */ geometry_shader_resource->storage.data()));
  core::stack_ptr<vk::layer1::shader_module> fragment_shader;
  fragment_shader.create(
    *_device, vk::shader_module_create_info(
                /* next */ nullptr,
                /* flags */ {},
                /* code_size */ fragment_shader_resource->storage.size() * 4,
                /* code */ fragment_shader_resource->storage.data()));

  const auto shader_stages = core::make_array(
    vk::pipeline_shader_stage_create_info(
      /* next */ nullptr,
      /* flags */ vk::pipeline_shader_stage_create_flag::none,
      /* stage */ vk::shader_stage_flag::vertex_bit,
      /* module */ vertex_shader->handle(),
      /* name */ "main",
      /* specialization_info */ nullptr),
    vk::pipeline_shader_stage_create_info(
      /* next */ nullptr,
      /* flags */ vk::pipeline_shader_stage_create_flag::none,
      /* stage */ vk::shader_stage_flag::tessellation_control_bit,
      /* module */ tessellation_control_shader->handle(),
      /* name */ "main",
      /* specialization_info */ nullptr),
    vk::pipeline_shader_stage_create_info(
      /* next */ nullptr,
      /* flags */ vk::pipeline_shader_stage_create_flag::none,
      /* stage */ vk::shader_stage_flag::tessellation_evaluation_bit,
      /* module */ tessellation_evaluation_shader->handle(),
      /* name */ "main",
      /* specialization_info */ nullptr),
    vk::pipeline_shader_stage_create_info(
      /* next */ nullptr,
      /* flags */ vk::pipeline_shader_stage_create_flag::none,
      /* stage */ vk::shader_stage_flag::geometry_bit,
      /* module */ geometry_shader->handle(),
      /* name */ "main",
      /* specialization_info */ nullptr),
    vk::pipeline_shader_stage_create_info(
      /* next */ nullptr,
      /* flags */ vk::pipeline_shader_stage_create_flag::none,
      /* stage */ vk::shader_stage_flag::fragment_bit,
      /* module */ fragment_shader->handle(),
      /* name */ "main",
      /* specialization_info */ nullptr));

  const auto vertex_input_state = vk::pipeline_vertex_input_state_create_info(
    /* next */ nullptr,
    /* flags */ vk::pipeline_vertex_input_state_create_flag::none,
    /* vertex_binding_description_count */ 0,
    /* vertex_binding_descriptions */ nullptr,
    /* vertex_attribute_description_count */ 0,
    /* vertex_attribute_descriptions */ nullptr);

  const auto input_assembly_state =
    vk::pipeline_input_assembly_state_create_info(
      /* next */ nullptr,
      /* flags */ vk::pipeline_input_assembly_state_create_flag::none,
      /* topology */ vk::primitive_topology::patch_list,
      /* primitive_restart_enable */ VK_FALSE);

  const auto tessellation_state = vk::pipeline_tessellation_state_create_info(
    /* next */ nullptr,
    /* flags */ vk::pipeline_tessellation_state_create_flag::none,
    /* patch_control_points */ 16);

  const auto viewport_state = vk::pipeline_viewport_state_create_info(
    /* next */ nullptr,
    /* flags */ vk::pipeline_viewport_state_create_flag::none,
    /* viewport_count */ 1,  // Dynamic state.
    /* viewports */ nullptr,
    /* scissor_count */ 1,  // Dynamic state.
    /* scissors */ nullptr);

  const auto rasterization_state = vk::pipeline_rasterization_state_create_info(
    /* next */ nullptr,
    /* flags */ vk::pipeline_rasterization_state_create_flag::none,
    /* depth_clamp_enable */ VK_FALSE,
    /* rasterizer_discard_enable */ VK_FALSE,
    /* polygon_mode */ vk::polygon_mode::fill,
    /* cull_mode */ vk::cull_mode_flag::back_bit,
    /* front_face */ vk::front_face::counter_clockwise,
    /* depth_bias_enable */ VK_FALSE,
    /* depth_bias_constant_factor */ 0.0f,
    /* depth_bias_clamp */ 0.0f,
    /* depth_bias_slope_factor */ 0.0f,
    /* line_width */ 1.0f);

  const auto multisample_state = vk::pipeline_multisample_state_create_info(
    /* next */ nullptr,
    /* flags */ vk::pipeline_multisample_state_create_flag::none,
    /* rasterization_samples */ vk::sample_count_flag::_1_bit,
    /* sample_shading_enable */ VK_FALSE,
    /* min_sample_shading */ 0.0f,
    /* sample_mask */ nullptr,
    /* alpha_to_coverage_enable */ VK_FALSE,
    /* alpha_to_one_enable */ VK_FALSE);

  const auto color_blend_attachment_states =
    core::make_array(vk::pipeline_color_blend_attachment_state(
      /* blend_enable */ VK_FALSE,
      /* src_color_blend_factor */ vk::blend_factor::one,
      /* dst_color_blend_factor */ vk::blend_factor::zero,
      /* color_blend_op */ vk::blend_op::add,
      /* src_alpha_blend_factor */ vk::blend_factor::one,
      /* dst_alpha_blend_factor */ vk::blend_factor::zero,
      /* alpha_blend_op */ vk::blend_op::add,
      /* color_write_mask */ vk::color_component_flag::r_bit |
        vk::color_component_flag::g_bit | vk::color_component_flag::b_bit |
        vk::color_component_flag::a_bit));

  const auto color_blend_state = vk::pipeline_color_blend_state_create_info(
    /* next */ nullptr,
    /* flags */ vk::pipeline_color_blend_state_create_flag::none,
    /* logic_op_enable */ VK_FALSE,
    /* logic_op */ vk::logic_op::clear_op,
    /* attachment_count */ color_blend_attachment_states.size(),
    /* attachments */ color_blend_attachment_states.data(),
    /* blend_constants */ {0.0f, 0.0f, 0.0f, 0.0f});

  _pipeline = vk::make_framed_shared<vk::layer1::pipeline>(
    *_device, pipeline_cache,
    vk::graphics_pipeline_create_info(
      /* next */ nullptr,
      /* flags */ vk::pipeline_create_flag::none,
      /* stage_count */ shader_stages.size(),
      /* stages */ shader_stages.data(),
      /* vertex_input_state */ &vertex_input_state,
      /* input_assembly_state */ &input_assembly_state,
      /* tessellation_state */ &tessellation_state,
      /* viewport_state */ &viewport_state,
      /* rasterization_state */ &rasterization_state,
      /* multisample_state */ &multisample_state,
      /* depth_stencil_state */ nullptr,
      /* color_blend_state */ &color_blend_state,
      /* dynamic_state */ &dynamic_state,
      /* layout */ _pipeline_layout->handle(),
      /* render_pass */ _render_pass->handle(),
      /* subpass */ 0,
      /* base_pipeline_handle */ nullptr,
      /* base_pipeline_index */ 0));

  fragment_shader.reset();
  geometry_shader.reset();
  tessellation_evaluation_shader.reset();
  tessellation_control_shader.reset();
  vertex_shader.reset();

  _image_sampler = vk::make_framed_shared<vk::layer1::sampler>(
    *_device, vk::sampler_create_info(
                /* next */ nullptr,
                /* flags */ vk::sampler_create_flag::none,
                /* mag_filter */ vk::filter::linear,
                /* min_filter */ vk::filter::linear,
                /* mipmap_mode */ vk::sampler_mipmap_mode::nearest,
                /* address_mode_u */ vk::sampler_address_mode::repeat,
                /* address_mode_v */ vk::sampler_address_mode::repeat,
                /* address_mode_w */ vk::sampler_address_mode::repeat,
                /* mip_lod_bias */ 0.0f,
                /* anisotropy_enable */ VK_TRUE,
                /* max_anisotropy */ 1.0f,
                /* compare_enable */ VK_FALSE,
                /* compare_op */ vk::compare_op::never,
                /* min_lod */ 0.0f,
                /* max_lod */ 1.0f,
                /* border_color */ vk::border_color::float_transparent_black,
                /* unnormalized_coordinates */ VK_FALSE));
}

void pass_warp::destroy_pipeline()
{
  _image_sampler.reset();
  _pipeline.reset();
  _render_pass.reset();
  _pipeline_layout.reset();
  _descriptor_set_layout.reset();
}

void pass_warp::create_resources(resource_db::scene& /*scene*/)
{
  /// ToDo...
}

void pass_warp::destroy_resources()
{
  /// ToDo...
}

void pass_warp::update(std::uint32_t /*buffer_index*/)
{
}
}
//#include "shift/render/vk/render_passes/warp_instance.hpp"
//#include "shift/render/vk/render_passes/warp_schematic.hpp"
//#include "vk/cube_application.h"
//#include <shift/resource_db/repository.hpp>
//#include <shift/resource_db/shader.hpp>
//#include <shift/core/exception.hpp>
//#include <shift/core/algorithm.hpp>
//#include <shift/core/at_exit_scope.hpp>
//#include <boost/filesystem.hpp>
//#include <fstream>
//#include <chrono>
//
// namespace shift::render::vk::render_passes
//{
// render_pass_warp_instance::render_pass_warp_instance(
//  cube_application& application, vk::device& device,
//  render_pass_warp_class& technique)
//: render_pass_instance(application, device, true), _technique(technique)
//{
//}
//
// void render_pass_warp_instance::pre_update(std::uint32_t buffer_index,
//                                           std::chrono::milliseconds
//                                           frame_time)
//{
//  auto* mapped_data = static_cast<warp_uniform_data*>(
//    _uniform_buffer->address(0, sizeof(warp_uniform_data)));
//
//  for (unsigned int i = 0, patch = 0; patch < patches; ++patch)
//  {
//    for (unsigned int row = 0; row != rows; ++row)
//    {
//      for (unsigned int column = 0; column != columns; ++column, ++i)
//      {
//        mapped_data->vertices[i].position = math::make_vector_from(
//          (column + patch * (columns - 1.0f)) / (columns * patches - patches)
//          *
//              2.0f -
//            1.0f  // +
//          // sinf(frame_time.count() / 1000.0f *
//          //     sinf((patch + 1) * 22.1f + (row + 1) * 12.7f +
//          //          (column + 1) * 15.3f)) *
//          //  0.2f / patches
//          ,
//          row / (rows - 1.0f) * 2.0f - 1.0f  // +
//          // sinf(frame_time.count() / 1000.0f *
//          //     sinf((patch + 1) * 27.1f + (row + 1) * 18.9f +
//          //          (column + 1) * 11.4f)) *
//          //  0.2f
//          ,
//          0.0f, 1.0f);
//        mapped_data->vertices[i].textureCoord = math::make_vector_from(
//          (column + patch * (columns - 1.0f)) / (columns * patches - patches),
//          1.0f - row / (rows - 1.0f), 0.0f, 0.0f);
//      }
//    }
//  }
//
//  render_pass_instance::pre_update(buffer_index, frame_time);
//}
//
// void render_pass_warp_instance::update()
//{
//}
//
// void render_pass_warp_instance::create_resources()
//{
//  render_pass_instance::create_resources();
//
//  // Create buffers.
//  _uniform_buffer = _device->create_buffer(vk::buffer_create_info(
//    /* flags */ vk::buffer_create_flag::none,
//    /* size */ sizeof(warp_uniform_data),
//    /* usage */ vk::buffer_usage_flag::uniform_buffer_bit,
//    /* sharing_mode */ vk::sharing_mode::exclusive,
//    /* queue_family_index_count */ 0,
//    /* queue_family_indices */ nullptr));
//  _uniform_buffer->allocate_storage(
//    vk::memory_property_flag::host_visible_bit |
//    vk::memory_property_flag::host_coherent_bit);
//
//  // Create framebuffer and descriptor sets.
//  const auto& swapchain = _device->window()->swapchain();
//  _framebuffers.reserve(swapchain.image_count());
//
//  _descriptor_set =
//    _device->create_descriptor_set(vk::descriptor_set_allocate_info(
//      /* descriptor_pool */ _application.descriptor_pool().handle(),
//      /* descriptor_set_count */ 1,
//      /* set_layouts */ &_technique.descriptor_set_layout().handle()));
//}
//
// void render_pass_warp_instance::destroy_resources()
//{
//  _descriptor_set.reset();
//  _uniform_buffer.reset();
//  render_pass_instance::destroy_resources();
//}
//
// void render_pass_warp_instance::create_framebuffers(
//  const std::vector<vk::shared_ptr<vk::image_view>>& source_image_views,
//  const std::vector<vk::shared_ptr<vk::image_view>>& target_image_views)
//{
//  render_pass_instance::create_framebuffers();
//
//  const auto& swapchain = _device->window()->swapchain();
//  _framebuffers.reserve(swapchain.image_count());
//  for (std::uint32_t i = 0; i < swapchain.image_count(); ++i)
//  {
//    // Prepare descriptor sets.
//    const auto uniform_buffer_info =
//      core::make_array(vk::descriptor_buffer_info(
//        /* buffer */ _uniform_buffer->handle(),
//        /* offset */ 0,
//        /* range */ _uniform_buffer->size()));
//
//    const auto descriptor_image_info =
//      core::make_array(vk::descriptor_image_info(
//        /* sampler */ _technique.image_sampler().handle(),
//        /* image_view */ source_image_views[i]->handle(),
//        /* image_layout */ vk::image_layout::general));
//
//    _device->update_descriptor_set(vk::write_descriptor_set(
//      /* dst_set */ _descriptor_set->handle(),
//      /* dst_binding */ 0,
//      /* dst_array_element */ 0,
//      /* descriptor_count */ uniform_buffer_info.size(),
//      /* descriptor_type */ vk::descriptor_type::uniform_buffer,
//      /* image_info */ nullptr,
//      /* buffer_info */ uniform_buffer_info.data(),
//      /* texel_buffer_view */ nullptr));
//    _device->update_descriptor_set(vk::write_descriptor_set(
//      /* dst_set */ _descriptor_set->handle(),
//      /* dst_binding */ 1,
//      /* dst_array_element */ 0,
//      /* descriptor_count */ descriptor_image_info.size(),
//      /* descriptor_type */ vk::descriptor_type::combined_image_sampler,
//      /* image_info */ descriptor_image_info.data(),
//      /* buffer_info */ nullptr,
//      /* texel_buffer_view */ nullptr));
//
//    // Prepare framebuffer.
//    _framebuffers.push_back(
//      _device->create_framebuffer(vk::framebuffer_create_info(
//        /* flags */ vk::framebuffer_create_flag::none,
//        /* render_pass */ _technique.render_pass().handle(),
//        /* attachment_count */ 1,
//        /* attachments */ &target_image_views[i]->handle(),
//        /* width */ target_image_views[i]->image().extent().width(),
//        /* height */ target_image_views[i]->image().extent().height(),
//        /* layers */ 1u)));
//  }
//}
//
// void render_pass_warp_instance::destroy_framebuffers()
//{
//  _framebuffers.clear();
//  render_pass_instance::destroy_framebuffers();
//}
//
// void render_pass_warp_instance::execute(
//  vk::command_buffer& primary_command_buffer, std::uint32_t current_buffer,
//  const vk::rect_2d& render_area)
//{
//  const auto clear_values = core::make_array(vk::clear_value(
//    vk::clear_color_value(core::make_array(0.0f, 0.0f, 0.0f, 0.0f))));
//
//  primary_command_buffer.begin_render_pass(
//    _technique.render_pass(), *_framebuffers[current_buffer], render_area,
//    clear_values.size(), clear_values.data(),
//    vk::subpass_contents::secondary_command_buffers);
//  primary_command_buffer.execute_commands(*_command_buffers[current_buffer]);
//  primary_command_buffer.end_render_pass();
//}
//
// void render_pass_warp_instance::build_command_buffer(std::uint32_t
// buffer_index)
//{
//  /// ToDo: Memory leak! Stash object on a graveyard.
//  _command_buffers[buffer_index].reset();
//  _command_buffers[buffer_index] = _device->create_command_buffer(
//    queue_type::graphics, _transient_command_buffer,
//    vk::command_buffer_level::secondary);
//
//  _command_buffers[buffer_index]->begin(vk::command_buffer_begin_info(
//    /* flags */ vk::command_buffer_usage_flag::render_pass_continue_bit |
//      vk::command_buffer_usage_flag::simultaneous_use_bit,
//    /* inheritance_info */ core::rvalue_address(
//      vk::command_buffer_inheritance_info(
//        /* render_pass */ _technique.render_pass().handle(),
//        /* subpass */ 0,
//        /* framebuffer */ _framebuffers[buffer_index]->handle(),
//        /* occlusion_query_enable */ VK_FALSE,
//        /* query_flags */ vk::query_control_flag::none,
//        /* pipeline_statistics */ vk::query_pipeline_statistic_flag::none))));
//
//  _command_buffers[buffer_index]->bind_pipeline(
//    vk::pipeline_bind_point::graphics, _technique.pipeline());
//  _command_buffers[buffer_index]->bind_descriptor_sets(
//    vk::pipeline_bind_point::graphics, _technique.pipeline_layout(), 0,
//    core::make_array(_descriptor_set));
//
//  auto window_size = _application.primary_window().size();
//  const auto viewports = core::make_array(vk::viewport(
//    /* x */ 0.0f,
//    /* y */ 0.0f,
//    /* width */ static_cast<float>(window_size.width),
//    /* height */ static_cast<float>(window_size.height),
//    /* min_depth */ 0.0f,
//    /* max_depth */ 1.0f));
//  _command_buffers[buffer_index]->set_viewports(0, viewports);
//
//  const auto scissors = core::make_array(vk::rect_2d(
//    /* offset */ vk::offset_2d{0, 0},
//    /* extent */ vk::extent_2d{window_size.width, window_size.height}));
//  _command_buffers[buffer_index]->set_scissors(0, scissors);
//
//  _command_buffers[buffer_index]->draw(patches * rows * columns, 1, 0, 0);
//  _command_buffers[buffer_index]->end();
//}
//}

max_stars_repo_name: cspanier/shift

max_issues_repo_name: cspanier/shift

max_forks_repo_name: cspanier/shift


text:
#pragma once
#include <vector>
#include <memory>
#include <string>
#include <utility>
#include <map>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <spdlog_easy.hpp>
#include <boost/algorithm/hex.hpp>
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include "buffer.h"

inline std::string make_uuid()
{
    auto uuid = boost::uuids::random_generator()();
    return std::move(boost::uuids::to_string(uuid));
}

template<typename Range>
std::string to_hex(const Range& range)
{
    using namespace std;
    string hex;
    boost::algorithm::hex(begin(range), end(range), back_inserter(hex));
    return std::move(hex);
}

class session_t
{
    using io_context_t = boost::asio::io_context;
    using acceptor_t = boost::asio::ip::tcp::acceptor;
    using socket_t = boost::asio::ip::tcp::socket;
public:

    session_t() = default;

    session_t(
        std::shared_ptr<io_context_t> _io_context,
        std::shared_ptr<acceptor_t> _acceptor)
        :io_context(_io_context), acceptor(_acceptor), socket(*io_context),
        id(make_uuid()), unix_time(0)
    {
        LOG(info, id);
    }

    ~session_t()
    {
        LOG(info, id);
    }

    std::shared_ptr<io_context_t> io_context;
    std::shared_ptr<acceptor_t> acceptor;
    socket_t socket;
    buffer_t read_buffer;
    buffer_t write_buffer;
    std::string id;
    uint64_t unix_time;
};

template<typename F>
void async_accept(std::shared_ptr<session_t> session, F f)
{
    using error_code_t = boost::system::error_code;
    auto acceptor = session->acceptor;
    auto& socket = session->socket;
    acceptor->async_accept(socket,
        [session, f](error_code_t ec) {
        f(session, ec);
    });
}

template<typename F>
void async_read(std::shared_ptr<session_t> session, std::size_t read_size, F f)
{
    using error_code_t = boost::system::error_code;
    auto& socket = session->socket;
    auto& read_buffer = session->read_buffer;
    auto newsize = read_buffer.offset() + read_size;
    if (newsize > read_buffer.size())
    {
        read_buffer.resize(newsize);
    }
    socket.async_read_some(
        boost::asio::buffer(read_buffer.data() + read_buffer.offset(), read_size),
        [f, session](error_code_t ec, std::size_t size) {
        if (ec)
        {
            f(session, ec);
            return;
        }
        session->read_buffer.offset() += size;
        f(session, ec);
    });
}

template<typename F>
void async_write(std::shared_ptr<session_t> session, std::size_t write_size, F f)
{
    using error_code_t = boost::system::error_code;
    auto& socket = session->socket;
    auto& write_buffer = session->write_buffer;
    socket.async_write_some(
        boost::asio::buffer(write_buffer.data() + write_buffer.offset(), write_size),
        [f, session](error_code_t ec, std::size_t size) {
        if (ec)
        {
            f(session, ec);
            return;
        }
        session->write_buffer.offset() += size;
        f(session, ec);
    });
}

max_stars_repo_name: gwankyun/asio-echo-server

max_issues_repo_name: gwankyun/asio-echo-server

max_forks_repo_name: gwankyun/asio-echo-server