<commit_before>/*
 * Copyright 2014-2019, CNRS
 * Copyright 2018-2020, INRIA
 */

#ifndef __eigenpy_map_hpp__
#define __eigenpy_map_hpp__

#include "eigenpy/fwd.hpp"
#include "eigenpy/exception.hpp"
#include "eigenpy/stride.hpp"

namespace eigenpy
{
  template<typename MatType, typename InputScalar, typename Stride, bool IsVector = MatType::IsVectorAtCompileTime>
  struct MapNumpyTraits {};

  /* Wrap a numpy::array with an Eigen::Map. No memory copy. */
  template<typename MatType, typename InputScalar, typename Stride = typename StrideType<MatType>::type>
  struct MapNumpy
  {
    typedef MapNumpyTraits<MatType, InputScalar, Stride> Impl;
    typedef typename Impl::EigenMap EigenMap;

    static EigenMap map(PyArrayObject* pyArray);
   };

} // namespace eigenpy

/* --- DETAILS ------------------------------------------------------------------ */
/* --- DETAILS ------------------------------------------------------------------ */
/* --- DETAILS ------------------------------------------------------------------ */

namespace eigenpy
{
  template<typename MatType, typename InputScalar, typename Stride>
  struct MapNumpyTraits<MatType,InputScalar,Stride,false>
  {
    typedef Eigen::Matrix<InputScalar,MatType::RowsAtCompileTime,MatType::ColsAtCompileTime,MatType::Options> EquivalentInputMatrixType;
    typedef Eigen::Map<EquivalentInputMatrixType,EIGENPY_DEFAULT_ALIGNMENT_VALUE,Stride> EigenMap;

    static EigenMap mapImpl(PyArrayObject* pyArray)
    {
      enum {
        OuterStrideAtCompileTime = Stride::OuterStrideAtCompileTime,
        InnerStrideAtCompileTime = Stride::InnerStrideAtCompileTime,
      };

      assert(PyArray_NDIM(pyArray) == 2 ||  PyArray_NDIM(pyArray) == 1);

      const long int itemsize = PyArray_ITEMSIZE(pyArray);
      int inner_stride = -1, outer_stride = -1;
      int rows = -1, cols = -1;
      if(PyArray_NDIM(pyArray) == 2)
      {
        assert( (PyArray_DIMS(pyArray)[0]  < INT_MAX)
             && (PyArray_DIMS(pyArray)[1]  < INT_MAX)
             && (PyArray_STRIDE(pyArray,0) < INT_MAX)
             && (PyArray_STRIDE(pyArray,1) < INT_MAX) );

        rows = (int)PyArray_DIMS(pyArray)[0];
        cols = (int)PyArray_DIMS(pyArray)[1];

        if(EquivalentInputMatrixType::IsRowMajor)
        {
          inner_stride = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize;
          outer_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
        }
        else
        {
          inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
          outer_stride = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize;
        }
      }
      else if(PyArray_NDIM(pyArray) == 1)
      {
        assert( (PyArray_DIMS(pyArray)[0]  < INT_MAX)
             && (PyArray_STRIDE(pyArray,0) < INT_MAX));

        rows = (int)PyArray_DIMS(pyArray)[0];
        cols = 1;

        inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
        outer_stride = 0;
      }

      // Specific care for Eigen::Stride<-1,0>
      if(InnerStrideAtCompileTime==0 && OuterStrideAtCompileTime==Eigen::Dynamic)
      {
        outer_stride = std::max(inner_stride,outer_stride); inner_stride = 0;
      }

      Stride stride(OuterStrideAtCompileTime==Eigen::Dynamic?outer_stride:OuterStrideAtCompileTime,
                    InnerStrideAtCompileTime==Eigen::Dynamic?inner_stride:InnerStrideAtCompileTime);

      if(   (MatType::RowsAtCompileTime != rows)
         && (MatType::RowsAtCompileTime != Eigen::Dynamic) )
      { throw eigenpy::Exception("The number of rows does not fit with the matrix type."); }

      if(   (MatType::ColsAtCompileTime != cols)
         && (MatType::ColsAtCompileTime != Eigen::Dynamic) )
      {  throw eigenpy::Exception("The number of columns does not fit with the matrix type."); }

      InputScalar* pyData = reinterpret_cast<InputScalar*>(PyArray_DATA(pyArray));

      return EigenMap(pyData, rows, cols, stride);
    }
  };

  template<typename MatType, typename InputScalar, typename Stride>
  struct MapNumpyTraits<MatType,InputScalar,Stride,true>
  {
    typedef Eigen::Matrix<InputScalar,MatType::RowsAtCompileTime,MatType::ColsAtCompileTime,MatType::Options> EquivalentInputMatrixType;
    typedef Eigen::Map<EquivalentInputMatrixType,EIGENPY_DEFAULT_ALIGNMENT_VALUE,Stride> EigenMap;

    static EigenMap mapImpl(PyArrayObject* pyArray)
    {
      assert( PyArray_NDIM(pyArray) <= 2 );

      int rowMajor;
      if(  PyArray_NDIM(pyArray)==1 ) rowMajor = 0;
      else if (PyArray_DIMS(pyArray)[0] == 0) rowMajor = 0; // handle zero-size vector
      else if (PyArray_DIMS(pyArray)[1] == 0) rowMajor = 1; // handle zero-size vector
      else rowMajor = (PyArray_DIMS(pyArray)[0]>PyArray_DIMS(pyArray)[1])?0:1;

      assert( (PyArray_DIMS(pyArray)[rowMajor]< INT_MAX)
             && (PyArray_STRIDE(pyArray, rowMajor) ));
      const int R = (int)PyArray_DIMS(pyArray)[rowMajor];
      const long int itemsize = PyArray_ITEMSIZE(pyArray);
      const int stride = (int) PyArray_STRIDE(pyArray, rowMajor) / (int) itemsize;;

      if(   (MatType::MaxSizeAtCompileTime != R)
         && (MatType::MaxSizeAtCompileTime != Eigen::Dynamic) )
      { throw eigenpy::Exception("The number of elements does not fit with the vector type."); }

      InputScalar* pyData = reinterpret_cast<InputScalar*>(PyArray_DATA(pyArray));

      return EigenMap( pyData, R, Stride(stride) );
    }
  };

  template<typename MatType, typename InputScalar, typename Stride>
  typename MapNumpy<MatType,InputScalar,Stride>::EigenMap
  MapNumpy<MatType,InputScalar,Stride>::map(PyArrayObject * pyArray)
  {
    return Impl::mapImpl(pyArray);
  }

} // namespace eigenpy

#endif // define __eigenpy_map_hpp__
<commit_msg>core: add template AlignmentValue to MapNumpy<commit_after>/*
 * Copyright 2014-2019, CNRS
 * Copyright 2018-2020, INRIA
 */

#ifndef __eigenpy_map_hpp__
#define __eigenpy_map_hpp__

#include "eigenpy/fwd.hpp"
#include "eigenpy/exception.hpp"
#include "eigenpy/stride.hpp"

namespace eigenpy
{
  template<typename MatType, typename InputScalar, int AlignmentValue, typename Stride, bool IsVector = MatType::IsVectorAtCompileTime>
  struct MapNumpyTraits {};

  /* Wrap a numpy::array with an Eigen::Map. No memory copy. */
  template<typename MatType, typename InputScalar, int AlignmentValue = EIGENPY_NO_ALIGNMENT_VALUE, typename Stride = typename StrideType<MatType>::type>
  struct MapNumpy
  {
    typedef MapNumpyTraits<MatType, InputScalar, AlignmentValue, Stride> Impl;
    typedef typename Impl::EigenMap EigenMap;

    static EigenMap map(PyArrayObject* pyArray);
   };

} // namespace eigenpy

/* --- DETAILS ------------------------------------------------------------------ */
/* --- DETAILS ------------------------------------------------------------------ */
/* --- DETAILS ------------------------------------------------------------------ */

namespace eigenpy
{
  template<typename MatType, typename InputScalar, int AlignmentValue, typename Stride>
  struct MapNumpyTraits<MatType,InputScalar,AlignmentValue,Stride,false>
  {
    typedef Eigen::Matrix<InputScalar,MatType::RowsAtCompileTime,MatType::ColsAtCompileTime,MatType::Options> EquivalentInputMatrixType;
    typedef Eigen::Map<EquivalentInputMatrixType,AlignmentValue,Stride> EigenMap;

    static EigenMap mapImpl(PyArrayObject* pyArray)
    {
      enum {
        OuterStrideAtCompileTime = Stride::OuterStrideAtCompileTime,
        InnerStrideAtCompileTime = Stride::InnerStrideAtCompileTime,
      };

      assert(PyArray_NDIM(pyArray) == 2 ||  PyArray_NDIM(pyArray) == 1);

      const long int itemsize = PyArray_ITEMSIZE(pyArray);
      int inner_stride = -1, outer_stride = -1;
      int rows = -1, cols = -1;
      if(PyArray_NDIM(pyArray) == 2)
      {
        assert( (PyArray_DIMS(pyArray)[0]  < INT_MAX)
             && (PyArray_DIMS(pyArray)[1]  < INT_MAX)
             && (PyArray_STRIDE(pyArray,0) < INT_MAX)
             && (PyArray_STRIDE(pyArray,1) < INT_MAX) );

        rows = (int)PyArray_DIMS(pyArray)[0];
        cols = (int)PyArray_DIMS(pyArray)[1];

        if(EquivalentInputMatrixType::IsRowMajor)
        {
          inner_stride = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize;
          outer_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
        }
        else
        {
          inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
          outer_stride = (int)PyArray_STRIDE(pyArray, 1) / (int)itemsize;
        }
      }
      else if(PyArray_NDIM(pyArray) == 1)
      {
        assert( (PyArray_DIMS(pyArray)[0]  < INT_MAX)
             && (PyArray_STRIDE(pyArray,0) < INT_MAX));

        rows = (int)PyArray_DIMS(pyArray)[0];
        cols = 1;

        inner_stride = (int)PyArray_STRIDE(pyArray, 0) / (int)itemsize;
        outer_stride = 0;
      }

      // Specific care for Eigen::Stride<-1,0>
      if(InnerStrideAtCompileTime==0 && OuterStrideAtCompileTime==Eigen::Dynamic)
      {
        outer_stride = std::max(inner_stride,outer_stride); inner_stride = 0;
      }

      Stride stride(OuterStrideAtCompileTime==Eigen::Dynamic?outer_stride:OuterStrideAtCompileTime,
                    InnerStrideAtCompileTime==Eigen::Dynamic?inner_stride:InnerStrideAtCompileTime);

      if(   (MatType::RowsAtCompileTime != rows)
         && (MatType::RowsAtCompileTime != Eigen::Dynamic) )
      { throw eigenpy::Exception("The number of rows does not fit with the matrix type."); }

      if(   (MatType::ColsAtCompileTime != cols)
         && (MatType::ColsAtCompileTime != Eigen::Dynamic) )
      {  throw eigenpy::Exception("The number of columns does not fit with the matrix type."); }

      InputScalar* pyData = reinterpret_cast<InputScalar*>(PyArray_DATA(pyArray));

      std::cout << "OuterStrideAtCompileTime: " << OuterStrideAtCompileTime << std::endl;
      std::cout << "InnerStrideAtCompileTime: " << InnerStrideAtCompileTime << std::endl;
      std::cout << "rows: " << rows << std::endl;
      std::cout << "cols: " << cols << std::endl;
      std::cout << "inner_stride: " << inner_stride << std::endl;
      std::cout << "outer_stride: " << outer_stride << std::endl;
      return EigenMap(pyData, rows, cols, stride);
    }
  };

