<commit_before>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "main.h"

template<typename ArrayType> void array(const ArrayType& m)
{
  typedef typename ArrayType::Index Index;
  typedef typename ArrayType::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;
  typedef Array<Scalar, ArrayType::RowsAtCompileTime, 1> ColVectorType;
  typedef Array<Scalar, 1, ArrayType::ColsAtCompileTime> RowVectorType;

  Index rows = m.rows();
  Index cols = m.cols();

  ArrayType m1 = ArrayType::Random(rows, cols),
             m2 = ArrayType::Random(rows, cols),
             m3(rows, cols);

  ColVectorType cv1 = ColVectorType::Random(rows);
  RowVectorType rv1 = RowVectorType::Random(cols);

  Scalar  s1 = internal::random<Scalar>(),
          s2 = internal::random<Scalar>();

  // scalar addition
  VERIFY_IS_APPROX(m1 + s1, s1 + m1);
  VERIFY_IS_APPROX(m1 + s1, ArrayType::Constant(rows,cols,s1) + m1);
  VERIFY_IS_APPROX(s1 - m1, (-m1)+s1 );
  VERIFY_IS_APPROX(m1 - s1, m1 - ArrayType::Constant(rows,cols,s1));
  VERIFY_IS_APPROX(s1 - m1, ArrayType::Constant(rows,cols,s1) - m1);
  VERIFY_IS_APPROX((m1*Scalar(2)) - s2, (m1+m1) - ArrayType::Constant(rows,cols,s2) );
  m3 = m1;
  m3 += s2;
  VERIFY_IS_APPROX(m3, m1 + s2);
  m3 = m1;
  m3 -= s1;
  VERIFY_IS_APPROX(m3, m1 - s1);

  // scalar operators via Maps
  m3 = m1;
  ArrayType::Map(m1.data(), m1.rows(), m1.cols()) -= ArrayType::Map(m2.data(), m2.rows(), m2.cols());
  VERIFY_IS_APPROX(m1, m3 - m2);

  m3 = m1;
  ArrayType::Map(m1.data(), m1.rows(), m1.cols()) += ArrayType::Map(m2.data(), m2.rows(), m2.cols());
  VERIFY_IS_APPROX(m1, m3 + m2);

  m3 = m1;
  ArrayType::Map(m1.data(), m1.rows(), m1.cols()) *= ArrayType::Map(m2.data(), m2.rows(), m2.cols());
  VERIFY_IS_APPROX(m1, m3 * m2);

  m3 = m1;
  m2 = ArrayType::Random(rows,cols);
  m2 = (m2==0).select(1,m2);
  ArrayType::Map(m1.data(), m1.rows(), m1.cols()) /= ArrayType::Map(m2.data(), m2.rows(), m2.cols());
  VERIFY_IS_APPROX(m1, m3 / m2);

  // reductions
  VERIFY_IS_APPROX(m1.colwise().sum().sum(), m1.sum());
  VERIFY_IS_APPROX(m1.rowwise().sum().sum(), m1.sum());
  if (!internal::isApprox(m1.sum(), (m1+m2).sum(), test_precision<Scalar>()))
      VERIFY_IS_NOT_APPROX(((m1+m2).rowwise().sum()).sum(), m1.sum());
  VERIFY_IS_APPROX(m1.colwise().sum(), m1.colwise().redux(internal::scalar_sum_op<Scalar>()));

  // vector-wise ops
  m3 = m1;
  VERIFY_IS_APPROX(m3.colwise() += cv1, m1.colwise() + cv1);
  m3 = m1;
  VERIFY_IS_APPROX(m3.colwise() -= cv1, m1.colwise() - cv1);
  m3 = m1;
  VERIFY_IS_APPROX(m3.rowwise() += rv1, m1.rowwise() + rv1);
  m3 = m1;
  VERIFY_IS_APPROX(m3.rowwise() -= rv1, m1.rowwise() - rv1);
}

template<typename ArrayType> void comparisons(const ArrayType& m)
{
  typedef typename ArrayType::Index Index;
  typedef typename ArrayType::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;
  typedef Array<Scalar, ArrayType::RowsAtCompileTime, 1> VectorType;

  Index rows = m.rows();
  Index cols = m.cols();

  Index r = internal::random<Index>(0, rows-1),
        c = internal::random<Index>(0, cols-1);

  ArrayType m1 = ArrayType::Random(rows, cols),
             m2 = ArrayType::Random(rows, cols),
             m3(rows, cols);

  VERIFY(((m1 + Scalar(1)) > m1).all());
  VERIFY(((m1 - Scalar(1)) < m1).all());
  if (rows*cols>1)
  {
    m3 = m1;
    m3(r,c) += 1;
    VERIFY(! (m1 < m3).all() );
    VERIFY(! (m1 > m3).all() );
  }

  // comparisons to scalar
  VERIFY( (m1 != (m1(r,c)+1) ).any() );
  VERIFY( (m1 > (m1(r,c)-1) ).any() );
  VERIFY( (m1 < (m1(r,c)+1) ).any() );
  VERIFY( (m1 == m1(r,c) ).any() );

  // test Select
  VERIFY_IS_APPROX( (m1<m2).select(m1,m2), m1.cwiseMin(m2) );
  VERIFY_IS_APPROX( (m1>m2).select(m1,m2), m1.cwiseMax(m2) );
  Scalar mid = (m1.cwiseAbs().minCoeff() + m1.cwiseAbs().maxCoeff())/Scalar(2);
  for (int j=0; j<cols; ++j)
  for (int i=0; i<rows; ++i)
    m3(i,j) = internal::abs(m1(i,j))<mid ? 0 : m1(i,j);
  VERIFY_IS_APPROX( (m1.abs()<ArrayType::Constant(rows,cols,mid))
                        .select(ArrayType::Zero(rows,cols),m1), m3);
  // shorter versions:
  VERIFY_IS_APPROX( (m1.abs()<ArrayType::Constant(rows,cols,mid))
                        .select(0,m1), m3);
  VERIFY_IS_APPROX( (m1.abs()>=ArrayType::Constant(rows,cols,mid))
                        .select(m1,0), m3);
  // even shorter version:
  VERIFY_IS_APPROX( (m1.abs()<mid).select(0,m1), m3);

  // count
  VERIFY(((m1.abs()+1)>RealScalar(0.1)).count() == rows*cols);

  typedef Array<typename ArrayType::Index, Dynamic, 1> ArrayOfIndices;

  // TODO allows colwise/rowwise for array
  VERIFY_IS_APPROX(((m1.abs()+1)>RealScalar(0.1)).colwise().count(), ArrayOfIndices::Constant(cols,rows).transpose());
  VERIFY_IS_APPROX(((m1.abs()+1)>RealScalar(0.1)).rowwise().count(), ArrayOfIndices::Constant(rows, cols));
}

template<typename ArrayType> void array_real(const ArrayType& m)
{
  typedef typename ArrayType::Index Index;
  typedef typename ArrayType::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;

  Index rows = m.rows();
  Index cols = m.cols();

  ArrayType m1 = ArrayType::Random(rows, cols),
             m2 = ArrayType::Random(rows, cols),
             m3(rows, cols);

  VERIFY_IS_APPROX(m1.sin(), std::sin(m1));
  VERIFY_IS_APPROX(m1.sin(), internal::sin(m1));
  VERIFY_IS_APPROX(m1.cos(), std::cos(m1));
  VERIFY_IS_APPROX(m1.cos(), internal::cos(m1));

  VERIFY_IS_APPROX(internal::cos(m1+RealScalar(3)*m2), internal::cos((m1+RealScalar(3)*m2).eval()));
  VERIFY_IS_APPROX(std::cos(m1+RealScalar(3)*m2), std::cos((m1+RealScalar(3)*m2).eval()));

  VERIFY_IS_APPROX(m1.abs().sqrt(), std::sqrt(std::abs(m1)));
  VERIFY_IS_APPROX(m1.abs().sqrt(), internal::sqrt(internal::abs(m1)));
  VERIFY_IS_APPROX(m1.abs(), internal::sqrt(internal::abs2(m1)));

  VERIFY_IS_APPROX(internal::abs2(internal::real(m1)) + internal::abs2(internal::imag(m1)), internal::abs2(m1));
  VERIFY_IS_APPROX(internal::abs2(std::real(m1)) + internal::abs2(std::imag(m1)), internal::abs2(m1));
  if(!NumTraits<Scalar>::IsComplex)
    VERIFY_IS_APPROX(internal::real(m1), m1);

  VERIFY_IS_APPROX(m1.abs().log(), std::log(std::abs(m1)));
  VERIFY_IS_APPROX(m1.abs().log(), internal::log(internal::abs(m1)));

  VERIFY_IS_APPROX(m1.exp(), std::exp(m1));
  VERIFY_IS_APPROX(m1.exp() * m2.exp(), std::exp(m1+m2));
  VERIFY_IS_APPROX(m1.exp(), internal::exp(m1));
  VERIFY_IS_APPROX(m1.exp() / m2.exp(), std::exp(m1-m2));

  VERIFY_IS_APPROX(m1.pow(2), m1.square());
  VERIFY_IS_APPROX(std::pow(m1,2), m1.square());
  m3 = m1.abs();
  VERIFY_IS_APPROX(m3.pow(RealScalar(0.5)), m3.sqrt());
  VERIFY_IS_APPROX(std::pow(m3,RealScalar(0.5)), m3.sqrt());
}

void test_array()
{
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( array(Array<float, 1, 1>()) );
    //CALL_SUBTEST_2( array(Array22f()) );
    //CALL_SUBTEST_3( array(Array44d()) );
    //CALL_SUBTEST_4( array(ArrayXXcf(3, 3)) );
    //CALL_SUBTEST_5( array(ArrayXXf(8, 12)) );
    //CALL_SUBTEST_6( array(ArrayXXi(8, 12)) );
  }
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( comparisons(Array<float, 1, 1>()) );
    //CALL_SUBTEST_2( comparisons(Array22f()) );
    //CALL_SUBTEST_3( comparisons(Array44d()) );
    //CALL_SUBTEST_5( comparisons(ArrayXXf(8, 12)) );
    //CALL_SUBTEST_6( comparisons(ArrayXXi(8, 12)) );
  }
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( array_real(Array<float, 1, 1>()) );
    //CALL_SUBTEST_2( array_real(Array22f()) );
    //CALL_SUBTEST_3( array_real(Array44d()) );
    //CALL_SUBTEST_5( array_real(ArrayXXf(8, 12)) );
  }

  VERIFY((internal::is_same< internal::global_math_functions_filtering_base<int>::type, int >::value));
  VERIFY((internal::is_same< internal::global_math_functions_filtering_base<float>::type, float >::value));
  VERIFY((internal::is_same< internal::global_math_functions_filtering_base<Array2i>::type, ArrayBase<Array2i> >::value));
  typedef CwiseUnaryOp<internal::scalar_sum_op<double>, ArrayXd > Xpr;
  VERIFY((internal::is_same< internal::global_math_functions_filtering_base<Xpr>::type,
                           ArrayBase<Xpr>
                         >::value));
}
<commit_msg>Re-enabled the missing tests, again...<commit_after>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2008-2009 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "main.h"

