<commit_before>#ifndef ENTT_CORE_MEMORY_HPP
#define ENTT_CORE_MEMORY_HPP

#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"

namespace entt {

/**
 * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
 * @tparam Type Pointer type.
 * @param ptr Fancy or raw pointer.
 * @return A raw pointer that represents the address of the original pointer.
 */
template<typename Type>
[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
    if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
        return ptr;
    } else {
        return to_address(std::forward<Type>(ptr).operator->());
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
        lhs = rhs;
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
        lhs = std::move(rhs);
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");

    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
        using std::swap;
        swap(lhs, rhs);
    }
}

/**
 * @brief Checks whether a value is a power of two or not.
 * @param value A value that may or may not be a power of two.
 * @return True if the value is a power of two, false otherwise.
 */
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
    return value && ((value & (value - 1)) == 0);
}

/**
 * @brief Computes the smallest power of two greater than or equal to a value.
 * @param value The value to use.
 * @return The smallest power of two greater than or equal to the given value.
 */
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
    ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
    std::size_t curr = value - (value != 0u);

    for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
        curr |= curr >> next;
    }

    return ++curr;
}

/**
 * @brief Fast module utility function (powers of two only).
 * @param value A value for which to calculate the modulus.
 * @param mod _Modulus_, it must be a power of two.
 * @return The common remainder.
 */
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
    ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
    return value & (mod - 1u);
}

/**
 * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
 * @tparam Args Types of arguments to use to construct the object.
 */
template<typename Allocator>
struct allocation_deleter: private Allocator {
    /*! @brief Allocator type. */
    using allocator_type = Allocator;
    /*! @brief Pointer type. */
    using pointer = typename std::allocator_traits<Allocator>::pointer;

    /**
     * @brief Inherited constructors.
     * @param alloc The allocator to use.
     */
    allocation_deleter(const allocator_type &alloc)
        : Allocator{alloc} {}

    /**
     * @brief Destroys the pointed object and deallocates its memory.
     * @param ptr A valid pointer to an object of the given type.
     */
    void operator()(pointer ptr) {
        using alloc_traits = typename std::allocator_traits<Allocator>;
        alloc_traits::destroy(*this, to_address(ptr));
        alloc_traits::deallocate(*this, ptr, 1u);
    }
};

/**
 * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
 * @tparam Type Type of object to allocate for and to construct.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct the object.
 * @return A properly initialized unique pointer with a custom deleter.
 */
template<typename Type, typename Allocator, typename... Args>
auto allocate_unique(Allocator &allocator, Args &&...args) {
    static_assert(!std::is_array_v<Type>, "Array types are not supported");

    using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
    using allocator_type = typename alloc_traits::allocator_type;

    allocator_type alloc{allocator};
    auto ptr = alloc_traits::allocate(alloc, 1u);

    ENTT_TRY {
        alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
    }
    ENTT_CATCH {
        alloc_traits::deallocate(alloc, ptr, 1u);
        ENTT_THROW;
    }

    return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
}

/**
 * @cond TURN_OFF_DOXYGEN
 * Internal details not to be documented.
 */

namespace internal {

template<typename Type>
struct uses_allocator_construction {
    template<typename Allocator, typename... Params>
    static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
        if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
            return std::forward_as_tuple(std::forward<Params>(params)...);
        } else {
            static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");

            if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
                return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
            } else {
                static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
                return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
            }
        }
    }
};

template<typename Type, typename Other>
struct uses_allocator_construction<std::pair<Type, Other>> {
    using type = std::pair<Type, Other>;

    template<typename Allocator, typename First, typename Second>
    static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
        return std::make_tuple(
            std::piecewise_construct,
            std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
            std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
    }

    template<typename Allocator>
    static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
    }

    template<typename Allocator, typename First, typename Second>
    static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
    }

    template<typename Allocator, typename First, typename Second>
    static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
    }

    template<typename Allocator, typename First, typename Second>
    static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
    }
};

} // namespace internal

/**
 * Internal details not to be documented.
 * @endcond
 */

/**
 * @brief Uses-allocator construction utility (waiting for C++20).
 *
 * Primarily intended for internal use. Prepares the argument list needed to
 * create an object of a given type by means of uses-allocator construction.
 *
 * @tparam Type Type to return arguments for.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct the object.
 * @return The arguments needed to create an object of the given type.
 */
template<typename Type, typename Allocator, typename... Args>
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
    return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
}

/**
 * @brief Uses-allocator construction utility (waiting for C++20).
 *
 * Primarily intended for internal use. Creates an object of a given type by
 * means of uses-allocator construction.
 *
 * @tparam Type Type of object to create.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct the object.
 * @return A newly created object of the given type.
 */
template<typename Type, typename Allocator, typename... Args>
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
    return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}

/**
 * @brief Uses-allocator construction utility (waiting for C++20).
 *
 * Primarily intended for internal use. Creates an object of a given type by
 * means of uses-allocator construction at an uninitialized memory location.
 *
 * @tparam Type Type of object to create.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param value Memory location in which to place the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct the object.
 * @return A pointer to the newly created object of the given type.
 */
template<typename Type, typename Allocator, typename... Args>
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
    return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}

} // namespace entt

#endif
<commit_msg>memory: constexpr-ness all the way (see #883)<commit_after>#ifndef ENTT_CORE_MEMORY_HPP
#define ENTT_CORE_MEMORY_HPP

#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"

namespace entt {

/**
 * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
 * @tparam Type Pointer type.
 * @param ptr Fancy or raw pointer.
 * @return A raw pointer that represents the address of the original pointer.
 */
template<typename Type>
[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
    if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
        return ptr;
    } else {
        return to_address(std::forward<Type>(ptr).operator->());
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
        lhs = rhs;
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
        lhs = std::move(rhs);
    }
}

/**
 * @brief Utility function to design allocation-aware containers.
 * @tparam Allocator Type of allocator.
 * @param lhs A valid allocator.
 * @param rhs Another valid allocator.
 */
template<typename Allocator>
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
    ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");

    if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
        using std::swap;
        swap(lhs, rhs);
    }
}

/**
 * @brief Checks whether a value is a power of two or not.
 * @param value A value that may or may not be a power of two.
 * @return True if the value is a power of two, false otherwise.
 */
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
    return value && ((value & (value - 1)) == 0);
}

/**
 * @brief Computes the smallest power of two greater than or equal to a value.
 * @param value The value to use.
 * @return The smallest power of two greater than or equal to the given value.
 */
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
    ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
    std::size_t curr = value - (value != 0u);

    for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
        curr |= curr >> next;
    }

    return ++curr;
}

/**
 * @brief Fast module utility function (powers of two only).
 * @param value A value for which to calculate the modulus.
 * @param mod _Modulus_, it must be a power of two.
 * @return The common remainder.
 */
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
    ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
    return value & (mod - 1u);
}

/**
 * @brief Deleter for allocator-aware unique pointers (waiting for C++20).
 * @tparam Args Types of arguments to use to construct the object.
 */
template<typename Allocator>
struct allocation_deleter: private Allocator {
    /*! @brief Allocator type. */
    using allocator_type = Allocator;
    /*! @brief Pointer type. */
    using pointer = typename std::allocator_traits<Allocator>::pointer;

    /**
     * @brief Inherited constructors.
     * @param alloc The allocator to use.
     */
    constexpr allocation_deleter(const allocator_type &alloc)
        : Allocator{alloc} {}

    /**
     * @brief Destroys the pointed object and deallocates its memory.
     * @param ptr A valid pointer to an object of the given type.
     */
    constexpr void operator()(pointer ptr) {
        using alloc_traits = typename std::allocator_traits<Allocator>;
        alloc_traits::destroy(*this, to_address(ptr));
        alloc_traits::deallocate(*this, ptr, 1u);
    }
};

/**
 * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20).
 * @tparam Type Type of object to allocate for and to construct.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct the object.
 * @return A properly initialized unique pointer with a custom deleter.
 */
template<typename Type, typename Allocator, typename... Args>
ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
    static_assert(!std::is_array_v<Type>, "Array types are not supported");

    using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
    using allocator_type = typename alloc_traits::allocator_type;

    allocator_type alloc{allocator};
    auto ptr = alloc_traits::allocate(alloc, 1u);

    ENTT_TRY {
        alloc_traits::construct(alloc, to_address(ptr), std::forward<Args>(args)...);
    }
    ENTT_CATCH {
        alloc_traits::deallocate(alloc, ptr, 1u);
        ENTT_THROW;
    }

    return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
}

/**
 * @cond TURN_OFF_DOXYGEN
 * Internal details not to be documented.
 */

namespace internal {

template<typename Type>
struct uses_allocator_construction {
    template<typename Allocator, typename... Params>
    static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
        if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
            return std::forward_as_tuple(std::forward<Params>(params)...);
        } else {
            static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");

            if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
                return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
            } else {
                static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
                return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
            }
        }
    }
};

template<typename Type, typename Other>
struct uses_allocator_construction<std::pair<Type, Other>> {
    using type = std::pair<Type, Other>;