  template<typename MatType, typename InputScalar, int AlignmentValue, typename Stride>
  struct MapNumpyTraits<MatType,InputScalar,AlignmentValue,Stride,true>
  {
    typedef Eigen::Matrix<InputScalar,MatType::RowsAtCompileTime,MatType::ColsAtCompileTime,MatType::Options> EquivalentInputMatrixType;
    typedef Eigen::Map<EquivalentInputMatrixType,AlignmentValue,Stride> EigenMap;

    static EigenMap mapImpl(PyArrayObject* pyArray)
    {
      assert( PyArray_NDIM(pyArray) <= 2 );

      int rowMajor;
      if(  PyArray_NDIM(pyArray)==1 ) rowMajor = 0;
      else if (PyArray_DIMS(pyArray)[0] == 0) rowMajor = 0; // handle zero-size vector
      else if (PyArray_DIMS(pyArray)[1] == 0) rowMajor = 1; // handle zero-size vector
      else rowMajor = (PyArray_DIMS(pyArray)[0]>PyArray_DIMS(pyArray)[1])?0:1;

      assert( (PyArray_DIMS(pyArray)[rowMajor]< INT_MAX)
             && (PyArray_STRIDE(pyArray, rowMajor) ));
      const int R = (int)PyArray_DIMS(pyArray)[rowMajor];
      const long int itemsize = PyArray_ITEMSIZE(pyArray);
      const int stride = (int) PyArray_STRIDE(pyArray, rowMajor) / (int) itemsize;;

      if(   (MatType::MaxSizeAtCompileTime != R)
         && (MatType::MaxSizeAtCompileTime != Eigen::Dynamic) )
      { throw eigenpy::Exception("The number of elements does not fit with the vector type."); }

      InputScalar* pyData = reinterpret_cast<InputScalar*>(PyArray_DATA(pyArray));

      return EigenMap( pyData, R, Stride(stride) );
    }
  };

  template<typename MatType, typename InputScalar, int AlignmentValue, typename Stride>
  typename MapNumpy<MatType,InputScalar,AlignmentValue,Stride>::EigenMap
  MapNumpy<MatType,InputScalar,AlignmentValue,Stride>::map(PyArrayObject * pyArray)
  {
    return Impl::mapImpl(pyArray);
  }

} // namespace eigenpy

#endif // define __eigenpy_map_hpp__
<|endoftext|>


<commit_before>#ifndef BROADCAST_HPP
#define BROADCAST_HPP

#include <utility>
#include <tuple>
#include <type_traits>
#include <stdexcept>
#include <iterator>
#include <array>
#include <algorithm>

#include "xindex.hpp"
#include "utils.hpp"

namespace qs
{

    /*************************
     * Broadcast functions
     *************************/

    template <class S, size_t N>
    S broadcast_dim(std::array<S, N>& dim_list);

    template <class S>
    bool broadcast_shape(const xshape<S>& input, xshape<S>& output);

    template <class S>
    bool check_trivial_broadcast(const xstrides<S>& strides1,
                                 const xstrides<S>& strides2);

    /***************************
     * xstepper
     ***************************/

    namespace detail
    {
        template <class C>
        struct get_storage_iterator_impl
        {
            using type = typename C::storage_iterator;
        };

        template <class C>
        struct get_storage_iterator_impl<const C>
        {
            using type = typename C::const_storage_iterator;
        };
    }

    template <class C>
    using get_storage_iterator = typename detail::get_storage_iterator_impl<C>::type;

    template <class C>
    class xstepper
    {

    public:

        using container_type = C;
        using subiterator_type = get_storage_iterator<C>;
        using value_type = typename subiterator_type::value_type;
        using reference = typename subiterator_type::reference;
        using pointer = typename subiterator_type::pointer;
        using difference_type = typename subiterator_type::difference_type;
        using size_type = typename container_type::size_type;

        xstepper(container_type* c, subiterator_type it, size_type offset);

        reference operator*() const;

        void step(size_type i);
        void reset(size_type i);

        void to_end();

        bool equal(const xstepper& rhs) const;

    private:

        container_type* p_c;
        subiterator_type m_it;
        size_type m_offset;
    };

    template <class C>
    bool operator==(const xstepper<C>& lhs,
                    const xstepper<C>& rhs);

    template <class C>
    bool operator!=(const xstepper<C>& lhs,
                    const xstepper<C>& rhs);

    /**********************
     * xiterator
     **********************/

    template <class It>
    class xiterator
    {

    public:

        using self_type = xiterator<It>;

        using subiterator_type = It;
        using value_type = typename subiterator_type::value_type;
        using reference = typename subiterator_type::reference;
        using pointer = typename subiterator_type::pointer;
        using difference_type = typename subiterator_type::difference_type;
        using size_type = typename subiterator_type::size_type;
        using iterator_category = std::input_iterator_tag;

        using shape_type = xshape<size_type>;

        xiterator(It it, const shape_type& shape);

        self_type& operator++();
        self_type operator++(int);

        reference operator*() const;

        bool equal(const xiterator& rhs) const;

    private:

        subiterator_type m_it;
        shape_type m_shape;
        shape_type m_index;
    };

    template <class It>
    bool operator==(const xiterator<It>& lhs,
                    const xiterator<It>& rhs);

    template <class It>
    bool operator!=(const xiterator<It>& lhs,
                    const xiterator<It>& rhs);

    /****************************************
     * Broadcast functions implementation
     ****************************************/

    template <class S, size_t N>
    inline S broadcast_dim(const std::array<S, N>& dim_list)
    {
        S ndim = std::accumulate(dim_list.begin(), dim_list.end(), S(0),
                [](S res, const S& dim) { return std::max(dim, res); });
        return ndim;
    }

    template <class S>
    inline bool broadcast_shape(const xshape<S>& input, xshape<S>& output)
    {
        size_t size = output.size();
        bool trivial_broadcast = (input.size() == output.size());
        auto output_iter = output.rbegin();
        auto input_rend = input.rend();
        for(auto input_iter = input.rbegin(); input_iter != input_rend;
            ++input_iter, ++output_iter)
        {
            if(*output_iter == 1)
            {
                *output_iter = *input_iter;
            }
            else if((*input_iter != 1) && (*output_iter != *input_iter))
            {
                throw std::runtime_error("broadcast error : incompatible dimension of arrays");
            }
            trivial_broadcast = trivial_broadcast && (*output_iter == *input_iter);
        }
        return trivial_broadcast;
    }

    template <class S>
    inline bool check_trivial_broadcast(const xstrides<S>& strides1,
                                        const xstrides<S>& strides2)
    {
        return strides1 == strides2;
    }

    /*****************************
     * xstepper implementation
     *****************************/

    template <class C>
    inline xstepper<C>::xstepper(container_type* c, subiterator_type it, size_type offset)
        : p_c(c), m_it(it), m_offset(offset)
    {
    }

    template <class C>
    inline auto xstepper<C>::operator*() const -> reference
    {
        return *m_it;
    }

    template <class C>
    inline void xstepper<C>::step(size_type dim)
    {
        if(dim >= m_offset)
            m_it += p_c->strides()[dim - m_offset];
    }

    template <class C>
    inline void xstepper<C>::reset(size_type dim)
    {
        if(dim >= m_offset)
            m_it -= p_c->backstrides()[dim - m_offset];
    }

    template <class C>
    inline void xstepper<C>::to_end()
    {
        m_it = p_c->storage_end();
    }

    template <class C>
    inline bool xstepper<C>::equal(const xstepper& rhs) const
    {
        return p_c == rhs.p_c && m_it == rhs.m_it;
    }

    template <class C>
    inline bool operator==(const xstepper<C>& lhs,
                           const xstepper<C>& rhs)
    {
        return lhs.equal(rhs);
    }

    template <class C>
    inline bool operator!=(const xstepper<C>& lhs,
                           const xstepper<C>& rhs)
    {
        return !(lhs.equal(rhs));
    }

    /*************************************
     * xiterator implementation
     *************************************/

    template <class It>
    inline xiterator<It>::xiterator(It it, const shape_type& shape)
        : m_it(it), m_shape(shape), m_index(shape.size(), size_type(0))
    {
    }

    template <class It>
    inline auto xiterator<It>::operator++() -> self_type&
    {
        for(size_type j = m_index.size(); j != 0; --j)
        {
            size_type i = j-1;
            if(++m_index[i] != m_shape[i])
            {
                m_it.step(i);
                break;
            }
            else if (i == 0)
            {
                m_it.to_end();
            }
            else
            {
                m_index[i] = 0;
                m_it.reset(i);
            }
        }
    }

    template <class It>
    inline auto xiterator<It>::operator++(int) -> self_type
    {
        self_type tmp(*this);
        ++(*this);
        return tmp;
    }

    template <class It>
    inline auto xiterator<It>::operator*() const -> reference
    {
        return *m_it;
    }

    template <class It>
    inline bool xiterator<It>::equal(const xiterator& rhs) const
    {
        return m_it == rhs.m_it && m_shape == rhs.m_shape;
    }

    template <class It>
    inline bool operator==(const xiterator<It>& lhs,
                           const xiterator<It>& rhs)
    {
        return lhs.equal(rhs);
    }

    template <class It>
    inline bool operator!=(const xiterator<It>& lhs,
                           const xiterator<It>& rhs)
    {
        return !(lhs.equal(rhs));
    }

}

#endif

<commit_msg>useless functions removed<commit_after>#ifndef BROADCAST_HPP
#define BROADCAST_HPP

#include <utility>
#include <tuple>
#include <type_traits>
#include <stdexcept>
#include <iterator>
#include <array>
#include <algorithm>

#include "xindex.hpp"
#include "utils.hpp"

namespace qs
{

    /*************************
     * Broadcast functions
     *************************/

    template <class S>
    bool broadcast_shape(const xshape<S>& input, xshape<S>& output);

    /***************************
     * xstepper
     ***************************/

    namespace detail
    {
        template <class C>
        struct get_storage_iterator_impl
        {
            using type = typename C::storage_iterator;
        };

        template <class C>
        struct get_storage_iterator_impl<const C>
        {
            using type = typename C::const_storage_iterator;
        };
    }

    template <class C>
    using get_storage_iterator = typename detail::get_storage_iterator_impl<C>::type;

    template <class C>
    class xstepper
    {

    public:

        using container_type = C;
        using subiterator_type = get_storage_iterator<C>;
        using value_type = typename subiterator_type::value_type;
        using reference = typename subiterator_type::reference;
        using pointer = typename subiterator_type::pointer;
        using difference_type = typename subiterator_type::difference_type;
        using size_type = typename container_type::size_type;

        xstepper(container_type* c, subiterator_type it, size_type offset);

        reference operator*() const;

        void step(size_type i);
        void reset(size_type i);

        void to_end();

        bool equal(const xstepper& rhs) const;

    private:

        container_type* p_c;
        subiterator_type m_it;
        size_type m_offset;
    };

    template <class C>
    bool operator==(const xstepper<C>& lhs,
                    const xstepper<C>& rhs);

    template <class C>
    bool operator!=(const xstepper<C>& lhs,
                    const xstepper<C>& rhs);

    /**********************
     * xiterator
     **********************/

    template <class It>
    class xiterator
    {

    public:

        using self_type = xiterator<It>;

        using subiterator_type = It;
        using value_type = typename subiterator_type::value_type;
        using reference = typename subiterator_type::reference;
        using pointer = typename subiterator_type::pointer;
        using difference_type = typename subiterator_type::difference_type;
        using size_type = typename subiterator_type::size_type;
        using iterator_category = std::input_iterator_tag;

        using shape_type = xshape<size_type>;

        xiterator(It it, const shape_type& shape);

        self_type& operator++();
        self_type operator++(int);

        reference operator*() const;

        bool equal(const xiterator& rhs) const;

    private:

