<commit_before>//============================================================================
// include/vsmc/core/state_tuple.hpp
//----------------------------------------------------------------------------
//
//                         vSMC: Scalable Monte Carlo
//
// This file is distribured under the 2-clauses BSD License.
// See LICENSE for details.
//============================================================================

#ifndef VSMC_CORE_STATE_TUPLE_HPP
#define VSMC_CORE_STATE_TUPLE_HPP

#include <vsmc/core/single_particle.hpp>
#include <tuple>
#include <vector>

#define VSMC_RUNTIME_ASSERT_CORE_STATE_TUPLE_COPY_SIZE_MISMATCH \
    VSMC_RUNTIME_ASSERT((N == static_cast<size_type>(this->size())),         \
            ("**StateTuple::copy** SIZE MISMATCH"))

namespace vsmc {

/// \brief Base type of StateTuple
/// \ingroup Core
template <MatrixOrder Order, typename T, typename... Types>
class StateTupleBase
{
    public :

    typedef std::size_t size_type;
    typedef std::tuple<T, Types...> state_tuple_type;
    typedef std::tuple<T *, Types *...> state_tuple_ptr_type;
    typedef std::tuple<const T *, const Types *...> state_tuple_cptr_type;

    template <std::size_t Pos> struct state_type
    {typedef typename std::tuple_element<Pos, state_tuple_type>::type type;};

    struct state_pack_type
    {
        state_pack_type () {}

        state_pack_type (const state_pack_type &other) : data_(other.data_) {}

        state_pack_type &operator= (const state_pack_type &other)
        {data_ = other.data_; return *this;}

#if VSMC_HAS_CXX11_RVALUE_REFERENCES
        state_pack_type (state_pack_type &&other) VSMC_NOEXCEPT :
            data_(cxx11::move(other.data_)) {}

        state_pack_type &operator= (state_pack_type &&other) VSMC_NOEXCEPT
        {data_ = cxx11::move(other.data_); return *this;}
#endif

        state_pack_type (const state_tuple_type &data) : data_(data) {}

        operator state_tuple_type () {return data_;}

        template <std::size_t Pos>
        typename state_type<Pos>::type &get ()
        {return std::get<Pos>(data_);}

        template <std::size_t Pos>
        const typename state_type<Pos>::type &get () const
        {return std::get<Pos>(data_);}

        template <typename Archive>
        void serialize (Archive &ar, const unsigned)
        {serialize(ar, Position<0>());}

        template <typename Archive>
        void serialize (Archive &ar, const unsigned) const
        {serialize(ar, Position<0>());}

        private :

        state_tuple_type data_;
        static VSMC_CONSTEXPR const std::size_t dim_ = sizeof...(Types) + 1;

        template <typename Archive, std::size_t Pos>
        void serialize (Archive &ar, Position<Pos>)
        {
            ar & std::get<Pos>(data_);
            serialize(ar, Position<Pos + 1>());
        }

        template <typename Archive>
        void serialize (Archive &, Position<dim_>) {}

        template <typename Archive, std::size_t Pos>
        void serialize (Archive &ar, Position<Pos>) const
        {
            ar & std::get<Pos>(data_);
            serialize(ar, Position<Pos + 1>());
        }

        template <typename Archive>
        void serialize (Archive &, Position<dim_>) const {}
    }; // struct state_pack_type

    template <typename S>
    struct single_particle_type : public SingleParticleBase<S>
    {
        single_particle_type (typename Particle<S>::size_type id,
                Particle<S> *particle_ptr) :
            SingleParticleBase<S>(id, particle_ptr) {}

        static VSMC_CONSTEXPR std::size_t dim () {return S::dim();}

        template <std::size_t Pos>
        typename state_type<Pos>::type &state (Position<Pos>) const
        {
            return this->mutable_particle_ptr()->value().
                state(this->id(), Position<Pos>());
        }

        template <std::size_t Pos>
        typename state_type<Pos>::type &state () const
        {return this->state(Position<Pos>());}
    }; // struct single_particle_type

    template <typename S>
    struct const_single_particle_type : public ConstSingleParticleBase<S>
    {
        const_single_particle_type (typename Particle<S>::size_type id,
                const Particle<S> *particle_ptr) :
            ConstSingleParticleBase<S>(id, particle_ptr) {}

        static VSMC_CONSTEXPR std::size_t dim () {return S::dim();}

        template <std::size_t Pos>
        const typename state_type<Pos>::type &state (Position<Pos>) const
        {
            return this->particle_ptr()->value().
                state(this->id(), Position<Pos>());
        }

        template <std::size_t Pos>
        const typename state_type<Pos>::type &state () const
        {return this->state(Position<Pos>());}
    }; // struct const_single_particle_type

    size_type size () const {return size_;}

    static VSMC_CONSTEXPR std::size_t dim () {return dim_;}

    state_pack_type state_pack (size_type id) const
    {
        state_pack_type pack;
        pack_particle(id, pack, Position<0>());

        return pack;
    }

    void state_unpack (size_type id, const state_pack_type &pack)
    {unpack_particle(id, pack, Position<0>());}

    template <typename IntType>
    void copy (size_type N, const IntType *copy_from)
    {
        VSMC_RUNTIME_ASSERT_CORE_STATE_TUPLE_COPY_SIZE_MISMATCH;

        for (size_type to = 0; to != N; ++to)
            copy_particle(copy_from[to], to);
    }

    template <std::size_t Pos, typename OutputIter>
    OutputIter read_state (Position<Pos>, OutputIter first) const
    {
        const StateTuple<Order, T, Types...> *sptr =
            static_cast<const StateTuple<Order, T, Types...> *>(this);
        for (size_type i = 0; i != size_; ++i, ++first)
                *first = sptr->state(i, Position<Pos>());

        return first;
    }

    template <std::size_t Pos, typename OutputIter>
    OutputIter read_state (OutputIter first) const
    {return read_state(Position<Pos>(), first);}

    template <typename CharT, typename Traits>
    std::basic_ostream<CharT, Traits> &print (
            std::basic_ostream<CharT, Traits> &os, std::size_t iter = 0,
            char sepchar = ' ', char eolchar = '\n') const
    {
        for (size_type i = 0; i != size_; ++i) {
            os << iter << sepchar;
            print_particle(os, i, sepchar, eolchar, Position<0>());
        }

        return os;
    }

    protected :

    explicit StateTupleBase (size_type N) : size_(N) {}

    void copy_particle (size_type from, size_type to)
    {
        if (from != to)
            copy_particle(from, to, Position<0>());
    }

    private :

    size_type size_;
    static VSMC_CONSTEXPR const std::size_t dim_ = sizeof...(Types) + 1;

    template <std::size_t Pos>
    void pack_particle (size_type id, state_pack_type &pack,
            Position<Pos>) const
    {
        const StateTuple<Order, T, Types...> *sptr =
            static_cast<const StateTuple<Order, T, Types...> *>(this);
        pack.template get<Pos>() = sptr->state(id, Position<Pos>());
        pack_particle(id, pack, Position<Pos + 1>());
    }

    void pack_particle (size_type, state_pack_type &,
            Position<dim_>) const {}

    template <std::size_t Pos>
    void unpack_particle (size_type id, const state_pack_type &pack,
            Position<Pos>)
    {
        StateTuple<Order, T, Types...> *sptr =
            static_cast<StateTuple<Order, T, Types...> *>(this);
        sptr->state(id, Position<Pos>()) = pack.template get<Pos>();
        unpack_particle(id, pack, Position<Pos + 1>());
    }

    void unpack_particle (size_type, const state_pack_type &,
            Position<dim_>) {}

    template <std::size_t Pos>
    void copy_particle (size_type from, size_type to, Position<Pos>)
    {
        StateTuple<Order, T, Types...> *sptr =
            static_cast<StateTuple<Order, T, Types...> *>(this);
        sptr->state(to, Position<Pos>()) = sptr->state(from, Position<Pos>());
        copy_particle(from, to, Position<Pos + 1>());
    }

    void copy_particle (size_type, size_type, Position<dim_>) {}

    template <std::size_t Pos, typename CharT, typename Traits>
    void print_particle (std::basic_ostream<CharT, Traits> &os, size_type id,
            char sepchar, char eolchar, Position<Pos>) const
    {
        const StateTuple<Order, T, Types...> *sptr =
            static_cast<const StateTuple<Order, T, Types...> *>(this);
        os << sptr->state(id, Position<Pos>()) << sepchar;
        print_particle(os, id, sepchar, eolchar, Position<Pos + 1>());
    }

    template <typename CharT, typename Traits>
    void print_particle (std::basic_ostream<CharT, Traits> &os, size_type id,
            char, char eolchar, Position<dim_ - 1>) const
    {
        const StateTuple<Order, T, Types...> *sptr =
            static_cast<const StateTuple<Order, T, Types...> *>(this);
        os << sptr->state(id, Position<dim_ - 1>()) << eolchar;
    }
}; // class StateTupleBase

/// \brief Particle::value_type subtype
/// \ingroup Core
template <typename T, typename... Types>
class StateTuple<RowMajor, T, Types...> :
    public StateTupleBase<RowMajor, T, Types...>
{
    public :

    typedef StateTupleBase<RowMajor, T, Types...>
        state_tuple_base_type;
    typedef typename state_tuple_base_type::size_type size_type;

    explicit StateTuple (size_type N) : state_tuple_base_type(N), state_(N) {}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id, Position<Pos>)
    {return std::get<Pos>(state_[id]);}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id, Position<Pos>) const
    {return std::get<Pos>(state_[id]);}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id)
    {return state(id, Position<Pos>());}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id) const
    {return state(id, Position<Pos>());}

    typename state_tuple_base_type::state_tuple_type *data ()
    {return &state_[0];}

    const typename state_tuple_base_type::state_tuple_type *data () const
    {return &state_[0];}

    private :

    static VSMC_CONSTEXPR const std::size_t dim_ = sizeof...(Types) + 1;
    std::vector<std::tuple<T, Types...> > state_;
}; // StateTuple

