<commit_before>///
/// @file   EigenSerialization.hpp
/// @author Paul Furgale <paul.furgale@utoronto.ca>
/// @date   Tue Oct 26 12:57:31 2010
///
/// @brief  Functions to serialize dense Eigen matrices.
///
///
///

#ifndef SM_EIGEN_SERIALIZATION_HPP
#define SM_EIGEN_SERIALIZATION_HPP

#include <Eigen/Core>
#include <sm/assert_macros.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/nvp.hpp>

namespace sm { namespace eigen {
    // An exception for errors during serialization/deserialization
    SM_DEFINE_EXCEPTION( SerializationException, std::runtime_error );
  }} // namespace sm::eigen

namespace boost { namespace serialization {

    ///
    /// boost::serialization helper function for the Eigen3 Matrix object.
    /// The matrix is a base class for dense matrices.
    /// http://eigen.tuxfamily.org/dox-devel/TutorialMatrixClass.html
    ///
    /// This function should not be called directly but will be called
    /// when serializing a matrix.
    ///
    ///
    /// @param ar  The boost::serialization archive
    /// @param M   The matrix to serialize
    /// @param file_version The file version to be serialized.
    ///
    template<class Archive, class Scalar, int A, int B, int C, int D, int E>
    inline void save( Archive & ar,
		      const Eigen::Matrix<Scalar,A,B,C,D,E> & M,
		      const unsigned int file_version)
    {
      typedef typename Eigen::Matrix<Scalar,A,B,C,D,E>::Index index_t;
      index_t rows = M.rows();
      index_t cols = M.cols();
      ar << BOOST_SERIALIZATION_NVP(rows);
      ar << BOOST_SERIALIZATION_NVP(cols);

      ar << make_nvp("data", make_array(&M(0,0),M.rows()*M.cols()));
    }

    ///
    /// Loads an Eigen matrix object from a boost::serialization archive.
    /// This function has several specializations to handle Matrices with
    /// fixed sizes and those with dynamic sizes. This function is called
    /// by the boost serialization code and should not be called directly.
    ///
    /// This specialization handles fixed sized matrices
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be loaded
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int A, int B, int C, int D, int E>
    inline void load( Archive & ar,
		      Eigen::Matrix<Scalar,A,B,C,D,E> & M,
		      const unsigned int file_version)
    {
      typedef typename Eigen::Matrix<Scalar,A,B,C,D,E>::Index index_t;
      index_t rows, cols;
      ar >> BOOST_SERIALIZATION_NVP(rows);
      ar >> BOOST_SERIALIZATION_NVP(cols);
      SM_ASSERT_EQ(sm::eigen::SerializationException,rows,A,"Unexpected number of rows found for fixed-sized type");
      SM_ASSERT_EQ(sm::eigen::SerializationException,cols,B,"Unexpected number of columns found for fixed-sized type");

      if(rows > 0 && cols > 0)
	{
	  ar >> make_nvp("data", make_array(&M(0,0),M.rows()*M.cols()));
	}
    }

    ///
    /// Loads an Eigen matrix object from a boost::serialization archive.
    /// This function has several specializations to handle Matrices with
    /// fixed sizes and those with dynamic sizes. This function is called
    /// by the boost serialization code and should not be called directly.
    ///
    /// This specialization handles dynamically-sized sized column vectors
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be loaded
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int B, int C, int D, int E>
    inline void load( Archive & ar,
		      Eigen::Matrix<Scalar,Eigen::Dynamic,B,C,D,E> & M,
		      const unsigned int file_version)
    {
        typedef typename Eigen::Matrix<Scalar,Eigen::Dynamic,B,C,D,E>::Index index_t;
      index_t rows, cols;
      ar >> BOOST_SERIALIZATION_NVP(rows);
      ar >> BOOST_SERIALIZATION_NVP(cols);
      SM_ASSERT_EQ(sm::eigen::SerializationException,cols,B,"Unexpected number of columns found for fixed-sized type");

      M.resize(rows,Eigen::NoChange);
      if(rows > 0 && cols > 0)
	{
	  ar >> make_nvp("data",make_array(&M(0,0),M.rows()*M.cols()));
	}
    }

    ///
    /// Loads an Eigen matrix object from a boost::serialization archive.
    /// This function has several specializations to handle Matrices with
    /// fixed sizes and those with dynamic sizes. This function is called
    /// by the boost serialization code and should not be called directly.
    ///
    /// This specialization handles dynamically sized row vectors
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be loaded
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int A, int C, int D, int E>
    inline void load( Archive & ar,
		      Eigen::Matrix<Scalar,A,Eigen::Dynamic,C,D,E> & M,
		      const unsigned int file_version)
    {
      typedef typename Eigen::Matrix<Scalar,A,Eigen::Dynamic,C,D,E>::Index index_t;
      index_t rows, cols;
      ar >> BOOST_SERIALIZATION_NVP(rows);
      ar >> BOOST_SERIALIZATION_NVP(cols);
      SM_ASSERT_EQ(sm::eigen::SerializationException,rows,A,"Unexpected number of rows found for fixed-sized type");

      M.resize(Eigen::NoChange,cols);
      ar >> make_nvp("data",make_array(&M(0,0),M.rows()*M.cols()));

    }

    ///
    /// Loads an Eigen matrix object from a boost::serialization archive.
    /// This function has several specializations to handle Matrices with
    /// fixed sizes and those with dynamic sizes. This function is called
    /// by the boost serialization code and should not be called directly.
    ///
    /// This specialization handles dynamically sized matrices
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be loaded
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int C, int D, int E>
    inline void load( Archive & ar,
		      Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic,C,D,E> & M,
		      const unsigned int file_version)
    {
      typedef typename Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic,C,D,E>::Index index_t;
      index_t rows, cols;
      ar >> BOOST_SERIALIZATION_NVP(rows);
      ar >> BOOST_SERIALIZATION_NVP(cols);

      M.resize(rows,cols);
      if(rows > 0 && cols > 0)
	{
	  ar >> make_nvp("data", make_array(&M(0,0),M.rows()*M.cols()));
	}
    }

    ///
    /// The function that causes boost::serialization to look for
    /// seperate save() and load() functions when serializing
    /// and Eigen matrix.
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be serialized
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int A, int B, int C, int D, int E>
    inline void serialize(
			  Archive & ar,
			  Eigen::Matrix<Scalar,A,B,C,D,E> & M,
			  const unsigned int file_version
			  ){
      boost::serialization::split_free(ar, M, file_version);
    }

    ///
    /// A function that serialized an Eigen Transform by causing the underlying
    /// Eigen matrix to be serialized.
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The transform to be serialized
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int Dim, int Mode>
     inline void serialize( Archive & ar,
			    Eigen::Transform<Scalar, Dim, Mode> & M,
			    const unsigned int file_version )
     {

       ar & boost::serialization::make_nvp("matrix",M.matrix());
     }

}}

#endif // SM_EIGEN_SERIALIZATION_HPP
<commit_msg>Fixed a bug in the eigen serialization that would cause a segfault on an empty dynamic matrix<commit_after>///
/// @file   EigenSerialization.hpp
/// @author Paul Furgale <paul.furgale@utoronto.ca>
/// @date   Tue Oct 26 12:57:31 2010
///
/// @brief  Functions to serialize dense Eigen matrices.
///
///
///

#ifndef SM_EIGEN_SERIALIZATION_HPP
#define SM_EIGEN_SERIALIZATION_HPP

#include <Eigen/Core>
#include <sm/assert_macros.hpp>
#include <boost/serialization/split_free.hpp>
#include <boost/serialization/nvp.hpp>

namespace sm { namespace eigen {
    // An exception for errors during serialization/deserialization
    SM_DEFINE_EXCEPTION( SerializationException, std::runtime_error );
  }} // namespace sm::eigen

namespace boost { namespace serialization {

    ///
    /// boost::serialization helper function for the Eigen3 Matrix object.
    /// The matrix is a base class for dense matrices.
    /// http://eigen.tuxfamily.org/dox-devel/TutorialMatrixClass.html
    ///
    /// This function should not be called directly but will be called
    /// when serializing a matrix.
    ///
    ///
    /// @param ar  The boost::serialization archive
    /// @param M   The matrix to serialize
    /// @param file_version The file version to be serialized.
    ///
    template<class Archive, class Scalar, int A, int B, int C, int D, int E>
    inline void save( Archive & ar,
		      const Eigen::Matrix<Scalar,A,B,C,D,E> & M,
		      const unsigned int file_version)
    {
      typedef typename Eigen::Matrix<Scalar,A,B,C,D,E>::Index index_t;
      index_t rows = M.rows();
      index_t cols = M.cols();
      ar << BOOST_SERIALIZATION_NVP(rows);
      ar << BOOST_SERIALIZATION_NVP(cols);

      if(rows > 0 && cols > 0)
      {
          ar << make_nvp("data", make_array(&M(0,0),M.rows()*M.cols()));
      }
    }