template<typename ArrayType> void array(const ArrayType& m)
{
  typedef typename ArrayType::Index Index;
  typedef typename ArrayType::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;
  typedef Array<Scalar, ArrayType::RowsAtCompileTime, 1> ColVectorType;
  typedef Array<Scalar, 1, ArrayType::ColsAtCompileTime> RowVectorType;

  Index rows = m.rows();
  Index cols = m.cols();

  ArrayType m1 = ArrayType::Random(rows, cols),
             m2 = ArrayType::Random(rows, cols),
             m3(rows, cols);

  ColVectorType cv1 = ColVectorType::Random(rows);
  RowVectorType rv1 = RowVectorType::Random(cols);

  Scalar  s1 = internal::random<Scalar>(),
          s2 = internal::random<Scalar>();

  // scalar addition
  VERIFY_IS_APPROX(m1 + s1, s1 + m1);
  VERIFY_IS_APPROX(m1 + s1, ArrayType::Constant(rows,cols,s1) + m1);
  VERIFY_IS_APPROX(s1 - m1, (-m1)+s1 );
  VERIFY_IS_APPROX(m1 - s1, m1 - ArrayType::Constant(rows,cols,s1));
  VERIFY_IS_APPROX(s1 - m1, ArrayType::Constant(rows,cols,s1) - m1);
  VERIFY_IS_APPROX((m1*Scalar(2)) - s2, (m1+m1) - ArrayType::Constant(rows,cols,s2) );
  m3 = m1;
  m3 += s2;
  VERIFY_IS_APPROX(m3, m1 + s2);
  m3 = m1;
  m3 -= s1;
  VERIFY_IS_APPROX(m3, m1 - s1);

  // scalar operators via Maps
  m3 = m1;
  ArrayType::Map(m1.data(), m1.rows(), m1.cols()) -= ArrayType::Map(m2.data(), m2.rows(), m2.cols());
  VERIFY_IS_APPROX(m1, m3 - m2);

  m3 = m1;
  ArrayType::Map(m1.data(), m1.rows(), m1.cols()) += ArrayType::Map(m2.data(), m2.rows(), m2.cols());
  VERIFY_IS_APPROX(m1, m3 + m2);

  m3 = m1;
  ArrayType::Map(m1.data(), m1.rows(), m1.cols()) *= ArrayType::Map(m2.data(), m2.rows(), m2.cols());
  VERIFY_IS_APPROX(m1, m3 * m2);

  m3 = m1;
  m2 = ArrayType::Random(rows,cols);
  m2 = (m2==0).select(1,m2);
  ArrayType::Map(m1.data(), m1.rows(), m1.cols()) /= ArrayType::Map(m2.data(), m2.rows(), m2.cols());
  VERIFY_IS_APPROX(m1, m3 / m2);

  // reductions
  VERIFY_IS_APPROX(m1.colwise().sum().sum(), m1.sum());
  VERIFY_IS_APPROX(m1.rowwise().sum().sum(), m1.sum());
  if (!internal::isApprox(m1.sum(), (m1+m2).sum(), test_precision<Scalar>()))
      VERIFY_IS_NOT_APPROX(((m1+m2).rowwise().sum()).sum(), m1.sum());
  VERIFY_IS_APPROX(m1.colwise().sum(), m1.colwise().redux(internal::scalar_sum_op<Scalar>()));

  // vector-wise ops
  m3 = m1;
  VERIFY_IS_APPROX(m3.colwise() += cv1, m1.colwise() + cv1);
  m3 = m1;
  VERIFY_IS_APPROX(m3.colwise() -= cv1, m1.colwise() - cv1);
  m3 = m1;
  VERIFY_IS_APPROX(m3.rowwise() += rv1, m1.rowwise() + rv1);
  m3 = m1;
  VERIFY_IS_APPROX(m3.rowwise() -= rv1, m1.rowwise() - rv1);
}

template<typename ArrayType> void comparisons(const ArrayType& m)
{
  typedef typename ArrayType::Index Index;
  typedef typename ArrayType::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;
  typedef Array<Scalar, ArrayType::RowsAtCompileTime, 1> VectorType;

  Index rows = m.rows();
  Index cols = m.cols();

  Index r = internal::random<Index>(0, rows-1),
        c = internal::random<Index>(0, cols-1);

  ArrayType m1 = ArrayType::Random(rows, cols),
             m2 = ArrayType::Random(rows, cols),
             m3(rows, cols);

  VERIFY(((m1 + Scalar(1)) > m1).all());
  VERIFY(((m1 - Scalar(1)) < m1).all());
  if (rows*cols>1)
  {
    m3 = m1;
    m3(r,c) += 1;
    VERIFY(! (m1 < m3).all() );
    VERIFY(! (m1 > m3).all() );
  }

  // comparisons to scalar
  VERIFY( (m1 != (m1(r,c)+1) ).any() );
  VERIFY( (m1 > (m1(r,c)-1) ).any() );
  VERIFY( (m1 < (m1(r,c)+1) ).any() );
  VERIFY( (m1 == m1(r,c) ).any() );

  // test Select
  VERIFY_IS_APPROX( (m1<m2).select(m1,m2), m1.cwiseMin(m2) );
  VERIFY_IS_APPROX( (m1>m2).select(m1,m2), m1.cwiseMax(m2) );
  Scalar mid = (m1.cwiseAbs().minCoeff() + m1.cwiseAbs().maxCoeff())/Scalar(2);
  for (int j=0; j<cols; ++j)
  for (int i=0; i<rows; ++i)
    m3(i,j) = internal::abs(m1(i,j))<mid ? 0 : m1(i,j);
  VERIFY_IS_APPROX( (m1.abs()<ArrayType::Constant(rows,cols,mid))
                        .select(ArrayType::Zero(rows,cols),m1), m3);
  // shorter versions:
  VERIFY_IS_APPROX( (m1.abs()<ArrayType::Constant(rows,cols,mid))
                        .select(0,m1), m3);
  VERIFY_IS_APPROX( (m1.abs()>=ArrayType::Constant(rows,cols,mid))
                        .select(m1,0), m3);
  // even shorter version:
  VERIFY_IS_APPROX( (m1.abs()<mid).select(0,m1), m3);

  // count
  VERIFY(((m1.abs()+1)>RealScalar(0.1)).count() == rows*cols);

  typedef Array<typename ArrayType::Index, Dynamic, 1> ArrayOfIndices;

  // TODO allows colwise/rowwise for array
  VERIFY_IS_APPROX(((m1.abs()+1)>RealScalar(0.1)).colwise().count(), ArrayOfIndices::Constant(cols,rows).transpose());
  VERIFY_IS_APPROX(((m1.abs()+1)>RealScalar(0.1)).rowwise().count(), ArrayOfIndices::Constant(rows, cols));
}

template<typename ArrayType> void array_real(const ArrayType& m)
{
  typedef typename ArrayType::Index Index;
  typedef typename ArrayType::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;

  Index rows = m.rows();
  Index cols = m.cols();

  ArrayType m1 = ArrayType::Random(rows, cols),
             m2 = ArrayType::Random(rows, cols),
             m3(rows, cols);

  VERIFY_IS_APPROX(m1.sin(), std::sin(m1));
  VERIFY_IS_APPROX(m1.sin(), internal::sin(m1));
  VERIFY_IS_APPROX(m1.cos(), std::cos(m1));
  VERIFY_IS_APPROX(m1.cos(), internal::cos(m1));

  VERIFY_IS_APPROX(internal::cos(m1+RealScalar(3)*m2), internal::cos((m1+RealScalar(3)*m2).eval()));
  VERIFY_IS_APPROX(std::cos(m1+RealScalar(3)*m2), std::cos((m1+RealScalar(3)*m2).eval()));

  VERIFY_IS_APPROX(m1.abs().sqrt(), std::sqrt(std::abs(m1)));
  VERIFY_IS_APPROX(m1.abs().sqrt(), internal::sqrt(internal::abs(m1)));
  VERIFY_IS_APPROX(m1.abs(), internal::sqrt(internal::abs2(m1)));

  VERIFY_IS_APPROX(internal::abs2(internal::real(m1)) + internal::abs2(internal::imag(m1)), internal::abs2(m1));
  VERIFY_IS_APPROX(internal::abs2(std::real(m1)) + internal::abs2(std::imag(m1)), internal::abs2(m1));
  if(!NumTraits<Scalar>::IsComplex)
    VERIFY_IS_APPROX(internal::real(m1), m1);

  VERIFY_IS_APPROX(m1.abs().log(), std::log(std::abs(m1)));
  VERIFY_IS_APPROX(m1.abs().log(), internal::log(internal::abs(m1)));

  VERIFY_IS_APPROX(m1.exp(), std::exp(m1));
  VERIFY_IS_APPROX(m1.exp() * m2.exp(), std::exp(m1+m2));
  VERIFY_IS_APPROX(m1.exp(), internal::exp(m1));
  VERIFY_IS_APPROX(m1.exp() / m2.exp(), std::exp(m1-m2));

  VERIFY_IS_APPROX(m1.pow(2), m1.square());
  VERIFY_IS_APPROX(std::pow(m1,2), m1.square());
  m3 = m1.abs();
  VERIFY_IS_APPROX(m3.pow(RealScalar(0.5)), m3.sqrt());
  VERIFY_IS_APPROX(std::pow(m3,RealScalar(0.5)), m3.sqrt());
}

