<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>#pragma once

#include "console_user_server_client.hpp"
#include "constants.hpp"
#include "input_source_manager.hpp"
#include "local_datagram/server_manager.hpp"
#include "shell_utility.hpp"
#include "types.hpp"
#include <vector>

namespace krbn {
class receiver final {
public:
  // Signals

  boost::signals2::signal<void(void)> bound;
  boost::signals2::signal<void(const boost::system::error_code&)> bind_failed;
  boost::signals2::signal<void(void)> closed;

  // Methods

  receiver(const receiver&) = delete;

  receiver(void) : last_select_input_source_time_stamp_(0) {
    dispatcher_ = std::make_unique<thread_utility::dispatcher>();
  }

  void async_start(void) {
    dispatcher_->enqueue([this] {
      if (server_manager_) {
        return;
      }

      auto uid = getuid();
      auto socket_file_path = console_user_server_client::make_console_user_server_socket_file_path(uid);

      unlink(socket_file_path.c_str());

      size_t buffer_size = 32 * 1024;
      std::chrono::milliseconds server_check_interval(3000);
      std::chrono::milliseconds reconnect_interval(1000);

      server_manager_ = std::make_unique<local_datagram::server_manager>(socket_file_path,
                                                                         buffer_size,
                                                                         server_check_interval,
                                                                         reconnect_interval);

      server_manager_->bound.connect([this] {
        dispatcher_->enqueue([this] {
          bound();
        });
      });

      server_manager_->bind_failed.connect([this](auto&& error_code) {
        dispatcher_->enqueue([this, error_code] {
          bind_failed(error_code);
        });
      });

      server_manager_->closed.connect([this] {
        dispatcher_->enqueue([this] {
          closed();
        });
      });

      server_manager_->received.connect([this](auto&& buffer) {
        dispatcher_->enqueue([this, buffer] {
          if (auto type = types::find_operation_type(*buffer)) {
            switch (*type) {
              case operation_type::shell_command_execution:
                if (buffer->size() != sizeof(operation_type_shell_command_execution_struct)) {
                  logger::get_logger().error("invalid size for operation_type::shell_command_execution");
                } else {
                  auto p = reinterpret_cast<operation_type_shell_command_execution_struct*>(&((*buffer)[0]));

                  // Ensure shell_command is null-terminated string even if corrupted data is sent.
                  p->shell_command[sizeof(p->shell_command) - 1] = '\0';

                  std::string background_shell_command = shell_utility::make_background_command(p->shell_command);
                  system(background_shell_command.c_str());
                }
                break;

              case operation_type::select_input_source:
                if (buffer->size() != sizeof(operation_type_select_input_source_struct)) {
                  logger::get_logger().error("invalid size for operation_type::select_input_source");
                } else {
                  auto p = reinterpret_cast<operation_type_select_input_source_struct*>(&((*buffer)[0]));

                  // Ensure input_source_selector's strings are null-terminated string even if corrupted data is sent.
                  p->language[sizeof(p->language) - 1] = '\0';
                  p->input_source_id[sizeof(p->input_source_id) - 1] = '\0';
                  p->input_mode_id[sizeof(p->input_mode_id) - 1] = '\0';

                  uint64_t time_stamp = p->time_stamp;
                  boost::optional<std::string> language(std::string(p->language));
                  boost::optional<std::string> input_source_id(std::string(p->input_source_id));
                  boost::optional<std::string> input_mode_id(std::string(p->input_mode_id));
                  if (language && language->empty()) {
                    language = boost::none;
                  }
                  if (input_source_id && input_source_id->empty()) {
                    input_source_id = boost::none;
                  }
                  if (input_mode_id && input_mode_id->empty()) {
                    input_mode_id = boost::none;
                  }

                  input_source_selector input_source_selector(language,
                                                              input_source_id,
                                                              input_mode_id);

                  if (last_select_input_source_time_stamp_ == time_stamp) {
                    return;
                  }
                  if (input_source_manager_.select(input_source_selector)) {
                    last_select_input_source_time_stamp_ = time_stamp;
                  }
                }
                break;

              default:
                break;
            }
          }
        });
      });

      server_manager_->async_start();

      logger::get_logger().info("receiver is initialized");
    });
  }

  ~receiver(void) {
    dispatcher_->enqueue([this] {
      server_manager_ = nullptr;
    });

    dispatcher_->terminate();
    dispatcher_ = nullptr;

    logger::get_logger().info("receiver is terminated");
  }

private:
  std::unique_ptr<thread_utility::dispatcher> dispatcher_;

  std::unique_ptr<local_datagram::server_manager> server_manager_;
  input_source_manager input_source_manager_;
  uint64_t last_select_input_source_time_stamp_;
};
} // namespace krbn
<commit_msg>fix console_user_server<commit_after>#pragma once

#include "console_user_server_client.hpp"
#include "constants.hpp"
#include "input_source_manager.hpp"
#include "local_datagram/server_manager.hpp"
#include "shell_utility.hpp"
#include "types.hpp"
#include <vector>

namespace krbn {
class receiver final {
public:
  // Signals

  boost::signals2::signal<void(void)> bound;
  boost::signals2::signal<void(const boost::system::error_code&)> bind_failed;
  boost::signals2::signal<void(void)> closed;

  // Methods

  receiver(const receiver&) = delete;

  receiver(void) : last_select_input_source_time_stamp_(0) {
    dispatcher_ = std::make_unique<thread_utility::dispatcher>();
  }

  void async_start(void) {
    dispatcher_->enqueue([this] {
      if (server_manager_) {
        return;
      }

      auto uid = getuid();
      auto socket_file_path = console_user_server_client::make_console_user_server_socket_file_path(uid);

      unlink(socket_file_path.c_str());

      size_t buffer_size = 32 * 1024;
      std::chrono::milliseconds server_check_interval(3000);
      std::chrono::milliseconds reconnect_interval(1000);

      server_manager_ = std::make_unique<local_datagram::server_manager>(socket_file_path,
                                                                         buffer_size,
                                                                         server_check_interval,
                                                                         reconnect_interval);

      server_manager_->bound.connect([this] {
        dispatcher_->enqueue([this] {
          bound();
        });
      });

      server_manager_->bind_failed.connect([this](auto&& error_code) {
        dispatcher_->enqueue([this, error_code] {
          bind_failed(error_code);
        });
      });

      server_manager_->closed.connect([this] {
        dispatcher_->enqueue([this] {
          closed();
        });
      });

      server_manager_->received.connect([this](auto&& buffer) {
        dispatcher_->enqueue([this, buffer] {
          if (auto type = types::find_operation_type(*buffer)) {
            switch (*type) {
              case operation_type::shell_command_execution:
                if (buffer->size() != sizeof(operation_type_shell_command_execution_struct)) {
                  logger::get_logger().error("invalid size for operation_type::shell_command_execution");
                } else {
                  auto p = reinterpret_cast<operation_type_shell_command_execution_struct*>(&((*buffer)[0]));

                  // Ensure shell_command is null-terminated string even if corrupted data is sent.
                  p->shell_command[sizeof(p->shell_command) - 1] = '\0';

                  std::string background_shell_command = shell_utility::make_background_command(p->shell_command);
                  system(background_shell_command.c_str());
                }
                break;

              case operation_type::select_input_source:
                if (buffer->size() != sizeof(operation_type_select_input_source_struct)) {
                  logger::get_logger().error("invalid size for operation_type::select_input_source");
                } else {
                  auto p = reinterpret_cast<operation_type_select_input_source_struct*>(&((*buffer)[0]));

                  // Ensure input_source_selector's strings are null-terminated string even if corrupted data is sent.
                  p->language[sizeof(p->language) - 1] = '\0';
                  p->input_source_id[sizeof(p->input_source_id) - 1] = '\0';
                  p->input_mode_id[sizeof(p->input_mode_id) - 1] = '\0';

                  auto time_stamp = p->time_stamp;
                  boost::optional<std::string> language(std::string(p->language));
                  boost::optional<std::string> input_source_id(std::string(p->input_source_id));
                  boost::optional<std::string> input_mode_id(std::string(p->input_mode_id));
                  if (language && language->empty()) {
                    language = boost::none;
                  }
                  if (input_source_id && input_source_id->empty()) {
                    input_source_id = boost::none;
                  }
                  if (input_mode_id && input_mode_id->empty()) {
                    input_mode_id = boost::none;
                  }

                  input_source_selector input_source_selector(language,
                                                              input_source_id,
                                                              input_mode_id);

                  if (last_select_input_source_time_stamp_ == time_stamp) {
                    return;
                  }
                  if (input_source_manager_.select(input_source_selector)) {
                    last_select_input_source_time_stamp_ = time_stamp;
                  }
                }
                break;

              default:
                break;
            }
          }
        });
      });

      server_manager_->async_start();

      logger::get_logger().info("receiver is initialized");
    });
  }