    ///
    /// Loads an Eigen matrix object from a boost::serialization archive.
    /// This function has several specializations to handle Matrices with
    /// fixed sizes and those with dynamic sizes. This function is called
    /// by the boost serialization code and should not be called directly.
    ///
    /// This specialization handles fixed sized matrices
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be loaded
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int A, int B, int C, int D, int E>
    inline void load( Archive & ar,
		      Eigen::Matrix<Scalar,A,B,C,D,E> & M,
		      const unsigned int file_version)
    {
      typedef typename Eigen::Matrix<Scalar,A,B,C,D,E>::Index index_t;
      index_t rows, cols;
      ar >> BOOST_SERIALIZATION_NVP(rows);
      ar >> BOOST_SERIALIZATION_NVP(cols);
      SM_ASSERT_EQ(sm::eigen::SerializationException,rows,A,"Unexpected number of rows found for fixed-sized type");
      SM_ASSERT_EQ(sm::eigen::SerializationException,cols,B,"Unexpected number of columns found for fixed-sized type");

      if(rows > 0 && cols > 0)
	{
	  ar >> make_nvp("data", make_array(&M(0,0),M.rows()*M.cols()));
	}
    }

    ///
    /// Loads an Eigen matrix object from a boost::serialization archive.
    /// This function has several specializations to handle Matrices with
    /// fixed sizes and those with dynamic sizes. This function is called
    /// by the boost serialization code and should not be called directly.
    ///
    /// This specialization handles dynamically-sized sized column vectors
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be loaded
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int B, int C, int D, int E>
    inline void load( Archive & ar,
		      Eigen::Matrix<Scalar,Eigen::Dynamic,B,C,D,E> & M,
		      const unsigned int file_version)
    {
        typedef typename Eigen::Matrix<Scalar,Eigen::Dynamic,B,C,D,E>::Index index_t;
      index_t rows, cols;
      ar >> BOOST_SERIALIZATION_NVP(rows);
      ar >> BOOST_SERIALIZATION_NVP(cols);
      SM_ASSERT_EQ(sm::eigen::SerializationException,cols,B,"Unexpected number of columns found for fixed-sized type");

      M.resize(rows,Eigen::NoChange);
      if(rows > 0 && cols > 0)
	{
	  ar >> make_nvp("data",make_array(&M(0,0),M.rows()*M.cols()));
	}
    }

    ///
    /// Loads an Eigen matrix object from a boost::serialization archive.
    /// This function has several specializations to handle Matrices with
    /// fixed sizes and those with dynamic sizes. This function is called
    /// by the boost serialization code and should not be called directly.
    ///
    /// This specialization handles dynamically sized row vectors
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be loaded
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int A, int C, int D, int E>
    inline void load( Archive & ar,
		      Eigen::Matrix<Scalar,A,Eigen::Dynamic,C,D,E> & M,
		      const unsigned int file_version)
    {
      typedef typename Eigen::Matrix<Scalar,A,Eigen::Dynamic,C,D,E>::Index index_t;
      index_t rows, cols;
      ar >> BOOST_SERIALIZATION_NVP(rows);
      ar >> BOOST_SERIALIZATION_NVP(cols);
      SM_ASSERT_EQ(sm::eigen::SerializationException,rows,A,"Unexpected number of rows found for fixed-sized type");

      M.resize(Eigen::NoChange,cols);
      ar >> make_nvp("data",make_array(&M(0,0),M.rows()*M.cols()));

    }

    ///
    /// Loads an Eigen matrix object from a boost::serialization archive.
    /// This function has several specializations to handle Matrices with
    /// fixed sizes and those with dynamic sizes. This function is called
    /// by the boost serialization code and should not be called directly.
    ///
    /// This specialization handles dynamically sized matrices
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be loaded
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int C, int D, int E>
    inline void load( Archive & ar,
		      Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic,C,D,E> & M,
		      const unsigned int file_version)
    {
      typedef typename Eigen::Matrix<Scalar,Eigen::Dynamic,Eigen::Dynamic,C,D,E>::Index index_t;
      index_t rows, cols;
      ar >> BOOST_SERIALIZATION_NVP(rows);
      ar >> BOOST_SERIALIZATION_NVP(cols);

      M.resize(rows,cols);
      if(rows > 0 && cols > 0)
	{
	  ar >> make_nvp("data", make_array(&M(0,0),M.rows()*M.cols()));
	}
    }

    ///
    /// The function that causes boost::serialization to look for
    /// seperate save() and load() functions when serializing
    /// and Eigen matrix.
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The matrix to be serialized
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int A, int B, int C, int D, int E>
    inline void serialize(
			  Archive & ar,
			  Eigen::Matrix<Scalar,A,B,C,D,E> & M,
			  const unsigned int file_version
			  ){
      boost::serialization::split_free(ar, M, file_version);
    }

    ///
    /// A function that serialized an Eigen Transform by causing the underlying
    /// Eigen matrix to be serialized.
    ///
    /// @param ar The boost::serialization archive
    /// @param M  The transform to be serialized
    /// @param file_version The archive version number.
    ///
    template<class Archive, class Scalar, int Dim, int Mode>
     inline void serialize( Archive & ar,
			    Eigen::Transform<Scalar, Dim, Mode> & M,
			    const unsigned int file_version )
     {

       ar & boost::serialization::make_nvp("matrix",M.matrix());
     }

}}

#endif // SM_EIGEN_SERIALIZATION_HPP
<|endoftext|>