void test_array()
{
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( array(Array<float, 1, 1>()) );
    CALL_SUBTEST_2( array(Array22f()) );
    CALL_SUBTEST_3( array(Array44d()) );
    CALL_SUBTEST_4( array(ArrayXXcf(3, 3)) );
    CALL_SUBTEST_5( array(ArrayXXf(8, 12)) );
    CALL_SUBTEST_6( array(ArrayXXi(8, 12)) );
  }
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( comparisons(Array<float, 1, 1>()) );
    CALL_SUBTEST_2( comparisons(Array22f()) );
    CALL_SUBTEST_3( comparisons(Array44d()) );
    CALL_SUBTEST_5( comparisons(ArrayXXf(8, 12)) );
    CALL_SUBTEST_6( comparisons(ArrayXXi(8, 12)) );
  }
  for(int i = 0; i < g_repeat; i++) {
    CALL_SUBTEST_1( array_real(Array<float, 1, 1>()) );
    CALL_SUBTEST_2( array_real(Array22f()) );
    CALL_SUBTEST_3( array_real(Array44d()) );
    CALL_SUBTEST_5( array_real(ArrayXXf(8, 12)) );
  }

  VERIFY((internal::is_same< internal::global_math_functions_filtering_base<int>::type, int >::value));
  VERIFY((internal::is_same< internal::global_math_functions_filtering_base<float>::type, float >::value));
  VERIFY((internal::is_same< internal::global_math_functions_filtering_base<Array2i>::type, ArrayBase<Array2i> >::value));
  typedef CwiseUnaryOp<internal::scalar_sum_op<double>, ArrayXd > Xpr;
  VERIFY((internal::is_same< internal::global_math_functions_filtering_base<Xpr>::type,
                           ArrayBase<Xpr>
                         >::value));
}
<|endoftext|>


<commit_before>
#include <gtest/gtest.h>
#include <arrayfire.h>
#include <testHelpers.hpp>

using namespace af;
using namespace std;

template<typename T>
class Array : public ::testing::Test
{

};

typedef ::testing::Types<float, double, af::cfloat, af::cdouble, char, unsigned char, int, uint, intl, uintl> TestTypes;
TYPED_TEST_CASE(Array, TestTypes);

TEST(Array, ConstructorDefault)
{
    array a;
    EXPECT_EQ(0,    a.numdims());
    EXPECT_EQ(0,    a.dims(0));
    EXPECT_EQ(0,    a.dims(1));
    EXPECT_EQ(0,    a.dims(2));
    EXPECT_EQ(0,    a.dims(3));
    EXPECT_EQ(0,    a.elements());
    EXPECT_EQ(f32,  a.type());
    EXPECT_EQ(0,    a.bytes());
    EXPECT_FALSE(   a.isrow());
    EXPECT_FALSE(   a.iscomplex());
    EXPECT_FALSE(   a.isdouble());
    EXPECT_FALSE(   a.isbool());

    EXPECT_FALSE(    a.isvector());
    EXPECT_FALSE(    a.iscolumn());

    EXPECT_TRUE(    a.isreal());
    EXPECT_TRUE(    a.isempty());
    EXPECT_TRUE(    a.issingle());
    EXPECT_TRUE(    a.isfloating());
    EXPECT_TRUE(    a.isrealfloating());
}

TYPED_TEST(Array, ConstructorEmptyDim4)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    dim4 dims(3, 3, 3, 3);
    array a(dims, type);
    EXPECT_EQ(4,    a.numdims());
    EXPECT_EQ(3,    a.dims(0));
    EXPECT_EQ(3,    a.dims(1));
    EXPECT_EQ(3,    a.dims(2));
    EXPECT_EQ(3,    a.dims(3));
    EXPECT_EQ(81,   a.elements());
    EXPECT_EQ(type,  a.type());
}

TYPED_TEST(Array, ConstructorEmpty1D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array a(2, type);
    EXPECT_EQ(1,    a.numdims());
    EXPECT_EQ(2,    a.dims(0));
    EXPECT_EQ(1,    a.dims(1));
    EXPECT_EQ(1,    a.dims(2));
    EXPECT_EQ(1,    a.dims(3));
    EXPECT_EQ(2,    a.elements());
    EXPECT_EQ(type,  a.type());
}

TYPED_TEST(Array, ConstructorEmpty2D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array a(2, 2, type);
    EXPECT_EQ(2,    a.numdims());
    EXPECT_EQ(2,    a.dims(0));
    EXPECT_EQ(2,    a.dims(1));
    EXPECT_EQ(1,    a.dims(2));
    EXPECT_EQ(1,    a.dims(3));
    EXPECT_EQ(4,    a.elements());
    EXPECT_EQ(type,  a.type());
}

TYPED_TEST(Array, ConstructorEmpty3D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array a(2, 2, 2, type);
    EXPECT_EQ(3,    a.numdims());
    EXPECT_EQ(2,    a.dims(0));
    EXPECT_EQ(2,    a.dims(1));
    EXPECT_EQ(2,    a.dims(2));
    EXPECT_EQ(1,    a.dims(3));
    EXPECT_EQ(8,    a.elements());
    EXPECT_EQ(type,  a.type());
}

TYPED_TEST(Array, ConstructorEmpty4D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array a(2, 2, 2, 2, type);
    EXPECT_EQ(4,    a.numdims());
    EXPECT_EQ(2,    a.dims(0));
    EXPECT_EQ(2,    a.dims(1));
    EXPECT_EQ(2,    a.dims(2));
    EXPECT_EQ(2,    a.dims(3));
    EXPECT_EQ(16,   a.elements());
    EXPECT_EQ(type, a.type());
}

TYPED_TEST(Array, ConstructorHostPointer1D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    size_t nelems = 10;
    vector<TypeParam> data(nelems, 4);
    array a(nelems, &data.front(), af::afHost);
    EXPECT_EQ(1,        a.numdims());
    EXPECT_EQ(nelems,   a.dims(0));
    EXPECT_EQ(1,        a.dims(1));
    EXPECT_EQ(1,        a.dims(2));
    EXPECT_EQ(1,        a.dims(3));
    EXPECT_EQ(nelems,   a.elements());
    EXPECT_EQ(type,     a.type());

    vector<TypeParam> out(nelems);
    a.host(&out.front());
    ASSERT_TRUE(std::equal(data.begin(), data.end(), out.begin()));
}

TYPED_TEST(Array, ConstructorHostPointer2D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    size_t ndims    = 2;
    size_t dim_size = 10;
    size_t nelems   = dim_size * dim_size;
    vector<TypeParam> data(nelems, 4);
    array a(dim_size, dim_size, &data.front(), af::afHost);
    EXPECT_EQ(ndims,    a.numdims());
    EXPECT_EQ(dim_size, a.dims(0));
    EXPECT_EQ(dim_size, a.dims(1));
    EXPECT_EQ(1,        a.dims(2));
    EXPECT_EQ(1,        a.dims(3));
    EXPECT_EQ(nelems,   a.elements());
    EXPECT_EQ(type,     a.type());

    vector<TypeParam> out(nelems);
    a.host(&out.front());
    ASSERT_TRUE(std::equal(data.begin(), data.end(), out.begin()));
}

TYPED_TEST(Array, ConstructorHostPointer3D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    size_t ndims    = 3;
    size_t dim_size = 10;
    size_t nelems   = dim_size * dim_size * dim_size;
    vector<TypeParam> data(nelems, 4);
    array a(dim_size, dim_size, dim_size, &data.front(), af::afHost);
    EXPECT_EQ(ndims,    a.numdims());
    EXPECT_EQ(dim_size, a.dims(0));
    EXPECT_EQ(dim_size, a.dims(1));
    EXPECT_EQ(dim_size, a.dims(2));
    EXPECT_EQ(1,        a.dims(3));
    EXPECT_EQ(nelems,   a.elements());
    EXPECT_EQ(type,     a.type());

    vector<TypeParam> out(nelems);
    a.host(&out.front());
    ASSERT_TRUE(std::equal(data.begin(), data.end(), out.begin()));
}

TYPED_TEST(Array, ConstructorHostPointer4D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    size_t ndims    = 4;
    size_t dim_size = 10;
    size_t nelems   = dim_size * dim_size * dim_size * dim_size;
    vector<TypeParam> data(nelems, 4);
    array a(dim_size, dim_size, dim_size, dim_size, &data.front(), af::afHost);
    EXPECT_EQ(ndims,    a.numdims());
    EXPECT_EQ(dim_size, a.dims(0));
    EXPECT_EQ(dim_size, a.dims(1));
    EXPECT_EQ(dim_size, a.dims(2));
    EXPECT_EQ(dim_size, a.dims(3));
    EXPECT_EQ(nelems,   a.elements());
    EXPECT_EQ(type,     a.type());

    vector<TypeParam> out(nelems);
    a.host(&out.front());
    ASSERT_TRUE(std::equal(data.begin(), data.end(), out.begin()));
}

TYPED_TEST(Array, TypeAttributes)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array one(10, type);
    switch(type) {
        case f32:
            EXPECT_TRUE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_TRUE(one.issingle());
            EXPECT_TRUE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;

        case f64:
            EXPECT_TRUE(one.isfloating());
            EXPECT_TRUE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_TRUE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case c32:
            EXPECT_TRUE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_TRUE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_FALSE(one.isreal());
            EXPECT_TRUE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case c64:
            EXPECT_TRUE(one.isfloating());
            EXPECT_TRUE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_FALSE(one.isreal());
            EXPECT_TRUE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case s32:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case u32:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case u8:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case b8:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_TRUE(one.isbool());
            break;
        case s64:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case u64:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;

    }

}

TEST(Array, ShapeAttributes)
{
    dim_type dim_size = 10;
    array scalar(1);
    array col(dim_size);
    array row(1, dim_size);
    array matrix(dim_size, dim_size);
    array volume(dim_size, dim_size, dim_size);
    array hypercube(dim_size, dim_size, dim_size, dim_size);

    EXPECT_FALSE(scalar.    isempty());
    EXPECT_FALSE(col.       isempty());
    EXPECT_FALSE(row.       isempty());
    EXPECT_FALSE(matrix.    isempty());
    EXPECT_FALSE(volume.    isempty());
    EXPECT_FALSE(hypercube. isempty());

    EXPECT_TRUE(scalar.     isscalar());
    EXPECT_FALSE(col.       isscalar());
    EXPECT_FALSE(row.       isscalar());
    EXPECT_FALSE(matrix.    isscalar());
    EXPECT_FALSE(volume.    isscalar());
    EXPECT_FALSE(hypercube. isscalar());

    EXPECT_FALSE(scalar.    isvector());
    EXPECT_TRUE(col.        isvector());
    EXPECT_TRUE(row.        isvector());
    EXPECT_FALSE(matrix.    isvector());
    EXPECT_FALSE(volume.    isvector());
    EXPECT_FALSE(hypercube. isvector());

    EXPECT_FALSE(scalar.    isrow());
    EXPECT_FALSE(col.       isrow());
    EXPECT_TRUE(row.        isrow());
    EXPECT_FALSE(matrix.    isrow());
    EXPECT_FALSE(volume.    isrow());
    EXPECT_FALSE(hypercube. isrow());

    EXPECT_FALSE(scalar.    iscolumn());
    EXPECT_TRUE(col.        iscolumn());
    EXPECT_FALSE(row.       iscolumn());
    EXPECT_FALSE(matrix.    iscolumn());
    EXPECT_FALSE(volume.    iscolumn());
    EXPECT_FALSE(hypercube. iscolumn());
}
<commit_msg>BUGFIX fixed signed-unsigned comparison warnings<commit_after>
#include <gtest/gtest.h>
#include <arrayfire.h>
#include <testHelpers.hpp>