    template<typename Allocator, typename First, typename Second>
    static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
        return std::make_tuple(
            std::piecewise_construct,
            std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
            std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<Second>(second)));
    }

    template<typename Allocator>
    static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
    }

    template<typename Allocator, typename First, typename Second>
    static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
    }

    template<typename Allocator, typename First, typename Second>
    static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
    }

    template<typename Allocator, typename First, typename Second>
    static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
        return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
    }
};

} // namespace internal

/**
 * Internal details not to be documented.
 * @endcond
 */

/**
 * @brief Uses-allocator construction utility (waiting for C++20).
 *
 * Primarily intended for internal use. Prepares the argument list needed to
 * create an object of a given type by means of uses-allocator construction.
 *
 * @tparam Type Type to return arguments for.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct the object.
 * @return The arguments needed to create an object of the given type.
 */
template<typename Type, typename Allocator, typename... Args>
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
    return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
}

/**
 * @brief Uses-allocator construction utility (waiting for C++20).
 *
 * Primarily intended for internal use. Creates an object of a given type by
 * means of uses-allocator construction.
 *
 * @tparam Type Type of object to create.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct the object.
 * @return A newly created object of the given type.
 */
template<typename Type, typename Allocator, typename... Args>
constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) {
    return std::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}

/**
 * @brief Uses-allocator construction utility (waiting for C++20).
 *
 * Primarily intended for internal use. Creates an object of a given type by
 * means of uses-allocator construction at an uninitialized memory location.
 *
 * @tparam Type Type of object to create.
 * @tparam Allocator Type of allocator used to manage memory and elements.
 * @tparam Args Types of arguments to use to construct the object.
 * @param value Memory location in which to place the object.
 * @param allocator The allocator to use.
 * @param args Parameters to use to construct the object.
 * @return A pointer to the newly created object of the given type.
 */
template<typename Type, typename Allocator, typename... Args>
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
    return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}

} // namespace entt

#endif
<|endoftext|>


<commit_before>#ifndef ENTT_ENTITY_POOL_HPP
#define ENTT_ENTITY_POOL_HPP

#include <iterator>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "../signal/sigh.hpp"
#include "fwd.hpp"
#include "storage.hpp"

namespace entt {

/**
 * @brief Mixin type to use to wrap basic storage classes.
 * @tparam Storage The type of the underlying storage.
 */
template<typename Storage>
struct storage_adapter_mixin: Storage {
    static_assert(std::is_same_v<typename Storage::value_type, std::decay_t<typename Storage::value_type>>, "Invalid object type");

    /*! @brief Type of the objects associated with the entities. */
    using value_type = typename Storage::value_type;
    /*! @brief Underlying entity identifier. */
    using entity_type = typename Storage::entity_type;

    /**
     * @brief Assigns entities to a pool.
     * @tparam Args Types of arguments to use to construct the object.
     * @param entity A valid entity identifier.
     * @param args Parameters to use to initialize the object.
     * @return A reference to the newly created object.
     */
    template<typename... Args>
    decltype(auto) emplace(basic_registry<entity_type> &, const entity_type entity, Args &&... args) {
        return Storage::emplace(entity, std::forward<Args>(args)...);
    }

    /**
     * @brief Assigns entities to a pool.
     * @tparam It Type of input iterator.
     * @tparam Args Types of arguments to use to construct the objects
     * associated with the entities.
     * @param first An iterator to the first element of the range of entities.
     * @param last An iterator past the last element of the range of entities.
     * @param args Parameters to use to initialize the objects associated with
     * the entities.
     */
    template<typename It, typename... Args>
    void insert(basic_registry<entity_type> &, It first, It last, Args &&... args) {
        Storage::insert(first, last, std::forward<Args>(args)...);
    }

    /**
     * @brief Removes entities from a pool.
     * @param entity A valid entity identifier.
     */
    void remove(basic_registry<entity_type> &, const entity_type entity) {
        Storage::remove(entity);
    }

    /**
     * @copybrief remove
     * @tparam It Type of input iterator.
     * @param first An iterator to the first element of the range of entities.
     * @param last An iterator past the last element of the range of entities.
     */
    template<typename It>
    void remove(basic_registry<entity_type> &, It first, It last) {
        Storage::remove(first, last);
    }

    /**
     * @brief Patches the given instance for an entity.
     * @tparam Func Types of the function objects to invoke.
     * @param entity A valid entity identifier.
     * @param func Valid function objects.
     * @return A reference to the patched instance.
     */
    template<typename... Func>
    decltype(auto) patch(basic_registry<entity_type> &, const entity_type entity, [[maybe_unused]] Func &&... func) {
        auto &instance = this->get(entity);
        (std::forward<Func>(func)(instance), ...);
        return instance;
    }
};

/**
 * @brief Mixin type to use to add signal support to pools.
 * @tparam Pool The type of the underlying pool.
 */
template<typename Pool>
struct sigh_pool_mixin: Pool {
    /*! @brief Underlying value type. */
    using value_type = typename Pool::value_type;
    /*! @brief Underlying entity identifier. */
    using entity_type = typename Pool::entity_type;

    /**
     * @brief Returns a sink object.
     *
     * The sink returned by this function can be used to receive notifications
     * whenever a new instance is created and assigned to an entity.

     * The function type for a listener is equivalent to:
     *
     * @code{.cpp}
     * void(basic_registry<entity_type> &, entity_type);
     * @endcode
     *
     * Listeners are invoked **after** the object has been assigned to the
     * entity.
     *
     * @sa sink
     *
     * @return A temporary sink object.
     */
    [[nodiscard]] auto on_construct() ENTT_NOEXCEPT {
        return sink{construction};
    }

    /**
     * @brief Returns a sink object.
     *
     * The sink returned by this function can be used to receive notifications
     * whenever an instance is explicitly updated.

     * The function type for a listener is equivalent to:
     *
     * @code{.cpp}
     * void(basic_registry<entity_type> &, entity_type);
     * @endcode
     *
     * Listeners are invoked **after** the object has been updated.
     *
     * @sa sink
     *
     * @return A temporary sink object.
     */
    [[nodiscard]] auto on_update() ENTT_NOEXCEPT {
        return sink{update};
    }

    /**
     * @brief Returns a sink object.
     *
     * The sink returned by this function can be used to receive notifications
     * whenever an instance is removed from an entity and thus destroyed.

     * The function type for a listener is equivalent to:
     *
     * @code{.cpp}
     * void(basic_registry<entity_type> &, entity_type);
     * @endcode
     *
     * Listeners are invoked **before** the object has been removed from the
     * entity.
     *
     * @sa sink
     *
     * @return A temporary sink object.
     */
    [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT {
        return sink{destruction};
    }

    /**
     * @copybrief storage_adapter_mixin::emplace
     * @tparam Args Types of arguments to use to construct the object.
     * @param owner The registry that issued the request.
     * @param entity A valid entity identifier.
     * @param args Parameters to use to initialize the object.
     * @return A reference to the newly created object.
     */
    template<typename... Args>
    decltype(auto) emplace(basic_registry<entity_type> &owner, const entity_type entity, Args &&... args) {
        Pool::emplace(owner, entity, std::forward<Args>(args)...);
        construction.publish(owner, entity);

        if constexpr(!is_empty_v<value_type>) {
            return this->get(entity);
        }
    }

    /**
     * @copybrief storage_adapter_mixin::insert
     * @tparam It Type of input iterator.
     * @tparam Args Types of arguments to use to construct the objects
     * associated with the entities.
     * @param owner The registry that issued the request.
     * @param first An iterator to the first element of the range of entities.
     * @param last An iterator past the last element of the range of entities.
     * @param args Parameters to use to initialize the objects associated with
     * the entities.
     */
    template<typename It, typename... Args>
    void insert(basic_registry<entity_type> &owner, It first, It last, Args &&... args) {
        Pool::insert(owner, first, last, std::forward<Args>(args)...);

        if(!construction.empty()) {
            for(; first != last; ++first) {
                construction.publish(owner, *first);
            }
        }
    }

    /**
     * @copybrief storage_adapter_mixin::remove
     * @param owner The registry that issued the request.
     * @param entity A valid entity identifier.
     */
    void remove(basic_registry<entity_type> &owner, const entity_type entity) {
        destruction.publish(owner, entity);
        Pool::remove(owner, entity);
    }

    /**
     * @copybrief storage_adapter_mixin::remove
     * @tparam It Type of input iterator.
     * @param owner The registry that issued the request.
     * @param first An iterator to the first element of the range of entities.
     * @param last An iterator past the last element of the range of entities.
     */
    template<typename It>
    void remove(basic_registry<entity_type> &owner, It first, It last) {
        if(!destruction.empty()) {
            for(auto it = first; it != last; ++it) {
                destruction.publish(owner, *it);
            }
        }

        Pool::remove(owner, first, last);
    }

    /**
     * @copybrief storage_adapter_mixin::patch
     * @tparam Func Types of the function objects to invoke.
     * @param owner The registry that issued the request.
     * @param entity A valid entity identifier.
     * @param func Valid function objects.
     * @return A reference to the patched instance.
     */
    template<typename... Func>
    decltype(auto) patch(basic_registry<entity_type> &owner, const entity_type entity, [[maybe_unused]] Func &&... func) {
        if constexpr(is_empty_v<value_type>) {
            update.publish(owner, entity);
        } else {
            Pool::patch(owner, entity, std::forward<Func>(func)...);
            update.publish(owner, entity);
            return this->get(entity);
        }
    }

private:
    sigh<void(basic_registry<entity_type> &, const entity_type)> construction{};
    sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{};
    sigh<void(basic_registry<entity_type> &, const entity_type)> update{};
};

/**
 * @brief Applies component-to-pool conversion and defines the resulting type as
 * the member typedef type.
 *
 * Formally:
 *
 * * If the component type is a non-const one, the member typedef type is the
 *   declared storage type.
 * * If the component type is a const one, the member typedef type is the
 *   declared storage type, except it has a const-qualifier added.
 *
 * @tparam Entity A valid entity type (see entt_traits for more details).
 * @tparam Type Type of objects assigned to the entities.
 */
template<typename Entity, typename Type, typename = void>
struct pool {
    /*! @brief Resulting type after component-to-pool conversion. */
    using type = sigh_pool_mixin<storage_adapter_mixin<basic_storage<Entity, Type>>>;
};

/*! @copydoc pool */
template<typename Entity, typename Type>
struct pool<Entity, const Type> {
    /*! @brief Resulting type after component-to-pool conversion. */
    using type = std::add_const_t<typename pool<Entity, std::remove_const_t<Type>>::type>;
};

/**
 * @brief Alias declaration to use to make component-to-pool conversions.
 * @tparam Entity A valid entity type (see entt_traits for more details).
 * @tparam Type Type of objects assigned to the entities.
 */
template<typename Entity, typename Type>
using pool_t = typename pool<Entity, Type>::type;

}

#endif
<commit_msg>pool: minor changes<commit_after>#ifndef ENTT_ENTITY_POOL_HPP
#define ENTT_ENTITY_POOL_HPP

#include <iterator>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/type_traits.hpp"
#include "../signal/sigh.hpp"
#include "fwd.hpp"
#include "storage.hpp"

namespace entt {

/**
 * @brief Mixin type to use to wrap basic storage classes.
 * @tparam Type The type of the underlying storage.
 */
template<typename Type>
struct storage_adapter_mixin: Type {
    static_assert(std::is_same_v<typename Type::value_type, std::decay_t<typename Type::value_type>>, "Invalid object type");