<commit_before>/*
 *  Copyright 2008-2013 NVIDIA Corporation
 *
 *  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 <thrust/reverse.h>
#include <thrust/detail/type_traits.h>
#include <thrust/iterator/iterator_traits.h>
#include <thrust/system/detail/sequential/stable_merge_sort.h>
#include <thrust/system/detail/sequential/stable_primitive_sort.h>

namespace thrust
{
namespace system
{
namespace detail
{
namespace sequential
{
namespace sort_detail
{

////////////////////
// Primitive Sort //
////////////////////

template<typename KeyType, typename Compare>
struct needs_reverse
  : thrust::detail::integral_constant<
      bool,
      thrust::detail::is_same<Compare, typename thrust::greater<KeyType> >::value
    >
{};

template<typename DerivedPolicy,
         typename RandomAccessIterator,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort(sequential::execution_policy<DerivedPolicy> &exec,
                 RandomAccessIterator first,
                 RandomAccessIterator last,
                 StrictWeakOrdering comp,
                 thrust::detail::true_type)
{
  thrust::system::detail::sequential::stable_primitive_sort(exec, first, last);

  // if comp is greater<T> then reverse the keys
  typedef typename thrust::iterator_traits<RandomAccessIterator>::value_type KeyType;

  if(needs_reverse<KeyType,StrictWeakOrdering>::value)
  {
    thrust::reverse(exec, first, last);
  }
}

template<typename DerivedPolicy,
         typename RandomAccessIterator1,
         typename RandomAccessIterator2,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort_by_key(sequential::execution_policy<DerivedPolicy> &exec,
                        RandomAccessIterator1 first1,
                        RandomAccessIterator1 last1,
                        RandomAccessIterator2 first2,
                        StrictWeakOrdering comp,
                        thrust::detail::true_type)
{
  // if comp is greater<T> then reverse the keys and values
  typedef typename thrust::iterator_traits<RandomAccessIterator1>::value_type KeyType;

  // note, we also have to reverse the (unordered) input to preserve stability
  if(needs_reverse<KeyType,StrictWeakOrdering>::value)
  {
    thrust::reverse(exec, first1,  last1);
    thrust::reverse(exec, first2, first2 + (last1 - first1));
  }

  thrust::system::detail::sequential::stable_primitive_sort_by_key(exec, first1, last1, first2);

  if(needs_reverse<KeyType,StrictWeakOrdering>::value)
  {
    thrust::reverse(exec, first1,  last1);
    thrust::reverse(exec, first2, first2 + (last1 - first1));
  }
}

////////////////
// Merge Sort //
////////////////

template<typename DerivedPolicy,
         typename RandomAccessIterator,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort(sequential::execution_policy<DerivedPolicy> &exec,
                 RandomAccessIterator first,
                 RandomAccessIterator last,
                 StrictWeakOrdering comp,
                 thrust::detail::false_type)
{
  thrust::system::detail::sequential::stable_merge_sort(exec, first, last, comp);
}

template<typename DerivedPolicy,
         typename RandomAccessIterator1,
         typename RandomAccessIterator2,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort_by_key(sequential::execution_policy<DerivedPolicy> &exec,
                        RandomAccessIterator1 first1,
                        RandomAccessIterator1 last1,
                        RandomAccessIterator2 first2,
                        StrictWeakOrdering comp,
                        thrust::detail::false_type)
{
  thrust::system::detail::sequential::stable_merge_sort_by_key(exec, first1, last1, first2, comp);
}

template<typename KeyType, typename Compare>
struct use_primitive_sort
  : thrust::detail::and_<
      thrust::detail::is_arithmetic<KeyType>,
      thrust::detail::or_<
        thrust::detail::is_same<Compare, thrust::less<KeyType> >,
        thrust::detail::is_same<Compare, thrust::greater<KeyType> >
      >
    >
{};

} // end namespace sort_detail

template<typename DerivedPolicy,
         typename RandomAccessIterator,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort(sequential::execution_policy<DerivedPolicy> &exec,
                 RandomAccessIterator first,
                 RandomAccessIterator last,
                 StrictWeakOrdering comp)
{
  typedef typename thrust::iterator_traits<RandomAccessIterator>::value_type KeyType;

  // the compilation time of stable_primitive_sort is too expensive to use within a single CUDA thread
#ifndef __CUDA_ARCH__
  sort_detail::use_primitive_sort<KeyType,StrictWeakOrdering> use_primitive_sort;
#else
  thrust::detail::false_type use_primitive_sort;
#endif

  sort_detail::stable_sort(exec, first, last, comp, use_primitive_sort);
}

template<typename DerivedPolicy,
         typename RandomAccessIterator1,
         typename RandomAccessIterator2,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort_by_key(sequential::execution_policy<DerivedPolicy> &exec,
                        RandomAccessIterator1 first1,
                        RandomAccessIterator1 last1,
                        RandomAccessIterator2 first2,
                        StrictWeakOrdering comp)
{
  typedef typename thrust::iterator_traits<RandomAccessIterator1>::value_type KeyType;

  // the compilation time of stable_primitive_sort_by_key is too expensive to use within a single CUDA thread
#ifndef __CUDA_ARCH__
  sort_detail::use_primitive_sort<KeyType,StrictWeakOrdering> use_primitive_sort;
#else
  thrust::detail::false_type use_primitive_sort;
#endif

  sort_detail::stable_sort_by_key(exec, first1, last1, first2, comp, use_primitive_sort);
}

} // end namespace sequential
} // end namespace detail
} // end namespace system
} // end namespace thrust

<commit_msg>move KeyType inside ifdef statement where it is use to avoid unused type WAR<commit_after>/*
 *  Copyright 2008-2013 NVIDIA Corporation
 *
 *  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 <thrust/reverse.h>
#include <thrust/detail/type_traits.h>
#include <thrust/iterator/iterator_traits.h>
#include <thrust/system/detail/sequential/stable_merge_sort.h>
#include <thrust/system/detail/sequential/stable_primitive_sort.h>

namespace thrust
{
namespace system
{
namespace detail
{
namespace sequential
{
namespace sort_detail
{

////////////////////
// Primitive Sort //
////////////////////

template<typename KeyType, typename Compare>
struct needs_reverse
  : thrust::detail::integral_constant<
      bool,
      thrust::detail::is_same<Compare, typename thrust::greater<KeyType> >::value
    >
{};

template<typename DerivedPolicy,
         typename RandomAccessIterator,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort(sequential::execution_policy<DerivedPolicy> &exec,
                 RandomAccessIterator first,
                 RandomAccessIterator last,
                 StrictWeakOrdering comp,
                 thrust::detail::true_type)
{
  thrust::system::detail::sequential::stable_primitive_sort(exec, first, last);

  // if comp is greater<T> then reverse the keys
  typedef typename thrust::iterator_traits<RandomAccessIterator>::value_type KeyType;

  if(needs_reverse<KeyType,StrictWeakOrdering>::value)
  {
    thrust::reverse(exec, first, last);
  }
}

template<typename DerivedPolicy,
         typename RandomAccessIterator1,
         typename RandomAccessIterator2,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort_by_key(sequential::execution_policy<DerivedPolicy> &exec,
                        RandomAccessIterator1 first1,
                        RandomAccessIterator1 last1,
                        RandomAccessIterator2 first2,
                        StrictWeakOrdering comp,
                        thrust::detail::true_type)
{
  // if comp is greater<T> then reverse the keys and values
  typedef typename thrust::iterator_traits<RandomAccessIterator1>::value_type KeyType;

  // note, we also have to reverse the (unordered) input to preserve stability
  if(needs_reverse<KeyType,StrictWeakOrdering>::value)
  {
    thrust::reverse(exec, first1,  last1);
    thrust::reverse(exec, first2, first2 + (last1 - first1));
  }

  thrust::system::detail::sequential::stable_primitive_sort_by_key(exec, first1, last1, first2);

  if(needs_reverse<KeyType,StrictWeakOrdering>::value)
  {
    thrust::reverse(exec, first1,  last1);
    thrust::reverse(exec, first2, first2 + (last1 - first1));
  }
}

////////////////
// Merge Sort //
////////////////

template<typename DerivedPolicy,
         typename RandomAccessIterator,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort(sequential::execution_policy<DerivedPolicy> &exec,
                 RandomAccessIterator first,
                 RandomAccessIterator last,
                 StrictWeakOrdering comp,
                 thrust::detail::false_type)
{
  thrust::system::detail::sequential::stable_merge_sort(exec, first, last, comp);
}

template<typename DerivedPolicy,
         typename RandomAccessIterator1,
         typename RandomAccessIterator2,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort_by_key(sequential::execution_policy<DerivedPolicy> &exec,
                        RandomAccessIterator1 first1,
                        RandomAccessIterator1 last1,
                        RandomAccessIterator2 first2,
                        StrictWeakOrdering comp,
                        thrust::detail::false_type)
{
  thrust::system::detail::sequential::stable_merge_sort_by_key(exec, first1, last1, first2, comp);
}

template<typename KeyType, typename Compare>
struct use_primitive_sort
  : thrust::detail::and_<
      thrust::detail::is_arithmetic<KeyType>,
      thrust::detail::or_<
        thrust::detail::is_same<Compare, thrust::less<KeyType> >,
        thrust::detail::is_same<Compare, thrust::greater<KeyType> >
      >
    >
{};

} // end namespace sort_detail

template<typename DerivedPolicy,
         typename RandomAccessIterator,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort(sequential::execution_policy<DerivedPolicy> &exec,
                 RandomAccessIterator first,
                 RandomAccessIterator last,
                 StrictWeakOrdering comp)
{

  // the compilation time of stable_primitive_sort is too expensive to use within a single CUDA thread
#ifndef __CUDA_ARCH__
  typedef typename thrust::iterator_traits<RandomAccessIterator>::value_type KeyType;
  sort_detail::use_primitive_sort<KeyType,StrictWeakOrdering> use_primitive_sort;
#else
  thrust::detail::false_type use_primitive_sort;
#endif

  sort_detail::stable_sort(exec, first, last, comp, use_primitive_sort);
}

template<typename DerivedPolicy,
         typename RandomAccessIterator1,
         typename RandomAccessIterator2,
         typename StrictWeakOrdering>
__host__ __device__
void stable_sort_by_key(sequential::execution_policy<DerivedPolicy> &exec,
                        RandomAccessIterator1 first1,
                        RandomAccessIterator1 last1,
                        RandomAccessIterator2 first2,
                        StrictWeakOrdering comp)
{

  // the compilation time of stable_primitive_sort_by_key is too expensive to use within a single CUDA thread
#ifndef __CUDA_ARCH__
  typedef typename thrust::iterator_traits<RandomAccessIterator1>::value_type KeyType;
  sort_detail::use_primitive_sort<KeyType,StrictWeakOrdering> use_primitive_sort;
#else
  thrust::detail::false_type use_primitive_sort;
#endif

  sort_detail::stable_sort_by_key(exec, first1, last1, first2, comp, use_primitive_sort);
}

} // end namespace sequential
} // end namespace detail
} // end namespace system
} // end namespace thrust

<|endoftext|>


<commit_before> /*
 * PassOptimizer.cpp
 *
 *  Created on: Dec 9, 2009
 *      Author: alexgc
 */

#include <boost/foreach.hpp>
#include <gtsam/Ordering.h>
#include <PassOptimizer.hpp>
#include <passOptimization.hpp>

// factors
#include <DrivingFactors.hpp>
#include <ShootingFactors.hpp>
#include <PassingFactors.hpp>

using namespace std;
using namespace boost;
using namespace gtsam;
using namespace Gameplay;
using namespace Optimization;
using namespace Geometry2d;

Gameplay::Optimization::PassOptimizer::PassOptimizer(GameplayModule* gameplay)
: gameplay_(gameplay)
{
	// initialize the parameters

	// NOTE: to make the optimization more aggressive, make these smaller
	fetchSigma = 1.0;
	passRecSigma = 1.5;
	reaimSigma = 0.5;
	shotLengthSigma = 3.0;
	passLengthSigma = 2.0;
	facingSigma = 1.0;
	shotFacingSigma = 1.0;

	// NOTE: to bound the strength of optimization, make this smaller
	priorSigma = 2.0;
}

Gameplay::Optimization::PassOptimizer::~PassOptimizer() {}