        subiterator_type m_it;
        shape_type m_shape;
        shape_type m_index;
    };

    template <class It>
    bool operator==(const xiterator<It>& lhs,
                    const xiterator<It>& rhs);

    template <class It>
    bool operator!=(const xiterator<It>& lhs,
                    const xiterator<It>& rhs);

    /****************************************
     * Broadcast functions implementation
     ****************************************/

    template <class S>
    inline bool broadcast_shape(const xshape<S>& input, xshape<S>& output)
    {
        size_t size = output.size();
        bool trivial_broadcast = (input.size() == output.size());
        auto output_iter = output.rbegin();
        auto input_rend = input.rend();
        for(auto input_iter = input.rbegin(); input_iter != input_rend;
            ++input_iter, ++output_iter)
        {
            if(*output_iter == 1)
            {
                *output_iter = *input_iter;
            }
            else if((*input_iter != 1) && (*output_iter != *input_iter))
            {
                throw std::runtime_error("broadcast error : incompatible dimension of arrays");
            }
            trivial_broadcast = trivial_broadcast && (*output_iter == *input_iter);
        }
        return trivial_broadcast;
    }

    /*****************************
     * xstepper implementation
     *****************************/

    template <class C>
    inline xstepper<C>::xstepper(container_type* c, subiterator_type it, size_type offset)
        : p_c(c), m_it(it), m_offset(offset)
    {
    }

    template <class C>
    inline auto xstepper<C>::operator*() const -> reference
    {
        return *m_it;
    }

    template <class C>
    inline void xstepper<C>::step(size_type dim)
    {
        if(dim >= m_offset)
            m_it += p_c->strides()[dim - m_offset];
    }

    template <class C>
    inline void xstepper<C>::reset(size_type dim)
    {
        if(dim >= m_offset)
            m_it -= p_c->backstrides()[dim - m_offset];
    }

    template <class C>
    inline void xstepper<C>::to_end()
    {
        m_it = p_c->storage_end();
    }

    template <class C>
    inline bool xstepper<C>::equal(const xstepper& rhs) const
    {
        return p_c == rhs.p_c && m_it == rhs.m_it;
    }

    template <class C>
    inline bool operator==(const xstepper<C>& lhs,
                           const xstepper<C>& rhs)
    {
        return lhs.equal(rhs);
    }

    template <class C>
    inline bool operator!=(const xstepper<C>& lhs,
                           const xstepper<C>& rhs)
    {
        return !(lhs.equal(rhs));
    }

    /*************************************
     * xiterator implementation
     *************************************/

    template <class It>
    inline xiterator<It>::xiterator(It it, const shape_type& shape)
        : m_it(it), m_shape(shape), m_index(shape.size(), size_type(0))
    {
    }

    template <class It>
    inline auto xiterator<It>::operator++() -> self_type&
    {
        for(size_type j = m_index.size(); j != 0; --j)
        {
            size_type i = j-1;
            if(++m_index[i] != m_shape[i])
            {
                m_it.step(i);
                break;
            }
            else if (i == 0)
            {
                m_it.to_end();
            }
            else
            {
                m_index[i] = 0;
                m_it.reset(i);
            }
        }
    }

    template <class It>
    inline auto xiterator<It>::operator++(int) -> self_type
    {
        self_type tmp(*this);
        ++(*this);
        return tmp;
    }

    template <class It>
    inline auto xiterator<It>::operator*() const -> reference
    {
        return *m_it;
    }

    template <class It>
    inline bool xiterator<It>::equal(const xiterator& rhs) const
    {
        return m_it == rhs.m_it && m_shape == rhs.m_shape;
    }

    template <class It>
    inline bool operator==(const xiterator<It>& lhs,
                           const xiterator<It>& rhs)
    {
        return lhs.equal(rhs);
    }

    template <class It>
    inline bool operator!=(const xiterator<It>& lhs,
                           const xiterator<It>& rhs)
    {
        return !(lhs.equal(rhs));
    }

}

#endif

<|endoftext|>


<commit_before>/*
 *     Copyright 2019 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 "clustertest.h"

#include "bucket.h"
#include "cluster.h"

#include <protocol/connection/client_connection.h>
#include <protocol/connection/client_mcbp_commands.h>
#include <protocol/connection/frameinfo.h>
#include <condition_variable>
#include <cstdlib>
#include <string>

/**
 * Get a frame info vector with a durability entry with Majority
 */
static FrameInfoVector GetMajorityDurabilityFrameInfoVector() {
    FrameInfoVector ret;
    ret.emplace_back(std::make_unique<DurabilityFrameInfo>(
            cb::durability::Level::Majority));
    return ret;
}

class DurabilityTest : public cb::test::ClusterTest {
protected:
    static void mutate(MemcachedConnection& conn,
                       std::string id,
                       MutationType type) {
        MutationInfo old{};
        if (type != MutationType::Add) {
            old = conn.store(id, Vbid{0}, "", cb::mcbp::Datatype::Raw);
        }

        Document doc{};
        doc.value = "body";
        doc.info.id = std::move(id);
        doc.info.datatype = cb::mcbp::Datatype::Raw;
        const auto info = conn.mutate(
                doc, Vbid{0}, type, GetMajorityDurabilityFrameInfoVector);
        EXPECT_NE(0, info.cas);
        EXPECT_NE(old.cas, info.cas);
    }

    static void subdoc(MemcachedConnection& conn,
                       std::string id,
                       cb::mcbp::ClientOpcode opcode) {
        BinprotSubdocCommand cmd(opcode);
        cmd.setKey(std::move(id));
        cmd.setPath("foo");
        if (opcode != cb::mcbp::ClientOpcode::SubdocDelete) {
            cmd.setValue("1");
            if (opcode == cb::mcbp::ClientOpcode::SubdocArrayInsert) {
                cmd.setPath("foo.[0]");
            } else if (opcode != cb::mcbp::ClientOpcode::SubdocReplace) {
                cmd.addPathFlags(SUBDOC_FLAG_MKDIR_P);
                cmd.addDocFlags(mcbp::subdoc::doc_flag::Mkdoc);
            }
        }
        cmd.addFrameInfo(DurabilityFrameInfo{cb::durability::Level::Majority});
        auto rsp = conn.execute(cmd);
        EXPECT_TRUE(rsp.isSuccess())
                << "Status: " << to_string(rsp.getStatus()) << std::endl
                << "Value: " << rsp.getDataString();
        EXPECT_NE(0, rsp.getCas());
    }

    static std::unique_ptr<MemcachedConnection> getConnection() {
        auto bucket = cluster->getBucket("default");
        auto conn = bucket->getConnection(Vbid(0));
        conn->authenticate("@admin", "password", "PLAIN");
        conn->selectBucket(bucket->getName());
        return conn;
    }
};

TEST_F(DurabilityTest, Set) {
    mutate(*getConnection(), "Set", MutationType::Set);
}

TEST_F(DurabilityTest, Add) {
    mutate(*getConnection(), "Add", MutationType::Add);
}

TEST_F(DurabilityTest, Replace) {
    mutate(*getConnection(), "Replace", MutationType::Replace);
}

TEST_F(DurabilityTest, Append) {
    mutate(*getConnection(), "Append", MutationType::Append);
}

TEST_F(DurabilityTest, Prepend) {
    mutate(*getConnection(), "Prepend", MutationType::Prepend);
}

TEST_F(DurabilityTest, Delete) {
    auto conn = getConnection();
    const auto old =
            conn->store("Delete", Vbid{0}, "", cb::mcbp::Datatype::Raw);
    auto info = getConnection()->remove(
            "Delete", Vbid{0}, 0, GetMajorityDurabilityFrameInfoVector);
    EXPECT_NE(0, info.cas);
    EXPECT_NE(old.cas, info.cas);
}

TEST_F(DurabilityTest, Increment) {
    auto conn = getConnection();
    for (uint64_t ii = 0; ii < 10ULL; ++ii)
        EXPECT_EQ(ii,
                  conn->increment("Increment",
                                  1,
                                  0,
                                  0,
                                  nullptr,
                                  GetMajorityDurabilityFrameInfoVector));
}

TEST_F(DurabilityTest, Decrement) {
    auto conn = getConnection();
    for (uint64_t ii = 10; ii > 0ULL; --ii)
        EXPECT_EQ(ii,
                  conn->decrement("Decrement",
                                  1,
                                  10,
                                  0,
                                  nullptr,
                                  GetMajorityDurabilityFrameInfoVector));
}

// MB-35548 blocks this
TEST_F(DurabilityTest, DISABLED_Touch) {
    auto conn = getConnection();
    const auto info =
            conn->store("Touch", Vbid{0}, "", cb::mcbp::Datatype::Raw);

    BinprotTouchCommand cmd;
    cmd.setKey("Touch");
    cmd.setExpirytime(0x32);
    cmd.addFrameInfo(DurabilityFrameInfo{cb::durability::Level::Majority});

    auto rsp = conn->execute(cmd);
    EXPECT_TRUE(rsp.isSuccess())
            << "Status: " << to_string(rsp.getStatus()) << std::endl
            << "Value: " << rsp.getDataString();
    EXPECT_NE(info.cas, rsp.getCas());
    EXPECT_NE(0, rsp.getCas());
}

// MB-35548 blocks this
TEST_F(DurabilityTest, DISABLED_Gat) {
    auto conn = getConnection();
    const auto info =
            conn->store("GetAndTouch", Vbid{0}, "foo", cb::mcbp::Datatype::Raw);

    BinprotGetAndTouchCommand cmd;
    cmd.setKey("GetAndTouch");
    cmd.setExpirytime(0x32);
    cmd.addFrameInfo(DurabilityFrameInfo{cb::durability::Level::Majority});

    auto rsp = conn->execute(cmd);
    EXPECT_TRUE(rsp.isSuccess())
            << "Status: " << to_string(rsp.getStatus()) << std::endl
            << "Value: " << rsp.getDataString();
    EXPECT_NE(info.cas, rsp.getCas());
    EXPECT_NE(0, rsp.getCas());
    EXPECT_EQ("foo", rsp.getDataString());
}

TEST_F(DurabilityTest, SubdocDictAdd) {
    subdoc(*getConnection(),
           "SubdocDictAdd",
           cb::mcbp::ClientOpcode::SubdocDictAdd);
}

TEST_F(DurabilityTest, SubdocDictUpsert) {
    subdoc(*getConnection(),
           "SubdocDictUpsert",
           cb::mcbp::ClientOpcode::SubdocDictUpsert);
}

TEST_F(DurabilityTest, SubdocDelete) {
    auto conn = getConnection();
    conn->store("SubdocDelete",
                Vbid{0},
                R"({"foo":"bar"})",
                cb::mcbp::Datatype::JSON);
    subdoc(*conn, "SubdocDelete", cb::mcbp::ClientOpcode::SubdocDelete);
}

TEST_F(DurabilityTest, SubdocReplace) {
    auto conn = getConnection();
    conn->store("SubdocReplace",
                Vbid{0},
                R"({"foo":"bar"})",
                cb::mcbp::Datatype::JSON);
    subdoc(*conn, "SubdocReplace", cb::mcbp::ClientOpcode::SubdocReplace);
}

TEST_F(DurabilityTest, SubdocArrayPushLast) {
    subdoc(*getConnection(),
           "SubdocArrayPushLast",
           cb::mcbp::ClientOpcode::SubdocArrayPushLast);
}
TEST_F(DurabilityTest, SubdocArrayPushFirst) {
    subdoc(*getConnection(),
           "SubdocArrayPushFirst",
           cb::mcbp::ClientOpcode::SubdocArrayPushFirst);
}
TEST_F(DurabilityTest, SubdocArrayInsert) {
    auto conn = getConnection();
    conn->store("SubdocArrayInsert",
                Vbid{0},
                R"({"foo":[]})",
                cb::mcbp::Datatype::JSON);
    subdoc(*conn,
           "SubdocArrayInsert",
           cb::mcbp::ClientOpcode::SubdocArrayInsert);
}
TEST_F(DurabilityTest, SubdocArrayAddUnique) {
    subdoc(*getConnection(),
           "SubdocArrayAddUnique",
           cb::mcbp::ClientOpcode::SubdocArrayAddUnique);
}