using namespace af;
using namespace std;

template<typename T>
class Array : public ::testing::Test
{

};

typedef ::testing::Types<float, double, af::cfloat, af::cdouble, char, unsigned char, int, uint, intl, uintl> TestTypes;
TYPED_TEST_CASE(Array, TestTypes);

TEST(Array, ConstructorDefault)
{
    array a;
    EXPECT_EQ(0u,    a.numdims());
    EXPECT_EQ(dim_type(0),    a.dims(0));
    EXPECT_EQ(dim_type(0),    a.dims(1));
    EXPECT_EQ(dim_type(0),    a.dims(2));
    EXPECT_EQ(dim_type(0),    a.dims(3));
    EXPECT_EQ(dim_type(0),    a.elements());
    EXPECT_EQ(f32,  a.type());
    EXPECT_EQ(0u,    a.bytes());
    EXPECT_FALSE(   a.isrow());
    EXPECT_FALSE(   a.iscomplex());
    EXPECT_FALSE(   a.isdouble());
    EXPECT_FALSE(   a.isbool());

    EXPECT_FALSE(    a.isvector());
    EXPECT_FALSE(    a.iscolumn());

    EXPECT_TRUE(    a.isreal());
    EXPECT_TRUE(    a.isempty());
    EXPECT_TRUE(    a.issingle());
    EXPECT_TRUE(    a.isfloating());
    EXPECT_TRUE(    a.isrealfloating());
}

TYPED_TEST(Array, ConstructorEmptyDim4)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    dim4 dims(3, 3, 3, 3);
    array a(dims, type);
    EXPECT_EQ(4u,    a.numdims());
    EXPECT_EQ(dim_type(3),    a.dims(0));
    EXPECT_EQ(dim_type(3),    a.dims(1));
    EXPECT_EQ(dim_type(3),    a.dims(2));
    EXPECT_EQ(dim_type(3),    a.dims(3));
    EXPECT_EQ(dim_type(81),   a.elements());
    EXPECT_EQ(type,  a.type());
}

TYPED_TEST(Array, ConstructorEmpty1D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array a(2, type);
    EXPECT_EQ(1u,    a.numdims());
    EXPECT_EQ(dim_type(2),    a.dims(0));
    EXPECT_EQ(dim_type(1),    a.dims(1));
    EXPECT_EQ(dim_type(1),    a.dims(2));
    EXPECT_EQ(dim_type(1),    a.dims(3));
    EXPECT_EQ(dim_type(2),    a.elements());
    EXPECT_EQ(type,  a.type());
}

TYPED_TEST(Array, ConstructorEmpty2D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array a(2, 2, type);
    EXPECT_EQ(2u,    a.numdims());
    EXPECT_EQ(dim_type(2),    a.dims(0));
    EXPECT_EQ(dim_type(2),    a.dims(1));
    EXPECT_EQ(dim_type(1),    a.dims(2));
    EXPECT_EQ(dim_type(1),    a.dims(3));
    EXPECT_EQ(dim_type(4),    a.elements());
    EXPECT_EQ(type,  a.type());
}

TYPED_TEST(Array, ConstructorEmpty3D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array a(2, 2, 2, type);
    EXPECT_EQ(3u,    a.numdims());
    EXPECT_EQ(dim_type(2),    a.dims(0));
    EXPECT_EQ(dim_type(2),    a.dims(1));
    EXPECT_EQ(dim_type(2),    a.dims(2));
    EXPECT_EQ(dim_type(1),    a.dims(3));
    EXPECT_EQ(dim_type(8),    a.elements());
    EXPECT_EQ(type,  a.type());
}

TYPED_TEST(Array, ConstructorEmpty4D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array a(2, 2, 2, 2, type);
    EXPECT_EQ(4u,    a.numdims());
    EXPECT_EQ(dim_type(2),    a.dims(0));
    EXPECT_EQ(dim_type(2),    a.dims(1));
    EXPECT_EQ(dim_type(2),    a.dims(2));
    EXPECT_EQ(dim_type(2),    a.dims(3));
    EXPECT_EQ(dim_type(16),   a.elements());
    EXPECT_EQ(type, a.type());
}

TYPED_TEST(Array, ConstructorHostPointer1D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    size_t nelems = 10;
    vector<TypeParam> data(nelems, 4);
    array a(nelems, &data.front(), af::afHost);
    EXPECT_EQ(1u,        a.numdims());
    EXPECT_EQ(dim_type(nelems),   a.dims(0));
    EXPECT_EQ(dim_type(1),        a.dims(1));
    EXPECT_EQ(dim_type(1),        a.dims(2));
    EXPECT_EQ(dim_type(1),        a.dims(3));
    EXPECT_EQ(dim_type(nelems),   a.elements());
    EXPECT_EQ(type,     a.type());

    vector<TypeParam> out(nelems);
    a.host(&out.front());
    ASSERT_TRUE(std::equal(data.begin(), data.end(), out.begin()));
}

TYPED_TEST(Array, ConstructorHostPointer2D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    size_t ndims    = 2;
    size_t dim_size = 10;
    size_t nelems   = dim_size * dim_size;
    vector<TypeParam> data(nelems, 4);
    array a(dim_size, dim_size, &data.front(), af::afHost);
    EXPECT_EQ(ndims,    a.numdims());
    EXPECT_EQ(dim_type(dim_size), a.dims(0));
    EXPECT_EQ(dim_type(dim_size), a.dims(1));
    EXPECT_EQ(dim_type(1),        a.dims(2));
    EXPECT_EQ(dim_type(1),        a.dims(3));
    EXPECT_EQ(dim_type(nelems),   a.elements());
    EXPECT_EQ(type,     a.type());

    vector<TypeParam> out(nelems);
    a.host(&out.front());
    ASSERT_TRUE(std::equal(data.begin(), data.end(), out.begin()));
}

TYPED_TEST(Array, ConstructorHostPointer3D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    size_t ndims    = 3;
    size_t dim_size = 10;
    size_t nelems   = dim_size * dim_size * dim_size;
    vector<TypeParam> data(nelems, 4);
    array a(dim_size, dim_size, dim_size, &data.front(), af::afHost);
    EXPECT_EQ(ndims,    a.numdims());
    EXPECT_EQ(dim_type(dim_size), a.dims(0));
    EXPECT_EQ(dim_type(dim_size), a.dims(1));
    EXPECT_EQ(dim_type(dim_size), a.dims(2));
    EXPECT_EQ(dim_type(1),        a.dims(3));
    EXPECT_EQ(dim_type(nelems),   a.elements());
    EXPECT_EQ(type,     a.type());

    vector<TypeParam> out(nelems);
    a.host(&out.front());
    ASSERT_TRUE(std::equal(data.begin(), data.end(), out.begin()));
}

TYPED_TEST(Array, ConstructorHostPointer4D)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    size_t ndims    = 4;
    size_t dim_size = 10;
    size_t nelems   = dim_size * dim_size * dim_size * dim_size;
    vector<TypeParam> data(nelems, 4);
    array a(dim_size, dim_size, dim_size, dim_size, &data.front(), af::afHost);
    EXPECT_EQ(ndims,    a.numdims());
    EXPECT_EQ(dim_type(dim_size), a.dims(0));
    EXPECT_EQ(dim_type(dim_size), a.dims(1));
    EXPECT_EQ(dim_type(dim_size), a.dims(2));
    EXPECT_EQ(dim_type(dim_size), a.dims(3));
    EXPECT_EQ(dim_type(nelems),   a.elements());
    EXPECT_EQ(type,     a.type());

    vector<TypeParam> out(nelems);
    a.host(&out.front());
    ASSERT_TRUE(std::equal(data.begin(), data.end(), out.begin()));
}

TYPED_TEST(Array, TypeAttributes)
{
    if (noDoubleTests<TypeParam>()) return;

    dtype type = (dtype)af::dtype_traits<TypeParam>::af_type;
    array one(10, type);
    switch(type) {
        case f32:
            EXPECT_TRUE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_TRUE(one.issingle());
            EXPECT_TRUE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;

        case f64:
            EXPECT_TRUE(one.isfloating());
            EXPECT_TRUE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_TRUE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case c32:
            EXPECT_TRUE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_TRUE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_FALSE(one.isreal());
            EXPECT_TRUE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case c64:
            EXPECT_TRUE(one.isfloating());
            EXPECT_TRUE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_FALSE(one.isreal());
            EXPECT_TRUE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case s32:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case u32:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case u8:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case b8:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_FALSE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_TRUE(one.isbool());
            break;
        case s64:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;
        case u64:
            EXPECT_FALSE(one.isfloating());
            EXPECT_FALSE(one.isdouble());
            EXPECT_FALSE(one.issingle());
            EXPECT_FALSE(one.isrealfloating());
            EXPECT_TRUE(one.isinteger());
            EXPECT_TRUE(one.isreal());
            EXPECT_FALSE(one.iscomplex());
            EXPECT_FALSE(one.isbool());
            break;

    }

}

TEST(Array, ShapeAttributes)
{
    dim_type dim_size = 10;
    array scalar(1);
    array col(dim_size);
    array row(1, dim_size);
    array matrix(dim_size, dim_size);
    array volume(dim_size, dim_size, dim_size);
    array hypercube(dim_size, dim_size, dim_size, dim_size);

    EXPECT_FALSE(scalar.    isempty());
    EXPECT_FALSE(col.       isempty());
    EXPECT_FALSE(row.       isempty());
    EXPECT_FALSE(matrix.    isempty());
    EXPECT_FALSE(volume.    isempty());
    EXPECT_FALSE(hypercube. isempty());

    EXPECT_TRUE(scalar.     isscalar());
    EXPECT_FALSE(col.       isscalar());
    EXPECT_FALSE(row.       isscalar());
    EXPECT_FALSE(matrix.    isscalar());
    EXPECT_FALSE(volume.    isscalar());
    EXPECT_FALSE(hypercube. isscalar());

    EXPECT_FALSE(scalar.    isvector());
    EXPECT_TRUE(col.        isvector());
    EXPECT_TRUE(row.        isvector());
    EXPECT_FALSE(matrix.    isvector());
    EXPECT_FALSE(volume.    isvector());
    EXPECT_FALSE(hypercube. isvector());

    EXPECT_FALSE(scalar.    isrow());
    EXPECT_FALSE(col.       isrow());
    EXPECT_TRUE(row.        isrow());
    EXPECT_FALSE(matrix.    isrow());
    EXPECT_FALSE(volume.    isrow());
    EXPECT_FALSE(hypercube. isrow());

    EXPECT_FALSE(scalar.    iscolumn());
    EXPECT_TRUE(col.        iscolumn());
    EXPECT_FALSE(row.       iscolumn());
    EXPECT_FALSE(matrix.    iscolumn());
    EXPECT_FALSE(volume.    iscolumn());
    EXPECT_FALSE(hypercube. iscolumn());
}
<|endoftext|>