  ~receiver(void) {
    dispatcher_->enqueue([this] {
      server_manager_ = nullptr;
    });

    dispatcher_->terminate();
    dispatcher_ = nullptr;

    logger::get_logger().info("receiver is terminated");
  }

private:
  std::unique_ptr<thread_utility::dispatcher> dispatcher_;

  std::unique_ptr<local_datagram::server_manager> server_manager_;
  input_source_manager input_source_manager_;
  absolute_time last_select_input_source_time_stamp_;
};
} // namespace krbn
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2015 gRPC authors.
 *
 * 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 "src/core/ext/filters/client_channel/uri_parser.h"

#include <string.h>

#include <grpc/slice_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
#include <grpc/support/string_util.h>

#include "src/core/lib/slice/percent_encoding.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/slice/slice_string_helpers.h"
#include "src/core/lib/support/string.h"

/** a size_t default value... maps to all 1's */
#define NOT_SET (~(size_t)0)

static grpc_uri* bad_uri(const char* uri_text, size_t pos, const char* section,
                         bool suppress_errors) {
  char* line_prefix;
  size_t pfx_len;

  if (!suppress_errors) {
    gpr_asprintf(&line_prefix, "bad uri.%s: '", section);
    pfx_len = strlen(line_prefix) + pos;
    gpr_log(GPR_ERROR, "%s%s'", line_prefix, uri_text);
    gpr_free(line_prefix);

    line_prefix = (char*)gpr_malloc(pfx_len + 1);
    memset(line_prefix, ' ', pfx_len);
    line_prefix[pfx_len] = 0;
    gpr_log(GPR_ERROR, "%s^ here", line_prefix);
    gpr_free(line_prefix);
  }

  return NULL;
}

/** Returns a copy of percent decoded \a src[begin, end) */
static char* decode_and_copy_component(grpc_exec_ctx* exec_ctx, const char* src,
                                       size_t begin, size_t end) {
  grpc_slice component =
      grpc_slice_from_copied_buffer(src + begin, end - begin);
  grpc_slice decoded_component =
      grpc_permissive_percent_decode_slice(component);
  char* out = grpc_dump_slice(decoded_component, GPR_DUMP_ASCII);
  grpc_slice_unref_internal(exec_ctx, component);
  grpc_slice_unref_internal(exec_ctx, decoded_component);
  return out;
}

static bool valid_hex(char c) {
  return ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) ||
         ((c >= '0') && (c <= '9'));
}

/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar
 * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent
 * sign not followed by two hex digits), NOT_SET is returned. */
static size_t parse_pchar(const char* uri_text, size_t i) {
  /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
   * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
   * pct-encoded = "%" HEXDIG HEXDIG
   * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
   / "*" / "+" / "," / ";" / "=" */
  char c = uri_text[i];
  switch (c) {
    default:
      if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ||
          ((c >= '0') && (c <= '9'))) {
        return 1;
      }
      break;
    case ':':
    case '@':
    case '-':
    case '.':
    case '_':
    case '~':
    case '!':
    case '$':
    case '&':
    case '\'':
    case '(':
    case ')':
    case '*':
    case '+':
    case ',':
    case ';':
    case '=':
      return 1;
    case '%': /* pct-encoded */
      if (valid_hex(uri_text[i + 1]) && valid_hex(uri_text[i + 2])) {
        return 2;
      }
      return NOT_SET;
  }
  return 0;
}

/* *( pchar / "?" / "/" ) */
static int parse_fragment_or_query(const char* uri_text, size_t* i) {
  char c;
  while ((c = uri_text[*i]) != 0) {
    const size_t advance = parse_pchar(uri_text, *i); /* pchar */
    switch (advance) {
      case 0: /* uri_text[i] isn't in pchar */
        /* maybe it's ? or / */
        if (uri_text[*i] == '?' || uri_text[*i] == '/') {
          (*i)++;
          break;
        } else {
          return 1;
        }
        GPR_UNREACHABLE_CODE(return 0);
      default:
        (*i) += advance;
        break;
      case NOT_SET: /* uri_text[i] introduces an invalid URI */
        return 0;
    }
  }
  /* *i is the first uri_text position past the \a query production, maybe \0 */
  return 1;
}

static void parse_query_parts(grpc_uri* uri) {
  static const char* QUERY_PARTS_SEPARATOR = "&";
  static const char* QUERY_PARTS_VALUE_SEPARATOR = "=";
  GPR_ASSERT(uri->query != NULL);
  if (uri->query[0] == '\0') {
    uri->query_parts = NULL;
    uri->query_parts_values = NULL;
    uri->num_query_parts = 0;
    return;
  }

  gpr_string_split(uri->query, QUERY_PARTS_SEPARATOR, &uri->query_parts,
                   &uri->num_query_parts);
  uri->query_parts_values =
      (char**)gpr_malloc(uri->num_query_parts * sizeof(char**));
  for (size_t i = 0; i < uri->num_query_parts; i++) {
    char** query_param_parts;
    size_t num_query_param_parts;
    char* full = uri->query_parts[i];
    gpr_string_split(full, QUERY_PARTS_VALUE_SEPARATOR, &query_param_parts,
                     &num_query_param_parts);
    GPR_ASSERT(num_query_param_parts > 0);
    uri->query_parts[i] = query_param_parts[0];
    if (num_query_param_parts > 1) {
      /* TODO(dgq): only the first value after the separator is considered.
       * Perhaps all chars after the first separator for the query part should
       * be included, even if they include the separator. */
      uri->query_parts_values[i] = query_param_parts[1];
    } else {
      uri->query_parts_values[i] = NULL;
    }
    for (size_t j = 2; j < num_query_param_parts; j++) {
      gpr_free(query_param_parts[j]);
    }
    gpr_free(query_param_parts);
    gpr_free(full);
  }
}

grpc_uri* grpc_uri_parse(grpc_exec_ctx* exec_ctx, const char* uri_text,
                         bool suppress_errors) {
  grpc_uri* uri;
  size_t scheme_begin = 0;
  size_t scheme_end = NOT_SET;
  size_t authority_begin = NOT_SET;
  size_t authority_end = NOT_SET;
  size_t path_begin = NOT_SET;
  size_t path_end = NOT_SET;
  size_t query_begin = NOT_SET;
  size_t query_end = NOT_SET;
  size_t fragment_begin = NOT_SET;
  size_t fragment_end = NOT_SET;
  size_t i;

  for (i = scheme_begin; uri_text[i] != 0; i++) {
    if (uri_text[i] == ':') {
      scheme_end = i;
      break;
    }
    if (uri_text[i] >= 'a' && uri_text[i] <= 'z') continue;
    if (uri_text[i] >= 'A' && uri_text[i] <= 'Z') continue;
    if (i != scheme_begin) {
      if (uri_text[i] >= '0' && uri_text[i] <= '9') continue;
      if (uri_text[i] == '+') continue;
      if (uri_text[i] == '-') continue;
      if (uri_text[i] == '.') continue;
    }
    break;
  }
  if (scheme_end == NOT_SET) {
    return bad_uri(uri_text, i, "scheme", suppress_errors);
  }

  if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') {
    authority_begin = scheme_end + 3;
    for (i = authority_begin; uri_text[i] != 0 && authority_end == NOT_SET;
         i++) {
      if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') {
        authority_end = i;
      }
    }
    if (authority_end == NOT_SET && uri_text[i] == 0) {
      authority_end = i;
    }
    if (authority_end == NOT_SET) {
      return bad_uri(uri_text, i, "authority", suppress_errors);
    }
    /* TODO(ctiller): parse the authority correctly */
    path_begin = authority_end;
  } else {
    path_begin = scheme_end + 1;
  }

  for (i = path_begin; uri_text[i] != 0; i++) {
    if (uri_text[i] == '?' || uri_text[i] == '#') {
      path_end = i;
      break;
    }
  }
  if (path_end == NOT_SET && uri_text[i] == 0) {
    path_end = i;
  }
  if (path_end == NOT_SET) {
    return bad_uri(uri_text, i, "path", suppress_errors);
  }

  if (uri_text[i] == '?') {
    query_begin = ++i;
    if (!parse_fragment_or_query(uri_text, &i)) {
      return bad_uri(uri_text, i, "query", suppress_errors);
    } else if (uri_text[i] != 0 && uri_text[i] != '#') {
      /* We must be at the end or at the beginning of a fragment */
      return bad_uri(uri_text, i, "query", suppress_errors);
    }
    query_end = i;
  }
  if (uri_text[i] == '#') {
    fragment_begin = ++i;
    if (!parse_fragment_or_query(uri_text, &i)) {
      return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors);
    } else if (uri_text[i] != 0) {
      /* We must be at the end */
      return bad_uri(uri_text, i, "fragment", suppress_errors);
    }
    fragment_end = i;
  }

  uri = (grpc_uri*)gpr_zalloc(sizeof(*uri));
  uri->scheme =
      decode_and_copy_component(exec_ctx, uri_text, scheme_begin, scheme_end);
  uri->authority = decode_and_copy_component(exec_ctx, uri_text,
                                             authority_begin, authority_end);
  uri->path =
      decode_and_copy_component(exec_ctx, uri_text, path_begin, path_end);
  uri->query =
      decode_and_copy_component(exec_ctx, uri_text, query_begin, query_end);
  uri->fragment = decode_and_copy_component(exec_ctx, uri_text, fragment_begin,
                                            fragment_end);
  parse_query_parts(uri);

  return uri;
}