TEST_F(DurabilityTest, SubdocCounter) {
    subdoc(*getConnection(),
           "SubdocCounter",
           cb::mcbp::ClientOpcode::SubdocCounter);
}

TEST_F(DurabilityTest, SubdocMultiMutation) {
    BinprotSubdocMultiMutationCommand cmd;
    cmd.setKey("SubdocMultiMutation");
    cmd.addMutation(cb::mcbp::ClientOpcode::SubdocDictAdd,
                    SUBDOC_FLAG_MKDIR_P,
                    "foo",
                    R"("value")");
    cmd.addDocFlag(mcbp::subdoc::doc_flag::Mkdoc);
    cmd.addFrameInfo(DurabilityFrameInfo{cb::durability::Level::Majority});
    auto rsp = getConnection()->execute(cmd);
    EXPECT_TRUE(rsp.isSuccess())
            << "Status: " << to_string(rsp.getStatus()) << std::endl
            << "Value: " << rsp.getDataString();
    EXPECT_NE(0, rsp.getCas());
}

/**
 * MB-34780 - Bucket delete fails if we've got pending sync writes
 *
 * As part of bucket deletion all of the DCP streams get torn down so
 * a sync write will _never_ complete. This caused bucket deletion to
 * block as the cookie was in an ewouldblock state
 */
TEST_F(DurabilityTest, MB34780) {
    cluster->deleteBucket("default");
    std::mutex mutex;
    std::condition_variable cond;
    bool prepare_seen = false;

    auto bucket = cluster->createBucket(
            "default",
            {{"replicas", 2}, {"max_vbuckets", 8}},
            [&mutex, &cond, &prepare_seen](const std::string& source,
                                           const std::string& destination,
                                           std::vector<uint8_t>& packet) {
                if (prepare_seen) {
                    // Swallow everything..
                    packet.clear();
                    return;
                }

                const auto* h = reinterpret_cast<const cb::mcbp::Header*>(
                        packet.data());
                if (h->isRequest()) {
                    const auto& req = h->getRequest();
                    if (req.getClientOpcode() ==
                        cb::mcbp::ClientOpcode::DcpPrepare) {
                        std::lock_guard<std::mutex> guard(mutex);
                        prepare_seen = true;
                        cond.notify_all();
                        packet.clear();
                    }
                }
            });
    ASSERT_TRUE(bucket) << "Failed to create bucket default";

    auto conn = bucket->getConnection(Vbid(0));
    conn->authenticate("@admin", "password", "PLAIN");
    conn->selectBucket(bucket->getName());

    BinprotMutationCommand command;
    command.setKey("MB34780");
    command.setMutationType(MutationType::Set);
    DurabilityFrameInfo frameInfo(cb::durability::Level::Majority);
    command.addFrameInfo(frameInfo);
    conn->sendCommand(command);

    std::unique_lock<std::mutex> lock(mutex);
    cond.wait(lock, [&prepare_seen]() { return prepare_seen; });

    // At this point we've sent the prepare, it is registered in the
    // durability manager.. We should be able to delete the bucket
    cluster->deleteBucket("default");
}
<commit_msg>Remove disabled (and obsolete) tests<commit_after>/*
 *     Copyright 2019 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 "clustertest.h"

#include "bucket.h"
#include "cluster.h"

#include <protocol/connection/client_connection.h>
#include <protocol/connection/client_mcbp_commands.h>
#include <protocol/connection/frameinfo.h>
#include <condition_variable>
#include <cstdlib>
#include <string>

/**
 * Get a frame info vector with a durability entry with Majority
 */
static FrameInfoVector GetMajorityDurabilityFrameInfoVector() {
    FrameInfoVector ret;
    ret.emplace_back(std::make_unique<DurabilityFrameInfo>(
            cb::durability::Level::Majority));
    return ret;
}

class DurabilityTest : public cb::test::ClusterTest {
protected:
    static void mutate(MemcachedConnection& conn,
                       std::string id,
                       MutationType type) {
        MutationInfo old{};
        if (type != MutationType::Add) {
            old = conn.store(id, Vbid{0}, "", cb::mcbp::Datatype::Raw);
        }

        Document doc{};
        doc.value = "body";
        doc.info.id = std::move(id);
        doc.info.datatype = cb::mcbp::Datatype::Raw;
        const auto info = conn.mutate(
                doc, Vbid{0}, type, GetMajorityDurabilityFrameInfoVector);
        EXPECT_NE(0, info.cas);
        EXPECT_NE(old.cas, info.cas);
    }

    static void subdoc(MemcachedConnection& conn,
                       std::string id,
                       cb::mcbp::ClientOpcode opcode) {
        BinprotSubdocCommand cmd(opcode);
        cmd.setKey(std::move(id));
        cmd.setPath("foo");
        if (opcode != cb::mcbp::ClientOpcode::SubdocDelete) {
            cmd.setValue("1");
            if (opcode == cb::mcbp::ClientOpcode::SubdocArrayInsert) {
                cmd.setPath("foo.[0]");
            } else if (opcode != cb::mcbp::ClientOpcode::SubdocReplace) {
                cmd.addPathFlags(SUBDOC_FLAG_MKDIR_P);
                cmd.addDocFlags(mcbp::subdoc::doc_flag::Mkdoc);
            }
        }
        cmd.addFrameInfo(DurabilityFrameInfo{cb::durability::Level::Majority});
        auto rsp = conn.execute(cmd);
        EXPECT_TRUE(rsp.isSuccess())
                << "Status: " << to_string(rsp.getStatus()) << std::endl
                << "Value: " << rsp.getDataString();
        EXPECT_NE(0, rsp.getCas());
    }

    static std::unique_ptr<MemcachedConnection> getConnection() {
        auto bucket = cluster->getBucket("default");
        auto conn = bucket->getConnection(Vbid(0));
        conn->authenticate("@admin", "password", "PLAIN");
        conn->selectBucket(bucket->getName());
        return conn;
    }
};

TEST_F(DurabilityTest, Set) {
    mutate(*getConnection(), "Set", MutationType::Set);
}

TEST_F(DurabilityTest, Add) {
    mutate(*getConnection(), "Add", MutationType::Add);
}

TEST_F(DurabilityTest, Replace) {
    mutate(*getConnection(), "Replace", MutationType::Replace);
}

TEST_F(DurabilityTest, Append) {
    mutate(*getConnection(), "Append", MutationType::Append);
}

TEST_F(DurabilityTest, Prepend) {
    mutate(*getConnection(), "Prepend", MutationType::Prepend);
}

TEST_F(DurabilityTest, Delete) {
    auto conn = getConnection();
    const auto old =
            conn->store("Delete", Vbid{0}, "", cb::mcbp::Datatype::Raw);
    auto info = getConnection()->remove(
            "Delete", Vbid{0}, 0, GetMajorityDurabilityFrameInfoVector);
    EXPECT_NE(0, info.cas);
    EXPECT_NE(old.cas, info.cas);
}

TEST_F(DurabilityTest, Increment) {
    auto conn = getConnection();
    for (uint64_t ii = 0; ii < 10ULL; ++ii)
        EXPECT_EQ(ii,
                  conn->increment("Increment",
                                  1,
                                  0,
                                  0,
                                  nullptr,
                                  GetMajorityDurabilityFrameInfoVector));
}

TEST_F(DurabilityTest, Decrement) {
    auto conn = getConnection();
    for (uint64_t ii = 10; ii > 0ULL; --ii)
        EXPECT_EQ(ii,
                  conn->decrement("Decrement",
                                  1,
                                  10,
                                  0,
                                  nullptr,
                                  GetMajorityDurabilityFrameInfoVector));
}

TEST_F(DurabilityTest, SubdocDictAdd) {
    subdoc(*getConnection(),
           "SubdocDictAdd",
           cb::mcbp::ClientOpcode::SubdocDictAdd);
}

TEST_F(DurabilityTest, SubdocDictUpsert) {
    subdoc(*getConnection(),
           "SubdocDictUpsert",
           cb::mcbp::ClientOpcode::SubdocDictUpsert);
}

TEST_F(DurabilityTest, SubdocDelete) {
    auto conn = getConnection();
    conn->store("SubdocDelete",
                Vbid{0},
                R"({"foo":"bar"})",
                cb::mcbp::Datatype::JSON);
    subdoc(*conn, "SubdocDelete", cb::mcbp::ClientOpcode::SubdocDelete);
}

TEST_F(DurabilityTest, SubdocReplace) {
    auto conn = getConnection();
    conn->store("SubdocReplace",
                Vbid{0},
                R"({"foo":"bar"})",
                cb::mcbp::Datatype::JSON);
    subdoc(*conn, "SubdocReplace", cb::mcbp::ClientOpcode::SubdocReplace);
}

TEST_F(DurabilityTest, SubdocArrayPushLast) {
    subdoc(*getConnection(),
           "SubdocArrayPushLast",
           cb::mcbp::ClientOpcode::SubdocArrayPushLast);
}
TEST_F(DurabilityTest, SubdocArrayPushFirst) {
    subdoc(*getConnection(),
           "SubdocArrayPushFirst",
           cb::mcbp::ClientOpcode::SubdocArrayPushFirst);
}
TEST_F(DurabilityTest, SubdocArrayInsert) {
    auto conn = getConnection();
    conn->store("SubdocArrayInsert",
                Vbid{0},
                R"({"foo":[]})",
                cb::mcbp::Datatype::JSON);
    subdoc(*conn,
           "SubdocArrayInsert",
           cb::mcbp::ClientOpcode::SubdocArrayInsert);
}
TEST_F(DurabilityTest, SubdocArrayAddUnique) {
    subdoc(*getConnection(),
           "SubdocArrayAddUnique",
           cb::mcbp::ClientOpcode::SubdocArrayAddUnique);
}

TEST_F(DurabilityTest, SubdocCounter) {
    subdoc(*getConnection(),
           "SubdocCounter",
           cb::mcbp::ClientOpcode::SubdocCounter);
}

TEST_F(DurabilityTest, SubdocMultiMutation) {
    BinprotSubdocMultiMutationCommand cmd;
    cmd.setKey("SubdocMultiMutation");
    cmd.addMutation(cb::mcbp::ClientOpcode::SubdocDictAdd,
                    SUBDOC_FLAG_MKDIR_P,
                    "foo",
                    R"("value")");
    cmd.addDocFlag(mcbp::subdoc::doc_flag::Mkdoc);
    cmd.addFrameInfo(DurabilityFrameInfo{cb::durability::Level::Majority});
    auto rsp = getConnection()->execute(cmd);
    EXPECT_TRUE(rsp.isSuccess())
            << "Status: " << to_string(rsp.getStatus()) << std::endl
            << "Value: " << rsp.getDataString();
    EXPECT_NE(0, rsp.getCas());
}

/**
 * MB-34780 - Bucket delete fails if we've got pending sync writes
 *
 * As part of bucket deletion all of the DCP streams get torn down so
 * a sync write will _never_ complete. This caused bucket deletion to
 * block as the cookie was in an ewouldblock state
 */