<commit_before>/* nobleNote, a note taking application
 * Copyright (C) 2012 Christian Metscher <hakaishi@web.de>,
                      Fabian Deuchler <Taiko000@gmail.com>

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.

 * nobleNote is licensed under the MIT, see `http://copyfree.org/licenses/mit/license.txt'.
 */

#include "trash.h"
#include <QSettings>
#include <QMessageBox>

Trash::Trash(QHash<QString,QStringList> *backupDataHash, QWidget *parent): QDialog(parent){
     setupUi(this);

     setAttribute(Qt::WA_DeleteOnClose);

     foreach(QString key, backupDataHash->keys())
     {
          QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
          item->setText(0,backupDataHash->value(key).first()); //title
          item->setData(0,Qt::UserRole,backupDataHash->value(key)); //title, path and content
     }

     treeWidget->sortByColumn(0,Qt::AscendingOrder);
     // TODO flickcharm here

     connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(showPreview()));
     connect(deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteBackup()));
     connect(restoreButton, SIGNAL(clicked(bool)), this, SLOT(restoreBackup()));
}

void Trash::restoreBackup()
{
     if(treeWidget->selectedItems().isEmpty())
       return;
     int i = 0;
     foreach(QTreeWidgetItem *item, treeWidget->selectedItems())
     {
          QStringList dataList = item->data(0,Qt::UserRole).toStringList();
          QString title = dataList.takeFirst();
          if(!QFile(dataList.first()).exists())
            return;
          else
          {
             if(!QDir(QSettings().value("root_path").toString()+"/restored notes").exists())
               QDir().mkpath(QSettings().value("root_path").toString()+"/restored notes");
             if(!QFile(dataList.first()).copy(QSettings().value("root_path").toString()+"/restored notes/"+title))
             {
                i++;
                QFile(dataList.first()).copy(QSettings().value("root_path").toString()+"/restored notes/"+title+" ("+QString::number(i)+")");
             }
          }
          delete item;
     }
}

void Trash::deleteBackup()
{
     if(treeWidget->selectedItems().isEmpty())
        return;

     QStringList files;
     QList<QTreeWidgetItem*> itemList = treeWidget->selectedItems();
     foreach(QTreeWidgetItem *item, itemList)
     {
          QStringList dataList = item->data(0,Qt::UserRole).toStringList();
          dataList.takeFirst(); //removing title from the list
          files << dataList.first();
     }

     if(QMessageBox::warning(this,tr("Deleting notes"),
          tr("Are you sure you want to permanently delete the selected notes?")
             ,QMessageBox::Yes | QMessageBox::Abort) != QMessageBox::Yes)
        return;

     foreach(QString file, files)
     {
          if(QFile(file).exists())
            QFile(file).remove();

          QString uuid = file;
          uuid.remove(QSettings().value("backup_dir_path").toString() + "/");
          QSettings().remove("Notes/" + QUuid(uuid).toString() + "_size");
          QSettings().remove("Notes/" + QUuid(uuid).toString() + "_cursor_position");
     }

     foreach(QTreeWidgetItem *item, itemList)
          delete item;
}

void Trash::showPreview()
{
     if(treeWidget->currentItem() == NULL) //Prevents program crush
     {
          textEdit->clear();
          return;
     }

     if(!treeWidget->currentItem()->isSelected())
     {
          if(treeWidget->selectedItems().count() != 1)
             textEdit->clear();
          else
             textEdit->setText(treeWidget->selectedItems().first()->data(0,Qt::UserRole).toStringList().last());
     }
     else
        textEdit->setText(treeWidget->currentItem()->data(0,Qt::UserRole).toStringList().last());
}
<commit_msg>fixed renaming of notes that are to be restored<commit_after>/* nobleNote, a note taking application
 * Copyright (C) 2012 Christian Metscher <hakaishi@web.de>,
                      Fabian Deuchler <Taiko000@gmail.com>

 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:

 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.

 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.

 * nobleNote is licensed under the MIT, see `http://copyfree.org/licenses/mit/license.txt'.
 */

#include "trash.h"
#include <QSettings>
#include <QMessageBox>

Trash::Trash(QHash<QString,QStringList> *backupDataHash, QWidget *parent): QDialog(parent){
     setupUi(this);

     setAttribute(Qt::WA_DeleteOnClose);

     foreach(QString key, backupDataHash->keys())
     {
          QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
          item->setText(0,backupDataHash->value(key).first()); //title
          item->setData(0,Qt::UserRole,backupDataHash->value(key)); //title, path and content
     }

     treeWidget->sortByColumn(0,Qt::AscendingOrder);
     // TODO flickcharm here

     connect(treeWidget, SIGNAL(itemSelectionChanged()), this, SLOT(showPreview()));
     connect(deleteButton, SIGNAL(clicked(bool)), this, SLOT(deleteBackup()));
     connect(restoreButton, SIGNAL(clicked(bool)), this, SLOT(restoreBackup()));
}

void Trash::restoreBackup()
{
     if(treeWidget->selectedItems().isEmpty())
       return;

     QString dir = QSettings().value("root_path").toString()+"/restored notes";
     if(!QDir(dir).exists())
       QDir().mkpath(dir);

     foreach(QTreeWidgetItem *item, treeWidget->selectedItems())
     {
          QStringList dataList = item->data(0,Qt::UserRole).toStringList();
          QString title = dataList.takeFirst();
          if(!QFile(dataList.first()).exists())
            return;
          else
          {
             QString filePath = dir+"/"+title;
             int i = 0;
             while(QFile::exists(filePath))
             {
                i++;
                filePath = dir+"/"+title+" ("+QString::number(i)+")";
             }
             QFile(dataList.first()).copy(filePath);
          }
          delete item;
     }
}

void Trash::deleteBackup()
{
     if(treeWidget->selectedItems().isEmpty())
        return;

     QStringList files;
     QList<QTreeWidgetItem*> itemList = treeWidget->selectedItems();
     foreach(QTreeWidgetItem *item, itemList)
     {
          QStringList dataList = item->data(0,Qt::UserRole).toStringList();
          dataList.takeFirst(); //removing title from the list
          files << dataList.first();
     }

     if(QMessageBox::warning(this,tr("Deleting notes"),
          tr("Are you sure you want to permanently delete the selected notes?")
             ,QMessageBox::Yes | QMessageBox::Abort) != QMessageBox::Yes)
        return;

     foreach(QString file, files)
     {
          if(QFile(file).exists())
            QFile(file).remove();

          QString uuid = file;
          uuid.remove(QSettings().value("backup_dir_path").toString() + "/");
          QSettings().remove("Notes/" + QUuid(uuid).toString() + "_size");
          QSettings().remove("Notes/" + QUuid(uuid).toString() + "_cursor_position");
     }

     foreach(QTreeWidgetItem *item, itemList)
          delete item;
}