const char* grpc_uri_get_query_arg(const grpc_uri* uri, const char* key) {
  GPR_ASSERT(key != NULL);
  if (key[0] == '\0') return NULL;

  for (size_t i = 0; i < uri->num_query_parts; ++i) {
    if (0 == strcmp(key, uri->query_parts[i])) {
      return uri->query_parts_values[i];
    }
  }
  return NULL;
}

void grpc_uri_destroy(grpc_uri* uri) {
  if (!uri) return;
  gpr_free(uri->scheme);
  gpr_free(uri->authority);
  gpr_free(uri->path);
  gpr_free(uri->query);
  for (size_t i = 0; i < uri->num_query_parts; ++i) {
    gpr_free(uri->query_parts[i]);
    gpr_free(uri->query_parts_values[i]);
  }
  gpr_free(uri->query_parts);
  gpr_free(uri->query_parts_values);
  gpr_free(uri->fragment);
  gpr_free(uri);
}
<commit_msg>Fix internal UBSAN failure<commit_after>/*
 *
 * Copyright 2015 gRPC authors.
 *
 * 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 "src/core/ext/filters/client_channel/uri_parser.h"

#include <string.h>

#include <grpc/slice_buffer.h>
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h>
#include <grpc/support/string_util.h>

#include "src/core/lib/slice/percent_encoding.h"
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/slice/slice_string_helpers.h"
#include "src/core/lib/support/string.h"

/** a size_t default value... maps to all 1's */
#define NOT_SET (~(size_t)0)

static grpc_uri* bad_uri(const char* uri_text, size_t pos, const char* section,
                         bool suppress_errors) {
  char* line_prefix;
  size_t pfx_len;

  if (!suppress_errors) {
    gpr_asprintf(&line_prefix, "bad uri.%s: '", section);
    pfx_len = strlen(line_prefix) + pos;
    gpr_log(GPR_ERROR, "%s%s'", line_prefix, uri_text);
    gpr_free(line_prefix);

    line_prefix = (char*)gpr_malloc(pfx_len + 1);
    memset(line_prefix, ' ', pfx_len);
    line_prefix[pfx_len] = 0;
    gpr_log(GPR_ERROR, "%s^ here", line_prefix);
    gpr_free(line_prefix);
  }

  return NULL;
}

/** Returns a copy of percent decoded \a src[begin, end) */
static char* decode_and_copy_component(grpc_exec_ctx* exec_ctx, const char* src,
                                       size_t begin, size_t end) {
  grpc_slice component =
      (begin == NOT_SET || end == NOT_SET)
          ? grpc_empty_slice()
          : grpc_slice_from_copied_buffer(src + begin, end - begin);
  grpc_slice decoded_component =
      grpc_permissive_percent_decode_slice(component);
  char* out = grpc_dump_slice(decoded_component, GPR_DUMP_ASCII);
  grpc_slice_unref_internal(exec_ctx, component);
  grpc_slice_unref_internal(exec_ctx, decoded_component);
  return out;
}

static bool valid_hex(char c) {
  return ((c >= 'a') && (c <= 'f')) || ((c >= 'A') && (c <= 'F')) ||
         ((c >= '0') && (c <= '9'));
}

/** Returns how many chars to advance if \a uri_text[i] begins a valid \a pchar
 * production. If \a uri_text[i] introduces an invalid \a pchar (such as percent
 * sign not followed by two hex digits), NOT_SET is returned. */
static size_t parse_pchar(const char* uri_text, size_t i) {
  /* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
   * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
   * pct-encoded = "%" HEXDIG HEXDIG
   * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
   / "*" / "+" / "," / ";" / "=" */
  char c = uri_text[i];
  switch (c) {
    default:
      if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ||
          ((c >= '0') && (c <= '9'))) {
        return 1;
      }
      break;
    case ':':
    case '@':
    case '-':
    case '.':
    case '_':
    case '~':
    case '!':
    case '$':
    case '&':
    case '\'':
    case '(':
    case ')':
    case '*':
    case '+':
    case ',':
    case ';':
    case '=':
      return 1;
    case '%': /* pct-encoded */
      if (valid_hex(uri_text[i + 1]) && valid_hex(uri_text[i + 2])) {
        return 2;
      }
      return NOT_SET;
  }
  return 0;
}

/* *( pchar / "?" / "/" ) */
static int parse_fragment_or_query(const char* uri_text, size_t* i) {
  char c;
  while ((c = uri_text[*i]) != 0) {
    const size_t advance = parse_pchar(uri_text, *i); /* pchar */
    switch (advance) {
      case 0: /* uri_text[i] isn't in pchar */
        /* maybe it's ? or / */
        if (uri_text[*i] == '?' || uri_text[*i] == '/') {
          (*i)++;
          break;
        } else {
          return 1;
        }
        GPR_UNREACHABLE_CODE(return 0);
      default:
        (*i) += advance;
        break;
      case NOT_SET: /* uri_text[i] introduces an invalid URI */
        return 0;
    }
  }
  /* *i is the first uri_text position past the \a query production, maybe \0 */
  return 1;
}

static void parse_query_parts(grpc_uri* uri) {
  static const char* QUERY_PARTS_SEPARATOR = "&";
  static const char* QUERY_PARTS_VALUE_SEPARATOR = "=";
  GPR_ASSERT(uri->query != NULL);
  if (uri->query[0] == '\0') {
    uri->query_parts = NULL;
    uri->query_parts_values = NULL;
    uri->num_query_parts = 0;
    return;
  }

  gpr_string_split(uri->query, QUERY_PARTS_SEPARATOR, &uri->query_parts,
                   &uri->num_query_parts);
  uri->query_parts_values =
      (char**)gpr_malloc(uri->num_query_parts * sizeof(char**));
  for (size_t i = 0; i < uri->num_query_parts; i++) {
    char** query_param_parts;
    size_t num_query_param_parts;
    char* full = uri->query_parts[i];
    gpr_string_split(full, QUERY_PARTS_VALUE_SEPARATOR, &query_param_parts,
                     &num_query_param_parts);
    GPR_ASSERT(num_query_param_parts > 0);
    uri->query_parts[i] = query_param_parts[0];
    if (num_query_param_parts > 1) {
      /* TODO(dgq): only the first value after the separator is considered.
       * Perhaps all chars after the first separator for the query part should
       * be included, even if they include the separator. */
      uri->query_parts_values[i] = query_param_parts[1];
    } else {
      uri->query_parts_values[i] = NULL;
    }
    for (size_t j = 2; j < num_query_param_parts; j++) {
      gpr_free(query_param_parts[j]);
    }
    gpr_free(query_param_parts);
    gpr_free(full);
  }
}