/// \brief Particle::value_type subtype
/// \ingroup Core
template <typename T, typename... Types>
class StateTuple<ColMajor, T, Types...> :
    public StateTupleBase<ColMajor, T, Types...>
{
    public :

    typedef StateTupleBase<ColMajor, T, Types...>
        state_tuple_base_type;
    typedef typename state_tuple_base_type::size_type size_type;

    explicit StateTuple (size_type N) : state_tuple_base_type(N)
    {init_state(N, Position<0>());}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id, Position<Pos>)
    {return std::get<Pos>(state_)[id];}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id, Position<Pos>) const
    {return std::get<Pos>(state_)[id];}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id)
    {return state(id, Position<Pos>());}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id) const
    {return state(id, Position<Pos>());}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    *data (Position<Pos>) {return &std::get<Pos>(state_)[0];}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    *data (Position<Pos>) const {return &std::get<Pos>(state_)[0];}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    *data () {return &std::get<Pos>(state_)[0];}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    *data () const {return &std::get<Pos>(state_)[0];}

    typename state_tuple_base_type::state_tuple_cptr_type data () const
    {
        typename state_tuple_base_type::state_tuple_cptr_type dptr;
        insert_data(dptr, Position<0>());

        return dptr;
    }

    private :

    static VSMC_CONSTEXPR const std::size_t dim_ = sizeof...(Types) + 1;
    std::tuple<std::vector<T>, std::vector<Types>...> state_;

    template <std::size_t Pos>
    void init_state (size_type N, Position<Pos>)
    {
        std::get<Pos>(state_).resize(N);
        init_state(N, Position<Pos + 1>());
    }

    void init_state (size_type N, Position<sizeof...(Types)>)
    {std::get<sizeof...(Types)>(state_).resize(N);}

    template <std::size_t Pos>
    void insert_data (
            typename state_tuple_base_type::state_tuple_cptr_type &dptr,
            Position<Pos>) const
    {
        std::get<Pos>(dptr) = data<Pos>();
        insert_data(dptr, Position<Pos + 1>());
    }

    void insert_data (
            typename state_tuple_base_type::state_tuple_cptr_type &dptr,
            Position<sizeof...(Types)>) const
    {std::get<sizeof...(Types)>(dptr) = data<sizeof...(Types)>();}
}; // class StateTuple

} // namespace vsmc

#endif // VSMC_CORE_STATE_TUPLE_HPP
<commit_msg>add non-const data member for StateTuple<ColMajor><commit_after>//============================================================================
// include/vsmc/core/state_tuple.hpp
//----------------------------------------------------------------------------
//
//                         vSMC: Scalable Monte Carlo
//
// This file is distribured under the 2-clauses BSD License.
// See LICENSE for details.
//============================================================================

#ifndef VSMC_CORE_STATE_TUPLE_HPP
#define VSMC_CORE_STATE_TUPLE_HPP

#include <vsmc/core/single_particle.hpp>
#include <tuple>
#include <vector>

#define VSMC_RUNTIME_ASSERT_CORE_STATE_TUPLE_COPY_SIZE_MISMATCH \
    VSMC_RUNTIME_ASSERT((N == static_cast<size_type>(this->size())),         \
            ("**StateTuple::copy** SIZE MISMATCH"))

namespace vsmc {

/// \brief Base type of StateTuple
/// \ingroup Core
template <MatrixOrder Order, typename T, typename... Types>
class StateTupleBase
{
    public :

    typedef std::size_t size_type;
    typedef std::tuple<T, Types...> state_tuple_type;
    typedef std::tuple<T *, Types *...> state_tuple_ptr_type;
    typedef std::tuple<const T *, const Types *...> state_tuple_cptr_type;

    template <std::size_t Pos> struct state_type
    {typedef typename std::tuple_element<Pos, state_tuple_type>::type type;};

    struct state_pack_type
    {
        state_pack_type () {}

        state_pack_type (const state_pack_type &other) : data_(other.data_) {}

        state_pack_type &operator= (const state_pack_type &other)
        {data_ = other.data_; return *this;}

#if VSMC_HAS_CXX11_RVALUE_REFERENCES
        state_pack_type (state_pack_type &&other) VSMC_NOEXCEPT :
            data_(cxx11::move(other.data_)) {}

        state_pack_type &operator= (state_pack_type &&other) VSMC_NOEXCEPT
        {data_ = cxx11::move(other.data_); return *this;}
#endif

        state_pack_type (const state_tuple_type &data) : data_(data) {}

        operator state_tuple_type () {return data_;}

        template <std::size_t Pos>
        typename state_type<Pos>::type &get ()
        {return std::get<Pos>(data_);}

        template <std::size_t Pos>
        const typename state_type<Pos>::type &get () const
        {return std::get<Pos>(data_);}

        template <typename Archive>
        void serialize (Archive &ar, const unsigned)
        {serialize(ar, Position<0>());}

        template <typename Archive>
        void serialize (Archive &ar, const unsigned) const
        {serialize(ar, Position<0>());}

        private :

        state_tuple_type data_;
        static VSMC_CONSTEXPR const std::size_t dim_ = sizeof...(Types) + 1;

        template <typename Archive, std::size_t Pos>
        void serialize (Archive &ar, Position<Pos>)
        {
            ar & std::get<Pos>(data_);
            serialize(ar, Position<Pos + 1>());
        }

        template <typename Archive>
        void serialize (Archive &, Position<dim_>) {}

        template <typename Archive, std::size_t Pos>
        void serialize (Archive &ar, Position<Pos>) const
        {
            ar & std::get<Pos>(data_);
            serialize(ar, Position<Pos + 1>());
        }

        template <typename Archive>
        void serialize (Archive &, Position<dim_>) const {}
    }; // struct state_pack_type

    template <typename S>
    struct single_particle_type : public SingleParticleBase<S>
    {
        single_particle_type (typename Particle<S>::size_type id,
                Particle<S> *particle_ptr) :
            SingleParticleBase<S>(id, particle_ptr) {}

        static VSMC_CONSTEXPR std::size_t dim () {return S::dim();}

        template <std::size_t Pos>
        typename state_type<Pos>::type &state (Position<Pos>) const
        {
            return this->mutable_particle_ptr()->value().
                state(this->id(), Position<Pos>());
        }

        template <std::size_t Pos>
        typename state_type<Pos>::type &state () const
        {return this->state(Position<Pos>());}
    }; // struct single_particle_type

    template <typename S>
    struct const_single_particle_type : public ConstSingleParticleBase<S>
    {
        const_single_particle_type (typename Particle<S>::size_type id,
                const Particle<S> *particle_ptr) :
            ConstSingleParticleBase<S>(id, particle_ptr) {}

        static VSMC_CONSTEXPR std::size_t dim () {return S::dim();}

        template <std::size_t Pos>
        const typename state_type<Pos>::type &state (Position<Pos>) const
        {
            return this->particle_ptr()->value().
                state(this->id(), Position<Pos>());
        }

        template <std::size_t Pos>
        const typename state_type<Pos>::type &state () const
        {return this->state(Position<Pos>());}
    }; // struct const_single_particle_type

    size_type size () const {return size_;}

    static VSMC_CONSTEXPR std::size_t dim () {return dim_;}

    state_pack_type state_pack (size_type id) const
    {
        state_pack_type pack;
        pack_particle(id, pack, Position<0>());

        return pack;
    }

    void state_unpack (size_type id, const state_pack_type &pack)
    {unpack_particle(id, pack, Position<0>());}

    template <typename IntType>
    void copy (size_type N, const IntType *copy_from)
    {
        VSMC_RUNTIME_ASSERT_CORE_STATE_TUPLE_COPY_SIZE_MISMATCH;

        for (size_type to = 0; to != N; ++to)
            copy_particle(copy_from[to], to);
    }

    template <std::size_t Pos, typename OutputIter>
    OutputIter read_state (Position<Pos>, OutputIter first) const
    {
        const StateTuple<Order, T, Types...> *sptr =
            static_cast<const StateTuple<Order, T, Types...> *>(this);
        for (size_type i = 0; i != size_; ++i, ++first)
                *first = sptr->state(i, Position<Pos>());

        return first;
    }

    template <std::size_t Pos, typename OutputIter>
    OutputIter read_state (OutputIter first) const
    {return read_state(Position<Pos>(), first);}

    template <typename CharT, typename Traits>
    std::basic_ostream<CharT, Traits> &print (
            std::basic_ostream<CharT, Traits> &os, std::size_t iter = 0,
            char sepchar = ' ', char eolchar = '\n') const
    {
        for (size_type i = 0; i != size_; ++i) {
            os << iter << sepchar;
            print_particle(os, i, sepchar, eolchar, Position<0>());
        }

        return os;
    }

    protected :

    explicit StateTupleBase (size_type N) : size_(N) {}

    void copy_particle (size_type from, size_type to)
    {
        if (from != to)
            copy_particle(from, to, Position<0>());
    }

    private :

    size_type size_;
    static VSMC_CONSTEXPR const std::size_t dim_ = sizeof...(Types) + 1;

    template <std::size_t Pos>
    void pack_particle (size_type id, state_pack_type &pack,
            Position<Pos>) const
    {
        const StateTuple<Order, T, Types...> *sptr =
            static_cast<const StateTuple<Order, T, Types...> *>(this);
        pack.template get<Pos>() = sptr->state(id, Position<Pos>());
        pack_particle(id, pack, Position<Pos + 1>());
    }

    void pack_particle (size_type, state_pack_type &,
            Position<dim_>) const {}

    template <std::size_t Pos>
    void unpack_particle (size_type id, const state_pack_type &pack,
            Position<Pos>)
    {
        StateTuple<Order, T, Types...> *sptr =
            static_cast<StateTuple<Order, T, Types...> *>(this);
        sptr->state(id, Position<Pos>()) = pack.template get<Pos>();
        unpack_particle(id, pack, Position<Pos + 1>());
    }

    void unpack_particle (size_type, const state_pack_type &,
            Position<dim_>) {}

    template <std::size_t Pos>
    void copy_particle (size_type from, size_type to, Position<Pos>)
    {
        StateTuple<Order, T, Types...> *sptr =
            static_cast<StateTuple<Order, T, Types...> *>(this);
        sptr->state(to, Position<Pos>()) = sptr->state(from, Position<Pos>());
        copy_particle(from, to, Position<Pos + 1>());
    }

    void copy_particle (size_type, size_type, Position<dim_>) {}

    template <std::size_t Pos, typename CharT, typename Traits>
    void print_particle (std::basic_ostream<CharT, Traits> &os, size_type id,
            char sepchar, char eolchar, Position<Pos>) const
    {
        const StateTuple<Order, T, Types...> *sptr =
            static_cast<const StateTuple<Order, T, Types...> *>(this);
        os << sptr->state(id, Position<Pos>()) << sepchar;
        print_particle(os, id, sepchar, eolchar, Position<Pos + 1>());
    }