TEST_F(DurabilityTest, MB34780) {
    cluster->deleteBucket("default");
    std::mutex mutex;
    std::condition_variable cond;
    bool prepare_seen = false;

    auto bucket = cluster->createBucket(
            "default",
            {{"replicas", 2}, {"max_vbuckets", 8}},
            [&mutex, &cond, &prepare_seen](const std::string& source,
                                           const std::string& destination,
                                           std::vector<uint8_t>& packet) {
                if (prepare_seen) {
                    // Swallow everything..
                    packet.clear();
                    return;
                }

                const auto* h = reinterpret_cast<const cb::mcbp::Header*>(
                        packet.data());
                if (h->isRequest()) {
                    const auto& req = h->getRequest();
                    if (req.getClientOpcode() ==
                        cb::mcbp::ClientOpcode::DcpPrepare) {
                        std::lock_guard<std::mutex> guard(mutex);
                        prepare_seen = true;
                        cond.notify_all();
                        packet.clear();
                    }
                }
            });
    ASSERT_TRUE(bucket) << "Failed to create bucket default";

    auto conn = bucket->getConnection(Vbid(0));
    conn->authenticate("@admin", "password", "PLAIN");
    conn->selectBucket(bucket->getName());

    BinprotMutationCommand command;
    command.setKey("MB34780");
    command.setMutationType(MutationType::Set);
    DurabilityFrameInfo frameInfo(cb::durability::Level::Majority);
    command.addFrameInfo(frameInfo);
    conn->sendCommand(command);

    std::unique_lock<std::mutex> lock(mutex);
    cond.wait(lock, [&prepare_seen]() { return prepare_seen; });

    // At this point we've sent the prepare, it is registered in the
    // durability manager.. We should be able to delete the bucket
    cluster->deleteBucket("default");
}
<|endoftext|>


<commit_before>// $Id: GEOSNearestPointsTest.cpp 2424 2009-04-29 23:52:36Z mloskot $
//
// Test Suite for C-API GEOSNearestPoints

#include <tut/tut.hpp>
// geos
#include <geos_c.h>
// std
#include <cstdarg>
#include <cstdio>
#include <cstdlib>

namespace tut {
//
// Test Group
//

// Common data used in test cases.
struct test_capigeosnearestpoints_data {
    GEOSGeometry* geom1_;
    GEOSGeometry* geom2_;

    static void
    notice(const char* fmt, ...)
    {
        std::fprintf(stdout, "NOTICE: ");

        va_list ap;
        va_start(ap, fmt);
        std::vfprintf(stdout, fmt, ap);
        va_end(ap);

        std::fprintf(stdout, "\n");
    }

    test_capigeosnearestpoints_data()
        : geom1_(nullptr), geom2_(nullptr)
    {
        initGEOS(notice, notice);
    }

    ~test_capigeosnearestpoints_data()
    {
        GEOSGeom_destroy(geom1_);
        GEOSGeom_destroy(geom2_);
        geom1_ = nullptr;
        geom2_ = nullptr;
        finishGEOS();
    }

};

typedef test_group<test_capigeosnearestpoints_data> group;
typedef group::object object;

group test_capigeosnearestpoints_group("capi::GEOSNearestPoints");

//
// Test Cases
//

template<>
template<>
void object::test<1>
()
{
    geom1_ = GEOSGeomFromWKT("POLYGON EMPTY");
    geom2_ = GEOSGeomFromWKT("POLYGON EMPTY");

    ensure(nullptr != geom1_);
    ensure(nullptr != geom2_);

    GEOSCoordSequence* coords_;
    coords_ = GEOSNearestPoints(geom1_, geom2_);

    ensure(nullptr == coords_);
}

template<>
template<>
void object::test<2>
()
{
    geom1_ = GEOSGeomFromWKT("POLYGON((1 1,1 5,5 5,5 1,1 1))");
    // geom2_ = GEOSGeomFromWKT("POINT(8 8)");
    geom2_ = GEOSGeomFromWKT("POLYGON((8 8, 9 9, 9 10, 8 8))");

    ensure(nullptr != geom1_);
    ensure(nullptr != geom2_);

    GEOSCoordSequence* coords_;
    coords_ = GEOSNearestPoints(geom1_, geom2_);

    ensure(nullptr != coords_);

    unsigned int size;
    GEOSCoordSeq_getSize(coords_, &size);
    ensure(2 == size);

    double  x1, x2, y1, y2;

    /* Point in geom1_
     */
    GEOSCoordSeq_getOrdinate(coords_, 0, 0, &x1);
    GEOSCoordSeq_getOrdinate(coords_, 0, 1, &y1);

    /* Point in geom2_
     */
    GEOSCoordSeq_getOrdinate(coords_, 1, 0, &x2);
    GEOSCoordSeq_getOrdinate(coords_, 1, 1, &y2);

    ensure(5 == x1);
    ensure(5 == y1);
    ensure(8 == x2);
    ensure(8 == y2);

    GEOSCoordSeq_destroy(coords_);
}

} // namespace tut

<commit_msg>Add GEOSNearestPoints test for point contained in polygon<commit_after>// $Id: GEOSNearestPointsTest.cpp 2424 2009-04-29 23:52:36Z mloskot $
//
// Test Suite for C-API GEOSNearestPoints

#include <tut/tut.hpp>
// geos
#include <geos_c.h>
// std
#include <cstdarg>
#include <cstdio>
#include <cstdlib>

namespace tut {
//
// Test Group
//

// Common data used in test cases.
struct test_capigeosnearestpoints_data {
    GEOSGeometry* geom1_;
    GEOSGeometry* geom2_;

    static void
    notice(const char* fmt, ...)
    {
        std::fprintf(stdout, "NOTICE: ");

        va_list ap;
        va_start(ap, fmt);
        std::vfprintf(stdout, fmt, ap);
        va_end(ap);

        std::fprintf(stdout, "\n");
    }

    test_capigeosnearestpoints_data()
        : geom1_(nullptr), geom2_(nullptr)
    {
        initGEOS(notice, notice);
    }

    ~test_capigeosnearestpoints_data()
    {
        GEOSGeom_destroy(geom1_);
        GEOSGeom_destroy(geom2_);
        geom1_ = nullptr;
        geom2_ = nullptr;
        finishGEOS();
    }

};

typedef test_group<test_capigeosnearestpoints_data> group;
typedef group::object object;

group test_capigeosnearestpoints_group("capi::GEOSNearestPoints");

//
// Test Cases
//

template<>
template<>
void object::test<1>
()
{
    geom1_ = GEOSGeomFromWKT("POLYGON EMPTY");
    geom2_ = GEOSGeomFromWKT("POLYGON EMPTY");

    ensure(nullptr != geom1_);
    ensure(nullptr != geom2_);

    GEOSCoordSequence* coords_;
    coords_ = GEOSNearestPoints(geom1_, geom2_);

    ensure(nullptr == coords_);
}

template<>
template<>
void object::test<2>
()
{
    geom1_ = GEOSGeomFromWKT("POLYGON((1 1,1 5,5 5,5 1,1 1))");
    // geom2_ = GEOSGeomFromWKT("POINT(8 8)");
    geom2_ = GEOSGeomFromWKT("POLYGON((8 8, 9 9, 9 10, 8 8))");

    ensure(nullptr != geom1_);
    ensure(nullptr != geom2_);

    GEOSCoordSequence* coords_;
    coords_ = GEOSNearestPoints(geom1_, geom2_);

    ensure(nullptr != coords_);

    unsigned int size;
    GEOSCoordSeq_getSize(coords_, &size);
    ensure(2 == size);

    double  x1, x2, y1, y2;

    /* Point in geom1_
     */
    GEOSCoordSeq_getOrdinate(coords_, 0, 0, &x1);
    GEOSCoordSeq_getOrdinate(coords_, 0, 1, &y1);

    /* Point in geom2_
     */
    GEOSCoordSeq_getOrdinate(coords_, 1, 0, &x2);
    GEOSCoordSeq_getOrdinate(coords_, 1, 1, &y2);

    ensure(5 == x1);
    ensure(5 == y1);
    ensure(8 == x2);
    ensure(8 == y2);

    GEOSCoordSeq_destroy(coords_);
}

template<>
template<>
void object::test<3>
()
{
    geom1_ = GEOSGeomFromWKT("POLYGON((1 1,1 5,5 5,5 1,1 1))");
    geom2_ = GEOSGeomFromWKT("POINT(2 2)");

    ensure(nullptr != geom1_);
    ensure(nullptr != geom2_);

    GEOSCoordSequence* coords_;
    coords_ = GEOSNearestPoints(geom1_, geom2_);

    ensure(nullptr != coords_);

    unsigned int size;
    GEOSCoordSeq_getSize(coords_, &size);
    ensure(2 == size);

    double  x1, x2, y1, y2;

    /* Point in geom1_
     */
    GEOSCoordSeq_getOrdinate(coords_, 0, 0, &x1);
    GEOSCoordSeq_getOrdinate(coords_, 0, 1, &y1);

    /* Point in geom2_
     */
    GEOSCoordSeq_getOrdinate(coords_, 1, 0, &x2);
    GEOSCoordSeq_getOrdinate(coords_, 1, 1, &y2);

    ensure(2 == x1);
    ensure(2 == y1);
    ensure(2 == x2);
    ensure(2 == y2);

    GEOSCoordSeq_destroy(coords_);
}

} // namespace tut

<|endoftext|>


<commit_before>// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#include "unit_tests.hpp"
#include "mfem.hpp"

using namespace mfem;

#ifdef MFEM_USE_SUITESPARSE
#define DIRECT_SOLVE_SERIAL
#endif
#ifdef MFEM_USE_MUMPS
#define DIRECT_SOLVE_PARALLEL
#endif
#ifdef MFEM_USE_SUPERLU
#define DIRECT_SOLVE_PARALLEL
#endif

#if defined(DIRECT_SOLVE_SERIAL) || defined(DIRECT_SOLVE_PARALLEL)

int dim;
double uexact(const Vector& x)
{
   double u;
   switch (dim)
   {
      case 1:
         u  = 3.0 + 2.0 * x(0) - 0.5 * x(0) * x(0);
         break;
      case 2:
         u = 1.0 + 0.2 * x(0) - 0.9 * x(0) * x(1) + x(1) * x(1) * x(0);
         break;
      default:
         u = x(2) * x(2) * x(2) - 5.0 * x(0) * x(0) * x(1) * x(2);
         break;
   }
   return u;
}

void gradexact(const Vector& x, Vector & grad)
{
   grad.SetSize(dim);
   switch (dim)
   {
      case 1:
         grad[0] = 2.0 - x(0);
         break;
      case 2:
         grad[0] = 0.2 - 0.9 * x(1) + x(1) * x (1);
         grad[1] = - 0.9 * x(0) + 2.0 * x(0) * x(1);
         break;
      default:
         grad[0] = -10.0 * x(0) * x(1) * x(2);
         grad[1] = - 5.0 * x(0) * x(0) * x(2);
         grad[2] = 3.0 * x(2) * x(2) - 5.0 * x(0) * x(0) * x(1);
         break;
   }
}

double d2uexact(const Vector& x) // retuns \Delta u
{
   double d2u;
   switch (dim)
   {
      case 1:
         d2u  = -1.0;
         break;
      case 2:
         d2u = 2.0 * x(0);
         break;
      default:
         d2u = -10.0 * x(1) * x(2) + 6.0 * x(2);
         break;
   }
   return d2u;
}

double fexact(const Vector& x) // retuns -\Delta u
{
   double d2u = d2uexact(x);
   return -d2u;
}

#endif

#ifdef DIRECT_SOLVE_SERIAL