PassConfig Gameplay::Optimization::PassOptimizer::optimizePlan(
		const PassConfig& init, bool verbose) const
{
	cout << "Optimizing plan!" << endl;

	// create graph and config
	shared_config config(new Config());
	shared_graph graph(new Graph());

	// initialize current position for opp robot
	BOOST_FOREACH(Gameplay::Robot * robot, gameplay_->opp) {
		if (robot->visible()) {
			int id = robot->id();
			Point pos = robot->pos();
			graph->add(RobotOppConstraint(id, 1, pos));
			config->insert(OppKey(encodeID(id, 1)), rc2gt_Point2(pos));
		}
	}

	// DEBUGGING: a model for priors
	SharedDiagonal prior_model = noiseModel::Isotropic::Sigma(3, priorSigma);
	SharedDiagonal facingModel = noiseModel::Isotropic::Sigma(2, facingSigma);
	SharedDiagonal shotFacingModel = noiseModel::Isotropic::Sigma(2, shotFacingSigma);

	// store shells for robots so we don't copy them in multiple times
	set<int> self_shells;

	// go through initial passconfig and initialize the graph and the config
	size_t curFrame = 1;
	BOOST_FOREACH(PassState s, init.passStateVector) {
		// get the robots
		Robot * r1 = s.robot1, * r2 = s.robot2;

		// get robot id's
		int r1id = s.robot1->id(),
		    r2id = s.robot2->id();

		// extract robot poses and convert
		Pose2 r1pos = rc2gt_Pose2(s.robot1Pos, s.robot1Rot),
			  r2pos = rc2gt_Pose2(s.robot2Pos, s.robot2Rot);

		switch (s.stateType) {
		case PassState::INTERMEDIATE :
			// only for initialization
			if (curFrame == 1) {
				graph->add(RobotSelfConstraint(r1id, 1, r1->pos(), r1->angle()));
				graph->add(RobotSelfConstraint(r2id, 1, r2->pos(), r2->angle()));
				config->insert(SelfKey(encodeID(r1id, 1)), rc2gt_Pose2(r1->pos(), r1->angle()));
				config->insert(SelfKey(encodeID(r2id, 1)), rc2gt_Pose2(r2->pos(), r2->angle()));

				// remember the shells in use
				self_shells.insert(r1id);
				self_shells.insert(r2id);
			}

			break;
		case PassState::KICKPASS :
			// initialize the fetch state for robot 1
			config->insert(SelfKey(encodeID(r1id, 2)), r1pos);

			// add a constraint for this pose, as it is entirely determined by the
			// ball position
			graph->add(SelfConstraint(SelfKey(encodeID(r1id, 2)), r1pos));

			// initialize with receive state for robot 2
			config->insert(SelfKey(encodeID(r2id, 2)), r2pos);

			// add a prior to the new pos
			graph->add(SelfPrior(SelfKey(encodeID(r2id, 2)), r2pos, prior_model));

			// add driving factors from initial state to final state
			graph->add(PathShorteningFactor(SelfKey(encodeID(r2id, 1)),
											SelfKey(encodeID(r2id, 2)), passRecSigma));

			// add pass factors
			graph->add(PassShorteningFactor(SelfKey(encodeID(r1id, 2)),
											SelfKey(encodeID(r2id, 2)), passLengthSigma));

			// add a pass facing factor for each direction
			graph->add(PassFacingFactor(SelfKey(encodeID(r1id, 2)),
										SelfKey(encodeID(r2id, 2)), facingModel));
			graph->add(PassFacingFactor(SelfKey(encodeID(r2id, 2)),
										SelfKey(encodeID(r1id, 2)), facingModel));
			break;
		case PassState::RECEIVEPASS :
			// This state doesn't do anything

			break;
		case PassState::KICKGOAL :
			// initialize reaim state for robot 2
			config->insert(SelfKey(encodeID(r2id, 3)), r2pos);

			// add a prior to the new pos
			graph->add(SelfPrior(SelfKey(encodeID(r2id, 3)), r2pos, prior_model));

			// add aiming factor from previous state
			graph->add(ReaimFactor(SelfKey(encodeID(r2id, 2)),
								   SelfKey(encodeID(r2id, 3)), reaimSigma));

			// add shooting factor on goal
			graph->add(ShotShorteningFactor(SelfKey(encodeID(r2id, 3)), shotLengthSigma));
			graph->add(ShootFacingFactor(SelfKey(encodeID(r2id, 3)), shotFacingModel));
			break;
		}
		++curFrame;
	}

	// initialize current positions of robots not involved
	BOOST_FOREACH(Gameplay::Robot * robot, gameplay_->self) {
		int id = robot->id();
		if (robot->visible() && self_shells.find(id) == self_shells.end()) {
			Point pos = robot->pos();
			float angle = robot->angle();
			graph->add(RobotSelfConstraint(id, 1, pos, angle));
			config->insert(SelfKey(encodeID(id, 1)), rc2gt_Pose2(pos, angle));
		}
	}

	// optimize
	shared_ptr<Ordering> ordering(new Ordering(graph->getOrdering()));
	Optimizer::shared_solver solver(new Optimizer::solver(ordering));
	Optimizer optimizer(graph, config, solver);
	double relThresh = 1e-2;
	double absThresh = 1e-2;
	size_t maxIt = 5;

	// printing
	if (true) {
		graph->print("Graph before optimization");
		config->print("Config before optimization");
	}

	// execute optimization - preferred is LM
	//Optimizer result = optimizer.gaussNewton(relThresh, absThresh, Optimizer::CONFIG);
	Optimizer result = optimizer.levenbergMarquardt(relThresh, absThresh, Optimizer::CONFIG, maxIt);
	//Optimizer result = optimizer.iterateLM();
	//Optimizer result = optimizer.iterate();

	// reconstruct a passconfig by starting with initial config
	Point2 defBallPos(Constants::Robot::Radius+Constants::Ball::Radius, 0.0f);
	PassConfig optConfig(init);
	BOOST_FOREACH(PassState &s, optConfig.passStateVector) {
		// get robot id's
		int r1id = s.robot1->id(),
			r2id = s.robot2->id();

		Point r1t, r2t; float r1r, r2r; Pose2 r1pose, r2pose;
		switch (s.stateType) {
		case PassState::INTERMEDIATE :
			// Just provided initial cases anyway, so no change
			break;

		case PassState::KICKPASS :
			// first state for robot 1
			r1pose = result.config()->at(SelfKey(encodeID(r1id, 2)));
			boost::tie(r1t, r1r) = gt2rc_Pose2(r1pose);
			s.robot1Pos = r1t;
			s.robot1Rot = r1r;

			// receive state for robot 2
			r2pose = result.config()->at(SelfKey(encodeID(r2id, 2)));
			boost::tie(r2t, r2r) = gt2rc_Pose2(r2pose);
			s.robot2Pos = r2t;
			s.robot2Rot = r2r;

			// FIXME: setting the ball pose, likely wrong
			s.ballPos = gt2rc_Point2(r1pose * defBallPos);
			break;

		case PassState::RECEIVEPASS :

			// receive state for robot 2
			r2pose = result.config()->at(SelfKey(encodeID(r2id, 2)));
			boost::tie(r2t, r2r) = gt2rc_Pose2(r2pose);
			s.robot2Pos = r2t;
			s.robot2Rot = r2r;

			// FIXME: setting the ball pose, likely wrong
			s.ballPos = gt2rc_Point2(r2pose * defBallPos);
			break;

		case PassState::KICKGOAL :
			// reaim state for robot 2
			r2pose = result.config()->at(SelfKey(encodeID(r2id, 3)));
			boost::tie(r2t, r2r) = gt2rc_Pose2(r2pose);
			s.robot2Pos = r2t;
			s.robot2Rot = r2r;
			// FIXME: setting the ball pose, likely wrong
			s.ballPos = gt2rc_Point2(r2pose * defBallPos);
			break;
		}
	}
	cout << "   Finished optimizing plan" << endl;

	return optConfig;
}
<commit_msg>Changed PassOptimizer to use the new states<commit_after> /*
 * PassOptimizer.cpp
 *
 *  Created on: Dec 9, 2009
 *      Author: alexgc
 */

#include <boost/foreach.hpp>
#include <gtsam/Ordering.h>
#include <PassOptimizer.hpp>
#include <passOptimization.hpp>

// factors
#include <DrivingFactors.hpp>
#include <ShootingFactors.hpp>
#include <PassingFactors.hpp>

using namespace std;
using namespace boost;
using namespace gtsam;
using namespace Gameplay;
using namespace Optimization;
using namespace Geometry2d;

Gameplay::Optimization::PassOptimizer::PassOptimizer(GameplayModule* gameplay)
: gameplay_(gameplay)
{
	// initialize the parameters

	// NOTE: to make the optimization more aggressive, make these smaller
	fetchSigma = 1.0;
	passRecSigma = 1.5;
	reaimSigma = 0.5;
	shotLengthSigma = 3.0;
	passLengthSigma = 2.0;
	facingSigma = 1.0;
	shotFacingSigma = 1.0;

	// NOTE: to bound the strength of optimization, make this smaller
	priorSigma = 2.0;
}

Gameplay::Optimization::PassOptimizer::~PassOptimizer() {}