    /*! @brief Type of the objects associated with the entities. */
    using value_type = typename Type::value_type;
    /*! @brief Underlying entity identifier. */
    using entity_type = typename Type::entity_type;

    /**
     * @brief Assigns entities to a storage.
     * @tparam Args Types of arguments to use to construct the object.
     * @param entity A valid entity identifier.
     * @param args Parameters to use to initialize the object.
     * @return A reference to the newly created object.
     */
    template<typename... Args>
    decltype(auto) emplace(basic_registry<entity_type> &, const entity_type entity, Args &&... args) {
        return Type::emplace(entity, std::forward<Args>(args)...);
    }

    /**
     * @brief Assigns entities to a storage.
     * @tparam It Type of input iterator.
     * @tparam Args Types of arguments to use to construct the objects
     * associated with the entities.
     * @param first An iterator to the first element of the range of entities.
     * @param last An iterator past the last element of the range of entities.
     * @param args Parameters to use to initialize the objects associated with
     * the entities.
     */
    template<typename It, typename... Args>
    void insert(basic_registry<entity_type> &, It first, It last, Args &&... args) {
        Type::insert(first, last, std::forward<Args>(args)...);
    }

    /**
     * @brief Removes entities from a storage.
     * @param entity A valid entity identifier.
     */
    void remove(basic_registry<entity_type> &, const entity_type entity) {
        Type::remove(entity);
    }

    /**
     * @copybrief remove
     * @tparam It Type of input iterator.
     * @param first An iterator to the first element of the range of entities.
     * @param last An iterator past the last element of the range of entities.
     */
    template<typename It>
    void remove(basic_registry<entity_type> &, It first, It last) {
        Type::remove(first, last);
    }

    /**
     * @brief Patches the given instance for an entity.
     * @tparam Func Types of the function objects to invoke.
     * @param entity A valid entity identifier.
     * @param func Valid function objects.
     * @return A reference to the patched instance.
     */
    template<typename... Func>
    decltype(auto) patch(basic_registry<entity_type> &, const entity_type entity, [[maybe_unused]] Func &&... func) {
        auto &instance = this->get(entity);
        (std::forward<Func>(func)(instance), ...);
        return instance;
    }
};

/**
 * @brief Mixin type to use to add signal support to storage types.
 * @tparam Type The type of the underlying storage.
 */
template<typename Type>
struct sigh_storage_mixin: Type {
    /*! @brief Underlying value type. */
    using value_type = typename Type::value_type;
    /*! @brief Underlying entity identifier. */
    using entity_type = typename Type::entity_type;

    /**
     * @brief Returns a sink object.
     *
     * The sink returned by this function can be used to receive notifications
     * whenever a new instance is created and assigned to an entity.

     * The function type for a listener is equivalent to:
     *
     * @code{.cpp}
     * void(basic_registry<entity_type> &, entity_type);
     * @endcode
     *
     * Listeners are invoked **after** the object has been assigned to the
     * entity.
     *
     * @sa sink
     *
     * @return A temporary sink object.
     */
    [[nodiscard]] auto on_construct() ENTT_NOEXCEPT {
        return sink{construction};
    }

    /**
     * @brief Returns a sink object.
     *
     * The sink returned by this function can be used to receive notifications
     * whenever an instance is explicitly updated.

     * The function type for a listener is equivalent to:
     *
     * @code{.cpp}
     * void(basic_registry<entity_type> &, entity_type);
     * @endcode
     *
     * Listeners are invoked **after** the object has been updated.
     *
     * @sa sink
     *
     * @return A temporary sink object.
     */
    [[nodiscard]] auto on_update() ENTT_NOEXCEPT {
        return sink{update};
    }