TEST_CASE("direct-serial","[CUDA]")
{
   const int ne = 2;
   for (dim = 1; dim < 4; ++dim)
   {
      for (int order = 1; order < 4; ++order)
      {
         Mesh* mesh;
         if (dim == 1)
         {
            mesh = new Mesh(ne,  1.0);
         }
         else if (dim == 2)
         {
            mesh = new Mesh(ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
         }
         else
         {
            mesh = new Mesh(ne, ne, ne, Element::HEXAHEDRON, 1, 1.0, 1.0,
                            1.0);
         }

         FiniteElementCollection* fec = new H1_FECollection(order, dim);
         FiniteElementSpace fespace(mesh, fec);
         Array<int> ess_tdof_list;
         Array<int> ess_bdr(mesh->bdr_attributes.Max());
         ess_bdr = 1;
         fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);

         FunctionCoefficient f(fexact);
         LinearForm b(&fespace);
         b.AddDomainIntegrator(new DomainLFIntegrator(f));
         b.Assemble();

         BilinearForm a(&fespace);
         ConstantCoefficient one(1.0);
         a.AddDomainIntegrator(new DiffusionIntegrator(one));
         a.Assemble();

         GridFunction x(&fespace);
         FunctionCoefficient uex(uexact);
         x = 0.0;
         x.ProjectBdrCoefficient(uex,ess_bdr);

         OperatorPtr A;
         Vector B, X;
         a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);

         UMFPackSolver umf_solver;
         umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
         umf_solver.SetOperator(*A);
         umf_solver.Mult(B, X);

         Vector Y(X.Size());
         A->Mult(X,Y);
         Y-=B;
         REQUIRE(Y.Norml2() < 1.e-12);

         if (order == 3)
         {
            // recover polynomial exact solution if order>=3
            a.RecoverFEMSolution(X, b, x);
            VectorFunctionCoefficient grad(dim,gradexact);
            double err = x.ComputeH1Error(&uex,&grad);
            REQUIRE(err < 1.e-12);
         }
         delete fec;
         delete mesh;
      }
   }
}

#endif

#ifdef DIRECT_SOLVE_PARALLEL

TEST_CASE("direct-parallel", "[Parallel], [CUDA]")
{
   int rank;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   const int ne = 2;
   for (dim = 1; dim < 4; ++dim)
   {
      for (int order = 1; order < 4; ++order)
      {
         Mesh* mesh;
         if (dim == 1)
         {
            mesh = new Mesh(ne,  1.0);
         }
         else if (dim == 2)
         {
            mesh = new Mesh(ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
         }
         else
         {
            mesh = new Mesh(ne, ne, ne, Element::HEXAHEDRON, 1, 1.0, 1.0,
                            1.0);
         }

         ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
         delete mesh;
         FiniteElementCollection* fec = new H1_FECollection(order, dim);
         ParFiniteElementSpace fespace(pmesh, fec);
         Array<int> ess_tdof_list;
         Array<int> ess_bdr;
         if (pmesh->bdr_attributes.Size())
         {
            ess_bdr.SetSize(pmesh->bdr_attributes.Max());
            ess_bdr = 1;
            fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
         }
         FunctionCoefficient f(fexact);
         ParLinearForm b(&fespace);
         b.AddDomainIntegrator(new DomainLFIntegrator(f));
         b.Assemble();

         ParBilinearForm a(&fespace);
         ConstantCoefficient one(1.0);
         a.AddDomainIntegrator(new DiffusionIntegrator(one));
         a.Assemble();

         ParGridFunction x(&fespace);
         FunctionCoefficient uex(uexact);
         x = 0.0;
         x.ProjectBdrCoefficient(uex,ess_bdr);

         OperatorPtr A;
         Vector B, X;
         a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);

#ifdef MFEM_USE_MUMPS
         {
            MUMPSSolver mumps;
            mumps.SetPrintLevel(0);
            mumps.SetOperator(*A.As<HypreParMatrix>());
            mumps.Mult(B,X);
            Vector Y(X.Size());
            A->Mult(X,Y);
            Y-=B;
            REQUIRE(Y.Norml2() < 1.e-12);
            if (order == 3)
            {
               // recover polynomial exact solution if order>=3
               a.RecoverFEMSolution(X, b, x);
               VectorFunctionCoefficient grad(dim,gradexact);
               double err = x.ComputeH1Error(&uex,&grad);
               REQUIRE(err < 1.e-12);
            }
         }
#endif
#ifdef MFEM_USE_SUPERLU
         // Transform to monolithic HypreParMatrix
         {
            SuperLURowLocMatrix SA(*A.As<HypreParMatrix>());
            SuperLUSolver superlu(MPI_COMM_WORLD);
            superlu.SetPrintStatistics(false);
            superlu.SetSymmetricPattern(false);
            superlu.SetColumnPermutation(superlu::PARMETIS);
            superlu.SetOperator(SA);
            superlu.Mult(B, X);
            Vector Y(X.Size());
            A->Mult(X,Y);
            Y-=B;
            REQUIRE(Y.Norml2() < 1.e-12);
            if (order == 3)
            {
               // recover polynomial exact solution if order>=3
               a.RecoverFEMSolution(X, b, x);
               VectorFunctionCoefficient grad(dim,gradexact);
               double err = x.ComputeH1Error(&uex,&grad);
               REQUIRE(err < 1.e-12);
            }
         }
#endif
         delete fec;
         delete pmesh;
      }
   }
}

#endif<commit_msg>removing some tests<commit_after>// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#include "unit_tests.hpp"
#include "mfem.hpp"

using namespace mfem;

#ifdef MFEM_USE_SUITESPARSE
#define DIRECT_SOLVE_SERIAL
#endif
#ifdef MFEM_USE_MUMPS
#define DIRECT_SOLVE_PARALLEL
#endif
#ifdef MFEM_USE_SUPERLU
#define DIRECT_SOLVE_PARALLEL
#endif

#if defined(DIRECT_SOLVE_SERIAL) || defined(DIRECT_SOLVE_PARALLEL)

int dim;
double uexact(const Vector& x)
{
   double u;
   switch (dim)
   {
      case 1:
         u  = 3.0 + 2.0 * x(0) - 0.5 * x(0) * x(0);
         break;
      case 2:
         u = 1.0 + 0.2 * x(0) - 0.9 * x(0) * x(1) + x(1) * x(1) * x(0);
         break;
      default:
         u = x(2) * x(2) * x(2) - 5.0 * x(0) * x(0) * x(1) * x(2);
         break;
   }
   return u;
}

void gradexact(const Vector& x, Vector & grad)
{
   grad.SetSize(dim);
   switch (dim)
   {
      case 1:
         grad[0] = 2.0 - x(0);
         break;
      case 2:
         grad[0] = 0.2 - 0.9 * x(1) + x(1) * x (1);
         grad[1] = - 0.9 * x(0) + 2.0 * x(0) * x(1);
         break;
      default:
         grad[0] = -10.0 * x(0) * x(1) * x(2);
         grad[1] = - 5.0 * x(0) * x(0) * x(2);
         grad[2] = 3.0 * x(2) * x(2) - 5.0 * x(0) * x(0) * x(1);
         break;
   }
}

double d2uexact(const Vector& x) // retuns \Delta u
{
   double d2u;
   switch (dim)
   {
      case 1:
         d2u  = -1.0;
         break;
      case 2:
         d2u = 2.0 * x(0);
         break;
      default:
         d2u = -10.0 * x(1) * x(2) + 6.0 * x(2);
         break;
   }
   return d2u;
}

double fexact(const Vector& x) // retuns -\Delta u
{
   double d2u = d2uexact(x);
   return -d2u;
}

#endif

#ifdef DIRECT_SOLVE_SERIAL

TEST_CASE("direct-serial","[CUDA]")
{
   const int ne = 2;
   for (dim = 1; dim < 4; ++dim)
   {
      Mesh* mesh;
      if (dim == 1)
      {
         mesh = new Mesh(ne,  1.0);
      }
      else if (dim == 2)
      {
         mesh = new Mesh(ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
      }
      else
      {
         mesh = new Mesh(ne, ne, ne, Element::HEXAHEDRON, 1, 1.0, 1.0,
                         1.0);
      }
      int order = 3;
      FiniteElementCollection* fec = new H1_FECollection(order, dim);
      FiniteElementSpace fespace(mesh, fec);
      Array<int> ess_tdof_list;
      Array<int> ess_bdr(mesh->bdr_attributes.Max());
      ess_bdr = 1;
      fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);

      FunctionCoefficient f(fexact);
      LinearForm b(&fespace);
      b.AddDomainIntegrator(new DomainLFIntegrator(f));
      b.Assemble();

      BilinearForm a(&fespace);
      ConstantCoefficient one(1.0);
      a.AddDomainIntegrator(new DiffusionIntegrator(one));
      a.Assemble();

      GridFunction x(&fespace);
      FunctionCoefficient uex(uexact);
      x = 0.0;
      x.ProjectBdrCoefficient(uex,ess_bdr);

      OperatorPtr A;
      Vector B, X;
      a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);

      UMFPackSolver umf_solver;
      umf_solver.Control[UMFPACK_ORDERING] = UMFPACK_ORDERING_METIS;
      umf_solver.SetOperator(*A);
      umf_solver.Mult(B, X);

      Vector Y(X.Size());
      A->Mult(X,Y);
      Y-=B;
      REQUIRE(Y.Norml2() < 1.e-12);

      a.RecoverFEMSolution(X, b, x);
      VectorFunctionCoefficient grad(dim,gradexact);
      double err = x.ComputeH1Error(&uex,&grad);
      REQUIRE(err < 1.e-12);
      delete fec;
      delete mesh;
   }
}

#endif

#ifdef DIRECT_SOLVE_PARALLEL

TEST_CASE("direct-parallel", "[Parallel], [CUDA]")
{
   int rank;
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
   const int ne = 2;
   for (dim = 1; dim < 4; ++dim)
   {
      Mesh* mesh;
      if (dim == 1)
      {
         mesh = new Mesh(ne,  1.0);
      }
      else if (dim == 2)
      {
         mesh = new Mesh(ne, ne, Element::QUADRILATERAL, 1, 1.0, 1.0);
      }
      else
      {
         mesh = new Mesh(ne, ne, ne, Element::HEXAHEDRON, 1, 1.0, 1.0,
                         1.0);
      }

      ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
      delete mesh;
      int order = 3;
      FiniteElementCollection* fec = new H1_FECollection(order, dim);
      ParFiniteElementSpace fespace(pmesh, fec);
      Array<int> ess_tdof_list;
      Array<int> ess_bdr;
      if (pmesh->bdr_attributes.Size())
      {
         ess_bdr.SetSize(pmesh->bdr_attributes.Max());
         ess_bdr = 1;
         fespace.GetEssentialTrueDofs(ess_bdr, ess_tdof_list);
      }
      FunctionCoefficient f(fexact);
      ParLinearForm b(&fespace);
      b.AddDomainIntegrator(new DomainLFIntegrator(f));
      b.Assemble();

      ParBilinearForm a(&fespace);
      ConstantCoefficient one(1.0);
      a.AddDomainIntegrator(new DiffusionIntegrator(one));
      a.Assemble();

      ParGridFunction x(&fespace);
      FunctionCoefficient uex(uexact);
      x = 0.0;
      x.ProjectBdrCoefficient(uex,ess_bdr);

      OperatorPtr A;
      Vector B, X;
      a.FormLinearSystem(ess_tdof_list, x, b, A, X, B);

#ifdef MFEM_USE_MUMPS
      {
         MUMPSSolver mumps;
         mumps.SetPrintLevel(0);
         mumps.SetOperator(*A.As<HypreParMatrix>());
         mumps.Mult(B,X);
         Vector Y(X.Size());
         A->Mult(X,Y);
         Y-=B;
         REQUIRE(Y.Norml2() < 1.e-12);

         a.RecoverFEMSolution(X, b, x);
         VectorFunctionCoefficient grad(dim,gradexact);
         double err = x.ComputeH1Error(&uex,&grad);
         REQUIRE(err < 1.e-12);
      }
#endif
#ifdef MFEM_USE_SUPERLU
      // Transform to monolithic HypreParMatrix
      {
         SuperLURowLocMatrix SA(*A.As<HypreParMatrix>());
         SuperLUSolver superlu(MPI_COMM_WORLD);
         superlu.SetPrintStatistics(false);
         superlu.SetSymmetricPattern(false);
         superlu.SetColumnPermutation(superlu::PARMETIS);
         superlu.SetOperator(SA);
         superlu.Mult(B, X);
         Vector Y(X.Size());
         A->Mult(X,Y);
         Y-=B;
         REQUIRE(Y.Norml2() < 1.e-12);
         a.RecoverFEMSolution(X, b, x);
         VectorFunctionCoefficient grad(dim,gradexact);
         double err = x.ComputeH1Error(&uex,&grad);
         REQUIRE(err < 1.e-12);
      }
#endif
      delete fec;
      delete pmesh;
   }
}