PassConfig Gameplay::Optimization::PassOptimizer::optimizePlan(
		const PassConfig& init, bool verbose) const
{
	cout << "Optimizing plan!" << endl;

	// create graph and config
	shared_config config(new Config());
	shared_graph graph(new Graph());

	// initialize current position for opp robot
	BOOST_FOREACH(Gameplay::Robot * robot, gameplay_->opp) {
		if (robot->visible()) {
			int id = robot->id();
			Point pos = robot->pos();
			graph->add(RobotOppConstraint(id, 1, pos));
			config->insert(OppKey(encodeID(id, 1)), rc2gt_Point2(pos));
		}
	}

	// DEBUGGING: a model for priors
	SharedDiagonal prior_model = noiseModel::Isotropic::Sigma(3, priorSigma);
	SharedDiagonal facingModel = noiseModel::Isotropic::Sigma(2, facingSigma);
	SharedDiagonal shotFacingModel = noiseModel::Isotropic::Sigma(2, shotFacingSigma);

	// store shells for robots so we don't copy them in multiple times
	set<int> self_shells;

	// go through initial passconfig and initialize the graph and the config
	size_t curFrame = 1;
	BOOST_FOREACH(PassState s, init.passStateVector) {
		// get the robots
		Robot * r1 = s.robot1, * r2 = s.robot2;

		// get robot id's
		int r1id = s.robot1->id(),
		    r2id = s.robot2->id();

		// extract robot poses and convert
		Pose2 r1pos = rc2gt_Pose2(s.robot1Pos, s.robot1Rot),
			  r2pos = rc2gt_Pose2(s.robot2Pos, s.robot2Rot);

		switch (s.stateType) {
		case PassState::INITIAL:
			graph->add(RobotSelfConstraint(r1id, 1, r1->pos(), r1->angle()));
			graph->add(RobotSelfConstraint(r2id, 1, r2->pos(), r2->angle()));
			config->insert(SelfKey(encodeID(r1id, 1)), rc2gt_Pose2(r1->pos(), r1->angle()));
			config->insert(SelfKey(encodeID(r2id, 1)), rc2gt_Pose2(r2->pos(), r2->angle()));

			// remember the shells in use
			self_shells.insert(r1id);
			self_shells.insert(r2id);

			break;
		case PassState::KICKPASS :
			// initialize the fetch state for robot 1
			config->insert(SelfKey(encodeID(r1id, 2)), r1pos);

			// add a constraint for this pose, as it is entirely determined by the
			// ball position
			graph->add(SelfConstraint(SelfKey(encodeID(r1id, 2)), r1pos));

			// initialize with receive state for robot 2
			config->insert(SelfKey(encodeID(r2id, 2)), r2pos);

			// add a prior to the new pos
			graph->add(SelfPrior(SelfKey(encodeID(r2id, 2)), r2pos, prior_model));

			// add driving factors from initial state to final state
			graph->add(PathShorteningFactor(SelfKey(encodeID(r2id, 1)),
											SelfKey(encodeID(r2id, 2)), passRecSigma));

			// add pass factors
			graph->add(PassShorteningFactor(SelfKey(encodeID(r1id, 2)),
											SelfKey(encodeID(r2id, 2)), passLengthSigma));

			// add a pass facing factor for each direction
			graph->add(PassFacingFactor(SelfKey(encodeID(r1id, 2)),
										SelfKey(encodeID(r2id, 2)), facingModel));
			graph->add(PassFacingFactor(SelfKey(encodeID(r2id, 2)),
										SelfKey(encodeID(r1id, 2)), facingModel));
			break;
		case PassState::RECEIVEPASS :
			// This state doesn't do anything

			break;
		case PassState::KICKGOAL :
			// initialize reaim state for robot 2
			config->insert(SelfKey(encodeID(r2id, 3)), r2pos);

			// add a prior to the new pos
			graph->add(SelfPrior(SelfKey(encodeID(r2id, 3)), r2pos, prior_model));

			// add aiming factor from previous state
			graph->add(ReaimFactor(SelfKey(encodeID(r2id, 2)),
								   SelfKey(encodeID(r2id, 3)), reaimSigma));

			// add shooting factor on goal
			graph->add(ShotShorteningFactor(SelfKey(encodeID(r2id, 3)), shotLengthSigma));
			graph->add(ShootFacingFactor(SelfKey(encodeID(r2id, 3)), shotFacingModel));
			break;
		case PassState::GOAL :
			// does nothing
			break;
		}
		++curFrame;
	}

	// initialize current positions of robots not involved
	BOOST_FOREACH(Gameplay::Robot * robot, gameplay_->self) {
		int id = robot->id();
		if (robot->visible() && self_shells.find(id) == self_shells.end()) {
			Point pos = robot->pos();
			float angle = robot->angle();
			graph->add(RobotSelfConstraint(id, 1, pos, angle));
			config->insert(SelfKey(encodeID(id, 1)), rc2gt_Pose2(pos, angle));
		}
	}

	// optimize
	shared_ptr<Ordering> ordering(new Ordering(graph->getOrdering()));
	Optimizer::shared_solver solver(new Optimizer::solver(ordering));
	Optimizer optimizer(graph, config, solver);
	double relThresh = 1e-2;
	double absThresh = 1e-2;
	size_t maxIt = 5;

	// printing
	if (true) {
		graph->print("Graph before optimization");
		config->print("Config before optimization");
	}

	// execute optimization - preferred is LM
	//Optimizer result = optimizer.gaussNewton(relThresh, absThresh, Optimizer::CONFIG);
	Optimizer result = optimizer.levenbergMarquardt(relThresh, absThresh, Optimizer::CONFIG, maxIt);
	//Optimizer result = optimizer.iterateLM();
	//Optimizer result = optimizer.iterate();

	// reconstruct a passconfig by starting with initial config
	Point2 defBallPos(Constants::Robot::Radius+Constants::Ball::Radius, 0.0f);
	PassConfig optConfig(init);
	BOOST_FOREACH(PassState &s, optConfig.passStateVector) {
		// get robot id's
		int r1id = s.robot1->id(),
			r2id = s.robot2->id();

		Point r1t, r2t; float r1r, r2r; Pose2 r1pose, r2pose;
		switch (s.stateType) {
		case PassState::INITIAL :
			// Just provided initial cases anyway, so no change
			break;

		case PassState::KICKPASS :
			// first state for robot 1
			r1pose = result.config()->at(SelfKey(encodeID(r1id, 2)));
			boost::tie(r1t, r1r) = gt2rc_Pose2(r1pose);
			s.robot1Pos = r1t;
			s.robot1Rot = r1r;

			// receive state for robot 2
			r2pose = result.config()->at(SelfKey(encodeID(r2id, 2)));
			boost::tie(r2t, r2r) = gt2rc_Pose2(r2pose);
			s.robot2Pos = r2t;
			s.robot2Rot = r2r;

			// FIXME: setting the ball pose, likely wrong
			s.ballPos = gt2rc_Point2(r1pose * defBallPos);
			break;

		case PassState::RECEIVEPASS :

			// receive state for robot 2
			r2pose = result.config()->at(SelfKey(encodeID(r2id, 2)));
			boost::tie(r2t, r2r) = gt2rc_Pose2(r2pose);
			s.robot2Pos = r2t;
			s.robot2Rot = r2r;

			// FIXME: setting the ball pose, likely wrong
			s.ballPos = gt2rc_Point2(r2pose * defBallPos);
			break;

		case PassState::KICKGOAL :
			// reaim state for robot 2
			r2pose = result.config()->at(SelfKey(encodeID(r2id, 3)));
			boost::tie(r2t, r2r) = gt2rc_Pose2(r2pose);
			s.robot2Pos = r2t;
			s.robot2Rot = r2r;
			// FIXME: setting the ball pose, likely wrong
			s.ballPos = gt2rc_Point2(r2pose * defBallPos);
			break;
		case PassState::GOAL :
			// does nothing
			break;
		}
	}
	cout << "   Finished optimizing plan" << endl;

	return optConfig;
}
<|endoftext|>


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

#include <system.hh>

#include "journal.h"
#include "amount.h"
#include "commodity.h"
#include "pool.h"
#include "xact.h"
#include "account.h"