void Trash::showPreview()
{
     if(treeWidget->currentItem() == NULL) //Prevents program crush
     {
          textEdit->clear();
          return;
     }

     if(!treeWidget->currentItem()->isSelected())
     {
          if(treeWidget->selectedItems().count() != 1)
             textEdit->clear();
          else
             textEdit->setText(treeWidget->selectedItems().first()->data(0,Qt::UserRole).toStringList().last());
     }
     else
        textEdit->setText(treeWidget->currentItem()->data(0,Qt::UserRole).toStringList().last());
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 - 2021, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY LOG OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef SDBUS_HPP_INCLUDE
#define SDBUS_HPP_INCLUDE

#include <memory>
#include <string>
#include <vector>

struct sd_bus;

namespace geopm
{
    class SDBusMessage;

    /// @brief Abstraction around sd_bus interface for calling methods
    ///
    /// A mock-able C++ interface wrapper around the sd_bus functions
    /// that initiate calls to GEOPM D-Bus methods.  The sd_bus
    /// functions are provided by the libsystemd.so library and
    /// defined int the systemd/sd-bus.h" header file.  The messages
    /// passed to and from these calls are abstracted by the
    /// SDBusMessage interface.  The SDBus method syntax reflects the
    /// syntax of the underlying sd_bus interface, and for further
    /// information look at the sd-bus(3) man page and links therein.
    class SDBus
    {
        public:
            SDBus() = default;
            virtual ~SDBus() = default;
            /// @brief Factory method for SDBus interface
            ///
            /// @return Unique pointer to an implementation of the
            ///         SDBus interface.
            static std::unique_ptr<SDBus> make_unique(void);
            /// @brief Wrapper for the sd_bus_call(3) function.
            ///
            /// Used to execute a GEOPM D-Bus API using an
            /// SDBusMessage created by the make_call_message()
            /// method.  This enables the user to update the message
            /// with complex data types like arrays and structs to
            /// pass into the method.
            ///
            /// One use case is to to pass lists of strings as inputs
            /// to GEOPM D-Bus APIs.  The SDBusMessage that is created
            /// by make_call_message() is updated using
            /// SDBusMessage::append_strings() prior to passing the
            /// SDBusMessage to the call() method.
            ///
            /// @param message [in] Complete SDBusMessage returned by
            ///                call to make_call_message().
            /// @return Reply message that resulted from the call.
            virtual std::shared_ptr<SDBusMessage> call(
                std::shared_ptr<SDBusMessage> message) = 0;
            /// @brief Wrapper for the sd_bus_call_method(3) function.
            ///
            /// Used to execute a GEOPM D-Bus API that takes no
            /// arguments.
            ///
            /// @param member [in] Name of the API from the
            ///               io.github.geopm interface.
            /// @return Reply message that resulted from the call.
            virtual std::shared_ptr<SDBusMessage> call_method(
                const std::string &member) = 0;
            /// @brief Wrapper for the sd_bus_call_method(3) function.
            ///
            /// Used to execute a GEOPM D-Bus API that takes three
            /// arguments with types (string, integer, integer).
            ///
            /// @param member [in] Name of the API from the
            ///               io.github.geopm interface.
            /// @param arg0 [in] First parameter to pass to the D-Bus
            ///             API.
            /// @param arg1 [in] Second parameter to pass to the D-Bus
            ///             API.
            /// @param arg2 [in] Third parameter to pass to the D-Bus
            ///             API.
            /// @return Reply message that resulted from the call.
            virtual std::shared_ptr<SDBusMessage> call_method(
                const std::string &member,
                const std::string &arg0,
                int arg1,
                int arg2) = 0;
            /// @brief Wrapper for the sd_bus_call_method(3) function.
            ///
            /// Used to execute a GEOPM D-Bus API that takes four
            /// arguments with types (string, integer, integer, double).
            ///
            /// @param member [in] Name of the API from the
            ///               io.github.geopm interface.
            /// @param arg0 [in] First parameter to pass to the D-Bus
            ///             API.
            /// @param arg1 [in] Second parameter to pass to the D-Bus
            ///             API.
            /// @param arg2 [in] Third parameter to pass to the D-Bus
            ///             API.
            /// @param arg2 [in] Fourth parameter to pass to the D-Bus
            ///             API.
            /// @return Reply message that resulted from the call.
            virtual std::shared_ptr<SDBusMessage> call_method(
                const std::string &member,
                const std::string &arg0,
                int arg1,
                int arg2,
                double arg3) = 0;
            /// @brief Wrapper for the
            ///        sd_bus_message_new_method_call(3) function
            ///
            /// This is used to create a SDBusMessage that can be
            /// passed to the call() method.  The user can append data
            /// to the message prior to passing the result to the
            /// call() method in order to send complex data types like
            /// arrays and structures.  The D-Bus API that will be
            /// called later is a parameter to this function.
            ///
            /// @param member [in] Name of the API from the
            ///               io.github.geopm interface.
            /// @return Complete message that can be passed to the
            ///         call() method.
            virtual std::shared_ptr<SDBusMessage> make_call_message(
                const std::string &member) = 0;
    };

    class SDBusImp : public SDBus
    {
        public:
            SDBusImp();
            virtual ~SDBusImp();
            std::shared_ptr<SDBusMessage> call(
                std::shared_ptr<SDBusMessage> message) override;
            std::shared_ptr<SDBusMessage> call_method(
                const std::string &member) override;
            std::shared_ptr<SDBusMessage> call_method(
                const std::string &member,
                const std::string &arg0,
                int arg1,
                int arg2) override;
            std::shared_ptr<SDBusMessage> call_method(
                const std::string &member,
                const std::string &arg0,
                int arg1,
                int arg2,
                double arg3) override;
            std::shared_ptr<SDBusMessage> make_call_message(
                const std::string &member) override;
        private:
            sd_bus *m_bus;
            const char * m_dbus_destination;
            const char * m_dbus_path;
            const char * m_dbus_interface;
            const uint64_t m_dbus_timeout_usec;
    };
}

#endif
<commit_msg>Fixup whitespace<commit_after>/*
 * Copyright (c) 2015 - 2021, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY LOG OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef SDBUS_HPP_INCLUDE
#define SDBUS_HPP_INCLUDE

#include <memory>
#include <string>
#include <vector>

struct sd_bus;

namespace geopm
{
    class SDBusMessage;

    /// @brief Abstraction around sd_bus interface for calling methods
    ///
    /// A mock-able C++ interface wrapper around the sd_bus functions
    /// that initiate calls to GEOPM D-Bus methods.  The sd_bus
    /// functions are provided by the libsystemd.so library and
    /// defined int the systemd/sd-bus.h" header file.  The messages
    /// passed to and from these calls are abstracted by the
    /// SDBusMessage interface.  The SDBus method syntax reflects the
    /// syntax of the underlying sd_bus interface, and for further
    /// information look at the sd-bus(3) man page and links therein.
    class SDBus
    {
        public:
            SDBus() = default;
            virtual ~SDBus() = default;
            /// @brief Factory method for SDBus interface
            ///
            /// @return Unique pointer to an implementation of the
            ///         SDBus interface.
            static std::unique_ptr<SDBus> make_unique(void);
            /// @brief Wrapper for the sd_bus_call(3) function.
            ///
            /// Used to execute a GEOPM D-Bus API using an
            /// SDBusMessage created by the make_call_message()
            /// method.  This enables the user to update the message
            /// with complex data types like arrays and structs to
            /// pass into the method.
            ///
            /// One use case is to to pass lists of strings as inputs
            /// to GEOPM D-Bus APIs.  The SDBusMessage that is created
            /// by make_call_message() is updated using
            /// SDBusMessage::append_strings() prior to passing the
            /// SDBusMessage to the call() method.
            ///
            /// @param message [in] Complete SDBusMessage returned by
            ///                call to make_call_message().
            /// @return Reply message that resulted from the call.
            virtual std::shared_ptr<SDBusMessage> call(
                std::shared_ptr<SDBusMessage> message) = 0;
            /// @brief Wrapper for the sd_bus_call_method(3) function.
            ///
            /// Used to execute a GEOPM D-Bus API that takes no
            /// arguments.
            ///
            /// @param member [in] Name of the API from the
            ///               io.github.geopm interface.
            /// @return Reply message that resulted from the call.
            virtual std::shared_ptr<SDBusMessage> call_method(
                const std::string &member) = 0;
            /// @brief Wrapper for the sd_bus_call_method(3) function.
            ///
            /// Used to execute a GEOPM D-Bus API that takes three
            /// arguments with types (string, integer, integer).
            ///
            /// @param member [in] Name of the API from the
            ///               io.github.geopm interface.
            /// @param arg0 [in] First parameter to pass to the D-Bus
            ///             API.
            /// @param arg1 [in] Second parameter to pass to the D-Bus
            ///             API.
            /// @param arg2 [in] Third parameter to pass to the D-Bus
            ///             API.
            /// @return Reply message that resulted from the call.
            virtual std::shared_ptr<SDBusMessage> call_method(
                const std::string &member,
                const std::string &arg0,
                int arg1,
                int arg2) = 0;
            /// @brief Wrapper for the sd_bus_call_method(3) function.
            ///
            /// Used to execute a GEOPM D-Bus API that takes four
            /// arguments with types (string, integer, integer, double).
            ///
            /// @param member [in] Name of the API from the
            ///               io.github.geopm interface.
            /// @param arg0 [in] First parameter to pass to the D-Bus
            ///             API.
            /// @param arg1 [in] Second parameter to pass to the D-Bus
            ///             API.
            /// @param arg2 [in] Third parameter to pass to the D-Bus
            ///             API.
            /// @param arg2 [in] Fourth parameter to pass to the D-Bus
            ///             API.
            /// @return Reply message that resulted from the call.
            virtual std::shared_ptr<SDBusMessage> call_method(
                const std::string &member,
                const std::string &arg0,
                int arg1,
                int arg2,
                double arg3) = 0;
            /// @brief Wrapper for the
            ///        sd_bus_message_new_method_call(3) function
            ///
            /// This is used to create a SDBusMessage that can be
            /// passed to the call() method.  The user can append data
            /// to the message prior to passing the result to the
            /// call() method in order to send complex data types like
            /// arrays and structures.  The D-Bus API that will be
            /// called later is a parameter to this function.
            ///
            /// @param member [in] Name of the API from the
            ///               io.github.geopm interface.
            /// @return Complete message that can be passed to the
            ///         call() method.
            virtual std::shared_ptr<SDBusMessage> make_call_message(
                const std::string &member) = 0;
    };

    class SDBusImp : public SDBus
    {
        public:
            SDBusImp();
            virtual ~SDBusImp();
            std::shared_ptr<SDBusMessage> call(
                std::shared_ptr<SDBusMessage> message) override;
            std::shared_ptr<SDBusMessage> call_method(
                const std::string &member) override;
            std::shared_ptr<SDBusMessage> call_method(
                const std::string &member,
                const std::string &arg0,
                int arg1,
                int arg2) override;
            std::shared_ptr<SDBusMessage> call_method(
                const std::string &member,
                const std::string &arg0,
                int arg1,
                int arg2,
                double arg3) override;
            std::shared_ptr<SDBusMessage> make_call_message(
                const std::string &member) override;
        private:
            sd_bus *m_bus;
            const char *m_dbus_destination;
            const char *m_dbus_path;
            const char *m_dbus_interface;
            const uint64_t m_dbus_timeout_usec;
    };
}

#endif
<|endoftext|>


<commit_before>//===-- xray_interface.cpp --------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of XRay, a dynamic runtime instrumentation system.
//
// Implementation of the API functions.
//
//===----------------------------------------------------------------------===//

#include "xray_interface_internal.h"

#include <cstdint>
#include <cstdio>
#include <errno.h>
#include <limits>
#include <sys/mman.h>

#include "sanitizer_common/sanitizer_common.h"
#include "xray_defs.h"

namespace __xray {

#if defined(__x86_64__)
// FIXME: The actual length is 11 bytes. Why was length 12 passed to mprotect()
// ?
static const int16_t cSledLength = 12;
#elif defined(__aarch64__)
static const int16_t cSledLength = 32;
#elif defined(__arm__)
static const int16_t cSledLength = 28;
#elif SANITIZER_MIPS32
static const int16_t cSledLength = 48;
#elif SANITIZER_MIPS64
static const int16_t cSledLength = 64;
#elif defined(__powerpc64__)
static const int16_t cSledLength = 8;
#else
#error "Unsupported CPU Architecture"
#endif /* CPU architecture */

// This is the function to call when we encounter the entry or exit sleds.
__sanitizer::atomic_uintptr_t XRayPatchedFunction{0};

// This is the function to call from the arg1-enabled sleds/trampolines.
__sanitizer::atomic_uintptr_t XRayArgLogger{0};

// This is the function to call when we encounter a custom event log call.
__sanitizer::atomic_uintptr_t XRayPatchedCustomEvent{0};

// MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo
// any successful mprotect(...) changes. This is used to make a page writeable
// and executable, and upon destruction if it was successful in doing so returns
// the page into a read-only and executable page.
//
// This is only used specifically for runtime-patching of the XRay
// instrumentation points. This assumes that the executable pages are originally
// read-and-execute only.
class MProtectHelper {
  void *PageAlignedAddr;
  std::size_t MProtectLen;
  bool MustCleanup;

public:
  explicit MProtectHelper(void *PageAlignedAddr,
                          std::size_t MProtectLen) XRAY_NEVER_INSTRUMENT
      : PageAlignedAddr(PageAlignedAddr),
        MProtectLen(MProtectLen),
        MustCleanup(false) {}

  int MakeWriteable() XRAY_NEVER_INSTRUMENT {
    auto R = mprotect(PageAlignedAddr, MProtectLen,
                      PROT_READ | PROT_WRITE | PROT_EXEC);
    if (R != -1)
      MustCleanup = true;
    return R;
  }

  ~MProtectHelper() XRAY_NEVER_INSTRUMENT {
    if (MustCleanup) {
      mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);
    }
  }
};

} // namespace __xray

extern __sanitizer::SpinMutex XRayInstrMapMutex;
extern __sanitizer::atomic_uint8_t XRayInitialized;
extern __xray::XRaySledMap XRayInstrMap;