grpc_uri* grpc_uri_parse(grpc_exec_ctx* exec_ctx, const char* uri_text,
                         bool suppress_errors) {
  grpc_uri* uri;
  size_t scheme_begin = 0;
  size_t scheme_end = NOT_SET;
  size_t authority_begin = NOT_SET;
  size_t authority_end = NOT_SET;
  size_t path_begin = NOT_SET;
  size_t path_end = NOT_SET;
  size_t query_begin = NOT_SET;
  size_t query_end = NOT_SET;
  size_t fragment_begin = NOT_SET;
  size_t fragment_end = NOT_SET;
  size_t i;

  for (i = scheme_begin; uri_text[i] != 0; i++) {
    if (uri_text[i] == ':') {
      scheme_end = i;
      break;
    }
    if (uri_text[i] >= 'a' && uri_text[i] <= 'z') continue;
    if (uri_text[i] >= 'A' && uri_text[i] <= 'Z') continue;
    if (i != scheme_begin) {
      if (uri_text[i] >= '0' && uri_text[i] <= '9') continue;
      if (uri_text[i] == '+') continue;
      if (uri_text[i] == '-') continue;
      if (uri_text[i] == '.') continue;
    }
    break;
  }
  if (scheme_end == NOT_SET) {
    return bad_uri(uri_text, i, "scheme", suppress_errors);
  }

  if (uri_text[scheme_end + 1] == '/' && uri_text[scheme_end + 2] == '/') {
    authority_begin = scheme_end + 3;
    for (i = authority_begin; uri_text[i] != 0 && authority_end == NOT_SET;
         i++) {
      if (uri_text[i] == '/' || uri_text[i] == '?' || uri_text[i] == '#') {
        authority_end = i;
      }
    }
    if (authority_end == NOT_SET && uri_text[i] == 0) {
      authority_end = i;
    }
    if (authority_end == NOT_SET) {
      return bad_uri(uri_text, i, "authority", suppress_errors);
    }
    /* TODO(ctiller): parse the authority correctly */
    path_begin = authority_end;
  } else {
    path_begin = scheme_end + 1;
  }

  for (i = path_begin; uri_text[i] != 0; i++) {
    if (uri_text[i] == '?' || uri_text[i] == '#') {
      path_end = i;
      break;
    }
  }
  if (path_end == NOT_SET && uri_text[i] == 0) {
    path_end = i;
  }
  if (path_end == NOT_SET) {
    return bad_uri(uri_text, i, "path", suppress_errors);
  }

  if (uri_text[i] == '?') {
    query_begin = ++i;
    if (!parse_fragment_or_query(uri_text, &i)) {
      return bad_uri(uri_text, i, "query", suppress_errors);
    } else if (uri_text[i] != 0 && uri_text[i] != '#') {
      /* We must be at the end or at the beginning of a fragment */
      return bad_uri(uri_text, i, "query", suppress_errors);
    }
    query_end = i;
  }
  if (uri_text[i] == '#') {
    fragment_begin = ++i;
    if (!parse_fragment_or_query(uri_text, &i)) {
      return bad_uri(uri_text, i - fragment_end, "fragment", suppress_errors);
    } else if (uri_text[i] != 0) {
      /* We must be at the end */
      return bad_uri(uri_text, i, "fragment", suppress_errors);
    }
    fragment_end = i;
  }

  uri = (grpc_uri*)gpr_zalloc(sizeof(*uri));
  uri->scheme =
      decode_and_copy_component(exec_ctx, uri_text, scheme_begin, scheme_end);
  uri->authority = decode_and_copy_component(exec_ctx, uri_text,
                                             authority_begin, authority_end);
  uri->path =
      decode_and_copy_component(exec_ctx, uri_text, path_begin, path_end);
  uri->query =
      decode_and_copy_component(exec_ctx, uri_text, query_begin, query_end);
  uri->fragment = decode_and_copy_component(exec_ctx, uri_text, fragment_begin,
                                            fragment_end);
  parse_query_parts(uri);

  return uri;
}

const char* grpc_uri_get_query_arg(const grpc_uri* uri, const char* key) {
  GPR_ASSERT(key != NULL);
  if (key[0] == '\0') return NULL;

  for (size_t i = 0; i < uri->num_query_parts; ++i) {
    if (0 == strcmp(key, uri->query_parts[i])) {
      return uri->query_parts_values[i];
    }
  }
  return NULL;
}

void grpc_uri_destroy(grpc_uri* uri) {
  if (!uri) return;
  gpr_free(uri->scheme);
  gpr_free(uri->authority);
  gpr_free(uri->path);
  gpr_free(uri->query);
  for (size_t i = 0; i < uri->num_query_parts; ++i) {
    gpr_free(uri->query_parts[i]);
    gpr_free(uri->query_parts_values[i]);
  }
  gpr_free(uri->query_parts);
  gpr_free(uri->query_parts_values);
  gpr_free(uri->fragment);
  gpr_free(uri);
}
<|endoftext|>


<commit_before>/*
 *The MIT License (MIT)
 *
 * Copyright (c) <2016> <Stephan Gatzka>
 *
 * 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.
 */

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE http_connection_tests

#include <boost/test/unit_test.hpp>
#include <errno.h>

#include "buffered_socket.h"
#include "eventloop.h"
#include "http_connection.h"
#include "socket.h"

#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))

#ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif

static bool close_called = false;
static bool create_called = false;

static const char *readbuffer;
static const char *readbuffer_ptr;
static size_t readbuffer_length;

static char write_buffer[5000];
size_t write_buffer_written;
static char *write_buffer_ptr;

static const int FD_WOULDBLOCK = 1;
static const int FD_COMPLETE_STARTLINE = 2;
static const int FD_CLOSE = 3;
static const int FD_ERROR = 4;

extern "C" {
	ssize_t socket_writev(socket_type sock, struct buffered_socket_io_vector *io_vec, unsigned int count)
	{
		(void)io_vec;
		(void)count;

		switch (sock) {
		case FD_COMPLETE_STARTLINE: {
			size_t complete_length = 0;
			for (unsigned int i = 0; i < count; i++) {
				memcpy(write_buffer_ptr, io_vec[i].iov_base, io_vec[i].iov_len);
				complete_length += io_vec[i].iov_len;
				write_buffer_ptr += io_vec[i].iov_len;
			}
			write_buffer_written = complete_length;
			return complete_length;
		}

		case FD_WOULDBLOCK:
		default:
			errno = EWOULDBLOCK;
			return -1;
		}
	}

	ssize_t socket_read(socket_type sock, void *buf, size_t count)
	{
		(void)buf;
		(void)count;

		switch (sock) {
		case FD_COMPLETE_STARTLINE:
			if (readbuffer_length > 0) {
				size_t len = MIN(readbuffer_length, count);
				memcpy(buf, readbuffer_ptr, len);
				readbuffer_length -= len;
				readbuffer_ptr += len;
				return len;
			} else {
				errno = EWOULDBLOCK;
				return -1;
			}
			break;

		case FD_CLOSE:
			return 0;

		case FD_ERROR:
			errno = EINVAL;
			return -1;

		case FD_WOULDBLOCK:
		default:
			errno = EWOULDBLOCK;
			return -1;
		}
	}

	int socket_close(socket_type sock)
	{
		(void)sock;
		close_called = true;
		return 0;
	}
}

static enum eventloop_return eventloop_fake_add(const void *this_ptr, const struct io_event *ev)
{
	(void)this_ptr;
	(void)ev;
	return EL_CONTINUE_LOOP;
}

static void eventloop_fake_remove(const void *this_ptr, const struct io_event *ev)
{
	(void)this_ptr;
	(void)ev;
}

int on_create(struct http_connection *connection)
{
	(void)connection;
	create_called = true;
	return 0;
}

struct F {
	F()
	{
		close_called = false;
		create_called = false;

		loop.init = NULL;
		loop.destroy = NULL;
		loop.run = NULL;
		loop.add = eventloop_fake_add;
		loop.remove = eventloop_fake_remove;

		readbuffer_ptr = readbuffer;
		write_buffer_ptr = write_buffer;
		write_buffer_written = 0;

		http_parser_settings_init(&parser_settings);
		http_parser_init(&parser, HTTP_RESPONSE);

		handler[0].request_target = "/";
		handler[0].create = NULL;
		handler[0].on_header_field = NULL;
		handler[0].on_header_value = NULL;
		handler[0].on_headers_complete = NULL;
		handler[0].on_body = NULL;
		handler[0].on_message_complete = NULL;

		http_server.handler = handler;
		http_server.num_handlers = ARRAY_SIZE(handler);
	}

	~F()
	{
	}

	struct eventloop loop;
	http_parser parser;
	http_parser_settings parser_settings;
	struct url_handler handler[1];
	struct http_server http_server;
};

BOOST_FIXTURE_TEST_CASE(test_websocket_alloc, F)
{
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_WOULDBLOCK, false);
	BOOST_CHECK_MESSAGE(connection != NULL, "Connection allocation failed!");

	free_connection(connection);
}