    /**
     * @brief Returns a sink object.
     *
     * The sink returned by this function can be used to receive notifications
     * whenever an instance is removed from an entity and thus destroyed.

     * The function type for a listener is equivalent to:
     *
     * @code{.cpp}
     * void(basic_registry<entity_type> &, entity_type);
     * @endcode
     *
     * Listeners are invoked **before** the object has been removed from the
     * entity.
     *
     * @sa sink
     *
     * @return A temporary sink object.
     */
    [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT {
        return sink{destruction};
    }

    /**
     * @copybrief storage_adapter_mixin::emplace
     * @tparam Args Types of arguments to use to construct the object.
     * @param owner The registry that issued the request.
     * @param entity A valid entity identifier.
     * @param args Parameters to use to initialize the object.
     * @return A reference to the newly created object.
     */
    template<typename... Args>
    decltype(auto) emplace(basic_registry<entity_type> &owner, const entity_type entity, Args &&... args) {
        Type::emplace(owner, entity, std::forward<Args>(args)...);
        construction.publish(owner, entity);

        if constexpr(!is_empty_v<value_type>) {
            return this->get(entity);
        }
    }

    /**
     * @copybrief storage_adapter_mixin::insert
     * @tparam It Type of input iterator.
     * @tparam Args Types of arguments to use to construct the objects
     * associated with the entities.
     * @param owner The registry that issued the request.
     * @param first An iterator to the first element of the range of entities.
     * @param last An iterator past the last element of the range of entities.
     * @param args Parameters to use to initialize the objects associated with
     * the entities.
     */
    template<typename It, typename... Args>
    void insert(basic_registry<entity_type> &owner, It first, It last, Args &&... args) {
        Type::insert(owner, first, last, std::forward<Args>(args)...);

        if(!construction.empty()) {
            for(; first != last; ++first) {
                construction.publish(owner, *first);
            }
        }
    }

    /**
     * @copybrief storage_adapter_mixin::remove
     * @param owner The registry that issued the request.
     * @param entity A valid entity identifier.
     */
    void remove(basic_registry<entity_type> &owner, const entity_type entity) {
        destruction.publish(owner, entity);
        Type::remove(owner, entity);
    }

    /**
     * @copybrief storage_adapter_mixin::remove
     * @tparam It Type of input iterator.
     * @param owner The registry that issued the request.
     * @param first An iterator to the first element of the range of entities.
     * @param last An iterator past the last element of the range of entities.
     */
    template<typename It>
    void remove(basic_registry<entity_type> &owner, It first, It last) {
        if(!destruction.empty()) {
            for(auto it = first; it != last; ++it) {
                destruction.publish(owner, *it);
            }
        }

        Type::remove(owner, first, last);
    }

    /**
     * @copybrief storage_adapter_mixin::patch
     * @tparam Func Types of the function objects to invoke.
     * @param owner The registry that issued the request.
     * @param entity A valid entity identifier.
     * @param func Valid function objects.
     * @return A reference to the patched instance.
     */
    template<typename... Func>
    decltype(auto) patch(basic_registry<entity_type> &owner, const entity_type entity, [[maybe_unused]] Func &&... func) {
        if constexpr(is_empty_v<value_type>) {
            update.publish(owner, entity);
        } else {
            Type::patch(owner, entity, std::forward<Func>(func)...);
            update.publish(owner, entity);
            return this->get(entity);
        }
    }

private:
    sigh<void(basic_registry<entity_type> &, const entity_type)> construction{};
    sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{};
    sigh<void(basic_registry<entity_type> &, const entity_type)> update{};
};

/**
 * @brief Applies component-to-storage conversion and defines the resulting type
 * as the member typedef type.
 *
 * Formally:
 *
 * * If the component type is a non-const one, the member typedef type is the
 *   declared storage type.
 * * If the component type is a const one, the member typedef type is the
 *   declared storage type, except it has a const-qualifier added.
 *
 * @tparam Entity A valid entity type (see entt_traits for more details).
 * @tparam Type Type of objects assigned to the entities.
 */
template<typename Entity, typename Type, typename = void>
struct pool_traits {
    /*! @brief Resulting type after component-to-pool conversion. */
    using value_type = sigh_storage_mixin<storage_adapter_mixin<basic_storage<Entity, Type>>>;
};

/*! @copydoc pool_traits */
template<typename Entity, typename Type>
struct pool_traits<Entity, const Type> {
    /*! @brief Resulting type after component-to-pool conversion. */
    using value_type = std::add_const_t<typename pool_traits<Entity, std::remove_const_t<Type>>::value_type>;
};

/**
 * @brief Alias declaration to use to make component-to-pool conversions.
 * @tparam Entity A valid entity type (see entt_traits for more details).
 * @tparam Type Type of objects assigned to the entities.
 */
template<typename Entity, typename Type>
using pool_t = typename pool_traits<Entity, Type>::value_type;

}

#endif
<|endoftext|>


<commit_before>/**
 * @file eventCommunicator.cc
 * @author Michal Sitko
 * @copyright (C) 2014 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in 'LICENSE.txt'
 */

#include "events/eventCommunicator.h"
#include "communication_protocol.pb.h"

#include <boost/algorithm/string/predicate.hpp>
#include <google/protobuf/descriptor.h>

using namespace veil::client;
using namespace veil::client::events;
using namespace std;
using namespace boost;
using namespace veil::protocol::fuse_messages;
using namespace veil::protocol::communication_protocol;

EventCommunicator::EventCommunicator(shared_ptr<EventStreamCombiner> eventsStream) : m_eventsStream(eventsStream), m_writeEnabled(true)
{
	if(!eventsStream){
		m_eventsStream = shared_ptr<EventStreamCombiner>(new EventStreamCombiner());
	}
	m_messageBuilder.reset(new MessageBuilder());
}

void EventCommunicator::handlePushedConfig(const Answer &msg)
{
	EventStreamConfig eventStreamConfig;
    if(eventStreamConfig.ParseFromString(msg.worker_answer())){
        addEventSubstreamFromConfig(eventStreamConfig);
    }else{
    	LOG(WARNING) << "Cannot parse pushed message as " << eventStreamConfig.GetDescriptor()->name();
    }
}

void EventCommunicator::handlePushedAtom(const Answer &msg)
{
	Atom atom;
    if(atom.ParseFromString(msg.worker_answer())){
		if(atom.value() == "write_enabled"){
	        m_writeEnabled = true;
	        LOG(INFO) << "writeEnabled true";
	    }else if(atom.value() == "write_disabled"){
	        m_writeEnabled = false;
	        LOG(INFO) << "writeEnabled false";
	    }else if(atom.value() == "test_atom2"){
	        // just for test purposes
	        // do nothing
	    }else if(atom.value() == "test_atom2_ack" && msg.has_message_id() && msg.message_id() < -1){
	        // just for test purposes
	        PushListener::sendPushMessageAck("rule_manager", msg.message_id());
	    }
    }else{
    	LOG(WARNING) << "Cannot parse pushed message as " << atom.GetDescriptor()->name();
    }
}

bool EventCommunicator::pushMessagesHandler(const protocol::communication_protocol::Answer &msg)
{
	string messageType = msg.message_type();

	if(boost::iequals(messageType, EventStreamConfig::descriptor()->name())){
		handlePushedConfig(msg);
    }else if(boost::iequals(messageType, Atom::descriptor()->name())){
		handlePushedAtom(msg);
    }

    return true;
}

void EventCommunicator::configureByCluster()
{
    Atom atom;
    atom.set_value(EVENT_PRODUCER_CONFIG_REQUEST);

    ClusterMsg clm = m_messageBuilder->createClusterMessage(RULE_MANAGER, ATOM, COMMUNICATION_PROTOCOL, EVENT_PRODUCER_CONFIG, FUSE_MESSAGES, true, atom.SerializeAsString());

    shared_ptr<CommunicationHandler> connection = VeilFS::getConnectionPool()->selectConnection();

    Answer ans;
    if(!connection || (ans=connection->communicate(clm, 0)).answer_status() == VEIO) {
        LOG(WARNING) << "sending atom eventproducerconfigrequest failed: " << (connection ? "failed" : "not needed");
    } else {
        VeilFS::getConnectionPool()->releaseConnection(connection);
        LOG(INFO) << "atom eventproducerconfigrequest sent";
    }

    LOG(INFO) << "eventproducerconfigrequest answer_status: " << ans.answer_status();

    EventProducerConfig config;
    if(!config.ParseFromString(ans.worker_answer())){
    	LOG(WARNING) << "Cannot parse eventproducerconfigrequest answer as EventProducerConfig";
    	return;
    }

    LOG(INFO) << "Fetched EventProducerConfig contains " << config.event_streams_configs_size() << " stream configurations";

    for(int i=0; i<config.event_streams_configs_size(); ++i)
    {
        addEventSubstreamFromConfig(config.event_streams_configs(i));
    }
}

void EventCommunicator::sendEvent(shared_ptr<EventMessage> eventMessage)
{
    string encodedEventMessage = eventMessage->SerializeAsString();

    MessageBuilder messageBuilder;
    ClusterMsg clm = messageBuilder.createClusterMessage(CLUSTER_RENGINE, EVENT_MESSAGE, FUSE_MESSAGES, ATOM, COMMUNICATION_PROTOCOL, false, encodedEventMessage);

    shared_ptr<CommunicationHandler> connection = VeilFS::getConnectionPool()->selectConnection();

    Answer ans;
    if(!connection || (ans=connection->communicate(clm, 0)).answer_status() == VEIO) {
        LOG(WARNING) << "sending event message failed";
    } else {
        VeilFS::getConnectionPool()->releaseConnection(connection);
        DLOG(INFO) << "Event message sent";
    }
}

void EventCommunicator::addEventSubstream(shared_ptr<IEventStream> newStream)
{
	AutoLock lock(m_eventsStreamLock, WRITE_LOCK);
    m_eventsStream->addSubstream(newStream);
    LOG(INFO) << "New EventStream added to EventCommunicator.";
}

void EventCommunicator::addEventSubstreamFromConfig(const EventStreamConfig & eventStreamConfig)
{
	shared_ptr<IEventStream> newStream = IEventStreamFactory::fromConfig(eventStreamConfig);
    if(newStream){
    	addEventSubstream(newStream);
    }
}

void EventCommunicator::processEvent(shared_ptr<Event> event)
{
	if(event){
		m_eventsStream->pushEventToProcess(event);
		VeilFS::getScheduler()->addTask(Job(time(NULL) + 1, m_eventsStream, ISchedulable::TASK_PROCESS_EVENT));
	}
}

bool EventCommunicator::runTask(TaskID taskId, string arg0, string arg1, string arg2)
{
	switch(taskId)
	{
	case TASK_GET_EVENT_PRODUCER_CONFIG:
        configureByCluster();
        return true;

	case TASK_IS_WRITE_ENABLED:
        m_writeEnabled = isWriteEnabled();
        return true;

	default:
		return false;
	}
}

void EventCommunicator::addStatAfterWritesRule(int bytes){
    shared_ptr<IEventStream> filter(new EventFilter("type", "write_event"));
    shared_ptr<IEventStream> aggregator(new EventAggregator(filter, "filePath", bytes, "bytes"));
    shared_ptr<IEventStream> customAction(new CustomActionStream(aggregator, boost::bind(&EventCommunicator::statFromWriteEvent, this, _1)));

    addEventSubstream(customAction);
}

void EventCommunicator::setVeilFS(shared_ptr<VeilFS> veilFS){
	m_veilFS = veilFS;
}

bool EventCommunicator::isWriteEnabled()
{
	return m_writeEnabled;
}

shared_ptr<Event> EventCommunicator::statFromWriteEvent(shared_ptr<Event> event){
    string path = event->getStringProperty("filePath", "");
    if(!path.empty() && m_veilFS){
        m_veilFS->statAndUpdatetimes(path);
    }
    return event;
}<commit_msg>unneccessary events from statFromWriteEvent removed<commit_after>/**
 * @file eventCommunicator.cc
 * @author Michal Sitko
 * @copyright (C) 2014 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in 'LICENSE.txt'
 */

#include "events/eventCommunicator.h"
#include "communication_protocol.pb.h"

#include <boost/algorithm/string/predicate.hpp>
#include <google/protobuf/descriptor.h>

using namespace veil::client;
using namespace veil::client::events;
using namespace std;
using namespace boost;
using namespace veil::protocol::fuse_messages;
using namespace veil::protocol::communication_protocol;

EventCommunicator::EventCommunicator(shared_ptr<EventStreamCombiner> eventsStream) : m_eventsStream(eventsStream), m_writeEnabled(true)
{
	if(!eventsStream){
		m_eventsStream = shared_ptr<EventStreamCombiner>(new EventStreamCombiner());
	}
	m_messageBuilder.reset(new MessageBuilder());
}

void EventCommunicator::handlePushedConfig(const Answer &msg)
{
	EventStreamConfig eventStreamConfig;
    if(eventStreamConfig.ParseFromString(msg.worker_answer())){
        addEventSubstreamFromConfig(eventStreamConfig);
    }else{
    	LOG(WARNING) << "Cannot parse pushed message as " << eventStreamConfig.GetDescriptor()->name();
    }
}

void EventCommunicator::handlePushedAtom(const Answer &msg)
{
	Atom atom;
    if(atom.ParseFromString(msg.worker_answer())){
		if(atom.value() == "write_enabled"){
	        m_writeEnabled = true;
	        LOG(INFO) << "writeEnabled true";
	    }else if(atom.value() == "write_disabled"){
	        m_writeEnabled = false;
	        LOG(INFO) << "writeEnabled false";
	    }else if(atom.value() == "test_atom2"){
	        // just for test purposes
	        // do nothing
	    }else if(atom.value() == "test_atom2_ack" && msg.has_message_id() && msg.message_id() < -1){
	        // just for test purposes
	        PushListener::sendPushMessageAck("rule_manager", msg.message_id());
	    }
    }else{
    	LOG(WARNING) << "Cannot parse pushed message as " << atom.GetDescriptor()->name();
    }
}

bool EventCommunicator::pushMessagesHandler(const protocol::communication_protocol::Answer &msg)
{
	string messageType = msg.message_type();

	if(boost::iequals(messageType, EventStreamConfig::descriptor()->name())){
		handlePushedConfig(msg);
    }else if(boost::iequals(messageType, Atom::descriptor()->name())){
		handlePushedAtom(msg);
    }

    return true;
}

void EventCommunicator::configureByCluster()
{
    Atom atom;
    atom.set_value(EVENT_PRODUCER_CONFIG_REQUEST);

    ClusterMsg clm = m_messageBuilder->createClusterMessage(RULE_MANAGER, ATOM, COMMUNICATION_PROTOCOL, EVENT_PRODUCER_CONFIG, FUSE_MESSAGES, true, atom.SerializeAsString());

    shared_ptr<CommunicationHandler> connection = VeilFS::getConnectionPool()->selectConnection();

    Answer ans;
    if(!connection || (ans=connection->communicate(clm, 0)).answer_status() == VEIO) {
        LOG(WARNING) << "sending atom eventproducerconfigrequest failed: " << (connection ? "failed" : "not needed");
    } else {
        VeilFS::getConnectionPool()->releaseConnection(connection);
        LOG(INFO) << "atom eventproducerconfigrequest sent";
    }

    LOG(INFO) << "eventproducerconfigrequest answer_status: " << ans.answer_status();

    EventProducerConfig config;
    if(!config.ParseFromString(ans.worker_answer())){
    	LOG(WARNING) << "Cannot parse eventproducerconfigrequest answer as EventProducerConfig";
    	return;
    }

    LOG(INFO) << "Fetched EventProducerConfig contains " << config.event_streams_configs_size() << " stream configurations";

    for(int i=0; i<config.event_streams_configs_size(); ++i)
    {
        addEventSubstreamFromConfig(config.event_streams_configs(i));
    }
}

void EventCommunicator::sendEvent(shared_ptr<EventMessage> eventMessage)
{
    string encodedEventMessage = eventMessage->SerializeAsString();

    MessageBuilder messageBuilder;
    ClusterMsg clm = messageBuilder.createClusterMessage(CLUSTER_RENGINE, EVENT_MESSAGE, FUSE_MESSAGES, ATOM, COMMUNICATION_PROTOCOL, false, encodedEventMessage);

    shared_ptr<CommunicationHandler> connection = VeilFS::getConnectionPool()->selectConnection();

    Answer ans;
    if(!connection || (ans=connection->communicate(clm, 0)).answer_status() == VEIO) {
        LOG(WARNING) << "sending event message failed";
    } else {
        VeilFS::getConnectionPool()->releaseConnection(connection);
        DLOG(INFO) << "Event message sent";
    }
}

void EventCommunicator::addEventSubstream(shared_ptr<IEventStream> newStream)
{
	AutoLock lock(m_eventsStreamLock, WRITE_LOCK);
    m_eventsStream->addSubstream(newStream);
    LOG(INFO) << "New EventStream added to EventCommunicator.";
}

void EventCommunicator::addEventSubstreamFromConfig(const EventStreamConfig & eventStreamConfig)
{
	shared_ptr<IEventStream> newStream = IEventStreamFactory::fromConfig(eventStreamConfig);
    if(newStream){
    	addEventSubstream(newStream);
    }
}

void EventCommunicator::processEvent(shared_ptr<Event> event)
{
	if(event){
		m_eventsStream->pushEventToProcess(event);
		VeilFS::getScheduler()->addTask(Job(time(NULL) + 1, m_eventsStream, ISchedulable::TASK_PROCESS_EVENT));
	}
}

bool EventCommunicator::runTask(TaskID taskId, string arg0, string arg1, string arg2)
{
	switch(taskId)
	{
	case TASK_GET_EVENT_PRODUCER_CONFIG:
        configureByCluster();
        return true;

	case TASK_IS_WRITE_ENABLED:
        m_writeEnabled = isWriteEnabled();
        return true;

	default:
		return false;
	}
}

void EventCommunicator::addStatAfterWritesRule(int bytes){
    shared_ptr<IEventStream> filter(new EventFilter("type", "write_event"));
    shared_ptr<IEventStream> aggregator(new EventAggregator(filter, "filePath", bytes, "bytes"));
    shared_ptr<IEventStream> customAction(new CustomActionStream(aggregator, boost::bind(&EventCommunicator::statFromWriteEvent, this, _1)));

    addEventSubstream(customAction);
}

void EventCommunicator::setVeilFS(shared_ptr<VeilFS> veilFS){
	m_veilFS = veilFS;
}

bool EventCommunicator::isWriteEnabled()
{
	return m_writeEnabled;
}

shared_ptr<Event> EventCommunicator::statFromWriteEvent(shared_ptr<Event> event){
    string path = event->getStringProperty("filePath", "");
    if(!path.empty() && m_veilFS){
        m_veilFS->statAndUpdatetimes(path);
    }

    // we don't want to forward this event - it has already been handled by this function
    return shared_ptr<Event> ();
}<|endoftext|>


<commit_before>/**
 * Request.h
 *
 *      Author: Sara Vallero
 *      Author: Valentina Zaccolo
 */

#include "Configurator.h"
#include "FassLog.h"

//#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>
#include <sstream>

//#include <unistd.h>
//#include <sys/stat.h>

using namespace std;

/* -------------------------------------------------------------------------- */

bool Configurator::load_configuration(){

    /// Cannot use logsystem yet...
    cout << "Loading configuration..." << endl;
    /// Declare the supported options.
    po::options_description desc("Allowed options");

    desc.add_options()
    		("help", "produce help message")
                /// General
     		("fass.one_port", po::value<string>()->default_value("2633"), "OpenNebula listen port")
    		("fass.one_endpoint", po::value<string>()->default_value("http://localhost"), "OpenNebula listen endpoint")
    		("fass.one_secret", po::value<string>()->default_value("oneadmin:opennebula"), "OpenNebula authentication")
    		("fass.log_level", po::value<int>()->default_value(3), "log level common to all managers")
		/// RPC manager
    		("rpcm.listen_port", po::value<string>()->default_value("2634"), "listen port")
    		("rpcm.listen_address", po::value<string>()->default_value("127.0.0.1"), "listen address")
		("rpcm.timeout", po::value<int>()->default_value(15), "timeout")
		("rpcm.message_size", po::value<int>()->default_value(1073741824), "message size")
    		("rpcm.max_conn", po::value<int>()->default_value(15), "max connections")
    		("rpcm.max_conn_backlog", po::value<int>()->default_value(15), "max connections backlog")
    		("rpcm.keepalive_timeout", po::value<int>()->default_value(15), "keepalive timeout")
    		("rpcm.keepalive_max_conn", po::value<int>()->default_value(30), "keepalive max connections")
    		("rpcm.rpc_log", po::value<bool>()->default_value(true), "separate rpc log")
    		("rpcm.log_call_format", po::value<string>()->default_value("Req:%i UID:%u %m invoked %l"), "log call format")
                // Database
    		("database.type", po::value<string>()->default_value("influxdb"), "DB back-end")
    		("database.endpoint", po::value<string>()->default_value("localhost"), "DB endpoint")
    		("database.port", po::value<int>()->default_value(8086), "DB listen port")
    		("database.name", po::value<string>()->default_value("fassdb"), "DB name")
    		/// Priority manager
    		("pm.manager_timer", po::value<int>()->default_value(60), "manager period (s)")
    		("pm.max_vm", po::value<int>()->default_value(5000), "Maximum number of Virtual Machines scheduled in each scheduling action")
		;

    /// Read the configuration file
    ifstream settings_file(conf_file.c_str());

    /// Clear the map
    vm = po::variables_map();

    try{
        po::store(po::parse_config_file(settings_file , desc), vm);
    } catch(const exception& re){
        cerr << "Error: " << re.what() << endl;
        return false;
    }

    po::notify(vm);

    return true;
}

/* -------------------------------------------------------------------------- */

void Configurator::print_loaded_options(){
        ostringstream os;
        os <<"Reading configuration..." << endl;
        for (po::variables_map::iterator it = vm.begin(); it != vm.end(); it++) {

                string opt_name = it->first;
                os << "> " << opt_name;

                if (((boost::any)it->second.value()).empty()) {
                        os << "(empty)";
                }
                if (vm[opt_name].defaulted() || it->second.defaulted()) {
                        os << "(default)";
                }
                os << "=";

                allowed_types tp = get_option_type(it->second.value());
                switch (tp){
                        case is_string: os << boost::any_cast<string>(vm[opt_name].value());
                        break;
                        case is_int: os << boost::any_cast<int>(vm[opt_name].value());
                        break;
                        case is_double: os << boost::any_cast<double>(vm[opt_name].value());
                        break;
                        case is_bool: os << boost::any_cast<bool>(vm[opt_name].value());
                        break;
                        case is_unkn: os << "UnknownType";
                        break;
                }
                os << endl;

        }
        FassLog::log("CONF",Log::INFO,os);
}

/* -------------------------------------------------------------------------- */

Configurator::allowed_types Configurator::get_option_type(boost::any value){

        const type_info& type = value.type();
        if (type == typeid (string)) return is_string;
        else if (type == typeid (int)) return is_int;
        else if (type == typeid (double)) return is_double;
        else if (type == typeid (bool)) return is_bool;
        else return is_unkn;

}

/* -------------------------------------------------------------------------- */

const char * FassConfigurator::conf_name="fassd.conf";

<commit_msg>Update Configurator.cc<commit_after>/**
 * Copyright © 2017 INFN Torino - INDIGO-DataCloud
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License 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 "Configurator.h"
#include "FassLog.h"

//#include <boost/program_options.hpp>
#include <fstream>
#include <iostream>
#include <sstream>

//#include <unistd.h>
//#include <sys/stat.h>

using namespace std;

/* -------------------------------------------------------------------------- */

bool Configurator::load_configuration(){

    /// Cannot use logsystem yet...
    cout << "Loading configuration..." << endl;
    /// Declare the supported options.
    po::options_description desc("Allowed options");

    desc.add_options()
    		("help", "produce help message")
                /// General
     		("fass.one_port", po::value<string>()->default_value("2633"), "OpenNebula listen port")
    		("fass.one_endpoint", po::value<string>()->default_value("http://localhost"), "OpenNebula listen endpoint")
    		("fass.one_secret", po::value<string>()->default_value("oneadmin:opennebula"), "OpenNebula authentication")
    		("fass.log_level", po::value<int>()->default_value(3), "log level common to all managers")
		/// RPC manager
    		("rpcm.listen_port", po::value<string>()->default_value("2634"), "listen port")
    		("rpcm.listen_address", po::value<string>()->default_value("127.0.0.1"), "listen address")
		("rpcm.timeout", po::value<int>()->default_value(15), "timeout")
		("rpcm.message_size", po::value<int>()->default_value(1073741824), "message size")
    		("rpcm.max_conn", po::value<int>()->default_value(15), "max connections")
    		("rpcm.max_conn_backlog", po::value<int>()->default_value(15), "max connections backlog")
    		("rpcm.keepalive_timeout", po::value<int>()->default_value(15), "keepalive timeout")
    		("rpcm.keepalive_max_conn", po::value<int>()->default_value(30), "keepalive max connections")
    		("rpcm.rpc_log", po::value<bool>()->default_value(true), "separate rpc log")
    		("rpcm.log_call_format", po::value<string>()->default_value("Req:%i UID:%u %m invoked %l"), "log call format")
                // Database
    		("database.type", po::value<string>()->default_value("influxdb"), "DB back-end")
    		("database.endpoint", po::value<string>()->default_value("localhost"), "DB endpoint")
    		("database.port", po::value<int>()->default_value(8086), "DB listen port")
    		("database.name", po::value<string>()->default_value("fassdb"), "DB name")
    		/// Priority manager
    		("pm.manager_timer", po::value<int>()->default_value(60), "manager period (s)")
    		("pm.max_vm", po::value<int>()->default_value(5000), "Maximum number of Virtual Machines scheduled in each scheduling action")
		;

    /// Read the configuration file
    ifstream settings_file(conf_file.c_str());

    /// Clear the map
    vm = po::variables_map();

    try{
        po::store(po::parse_config_file(settings_file , desc), vm);
    } catch(const exception& re){
        cerr << "Error: " << re.what() << endl;
        return false;
    }

    po::notify(vm);

    return true;
}

/* -------------------------------------------------------------------------- */

void Configurator::print_loaded_options(){
        ostringstream os;
        os <<"Reading configuration..." << endl;
        for (po::variables_map::iterator it = vm.begin(); it != vm.end(); it++) {

                string opt_name = it->first;
                os << "> " << opt_name;

                if (((boost::any)it->second.value()).empty()) {
                        os << "(empty)";
                }
                if (vm[opt_name].defaulted() || it->second.defaulted()) {
                        os << "(default)";
                }
                os << "=";

                allowed_types tp = get_option_type(it->second.value());
                switch (tp){
                        case is_string: os << boost::any_cast<string>(vm[opt_name].value());
                        break;
                        case is_int: os << boost::any_cast<int>(vm[opt_name].value());
                        break;
                        case is_double: os << boost::any_cast<double>(vm[opt_name].value());
                        break;
                        case is_bool: os << boost::any_cast<bool>(vm[opt_name].value());
                        break;
                        case is_unkn: os << "UnknownType";
                        break;
                }
                os << endl;

        }
        FassLog::log("CONF",Log::INFO,os);
}

/* -------------------------------------------------------------------------- */

Configurator::allowed_types Configurator::get_option_type(boost::any value){

        const type_info& type = value.type();
        if (type == typeid (string)) return is_string;
        else if (type == typeid (int)) return is_int;
        else if (type == typeid (double)) return is_double;
        else if (type == typeid (bool)) return is_bool;
        else return is_unkn;

}

/* -------------------------------------------------------------------------- */

const char * FassConfigurator::conf_name="fassd.conf";

<|endoftext|>


<commit_before>#if defined(__MINGW32__) && !defined(__clang__)
//mingw-gcc's std::randome_device is broken.
//gccをwindowsで使うならstd::random_deviceを使ってはいけない - Qiita
//http://qiita.com/nanashi/items/f94b78398a6c79d939e1
#	define _CRT_RAND_S
#	include <stdlib.h> //rand_s
#	include <thread>
#endif //defined(__MINGW32__) && !defined(__clang__)
#if defined(_WIN32) || defined(_WIN64)
#	define MY_ARC_FOR_WINDWOS 1
#	if !defined(CINTERFACE) && defined(__c2__) &&  __clang_major__ == 3 && __clang_minor__ == 8
//To avoid compile error
//C:\Program Files (x86)\Windows Kits\8.1\Include\um\combaseapi.h(229,21): error : unknown type name 'IUnknown'
//          static_cast<IUnknown*>(*pp);    // make sure everyone derives from IUnknown
#		define CINTERFACE
#	endif
#	include "no_min_max.h"
#	include <Windows.h>
#	include <tchar.h>
#elif defined(__linux__)
#	define MY_ARC_FOR_LINUX 1
#	include <sys/types.h>
#	include <unistd.h>
#	include <fstream>
unsigned int get_randome_from_dev_random() {
	std::ifstream file("/dev/random", std::ios::binary);
	if (file.is_open())
	{
		char buf[sizeof(unsigned int)];
		file.read(buf, sizeof(unsigned int));
		file.close();
		return *reinterpret_cast<int*>(buf);
	}
	return 0;
}
#endif
#include "random.hpp"
#include <memory>
#include <algorithm>//std::generate
#include <ctime>//clock(), time()
#include <functional>//std::ref in gcc
#include <chrono>
#include <climits>
#if !defined(__c2__) || (defined(__clang__) && (__clang_major__ >= 4 ) || __clang_major__ == 3 && __clang_minor__ >= 9)//古いClang with Microsoft CodeGenはasmに対応していない
#	ifndef __INTEL_COMPILER
#		include <immintrin.h>
#		if defined(_WIN32) || defined(_WIN64)
#		include <intrin.h>
#		else
#			include <x86intrin.h>
#		endif //defined(_WIN32) || defined(_WIN64)
#		ifdef __GNUC__
#			include <cpuid.h>
#		endif //__GNUC__
#	endif //__INTEL_COMPILER
#	include <cstring>
using std::uint32_t;
namespace intrin {
	struct regs_t { uint32_t EAX, EBX, ECX, EDX; };
	union register_str_cvt {
		uint32_t u32[3];
		char str[12];
	};
	regs_t get_cpuid(unsigned int level) {
		regs_t re = {};
		static_assert(sizeof(re) == (sizeof(uint32_t) * 4), "illegal size of struct regs_t ");
#	if defined(__INTEL_COMPILER) || defined(_MSC_VER) && !defined(__clang__)
		__cpuid(reinterpret_cast<int*>(&re), static_cast<int>(level));
#	elif defined(__GNUC__)
		__get_cpuid(level, &re.EAX, &re.EBX, &re.ECX, &re.EDX);
#	endif
		return re;
	}
	bool check_vender(const char* s) {
		const auto id = get_cpuid(0);
		register_str_cvt vender = { { id.EBX, id.EDX, id.ECX } };
		return (0 == std::memcmp(vender.str, s, sizeof(vender.str)));
	}
	bool IsIntelCPU() {
		static const auto is_intel_cpu = check_vender("GenuineIntel");//実行中にCPUは変わらないとする
		return is_intel_cpu;
	}
	bool IsAMDCPU() {
		static const auto is_amd_cpu = check_vender("AuthenticAMD");
		return is_amd_cpu;
	}
	bool IsRDRANDsupport() {
		static constexpr uint32_t RDRAND_MASK = 1U << 30U;
		if (!IsIntelCPU() && !IsAMDCPU()) return false;
		const auto reg = get_cpuid(1);//If RDRAND is supported, the bit 30 of the ECX register is set after calling CPUID standard function 01H.
		return (RDRAND_MASK == (reg.ECX & RDRAND_MASK));
	}
	bool IsRDSEEDsupport() {
		static constexpr uint32_t RDSEED_MASK = 1U << 18U;
		if (!IsIntelCPU()) return false;
		const auto reg = get_cpuid(7);//If RDSEED is supported, the bit 18 of the EBX register is set after calling CPUID standard function 07H.
		return (RDSEED_MASK == (reg.EBX & RDSEED_MASK));
	}
}
#else
#	define MY_NO_ASM
#endif//!defined(_MSC_VER) || !defined(__clang__)
namespace detail {
	template<typename Pointer>
	struct vector_push_back_helper {
		Pointer value;
	};
	template<typename value_type, typename T, bool T_is_larger>
	struct vector_push_back_operator_impl {
		void operator()(std::vector<value_type>& v, vector_push_back_helper<T> info) {
			if (info.value) v.push_back((value_type)(info.value));
		}
	};
	template<typename value_type>
	struct vector_push_back_operator_impl<value_type, value_type, false> {
		void operator()(std::vector<value_type>& v, vector_push_back_helper<value_type> info) {
			if (info.value) v.push_back(info.value);
		}
	};
	template<typename value_type, typename T>
	struct vector_push_back_operator_impl<value_type, T, true> {
		void operator()(std::vector<value_type>& v, vector_push_back_helper<T> info) {
			static constexpr size_t size_time = sizeof(T) / sizeof(value_type);
			static constexpr size_t rshft_num = sizeof(value_type) * CHAR_BIT;
			for (size_t i = 0; i < size_time; ++i) {
				const auto tmp = static_cast<value_type>((std::uintmax_t)(info.value) >> (rshft_num * i));
				if (tmp) v.push_back(tmp);
			}
		}
	};
	template<typename value_type, typename T, std::enable_if_t<std::is_pointer<T>::value || std::is_arithmetic<T>::value, std::nullptr_t> = nullptr>
	void operator| (std::vector<value_type>& v, vector_push_back_helper<T> info) {
		vector_push_back_operator_impl<value_type, T, (sizeof(value_type) < sizeof(T))> ()(v, info);
	}
}
template<typename T>
detail::vector_push_back_helper<T> push_back(T pointer) { return{ pointer }; }
using seed_v_t = std::vector<unsigned int>;
static seed_v_t create_seed_v() {
	const auto begin_time = std::chrono::high_resolution_clock::now();
#if defined(__c2__) && __clang_minor__ < 9
	constexpr std::size_t randome_device_generate_num = 12;//Clnag with Microsoft CodeGen does not support RDRND/RDSEED so that use std::random_device agressively.
#else
	static constexpr std::size_t randome_device_generate_num = 9;
#endif
	seed_v_t sed_v(randome_device_generate_num);// 初期化用ベクター
#ifndef _CRT_RAND_S
	std::random_device rnd;// ランダムデバイス
	std::generate(sed_v.begin(), sed_v.end(), std::ref(rnd));// ベクタの初期化
#else //_CRT_RAND_S
	std::generate(sed_v.begin(), sed_v.end(), []() {
		unsigned int re;
		if (rand_s(&re)) {
			std::this_thread::sleep_for(std::chrono::milliseconds(300));
			if (auto err = rand_s(&re)) throw std::runtime_error("rand_s failed. error code:" + std::to_string(err));
		}
		return re;
	});// ベクタの初期化
#endif //_CRT_RAND_S
#ifndef MY_NO_ASM
#	ifndef NO_RDRAND
	if (intrin::IsRDRANDsupport()) {//RDRAND命令の結果もベクターに追加
		for (unsigned int i = 0; i < 4; i++) {
			unsigned int rdrand_value = 0;
#		if defined(_MSC_VER) || defined(__INTEL_COMPILER)
			_rdrand32_step(&rdrand_value);
#		else//defined(_MSC_VER) || defined(__INTEL_COMPILER)
			__builtin_ia32_rdrand32_step(&rdrand_value);
#		endif//defined(_MSC_VER) || defined(__INTEL_COMPILER)
			if (0 != rdrand_value) {
				sed_v.push_back((rdrand_value < std::numeric_limits<decltype(rdrand_value)>::max() - i) ? rdrand_value + i : rdrand_value);
			}
		}
	}
#	endif
#	ifndef NO_RDSEED
	if (intrin::IsRDSEEDsupport()) {
		for (unsigned int i = 0; i < 5; i++) {
			unsigned int rdseed_value = 0;
#		if defined(_MSC_VER) || defined(__INTEL_COMPILER)
			_rdseed32_step(&rdseed_value);
#		else//defined(_MSC_VER) || defined(__INTEL_COMPILER)
			__builtin_ia32_rdseed_si_step(&rdseed_value);
#		endif//defined(_MSC_VER) || defined(__INTEL_COMPILER)
			if (0 != rdseed_value) {
				sed_v.push_back((rdseed_value < std::numeric_limits<decltype(rdseed_value)>::max() - i) ? rdseed_value + i : rdseed_value);
			}
		}
	}
#	endif
#endif//!defined(MY_NO_ASM)
#ifdef MY_ARC_FOR_WINDWOS
	POINT point;
	GetCursorPos(&point);
	sed_v | push_back(point.x);
	sed_v | push_back(point.y);
	sed_v | push_back(GetCurrentProcessId());
#endif //MY_ARC_FOR_WINDWOS
#ifdef MY_ARC_FOR_LINUX
	sed_v | push_back(get_randome_from_dev_random());
	sed_v | push_back(getppid());
	sed_v | push_back(get_randome_from_dev_random());
#endif
	sed_v | push_back(clock());//clock関数の結果もベクターに追加
	sed_v | push_back(time(nullptr));//time関数の結果もベクターに追加
									 //ヒープ領域のアドレスもベクターに追加
	auto heap = std::make_unique<char>();
	sed_v | push_back(heap.get());
	sed_v | push_back(&heap);
	sed_v | push_back(time);
	sed_v | push_back(create_engine);
	const auto end_time = std::chrono::high_resolution_clock::now();
	sed_v | push_back((end_time - begin_time).count());
	return sed_v;
}
std::mt19937 create_engine() {
	auto sed_v = create_seed_v();
	std::seed_seq seq(sed_v.begin(), sed_v.end());
	return std::mt19937(seq);
}
<commit_msg>fix: mingw gcc's random_device was fixed<commit_after>#if defined(__MINGW32__) && defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <2))
//mingw-gcc's std::randome_device was broken.
//https://cpprefjp.github.io/reference/random/random_device.html
#	define _CRT_RAND_S
#	include <stdlib.h> //rand_s
#	include <thread>
#endif //defined(__MINGW32__) && defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 9 || (__GNUC__ == 9 && __GNUC_MINOR__ <2))
struct IUnknown;
#if defined(_WIN32) || defined(_WIN64)
#	define MY_ARC_FOR_WINDWOS 1
#	if !defined(CINTERFACE) && defined(__c2__) &&  __clang_major__ == 3 && __clang_minor__ == 8
//To avoid compile error
//C:\Program Files (x86)\Windows Kits\8.1\Include\um\combaseapi.h(229,21): error : unknown type name 'IUnknown'
//          static_cast<IUnknown*>(*pp);    // make sure everyone derives from IUnknown
#		define CINTERFACE
#	endif
#	include "no_min_max.h"
#	include <Windows.h>
#	include <tchar.h>
#elif defined(__linux__)
#	define MY_ARC_FOR_LINUX 1
#	include <sys/types.h>
#	include <unistd.h>
#	include <fstream>
unsigned int get_randome_from_dev_random() {
	std::ifstream file("/dev/random", std::ios::binary);
	if (file.is_open())
	{
		char buf[sizeof(unsigned int)];
		file.read(buf, sizeof(unsigned int));
		file.close();
		return *reinterpret_cast<int*>(buf);
	}
	return 0;
}
#endif
#include "random.hpp"
#include <memory>
#include <algorithm>//std::generate
#include <ctime>//clock(), time()
#include <functional>//std::ref in gcc
#include <chrono>
#include <climits>
#if !defined(__c2__) || (defined(__clang__) && (__clang_major__ >= 4 ) || __clang_major__ == 3 && __clang_minor__ >= 9)//古いClang with Microsoft CodeGenはasmに対応していない
#	ifndef __INTEL_COMPILER
#		include <immintrin.h>
#		if defined(_WIN32) || defined(_WIN64)
#		include <intrin.h>
#		else
#			include <x86intrin.h>
#		endif //defined(_WIN32) || defined(_WIN64)
#		ifdef __GNUC__
#			include <cpuid.h>
#		endif //__GNUC__
#	endif //__INTEL_COMPILER
#	include <cstring>
using std::uint32_t;
namespace intrin {
	struct regs_t { uint32_t EAX, EBX, ECX, EDX; };
	union register_str_cvt {
		uint32_t u32[3];
		char str[12];
	};
	regs_t get_cpuid(unsigned int level) {
		regs_t re = {};
		static_assert(sizeof(re) == (sizeof(uint32_t) * 4), "illegal size of struct regs_t ");
#	if defined(__INTEL_COMPILER) || defined(_MSC_VER) && !defined(__clang__)
		__cpuid(reinterpret_cast<int*>(&re), static_cast<int>(level));
#	elif defined(__GNUC__)
		__get_cpuid(level, &re.EAX, &re.EBX, &re.ECX, &re.EDX);
#	endif
		return re;
	}
	bool check_vender(const char* s) {
		const auto id = get_cpuid(0);
		register_str_cvt vender = { { id.EBX, id.EDX, id.ECX } };
		return (0 == std::memcmp(vender.str, s, sizeof(vender.str)));
	}
	bool IsIntelCPU() {
		static const auto is_intel_cpu = check_vender("GenuineIntel");//実行中にCPUは変わらないとする
		return is_intel_cpu;
	}
	bool IsAMDCPU() {
		static const auto is_amd_cpu = check_vender("AuthenticAMD");
		return is_amd_cpu;
	}
	bool IsRDRANDsupport() {
		static constexpr uint32_t RDRAND_MASK = 1U << 30U;
		if (!IsIntelCPU() && !IsAMDCPU()) return false;
		const auto reg = get_cpuid(1);//If RDRAND is supported, the bit 30 of the ECX register is set after calling CPUID standard function 01H.
		return (RDRAND_MASK == (reg.ECX & RDRAND_MASK));
	}
	bool IsRDSEEDsupport() {
		static constexpr uint32_t RDSEED_MASK = 1U << 18U;
		if (!IsIntelCPU()) return false;
		const auto reg = get_cpuid(7);//If RDSEED is supported, the bit 18 of the EBX register is set after calling CPUID standard function 07H.
		return (RDSEED_MASK == (reg.EBX & RDSEED_MASK));
	}
}
#else
#	define MY_NO_ASM
#endif//!defined(_MSC_VER) || !defined(__clang__)
namespace detail {
	template<typename Pointer>
	struct vector_push_back_helper {
		Pointer value;
	};
	template<typename value_type, typename T, bool T_is_larger>
	struct vector_push_back_operator_impl {
		void operator()(std::vector<value_type>& v, vector_push_back_helper<T> info) {
			if (info.value) v.push_back((value_type)(info.value));
		}
	};
	template<typename value_type>
	struct vector_push_back_operator_impl<value_type, value_type, false> {
		void operator()(std::vector<value_type>& v, vector_push_back_helper<value_type> info) {
			if (info.value) v.push_back(info.value);
		}
	};
	template<typename value_type, typename T>
	struct vector_push_back_operator_impl<value_type, T, true> {
		void operator()(std::vector<value_type>& v, vector_push_back_helper<T> info) {
			static constexpr size_t size_time = sizeof(T) / sizeof(value_type);
			static constexpr size_t rshft_num = sizeof(value_type) * CHAR_BIT;
			for (size_t i = 0; i < size_time; ++i) {
				const auto tmp = static_cast<value_type>((std::uintmax_t)(info.value) >> (rshft_num * i));
				if (tmp) v.push_back(tmp);
			}
		}
	};
	template<typename value_type, typename T, std::enable_if_t<std::is_pointer<T>::value || std::is_arithmetic<T>::value, std::nullptr_t> = nullptr>
	void operator| (std::vector<value_type>& v, vector_push_back_helper<T> info) {
		vector_push_back_operator_impl<value_type, T, (sizeof(value_type) < sizeof(T))> ()(v, info);
	}
}
template<typename T>
detail::vector_push_back_helper<T> push_back(T pointer) { return{ pointer }; }
using seed_v_t = std::vector<unsigned int>;
static seed_v_t create_seed_v() {
	const auto begin_time = std::chrono::high_resolution_clock::now();
#if defined(__c2__) && __clang_minor__ < 9
	constexpr std::size_t randome_device_generate_num = 12;//Clnag with Microsoft CodeGen does not support RDRND/RDSEED so that use std::random_device agressively.
#else
	static constexpr std::size_t randome_device_generate_num = 9;
#endif
	seed_v_t sed_v(randome_device_generate_num);// 初期化用ベクター
#ifndef _CRT_RAND_S
	std::random_device rnd;// ランダムデバイス
	std::generate(sed_v.begin(), sed_v.end(), std::ref(rnd));// ベクタの初期化
#else //_CRT_RAND_S
	std::generate(sed_v.begin(), sed_v.end(), []() {
		unsigned int re;
		if (rand_s(&re)) {
			std::this_thread::sleep_for(std::chrono::milliseconds(300));
			if (auto err = rand_s(&re)) throw std::runtime_error("rand_s failed. error code:" + std::to_string(err));
		}
		return re;
	});// ベクタの初期化
#endif //_CRT_RAND_S
#ifndef MY_NO_ASM
#	ifndef NO_RDRAND
	if (intrin::IsRDRANDsupport()) {//RDRAND命令の結果もベクターに追加
		for (unsigned int i = 0; i < 4; i++) {
			unsigned int rdrand_value = 0;
#		if defined(_MSC_VER) || defined(__INTEL_COMPILER)
			_rdrand32_step(&rdrand_value);
#		else//defined(_MSC_VER) || defined(__INTEL_COMPILER)
			__builtin_ia32_rdrand32_step(&rdrand_value);
#		endif//defined(_MSC_VER) || defined(__INTEL_COMPILER)
			if (0 != rdrand_value) {
				sed_v.push_back((rdrand_value < std::numeric_limits<decltype(rdrand_value)>::max() - i) ? rdrand_value + i : rdrand_value);
			}
		}
	}
#	endif
#	ifndef NO_RDSEED
	if (intrin::IsRDSEEDsupport()) {
		for (unsigned int i = 0; i < 5; i++) {
			unsigned int rdseed_value = 0;
#		if defined(_MSC_VER) || defined(__INTEL_COMPILER)
			_rdseed32_step(&rdseed_value);
#		else//defined(_MSC_VER) || defined(__INTEL_COMPILER)
			__builtin_ia32_rdseed_si_step(&rdseed_value);
#		endif//defined(_MSC_VER) || defined(__INTEL_COMPILER)
			if (0 != rdseed_value) {
				sed_v.push_back((rdseed_value < std::numeric_limits<decltype(rdseed_value)>::max() - i) ? rdseed_value + i : rdseed_value);
			}
		}
	}
#	endif
#endif//!defined(MY_NO_ASM)
#ifdef MY_ARC_FOR_WINDWOS
	POINT point;
	GetCursorPos(&point);
	sed_v | push_back(point.x);
	sed_v | push_back(point.y);
	sed_v | push_back(GetCurrentProcessId());
#endif //MY_ARC_FOR_WINDWOS
#ifdef MY_ARC_FOR_LINUX
	sed_v | push_back(get_randome_from_dev_random());
	sed_v | push_back(getppid());
	sed_v | push_back(get_randome_from_dev_random());
#endif
	sed_v | push_back(clock());//clock関数の結果もベクターに追加
	sed_v | push_back(time(nullptr));//time関数の結果もベクターに追加
									 //ヒープ領域のアドレスもベクターに追加
	auto heap = std::make_unique<char>();
	sed_v | push_back(heap.get());
	sed_v | push_back(&heap);
	sed_v | push_back(time);
	sed_v | push_back(create_engine);
	const auto end_time = std::chrono::high_resolution_clock::now();
	sed_v | push_back((end_time - begin_time).count());
	return sed_v;
}
std::mt19937 create_engine() {
	auto sed_v = create_seed_v();
	std::seed_seq seq(sed_v.begin(), sed_v.end());
	return std::mt19937(seq);
}
<|endoftext|>