int __xray_set_handler(void (*entry)(int32_t,
                                     XRayEntryType)) XRAY_NEVER_INSTRUMENT {
  if (__sanitizer::atomic_load(&XRayInitialized,
                               __sanitizer::memory_order_acquire)) {

    __sanitizer::atomic_store(&__xray::XRayPatchedFunction,
                              reinterpret_cast<uintptr_t>(entry),
                              __sanitizer::memory_order_release);
    return 1;
  }
  return 0;
}

int __xray_set_customevent_handler(void (*entry)(void *, size_t))
    XRAY_NEVER_INSTRUMENT {
  if (__sanitizer::atomic_load(&XRayInitialized,
                               __sanitizer::memory_order_acquire)) {
    __sanitizer::atomic_store(&__xray::XRayPatchedCustomEvent,
                              reinterpret_cast<uintptr_t>(entry),
                              __sanitizer::memory_order_release);
    return 1;
  }
  return 0;
}

int __xray_remove_handler() XRAY_NEVER_INSTRUMENT {
  return __xray_set_handler(nullptr);
}

int __xray_remove_customevent_handler() XRAY_NEVER_INSTRUMENT {
  return __xray_set_customevent_handler(nullptr);
}

__sanitizer::atomic_uint8_t XRayPatching{0};

using namespace __xray;

// FIXME: Figure out whether we can move this class to sanitizer_common instead
// as a generic "scope guard".
template <class Function> class CleanupInvoker {
  Function Fn;

public:
  explicit CleanupInvoker(Function Fn) XRAY_NEVER_INSTRUMENT : Fn(Fn) {}
  CleanupInvoker(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = default;
  CleanupInvoker(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = default;
  CleanupInvoker &
  operator=(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = delete;
  CleanupInvoker &operator=(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = delete;
  ~CleanupInvoker() XRAY_NEVER_INSTRUMENT { Fn(); }
};

template <class Function>
CleanupInvoker<Function> scopeCleanup(Function Fn) XRAY_NEVER_INSTRUMENT {
  return CleanupInvoker<Function>{Fn};
}

inline bool patchSled(const XRaySledEntry &Sled, bool Enable,
                      int32_t FuncId) XRAY_NEVER_INSTRUMENT {
  // While we're here, we should patch the nop sled. To do that we mprotect
  // the page containing the function to be writeable.
  const uint64_t PageSize = GetPageSizeCached();
  void *PageAlignedAddr =
      reinterpret_cast<void *>(Sled.Address & ~(PageSize - 1));
  std::size_t MProtectLen = (Sled.Address + cSledLength) -
                            reinterpret_cast<uint64_t>(PageAlignedAddr);
  MProtectHelper Protector(PageAlignedAddr, MProtectLen);
  if (Protector.MakeWriteable() == -1) {
    printf("Failed mprotect: %d\n", errno);
    return XRayPatchingStatus::FAILED;
  }

  bool Success = false;
  switch (Sled.Kind) {
  case XRayEntryType::ENTRY:
    Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_FunctionEntry);
    break;
  case XRayEntryType::EXIT:
    Success = patchFunctionExit(Enable, FuncId, Sled);
    break;
  case XRayEntryType::TAIL:
    Success = patchFunctionTailExit(Enable, FuncId, Sled);
    break;
  case XRayEntryType::LOG_ARGS_ENTRY:
    Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_ArgLoggerEntry);
    break;
  case XRayEntryType::CUSTOM_EVENT:
    Success = patchCustomEvent(Enable, FuncId, Sled);
    break;
  default:
    Report("Unsupported sled kind '%d' @%04x\n", Sled.Address, int(Sled.Kind));
    return false;
  }
  return Success;
}

// controlPatching implements the common internals of the patching/unpatching
// implementation. |Enable| defines whether we're enabling or disabling the
// runtime XRay instrumentation.
XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
  if (!__sanitizer::atomic_load(&XRayInitialized,
                                __sanitizer::memory_order_acquire))
    return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.

  uint8_t NotPatching = false;
  if (!__sanitizer::atomic_compare_exchange_strong(
          &XRayPatching, &NotPatching, true, __sanitizer::memory_order_acq_rel))
    return XRayPatchingStatus::ONGOING; // Already patching.

  uint8_t PatchingSuccess = false;
  auto XRayPatchingStatusResetter = scopeCleanup([&PatchingSuccess] {
    if (!PatchingSuccess)
      __sanitizer::atomic_store(&XRayPatching, false,
                                __sanitizer::memory_order_release);
  });

  // Step 1: Compute the function id, as a unique identifier per function in the
  // instrumentation map.
  XRaySledMap InstrMap;
  {
    __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
    InstrMap = XRayInstrMap;
  }
  if (InstrMap.Entries == 0)
    return XRayPatchingStatus::NOT_INITIALIZED;

  const uint64_t PageSize = GetPageSizeCached();
  if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
    Report("System page size is not a power of two: %lld\n", PageSize);
    return XRayPatchingStatus::FAILED;
  }

  uint32_t FuncId = 1;
  uint64_t CurFun = 0;
  for (std::size_t I = 0; I < InstrMap.Entries; I++) {
    auto Sled = InstrMap.Sleds[I];
    auto F = Sled.Function;
    if (CurFun == 0)
      CurFun = F;
    if (F != CurFun) {
      ++FuncId;
      CurFun = F;
    }
    patchSled(Sled, Enable, FuncId);
  }
  __sanitizer::atomic_store(&XRayPatching, false,
                            __sanitizer::memory_order_release);
  PatchingSuccess = true;
  return XRayPatchingStatus::SUCCESS;
}

XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT {
  return controlPatching(true);
}

XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT {
  return controlPatching(false);
}

XRayPatchingStatus patchFunction(int32_t FuncId,
                                 bool Enable) XRAY_NEVER_INSTRUMENT {
  if (!__sanitizer::atomic_load(&XRayInitialized,
                                __sanitizer::memory_order_acquire))
    return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.

  uint8_t NotPatching = false;
  if (!__sanitizer::atomic_compare_exchange_strong(
          &XRayPatching, &NotPatching, true, __sanitizer::memory_order_acq_rel))
    return XRayPatchingStatus::ONGOING; // Already patching.

  // Next, we look for the function index.
  XRaySledMap InstrMap;
  {
    __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
    InstrMap = XRayInstrMap;
  }

  // If we don't have an index, we can't patch individual functions.
  if (InstrMap.Functions == 0)
    return XRayPatchingStatus::NOT_INITIALIZED;

  // FuncId must be a positive number, less than the number of functions
  // instrumented.
  if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) {
    Report("Invalid function id provided: %d\n", FuncId);
    return XRayPatchingStatus::FAILED;
  }

  // Now we patch ths sleds for this specific function.
  auto SledRange = InstrMap.SledsIndex[FuncId - 1];
  auto *f = SledRange.Begin;
  auto *e = SledRange.End;

  bool SucceedOnce = false;
  while (f != e)
    SucceedOnce |= patchSled(*f++, Enable, FuncId);

  __sanitizer::atomic_store(&XRayPatching, false,
                            __sanitizer::memory_order_release);

  if (!SucceedOnce) {
    Report("Failed patching any sled for function '%d'.", FuncId);
    return XRayPatchingStatus::FAILED;
  }

  return XRayPatchingStatus::SUCCESS;
}

XRayPatchingStatus __xray_patch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
  return patchFunction(FuncId, true);
}

XRayPatchingStatus
__xray_unpatch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
  return patchFunction(FuncId, false);
}

int __xray_set_handler_arg1(void (*entry)(int32_t, XRayEntryType, uint64_t)) {
  if (!__sanitizer::atomic_load(&XRayInitialized,
                                __sanitizer::memory_order_acquire))
    return 0;

  // A relaxed write might not be visible even if the current thread gets
  // scheduled on a different CPU/NUMA node.  We need to wait for everyone to
  // have this handler installed for consistency of collected data across CPUs.
  __sanitizer::atomic_store(&XRayArgLogger, reinterpret_cast<uint64_t>(entry),
                            __sanitizer::memory_order_release);
  return 1;
}

int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); }

uintptr_t __xray_function_address(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
  __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
  if (FuncId <= 0 || static_cast<size_t>(FuncId) > XRayInstrMap.Functions)
    return 0;
  return XRayInstrMap.SledsIndex[FuncId - 1].Begin->Address
// On PPC, function entries are always aligned to 16 bytes. The beginning of a
// sled might be a local entry, which is always +8 based on the global entry.
// Always return the global entry.
#ifdef __PPC__
         & ~0xf
#endif
      ;
}

size_t __xray_max_function_id() XRAY_NEVER_INSTRUMENT {
  __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
  return XRayInstrMap.Functions;
}
<commit_msg>[XRay][compiler-rt] Return the pointer associated with the function instead of the sled<commit_after>//===-- xray_interface.cpp --------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of XRay, a dynamic runtime instrumentation system.
//
// Implementation of the API functions.
//
//===----------------------------------------------------------------------===//

#include "xray_interface_internal.h"

#include <cstdint>
#include <cstdio>
#include <errno.h>
#include <limits>
#include <sys/mman.h>

#include "sanitizer_common/sanitizer_common.h"
#include "xray_defs.h"

namespace __xray {

#if defined(__x86_64__)
// FIXME: The actual length is 11 bytes. Why was length 12 passed to mprotect()
// ?
static const int16_t cSledLength = 12;
#elif defined(__aarch64__)
static const int16_t cSledLength = 32;
#elif defined(__arm__)
static const int16_t cSledLength = 28;
#elif SANITIZER_MIPS32
static const int16_t cSledLength = 48;
#elif SANITIZER_MIPS64
static const int16_t cSledLength = 64;
#elif defined(__powerpc64__)
static const int16_t cSledLength = 8;
#else
#error "Unsupported CPU Architecture"
#endif /* CPU architecture */

// This is the function to call when we encounter the entry or exit sleds.
__sanitizer::atomic_uintptr_t XRayPatchedFunction{0};

// This is the function to call from the arg1-enabled sleds/trampolines.
__sanitizer::atomic_uintptr_t XRayArgLogger{0};

// This is the function to call when we encounter a custom event log call.
__sanitizer::atomic_uintptr_t XRayPatchedCustomEvent{0};

// MProtectHelper is an RAII wrapper for calls to mprotect(...) that will undo
// any successful mprotect(...) changes. This is used to make a page writeable
// and executable, and upon destruction if it was successful in doing so returns
// the page into a read-only and executable page.
//
// This is only used specifically for runtime-patching of the XRay
// instrumentation points. This assumes that the executable pages are originally
// read-and-execute only.
class MProtectHelper {
  void *PageAlignedAddr;
  std::size_t MProtectLen;
  bool MustCleanup;

public:
  explicit MProtectHelper(void *PageAlignedAddr,
                          std::size_t MProtectLen) XRAY_NEVER_INSTRUMENT
      : PageAlignedAddr(PageAlignedAddr),
        MProtectLen(MProtectLen),
        MustCleanup(false) {}