BOOST_FIXTURE_TEST_CASE(test_buffered_socket_migration, F)
{
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_WOULDBLOCK, false);
	BOOST_CHECK(connection != NULL);

	struct buffered_socket *bs = connection->bs;
	connection->bs = NULL;
	free_connection(connection);
	BOOST_CHECK_MESSAGE(!close_called, "Close was called after buffered_socket migration!");
	buffered_socket_close(bs);
	free(bs);
	BOOST_CHECK_MESSAGE(close_called, "Close was not called after buffered_socket_close!");
}

BOOST_FIXTURE_TEST_CASE(test_read_invalid_startline, F)
{
	readbuffer = "aaaa\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection == NULL);
}

BOOST_FIXTURE_TEST_CASE(test_read_close, F)
{
	readbuffer = "aaaa\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_CLOSE, false);
	BOOST_CHECK(connection == NULL);
}

BOOST_FIXTURE_TEST_CASE(test_read_error, F)
{
	readbuffer = "aaaa\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_ERROR, false);
	BOOST_CHECK(connection == NULL);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_url_match, F)
{
	readbuffer = "GET /infotext.html HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection != NULL);

	free_connection(connection);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_url_match_create_called, F)
{
	handler[0].create = on_create;
	readbuffer = "GET /infotext.html HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection != NULL);
	BOOST_CHECK_MESSAGE(create_called, "Create callback was not called!");

	free_connection(connection);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_url_no_match, F)
{
	handler[0].request_target = "/foobar/";

	readbuffer = "GET /infotext.html HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection == NULL);

	size_t nparsed = http_parser_execute(&parser, &parser_settings, write_buffer, write_buffer_written);
	BOOST_CHECK(nparsed == write_buffer_written);
	BOOST_CHECK(parser.status_code == 404);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_invalid_url, F)
{
	handler[0].request_target = "/foobar/";

	readbuffer = "GET http://ww%.google.de/ HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection == NULL);

	size_t nparsed = http_parser_execute(&parser, &parser_settings, write_buffer, write_buffer_written);
	BOOST_CHECK(nparsed == write_buffer_written);
	BOOST_CHECK(parser.status_code == 400);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_connect_request_url_match, F)
{
	readbuffer = "CONNECT www.example.com:443 HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection == NULL);

	size_t nparsed = http_parser_execute(&parser, &parser_settings, write_buffer, write_buffer_written);
	BOOST_CHECK(nparsed == write_buffer_written);
	BOOST_CHECK(parser.status_code == 400);
}
<commit_msg>Initialize loop variable completely.<commit_after>/*
 *The MIT License (MIT)
 *
 * Copyright (c) <2016> <Stephan Gatzka>
 *
 * 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.
 */

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE http_connection_tests

#include <boost/test/unit_test.hpp>
#include <errno.h>

#include "buffered_socket.h"
#include "eventloop.h"
#include "http_connection.h"
#include "socket.h"

#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))

#ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
#endif

static bool close_called = false;
static bool create_called = false;

static const char *readbuffer;
static const char *readbuffer_ptr;
static size_t readbuffer_length;

static char write_buffer[5000];
size_t write_buffer_written;
static char *write_buffer_ptr;

static const int FD_WOULDBLOCK = 1;
static const int FD_COMPLETE_STARTLINE = 2;
static const int FD_CLOSE = 3;
static const int FD_ERROR = 4;

extern "C" {
	ssize_t socket_writev(socket_type sock, struct buffered_socket_io_vector *io_vec, unsigned int count)
	{
		(void)io_vec;
		(void)count;

		switch (sock) {
		case FD_COMPLETE_STARTLINE: {
			size_t complete_length = 0;
			for (unsigned int i = 0; i < count; i++) {
				memcpy(write_buffer_ptr, io_vec[i].iov_base, io_vec[i].iov_len);
				complete_length += io_vec[i].iov_len;
				write_buffer_ptr += io_vec[i].iov_len;
			}
			write_buffer_written = complete_length;
			return complete_length;
		}

		case FD_WOULDBLOCK:
		default:
			errno = EWOULDBLOCK;
			return -1;
		}
	}

	ssize_t socket_read(socket_type sock, void *buf, size_t count)
	{
		(void)buf;
		(void)count;

		switch (sock) {
		case FD_COMPLETE_STARTLINE:
			if (readbuffer_length > 0) {
				size_t len = MIN(readbuffer_length, count);
				memcpy(buf, readbuffer_ptr, len);
				readbuffer_length -= len;
				readbuffer_ptr += len;
				return len;
			} else {
				errno = EWOULDBLOCK;
				return -1;
			}
			break;

		case FD_CLOSE:
			return 0;

		case FD_ERROR:
			errno = EINVAL;
			return -1;

		case FD_WOULDBLOCK:
		default:
			errno = EWOULDBLOCK;
			return -1;
		}
	}

	int socket_close(socket_type sock)
	{
		(void)sock;
		close_called = true;
		return 0;
	}
}

static enum eventloop_return eventloop_fake_add(const void *this_ptr, const struct io_event *ev)
{
	(void)this_ptr;
	(void)ev;
	return EL_CONTINUE_LOOP;
}

static void eventloop_fake_remove(const void *this_ptr, const struct io_event *ev)
{
	(void)this_ptr;
	(void)ev;
}

int on_create(struct http_connection *connection)
{
	(void)connection;
	create_called = true;
	return 0;
}

struct F {
	F()
	{
		close_called = false;
		create_called = false;

		loop.this_ptr = NULL;
		loop.init = NULL;
		loop.destroy = NULL;
		loop.run = NULL;
		loop.add = eventloop_fake_add;
		loop.remove = eventloop_fake_remove;

		readbuffer_ptr = readbuffer;
		write_buffer_ptr = write_buffer;
		write_buffer_written = 0;

		http_parser_settings_init(&parser_settings);
		http_parser_init(&parser, HTTP_RESPONSE);

		handler[0].request_target = "/";
		handler[0].create = NULL;
		handler[0].on_header_field = NULL;
		handler[0].on_header_value = NULL;
		handler[0].on_headers_complete = NULL;
		handler[0].on_body = NULL;
		handler[0].on_message_complete = NULL;

		http_server.handler = handler;
		http_server.num_handlers = ARRAY_SIZE(handler);
	}

	~F()
	{
	}

	struct eventloop loop;
	http_parser parser;
	http_parser_settings parser_settings;
	struct url_handler handler[1];
	struct http_server http_server;
};

BOOST_FIXTURE_TEST_CASE(test_websocket_alloc, F)
{
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_WOULDBLOCK, false);
	BOOST_CHECK_MESSAGE(connection != NULL, "Connection allocation failed!");
	free_connection(connection);
}

BOOST_FIXTURE_TEST_CASE(test_buffered_socket_migration, F)
{
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_WOULDBLOCK, false);
	BOOST_CHECK(connection != NULL);

	struct buffered_socket *bs = connection->bs;
	connection->bs = NULL;
	free_connection(connection);
	BOOST_CHECK_MESSAGE(!close_called, "Close was called after buffered_socket migration!");
	buffered_socket_close(bs);
	free(bs);
	BOOST_CHECK_MESSAGE(close_called, "Close was not called after buffered_socket_close!");
}

BOOST_FIXTURE_TEST_CASE(test_read_invalid_startline, F)
{
	readbuffer = "aaaa\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection == NULL);
}

BOOST_FIXTURE_TEST_CASE(test_read_close, F)
{
	readbuffer = "aaaa\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_CLOSE, false);
	BOOST_CHECK(connection == NULL);
}

BOOST_FIXTURE_TEST_CASE(test_read_error, F)
{
	readbuffer = "aaaa\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(NULL, &loop, FD_ERROR, false);
	BOOST_CHECK(connection == NULL);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_url_match, F)
{
	readbuffer = "GET /infotext.html HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection != NULL);

	free_connection(connection);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_url_match_create_called, F)
{
	handler[0].create = on_create;
	readbuffer = "GET /infotext.html HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection != NULL);
	BOOST_CHECK_MESSAGE(create_called, "Create callback was not called!");

	free_connection(connection);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_url_no_match, F)
{
	handler[0].request_target = "/foobar/";

	readbuffer = "GET /infotext.html HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection == NULL);

	size_t nparsed = http_parser_execute(&parser, &parser_settings, write_buffer, write_buffer_written);
	BOOST_CHECK(nparsed == write_buffer_written);
	BOOST_CHECK(parser.status_code == 404);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_invalid_url, F)
{
	handler[0].request_target = "/foobar/";

	readbuffer = "GET http://ww%.google.de/ HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection == NULL);

	size_t nparsed = http_parser_execute(&parser, &parser_settings, write_buffer, write_buffer_written);
	BOOST_CHECK(nparsed == write_buffer_written);
	BOOST_CHECK(parser.status_code == 400);
}