#endif<|endoftext|>


<commit_before>/*
 * Copyright (C) 2009, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

// Network API based on boost::asio
#ifndef LIBPORT_ASIO_HH
# define LIBPORT_ASIO_HH

# include <libport/config.h>
# include <libport/sys/socket.h>
# include <libport/iostream>

// At least on OS X, many symbols are not available where we expect
// them.  And it is clearly connected to our using
// -fvisibility=hidden.  I (AD) could not exactly pinpoint the
// problem, but at least I know one cure: do not play dirty visibility
// tricks with Asio.
# pragma GCC visibility push(default)
#  include <boost/asio.hpp>
#  if defined LIBPORT_ENABLE_SSL
#    include <boost/asio/ssl.hpp>
#  endif
# pragma GCC visibility pop
# include <boost/function.hpp>

# include <libport/destructible.hh>
# include <libport/export.hh>
# include <libport/finally.hh>
# include <libport/utime.hh>

# include <boost/version.hpp>
# if BOOST_VERSION >= 103600
#  include <libport/rs232.hh>
# endif

namespace libport
{
  class LIBPORT_API AsioDestructible: public Destructible
  {
    protected:
      virtual void doDestroy();
  };

  //FIXME: extend to provide a way to ensure workerThread not started.
  /** Get the io_service handling all asynchronous operations.
   *
   * \param startWorkerThread create a thread in which to run the io_service.
   * If \b startWorkerThread is false on \b first invocation, the user
   * is responsible for calling the work or poll methods of the io_service.
   */
  LIBPORT_API boost::asio::io_service&
  get_io_service(bool startWorkerThread = true);

  /** BaseSocket class.
   *
   * This class has a callback-based API: onReadFunc() and onErrorFunc().
   */
  class LIBPORT_API BaseSocket: public AsioDestructible
  {
  public:
    virtual ~BaseSocket(){}
    libport::Finally deletor;
    /// Write data asynchronously to the socket.
    virtual void write(const void* data, unsigned int length) = 0;
    /// Alias on write() for API compatibility.
    void send(const void* addr, size_t len)
    {
      write(addr, len);
    }
    /// Alias on close() for API compatibility.
    void disconnect()
    {
      close();
    }
    /// Return if the socket is connected to a remote host.
    virtual bool isConnected() const = 0;
    /// Disconnect the socket from the remote host, calls onError.
    virtual void close() = 0;
    /// Get port of remote endpoint
    virtual unsigned short getRemotePort() const = 0;
    /// Get IP address of remote endpoint
    virtual std::string getRemoteHost() const = 0;
    /// Get port of local endpoint
    virtual unsigned short getLocalPort() const = 0;
    /// Get IP address of local endpoint
    virtual std::string getLocalHost() const = 0;
    virtual int stealFD() = 0;
    virtual int getFD() = 0;
    /// Callback function called each time new data is available.
    boost::function1<bool, boost::asio::streambuf&> onReadFunc;
    /// Callback function called in case of error on the socket.
    boost::function1<void, boost::system::error_code> onErrorFunc;
    /// Mutex to protect access to the above callbacks.
    Lockable callbackLock;
  };

  /// Endpoint on an UDP socket.
  class LIBPORT_API UDPLink
  {
  public:
    virtual ~UDPLink()
    {}
    virtual void reply(const void* data, size_t length) = 0;
    void reply(const std::string& s)
    {
      reply(s.c_str(), s.length());
    }
  };

  /** Socket class with a higher API.
   *
   * It is recommended that you always asynchronously destroy Socket instances
   * by calling the destroy() method. If you want to put instances in the stack,
   * or explicitly call delete for whatever reason, you must in your destructor:
   * - call close()
   * - call wasDestroyed()
   * - call waitForDestructionPermission(), if the io_service is running in
   *   another thread(the default behavior).
   */
  class LIBPORT_API Socket: public AsioDestructible
  {
  public:
    Socket(boost::asio::io_service& io = libport::get_io_service())
      : base_(0)
      , io_(io)
    {}
    virtual ~Socket();
    /** Set underlying BaseSocket object, setup its callbacks to call our virtual functions.
     */
    virtual void setBase(BaseSocket*);

    /** Called each time new data is received.
     *   \return the number of bytes used in buffer. The remaining data will
     *   be passed again to this function as soon as at least an extra byte
     *   is available.
     */
    virtual int onRead(const void*, size_t length)
    {
      return length;
    }

    /** Called in case of error on the socket. By default, do nothing.
     */
    virtual void onError(boost::system::error_code);

    /** Called on socket connection
     */
    virtual void onConnect()
    {}

    /// Ask for the asynchronous destruction of this object.
    virtual void destroy();

    void write(const void* data, unsigned int length)
    {
      base_->write(data, length);
    }
    /// Alias on write() for API compatibility.
    void send(const void* addr, size_t len)
    {
      write(addr, len);
    }
    void send(const std::string& s)
    {
      write(s.c_str(), s.length());
    }
    void close()
    {
      if (base_)
        base_->close();
    }

    unsigned short getRemotePort() const { return base_->getRemotePort();}
    std::string getRemoteHost() const    { return base_->getRemoteHost();}
    unsigned short getLocalPort() const  { return base_->getLocalPort();}
    std::string getLocalHost() const     { return base_->getLocalHost();}
    bool isConnected() const      {return base_ ? base_->isConnected() : false;}

    /** Connect to a remote host.
     * \param host hostname to connect to.
     * \param port port to connect to, as a service name or an int.
     * \param usTimeout timeout in microseconds, 0 meaning none.
     * \param asynchronous make an asynchronous attempt. The function will
     *    return immediately, and either onConnect will be called when the
     *    connection succeeds, or onError() will be called.
     * \return an error code if the connection failed.
     *
     */
    boost::system::error_code connect(const std::string& host,
                                      const std::string& port,
                                      bool udp=false, utime_t usTimeout = 0,
                                      bool asynchronous = false);

    boost::system::error_code connect(const std::string& host,
                                      unsigned port,
                                      bool udp=false, utime_t usTimeout = 0,
                                      bool asynchronous = false);

# if BOOST_VERSION >= 103600

    boost::system::error_code open_serial(const std::string& device,
                                          unsigned int rate);

# endif

    /** Make an asynchronous connection attempt to a remote host.
     *  This API garantees that either the onConnect() or the onError() function
     *  will be called.
     * \param host hostname to connect to.
     * \param port port to connect to, as a service name or an int.
     * \param usTimeout timeout in microseconds, 0 meaning none.
     */
    void async_connect(const std::string& host,
                       const std::string& port,
                       bool udp=false, utime_t usTimeout = 0)
    {
      connect(host, port, udp, usTimeout, true);
    }

    typedef void* Handle;
    typedef boost::function0<Socket*> SocketFactory;
    /** Listen using udp.
     * Call onRead(data, length, link) for each new packet. Link can be used
     * to reply to the sender through its UDPLink::reply() method.
     */
    static Handle listenUDP(const std::string& host, const std::string& port,
                            boost::function3<void, const void*, int,
                            boost::shared_ptr<UDPLink> > onRead,
                            boost::system::error_code& erc);

    /** Listen using TCP or UDP.
     *
     * \param f a socket factory. For each new connection the function will
     * be called and the resulting socket object bound to the new connection.
     */
    boost::system::error_code listen(SocketFactory f, const std::string& host,
                                     const std::string& port, bool udp = false);
    boost::system::error_code listen(SocketFactory f, const std::string& host,
                                     unsigned port, bool udp = false);

#if defined LIBPORT_ENABLE_SSL
    boost::system::error_code
    listenSSL(SocketFactory f,
              const std::string& host,
              const std::string& port,
              boost::asio::ssl::context_base::method ctx
                = boost::asio::ssl::context::sslv23_server,
              boost::asio::ssl::context::options options
                = boost::asio::ssl::context::verify_none,
              const std::string& privateKeyFile = "",
              const std::string& certChainFile = "",
              const std::string& tmpDHFile = "" ,
              const std::string& cipherList = "");
#endif

    /// Steal file descriptor from Socket. Return the file descriptor.
    int stealFD();
    /// Get file descriptor from Socket.
    int getFD();
    /// Set file descriptor
    template<class Sock>
    void setFD(int fd, typename Sock::protocol_type proto);
    /** Sleep for specified amount of time, polling if current thread is
     * the asio worker thread
     */
    static void sleep(utime_t duration);
    boost::asio::io_service& get_io_service();
  protected:
    bool onRead_(boost::asio::streambuf&);
    std::string buffer;
    BaseSocket* base_;
  private:
    template<typename Proto, typename BaseFactory> boost::system::error_code
    listenProto(SocketFactory f, const std::string& host,
                const std::string&port, BaseFactory bf);
    template<typename Proto, typename BaseFactory> boost::system::error_code
    connectProto(const std::string& host, const std::string& port,
                 utime_t timeout, bool async, BaseFactory bf);
    boost::asio::io_service& io_;
  };

  /** Wrapper of libport::Socket to be able to use Socket without inherit from
   *  it.
   */
  class LIBPORT_API ConcreteSocket: public Socket
  {
  public:
    typedef boost::function2<int, const void*, size_t> onread_type;
    typedef boost::function1<void, boost::system::error_code> onerror_type;
    typedef boost::function0<void> onconnect_type;
    typedef Socket super_type;

    ConcreteSocket(boost::asio::io_service& io = libport::get_io_service())
      : Socket(io)
      , onconnect_(boost::bind(&super_type::onConnect, this))
      , onerror_(boost::bind(&super_type::onError, this, _1))
      , onread_(boost::bind(&super_type::onRead, this, _1, _2))
      {}

    ConcreteSocket& onConnect(onconnect_type cb)
    {
      onconnect_ = cb;
      return *this;
    }

    ConcreteSocket& onError(onerror_type cb)
    {
      onerror_ = cb;
      return *this;
    }

    ConcreteSocket& onRead(onread_type cb)
    {
      onread_ = cb;
      return *this;
    }

    virtual void onConnect()
    {
      onconnect_();
    }
    virtual void onError(boost::system::error_code erc)
    {
      onerror_(erc);
    }
    virtual int onRead(const void* data, size_t length)
    {
      return onread_(data, length);
    }

  private:
    onconnect_type onconnect_;
    onerror_type onerror_;
    onread_type onread_;
  };

  LIBPORT_API bool
  isPollThread();

  /** Poll on an io_service for given duration in microseconds.
   *  @param duration max duration for which to poll in microseconds
   *  @param once process at most one handler before returning if true
   */
  LIBPORT_API void
  pollFor(utime_t duration, bool once = false,
          boost::asio::io_service& io = get_io_service());

  typedef boost::shared_ptr<boost::asio::deadline_timer> AsyncCallHandler;
  /** Call \b callback() in \b usDelay microseconds.
   *  If the method cancel() of the returned timer object is invoked before
   *  expiration of the delay, the call is aborted and callback() is not
   *  called.
   */
  AsyncCallHandler
  asyncCall(boost::function0<void> callback, utime_t usDelay,
            boost::asio::io_service& io = get_io_service());
}