    template <typename CharT, typename Traits>
    void print_particle (std::basic_ostream<CharT, Traits> &os, size_type id,
            char, char eolchar, Position<dim_ - 1>) const
    {
        const StateTuple<Order, T, Types...> *sptr =
            static_cast<const StateTuple<Order, T, Types...> *>(this);
        os << sptr->state(id, Position<dim_ - 1>()) << eolchar;
    }
}; // class StateTupleBase

/// \brief Particle::value_type subtype
/// \ingroup Core
template <typename T, typename... Types>
class StateTuple<RowMajor, T, Types...> :
    public StateTupleBase<RowMajor, T, Types...>
{
    public :

    typedef StateTupleBase<RowMajor, T, Types...>
        state_tuple_base_type;
    typedef typename state_tuple_base_type::size_type size_type;

    explicit StateTuple (size_type N) : state_tuple_base_type(N), state_(N) {}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id, Position<Pos>)
    {return std::get<Pos>(state_[id]);}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id, Position<Pos>) const
    {return std::get<Pos>(state_[id]);}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id)
    {return state(id, Position<Pos>());}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id) const
    {return state(id, Position<Pos>());}

    typename state_tuple_base_type::state_tuple_type *data ()
    {return &state_[0];}

    const typename state_tuple_base_type::state_tuple_type *data () const
    {return &state_[0];}

    private :

    static VSMC_CONSTEXPR const std::size_t dim_ = sizeof...(Types) + 1;
    std::vector<std::tuple<T, Types...> > state_;
}; // StateTuple

/// \brief Particle::value_type subtype
/// \ingroup Core
template <typename T, typename... Types>
class StateTuple<ColMajor, T, Types...> :
    public StateTupleBase<ColMajor, T, Types...>
{
    public :

    typedef StateTupleBase<ColMajor, T, Types...>
        state_tuple_base_type;
    typedef typename state_tuple_base_type::size_type size_type;

    explicit StateTuple (size_type N) : state_tuple_base_type(N)
    {init_state(N, Position<0>());}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id, Position<Pos>)
    {return std::get<Pos>(state_)[id];}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id, Position<Pos>) const
    {return std::get<Pos>(state_)[id];}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id)
    {return state(id, Position<Pos>());}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    &state (size_type id) const
    {return state(id, Position<Pos>());}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    *data (Position<Pos>) {return &std::get<Pos>(state_)[0];}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    *data (Position<Pos>) const {return &std::get<Pos>(state_)[0];}

    template <std::size_t Pos>
    typename state_tuple_base_type::template state_type<Pos>::type
    *data () {return &std::get<Pos>(state_)[0];}

    template <std::size_t Pos>
    const typename state_tuple_base_type::template state_type<Pos>::type
    *data () const {return &std::get<Pos>(state_)[0];}

    typename state_tuple_base_type::state_tuple_ptr_type data ()
    {
        typename state_tuple_base_type::state_tuple_ptr_type dptr;
        insert_data(dptr, Position<0>());

        return dptr;
    }

    typename state_tuple_base_type::state_tuple_cptr_type data () const
    {
        typename state_tuple_base_type::state_tuple_cptr_type dptr;
        insert_data(dptr, Position<0>());

        return dptr;
    }

    private :

    static VSMC_CONSTEXPR const std::size_t dim_ = sizeof...(Types) + 1;
    std::tuple<std::vector<T>, std::vector<Types>...> state_;

    template <std::size_t Pos>
    void init_state (size_type N, Position<Pos>)
    {
        std::get<Pos>(state_).resize(N);
        init_state(N, Position<Pos + 1>());
    }

    void init_state (size_type N, Position<sizeof...(Types)>)
    {std::get<sizeof...(Types)>(state_).resize(N);}

    template <std::size_t Pos, typename PTRType>
    void insert_data (PTRType &dptr, Position<Pos>) const
    {
        std::get<Pos>(dptr) = data<Pos>();
        insert_data(dptr, Position<Pos + 1>());
    }

    template <typename PTRType>
    void insert_data (PTRType &dptr, Position<sizeof...(Types)>) const
    {std::get<sizeof...(Types)>(dptr) = data<sizeof...(Types)>();}
}; // class StateTuple

} // namespace vsmc

#endif // VSMC_CORE_STATE_TUPLE_HPP
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017 Couchbase, Inc
 *
 *   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 "collections/vbucket_manifest_entry.h"
#include "bucket_logger.h"
#include "statwriter.h"

#include <platform/checked_snprintf.h>

Collections::VB::ManifestEntry::ManifestEntry(
        const Collections::VB::ManifestEntry& other) {
    *this = other;
}

Collections::VB::ManifestEntry& Collections::VB::ManifestEntry::operator=(
        const ManifestEntry& other) {
    startSeqno = other.startSeqno;
    scopeID = other.scopeID;
    maxTtl = other.maxTtl;
    diskCount = other.diskCount;
    highSeqno.reset(other.highSeqno);
    persistedHighSeqno.store(other.persistedHighSeqno,
                             std::memory_order_relaxed);
    return *this;
}

bool Collections::VB::ManifestEntry::operator==(
        const ManifestEntry& other) const {
    if (scopeID == other.scopeID && startSeqno == other.startSeqno &&
        maxTtl == other.maxTtl && highSeqno == other.highSeqno &&
        persistedHighSeqno == other.persistedHighSeqno) {
        return true;
    }
    return false;
}

std::string Collections::VB::ManifestEntry::getExceptionString(
        const std::string& thrower, const std::string& error) const {
    std::stringstream ss;
    ss << "VB::ManifestEntry::" << thrower << ": " << error
       << ", this:" << *this;
    return ss.str();
}

bool Collections::VB::ManifestEntry::addStats(const std::string& cid,
                                              Vbid vbid,
                                              const void* cookie,
                                              const AddStatFn& add_stat) const {
    try {
        const int bsize = 512;
        char buffer[bsize];
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:scope",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getScopeID(), add_stat, cookie);
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:start_seqno",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getStartSeqno(), add_stat, cookie);
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:high_seqno",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getHighSeqno(), add_stat, cookie);
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:persisted_high_seqno",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getPersistedHighSeqno(), add_stat, cookie);
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:items",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getDiskCount(), add_stat, cookie);

        if (getMaxTtl()) {
            checked_snprintf(buffer,
                             bsize,
                             "vb_%d:collection:%s:entry:maxTTL",
                             vbid.get(),
                             cid.c_str());
            add_casted_stat(buffer, getMaxTtl().value().count(), add_stat, cookie);
        }
        return true;
    } catch (const std::exception& error) {
        EP_LOG_WARN(
                "VB::ManifestEntry::addStats {}, failed to build stats, "
                "exception:{}",
                vbid,
                error.what());
    }
    return false;
}

std::ostream& Collections::VB::operator<<(
        std::ostream& os,
        const Collections::VB::ManifestEntry& manifestEntry) {
    os << "ManifestEntry: scope:" << manifestEntry.getScopeID()
       << ", startSeqno:" << manifestEntry.getStartSeqno()
       << ", highSeqno:" << manifestEntry.getHighSeqno()
       << ", persistedHighSeqno:" << manifestEntry.getPersistedHighSeqno()
       << ", diskCount:" << manifestEntry.getDiskCount();

    if (manifestEntry.getMaxTtl()) {
        os << ", maxTtl:" << manifestEntry.getMaxTtl().value().count();
    }
    return os;
}
<commit_msg>Make collection-details use hex ScopeID<commit_after>/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017 Couchbase, Inc
 *
 *   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 "collections/vbucket_manifest_entry.h"
#include "bucket_logger.h"
#include "statwriter.h"

#include <platform/checked_snprintf.h>

Collections::VB::ManifestEntry::ManifestEntry(
        const Collections::VB::ManifestEntry& other) {
    *this = other;
}

Collections::VB::ManifestEntry& Collections::VB::ManifestEntry::operator=(
        const ManifestEntry& other) {
    startSeqno = other.startSeqno;
    scopeID = other.scopeID;
    maxTtl = other.maxTtl;
    diskCount = other.diskCount;
    highSeqno.reset(other.highSeqno);
    persistedHighSeqno.store(other.persistedHighSeqno,
                             std::memory_order_relaxed);
    return *this;
}

bool Collections::VB::ManifestEntry::operator==(
        const ManifestEntry& other) const {
    if (scopeID == other.scopeID && startSeqno == other.startSeqno &&
        maxTtl == other.maxTtl && highSeqno == other.highSeqno &&
        persistedHighSeqno == other.persistedHighSeqno) {
        return true;
    }
    return false;
}

std::string Collections::VB::ManifestEntry::getExceptionString(
        const std::string& thrower, const std::string& error) const {
    std::stringstream ss;
    ss << "VB::ManifestEntry::" << thrower << ": " << error
       << ", this:" << *this;
    return ss.str();
}

bool Collections::VB::ManifestEntry::addStats(const std::string& cid,
                                              Vbid vbid,
                                              const void* cookie,
                                              const AddStatFn& add_stat) const {
    try {
        const int bsize = 512;
        char buffer[bsize];
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:scope",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getScopeID().to_string(), add_stat, cookie);
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:start_seqno",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getStartSeqno(), add_stat, cookie);
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:high_seqno",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getHighSeqno(), add_stat, cookie);
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:persisted_high_seqno",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getPersistedHighSeqno(), add_stat, cookie);
        checked_snprintf(buffer,
                         bsize,
                         "vb_%d:collection:%s:entry:items",
                         vbid.get(),
                         cid.c_str());
        add_casted_stat(buffer, getDiskCount(), add_stat, cookie);

        if (getMaxTtl()) {
            checked_snprintf(buffer,
                             bsize,
                             "vb_%d:collection:%s:entry:maxTTL",
                             vbid.get(),
                             cid.c_str());
            add_casted_stat(buffer, getMaxTtl().value().count(), add_stat, cookie);
        }
        return true;
    } catch (const std::exception& error) {
        EP_LOG_WARN(
                "VB::ManifestEntry::addStats {}, failed to build stats, "
                "exception:{}",
                vbid,
                error.what());
    }
    return false;
}