BOOST_FIXTURE_TEST_CASE(test_read_valid_startline_connect_request_url_match, F)
{
	readbuffer = "CONNECT www.example.com:443 HTTP/1.1\r\n";
	readbuffer_ptr = readbuffer;
	readbuffer_length = ::strlen(readbuffer);
	struct http_connection *connection = alloc_http_connection(&http_server, &loop, FD_COMPLETE_STARTLINE, false);
	BOOST_CHECK(connection == NULL);

	size_t nparsed = http_parser_execute(&parser, &parser_settings, write_buffer, write_buffer_written);
	BOOST_CHECK(nparsed == write_buffer_written);
	BOOST_CHECK(parser.status_code == 400);
}
<|endoftext|>


<commit_before>#include <vector>
#include <map>
#include <cstring>
#include <algorithm>
#include <iostream>

#include "alloc.hpp"
#include "safemacros.h"

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

namespace aL4nin
{
    template <>
    struct meta<int>
    {
        int* allocate(std::size_t elems)
        {
            return new int[elems];
        }
    };

    template <>
    meta<int>& get_meta<int>(std::size_t)
    {
        static meta<int> m;
        return m;
    }
}

// metadata entry defines the
// marking method:
// a) bitpattern (up to 31 bits representing pointers to follow)
// b) freestyle procedural marker
//     - uncommitted arrays must zero out object data before setting marker
//     - concurrency in data <--> metadata access???

// let's fix some hard conventions
// - T* is non-null collectable pointer that is to be followed
// - nullable<T> is a possibly NULL pointer that should be followed
// - T& is a non-assignable, non-null reference that is to be followed
// - const T is a non-assignable non-null reference that is to be followed
// - const nullable<T> is a non-assignable, possibly NULL pointer that should be followed
// - references to builtin basic types are not followed
// - non<T> is a pointer that will not be followed
// - const non<T> is a non-assignable pointer that will not be followed
// - thresholded<T, N> is like nullable but does not follow <= N

#define WRAP(TYPE, MODIFIER) \
    MODIFIER ## _WRAPPER(TYPE)

#define BARE_WRAPPER(TYPE) TYPE
#define _WRAPPER(TYPE) BARE_WRAPPER(TYPE)
#define NULLABLE_WRAPPER(TYPE) nullable<TYPE>
#define NON_WRAPPER(TYPE) non<TYPE>
#define NULLABLE_WRAPPER(TYPE) nullable<TYPE>

template <typename T>
struct nullable
{
    T* real;
    nullable(T* real)
        : real(real)
        {}
};

template <typename T>
struct non
{
    T real;
    non(const T& real)
        : real(real)
        {}
};

WRAP(int, NON) hh(42);
WRAP(int, /*BARE*/) hh1;
WRAP(int, NULLABLE) hh2(0);

struct foo
{
    foo& f;
    foo() : f(*this)
        {}
    void bar()
        {
///            int i(&foo::f);
        }

};

// Anatomy of the WORLD.
//
// The world is the part of the process memory that the GC
// is interested in. It is completely subdivided in clusters.
// The first cluster of the world is special.
// Every other cluster is subdivided into 2^p pages. The size
// of a page must match a (supported) VM page size.
// The number of pages a cluster consists of (2^p) is dependent
// on the cluster, so the world must store the ps in the special
// cluster in order to be able to find the cluster boundaries.
// At the start of each cluster we find the metaobjects.
// The metaobject at displacement 0 (into the cluster) is
// special, describing the metaobjects themselves, i.e. it is
// a meta-metaobject.
// Metaobjects are just additional information that is factored
// out of the objects or can add information about the validity
// and GC-properties of a group of objects.

// Note: later I may introduce a multiworld, which may be
// a linked list of worlds.

// Checker templates
//
template <unsigned>
struct IsZero;

template <>
struct IsZero<0>
{};

template <unsigned long DIVISOR, unsigned long DIVIDEND>
struct Divides : IsZero<DIVIDEND % DIVISOR>
{};

// World: define the layout of a world in the memory
// and subdivide in pages. Provide info about the
// location and size.
// NUMPAGES: how many pages should this world hold
// BASE: where should this world be located in the memory
// PAGE: bits needed to address a byte in a page. i.e.
//       pagesize == 2^PAGE bytes
//
template <unsigned NUMPAGES, unsigned BASE, unsigned PAGE>
struct World
{
    enum { PageSize = 1 << PAGE };

    struct Page
    {
        char raw[PageSize];
    };

    Page pages[NUMPAGES];

    static void* start(void)
        {
            Divides<PageSize, BASE> c1;
            return reinterpret_cast<void*>(BASE);
        }

    static size_t size(void)
        {
            return PageSize * NUMPAGES;
        }

    void protectPageRW(unsigned pagenum)
        {
            protectClusterRW(pagenum, 1);
        }

    void protectClusterRW(unsigned from_pagenum, unsigned ps)
        {
            if (0 != mprotect(pages + from_pagenum, PageSize * ps, PROT_NONE))
                perror("mprotect");
        }

    void* operator new(size_t s, int hdl)
        {
            void* here(mmap(start(),
                            s,
                            PROT_READ | PROT_WRITE,
                            MAP_SHARED,
                            hdl,
                            0));
            if (MAP_FAILED == here)
                perror("mmap");

            assert(s == size());
            assert(here == start());
            assert(sysconf(_SC_PAGESIZE) == PageSize);

            return here;
        }
};

// MISC ideas
// allocation: tell a cluster to allocate an obj of a certain
// metadata "class". returns a cluster address, which may be the
// same as the current cluster or pointer to a new cluster with
// the object being in there. In this case the pointer must be
// stored away for the next allocation request.

// GC: use a posix message queue to tell which thread stack ranges
// must be mprotected. This way the threads can be starved out
// systematically.

// RawObj2Meta is intended to return a pointer for an object
// living in the world, that describes its allocation and collection
// behaviour.
// PAGE: number of bits needed to address a byte in a VM-page
// CLUSTER: how many bits are needed to address a page in a VM cluster of pages
// SCALE: how many bytes together have the same metadata info (2^SCALE bytes)
//   ##!! not true: SCALE simply tells us how much "denser" metaobjects are
//        compared to objects. I.e. 32*8byte (cons cells) together share the same
//        metaobject, and the metaobject is 8bytes then SCALE is 5 because
//        2^5==32.
//
// Theory of operation:
//   Find the lowest address in the cluster and scale down the displacement
//   of the object to the appropriate metaobject.

template <unsigned long PAGE, unsigned long CLUSTER, unsigned long SCALE>
inline void* RawObj2Meta(void* obj)
{
    typedef unsigned long sptr_t;
    register sptr_t o(reinterpret_cast<sptr_t>(obj));
    enum
        {
            pat = (1 << PAGE + CLUSTER) - 1
        };

    register sptr_t b(o & ~static_cast<sptr_t>(pat));
    register sptr_t d(o & static_cast<sptr_t>(pat));
    return reinterpret_cast<void*>(b + (d >> SCALE));
}

template <unsigned NUMPAGES, unsigned BASE, unsigned PAGE>
struct ClusteredWorld : World<NUMPAGES, BASE, PAGE>
{
    template <unsigned long CLUSTER, unsigned long SCALE>
    struct Cluster
    {
        static inline void* Raw2Meta(void* obj)
        {
            return RawObj2Meta<PAGE, CLUSTER, SCALE>(obj);
        }
    };
};

#include "alloc.cpp"

using namespace std;
using namespace aL4nin;

namespace aL4nin
{
    template <>
    meta<void>* object_meta<void>(void* p)
    {
        if (!p)
            return 0;

        static meta<void> me;
        return &me;
    }

    template <>
    struct meta<std::_Rb_tree_node<std::pair<const int, int> > >
    {
        typedef std::_Rb_tree_node<std::pair<const int, int> > payload;

        payload* allocate(std::size_t elems)
        {
            return new payload[elems];
        }
    };

    template <>
    meta<std::_Rb_tree_node<std::pair<const int, int> > >& get_meta<std::_Rb_tree_node<std::pair<const int, int> > >(std::size_t)
    {
        static meta<std::_Rb_tree_node<std::pair<const int, int> > > m;
        return m;
    }

    struct cons : pair<void*, void*>
    {};