namespace ledger {

journal_t::journal_t()
{
  TRACE_CTOR(journal_t, "");
  initialize();
}

journal_t::journal_t(const path& pathname)
{
  TRACE_CTOR(journal_t, "path");
  initialize();
  read(pathname);
}

journal_t::journal_t(const string& str)
{
  TRACE_CTOR(journal_t, "string");
  initialize();
  read(str);
}

journal_t::~journal_t()
{
  TRACE_DTOR(journal_t);

  // Don't bother unhooking each xact's posts from the accounts they refer to,
  // because all accounts are about to be deleted.
  foreach (xact_t * xact, xacts)
    checked_delete(xact);

  foreach (auto_xact_t * xact, auto_xacts)
    checked_delete(xact);

  foreach (period_xact_t * xact, period_xacts)
    checked_delete(xact);

  checked_delete(master);
}

void journal_t::initialize()
{
  master     = new account_t;
  bucket     = NULL;
  was_loaded = false;
}

void journal_t::add_account(account_t * acct)
{
  master->add_account(acct);
}

bool journal_t::remove_account(account_t * acct)
{
  return master->remove_account(acct);
}

account_t * journal_t::find_account(const string& name, bool auto_create)
{
  return master->find_account(name, auto_create);
}

account_t * journal_t::find_account_re(const string& regexp)
{
  return master->find_account_re(regexp);
}

bool journal_t::add_xact(xact_t * xact)
{
  xact->journal = this;

  if (! xact->finalize()) {
    xact->journal = NULL;
    return false;
  }

  extend_xact(xact);
  xacts.push_back(xact);

  return true;
}

void journal_t::extend_xact(xact_base_t * xact)
{
  foreach (auto_xact_t * auto_xact, auto_xacts)
    auto_xact->extend_xact(*xact);
}

bool journal_t::remove_xact(xact_t * xact)
{
  bool found = false;
  xacts_list::iterator i;
  for (i = xacts.begin(); i != xacts.end(); i++)
    if (*i == xact) {
      found = true;
      break;
    }
  if (! found)
    return false;

  xacts.erase(i);
  xact->journal = NULL;

  return true;
}

std::size_t journal_t::read(std::istream& in,
                            const path&   pathname,
                            account_t *   master_alt,
                            scope_t *     scope)
{
  std::size_t count = 0;
  try {
    if (! scope)
      scope = scope_t::default_scope;

    if (! scope)
      throw_(std::runtime_error,
             _("No default scope in which to read journal file '%1'")
             << pathname);

    value_t strict = expr_t("strict").calc(*scope);

    count = parse(in, *scope, master_alt ? master_alt : master,
                  &pathname, strict.to_boolean());
  }
  catch (...) {
    clear_xdata();
    throw;
  }

  // xdata may have been set for some accounts and transaction due to the use
  // of balance assertions or other calculations performed in valexpr-based
  // posting amounts.
  clear_xdata();

  return count;
}

std::size_t journal_t::read(const path& pathname,
                            account_t * master,
                            scope_t *   scope)
{
  path filename = resolve_path(pathname);

  if (! exists(filename))
    throw_(std::runtime_error,
           _("Cannot read journal file '%1'") << filename);

  ifstream stream(filename);
  std::size_t count = read(stream, filename, master, scope);
  if (count > 0)
    sources.push_back(fileinfo_t(filename));
  return count;
}

bool journal_t::has_xdata()
{
  foreach (xact_t * xact, xacts)
    if (xact->has_xdata())
      return true;

  foreach (auto_xact_t * xact, auto_xacts)
    if (xact->has_xdata())
      return true;

  foreach (period_xact_t * xact, period_xacts)
    if (xact->has_xdata())
      return true;

  if (master->has_xdata() || master->children_with_xdata())
    return true;

  return false;
}

void journal_t::clear_xdata()
{
  foreach (xact_t * xact, xacts)
    if (! xact->has_flags(ITEM_TEMP))
      xact->clear_xdata();

  foreach (auto_xact_t * xact, auto_xacts)
    if (! xact->has_flags(ITEM_TEMP))
      xact->clear_xdata();

  foreach (period_xact_t * xact, period_xacts)
    if (! xact->has_flags(ITEM_TEMP))
      xact->clear_xdata();

  master->clear_xdata();
}

bool journal_t::valid() const
{
  if (! master->valid()) {
    DEBUG("ledger.validate", "journal_t: master not valid");
    return false;
  }

  foreach (const xact_t * xact, xacts)
    if (! xact->valid()) {
      DEBUG("ledger.validate", "journal_t: xact not valid");
      return false;
    }

  return true;
}

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

#include <system.hh>

#include "journal.h"
#include "amount.h"
#include "commodity.h"
#include "pool.h"
#include "xact.h"
#include "account.h"

namespace ledger {

journal_t::journal_t()
{
  TRACE_CTOR(journal_t, "");
  initialize();
}

journal_t::journal_t(const path& pathname)
{
  TRACE_CTOR(journal_t, "path");
  initialize();
  read(pathname);
}

journal_t::journal_t(const string& str)
{
  TRACE_CTOR(journal_t, "string");
  initialize();
  read(str);
}

journal_t::~journal_t()
{
  TRACE_DTOR(journal_t);

  // Don't bother unhooking each xact's posts from the accounts they refer to,
  // because all accounts are about to be deleted.
  foreach (xact_t * xact, xacts)
    checked_delete(xact);

  foreach (auto_xact_t * xact, auto_xacts)
    checked_delete(xact);

  foreach (period_xact_t * xact, period_xacts)
    checked_delete(xact);

  checked_delete(master);
}

void journal_t::initialize()
{
  master     = new account_t;
  bucket     = NULL;
  was_loaded = false;
}

void journal_t::add_account(account_t * acct)
{
  master->add_account(acct);
}

bool journal_t::remove_account(account_t * acct)
{
  return master->remove_account(acct);
}

account_t * journal_t::find_account(const string& name, bool auto_create)
{
  return master->find_account(name, auto_create);
}

account_t * journal_t::find_account_re(const string& regexp)
{
  return master->find_account_re(regexp);
}

bool journal_t::add_xact(xact_t * xact)
{
  xact->journal = this;

  if (! xact->finalize()) {
    xact->journal = NULL;
    return false;
  }

  extend_xact(xact);
  xacts.push_back(xact);

  return true;
}

void journal_t::extend_xact(xact_base_t * xact)
{
  foreach (auto_xact_t * auto_xact, auto_xacts)
    auto_xact->extend_xact(*xact);
}

bool journal_t::remove_xact(xact_t * xact)
{
  bool found = false;
  xacts_list::iterator i;
  for (i = xacts.begin(); i != xacts.end(); i++)
    if (*i == xact) {
      found = true;
      break;
    }
  if (! found)
    return false;

  xacts.erase(i);
  xact->journal = NULL;

  return true;
}

std::size_t journal_t::read(std::istream& in,
                            const path&   pathname,
                            account_t *   master_alt,
                            scope_t *     scope)
{
  std::size_t count = 0;
  try {
    if (! scope)
      scope = scope_t::default_scope;

    if (! scope)
      throw_(std::runtime_error,
             _("No default scope in which to read journal file '%1'")
             << pathname);

    value_t strict = expr_t("strict").calc(*scope);

    count = parse(in, *scope, master_alt ? master_alt : master,
                  &pathname, strict.to_boolean());
  }
  catch (...) {
    clear_xdata();
    throw;
  }

  // xdata may have been set for some accounts and transaction due to the use
  // of balance assertions or other calculations performed in valexpr-based
  // posting amounts.
  clear_xdata();

  return count;
}

std::size_t journal_t::read(const path& pathname,
                            account_t * master_account,
                            scope_t *   scope)
{
  path filename = resolve_path(pathname);

  if (! exists(filename))
    throw_(std::runtime_error,
           _("Cannot read journal file '%1'") << filename);

  ifstream stream(filename);
  std::size_t count = read(stream, filename, master_account, scope);
  if (count > 0)
    sources.push_back(fileinfo_t(filename));
  return count;
}

bool journal_t::has_xdata()
{
  foreach (xact_t * xact, xacts)
    if (xact->has_xdata())
      return true;

  foreach (auto_xact_t * xact, auto_xacts)
    if (xact->has_xdata())
      return true;

  foreach (period_xact_t * xact, period_xacts)
    if (xact->has_xdata())
      return true;

  if (master->has_xdata() || master->children_with_xdata())
    return true;

  return false;
}

void journal_t::clear_xdata()
{
  foreach (xact_t * xact, xacts)
    if (! xact->has_flags(ITEM_TEMP))
      xact->clear_xdata();

  foreach (auto_xact_t * xact, auto_xacts)
    if (! xact->has_flags(ITEM_TEMP))
      xact->clear_xdata();

  foreach (period_xact_t * xact, period_xacts)
    if (! xact->has_flags(ITEM_TEMP))
      xact->clear_xdata();

  master->clear_xdata();
}

bool journal_t::valid() const
{
  if (! master->valid()) {
    DEBUG("ledger.validate", "journal_t: master not valid");
    return false;
  }

  foreach (const xact_t * xact, xacts)
    if (! xact->valid()) {
      DEBUG("ledger.validate", "journal_t: xact not valid");
      return false;
    }

  return true;
}

} // namespace ledger
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 "tools/trace_to_text/trace_to_systrace.h"

#include <inttypes.h>
#include <stdio.h>

#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <utility>

#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/paged_memory.h"
#include "perfetto/ext/base/string_writer.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/trace_processor/trace_processor.h"

// When running in Web Assembly, fflush() is a no-op and the stdio buffering
// sends progress updates to JS only when a write ends with \n.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
#define PROGRESS_CHAR "\n"
#else
#define PROGRESS_CHAR "\r"
#endif

namespace perfetto {
namespace trace_to_text {

namespace {

// Having an empty traceEvents object is necessary for trace viewer to
// load the json properly.
const char kTraceHeader[] = R"({
  "traceEvents": [],
)";

const char kTraceFooter[] = R"(\n",
  "controllerTraceDataKey": "systraceController"
})";

const char kProcessDumpHeader[] =
    ""
    "\"androidProcessDump\": "
    "\"PROCESS DUMP\\nUSER           PID  PPID     VSZ    RSS WCHAN  "
    "PC S NAME                        COMM                       \\n";

const char kThreadHeader[] = "USER           PID   TID CMD \\n";

const char kSystemTraceEvents[] =
    ""
    "  \"systemTraceEvents\": \"";

const char kFtraceHeader[] =
    "# tracer: nop\n"
    "#\n"
    "# entries-in-buffer/entries-written: 30624/30624   #P:4\n"
    "#\n"
    "#                                      _-----=> irqs-off\n"
    "#                                     / _----=> need-resched\n"
    "#                                    | / _---=> hardirq/softirq\n"
    "#                                    || / _--=> preempt-depth\n"
    "#                                    ||| /     delay\n"
    "#           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION\n"
    "#              | |        |      |   ||||       |         |\n";

const char kFtraceJsonHeader[] =
    "# tracer: nop\\n"
    "#\\n"
    "# entries-in-buffer/entries-written: 30624/30624   #P:4\\n"
    "#\\n"
    "#                                      _-----=> irqs-off\\n"
    "#                                     / _----=> need-resched\\n"
    "#                                    | / _---=> hardirq/softirq\\n"
    "#                                    || / _--=> preempt-depth\\n"
    "#                                    ||| /     delay\\n"
    "#           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION\\n"
    "#              | |        |      |   ||||       |         |\\n";

inline void FormatProcess(uint32_t pid,
                          uint32_t ppid,
                          const base::StringView& name,
                          base::StringWriter* writer) {
  writer->AppendLiteral("root             ");
  writer->AppendInt(pid);
  writer->AppendLiteral("     ");
  writer->AppendInt(ppid);
  writer->AppendLiteral("   00000   000 null 0000000000 S ");
  writer->AppendString(name);
  writer->AppendLiteral("         null");
}

inline void FormatThread(uint32_t tid,
                         uint32_t tgid,
                         const base::StringView& name,
                         base::StringWriter* writer) {
  writer->AppendLiteral("root         ");
  writer->AppendInt(tgid);
  writer->AppendChar(' ');
  writer->AppendInt(tid);
  writer->AppendChar(' ');
  if (name.empty()) {
    writer->AppendLiteral("<...>");
  } else {
    writer->AppendString(name);
  }
}

class QueryWriter {
 public:
  QueryWriter(trace_processor::TraceProcessor* tp, std::ostream* output)
      : tp_(tp),
        buffer_(base::PagedMemory::Allocate(kBufferSize)),
        global_writer_(static_cast<char*>(buffer_.Get()), kBufferSize),
        output_(output) {}