  int MakeWriteable() XRAY_NEVER_INSTRUMENT {
    auto R = mprotect(PageAlignedAddr, MProtectLen,
                      PROT_READ | PROT_WRITE | PROT_EXEC);
    if (R != -1)
      MustCleanup = true;
    return R;
  }

  ~MProtectHelper() XRAY_NEVER_INSTRUMENT {
    if (MustCleanup) {
      mprotect(PageAlignedAddr, MProtectLen, PROT_READ | PROT_EXEC);
    }
  }
};

} // namespace __xray

extern __sanitizer::SpinMutex XRayInstrMapMutex;
extern __sanitizer::atomic_uint8_t XRayInitialized;
extern __xray::XRaySledMap XRayInstrMap;

int __xray_set_handler(void (*entry)(int32_t,
                                     XRayEntryType)) XRAY_NEVER_INSTRUMENT {
  if (__sanitizer::atomic_load(&XRayInitialized,
                               __sanitizer::memory_order_acquire)) {

    __sanitizer::atomic_store(&__xray::XRayPatchedFunction,
                              reinterpret_cast<uintptr_t>(entry),
                              __sanitizer::memory_order_release);
    return 1;
  }
  return 0;
}

int __xray_set_customevent_handler(void (*entry)(void *, size_t))
    XRAY_NEVER_INSTRUMENT {
  if (__sanitizer::atomic_load(&XRayInitialized,
                               __sanitizer::memory_order_acquire)) {
    __sanitizer::atomic_store(&__xray::XRayPatchedCustomEvent,
                              reinterpret_cast<uintptr_t>(entry),
                              __sanitizer::memory_order_release);
    return 1;
  }
  return 0;
}

int __xray_remove_handler() XRAY_NEVER_INSTRUMENT {
  return __xray_set_handler(nullptr);
}

int __xray_remove_customevent_handler() XRAY_NEVER_INSTRUMENT {
  return __xray_set_customevent_handler(nullptr);
}

__sanitizer::atomic_uint8_t XRayPatching{0};

using namespace __xray;

// FIXME: Figure out whether we can move this class to sanitizer_common instead
// as a generic "scope guard".
template <class Function> class CleanupInvoker {
  Function Fn;

public:
  explicit CleanupInvoker(Function Fn) XRAY_NEVER_INSTRUMENT : Fn(Fn) {}
  CleanupInvoker(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = default;
  CleanupInvoker(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = default;
  CleanupInvoker &
  operator=(const CleanupInvoker &) XRAY_NEVER_INSTRUMENT = delete;
  CleanupInvoker &operator=(CleanupInvoker &&) XRAY_NEVER_INSTRUMENT = delete;
  ~CleanupInvoker() XRAY_NEVER_INSTRUMENT { Fn(); }
};

template <class Function>
CleanupInvoker<Function> scopeCleanup(Function Fn) XRAY_NEVER_INSTRUMENT {
  return CleanupInvoker<Function>{Fn};
}

inline bool patchSled(const XRaySledEntry &Sled, bool Enable,
                      int32_t FuncId) XRAY_NEVER_INSTRUMENT {
  // While we're here, we should patch the nop sled. To do that we mprotect
  // the page containing the function to be writeable.
  const uint64_t PageSize = GetPageSizeCached();
  void *PageAlignedAddr =
      reinterpret_cast<void *>(Sled.Address & ~(PageSize - 1));
  std::size_t MProtectLen = (Sled.Address + cSledLength) -
                            reinterpret_cast<uint64_t>(PageAlignedAddr);
  MProtectHelper Protector(PageAlignedAddr, MProtectLen);
  if (Protector.MakeWriteable() == -1) {
    printf("Failed mprotect: %d\n", errno);
    return XRayPatchingStatus::FAILED;
  }

  bool Success = false;
  switch (Sled.Kind) {
  case XRayEntryType::ENTRY:
    Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_FunctionEntry);
    break;
  case XRayEntryType::EXIT:
    Success = patchFunctionExit(Enable, FuncId, Sled);
    break;
  case XRayEntryType::TAIL:
    Success = patchFunctionTailExit(Enable, FuncId, Sled);
    break;
  case XRayEntryType::LOG_ARGS_ENTRY:
    Success = patchFunctionEntry(Enable, FuncId, Sled, __xray_ArgLoggerEntry);
    break;
  case XRayEntryType::CUSTOM_EVENT:
    Success = patchCustomEvent(Enable, FuncId, Sled);
    break;
  default:
    Report("Unsupported sled kind '%d' @%04x\n", Sled.Address, int(Sled.Kind));
    return false;
  }
  return Success;
}

// controlPatching implements the common internals of the patching/unpatching
// implementation. |Enable| defines whether we're enabling or disabling the
// runtime XRay instrumentation.
XRayPatchingStatus controlPatching(bool Enable) XRAY_NEVER_INSTRUMENT {
  if (!__sanitizer::atomic_load(&XRayInitialized,
                                __sanitizer::memory_order_acquire))
    return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.

  uint8_t NotPatching = false;
  if (!__sanitizer::atomic_compare_exchange_strong(
          &XRayPatching, &NotPatching, true, __sanitizer::memory_order_acq_rel))
    return XRayPatchingStatus::ONGOING; // Already patching.

  uint8_t PatchingSuccess = false;
  auto XRayPatchingStatusResetter = scopeCleanup([&PatchingSuccess] {
    if (!PatchingSuccess)
      __sanitizer::atomic_store(&XRayPatching, false,
                                __sanitizer::memory_order_release);
  });

  // Step 1: Compute the function id, as a unique identifier per function in the
  // instrumentation map.
  XRaySledMap InstrMap;
  {
    __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
    InstrMap = XRayInstrMap;
  }
  if (InstrMap.Entries == 0)
    return XRayPatchingStatus::NOT_INITIALIZED;

  const uint64_t PageSize = GetPageSizeCached();
  if ((PageSize == 0) || ((PageSize & (PageSize - 1)) != 0)) {
    Report("System page size is not a power of two: %lld\n", PageSize);
    return XRayPatchingStatus::FAILED;
  }

  uint32_t FuncId = 1;
  uint64_t CurFun = 0;
  for (std::size_t I = 0; I < InstrMap.Entries; I++) {
    auto Sled = InstrMap.Sleds[I];
    auto F = Sled.Function;
    if (CurFun == 0)
      CurFun = F;
    if (F != CurFun) {
      ++FuncId;
      CurFun = F;
    }
    patchSled(Sled, Enable, FuncId);
  }
  __sanitizer::atomic_store(&XRayPatching, false,
                            __sanitizer::memory_order_release);
  PatchingSuccess = true;
  return XRayPatchingStatus::SUCCESS;
}

XRayPatchingStatus __xray_patch() XRAY_NEVER_INSTRUMENT {
  return controlPatching(true);
}

XRayPatchingStatus __xray_unpatch() XRAY_NEVER_INSTRUMENT {
  return controlPatching(false);
}

XRayPatchingStatus patchFunction(int32_t FuncId,
                                 bool Enable) XRAY_NEVER_INSTRUMENT {
  if (!__sanitizer::atomic_load(&XRayInitialized,
                                __sanitizer::memory_order_acquire))
    return XRayPatchingStatus::NOT_INITIALIZED; // Not initialized.

  uint8_t NotPatching = false;
  if (!__sanitizer::atomic_compare_exchange_strong(
          &XRayPatching, &NotPatching, true, __sanitizer::memory_order_acq_rel))
    return XRayPatchingStatus::ONGOING; // Already patching.

  // Next, we look for the function index.
  XRaySledMap InstrMap;
  {
    __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
    InstrMap = XRayInstrMap;
  }

  // If we don't have an index, we can't patch individual functions.
  if (InstrMap.Functions == 0)
    return XRayPatchingStatus::NOT_INITIALIZED;

  // FuncId must be a positive number, less than the number of functions
  // instrumented.
  if (FuncId <= 0 || static_cast<size_t>(FuncId) > InstrMap.Functions) {
    Report("Invalid function id provided: %d\n", FuncId);
    return XRayPatchingStatus::FAILED;
  }

  // Now we patch ths sleds for this specific function.
  auto SledRange = InstrMap.SledsIndex[FuncId - 1];
  auto *f = SledRange.Begin;
  auto *e = SledRange.End;

  bool SucceedOnce = false;
  while (f != e)
    SucceedOnce |= patchSled(*f++, Enable, FuncId);

  __sanitizer::atomic_store(&XRayPatching, false,
                            __sanitizer::memory_order_release);

  if (!SucceedOnce) {
    Report("Failed patching any sled for function '%d'.", FuncId);
    return XRayPatchingStatus::FAILED;
  }

  return XRayPatchingStatus::SUCCESS;
}

XRayPatchingStatus __xray_patch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
  return patchFunction(FuncId, true);
}

XRayPatchingStatus
__xray_unpatch_function(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
  return patchFunction(FuncId, false);
}

int __xray_set_handler_arg1(void (*entry)(int32_t, XRayEntryType, uint64_t)) {
  if (!__sanitizer::atomic_load(&XRayInitialized,
                                __sanitizer::memory_order_acquire))
    return 0;

  // A relaxed write might not be visible even if the current thread gets
  // scheduled on a different CPU/NUMA node.  We need to wait for everyone to
  // have this handler installed for consistency of collected data across CPUs.
  __sanitizer::atomic_store(&XRayArgLogger, reinterpret_cast<uint64_t>(entry),
                            __sanitizer::memory_order_release);
  return 1;
}

int __xray_remove_handler_arg1() { return __xray_set_handler_arg1(nullptr); }

uintptr_t __xray_function_address(int32_t FuncId) XRAY_NEVER_INSTRUMENT {
  __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
  if (FuncId <= 0 || static_cast<size_t>(FuncId) > XRayInstrMap.Functions)
    return 0;
  return XRayInstrMap.SledsIndex[FuncId - 1].Begin->Function
// On PPC, function entries are always aligned to 16 bytes. The beginning of a
// sled might be a local entry, which is always +8 based on the global entry.
// Always return the global entry.
#ifdef __PPC__
         & ~0xf
#endif
      ;
}

size_t __xray_max_function_id() XRAY_NEVER_INSTRUMENT {
  __sanitizer::SpinMutexLock Guard(&XRayInstrMapMutex);
  return XRayInstrMap.Functions;
}
<|endoftext|>