    template <>
    struct meta<cons>
    {
        enum { bits = 32 };

        cons* objects;
        bool bitmap[bits];
        bool marks[bits];

        meta(void)
            {
                memset(this, 0, sizeof *this);
            }

        cons* allocate(std::size_t)
            {
                if (!objects)
                {
                    objects = new cons[bits];
                    *bitmap = true;
                    return objects;
                }

                bool* end(bitmap + meta<cons>::bits);
                bool* freebit(find(bitmap, end, 0));
                if (freebit == end)
                    return 0;

                *freebit = true;

                return objects + (freebit - bitmap);
            }

        void mark(const cons* p PLUS_VERBOSITY_ARG())
            {
                // simple minded!
                int i(bits - 1);
                for (const cons* my(objects + i);
                     i >= 0;
                     --my, --i)
                {
                    if (bitmap[i] && p == my)
                    {
                        VERBOSE("identified as cons");
                        if (marks[i])
                        {
                            VERBOSE("already marked");
                            return /*true*/;
                        }

                        marks[i] = true;
                        if (object_meta(p->first))
                        {
                            object_meta(p->first)->mark(p->first PASS_VERBOSE);
                        }

                        if (object_meta(p->second))
                        {
                            object_meta(p->second)->mark(p->second PASS_VERBOSE);
                        }

                        return /*true*/;
                    }
                }

                VERBOSE_ABORT("not a cons");
            }
    };

    template <>
    meta<cons>& get_meta<cons>(std::size_t)
    {
        static meta<cons> m;

        if (find(m.bitmap, m.bitmap + meta<cons>::bits, 0))
            return m;

        abort();
    }

    void* rooty(0);

    void collect(VERBOSITY_ARG())
    {
        VERBOSE("starting");

        // do we need meta<void>
        // or can we safely assume
        // to know the exact meta type?
        // probably not: if a slot is just declared
        // <object> we never know the metadata
        meta<void>* m(object_meta(rooty));
        if (m)
            m->mark(rooty PASS_VERBOSE);
        /// if (m.trymark(rooty.first)) ...;

        VERBOSE("done");
    }

    void meta<void>::mark(void* p PLUS_VERBOSITY_ARG())
    {
        // look whether it is a cons
        VERBOSE("trying to mark: " << p);
        // hack!
        meta<cons>& m(get_meta<cons>(1));
        m.mark(static_cast<cons*>(p) PASS_VERBOSE);
    }
}

// http://www.opengroup.org/onlinepubs/007908799/xsh/sigaction.html
// http://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
// http://www.opengroup.org/onlinepubs/000095399/basedefs/signal.h.html
void yummy(int, siginfo_t *, void *);
void yummy(int, siginfo_t* indo, void *)
{
    printf("gggg\n");
    abort();
}

int main(void)
{
    vector<int, alloc<int> > v(3);
    map<int, int, less<int>, alloc<int> > m;
    m.insert(make_pair(1, 42));

    cons* c(alloc<cons>().allocate(1));
    c->first = c;
    c->second = 0;
    rooty = c;

    collect(true);

    int hdl(shm_open("/blubber",
                     O_RDWR | O_CREAT,
                     S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));

    perror("shm_open");

#   ifdef __APPLE__
    typedef ClusteredWorld<100, 0xFF000000UL, 12> world;
#   else
    typedef ClusteredWorld<100, 0xFF000000UL, 13> world;
#   endif

    int tr(ftruncate(hdl, world::size()));
    if (tr == -1)
        perror("ftruncate");

    world* w(new(hdl) world);
    void* area(w);

    if (MAP_FAILED == area)
        perror("mmap");

    w->protectPageRW(0);

    struct sigaction act, oact;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;
    act.sa_sigaction = yummy;

    if (sigaction(SIGBUS/*SEGV*/, &act, &oact))
        perror("sigaction");

    {
        for (int i(0); i < 100; i += 4)
        {
            char* p((char*)area + i);
            printf("i: %d, o: %p, m: %p\n", i, p, world::Cluster<4, 3>::Raw2Meta(p));
            *p = 0;
        }

        for (int i(10000); i < 10100; i += 4)
        {
            char* p((char*)area + i);
            printf("i: %d, o: %p, m: %p\n", i, p, world::Cluster<4, 3>::Raw2Meta(p));
            *p = 0;
        }

        sleep(3);
        int um(munmap(area, world::size()));
        if (um == -1)
            perror("munmap");
    }

    int ul(shm_unlink("/blubber"));
    if (ul == -1)
        perror("shm_unlink");

}
<commit_msg>do the right thing on Solaris<commit_after>#include <vector>
#include <map>
#include <cstring>
#include <algorithm>
#include <iostream>

#include "alloc.hpp"
#include "safemacros.h"

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

namespace aL4nin
{
    template <>
    struct meta<int>
    {
        int* allocate(std::size_t elems)
        {
            return new int[elems];
        }
    };

    template <>
    meta<int>& get_meta<int>(std::size_t)
    {
        static meta<int> m;
        return m;
    }
}

// metadata entry defines the
// marking method:
// a) bitpattern (up to 31 bits representing pointers to follow)
// b) freestyle procedural marker
//     - uncommitted arrays must zero out object data before setting marker
//     - concurrency in data <--> metadata access???

// let's fix some hard conventions
// - T* is non-null collectable pointer that is to be followed
// - nullable<T> is a possibly NULL pointer that should be followed
// - T& is a non-assignable, non-null reference that is to be followed
// - const T is a non-assignable non-null reference that is to be followed
// - const nullable<T> is a non-assignable, possibly NULL pointer that should be followed
// - references to builtin basic types are not followed
// - non<T> is a pointer that will not be followed
// - const non<T> is a non-assignable pointer that will not be followed
// - thresholded<T, N> is like nullable but does not follow <= N

#define WRAP(TYPE, MODIFIER) \
    MODIFIER ## _WRAPPER(TYPE)

#define BARE_WRAPPER(TYPE) TYPE
#define _WRAPPER(TYPE) BARE_WRAPPER(TYPE)
#define NULLABLE_WRAPPER(TYPE) nullable<TYPE>
#define NON_WRAPPER(TYPE) non<TYPE>
#define NULLABLE_WRAPPER(TYPE) nullable<TYPE>

template <typename T>
struct nullable
{
    T* real;
    nullable(T* real)
        : real(real)
        {}
};

template <typename T>
struct non
{
    T real;
    non(const T& real)
        : real(real)
        {}
};

WRAP(int, NON) hh(42);
WRAP(int, /*BARE*/) hh1;
WRAP(int, NULLABLE) hh2(0);

struct foo
{
    foo& f;
    foo() : f(*this)
        {}
    void bar()
        {
///            int i(&foo::f);
        }

};

// Anatomy of the WORLD.
//
// The world is the part of the process memory that the GC
// is interested in. It is completely subdivided in clusters.
// The first cluster of the world is special.
// Every other cluster is subdivided into 2^p pages. The size
// of a page must match a (supported) VM page size.
// The number of pages a cluster consists of (2^p) is dependent
// on the cluster, so the world must store the ps in the special
// cluster in order to be able to find the cluster boundaries.
// At the start of each cluster we find the metaobjects.
// The metaobject at displacement 0 (into the cluster) is
// special, describing the metaobjects themselves, i.e. it is
// a meta-metaobject.
// Metaobjects are just additional information that is factored
// out of the objects or can add information about the validity
// and GC-properties of a group of objects.

// Note: later I may introduce a multiworld, which may be
// a linked list of worlds.

// Checker templates
//
template <unsigned>
struct IsZero;

template <>
struct IsZero<0>
{};

template <unsigned long DIVISOR, unsigned long DIVIDEND>
struct Divides : IsZero<DIVIDEND % DIVISOR>
{};

// World: define the layout of a world in the memory
// and subdivide in pages. Provide info about the
// location and size.
// NUMPAGES: how many pages should this world hold
// BASE: where should this world be located in the memory
// PAGE: bits needed to address a byte in a page. i.e.
//       pagesize == 2^PAGE bytes
//
template <unsigned NUMPAGES, unsigned BASE, unsigned PAGE>
struct World
{
    enum { PageSize = 1 << PAGE };

    struct Page
    {
        char raw[PageSize];
    };

    Page pages[NUMPAGES];

    static void* start(void)
        {
            Divides<PageSize, BASE> c1;
            return reinterpret_cast<void*>(BASE);
        }

    static size_t size(void)
        {
            return PageSize * NUMPAGES;
        }

    void protectPageRW(unsigned pagenum)
        {
            protectClusterRW(pagenum, 1);
        }