  template <typename Callback>
  bool RunQuery(const std::string& sql, Callback callback) {
    char buffer[2048];
    auto iterator = tp_->ExecuteQuery(sql);
    for (uint32_t rows = 0; iterator.Next(); rows++) {
      base::StringWriter line_writer(buffer, base::ArraySize(buffer));
      callback(&iterator, &line_writer);

      if (global_writer_.pos() + line_writer.pos() >= global_writer_.size()) {
        fprintf(stderr, "Writing row %" PRIu32 PROGRESS_CHAR, rows);
        auto str = global_writer_.GetStringView();
        output_->write(str.data(), static_cast<std::streamsize>(str.size()));
        global_writer_.reset();
      }
      global_writer_.AppendStringView(line_writer.GetStringView());
    }

    // Check if we have an error in the iterator and print if so.
    auto status = iterator.Status();
    if (!status.ok()) {
      PERFETTO_ELOG("Error while writing systrace %s", status.c_message());
      return false;
    }

    // Flush any dangling pieces in the global writer.
    auto str = global_writer_.GetStringView();
    output_->write(str.data(), static_cast<std::streamsize>(str.size()));
    global_writer_.reset();
    return true;
  }

 private:
  static constexpr uint32_t kBufferSize = 1024u * 1024u * 16u;

  trace_processor::TraceProcessor* tp_ = nullptr;
  base::PagedMemory buffer_;
  base::StringWriter global_writer_;
  std::ostream* output_ = nullptr;
};

}  // namespace

int TraceToSystrace(std::istream* input,
                    std::ostream* output,
                    bool wrap_in_json) {
  trace_processor::Config config;
  std::unique_ptr<trace_processor::TraceProcessor> tp =
      trace_processor::TraceProcessor::CreateInstance(config);

  // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
  constexpr size_t kChunkSize = 1024 * 1024;

// Printing the status update on stderr can be a perf bottleneck. On WASM print
// status updates more frequently because it can be slower to parse each chunk.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
  constexpr int kStderrRate = 1;
#else
  constexpr int kStderrRate = 128;
#endif
  uint64_t file_size = 0;
  for (int i = 0;; i++) {
    if (i % kStderrRate == 0) {
      fprintf(stderr, "Loading trace %.2f MB" PROGRESS_CHAR, file_size / 1.0e6);
      fflush(stderr);
    }

    std::unique_ptr<uint8_t[]> buf(new uint8_t[kChunkSize]);
    input->read(reinterpret_cast<char*>(buf.get()), kChunkSize);
    if (input->bad()) {
      PERFETTO_ELOG("Failed when reading trace");
      return 1;
    }

    auto rsize = input->gcount();
    if (rsize <= 0)
      break;
    file_size += static_cast<uint64_t>(rsize);
    tp->Parse(std::move(buf), static_cast<size_t>(rsize));
  }
  tp->NotifyEndOfFile();

  fprintf(stderr, "Loaded trace" PROGRESS_CHAR);
  fflush(stderr);

  using Iterator = trace_processor::TraceProcessor::Iterator;

  QueryWriter q_writer(tp.get(), output);
  if (wrap_in_json) {
    *output << kTraceHeader;

    *output << kProcessDumpHeader;

    // Write out all the processes in the trace.
    // TODO(lalitm): change this query to actually use ppid when it is exposed
    // by the process table.
    static const char kPSql[] = "select pid, 0 as ppid, name from process";
    auto p_callback = [](Iterator* it, base::StringWriter* writer) {
      uint32_t pid = static_cast<uint32_t>(it->Get(0 /* col */).long_value);
      uint32_t ppid = static_cast<uint32_t>(it->Get(1 /* col */).long_value);
      const auto& name_col = it->Get(2 /* col */);
      auto name_view = name_col.type == trace_processor::SqlValue::kString
                           ? base::StringView(name_col.string_value)
                           : base::StringView();
      FormatProcess(pid, ppid, name_view, writer);
    };
    if (!q_writer.RunQuery(kPSql, p_callback))
      return 1;

    *output << kThreadHeader;

    // Write out all the threads in the trace.
    static const char kTSql[] =
        "select tid, COALESCE(upid, 0), thread.name "
        "from thread left join process using (upid)";
    auto t_callback = [](Iterator* it, base::StringWriter* writer) {
      uint32_t tid = static_cast<uint32_t>(it->Get(0 /* col */).long_value);
      uint32_t tgid = static_cast<uint32_t>(it->Get(1 /* col */).long_value);
      const auto& name_col = it->Get(2 /* col */);
      auto name_view = name_col.type == trace_processor::SqlValue::kString
                           ? base::StringView(name_col.string_value)
                           : base::StringView();
      FormatThread(tid, tgid, name_view, writer);
    };
    if (!q_writer.RunQuery(kTSql, t_callback))
      return 1;

    *output << "\",";
    *output << kSystemTraceEvents;
    *output << kFtraceJsonHeader;
  } else {
    *output << "TRACE:\n";
    *output << kFtraceHeader;
  }

  fprintf(stderr, "Converting trace events" PROGRESS_CHAR);
  fflush(stderr);

  static const char kRawSql[] = "select to_ftrace(id) from raw";
  auto raw_callback = [wrap_in_json](Iterator* it, base::StringWriter* writer) {
    const char* line = it->Get(0 /* col */).string_value;
    if (wrap_in_json) {
      for (uint32_t i = 0; line[i] != '\0'; i++) {
        char c = line[i];
        if (c == '\n') {
          writer->AppendLiteral("\\n");
          continue;
        }

        if (c == '\\' || c == '"')
          writer->AppendChar('\\');
        writer->AppendChar(c);
      }
      writer->AppendChar('\\');
      writer->AppendChar('n');
    } else {
      writer->AppendString(line);
      writer->AppendChar('\n');
    }
  };
  if (!q_writer.RunQuery(kRawSql, raw_callback))
    return 1;

  if (wrap_in_json)
    *output << kTraceFooter;

  return 0;
}

}  // namespace trace_to_text
}  // namespace perfetto
<commit_msg>trace_to_text: Handle unusual control characters<commit_after>/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * 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 "tools/trace_to_text/trace_to_systrace.h"

#include <inttypes.h>
#include <stdio.h>

#include <algorithm>
#include <functional>
#include <map>
#include <memory>
#include <utility>

#include "perfetto/base/build_config.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/paged_memory.h"
#include "perfetto/ext/base/string_writer.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/trace_processor/trace_processor.h"

// When running in Web Assembly, fflush() is a no-op and the stdio buffering
// sends progress updates to JS only when a write ends with \n.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
#define PROGRESS_CHAR "\n"
#else
#define PROGRESS_CHAR "\r"
#endif

namespace perfetto {
namespace trace_to_text {

namespace {

// Having an empty traceEvents object is necessary for trace viewer to
// load the json properly.
const char kTraceHeader[] = R"({
  "traceEvents": [],
)";

const char kTraceFooter[] = R"(\n",
  "controllerTraceDataKey": "systraceController"
})";