std::ostream& Collections::VB::operator<<(
        std::ostream& os,
        const Collections::VB::ManifestEntry& manifestEntry) {
    os << "ManifestEntry: scope:" << manifestEntry.getScopeID()
       << ", startSeqno:" << manifestEntry.getStartSeqno()
       << ", highSeqno:" << manifestEntry.getHighSeqno()
       << ", persistedHighSeqno:" << manifestEntry.getPersistedHighSeqno()
       << ", diskCount:" << manifestEntry.getDiskCount();

    if (manifestEntry.getMaxTtl()) {
        os << ", maxTtl:" << manifestEntry.getMaxTtl().value().count();
    }
    return os;
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2014.  All rights reserved
 */
#ifndef _Stroika_Foundation_Memory_BlockAllocator_inl_
#define _Stroika_Foundation_Memory_BlockAllocator_inl_  1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
#include    <algorithm>
#include    <mutex>
#include    <vector>

#include    "../Debug/Assertions.h"
#include    "../Memory/LeakChecker.h"

#include    "Common.h"

/*
 *	qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_ is probaly best true (empirical tests with the
 *	performance regression test indicated that this helped considerably).
 *
 *	It should be generally pretty safe because the locks are very narrow (so threads shoudlnt spend much time
 *	spinning. And we could reduce this further during mallocs of new blocks.
 */
#if     !defined (qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_)
#define qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_   1
#endif

#if    qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_
#include    "../Execution/SpinLock.h"
#endif

namespace   Stroika {
    namespace   Foundation {
        namespace   Memory {

            namespace   Private_ {

                struct  BlockAllocator_ModuleInit_ {
                    BlockAllocator_ModuleInit_ ();
                    ~BlockAllocator_ModuleInit_ ();
                };

#if    qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_
                using LockType_ = Execution::SpinLock;
#else
                using LockType_ = mutex;
#endif
                extern  LockType_*    sLock_;

                inline  LockType_&    GetLock_ ()
                {
                    AssertNotNull (sLock_);  // automatically initailized by BlockAllocated::Private_::ActualModuleInit
                    return *sLock_;
                }

            }

            namespace   Private_ {

#if     qCompilerAndStdLib_constexpr_Buggy
#define BlockAllocation_Private_AdjustSizeForPool_(n)\
    (((n + sizeof (void*) - 1u) / sizeof (void*)) * sizeof (void*))
#else
                // not quite right - too much of a PITA to support both constexpr and non- just wait til all our compilers support constexpr and then fix!
                inline    constexpr size_t  BlockAllocation_Private_AdjustSizeForPool_ (size_t n)
                {
                    // when we really fix constexpr usage, we can use the below!
                    //return  Math::RoundUpTo (sizeof(T), sizeof (void*));
                    return (((n + sizeof (void*) - 1u) / sizeof (void*)) * sizeof (void*));
                }
#endif

                /*
                * Picked particular kTargetMallocSize since with malloc overhead likely to turn out to be
                * a chunk which memory allocator can do a good job on.
                */
                constexpr   size_t  kTargetMallocSize_   =   16360;                  // 16384 = 16K - leave a few bytes sluff...

                inline    /*constexpr*/ size_t  BlockAllocation_Private_ComputeChunks_ (size_t poolElementSize)
                {
                    return std::max (static_cast<size_t> (kTargetMallocSize_ / poolElementSize), static_cast<size_t> (10));
                }

                // This must be included here to keep genclass happy, since the .cc file will not be included
                // in the genclassed .cc file....
                inline  void**  GetMem_Util_ (size_t sz)
                {
                    Require (sz >= sizeof (void*));

                    const       size_t  kChunks = BlockAllocation_Private_ComputeChunks_ (sz);

                    /*
                     * Please note that the following line is NOT a memory leak. Please look at the
                     * Led FAQ question#29 - "Does Led have any memory leaks?
                     * How does qAllowBlockAllocation affect memory leaks?"
                     */
                    void**  newLinks    =   (void**)DEBUG_NEW char [kChunks * sz];
                    void**  curLink     =   newLinks;
                    for (size_t i = 1; i < kChunks; i++) {
                        *curLink = &(((char*)newLinks)[i * sz]);
                        curLink = (void**)*curLink;
                    }
                    *curLink = nullptr;     // nullptr-terminate the link list
                    return newLinks;
                }

                /*
                 *       In order to reduce fragmentation, we allocate all chunks of common sizes out of
                 *   the same pool. Assuming the compiler is able to inline the references to these
                 *   below, there is really no cost in adding more. I picked the ones I thought most
                 *   likely to come up, but adding more should be just fine - strictly a win.
                 *
                 *       Don't bother implementing Block_Alloced_sizeof_N_GetMems() cuz flunging will
                 *   genericly give us the same code-sharing effect.
                 *
                 *  <<@todo - update docs -.... VERY OUT OF DATE>>>.
                 */
                template    <size_t SIZE>
                class   BlockAllocationPool_  {
                public:
                    static  void*   Allocate (size_t n);
                    static  void    Deallocate (void* p);
                    static  void    Compact ();

                private:
                    // make op new inline for MOST important case
                    // were alloc is cheap linked list operation...
                    static  void*   sNextLink_;
                };

            }

            /*
             ********************************************************************************
             ***************** Private_::BlockAllocationPool_<SIZE> *************************
             ********************************************************************************
             */
            template    <size_t SIZE>
            inline  void*   Private_::BlockAllocationPool_<SIZE>::Allocate (size_t n)
            {
                static_assert (SIZE >= sizeof (void*), "SIZE >= sizeof (void*)");
                Require (n <= SIZE);
                Arg_Unused (n);                         // n only used for debuggging, avoid compiler warning

                lock_guard<LockType_>  critSec (Private_::GetLock_ ());
                /*
                 * To implement linked list of BlockAllocated(T)'s before they are
                 * actually alloced, re-use the begining of this as a link pointer.
                 */
                if (sNextLink_ == nullptr) {
                    sNextLink_ = GetMem_Util_ (SIZE);
                }
                void*   result = sNextLink_;
                AssertNotNull (result);
                /*
                 * treat this as a linked list, and make head point to next member
                 */
                sNextLink_ = (*(void**)sNextLink_);
                return result;
            }
            template    <size_t SIZE>
            inline  void    Private_::BlockAllocationPool_<SIZE>::Deallocate (void* p)
            {
                static_assert (SIZE >= sizeof (void*), "SIZE >= sizeof (void*)");
                Require (p != nullptr);
                lock_guard<LockType_>  critSec (Private_::GetLock_ ());
                // push p onto the head of linked free list
                (*(void**)p) = sNextLink_;
                sNextLink_ = p;
            }
            template    <size_t SIZE>
            void    Private_::BlockAllocationPool_<SIZE>::Compact ()
            {
                lock_guard<LockType_>  critSec (Private_::GetLock_ ());

                // step one: put all the links into a single, sorted vector
                const   size_t  kChunks = BlockAllocation_Private_ComputeChunks_ (SIZE);
                std::vector<void*> links;
                try {
                    links.reserve (kChunks * 2);
                    void*   link = sNextLink_;
                    while (link != nullptr) {
                        // probably should use Containers::ReserveSpeedTweekAddN - but want to avoid embrace of dependencies
                        if (links.size () == links.capacity ()) {
                            links.reserve (links.size () * 2);
                        }
                        links.push_back (link);
                        link = *(void**)link;
                    }
                }
                catch (...) {
                    return;
                }

                std::sort (links.begin (), links.end ());

                // now look for unused memory blocks. Since the vector is sorted by pointer value, the first pointer encountered is potentially the start of a block.
                // Look at the next N vector elements, where N is the amount of elements that would have been alloced (the chunk size). If they are all there, then the
                // block is unused can can be freed. Otherwise do the same thing to the first element found outside of the block.
                size_t  originalSize = links.size ();
                size_t  index = 0;
                while (index < links.size ()) {
                    Byte*   deleteCandidate = (Byte*)links[index];
                    bool    canDelete = true;
                    size_t  i = 1;
                    for (; i < kChunks; ++i) {
                        if ((index + i) >= links.size ()) break;
                        Byte* next = (Byte*)links[index + i];
                        Byte* prev = (Byte*)links[index + i - 1];
                        if (canDelete and ((next - prev) != SIZE)) {
                            canDelete = false;  // don't break here, as have to find end up this block, which could be further on
                        }
                        if (static_cast <size_t> (std::abs (next - deleteCandidate) / SIZE) >= kChunks) {
                            Assert (not canDelete);
                            break;
                        }
                    }
                    if (canDelete) {
                        links.erase (links.begin () + index, links.begin () + index + kChunks);
                        delete static_cast<Byte*> ((void*)deleteCandidate);
                    }
                    else {
                        index += i;
                    }
                }

                // finally, clean up the pool. The head link is probably wrong, and the elements are no longer linked up correctly
                sNextLink_ = (links.size () == 0) ? nullptr : links[0];
                if (links.size () != originalSize and links.size () > 0) {
                    // we delete some, which means that the next pointers are bad
                    void**  curLink     =   (void**)sNextLink_;
                    for (size_t i = 1; i < links.size (); i++) {
                        *curLink = links[i];
                        curLink = (void**)*curLink;
                    }
                    *curLink = nullptr;
                }
            }
            template    <size_t SIZE>
            void*   Private_::BlockAllocationPool_<SIZE>::sNextLink_ = nullptr;

            /*
             ********************************************************************************
             ********************************** BlockAllocator<T> ***************************
             ********************************************************************************
             */
            template    <typename   T>
            inline  void*   BlockAllocator<T>::Allocate (size_t n)
            {
                using Private_::BlockAllocationPool_;
#if     !qCompilerAndStdLib_constexpr_Buggy
                using Private_::BlockAllocation_Private_AdjustSizeForPool_;
#endif
                Require (n == sizeof (T));
                Arg_Unused (n);                         // n only used for debuggging, avoid compiler warning

#if     qAllowBlockAllocation
                return BlockAllocationPool_<BlockAllocation_Private_AdjustSizeForPool_ (sizeof (T))>::Allocate (n);
#else
                return ::operator new (n);
#endif
            }
            template    <typename   T>
            inline  void    BlockAllocator<T>::Deallocate (void* p)
            {
                using Private_::BlockAllocationPool_;
#if     !qCompilerAndStdLib_constexpr_Buggy
                using Private_::BlockAllocation_Private_AdjustSizeForPool_;
#endif
#if     qAllowBlockAllocation
                if (p != nullptr) {
                    BlockAllocationPool_<BlockAllocation_Private_AdjustSizeForPool_ (sizeof (T))>::Deallocate (p);
                }
#else
                ::operator delete (p);
#endif
            }
            template    <typename   T>
            void    BlockAllocator<T>::Compact ()
            {
                using Private_::BlockAllocationPool_;
#if     !qCompilerAndStdLib_constexpr_Buggy
                using Private_::BlockAllocation_Private_AdjustSizeForPool_;
#endif
#if     qAllowBlockAllocation
                BlockAllocationPool_<BlockAllocation_Private_AdjustSizeForPool_ (sizeof (T))>::Compact ();
#endif
            }

        }
    }
}
namespace   {
    Stroika::Foundation::Execution::ModuleInitializer<Stroika::Foundation::Memory::Private_::BlockAllocator_ModuleInit_>   _Stroika_Foundation_Memory_BlockAllocator_ModuleInit_;   // this object constructed for the CTOR/DTOR per-module side-effects
}
#endif  /*_Stroika_Foundation_Memory_BlockAllocator_inl_*/
<commit_msg>runastyle<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2014.  All rights reserved
 */
#ifndef _Stroika_Foundation_Memory_BlockAllocator_inl_
#define _Stroika_Foundation_Memory_BlockAllocator_inl_  1

/*
 ********************************************************************************
 ***************************** Implementation Details ***************************
 ********************************************************************************
 */
#include    <algorithm>
#include    <mutex>
#include    <vector>

#include    "../Debug/Assertions.h"
#include    "../Memory/LeakChecker.h"

#include    "Common.h"

/*
 *  qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_ is probaly best true (empirical tests with the
 *  performance regression test indicated that this helped considerably).
 *
 *  It should be generally pretty safe because the locks are very narrow (so threads shoudlnt spend much time
 *  spinning. And we could reduce this further during mallocs of new blocks.
 */
#if     !defined (qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_)
#define qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_   1
#endif

#if    qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_
#include    "../Execution/SpinLock.h"
#endif

namespace   Stroika {
    namespace   Foundation {
        namespace   Memory {

            namespace   Private_ {

                struct  BlockAllocator_ModuleInit_ {
                    BlockAllocator_ModuleInit_ ();
                    ~BlockAllocator_ModuleInit_ ();
                };

#if    qStroika_Foundation_Memory_BlockAllocator_UseSpinLock_
                using LockType_ = Execution::SpinLock;
#else
                using LockType_ = mutex;
#endif
                extern  LockType_*    sLock_;

                inline  LockType_&    GetLock_ ()
                {
                    AssertNotNull (sLock_);  // automatically initailized by BlockAllocated::Private_::ActualModuleInit
                    return *sLock_;
                }

            }

            namespace   Private_ {

#if     qCompilerAndStdLib_constexpr_Buggy
#define BlockAllocation_Private_AdjustSizeForPool_(n)\
    (((n + sizeof (void*) - 1u) / sizeof (void*)) * sizeof (void*))
#else
                // not quite right - too much of a PITA to support both constexpr and non- just wait til all our compilers support constexpr and then fix!
                inline    constexpr size_t  BlockAllocation_Private_AdjustSizeForPool_ (size_t n)
                {
                    // when we really fix constexpr usage, we can use the below!
                    //return  Math::RoundUpTo (sizeof(T), sizeof (void*));
                    return (((n + sizeof (void*) - 1u) / sizeof (void*)) * sizeof (void*));
                }
#endif

                /*
                * Picked particular kTargetMallocSize since with malloc overhead likely to turn out to be
                * a chunk which memory allocator can do a good job on.
                */
                constexpr   size_t  kTargetMallocSize_   =   16360;                  // 16384 = 16K - leave a few bytes sluff...

                inline    /*constexpr*/ size_t  BlockAllocation_Private_ComputeChunks_ (size_t poolElementSize)
                {
                    return std::max (static_cast<size_t> (kTargetMallocSize_ / poolElementSize), static_cast<size_t> (10));
                }

                // This must be included here to keep genclass happy, since the .cc file will not be included
                // in the genclassed .cc file....
                inline  void**  GetMem_Util_ (size_t sz)
                {
                    Require (sz >= sizeof (void*));

                    const       size_t  kChunks = BlockAllocation_Private_ComputeChunks_ (sz);

                    /*
                     * Please note that the following line is NOT a memory leak. Please look at the
                     * Led FAQ question#29 - "Does Led have any memory leaks?
                     * How does qAllowBlockAllocation affect memory leaks?"
                     */
                    void**  newLinks    =   (void**)DEBUG_NEW char [kChunks * sz];
                    void**  curLink     =   newLinks;
                    for (size_t i = 1; i < kChunks; i++) {
                        *curLink = &(((char*)newLinks)[i * sz]);
                        curLink = (void**)*curLink;
                    }
                    *curLink = nullptr;     // nullptr-terminate the link list
                    return newLinks;
                }

                /*
                 *       In order to reduce fragmentation, we allocate all chunks of common sizes out of
                 *   the same pool. Assuming the compiler is able to inline the references to these
                 *   below, there is really no cost in adding more. I picked the ones I thought most
                 *   likely to come up, but adding more should be just fine - strictly a win.
                 *
                 *       Don't bother implementing Block_Alloced_sizeof_N_GetMems() cuz flunging will
                 *   genericly give us the same code-sharing effect.
                 *
                 *  <<@todo - update docs -.... VERY OUT OF DATE>>>.
                 */
                template    <size_t SIZE>
                class   BlockAllocationPool_  {
                public:
                    static  void*   Allocate (size_t n);
                    static  void    Deallocate (void* p);
                    static  void    Compact ();

                private:
                    // make op new inline for MOST important case
                    // were alloc is cheap linked list operation...
                    static  void*   sNextLink_;
                };

            }

            /*
             ********************************************************************************
             ***************** Private_::BlockAllocationPool_<SIZE> *************************
             ********************************************************************************
             */
            template    <size_t SIZE>
            inline  void*   Private_::BlockAllocationPool_<SIZE>::Allocate (size_t n)
            {
                static_assert (SIZE >= sizeof (void*), "SIZE >= sizeof (void*)");
                Require (n <= SIZE);
                Arg_Unused (n);                         // n only used for debuggging, avoid compiler warning

                lock_guard<LockType_>  critSec (Private_::GetLock_ ());
                /*
                 * To implement linked list of BlockAllocated(T)'s before they are
                 * actually alloced, re-use the begining of this as a link pointer.
                 */
                if (sNextLink_ == nullptr) {
                    sNextLink_ = GetMem_Util_ (SIZE);
                }
                void*   result = sNextLink_;
                AssertNotNull (result);
                /*
                 * treat this as a linked list, and make head point to next member
                 */
                sNextLink_ = (*(void**)sNextLink_);
                return result;
            }
            template    <size_t SIZE>
            inline  void    Private_::BlockAllocationPool_<SIZE>::Deallocate (void* p)
            {
                static_assert (SIZE >= sizeof (void*), "SIZE >= sizeof (void*)");
                Require (p != nullptr);
                lock_guard<LockType_>  critSec (Private_::GetLock_ ());
                // push p onto the head of linked free list
                (*(void**)p) = sNextLink_;
                sNextLink_ = p;
            }
            template    <size_t SIZE>
            void    Private_::BlockAllocationPool_<SIZE>::Compact ()
            {
                lock_guard<LockType_>  critSec (Private_::GetLock_ ());

                // step one: put all the links into a single, sorted vector
                const   size_t  kChunks = BlockAllocation_Private_ComputeChunks_ (SIZE);
                std::vector<void*> links;
                try {
                    links.reserve (kChunks * 2);
                    void*   link = sNextLink_;
                    while (link != nullptr) {
                        // probably should use Containers::ReserveSpeedTweekAddN - but want to avoid embrace of dependencies
                        if (links.size () == links.capacity ()) {
                            links.reserve (links.size () * 2);
                        }
                        links.push_back (link);
                        link = *(void**)link;
                    }
                }
                catch (...) {
                    return;
                }

                std::sort (links.begin (), links.end ());

                // now look for unused memory blocks. Since the vector is sorted by pointer value, the first pointer encountered is potentially the start of a block.
                // Look at the next N vector elements, where N is the amount of elements that would have been alloced (the chunk size). If they are all there, then the
                // block is unused can can be freed. Otherwise do the same thing to the first element found outside of the block.
                size_t  originalSize = links.size ();
                size_t  index = 0;
                while (index < links.size ()) {
                    Byte*   deleteCandidate = (Byte*)links[index];
                    bool    canDelete = true;
                    size_t  i = 1;
                    for (; i < kChunks; ++i) {
                        if ((index + i) >= links.size ()) break;
                        Byte* next = (Byte*)links[index + i];
                        Byte* prev = (Byte*)links[index + i - 1];
                        if (canDelete and ((next - prev) != SIZE)) {
                            canDelete = false;  // don't break here, as have to find end up this block, which could be further on
                        }
                        if (static_cast <size_t> (std::abs (next - deleteCandidate) / SIZE) >= kChunks) {
                            Assert (not canDelete);
                            break;
                        }
                    }
                    if (canDelete) {
                        links.erase (links.begin () + index, links.begin () + index + kChunks);
                        delete static_cast<Byte*> ((void*)deleteCandidate);
                    }
                    else {
                        index += i;
                    }
                }

                // finally, clean up the pool. The head link is probably wrong, and the elements are no longer linked up correctly
                sNextLink_ = (links.size () == 0) ? nullptr : links[0];
                if (links.size () != originalSize and links.size () > 0) {
                    // we delete some, which means that the next pointers are bad
                    void**  curLink     =   (void**)sNextLink_;
                    for (size_t i = 1; i < links.size (); i++) {
                        *curLink = links[i];
                        curLink = (void**)*curLink;
                    }
                    *curLink = nullptr;
                }
            }
            template    <size_t SIZE>
            void*   Private_::BlockAllocationPool_<SIZE>::sNextLink_ = nullptr;

            /*
             ********************************************************************************
             ********************************** BlockAllocator<T> ***************************
             ********************************************************************************
             */
            template    <typename   T>
            inline  void*   BlockAllocator<T>::Allocate (size_t n)
            {
                using Private_::BlockAllocationPool_;
#if     !qCompilerAndStdLib_constexpr_Buggy
                using Private_::BlockAllocation_Private_AdjustSizeForPool_;
#endif
                Require (n == sizeof (T));
                Arg_Unused (n);                         // n only used for debuggging, avoid compiler warning

#if     qAllowBlockAllocation
                return BlockAllocationPool_<BlockAllocation_Private_AdjustSizeForPool_ (sizeof (T))>::Allocate (n);
#else
                return ::operator new (n);
#endif
            }
            template    <typename   T>
            inline  void    BlockAllocator<T>::Deallocate (void* p)
            {
                using Private_::BlockAllocationPool_;
#if     !qCompilerAndStdLib_constexpr_Buggy
                using Private_::BlockAllocation_Private_AdjustSizeForPool_;
#endif
#if     qAllowBlockAllocation
                if (p != nullptr) {
                    BlockAllocationPool_<BlockAllocation_Private_AdjustSizeForPool_ (sizeof (T))>::Deallocate (p);
                }
#else
                ::operator delete (p);
#endif
            }
            template    <typename   T>
            void    BlockAllocator<T>::Compact ()
            {
                using Private_::BlockAllocationPool_;
#if     !qCompilerAndStdLib_constexpr_Buggy
                using Private_::BlockAllocation_Private_AdjustSizeForPool_;
#endif
#if     qAllowBlockAllocation
                BlockAllocationPool_<BlockAllocation_Private_AdjustSizeForPool_ (sizeof (T))>::Compact ();
#endif
            }

        }
    }
}
namespace   {
    Stroika::Foundation::Execution::ModuleInitializer<Stroika::Foundation::Memory::Private_::BlockAllocator_ModuleInit_>   _Stroika_Foundation_Memory_BlockAllocator_ModuleInit_;   // this object constructed for the CTOR/DTOR per-module side-effects
}
#endif  /*_Stroika_Foundation_Memory_BlockAllocator_inl_*/
<|endoftext|>


<commit_before>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "default_tensor_engine.h"
#include "tensor.h"
#include "default_tensor.h"
#include "wrapped_simple_tensor.h"
#include "serialization/typed_binary_format.h"
#include "dense/dense_tensor.h"
#include "dense/dense_tensor_builder.h"
#include "dense/dense_tensor_function_compiler.h"
#include <vespa/eval/eval/value.h>
#include <vespa/eval/eval/tensor_spec.h>
#include <vespa/eval/eval/operation_visitor.h>
#include <vespa/eval/eval/simple_tensor_engine.h>
#include <cassert>

namespace vespalib {
namespace tensor {

using eval::Aggr;
using eval::Aggregator;
using eval::DoubleValue;
using eval::ErrorValue;
using eval::TensorSpec;
using eval::TensorValue;
using eval::Value;
using eval::ValueType;

using map_fun_t = eval::TensorEngine::map_fun_t;
using join_fun_t = eval::TensorEngine::join_fun_t;

namespace {

const eval::TensorEngine &simple_engine() { return eval::SimpleTensorEngine::ref(); }
const eval::TensorEngine &default_engine() { return DefaultTensorEngine::ref(); }

// map tensors to simple tensors before fall-back evaluation

const eval::SimpleTensor &to_simple(const eval::Tensor &tensor, Stash &stash) {
    if (auto wrapped = dynamic_cast<const WrappedSimpleTensor *>(&tensor)) {
        return wrapped->get();
    }
    TensorSpec spec = tensor.engine().to_spec(tensor);
    using PTR = std::unique_ptr<eval::SimpleTensor>;
    return *stash.create<PTR>(eval::SimpleTensor::create(spec));
}

const Value &to_simple(const Value &value, Stash &stash) {
    if (auto tensor = value.as_tensor()) {
        return stash.create<TensorValue>(to_simple(*tensor, stash));
    }
    return value;
}

// map tensors to default tensors after fall-back evaluation

const Value &to_default(const Value &value, Stash &stash) {
    if (auto tensor = value.as_tensor()) {
        if (auto simple = dynamic_cast<const eval::SimpleTensor *>(tensor)) {
            if (!Tensor::supported({simple->type()})) {
                return stash.create<TensorValue>(std::make_unique<WrappedSimpleTensor>(*simple));
            }
        }
        TensorSpec spec = tensor->engine().to_spec(*tensor);
        return stash.create<TensorValue>(default_engine().create(spec));
    }
    return value;
}

const Value &to_value(std::unique_ptr<Tensor> tensor, Stash &stash) {
    if (!tensor) {
        return ErrorValue::instance;
    }
    if (tensor->getType().is_tensor()) {
        return stash.create<TensorValue>(std::move(tensor));
    }
    return stash.create<DoubleValue>(tensor->sum());
}

const Value &fallback_join(const Value &a, const Value &b, join_fun_t function, Stash &stash) {
    return to_default(simple_engine().join(to_simple(a, stash), to_simple(b, stash), function, stash), stash);
}

const Value &fallback_reduce(const Value &a, eval::Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) {
    return to_default(simple_engine().reduce(to_simple(a, stash), aggr, dimensions, stash), stash);
}

} // namespace vespalib::tensor::<unnamed>

const DefaultTensorEngine DefaultTensorEngine::_engine;

eval::ValueType
DefaultTensorEngine::type_of(const Tensor &tensor) const
{
    assert(&tensor.engine() == this);
    const tensor::Tensor &my_tensor = static_cast<const tensor::Tensor &>(tensor);
    return my_tensor.getType();
}

bool
DefaultTensorEngine::equal(const Tensor &a, const Tensor &b) const
{
    assert(&a.engine() == this);
    assert(&b.engine() == this);
    const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(a);
    const tensor::Tensor &my_b = static_cast<const tensor::Tensor &>(b);
    return my_a.equals(my_b);
}

vespalib::string
DefaultTensorEngine::to_string(const Tensor &tensor) const
{
    assert(&tensor.engine() == this);
    const tensor::Tensor &my_tensor = static_cast<const tensor::Tensor &>(tensor);
    return my_tensor.toString();
}

TensorSpec
DefaultTensorEngine::to_spec(const Tensor &tensor) const
{
    assert(&tensor.engine() == this);
    const tensor::Tensor &my_tensor = static_cast<const tensor::Tensor &>(tensor);
    return my_tensor.toSpec();
}

eval::TensorFunction::UP
DefaultTensorEngine::compile(eval::tensor_function::Node_UP expr) const
{
    return DenseTensorFunctionCompiler::compile(std::move(expr));
}

std::unique_ptr<eval::Tensor>
DefaultTensorEngine::create(const TensorSpec &spec) const
{
    ValueType type = ValueType::from_spec(spec.type());
    bool is_dense = false;
    bool is_sparse = false;
    for (const auto &dimension: type.dimensions()) {
        if (dimension.is_mapped()) {
            is_sparse = true;
        }
        if (dimension.is_indexed()) {
            is_dense = true;
        }
    }
    if (is_dense && is_sparse) {
        return std::make_unique<WrappedSimpleTensor>(eval::SimpleTensor::create(spec));
    } else if (is_dense) {
        DenseTensorBuilder builder;
        std::map<vespalib::string,DenseTensorBuilder::Dimension> dimension_map;
        for (const auto &dimension: type.dimensions()) {
            dimension_map[dimension.name] = builder.defineDimension(dimension.name, dimension.size);
        }
        for (const auto &cell: spec.cells()) {
            const auto &address = cell.first;
            for (const auto &binding: address) {
                builder.addLabel(dimension_map[binding.first], binding.second.index);
            }
            builder.addCell(cell.second);
        }
        return builder.build();
    } else { // sparse
        DefaultTensor::builder builder;
        std::map<vespalib::string,DefaultTensor::builder::Dimension> dimension_map;
        for (const auto &dimension: type.dimensions()) {
            dimension_map[dimension.name] = builder.define_dimension(dimension.name);
        }
        for (const auto &cell: spec.cells()) {
            const auto &address = cell.first;
            for (const auto &binding: address) {
                builder.add_label(dimension_map[binding.first], binding.second.name);
            }
            builder.add_cell(cell.second);
        }
        return builder.build();
    }
}

struct CellFunctionFunAdapter : tensor::CellFunction {
    map_fun_t fun;
    CellFunctionFunAdapter(map_fun_t fun_in) : fun(fun_in) {}
    virtual double apply(double value) const override { return fun(value); }
};

struct CellFunctionBindLeftAdapter : tensor::CellFunction {
    join_fun_t fun;
    double a;
    CellFunctionBindLeftAdapter(join_fun_t fun_in, double bound) : fun(fun_in), a(bound) {}
    virtual double apply(double b) const override { return fun(a, b); }
};

struct CellFunctionBindRightAdapter : tensor::CellFunction {
    join_fun_t fun;
    double b;
    CellFunctionBindRightAdapter(join_fun_t fun_in, double bound) : fun(fun_in), b(bound) {}
    virtual double apply(double a) const override { return fun(a, b); }
};

//-----------------------------------------------------------------------------

void
DefaultTensorEngine::encode(const Value &value, nbostream &output, Stash &) const
{
    if (auto tensor = value.as_tensor()) {
        TypedBinaryFormat::serialize(output, static_cast<const tensor::Tensor &>(*tensor));
    } else {
        TypedBinaryFormat::serialize(output, DenseTensor(ValueType::double_type(), {value.as_double()}));
    }
}

const Value &
DefaultTensorEngine::decode(nbostream &input, Stash &stash) const
{
    return to_value(TypedBinaryFormat::deserialize(input), stash);
}

//-----------------------------------------------------------------------------

const Value &
DefaultTensorEngine::map(const Value &a, map_fun_t function, Stash &stash) const
{
    if (a.is_double()) {
        return stash.create<DoubleValue>(function(a.as_double()));
    } else if (auto tensor = a.as_tensor()) {
        assert(&tensor->engine() == this);
        const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(*tensor);
        if (!tensor::Tensor::supported({my_a.getType()})) {
            return to_default(simple_engine().map(to_simple(a, stash), function, stash), stash);
        }
        CellFunctionFunAdapter cell_function(function);
        return to_value(my_a.apply(cell_function), stash);
    } else {
        return ErrorValue::instance;
    }
}

const Value &
DefaultTensorEngine::join(const Value &a, const Value &b, join_fun_t function, Stash &stash) const
{
    if (a.is_double()) {
        if (b.is_double()) {
            return stash.create<DoubleValue>(function(a.as_double(), b.as_double()));
        } else if (auto tensor_b = b.as_tensor()) {
            assert(&tensor_b->engine() == this);
            const tensor::Tensor &my_b = static_cast<const tensor::Tensor &>(*tensor_b);
            if (!tensor::Tensor::supported({my_b.getType()})) {
                return fallback_join(a, b, function, stash);
            }
            CellFunctionBindLeftAdapter cell_function(function, a.as_double());
            return to_value(my_b.apply(cell_function), stash);
        } else {
            return ErrorValue::instance;
        }
    } else if (auto tensor_a = a.as_tensor()) {
        assert(&tensor_a->engine() == this);
        const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(*tensor_a);
        if (b.is_double()) {
            if (!tensor::Tensor::supported({my_a.getType()})) {
                return fallback_join(a, b, function, stash);
            }
            CellFunctionBindRightAdapter cell_function(function, b.as_double());
            return to_value(my_a.apply(cell_function), stash);
        } else if (auto tensor_b = b.as_tensor()) {
            assert(&tensor_b->engine() == this);
            const tensor::Tensor &my_b = static_cast<const tensor::Tensor &>(*tensor_b);
            if (!tensor::Tensor::supported({my_a.getType(), my_b.getType()})) {
                return fallback_join(a, b, function, stash);
            }
            if (function == eval::operation::Mul::f) {
                if (my_a.getType() == my_b.getType()) {
                    return to_value(my_a.match(my_b), stash);
                } else {
                    return to_value(my_a.multiply(my_b), stash);
                }
            } else {
                return to_value(my_a.join(function, my_b), stash);
            }
        } else {
            return ErrorValue::instance;
        }
    } else {
        return ErrorValue::instance;
    }
}

const Value &
DefaultTensorEngine::reduce(const Value &a, Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) const
{
    if (a.is_double()) {
        Aggregator &aggregator = Aggregator::create(aggr, stash);
        aggregator.first(a.as_double());
        return stash.create<DoubleValue>(aggregator.result());
    } else if (auto tensor = a.as_tensor()) {
        assert(&tensor->engine() == this);
        const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(*tensor);
        if (!tensor::Tensor::supported({my_a.getType()})) {
            return fallback_reduce(a, aggr, dimensions, stash);
        }
        switch (aggr) {
        case Aggr::PROD: return to_value(my_a.reduce(eval::operation::Mul(), dimensions), stash);
        case Aggr::SUM:
            if (dimensions.empty()) {
                return stash.create<eval::DoubleValue>(my_a.sum());
            } else if (dimensions.size() == 1) {
                return to_value(my_a.sum(dimensions[0]), stash);
            } else {
                return to_value(my_a.reduce(eval::operation::Add(), dimensions), stash);
            }
        case Aggr::MAX: return to_value(my_a.reduce(eval::operation::Max(), dimensions), stash);
        case Aggr::MIN: return to_value(my_a.reduce(eval::operation::Min(), dimensions), stash);
        default:
            return fallback_reduce(a, aggr, dimensions, stash);
        }
    } else {
        return ErrorValue::instance;
    }
}

const Value &
DefaultTensorEngine::concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const
{
    return to_default(simple_engine().concat(to_simple(a, stash), to_simple(b, stash), dimension, stash), stash);
}

const Value &
DefaultTensorEngine::rename(const Value &a, const std::vector<vespalib::string> &from, const std::vector<vespalib::string> &to, Stash &stash) const
{
    return to_default(simple_engine().rename(to_simple(a, stash), from, to, stash), stash);
}

//-----------------------------------------------------------------------------

} // namespace vespalib::tensor
} // namespace vespalib
<commit_msg>it is not allowed to reduce over a non-existing dimension<commit_after>// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include "default_tensor_engine.h"
#include "tensor.h"
#include "default_tensor.h"
#include "wrapped_simple_tensor.h"
#include "serialization/typed_binary_format.h"
#include "dense/dense_tensor.h"
#include "dense/dense_tensor_builder.h"
#include "dense/dense_tensor_function_compiler.h"
#include <vespa/eval/eval/value.h>
#include <vespa/eval/eval/tensor_spec.h>
#include <vespa/eval/eval/operation_visitor.h>
#include <vespa/eval/eval/simple_tensor_engine.h>
#include <cassert>

namespace vespalib {
namespace tensor {

using eval::Aggr;
using eval::Aggregator;
using eval::DoubleValue;
using eval::ErrorValue;
using eval::TensorSpec;
using eval::TensorValue;
using eval::Value;
using eval::ValueType;

using map_fun_t = eval::TensorEngine::map_fun_t;
using join_fun_t = eval::TensorEngine::join_fun_t;

namespace {

const eval::TensorEngine &simple_engine() { return eval::SimpleTensorEngine::ref(); }
const eval::TensorEngine &default_engine() { return DefaultTensorEngine::ref(); }

// map tensors to simple tensors before fall-back evaluation

const eval::SimpleTensor &to_simple(const eval::Tensor &tensor, Stash &stash) {
    if (auto wrapped = dynamic_cast<const WrappedSimpleTensor *>(&tensor)) {
        return wrapped->get();
    }
    TensorSpec spec = tensor.engine().to_spec(tensor);
    using PTR = std::unique_ptr<eval::SimpleTensor>;
    return *stash.create<PTR>(eval::SimpleTensor::create(spec));
}

const Value &to_simple(const Value &value, Stash &stash) {
    if (auto tensor = value.as_tensor()) {
        return stash.create<TensorValue>(to_simple(*tensor, stash));
    }
    return value;
}

// map tensors to default tensors after fall-back evaluation

const Value &to_default(const Value &value, Stash &stash) {
    if (auto tensor = value.as_tensor()) {
        if (auto simple = dynamic_cast<const eval::SimpleTensor *>(tensor)) {
            if (!Tensor::supported({simple->type()})) {
                return stash.create<TensorValue>(std::make_unique<WrappedSimpleTensor>(*simple));
            }
        }
        TensorSpec spec = tensor->engine().to_spec(*tensor);
        return stash.create<TensorValue>(default_engine().create(spec));
    }
    return value;
}

const Value &to_value(std::unique_ptr<Tensor> tensor, Stash &stash) {
    if (!tensor) {
        return ErrorValue::instance;
    }
    if (tensor->getType().is_tensor()) {
        return stash.create<TensorValue>(std::move(tensor));
    }
    return stash.create<DoubleValue>(tensor->sum());
}

const Value &fallback_join(const Value &a, const Value &b, join_fun_t function, Stash &stash) {
    return to_default(simple_engine().join(to_simple(a, stash), to_simple(b, stash), function, stash), stash);
}

const Value &fallback_reduce(const Value &a, eval::Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) {
    return to_default(simple_engine().reduce(to_simple(a, stash), aggr, dimensions, stash), stash);
}

} // namespace vespalib::tensor::<unnamed>

const DefaultTensorEngine DefaultTensorEngine::_engine;

eval::ValueType
DefaultTensorEngine::type_of(const Tensor &tensor) const
{
    assert(&tensor.engine() == this);
    const tensor::Tensor &my_tensor = static_cast<const tensor::Tensor &>(tensor);
    return my_tensor.getType();
}

bool
DefaultTensorEngine::equal(const Tensor &a, const Tensor &b) const
{
    assert(&a.engine() == this);
    assert(&b.engine() == this);
    const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(a);
    const tensor::Tensor &my_b = static_cast<const tensor::Tensor &>(b);
    return my_a.equals(my_b);
}

vespalib::string
DefaultTensorEngine::to_string(const Tensor &tensor) const
{
    assert(&tensor.engine() == this);
    const tensor::Tensor &my_tensor = static_cast<const tensor::Tensor &>(tensor);
    return my_tensor.toString();
}

TensorSpec
DefaultTensorEngine::to_spec(const Tensor &tensor) const
{
    assert(&tensor.engine() == this);
    const tensor::Tensor &my_tensor = static_cast<const tensor::Tensor &>(tensor);
    return my_tensor.toSpec();
}

eval::TensorFunction::UP
DefaultTensorEngine::compile(eval::tensor_function::Node_UP expr) const
{
    return DenseTensorFunctionCompiler::compile(std::move(expr));
}

std::unique_ptr<eval::Tensor>
DefaultTensorEngine::create(const TensorSpec &spec) const
{
    ValueType type = ValueType::from_spec(spec.type());
    bool is_dense = false;
    bool is_sparse = false;
    for (const auto &dimension: type.dimensions()) {
        if (dimension.is_mapped()) {
            is_sparse = true;
        }
        if (dimension.is_indexed()) {
            is_dense = true;
        }
    }
    if (is_dense && is_sparse) {
        return std::make_unique<WrappedSimpleTensor>(eval::SimpleTensor::create(spec));
    } else if (is_dense) {
        DenseTensorBuilder builder;
        std::map<vespalib::string,DenseTensorBuilder::Dimension> dimension_map;
        for (const auto &dimension: type.dimensions()) {
            dimension_map[dimension.name] = builder.defineDimension(dimension.name, dimension.size);
        }
        for (const auto &cell: spec.cells()) {
            const auto &address = cell.first;
            for (const auto &binding: address) {
                builder.addLabel(dimension_map[binding.first], binding.second.index);
            }
            builder.addCell(cell.second);
        }
        return builder.build();
    } else { // sparse
        DefaultTensor::builder builder;
        std::map<vespalib::string,DefaultTensor::builder::Dimension> dimension_map;
        for (const auto &dimension: type.dimensions()) {
            dimension_map[dimension.name] = builder.define_dimension(dimension.name);
        }
        for (const auto &cell: spec.cells()) {
            const auto &address = cell.first;
            for (const auto &binding: address) {
                builder.add_label(dimension_map[binding.first], binding.second.name);
            }
            builder.add_cell(cell.second);
        }
        return builder.build();
    }
}

struct CellFunctionFunAdapter : tensor::CellFunction {
    map_fun_t fun;
    CellFunctionFunAdapter(map_fun_t fun_in) : fun(fun_in) {}
    virtual double apply(double value) const override { return fun(value); }
};

struct CellFunctionBindLeftAdapter : tensor::CellFunction {
    join_fun_t fun;
    double a;
    CellFunctionBindLeftAdapter(join_fun_t fun_in, double bound) : fun(fun_in), a(bound) {}
    virtual double apply(double b) const override { return fun(a, b); }
};

struct CellFunctionBindRightAdapter : tensor::CellFunction {
    join_fun_t fun;
    double b;
    CellFunctionBindRightAdapter(join_fun_t fun_in, double bound) : fun(fun_in), b(bound) {}
    virtual double apply(double a) const override { return fun(a, b); }
};

//-----------------------------------------------------------------------------

void
DefaultTensorEngine::encode(const Value &value, nbostream &output, Stash &) const
{
    if (auto tensor = value.as_tensor()) {
        TypedBinaryFormat::serialize(output, static_cast<const tensor::Tensor &>(*tensor));
    } else {
        TypedBinaryFormat::serialize(output, DenseTensor(ValueType::double_type(), {value.as_double()}));
    }
}

const Value &
DefaultTensorEngine::decode(nbostream &input, Stash &stash) const
{
    return to_value(TypedBinaryFormat::deserialize(input), stash);
}

//-----------------------------------------------------------------------------

const Value &
DefaultTensorEngine::map(const Value &a, map_fun_t function, Stash &stash) const
{
    if (a.is_double()) {
        return stash.create<DoubleValue>(function(a.as_double()));
    } else if (auto tensor = a.as_tensor()) {
        assert(&tensor->engine() == this);
        const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(*tensor);
        if (!tensor::Tensor::supported({my_a.getType()})) {
            return to_default(simple_engine().map(to_simple(a, stash), function, stash), stash);
        }
        CellFunctionFunAdapter cell_function(function);
        return to_value(my_a.apply(cell_function), stash);
    } else {
        return ErrorValue::instance;
    }
}

const Value &
DefaultTensorEngine::join(const Value &a, const Value &b, join_fun_t function, Stash &stash) const
{
    if (a.is_double()) {
        if (b.is_double()) {
            return stash.create<DoubleValue>(function(a.as_double(), b.as_double()));
        } else if (auto tensor_b = b.as_tensor()) {
            assert(&tensor_b->engine() == this);
            const tensor::Tensor &my_b = static_cast<const tensor::Tensor &>(*tensor_b);
            if (!tensor::Tensor::supported({my_b.getType()})) {
                return fallback_join(a, b, function, stash);
            }
            CellFunctionBindLeftAdapter cell_function(function, a.as_double());
            return to_value(my_b.apply(cell_function), stash);
        } else {
            return ErrorValue::instance;
        }
    } else if (auto tensor_a = a.as_tensor()) {
        assert(&tensor_a->engine() == this);
        const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(*tensor_a);
        if (b.is_double()) {
            if (!tensor::Tensor::supported({my_a.getType()})) {
                return fallback_join(a, b, function, stash);
            }
            CellFunctionBindRightAdapter cell_function(function, b.as_double());
            return to_value(my_a.apply(cell_function), stash);
        } else if (auto tensor_b = b.as_tensor()) {
            assert(&tensor_b->engine() == this);
            const tensor::Tensor &my_b = static_cast<const tensor::Tensor &>(*tensor_b);
            if (!tensor::Tensor::supported({my_a.getType(), my_b.getType()})) {
                return fallback_join(a, b, function, stash);
            }
            if (function == eval::operation::Mul::f) {
                if (my_a.getType() == my_b.getType()) {
                    return to_value(my_a.match(my_b), stash);
                } else {
                    return to_value(my_a.multiply(my_b), stash);
                }
            } else {
                return to_value(my_a.join(function, my_b), stash);
            }
        } else {
            return ErrorValue::instance;
        }
    } else {
        return ErrorValue::instance;
    }
}

const Value &
DefaultTensorEngine::reduce(const Value &a, Aggr aggr, const std::vector<vespalib::string> &dimensions, Stash &stash) const
{
    if (a.is_double()) {
        if (dimensions.empty()) {
            Aggregator &aggregator = Aggregator::create(aggr, stash);
            aggregator.first(a.as_double());
            return stash.create<DoubleValue>(aggregator.result());
        } else {
            return ErrorValue::instance;
        }
    } else if (auto tensor = a.as_tensor()) {
        assert(&tensor->engine() == this);
        const tensor::Tensor &my_a = static_cast<const tensor::Tensor &>(*tensor);
        if (!tensor::Tensor::supported({my_a.getType()})) {
            return fallback_reduce(a, aggr, dimensions, stash);
        }
        switch (aggr) {
        case Aggr::PROD: return to_value(my_a.reduce(eval::operation::Mul(), dimensions), stash);
        case Aggr::SUM:
            if (dimensions.empty()) {
                return stash.create<eval::DoubleValue>(my_a.sum());
            } else if (dimensions.size() == 1) {
                return to_value(my_a.sum(dimensions[0]), stash);
            } else {
                return to_value(my_a.reduce(eval::operation::Add(), dimensions), stash);
            }
        case Aggr::MAX: return to_value(my_a.reduce(eval::operation::Max(), dimensions), stash);
        case Aggr::MIN: return to_value(my_a.reduce(eval::operation::Min(), dimensions), stash);
        default:
            return fallback_reduce(a, aggr, dimensions, stash);
        }
    } else {
        return ErrorValue::instance;
    }
}

const Value &
DefaultTensorEngine::concat(const Value &a, const Value &b, const vespalib::string &dimension, Stash &stash) const
{
    return to_default(simple_engine().concat(to_simple(a, stash), to_simple(b, stash), dimension, stash), stash);
}

const Value &
DefaultTensorEngine::rename(const Value &a, const std::vector<vespalib::string> &from, const std::vector<vespalib::string> &to, Stash &stash) const
{
    return to_default(simple_engine().rename(to_simple(a, stash), from, to, stash), stash);
}

//-----------------------------------------------------------------------------

} // namespace vespalib::tensor
} // namespace vespalib
<|endoftext|>


<commit_before>/*
 * main.cpp -- This file is part of primesieve
 *
 * Copyright (C) 2011 Kim Walisch, <kim.walisch@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

#include <QtGui/QApplication>
#include "PrimeSieveGUI.h"
#include "../src/PrimeSieve.h"
#include "../src/PrimeNumberFinder.h"
#include "../src/utils/strtoull.h"

#include <QSharedMemory>
#include <stdint.h>
#include <stdexcept>
#include <cstdlib>
#include <iostream>

/**
 * The Qt GUI interface is launched if the user launches the
 * application by mouse click, a sieve of Eratosthenes process (no
 * GUI) is launched if exactly 5 arguments are provided.
 * @see createProcesses(...)
 *
 * @argv[1] start number < (2^64-1) - (2^32-1) * 10
 * @argv[2] stop number < (2^64-1) - (2^32-1) * 10
 * @argv[3] sieve size in KiloBytes, >= 1 && <= 8192
 * @argv[4] flags (settings for primesieve)
 * @argv[5] shared memory name
 */
int main(int argc, char *argv[]) {
  // GUI interface
  if (argc != 6) {
    QApplication a(argc, argv);
    PrimeSieveGUI w;
    w.show();
    return a.exec();
  }
  // PrimeSieveProcess
  else {
    uint64_t start     = utils::strtoull(argv[1]);
    uint64_t stop      = utils::strtoull(argv[2]);
    uint32_t sieveSize = std::strtol(argv[3], NULL, 10);
    uint32_t flags     = std::strtol(argv[4], NULL, 10);

    // open an existing and initialized shared memory
    QSharedMemory sharedMemory(argv[5]);
    if (!sharedMemory.attach()) {
      std::cerr << "Unable to attach shared memory " << argv[5] << std::endl;
      exit(EXIT_FAILURE);
    }
    // map the attached shared memory to the results structure
    PrimeNumberFinder::Results* results =
        static_cast<PrimeNumberFinder::Results*> (sharedMemory.data());
    try {
      // create a new PrimeSieve object that writes its sieving
      // results and status to the shared memory and prints prime
      // numbers or k-tuplets to stdout (if print flags are set)
      PrimeSieve primesieve;
      primesieve.setStartNumber(start);
      primesieve.setStopNumber(stop);
      primesieve.setSieveSize(sieveSize);
      primesieve.setFlags(flags);
      primesieve.setResults(results);
      // start sieving prime numbers and k-tuplets
      primesieve.sieve();
    }
    catch (std::exception& ex) {
      sharedMemory.detach();
      std::cerr << ex.what() << std::endl;
      exit(EXIT_FAILURE);
    }
    sharedMemory.detach();
  }
  // exit success
  return 0;
}
<commit_msg>removed strtoull.h<commit_after>/*
 * main.cpp -- This file is part of primesieve
 *
 * Copyright (C) 2011 Kim Walisch, <kim.walisch@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */

#include <QtGui/QApplication>
#include "PrimeSieveGUI.h"
#include "../src/PrimeSieve.h"

#include <QString>
#include <QSharedMemory>
#include <stdint.h>
#include <stdexcept>
#include <cstdlib>
#include <iostream>

/**
 * The Qt GUI interface is launched if the user launches the
 * application by mouse click, a sieve of Eratosthenes process (no
 * GUI) is launched if exactly 5 arguments are provided.
 * @see createProcesses(...)
 *
 * @argv[1] start number < (2^64-1) - (2^32-1) * 10
 * @argv[2] stop number < (2^64-1) - (2^32-1) * 10
 * @argv[3] sieve size in KiloBytes, >= 1 && <= 8192
 * @argv[4] flags (settings for primesieve)
 * @argv[5] shared memory name
 */
int main(int argc, char *argv[]) {
  // GUI interface
  if (argc != 6) {
    QApplication a(argc, argv);
    PrimeSieveGUI w;
    w.show();
    return a.exec();
  }
  // PrimeSieveProcess
  else {
    QString str;
    str                = argv[1];
    bool ok            = true;
    uint64_t start     = str.toULongLong(&ok, 10);
    str                = argv[2];
    uint64_t stop      = str.toULongLong(&ok, 10);
    uint32_t sieveSize = std::strtol(argv[3], NULL, 10);
    uint32_t flags     = std::strtol(argv[4], NULL, 10);

    // open an existing and initialized shared memory
    QSharedMemory sharedMemory(argv[5]);
    if (!sharedMemory.attach()) {
      std::cerr << "Unable to attach shared memory " << argv[5] << std::endl;
      exit(EXIT_FAILURE);
    }
    // map the attached shared memory to the results structure
    PrimeNumberFinder::Results* results =
        static_cast<PrimeNumberFinder::Results*> (sharedMemory.data());
    try {
      // create a new PrimeSieve object that writes its sieving
      // results and status to the shared memory and prints prime
      // numbers or k-tuplets to stdout (if print flags are set)
      PrimeSieve primesieve;
      primesieve.setStartNumber(start);
      primesieve.setStopNumber(stop);
      primesieve.setSieveSize(sieveSize);
      primesieve.setFlags(flags);
      primesieve.setResults(results);
      // start sieving prime numbers and k-tuplets
      primesieve.sieve();
    }
    catch (std::exception& ex) {
      sharedMemory.detach();
      std::cerr << ex.what() << std::endl;
      exit(EXIT_FAILURE);
    }
    sharedMemory.detach();
  }
  // exit success
  return 0;
}
<|endoftext|>