    void protectClusterRW(unsigned from_pagenum, unsigned ps)
        {
            if (0 != mprotect(pages + from_pagenum, PageSize * ps, PROT_NONE))
                perror("mprotect");
        }

    void* operator new(size_t s, int hdl)
        {
            void* here(mmap(start(),
                            s,
                            PROT_READ | PROT_WRITE,
                            MAP_SHARED,
                            hdl,
                            0));
            if (MAP_FAILED == here)
                perror("mmap");

            assert(s == size());
            assert(here == start());
            assert(sysconf(_SC_PAGESIZE) == PageSize);

            return here;
        }
};

// MISC ideas
// allocation: tell a cluster to allocate an obj of a certain
// metadata "class". returns a cluster address, which may be the
// same as the current cluster or pointer to a new cluster with
// the object being in there. In this case the pointer must be
// stored away for the next allocation request.

// GC: use a posix message queue to tell which thread stack ranges
// must be mprotected. This way the threads can be starved out
// systematically.

// RawObj2Meta is intended to return a pointer for an object
// living in the world, that describes its allocation and collection
// behaviour.
// PAGE: number of bits needed to address a byte in a VM-page
// CLUSTER: how many bits are needed to address a page in a VM cluster of pages
// SCALE: how many bytes together have the same metadata info (2^SCALE bytes)
//   ##!! not true: SCALE simply tells us how much "denser" metaobjects are
//        compared to objects. I.e. 32*8byte (cons cells) together share the same
//        metaobject, and the metaobject is 8bytes then SCALE is 5 because
//        2^5==32.
//
// Theory of operation:
//   Find the lowest address in the cluster and scale down the displacement
//   of the object to the appropriate metaobject.

template <unsigned long PAGE, unsigned long CLUSTER, unsigned long SCALE>
inline void* RawObj2Meta(void* obj)
{
    typedef unsigned long sptr_t;
    register sptr_t o(reinterpret_cast<sptr_t>(obj));
    enum
        {
            pat = (1 << PAGE + CLUSTER) - 1
        };

    register sptr_t b(o & ~static_cast<sptr_t>(pat));
    register sptr_t d(o & static_cast<sptr_t>(pat));
    return reinterpret_cast<void*>(b + (d >> SCALE));
}

template <unsigned NUMPAGES, unsigned BASE, unsigned PAGE>
struct ClusteredWorld : World<NUMPAGES, BASE, PAGE>
{
    template <unsigned long CLUSTER, unsigned long SCALE>
    struct Cluster
    {
        static inline void* Raw2Meta(void* obj)
        {
            return RawObj2Meta<PAGE, CLUSTER, SCALE>(obj);
        }
    };
};

#include "alloc.cpp"

using namespace std;
using namespace aL4nin;

namespace aL4nin
{
    template <>
    meta<void>* object_meta<void>(void* p)
    {
        if (!p)
            return 0;

        static meta<void> me;
        return &me;
    }

    template <>
    struct meta<std::_Rb_tree_node<std::pair<const int, int> > >
    {
        typedef std::_Rb_tree_node<std::pair<const int, int> > payload;

        payload* allocate(std::size_t elems)
        {
            return new payload[elems];
        }
    };

    template <>
    meta<std::_Rb_tree_node<std::pair<const int, int> > >& get_meta<std::_Rb_tree_node<std::pair<const int, int> > >(std::size_t)
    {
        static meta<std::_Rb_tree_node<std::pair<const int, int> > > m;
        return m;
    }

    struct cons : pair<void*, void*>
    {};

    template <>
    struct meta<cons>
    {
        enum { bits = 32 };

        cons* objects;
        bool bitmap[bits];
        bool marks[bits];

        meta(void)
            {
                memset(this, 0, sizeof *this);
            }

        cons* allocate(std::size_t)
            {
                if (!objects)
                {
                    objects = new cons[bits];
                    *bitmap = true;
                    return objects;
                }

                bool* end(bitmap + meta<cons>::bits);
                bool* freebit(find(bitmap, end, 0));
                if (freebit == end)
                    return 0;

                *freebit = true;

                return objects + (freebit - bitmap);
            }

        void mark(const cons* p PLUS_VERBOSITY_ARG())
            {
                // simple minded!
                int i(bits - 1);
                for (const cons* my(objects + i);
                     i >= 0;
                     --my, --i)
                {
                    if (bitmap[i] && p == my)
                    {
                        VERBOSE("identified as cons");
                        if (marks[i])
                        {
                            VERBOSE("already marked");
                            return /*true*/;
                        }

                        marks[i] = true;
                        if (object_meta(p->first))
                        {
                            object_meta(p->first)->mark(p->first PASS_VERBOSE);
                        }

                        if (object_meta(p->second))
                        {
                            object_meta(p->second)->mark(p->second PASS_VERBOSE);
                        }

                        return /*true*/;
                    }
                }

                VERBOSE_ABORT("not a cons");
            }
    };

    template <>
    meta<cons>& get_meta<cons>(std::size_t)
    {
        static meta<cons> m;

        if (find(m.bitmap, m.bitmap + meta<cons>::bits, 0))
            return m;

        abort();
    }

    void* rooty(0);

    void collect(VERBOSITY_ARG())
    {
        VERBOSE("starting");

        // do we need meta<void>
        // or can we safely assume
        // to know the exact meta type?
        // probably not: if a slot is just declared
        // <object> we never know the metadata
        meta<void>* m(object_meta(rooty));
        if (m)
            m->mark(rooty PASS_VERBOSE);
        /// if (m.trymark(rooty.first)) ...;

        VERBOSE("done");
    }

    void meta<void>::mark(void* p PLUS_VERBOSITY_ARG())
    {
        // look whether it is a cons
        VERBOSE("trying to mark: " << p);
        // hack!
        meta<cons>& m(get_meta<cons>(1));
        m.mark(static_cast<cons*>(p) PASS_VERBOSE);
    }
}

// http://www.opengroup.org/onlinepubs/007908799/xsh/sigaction.html
// http://www.gnu.org/software/libc/manual/html_node/Sigaction-Function-Example.html
// http://www.opengroup.org/onlinepubs/000095399/basedefs/signal.h.html
void yummy(int, siginfo_t *, void *);
void yummy(int, siginfo_t* indo, void *)
{
    printf("gggg\n");
    abort();
}

void wummy(int, siginfo_t *, void *);
void wummy(int, siginfo_t* indo, void *)
{
    printf("Solaris:gggg\n");
    abort();
}

int main(void)
{
    vector<int, alloc<int> > v(3);
    map<int, int, less<int>, alloc<int> > m;
    m.insert(make_pair(1, 42));

    cons* c(alloc<cons>().allocate(1));
    c->first = c;
    c->second = 0;
    rooty = c;

    collect(true);

    int hdl(shm_open("/blubber",
                     O_RDWR | O_CREAT,
                     S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP));

    perror("shm_open");

#   ifdef __APPLE__
    typedef ClusteredWorld<100, 0xFF000000UL, 12> world;
#   else
    typedef ClusteredWorld<100, 0xFF000000UL, 13> world;
#   endif

    int tr(ftruncate(hdl, world::size()));
    if (tr == -1)
        perror("ftruncate");

    world* w(new(hdl) world);
    void* area(w);

    if (MAP_FAILED == area)
        perror("mmap");

    w->protectPageRW(0);

    struct sigaction act, oact;
    memset(&act, 0, sizeof act);
    sigemptyset(&act.sa_mask);
    act.sa_flags = SA_SIGINFO;

#   ifdef __APPLE__
    act.sa_sigaction = yummy;
    if (sigaction(SIGBUS, &act, &oact))
#   else
    act.sa_sigaction = wummy;
    if (sigaction(SIGSEGV, &act, &oact))
#   endif
        perror("sigaction");

    {
        for (int i(0); i < 100; i += 4)
        {
            char* p((char*)area + i);
            printf("i: %d, o: %p, m: %p\n", i, p, world::Cluster<4, 3>::Raw2Meta(p));
            *p = 0;
        }

        for (int i(10000); i < 10100; i += 4)
        {
            char* p((char*)area + i);
            printf("i: %d, o: %p, m: %p\n", i, p, world::Cluster<4, 3>::Raw2Meta(p));
            *p = 0;
        }

        sleep(3);
        int um(munmap(area, world::size()));
        if (um == -1)
            perror("munmap");
    }

    int ul(shm_unlink("/blubber"));
    if (ul == -1)
        perror("shm_unlink");

}
<|endoftext|>