# include "libport/asio.hxx"

#endif
<commit_msg>Do not use #pragma visibility under mingw and vcxx.<commit_after>/*
 * Copyright (C) 2009, Gostai S.A.S.
 *
 * This software is provided "as is" without warranty of any kind,
 * either expressed or implied, including but not limited to the
 * implied warranties of fitness for a particular purpose.
 *
 * See the LICENSE file for more information.
 */

// Network API based on boost::asio
#ifndef LIBPORT_ASIO_HH
# define LIBPORT_ASIO_HH

# include <libport/config.h>
# include <libport/sys/socket.h>
# include <libport/iostream>

// At least on OS X, many symbols are not available where we expect
// them.  And it is clearly connected to our using
// -fvisibility=hidden.  I (AD) could not exactly pinpoint the
// problem, but at least I know one cure: do not play dirty visibility
// tricks with Asio.
# if ! defined WIN32
#  pragma GCC visibility push(default)
# endif
# include <boost/asio.hpp>
# if defined LIBPORT_ENABLE_SSL
#   include <boost/asio/ssl.hpp>
# endif
# if ! defined WIN32
#  pragma GCC visibility pop
# endif

# include <boost/function.hpp>

# include <libport/destructible.hh>
# include <libport/export.hh>
# include <libport/finally.hh>
# include <libport/utime.hh>

# include <boost/version.hpp>
# if BOOST_VERSION >= 103600
#  include <libport/rs232.hh>
# endif

namespace libport
{
  class LIBPORT_API AsioDestructible: public Destructible
  {
    protected:
      virtual void doDestroy();
  };

  //FIXME: extend to provide a way to ensure workerThread not started.
  /** Get the io_service handling all asynchronous operations.
   *
   * \param startWorkerThread create a thread in which to run the io_service.
   * If \b startWorkerThread is false on \b first invocation, the user
   * is responsible for calling the work or poll methods of the io_service.
   */
  LIBPORT_API boost::asio::io_service&
  get_io_service(bool startWorkerThread = true);

  /** BaseSocket class.
   *
   * This class has a callback-based API: onReadFunc() and onErrorFunc().
   */
  class LIBPORT_API BaseSocket: public AsioDestructible
  {
  public:
    virtual ~BaseSocket(){}
    libport::Finally deletor;
    /// Write data asynchronously to the socket.
    virtual void write(const void* data, unsigned int length) = 0;
    /// Alias on write() for API compatibility.
    void send(const void* addr, size_t len)
    {
      write(addr, len);
    }
    /// Alias on close() for API compatibility.
    void disconnect()
    {
      close();
    }
    /// Return if the socket is connected to a remote host.
    virtual bool isConnected() const = 0;
    /// Disconnect the socket from the remote host, calls onError.
    virtual void close() = 0;
    /// Get port of remote endpoint
    virtual unsigned short getRemotePort() const = 0;
    /// Get IP address of remote endpoint
    virtual std::string getRemoteHost() const = 0;
    /// Get port of local endpoint
    virtual unsigned short getLocalPort() const = 0;
    /// Get IP address of local endpoint
    virtual std::string getLocalHost() const = 0;
    virtual int stealFD() = 0;
    virtual int getFD() = 0;
    /// Callback function called each time new data is available.
    boost::function1<bool, boost::asio::streambuf&> onReadFunc;
    /// Callback function called in case of error on the socket.
    boost::function1<void, boost::system::error_code> onErrorFunc;
    /// Mutex to protect access to the above callbacks.
    Lockable callbackLock;
  };

  /// Endpoint on an UDP socket.
  class LIBPORT_API UDPLink
  {
  public:
    virtual ~UDPLink()
    {}
    virtual void reply(const void* data, size_t length) = 0;
    void reply(const std::string& s)
    {
      reply(s.c_str(), s.length());
    }
  };

  /** Socket class with a higher API.
   *
   * It is recommended that you always asynchronously destroy Socket instances
   * by calling the destroy() method. If you want to put instances in the stack,
   * or explicitly call delete for whatever reason, you must in your destructor:
   * - call close()
   * - call wasDestroyed()
   * - call waitForDestructionPermission(), if the io_service is running in
   *   another thread(the default behavior).
   */
  class LIBPORT_API Socket: public AsioDestructible
  {
  public:
    Socket(boost::asio::io_service& io = libport::get_io_service())
      : base_(0)
      , io_(io)
    {}
    virtual ~Socket();
    /** Set underlying BaseSocket object, setup its callbacks to call our virtual functions.
     */
    virtual void setBase(BaseSocket*);

    /** Called each time new data is received.
     *   \return the number of bytes used in buffer. The remaining data will
     *   be passed again to this function as soon as at least an extra byte
     *   is available.
     */
    virtual int onRead(const void*, size_t length)
    {
      return length;
    }

    /** Called in case of error on the socket. By default, do nothing.
     */
    virtual void onError(boost::system::error_code);

    /** Called on socket connection
     */
    virtual void onConnect()
    {}

    /// Ask for the asynchronous destruction of this object.
    virtual void destroy();

    void write(const void* data, unsigned int length)
    {
      base_->write(data, length);
    }
    /// Alias on write() for API compatibility.
    void send(const void* addr, size_t len)
    {
      write(addr, len);
    }
    void send(const std::string& s)
    {
      write(s.c_str(), s.length());
    }
    void close()
    {
      if (base_)
        base_->close();
    }

    unsigned short getRemotePort() const { return base_->getRemotePort();}
    std::string getRemoteHost() const    { return base_->getRemoteHost();}
    unsigned short getLocalPort() const  { return base_->getLocalPort();}
    std::string getLocalHost() const     { return base_->getLocalHost();}
    bool isConnected() const      {return base_ ? base_->isConnected() : false;}

    /** Connect to a remote host.
     * \param host hostname to connect to.
     * \param port port to connect to, as a service name or an int.
     * \param usTimeout timeout in microseconds, 0 meaning none.
     * \param asynchronous make an asynchronous attempt. The function will
     *    return immediately, and either onConnect will be called when the
     *    connection succeeds, or onError() will be called.
     * \return an error code if the connection failed.
     *
     */
    boost::system::error_code connect(const std::string& host,
                                      const std::string& port,
                                      bool udp=false, utime_t usTimeout = 0,
                                      bool asynchronous = false);

    boost::system::error_code connect(const std::string& host,
                                      unsigned port,
                                      bool udp=false, utime_t usTimeout = 0,
                                      bool asynchronous = false);

# if BOOST_VERSION >= 103600

    boost::system::error_code open_serial(const std::string& device,
                                          unsigned int rate);

# endif

    /** Make an asynchronous connection attempt to a remote host.
     *  This API garantees that either the onConnect() or the onError() function
     *  will be called.
     * \param host hostname to connect to.
     * \param port port to connect to, as a service name or an int.
     * \param usTimeout timeout in microseconds, 0 meaning none.
     */
    void async_connect(const std::string& host,
                       const std::string& port,
                       bool udp=false, utime_t usTimeout = 0)
    {
      connect(host, port, udp, usTimeout, true);
    }

    typedef void* Handle;
    typedef boost::function0<Socket*> SocketFactory;
    /** Listen using udp.
     * Call onRead(data, length, link) for each new packet. Link can be used
     * to reply to the sender through its UDPLink::reply() method.
     */
    static Handle listenUDP(const std::string& host, const std::string& port,
                            boost::function3<void, const void*, int,
                            boost::shared_ptr<UDPLink> > onRead,
                            boost::system::error_code& erc);

    /** Listen using TCP or UDP.
     *
     * \param f a socket factory. For each new connection the function will
     * be called and the resulting socket object bound to the new connection.
     */
    boost::system::error_code listen(SocketFactory f, const std::string& host,
                                     const std::string& port, bool udp = false);
    boost::system::error_code listen(SocketFactory f, const std::string& host,
                                     unsigned port, bool udp = false);

#if defined LIBPORT_ENABLE_SSL
    boost::system::error_code
    listenSSL(SocketFactory f,
              const std::string& host,
              const std::string& port,
              boost::asio::ssl::context_base::method ctx
                = boost::asio::ssl::context::sslv23_server,
              boost::asio::ssl::context::options options
                = boost::asio::ssl::context::verify_none,
              const std::string& privateKeyFile = "",
              const std::string& certChainFile = "",
              const std::string& tmpDHFile = "" ,
              const std::string& cipherList = "");
#endif

    /// Steal file descriptor from Socket. Return the file descriptor.
    int stealFD();
    /// Get file descriptor from Socket.
    int getFD();
    /// Set file descriptor
    template<class Sock>
    void setFD(int fd, typename Sock::protocol_type proto);
    /** Sleep for specified amount of time, polling if current thread is
     * the asio worker thread
     */
    static void sleep(utime_t duration);
    boost::asio::io_service& get_io_service();
  protected:
    bool onRead_(boost::asio::streambuf&);
    std::string buffer;
    BaseSocket* base_;
  private:
    template<typename Proto, typename BaseFactory> boost::system::error_code
    listenProto(SocketFactory f, const std::string& host,
                const std::string&port, BaseFactory bf);
    template<typename Proto, typename BaseFactory> boost::system::error_code
    connectProto(const std::string& host, const std::string& port,
                 utime_t timeout, bool async, BaseFactory bf);
    boost::asio::io_service& io_;
  };

  /** Wrapper of libport::Socket to be able to use Socket without inherit from
   *  it.
   */
  class LIBPORT_API ConcreteSocket: public Socket
  {
  public:
    typedef boost::function2<int, const void*, size_t> onread_type;
    typedef boost::function1<void, boost::system::error_code> onerror_type;
    typedef boost::function0<void> onconnect_type;
    typedef Socket super_type;

    ConcreteSocket(boost::asio::io_service& io = libport::get_io_service())
      : Socket(io)
      , onconnect_(boost::bind(&super_type::onConnect, this))
      , onerror_(boost::bind(&super_type::onError, this, _1))
      , onread_(boost::bind(&super_type::onRead, this, _1, _2))
      {}

    ConcreteSocket& onConnect(onconnect_type cb)
    {
      onconnect_ = cb;
      return *this;
    }

    ConcreteSocket& onError(onerror_type cb)
    {
      onerror_ = cb;
      return *this;
    }

    ConcreteSocket& onRead(onread_type cb)
    {
      onread_ = cb;
      return *this;
    }

    virtual void onConnect()
    {
      onconnect_();
    }
    virtual void onError(boost::system::error_code erc)
    {
      onerror_(erc);
    }
    virtual int onRead(const void* data, size_t length)
    {
      return onread_(data, length);
    }

  private:
    onconnect_type onconnect_;
    onerror_type onerror_;
    onread_type onread_;
  };

  LIBPORT_API bool
  isPollThread();

  /** Poll on an io_service for given duration in microseconds.
   *  @param duration max duration for which to poll in microseconds
   *  @param once process at most one handler before returning if true
   */
  LIBPORT_API void
  pollFor(utime_t duration, bool once = false,
          boost::asio::io_service& io = get_io_service());

  typedef boost::shared_ptr<boost::asio::deadline_timer> AsyncCallHandler;
  /** Call \b callback() in \b usDelay microseconds.
   *  If the method cancel() of the returned timer object is invoked before
   *  expiration of the delay, the call is aborted and callback() is not
   *  called.
   */
  AsyncCallHandler
  asyncCall(boost::function0<void> callback, utime_t usDelay,
            boost::asio::io_service& io = get_io_service());
}

# include "libport/asio.hxx"

#endif
<|endoftext|>