const char kProcessDumpHeader[] =
    ""
    "\"androidProcessDump\": "
    "\"PROCESS DUMP\\nUSER           PID  PPID     VSZ    RSS WCHAN  "
    "PC S NAME                        COMM                       \\n";

const char kThreadHeader[] = "USER           PID   TID CMD \\n";

const char kSystemTraceEvents[] =
    ""
    "  \"systemTraceEvents\": \"";

const char kFtraceHeader[] =
    "# tracer: nop\n"
    "#\n"
    "# entries-in-buffer/entries-written: 30624/30624   #P:4\n"
    "#\n"
    "#                                      _-----=> irqs-off\n"
    "#                                     / _----=> need-resched\n"
    "#                                    | / _---=> hardirq/softirq\n"
    "#                                    || / _--=> preempt-depth\n"
    "#                                    ||| /     delay\n"
    "#           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION\n"
    "#              | |        |      |   ||||       |         |\n";

const char kFtraceJsonHeader[] =
    "# tracer: nop\\n"
    "#\\n"
    "# entries-in-buffer/entries-written: 30624/30624   #P:4\\n"
    "#\\n"
    "#                                      _-----=> irqs-off\\n"
    "#                                     / _----=> need-resched\\n"
    "#                                    | / _---=> hardirq/softirq\\n"
    "#                                    || / _--=> preempt-depth\\n"
    "#                                    ||| /     delay\\n"
    "#           TASK-PID    TGID   CPU#  ||||    TIMESTAMP  FUNCTION\\n"
    "#              | |        |      |   ||||       |         |\\n";

inline void FormatProcess(uint32_t pid,
                          uint32_t ppid,
                          const base::StringView& name,
                          base::StringWriter* writer) {
  writer->AppendLiteral("root             ");
  writer->AppendInt(pid);
  writer->AppendLiteral("     ");
  writer->AppendInt(ppid);
  writer->AppendLiteral("   00000   000 null 0000000000 S ");
  writer->AppendString(name);
  writer->AppendLiteral("         null");
}

inline void FormatThread(uint32_t tid,
                         uint32_t tgid,
                         const base::StringView& name,
                         base::StringWriter* writer) {
  writer->AppendLiteral("root         ");
  writer->AppendInt(tgid);
  writer->AppendChar(' ');
  writer->AppendInt(tid);
  writer->AppendChar(' ');
  if (name.empty()) {
    writer->AppendLiteral("<...>");
  } else {
    writer->AppendString(name);
  }
}

class QueryWriter {
 public:
  QueryWriter(trace_processor::TraceProcessor* tp, std::ostream* output)
      : tp_(tp),
        buffer_(base::PagedMemory::Allocate(kBufferSize)),
        global_writer_(static_cast<char*>(buffer_.Get()), kBufferSize),
        output_(output) {}

  template <typename Callback>
  bool RunQuery(const std::string& sql, Callback callback) {
    char buffer[2048];
    auto iterator = tp_->ExecuteQuery(sql);
    for (uint32_t rows = 0; iterator.Next(); rows++) {
      base::StringWriter line_writer(buffer, base::ArraySize(buffer));
      callback(&iterator, &line_writer);

      if (global_writer_.pos() + line_writer.pos() >= global_writer_.size()) {
        fprintf(stderr, "Writing row %" PRIu32 PROGRESS_CHAR, rows);
        auto str = global_writer_.GetStringView();
        output_->write(str.data(), static_cast<std::streamsize>(str.size()));
        global_writer_.reset();
      }
      global_writer_.AppendStringView(line_writer.GetStringView());
    }

    // Check if we have an error in the iterator and print if so.
    auto status = iterator.Status();
    if (!status.ok()) {
      PERFETTO_ELOG("Error while writing systrace %s", status.c_message());
      return false;
    }

    // Flush any dangling pieces in the global writer.
    auto str = global_writer_.GetStringView();
    output_->write(str.data(), static_cast<std::streamsize>(str.size()));
    global_writer_.reset();
    return true;
  }

 private:
  static constexpr uint32_t kBufferSize = 1024u * 1024u * 16u;

  trace_processor::TraceProcessor* tp_ = nullptr;
  base::PagedMemory buffer_;
  base::StringWriter global_writer_;
  std::ostream* output_ = nullptr;
};

}  // namespace

int TraceToSystrace(std::istream* input,
                    std::ostream* output,
                    bool wrap_in_json) {
  trace_processor::Config config;
  std::unique_ptr<trace_processor::TraceProcessor> tp =
      trace_processor::TraceProcessor::CreateInstance(config);

  // 1MB chunk size seems the best tradeoff on a MacBook Pro 2013 - i7 2.8 GHz.
  constexpr size_t kChunkSize = 1024 * 1024;

// Printing the status update on stderr can be a perf bottleneck. On WASM print
// status updates more frequently because it can be slower to parse each chunk.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
  constexpr int kStderrRate = 1;
#else
  constexpr int kStderrRate = 128;
#endif
  uint64_t file_size = 0;
  for (int i = 0;; i++) {
    if (i % kStderrRate == 0) {
      fprintf(stderr, "Loading trace %.2f MB" PROGRESS_CHAR, file_size / 1.0e6);
      fflush(stderr);
    }

    std::unique_ptr<uint8_t[]> buf(new uint8_t[kChunkSize]);
    input->read(reinterpret_cast<char*>(buf.get()), kChunkSize);
    if (input->bad()) {
      PERFETTO_ELOG("Failed when reading trace");
      return 1;
    }

    auto rsize = input->gcount();
    if (rsize <= 0)
      break;
    file_size += static_cast<uint64_t>(rsize);
    tp->Parse(std::move(buf), static_cast<size_t>(rsize));
  }
  tp->NotifyEndOfFile();

  fprintf(stderr, "Loaded trace" PROGRESS_CHAR);
  fflush(stderr);

  using Iterator = trace_processor::TraceProcessor::Iterator;

  QueryWriter q_writer(tp.get(), output);
  if (wrap_in_json) {
    *output << kTraceHeader;

    *output << kProcessDumpHeader;

    // Write out all the processes in the trace.
    // TODO(lalitm): change this query to actually use ppid when it is exposed
    // by the process table.
    static const char kPSql[] = "select pid, 0 as ppid, name from process";
    auto p_callback = [](Iterator* it, base::StringWriter* writer) {
      uint32_t pid = static_cast<uint32_t>(it->Get(0 /* col */).long_value);
      uint32_t ppid = static_cast<uint32_t>(it->Get(1 /* col */).long_value);
      const auto& name_col = it->Get(2 /* col */);
      auto name_view = name_col.type == trace_processor::SqlValue::kString
                           ? base::StringView(name_col.string_value)
                           : base::StringView();
      FormatProcess(pid, ppid, name_view, writer);
    };
    if (!q_writer.RunQuery(kPSql, p_callback))
      return 1;

    *output << kThreadHeader;

    // Write out all the threads in the trace.
    static const char kTSql[] =
        "select tid, COALESCE(upid, 0), thread.name "
        "from thread left join process using (upid)";
    auto t_callback = [](Iterator* it, base::StringWriter* writer) {
      uint32_t tid = static_cast<uint32_t>(it->Get(0 /* col */).long_value);
      uint32_t tgid = static_cast<uint32_t>(it->Get(1 /* col */).long_value);
      const auto& name_col = it->Get(2 /* col */);
      auto name_view = name_col.type == trace_processor::SqlValue::kString
                           ? base::StringView(name_col.string_value)
                           : base::StringView();
      FormatThread(tid, tgid, name_view, writer);
    };
    if (!q_writer.RunQuery(kTSql, t_callback))
      return 1;

    *output << "\",";
    *output << kSystemTraceEvents;
    *output << kFtraceJsonHeader;
  } else {
    *output << "TRACE:\n";
    *output << kFtraceHeader;
  }

  fprintf(stderr, "Converting trace events" PROGRESS_CHAR);
  fflush(stderr);

  static const char kRawSql[] = "select to_ftrace(id) from raw";
  auto raw_callback = [wrap_in_json](Iterator* it, base::StringWriter* writer) {
    const char* line = it->Get(0 /* col */).string_value;
    if (wrap_in_json) {
      for (uint32_t i = 0; line[i] != '\0'; i++) {
        char c = line[i];
        switch (c) {
          case '\n':
            writer->AppendLiteral("\\n");
            break;
          case '\f':
            writer->AppendLiteral("\\f");
            break;
          case '\b':
            writer->AppendLiteral("\\b");
            break;
          case '\r':
            writer->AppendLiteral("\\r");
            break;
          case '\t':
            writer->AppendLiteral("\\t");
            break;
          case '\\':
            writer->AppendLiteral("\\\\");
            break;
          case '"':
            writer->AppendLiteral("\\\"");
            break;
          default:
            writer->AppendChar(c);
            break;
        }
      }
      writer->AppendChar('\\');
      writer->AppendChar('n');
    } else {
      writer->AppendString(line);
      writer->AppendChar('\n');
    }
  };
  if (!q_writer.RunQuery(kRawSql, raw_callback))
    return 1;

  if (wrap_in_json)
    *output << kTraceFooter;

  return 0;
}

}  // namespace trace_to_text
}  // namespace perfetto
<|endoftext|>