<commit_before>//---------------------------------------------------------------------------//
/*
  Copyright (c) 2012, Stuart R. Slattery
  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 the University of Wisconsin - Madison 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
  HOLDER 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.
*/
//---------------------------------------------------------------------------//
/*!
 * \file MCLS_EpetraAdapater.hpp
 * \author Stuart R. Slattery
 * \brief Epetra Helpers.
 */
//---------------------------------------------------------------------------//

#ifndef MCLS_EPETRAHELPERS_HPP
#define MCLS_EPETRAHELPERS_HPP

#include <MCLS_DBC.hpp>

#include <Teuchos_RCP.hpp>
#include <Teuchos_Array.hpp>
#include <Teuchos_ArrayView.hpp>
#include <Teuchos_as.hpp>

#include <Epetra_Map.h>
#include <Epetra_RowMatrix.h>
#include <Epetra_CrsMatrix.h>
#include <Epetra_Export.h>
#include <Epetra_RowMatrixTransposer.h>

#include <EpetraExt_MatrixMatrix.h>

//---------------------------------------------------------------------------//
/*!
 * \class UndefinedEpetraHelpers
 * \brief Class for undefined EpetraHelper functions.
 *
 * Will throw a compile-time error if these traits are not specialized.
 */
template<class Matrix>
struct UndefinedEpetraHelpers
{
    static inline void notDefined()
    {
	return Matrix::this_type_is_missing_a_specialization();
    }
};

namespace MCLS
{
//---------------------------------------------------------------------------//
/*!
 * \class EpetraMatrixHelpers
 * \brief Helper functions for Epetra implementations.
 */
template<class Matrix>
class EpetraMatrixHelpers
{
  public:

    //@{
    //! Typedefs.
    typedef Matrix                                  matrix_type;
    //@}

    /*!
     * \brief Get the on-process global matrix column indices that, as global
     * row indices, are off-process.
     */
    static Teuchos::Array<int> getOffProcColsAsRows( const Matrix& matrix )
    {
	UndefinedEpetraHelpers<Matrix>::notDefined();
	return Teuchos::Array<int>(0);
    }

    /*!
     * \brief Get a copy of the transpose of a matrix.
     */
    static Teuchos::RCP<matrix_type> copyTranspose( const matrix_type& matrix )
    {
	UndefinedEpetraHelpers<Matrix>::notDefined();
	return Teuchos::null;
    }

    /*
     * \brief Create a reference-counted pointer to a new matrix with a
     * specified number of off-process nearest-neighbor global rows.
     */
    static Teuchos::RCP<matrix_type> copyNearestNeighbors(
    	const matrix_type& matrix, const int& num_neighbors )
    {
	UndefinedEpetraHelpers<Matrix>::notDefined();
	return Teuchos::null;
    }

    /*!
     * \brief Matrix-Matrix multiply C = A*B
     */
    static void multiply( const Teuchos::RCP<const matrix_type>& A,
			  const Teuchos::RCP<const matrix_type>& B,
			  const Teuchos::RCP<matrix_type>& C )
    { UndefinedEpetraHelpers<Matrix>::notDefined(); }
};

//---------------------------------------------------------------------------//
/*!
 * \class EpetraMatrixHelpers
 * \brief EpetraMatrixHelpers specialization for Epetra_RowMatrix.
 */
template<>
class EpetraMatrixHelpers<Epetra_RowMatrix>
{
  public:

    //@{
    //! Typedefs.
    typedef Epetra_RowMatrix               matrix_type;
    //@}

    /*!
     * \brief Get the on-process global matrix column indices that, as global
     * row indices, are off-process.
     */
    static Teuchos::Array<int> getOffProcColsAsRows( const matrix_type& matrix )
    {
	MCLS_REQUIRE( matrix.Filled() );

	const Epetra_Map& row_map = matrix.RowMatrixRowMap();
	const Epetra_Map& col_map = matrix.RowMatrixColMap();

	Teuchos::ArrayView<const int> global_cols( col_map.MyGlobalElements(),
						   col_map.NumMyElements() );

	Teuchos::Array<int> off_proc_cols(0);
	Teuchos::ArrayView<const int>::const_iterator global_col_it;
	for ( global_col_it = global_cols.begin();
	      global_col_it != global_cols.end();
	      ++global_col_it )
	{
	    if ( !row_map.MyGID( *global_col_it ) )
	    {
		off_proc_cols.push_back( *global_col_it );
	    }
	}

	return off_proc_cols;
    }

    /*!
     * \brief Get a copy of the transpose of a matrix.
     */
    static Teuchos::RCP<matrix_type> copyTranspose( const matrix_type& matrix )
    {
	Epetra_RowMatrixTransposer transposer( const_cast<matrix_type*>(&matrix) );

	Epetra_CrsMatrix* transpose_matrix;
	transposer.CreateTranspose( true, transpose_matrix );

	MCLS_ENSURE( transpose_matrix->Filled() );
	return Teuchos::RCP<matrix_type>( transpose_matrix );
    }

    /*
     * \brief Create a reference-counted pointer to a new matrix with a
     * specified number of off-process nearest-neighbor global rows.
     */
    static Teuchos::RCP<matrix_type> copyNearestNeighbors(
    	const matrix_type& matrix, const int& num_neighbors )
    {
	MCLS_REQUIRE( num_neighbors >= 0 );

	// Setup for neighbor construction.
	Teuchos::RCP<const Epetra_Map> empty_map = Teuchos::rcp(
	    new Epetra_Map( 0, 0, matrix.Comm() ) );
	Teuchos::RCP<Epetra_CrsMatrix> neighbor_matrix =
	    Teuchos::rcp( new Epetra_CrsMatrix( Copy, *empty_map, 0 ) );
	neighbor_matrix->FillComplete();

	Teuchos::ArrayView<const int> global_rows;
	Teuchos::ArrayView<const int>::const_iterator global_rows_it;
	Teuchos::Array<int>::iterator ghost_global_bound;

	// Get the initial off proc columns.
	Teuchos::Array<int> ghost_global_rows = getOffProcColsAsRows( matrix );

	// Build the neighbors by traversing the graph.
	for ( int i = 0; i < num_neighbors; ++i )
	{
	    // Get rid of the global rows that belong to the original
	    // matrix. We don't need to store these, just the neighbors.
	    global_rows = Teuchos::ArrayView<const int>(
		matrix.RowMatrixRowMap().MyGlobalElements(),
		matrix.RowMatrixRowMap().NumMyElements() );
	    for ( global_rows_it = global_rows.begin();
		  global_rows_it != global_rows.end();
		  ++global_rows_it )
	    {
		ghost_global_bound = std::remove( ghost_global_rows.begin(),
						  ghost_global_rows.end(),
						  *global_rows_it );
		ghost_global_rows.resize( std::distance(ghost_global_rows.begin(),
							ghost_global_bound) );
	    }

	    // Get the current set of global rows in the neighbor matrix.
	    global_rows = Teuchos::ArrayView<const int>(
		neighbor_matrix->RowMatrixRowMap().MyGlobalElements(),
		neighbor_matrix->RowMatrixRowMap().NumMyElements() );

	    // Append the on proc neighbor columns to the off proc columns.
	    for ( global_rows_it = global_rows.begin();
		  global_rows_it != global_rows.end();
		  ++global_rows_it )
	    {
		ghost_global_rows.push_back( *global_rows_it );
	    }

	    // Make a new map of the combined global rows and off proc columns.
	    Teuchos::RCP<const Epetra_Map> ghost_map = Teuchos::rcp(
		new Epetra_Map( -1,
				Teuchos::as<int>(ghost_global_rows.size()),
				ghost_global_rows.getRawPtr(),
				0,
				neighbor_matrix->Comm() ) );

	    // Export the neighbor matrix with the new neighbor.
	    Epetra_Export ghost_exporter( matrix.RowMatrixRowMap(), *ghost_map );

	    neighbor_matrix = Teuchos::rcp(
		new Epetra_CrsMatrix( Copy, *ghost_map, 0 ) );

	    neighbor_matrix->Export( matrix, ghost_exporter, Insert );
	    neighbor_matrix->FillComplete();

	    // Get the next rows in the graph.
	    ghost_global_rows = getOffProcColsAsRows( *neighbor_matrix );
	}

	MCLS_ENSURE( !neighbor_matrix.is_null() );
	MCLS_ENSURE( neighbor_matrix->Filled() );
	return neighbor_matrix;
    }

    /*!
     * \brief Matrix-Matrix multiply C = A*B
     */
    static void multiply( const Teuchos::RCP<const matrix_type>& A,
			  const Teuchos::RCP<const matrix_type>& B,
			  const Teuchos::RCP<matrix_type>& C )
    {
	Teuchos::RCP<const Epetra_CrsMatrix> A_crs =
	    Teuchos::rcp_dynamic_cast<const Epetra_CrsMatrix>( A );

	Teuchos::RCP<const Epetra_CrsMatrix> B_crs =
	    Teuchos::rcp_dynamic_cast<const Epetra_CrsMatrix>( B );

	Teuchos::RCP<Epetra_CrsMatrix> C_crs =
	    Teuchos::rcp_dynamic_cast<Epetra_CrsMatrix>( C );

	if ( Teuchos::is_null(A_crs) )
	{
	    A_crs = createCrsMatrix( A );
	}

	if ( Teuchos::is_null(B_crs) )
	{
	    B_crs = createCrsMatrix( B );
	}

	if ( Teuchos::is_null(C_crs) )
	{
	    C_crs = Teuchos::rcp(
		new Epetra_CrsMatrix(Copy, A->RowMatrixRowMap(), 0) );
	}

	EpetraExt::MatrixMatrix::Multiply(
	    *A_crs, false, *B_crs, false, *C_crs );
    }

    /*!
     * \brief Create a copy of a RowMatrix in a CrsMatrix.
     */
    static Teuchos::RCP<Epetra_CrsMatrix>
    createCrsMatrix( const Teuchos::RCP<const matrix_type>& A )
    {
	Teuchos::RCP<Epetra_CrsMatrix> A_crs = Teuchos::rcp(
	    new Epetra_CrsMatrix(Copy, A->RowMatrixRowMap(),
				 A->RowMatrixColMap(),A->MaxNumEntries()) );

	int max_row_entries = A->MaxNumEntries();
	int num_local_rows = A->NumMyRows();
	int num_entries = 0;

	Teuchos::Array<double> values( max_row_entries );
	Teuchos::Array<int> indices( max_row_entries );

	for ( int n = 0; n < num_local_rows; ++n )
	{
	    A->ExtractMyRowCopy( n, max_row_entries, num_entries,
				 values.getRawPtr(), indices.getRawPtr() );

	    A_crs->ReplaceMyValues( n, num_entries,
				    values.getRawPtr(), indices.getRawPtr() );
	}

	A_crs->FillComplete();

	return A_crs;
    }
};

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

} // end namespace MCLS

#endif // end MCLS_EPETRAHELPERS_HPP

//---------------------------------------------------------------------------//
// end MCLS_EpetraHelpers.hpp
//---------------------------------------------------------------------------//
<commit_msg>fixing erroneous crs matrix creation in matrix-matrix multiply<commit_after>//---------------------------------------------------------------------------//
/*
  Copyright (c) 2012, Stuart R. Slattery
  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 the University of Wisconsin - Madison 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
  HOLDER 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.
*/
//---------------------------------------------------------------------------//
/*!
 * \file MCLS_EpetraAdapater.hpp
 * \author Stuart R. Slattery
 * \brief Epetra Helpers.
 */
//---------------------------------------------------------------------------//

#ifndef MCLS_EPETRAHELPERS_HPP
#define MCLS_EPETRAHELPERS_HPP

#include <MCLS_DBC.hpp>

#include <Teuchos_RCP.hpp>
#include <Teuchos_Array.hpp>
#include <Teuchos_ArrayView.hpp>
#include <Teuchos_as.hpp>

#include <Epetra_Map.h>
#include <Epetra_RowMatrix.h>
#include <Epetra_CrsMatrix.h>
#include <Epetra_Export.h>
#include <Epetra_RowMatrixTransposer.h>

#include <EpetraExt_MatrixMatrix.h>

//---------------------------------------------------------------------------//
/*!
 * \class UndefinedEpetraHelpers
 * \brief Class for undefined EpetraHelper functions.
 *
 * Will throw a compile-time error if these traits are not specialized.
 */
template<class Matrix>
struct UndefinedEpetraHelpers
{
    static inline void notDefined()
    {
	return Matrix::this_type_is_missing_a_specialization();
    }
};

namespace MCLS
{
//---------------------------------------------------------------------------//
/*!
 * \class EpetraMatrixHelpers
 * \brief Helper functions for Epetra implementations.
 */
template<class Matrix>
class EpetraMatrixHelpers
{
  public:

    //@{
    //! Typedefs.
    typedef Matrix                                  matrix_type;
    //@}

    /*!
     * \brief Get the on-process global matrix column indices that, as global
     * row indices, are off-process.
     */
    static Teuchos::Array<int> getOffProcColsAsRows( const Matrix& matrix )
    {
	UndefinedEpetraHelpers<Matrix>::notDefined();
	return Teuchos::Array<int>(0);
    }

    /*!
     * \brief Get a copy of the transpose of a matrix.
     */
    static Teuchos::RCP<matrix_type> copyTranspose( const matrix_type& matrix )
    {
	UndefinedEpetraHelpers<Matrix>::notDefined();
	return Teuchos::null;
    }

    /*
     * \brief Create a reference-counted pointer to a new matrix with a
     * specified number of off-process nearest-neighbor global rows.
     */
    static Teuchos::RCP<matrix_type> copyNearestNeighbors(
    	const matrix_type& matrix, const int& num_neighbors )
    {
	UndefinedEpetraHelpers<Matrix>::notDefined();
	return Teuchos::null;
    }

    /*!
     * \brief Matrix-Matrix multiply C = A*B
     */
    static void multiply( const Teuchos::RCP<const matrix_type>& A,
			  const Teuchos::RCP<const matrix_type>& B,
			  const Teuchos::RCP<matrix_type>& C )
    { UndefinedEpetraHelpers<Matrix>::notDefined(); }
};

//---------------------------------------------------------------------------//
/*!
 * \class EpetraMatrixHelpers
 * \brief EpetraMatrixHelpers specialization for Epetra_RowMatrix.
 */
template<>
class EpetraMatrixHelpers<Epetra_RowMatrix>
{
  public:

    //@{
    //! Typedefs.
    typedef Epetra_RowMatrix               matrix_type;
    //@}

    /*!
     * \brief Get the on-process global matrix column indices that, as global
     * row indices, are off-process.
     */
    static Teuchos::Array<int> getOffProcColsAsRows( const matrix_type& matrix )
    {
	MCLS_REQUIRE( matrix.Filled() );

	const Epetra_Map& row_map = matrix.RowMatrixRowMap();
	const Epetra_Map& col_map = matrix.RowMatrixColMap();

	Teuchos::ArrayView<const int> global_cols( col_map.MyGlobalElements(),
						   col_map.NumMyElements() );

	Teuchos::Array<int> off_proc_cols(0);
	Teuchos::ArrayView<const int>::const_iterator global_col_it;
	for ( global_col_it = global_cols.begin();
	      global_col_it != global_cols.end();
	      ++global_col_it )
	{
	    if ( !row_map.MyGID( *global_col_it ) )
	    {
		off_proc_cols.push_back( *global_col_it );
	    }
	}

	return off_proc_cols;
    }

    /*!
     * \brief Get a copy of the transpose of a matrix.
     */
    static Teuchos::RCP<matrix_type> copyTranspose( const matrix_type& matrix )
    {
	Epetra_RowMatrixTransposer transposer( const_cast<matrix_type*>(&matrix) );

	Epetra_CrsMatrix* transpose_matrix;
	transposer.CreateTranspose( true, transpose_matrix );

	MCLS_ENSURE( transpose_matrix->Filled() );
	return Teuchos::RCP<matrix_type>( transpose_matrix );
    }

    /*
     * \brief Create a reference-counted pointer to a new matrix with a
     * specified number of off-process nearest-neighbor global rows.
     */
    static Teuchos::RCP<matrix_type> copyNearestNeighbors(
    	const matrix_type& matrix, const int& num_neighbors )
    {
	MCLS_REQUIRE( num_neighbors >= 0 );

	// Setup for neighbor construction.
	Teuchos::RCP<const Epetra_Map> empty_map = Teuchos::rcp(
	    new Epetra_Map( 0, 0, matrix.Comm() ) );
	Teuchos::RCP<Epetra_CrsMatrix> neighbor_matrix =
	    Teuchos::rcp( new Epetra_CrsMatrix( Copy, *empty_map, 0 ) );
	neighbor_matrix->FillComplete();

	Teuchos::ArrayView<const int> global_rows;
	Teuchos::ArrayView<const int>::const_iterator global_rows_it;
	Teuchos::Array<int>::iterator ghost_global_bound;

	// Get the initial off proc columns.
	Teuchos::Array<int> ghost_global_rows = getOffProcColsAsRows( matrix );

	// Build the neighbors by traversing the graph.
	for ( int i = 0; i < num_neighbors; ++i )
	{
	    // Get rid of the global rows that belong to the original
	    // matrix. We don't need to store these, just the neighbors.
	    global_rows = Teuchos::ArrayView<const int>(
		matrix.RowMatrixRowMap().MyGlobalElements(),
		matrix.RowMatrixRowMap().NumMyElements() );
	    for ( global_rows_it = global_rows.begin();
		  global_rows_it != global_rows.end();
		  ++global_rows_it )
	    {
		ghost_global_bound = std::remove( ghost_global_rows.begin(),
						  ghost_global_rows.end(),
						  *global_rows_it );
		ghost_global_rows.resize( std::distance(ghost_global_rows.begin(),
							ghost_global_bound) );
	    }

	    // Get the current set of global rows in the neighbor matrix.
	    global_rows = Teuchos::ArrayView<const int>(
		neighbor_matrix->RowMatrixRowMap().MyGlobalElements(),
		neighbor_matrix->RowMatrixRowMap().NumMyElements() );

	    // Append the on proc neighbor columns to the off proc columns.
	    for ( global_rows_it = global_rows.begin();
		  global_rows_it != global_rows.end();
		  ++global_rows_it )
	    {
		ghost_global_rows.push_back( *global_rows_it );
	    }

	    // Make a new map of the combined global rows and off proc columns.
	    Teuchos::RCP<const Epetra_Map> ghost_map = Teuchos::rcp(
		new Epetra_Map( -1,
				Teuchos::as<int>(ghost_global_rows.size()),
				ghost_global_rows.getRawPtr(),
				0,
				neighbor_matrix->Comm() ) );

	    // Export the neighbor matrix with the new neighbor.
	    Epetra_Export ghost_exporter( matrix.RowMatrixRowMap(), *ghost_map );

	    neighbor_matrix = Teuchos::rcp(
		new Epetra_CrsMatrix( Copy, *ghost_map, 0 ) );

	    neighbor_matrix->Export( matrix, ghost_exporter, Insert );
	    neighbor_matrix->FillComplete();

	    // Get the next rows in the graph.
	    ghost_global_rows = getOffProcColsAsRows( *neighbor_matrix );
	}

	MCLS_ENSURE( !neighbor_matrix.is_null() );
	MCLS_ENSURE( neighbor_matrix->Filled() );
	return neighbor_matrix;
    }

    /*!
     * \brief Matrix-Matrix multiply C = A*B
     */
    static void multiply( const Teuchos::RCP<const matrix_type>& A,
			  const Teuchos::RCP<const matrix_type>& B,
			  const Teuchos::RCP<matrix_type>& C )
    {
	Teuchos::RCP<const Epetra_CrsMatrix> A_crs =
	    Teuchos::rcp_dynamic_cast<const Epetra_CrsMatrix>( A );

	Teuchos::RCP<const Epetra_CrsMatrix> B_crs =
	    Teuchos::rcp_dynamic_cast<const Epetra_CrsMatrix>( B );

	Teuchos::RCP<Epetra_CrsMatrix> C_crs =
	    Teuchos::rcp_dynamic_cast<Epetra_CrsMatrix>( C );

	if ( Teuchos::is_null(A_crs) )
	{
	    A_crs = createCrsMatrix( A );
	}

	if ( Teuchos::is_null(B_crs) )
	{
	    B_crs = createCrsMatrix( B );
	}

	if ( Teuchos::is_null(C_crs) )
	{
	    C_crs = Teuchos::rcp(
		new Epetra_CrsMatrix(Copy, A->RowMatrixRowMap(), 0) );
	}

	EpetraExt::MatrixMatrix::Multiply(
	    *A_crs, false, *B_crs, false, *C_crs );
    }

    /*!
     * \brief Create a copy of a RowMatrix in a CrsMatrix.
     */
    static Teuchos::RCP<Epetra_CrsMatrix>
    createCrsMatrix( const Teuchos::RCP<const matrix_type>& A )
    {
	Teuchos::RCP<Epetra_CrsMatrix> A_crs = Teuchos::rcp(
	    new Epetra_CrsMatrix(Copy, A->RowMatrixRowMap(),
				 A->RowMatrixColMap(),A->MaxNumEntries()) );

	int max_row_entries = A->MaxNumEntries();
	int num_local_rows = A->NumMyRows();
	int num_entries = 0;

	Teuchos::Array<double> values( max_row_entries );
	Teuchos::Array<int> indices( max_row_entries );

	for ( int n = 0; n < num_local_rows; ++n )
	{
	    A->ExtractMyRowCopy( n, max_row_entries, num_entries,
				 values.getRawPtr(), indices.getRawPtr() );

	    A_crs->InsertMyValues( n, num_entries,
				   values.getRawPtr(), indices.getRawPtr() );
	}

	A_crs->FillComplete();

	return A_crs;
    }
};

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

} // end namespace MCLS

#endif // end MCLS_EPETRAHELPERS_HPP

//---------------------------------------------------------------------------//
// end MCLS_EpetraHelpers.hpp
//---------------------------------------------------------------------------//
<|endoftext|>


<commit_before>/******************************************************************
*
*	CyberX3D for C++
*
*	Copyright (C) Satoshi Konno 1996-2007
*
*	File:	BoxNode.cpp
*
******************************************************************/

#include <cybergarage/x3d/BoxNode.h>
#include <cybergarage/x3d/Graphic3D.h>

using namespace CyberX3D;

BoxNode::BoxNode()
{
	setHeaderFlag(false);
	setType(BOX_NODE);

	// size exposed field
	sizeField = new SFVec3f(2.0f, 2.0f, 2.0f);
	sizeField->setName(sizeFieldString);
	addExposedField(sizeField);
}

BoxNode::~BoxNode()
{
}

////////////////////////////////////////////////
//	size
////////////////////////////////////////////////

SFVec3f *BoxNode::getSizeField() const
{
	if (isInstanceNode() == false)
		return sizeField;
	return (SFVec3f *)getExposedField(sizeFieldString);
}

void BoxNode::setSize(float value[])
{
	getSizeField()->setValue(value);
}

void BoxNode::setSize(float x, float y, float z)
{
	getSizeField()->setValue(x, y, z);
}

void BoxNode::getSize(float value[]) const
{
	getSizeField()->getValue(value);
}

float BoxNode::getX() const
{
	return getSizeField()->getX();
}

float BoxNode::getY()const
{
	return getSizeField()->getY();
}

float BoxNode::getZ()const
{
	return getSizeField()->getZ();
}

////////////////////////////////////////////////
//	List
////////////////////////////////////////////////

BoxNode *BoxNode::next()const
{
	return (BoxNode *)Node::next(getType());
}

BoxNode *BoxNode::nextTraversal()const
{
	return (BoxNode *)Node::nextTraversalByType(getType());
}

////////////////////////////////////////////////
//	functions
////////////////////////////////////////////////

bool BoxNode::isChildNodeType(Node *node) const
{
	return false;
}

void BoxNode::initialize()
{
	recomputeBoundingBox();
#ifdef CX3D_SUPPORT_OPENGL
	recomputeDisplayList();
#endif
}

void BoxNode::uninitialize()
{
}

void BoxNode::update()
{
}

////////////////////////////////////////////////
//	Infomation
////////////////////////////////////////////////

void BoxNode::outputContext(std::ostream &printStream, const char *indentString) const
{
	SFVec3f *size = getSizeField();
	printStream << indentString << "\t" << "size " << size << std::endl;
}

////////////////////////////////////////////////
//	BoxNode::recomputeBoundingBox
////////////////////////////////////////////////

void BoxNode::recomputeBoundingBox()
{
	setBoundingBoxCenter(0.0f, 0.0f, 0.0f);
	setBoundingBoxSize(getX()/2.0f, getY()/2.0f, getZ()/2.0f);
}

////////////////////////////////////////////////
//	BoxNode::getVertexArray
////////////////////////////////////////////////

size_t BoxNode::getNumVertexArrays()
{
	return 1;
}

void BoxNode::getVertexArray(VertexArray& array, size_t id)
{
	if (id != 0) return;

	VertexFormat format;
	format.addAttribute<float>("position", 3);
	format.addAttribute<float>("normal", 3);
	format.addAttribute<float>("texcoord", 2);
	array = VertexArray(6 * 4, 0, false, format);
}

////////////////////////////////////////////////
//	BoxNode::getVertexData
////////////////////////////////////////////////

void BoxNode::getVertexData(size_t id, void *vertex_data)
{
	if (id >= getNumVertexArrays()) {
		return;
	}

	VertexArray array;
	getVertexArray(array, id);
 	const VertexFormat& format = array.getFormat();

	static float n[6][3] = {
			{0.0, 0.0, 1.0}, {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0},
			{0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}};

	static int faces[6][4] = {
			{ 3, 2, 1, 0 }, { 7, 6, 2, 3 }, { 4, 5, 6, 7 },
			{ 0, 1, 5, 4 }, { 1, 2, 6, 5 }, { 3, 0, 4, 7 }};

	static float t[4][2] = {
			{ 0.0f, 1.0f }, { 1.0f, 1.0f },
			{ 1.0f, 0.0f }, { 0.0f, 0.0f } };

	float v[8][3];

	v[0][0] = v[3][0] = v[4][0] = v[7][0] = -getX()/2.0f;
	v[1][0] = v[2][0] = v[5][0] = v[6][0] = getX()/2.0f;
	v[2][1] = v[3][1] = v[6][1] = v[7][1] = -getY()/2.0f;
	v[0][1] = v[1][1] = v[4][1] = v[5][1] = getY()/2.0f;
	v[4][2] = v[5][2] = v[6][2] = v[7][2] = -getZ()/2.0f;
	v[0][2] = v[1][2] = v[2][2] = v[3][2] = getZ()/2.0f;

	char* buffer = (char*)vertex_data;

	for (int i = 0; i < 6; i++) {
	for (int j = 0; j < 4; j++) {

                for (int k = 0; k < format.getNumAttributes(); ++k) {
			const Attribute* attrib = format.getAttribute(k);
			memcpy(buffer + getIndexForVert(i*j, array, *attrib),
			       v[faces[i][j]], attrib->getByteSize());
		}

	}
	}
}

////////////////////////////////////////////////
//	DrawBox
//
//	   4+--------+5
//	   /|       /|
//	  / |      / |
//	0+--------+1 |
//	 |  |     |  |
//	 | 7+-----|--+6
//	 | /      | /
//	 |/       |/
//	3+--------+2
//
////////////////////////////////////////////////

#ifdef CX3D_SUPPORT_OPENGL

static void DrawBox(float x0, float x1, float y0, float y1,	float z0, float z1)
{
    static float n[6][3] = {
			{0.0, 0.0, 1.0}, {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0},
			{0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}};

    static int faces[6][4] = {
			{ 3, 2, 1, 0 }, { 7, 6, 2, 3 }, { 4, 5, 6, 7 },
			{ 0, 1, 5, 4 }, { 1, 2, 6, 5 }, { 3, 0, 4, 7 }};

    static float t[4][2] = {
			{ 0.0f, 1.0f }, { 1.0f, 1.0f },
			{ 1.0f, 0.0f }, { 0.0f, 0.0f } };

    float	v[8][3];

	v[0][0] = v[3][0] = v[4][0] = v[7][0] = x0;
	v[1][0] = v[2][0] = v[5][0] = v[6][0] = x1;
	v[2][1] = v[3][1] = v[6][1] = v[7][1] = y0;
	v[0][1] = v[1][1] = v[4][1] = v[5][1] = y1;
	v[4][2] = v[5][2] = v[6][2] = v[7][2] = z0;
	v[0][2] = v[1][2] = v[2][2] = v[3][2] = z1;

	glFrontFace(GL_CCW);

    for (int i = 0; i < 6; i++) {
		glBegin(GL_POLYGON);
		glNormal3fv(n[i]);
		glTexCoord2fv(t[0]);
		glVertex3fv(v[faces[i][0]]);
		glTexCoord2fv(t[1]);
		glVertex3fv(v[faces[i][1]]);
		glTexCoord2fv(t[2]);
		glVertex3fv(v[faces[i][2]]);
		glTexCoord2fv(t[3]);
		glVertex3fv(v[faces[i][3]]);
		glEnd();
    }
}

////////////////////////////////////////////////
//	BoxNode::recomputeDisplayList
////////////////////////////////////////////////

void BoxNode::recomputeDisplayList() {
	unsigned int nCurrentDisplayList = getDisplayList();
	if (0 < nCurrentDisplayList)
		glDeleteLists(nCurrentDisplayList, 1);

	unsigned int nNewDisplayList = glGenLists(1);
	glNewList(nNewDisplayList, GL_COMPILE);
		DrawBox(-getX()/2.0f, getX()/2.0f, -getY()/2.0f, getY()/2.0f, -getZ()/2.0f, getZ()/2.0f);
	glEndList();

	setDisplayList(nNewDisplayList);
};

#endif
<commit_msg>Update cube generation<commit_after>/******************************************************************
*
*	CyberX3D for C++
*
*	Copyright (C) Satoshi Konno 1996-2007
*
*	File:	BoxNode.cpp
*
******************************************************************/

#include <cybergarage/x3d/BoxNode.h>
#include <cybergarage/x3d/Graphic3D.h>

using namespace CyberX3D;

BoxNode::BoxNode()
{
	setHeaderFlag(false);
	setType(BOX_NODE);

	// size exposed field
	sizeField = new SFVec3f(2.0f, 2.0f, 2.0f);
	sizeField->setName(sizeFieldString);
	addExposedField(sizeField);
}

BoxNode::~BoxNode()
{
}

////////////////////////////////////////////////
//	size
////////////////////////////////////////////////

SFVec3f *BoxNode::getSizeField() const
{
	if (isInstanceNode() == false)
		return sizeField;
	return (SFVec3f *)getExposedField(sizeFieldString);
}

void BoxNode::setSize(float value[])
{
	getSizeField()->setValue(value);
}

void BoxNode::setSize(float x, float y, float z)
{
	getSizeField()->setValue(x, y, z);
}

void BoxNode::getSize(float value[]) const
{
	getSizeField()->getValue(value);
}

float BoxNode::getX() const
{
	return getSizeField()->getX();
}

float BoxNode::getY()const
{
	return getSizeField()->getY();
}

float BoxNode::getZ()const
{
	return getSizeField()->getZ();
}

////////////////////////////////////////////////
//	List
////////////////////////////////////////////////

BoxNode *BoxNode::next()const
{
	return (BoxNode *)Node::next(getType());
}

BoxNode *BoxNode::nextTraversal()const
{
	return (BoxNode *)Node::nextTraversalByType(getType());
}

////////////////////////////////////////////////
//	functions
////////////////////////////////////////////////

bool BoxNode::isChildNodeType(Node *node) const
{
	return false;
}

void BoxNode::initialize()
{
	recomputeBoundingBox();
#ifdef CX3D_SUPPORT_OPENGL
	recomputeDisplayList();
#endif
}

void BoxNode::uninitialize()
{
}

void BoxNode::update()
{
}

////////////////////////////////////////////////
//	Infomation
////////////////////////////////////////////////

void BoxNode::outputContext(std::ostream &printStream, const char *indentString) const
{
	SFVec3f *size = getSizeField();
	printStream << indentString << "\t" << "size " << size << std::endl;
}

////////////////////////////////////////////////
//	BoxNode::recomputeBoundingBox
////////////////////////////////////////////////

void BoxNode::recomputeBoundingBox()
{
	setBoundingBoxCenter(0.0f, 0.0f, 0.0f);
	setBoundingBoxSize(getX()/2.0f, getY()/2.0f, getZ()/2.0f);
}

////////////////////////////////////////////////
//	BoxNode::getVertexArray
////////////////////////////////////////////////

size_t BoxNode::getNumVertexArrays()
{
	return 1;
}

void BoxNode::getVertexArray(VertexArray& array, size_t id)
{
	if (id != 0) return;

	VertexFormat format;
	format.addAttribute<float>("position", 3);
	format.addAttribute<float>("normal", 3);
	format.addAttribute<float>("texcoord", 2);
	array = VertexArray(36, 0, false, format);
}

////////////////////////////////////////////////
//	BoxNode::getVertexData
////////////////////////////////////////////////

void BoxNode::getVertexData(size_t id, void *vertex_data)
{
	if (id >= getNumVertexArrays()) {
		return;
	}

	VertexArray array;
	getVertexArray(array, id);
 	const VertexFormat& format = array.getFormat();

	static float n[6][3] = {
			{0.0, 0.0, 1.0}, {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0},
			{0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}};

	static int faces[6][6] = {
			{ 3, 2, 1, 1, 0, 3 }, { 7, 6, 2, 2, 3, 7 },
			{ 4, 5, 6, 6, 7, 4 }, { 0, 1, 5, 5, 4, 0 },
			{ 1, 2, 6, 6, 5,1  }, { 3, 0, 4, 4, 7, 3 }};

	static float t[6][2] = {
			{ 0.0f, 1.0f }, { 1.0f, 1.0f },
			{ 1.0f, 0.0f }, { 1.0f, 0.0f },
			{ 0.0f, 0.0f }, { 0.0f, 1.0f } };

	float v[8][3];

	v[0][0] = v[3][0] = v[4][0] = v[7][0] = -getX()/2.0f;
	v[1][0] = v[2][0] = v[5][0] = v[6][0] = getX()/2.0f;
	v[2][1] = v[3][1] = v[6][1] = v[7][1] = -getY()/2.0f;
	v[0][1] = v[1][1] = v[4][1] = v[5][1] = getY()/2.0f;
	v[4][2] = v[5][2] = v[6][2] = v[7][2] = -getZ()/2.0f;
	v[0][2] = v[1][2] = v[2][2] = v[3][2] = getZ()/2.0f;

	char* buffer = (char*)vertex_data;

	const Attribute& position = *format.getAttribute(0);
	const Attribute& normal = *format.getAttribute(1);
	const Attribute& tex_coord = *format.getAttribute(2);

	for (int i = 0; i < 6; i++) {
		for (int j = 0; j < 6; j++) {
			const int vertex = i*6 + j;
			memcpy(buffer + getIndexForVert(vertex, array, position),
			       v[faces[i][j]], position.getByteSize());
			memcpy(buffer + getIndexForVert(vertex, array, normal),
			       n[i], normal.getByteSize());
			memcpy(buffer + getIndexForVert(vertex, array, tex_coord),
			       t[j], tex_coord.getByteSize());
		}
	}
}

////////////////////////////////////////////////
//	DrawBox
//
//	   4+--------+5
//	   /|       /|
//	  / |      / |
//	0+--------+1 |
//	 |  |     |  |
//	 | 7+-----|--+6
//	 | /      | /
//	 |/       |/
//	3+--------+2
//
////////////////////////////////////////////////

#ifdef CX3D_SUPPORT_OPENGL

static void DrawBox(float x0, float x1, float y0, float y1,	float z0, float z1)
{
    static float n[6][3] = {
			{0.0, 0.0, 1.0}, {0.0, -1.0, 0.0}, {0.0, 0.0, 1.0},
			{0.0, 1.0, 0.0}, {1.0, 0.0, 0.0}, {-1.0, 0.0, 0.0}};

    static int faces[6][4] = {
			{ 3, 2, 1, 0 }, { 7, 6, 2, 3 }, { 4, 5, 6, 7 },
			{ 0, 1, 5, 4 }, { 1, 2, 6, 5 }, { 3, 0, 4, 7 }};

    static float t[4][2] = {
			{ 0.0f, 1.0f }, { 1.0f, 1.0f },
			{ 1.0f, 0.0f }, { 0.0f, 0.0f } };

    float	v[8][3];

	v[0][0] = v[3][0] = v[4][0] = v[7][0] = x0;
	v[1][0] = v[2][0] = v[5][0] = v[6][0] = x1;
	v[2][1] = v[3][1] = v[6][1] = v[7][1] = y0;
	v[0][1] = v[1][1] = v[4][1] = v[5][1] = y1;
	v[4][2] = v[5][2] = v[6][2] = v[7][2] = z0;
	v[0][2] = v[1][2] = v[2][2] = v[3][2] = z1;

	glFrontFace(GL_CCW);

    for (int i = 0; i < 6; i++) {
		glBegin(GL_POLYGON);
		glNormal3fv(n[i]);
		glTexCoord2fv(t[0]);
		glVertex3fv(v[faces[i][0]]);
		glTexCoord2fv(t[1]);
		glVertex3fv(v[faces[i][1]]);
		glTexCoord2fv(t[2]);
		glVertex3fv(v[faces[i][2]]);
		glTexCoord2fv(t[3]);
		glVertex3fv(v[faces[i][3]]);
		glEnd();
    }
}

////////////////////////////////////////////////
//	BoxNode::recomputeDisplayList
////////////////////////////////////////////////

void BoxNode::recomputeDisplayList() {
	unsigned int nCurrentDisplayList = getDisplayList();
	if (0 < nCurrentDisplayList)
		glDeleteLists(nCurrentDisplayList, 1);

	unsigned int nNewDisplayList = glGenLists(1);
	glNewList(nNewDisplayList, GL_COMPILE);
		DrawBox(-getX()/2.0f, getX()/2.0f, -getY()/2.0f, getY()/2.0f, -getZ()/2.0f, getZ()/2.0f);
	glEndList();

	setDisplayList(nNewDisplayList);
};

#endif
<|endoftext|>


<commit_before>/* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "BloomFilterWrap.h"

namespace BloomFilterWrap {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Boolean;
using v8::Value;

Persistent<Function> BloomFilterWrap::constructor;

BloomFilterWrap::BloomFilterWrap(unsigned int bitsPerElement,
    unsigned int estimatedNumElements, HashFn hashFns[], int numHashFns)
  : BloomFilter(bitsPerElement, estimatedNumElements, hashFns, numHashFns) {
}

BloomFilterWrap::~BloomFilterWrap() {
}

void BloomFilterWrap::Init(Local<Object> exports) {
  Isolate* isolate = exports->GetIsolate();

  // Prepare constructor template
  Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
  tpl->SetClassName(String::NewFromUtf8(isolate, "BloomFilter"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  // Prototype
  NODE_SET_PROTOTYPE_METHOD(tpl, "add", BloomFilterWrap::Add);
  NODE_SET_PROTOTYPE_METHOD(tpl, "exists", BloomFilterWrap::Exists);

  constructor.Reset(isolate, tpl->GetFunction());
  exports->Set(String::NewFromUtf8(isolate, "BloomFilter"),
               tpl->GetFunction());
}

void BloomFilterWrap::New(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  if (args.IsConstructCall()) {
    // Invoked as constructor: `new BloomFilter(...)`
    BloomFilterWrap* obj = new BloomFilterWrap();
    obj->Wrap(args.This());
    args.GetReturnValue().Set(args.This());
  } else {
    // Invoked as plain function `BloomFilter(...)`, turn into construct call.
    const int argc = 1;
    Local<Value> argv[argc] = { args[0] };
    Local<Context> context = isolate->GetCurrentContext();
    Local<Function> cons = Local<Function>::New(isolate, constructor);
    Local<Object> result =
        cons->NewInstance(context, argc, argv).ToLocalChecked();
    args.GetReturnValue().Set(result);
  }
}

void BloomFilterWrap::Add(const FunctionCallbackInfo<Value>& args) {
  String::Utf8Value str(args[0]->ToString());
  const char * buffer = *str;

  BloomFilterWrap* obj = ObjectWrap::Unwrap<BloomFilterWrap>(args.Holder());
  obj->add(buffer);
}

void BloomFilterWrap::Exists(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  String::Utf8Value str(args[0]->ToString());
  const char * buffer = *str;

  BloomFilterWrap* obj = ObjectWrap::Unwrap<BloomFilterWrap>(args.Holder());
  bool exists = obj->exists(buffer);

  args.GetReturnValue().Set(Boolean::New(isolate, exists));
}

}  // namespace BloomFilterWrap
<commit_msg>Update deprecated calls for v8/node<commit_after>/* Copyright (c) 2015 Brian R. Bondy. Distributed under the MPL2 license.
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "BloomFilterWrap.h"

namespace BloomFilterWrap {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Isolate;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::Persistent;
using v8::String;
using v8::Boolean;
using v8::Value;

Persistent<Function> BloomFilterWrap::constructor;

BloomFilterWrap::BloomFilterWrap(unsigned int bitsPerElement,
    unsigned int estimatedNumElements, HashFn hashFns[], int numHashFns)
  : BloomFilter(bitsPerElement, estimatedNumElements, hashFns, numHashFns) {
}

BloomFilterWrap::~BloomFilterWrap() {
}

void BloomFilterWrap::Init(Local<Object> exports) {
  Isolate* isolate = exports->GetIsolate();

  // Prepare constructor template
  Local<FunctionTemplate> tpl = FunctionTemplate::New(isolate, New);
  tpl->SetClassName(String::NewFromUtf8(isolate, "BloomFilter"));
  tpl->InstanceTemplate()->SetInternalFieldCount(1);

  // Prototype
  NODE_SET_PROTOTYPE_METHOD(tpl, "add", BloomFilterWrap::Add);
  NODE_SET_PROTOTYPE_METHOD(tpl, "exists", BloomFilterWrap::Exists);

  constructor.Reset(isolate, tpl->GetFunction());
  exports->Set(String::NewFromUtf8(isolate, "BloomFilter"),
               tpl->GetFunction());
}

void BloomFilterWrap::New(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();

  if (args.IsConstructCall()) {
    // Invoked as constructor: `new BloomFilter(...)`
    BloomFilterWrap* obj = new BloomFilterWrap();
    obj->Wrap(args.This());
    args.GetReturnValue().Set(args.This());
  } else {
    // Invoked as plain function `BloomFilter(...)`, turn into construct call.
    const int argc = 1;
    Local<Value> argv[argc] = { args[0] };
    Local<Context> context = isolate->GetCurrentContext();
    Local<Function> cons = Local<Function>::New(isolate, constructor);
    Local<Object> result =
        cons->NewInstance(context, argc, argv).ToLocalChecked();
    args.GetReturnValue().Set(result);
  }
}

void BloomFilterWrap::Add(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  String::Utf8Value str(isolate, args[0]->ToString());
  const char * buffer = *str;

  BloomFilterWrap* obj = ObjectWrap::Unwrap<BloomFilterWrap>(args.Holder());
  obj->add(buffer);
}

void BloomFilterWrap::Exists(const FunctionCallbackInfo<Value>& args) {
  Isolate* isolate = args.GetIsolate();
  String::Utf8Value str(isolate, args[0]->ToString());
  const char * buffer = *str;

  BloomFilterWrap* obj = ObjectWrap::Unwrap<BloomFilterWrap>(args.Holder());
  bool exists = obj->exists(buffer);

  args.GetReturnValue().Set(Boolean::New(isolate, exists));
}

}  // namespace BloomFilterWrap
<|endoftext|>


<commit_before>///
/// @file  popcnt.hpp
/// @brief Functions to count the number of 1 bits inside
///        an array or a 64-bit word.
///
/// Copyright (C) 2018 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef POPCNT_HPP
#define POPCNT_HPP

#include <stdint.h>

#if !defined(DISABLE_POPCNT)

#ifndef __has_builtin
  #define __has_builtin(x) 0
#endif

#if defined(__GNUC__) || __has_builtin(__builtin_popcountll)

inline uint64_t popcnt64(uint64_t x)
{
  return __builtin_popcountll(x);
}

#elif defined(_MSC_VER) && \
      defined(_WIN64)

#include <nmmintrin.h>

inline uint64_t popcnt64(uint64_t x)
{
  return _mm_popcnt_u64(x);
}

#elif defined(_MSC_VER) && \
      defined(_WIN32)

#include <nmmintrin.h>

inline uint64_t popcnt64(uint64_t x)
{
  return _mm_popcnt_u32((uint32_t) x) +
         _mm_popcnt_u32((uint32_t)(x >> 32));
}

#else

// fallback mode
#define DISABLE_POPCNT

#endif
#endif

#if !defined(DISABLE_POPCNT)

inline uint64_t popcnt(const uint64_t* data, uint64_t size)
{
  uint64_t cnt = 0;
  uint64_t i = 0;
  uint64_t limit = size - size % 4;

  for (; i < limit; i += 4)
  {
    cnt += popcnt64(data[i+0]);
    cnt += popcnt64(data[i+1]);
    cnt += popcnt64(data[i+2]);
    cnt += popcnt64(data[i+3]);
  }

  for (; i < size; i++)
    cnt += popcnt64(data[i]);

  return cnt;
}

#endif

#if defined(DISABLE_POPCNT)

/// This uses fewer arithmetic operations than any other known
/// implementation on machines with fast multiplication.
/// It uses 12 arithmetic operations, one of which is a multiply.
/// https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation
///
inline uint64_t popcnt64(uint64_t x)
{
  uint64_t m1 = 0x5555555555555555ll;
  uint64_t m2 = 0x3333333333333333ll;
  uint64_t m4 = 0x0F0F0F0F0F0F0F0Fll;
  uint64_t h01 = 0x0101010101010101ll;

  x -= (x >> 1) & m1;
  x = (x & m2) + ((x >> 2) & m2);
  x = (x + (x >> 4)) & m4;

  return (x * h01) >> 56;
}

/// Carry-save adder (CSA).
/// @see Chapter 5 in "Hacker's Delight" 2nd edition.
///
inline void CSA(uint64_t& h, uint64_t& l, uint64_t a, uint64_t b, uint64_t c)
{
  uint64_t u = a ^ b;
  h = (a & b) | (u & c);
  l = u ^ c;
}

/// Harley-Seal popcount (3rd iteration).
/// The Harley-Seal popcount algorithm is one of the fastest algorithms
/// for counting 1 bits in an array using only integer operations.
/// This implementation uses only 6.38 instructions per 64-bit word.
/// @see Chapter 5 in "Hacker's Delight" 2nd edition.
///
inline uint64_t popcnt(const uint64_t* data, uint64_t size)
{
  uint64_t cnt = 0;
  uint64_t ones = 0, twos = 0, fours = 0, eights = 0;
  uint64_t twosA, twosB, foursA, foursB;
  uint64_t limit = size - size % 8;
  uint64_t i = 0;

  for(; i < limit; i += 8)
  {
    CSA(twosA, ones, ones, data[i+0], data[i+1]);
    CSA(twosB, ones, ones, data[i+2], data[i+3]);
    CSA(foursA, twos, twos, twosA, twosB);
    CSA(twosA, ones, ones, data[i+4], data[i+5]);
    CSA(twosB, ones, ones, data[i+6], data[i+7]);
    CSA(foursB, twos, twos, twosA, twosB);
    CSA(eights, fours, fours, foursA, foursB);

    cnt += popcnt64(eights);
  }

  cnt *= 8;
  cnt += 4 * popcnt64(fours);
  cnt += 2 * popcnt64(twos);
  cnt += 1 * popcnt64(ones);

  for(; i < size; i++)
    cnt += popcnt64(data[i]);

  return cnt;
}

#endif

#endif // POPCNT_HPP
<commit_msg>Refactor<commit_after>///
/// @file  popcnt.hpp
/// @brief Functions to count the number of 1 bits inside
///        an array or a 64-bit word.
///
/// Copyright (C) 2018 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef POPCNT_HPP
#define POPCNT_HPP

#include <stdint.h>

#if !defined(DISABLE_POPCNT)

#ifndef __has_builtin
  #define __has_builtin(x) 0
#endif

#if defined(__GNUC__) || __has_builtin(__builtin_popcountll)

inline uint64_t popcnt64(uint64_t x)
{
  return __builtin_popcountll(x);
}

#elif defined(_MSC_VER) && \
      defined(_WIN64)

#include <nmmintrin.h>

inline uint64_t popcnt64(uint64_t x)
{
  return _mm_popcnt_u64(x);
}

#elif defined(_MSC_VER) && \
      defined(_WIN32)

#include <nmmintrin.h>

inline uint64_t popcnt64(uint64_t x)
{
  return _mm_popcnt_u32((uint32_t) x) +
         _mm_popcnt_u32((uint32_t)(x >> 32));
}

#else

// fallback mode
#define DISABLE_POPCNT

#endif
#endif

namespace {

#if !defined(DISABLE_POPCNT)

inline uint64_t popcnt(const uint64_t* data, uint64_t size)
{
  uint64_t cnt = 0;
  uint64_t i = 0;
  uint64_t limit = size - size % 4;

  for (; i < limit; i += 4)
  {
    cnt += popcnt64(data[i+0]);
    cnt += popcnt64(data[i+1]);
    cnt += popcnt64(data[i+2]);
    cnt += popcnt64(data[i+3]);
  }

  for (; i < size; i++)
    cnt += popcnt64(data[i]);

  return cnt;
}

#endif

#if defined(DISABLE_POPCNT)

/// This uses fewer arithmetic operations than any other known
/// implementation on machines with fast multiplication.
/// It uses 12 arithmetic operations, one of which is a multiply.
/// https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation
///
inline uint64_t popcnt64(uint64_t x)
{
  uint64_t m1 = 0x5555555555555555ull;
  uint64_t m2 = 0x3333333333333333ull;
  uint64_t m4 = 0x0F0F0F0F0F0F0F0Full;
  uint64_t h01 = 0x0101010101010101ull;

  x -= (x >> 1) & m1;
  x = (x & m2) + ((x >> 2) & m2);
  x = (x + (x >> 4)) & m4;

  return (x * h01) >> 56;
}

/// Carry-save adder (CSA).
/// @see Chapter 5 in "Hacker's Delight" 2nd edition.
///
inline void CSA(uint64_t& h, uint64_t& l, uint64_t a, uint64_t b, uint64_t c)
{
  uint64_t u = a ^ b;
  h = (a & b) | (u & c);
  l = u ^ c;
}

/// Harley-Seal popcount (3rd iteration).
/// The Harley-Seal popcount algorithm is one of the fastest algorithms
/// for counting 1 bits in an array using only integer operations.
/// This implementation uses only 6.38 instructions per 64-bit word.
/// @see Chapter 5 in "Hacker's Delight" 2nd edition.
///
inline uint64_t popcnt(const uint64_t* data, uint64_t size)
{
  uint64_t cnt = 0;
  uint64_t ones = 0, twos = 0, fours = 0, eights = 0;
  uint64_t twosA, twosB, foursA, foursB;
  uint64_t limit = size - size % 8;
  uint64_t i = 0;

  for(; i < limit; i += 8)
  {
    CSA(twosA, ones, ones, data[i+0], data[i+1]);
    CSA(twosB, ones, ones, data[i+2], data[i+3]);
    CSA(foursA, twos, twos, twosA, twosB);
    CSA(twosA, ones, ones, data[i+4], data[i+5]);
    CSA(twosB, ones, ones, data[i+6], data[i+7]);
    CSA(foursB, twos, twos, twosA, twosB);
    CSA(eights, fours, fours, foursA, foursB);

    cnt += popcnt64(eights);
  }

  cnt *= 8;
  cnt += 4 * popcnt64(fours);
  cnt += 2 * popcnt64(twos);
  cnt += 1 * popcnt64(ones);

  for(; i < size; i++)
    cnt += popcnt64(data[i]);

  return cnt;
}

#endif

} // namespace

#endif // POPCNT_HPP
<|endoftext|>


<commit_before>///
/// @file  popcnt.hpp
/// @brief Functions to count the number of 1 bits inside
///        an array or a 64-bit word.
///
/// Copyright (C) 2020 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef POPCNT_HPP
#define POPCNT_HPP

#include <stdint.h>

#if defined(__has_include)
  #define HAS_INCLUDE(header) __has_include(header)
#else
  // If the __has_include() macro does not exist
  // we assume that the header file exists.
  #define HAS_INCLUDE(header) 1
#endif

#if defined(ENABLE_POPCNT)

#if !defined(__has_builtin)
  #define __has_builtin(x) 0
#endif

// GCC & Clang
#if defined(__GNUC__) || \
    __has_builtin(__builtin_popcountll)

namespace {

inline uint64_t popcnt64(uint64_t x)
{
  return __builtin_popcountll(x);
}

} // namespace

#elif defined(_MSC_VER) && \
      defined(_WIN64) && \
      HAS_INCLUDE(<nmmintrin.h>)

#include <nmmintrin.h>

namespace {

inline uint64_t popcnt64(uint64_t x)
{
  return _mm_popcnt_u64(x);
}

} // namespace

#elif defined(_MSC_VER) && \
      defined(_WIN32) && \
      HAS_INCLUDE(<nmmintrin.h>)

#include <nmmintrin.h>

namespace {

inline uint64_t popcnt64(uint64_t x)
{
  return _mm_popcnt_u32((uint32_t) x) +
         _mm_popcnt_u32((uint32_t)(x >> 32));
}

} // namespace

#else

// Since hardware popcount support is very important for
// performance in primecount we want the compilation to fail in
// case the compiler does not select any of the implementations
// above (which use compiler intrinsics to enable hardware
// popcount support). This way we can be relatively sure that
// when primecount has been compiled successfully, primecount
// will have hardware popcount support.
#error "No fast popcount function implemented for this compiler!"

#endif
#endif

#if !defined(ENABLE_POPCNT)

namespace {

/// This uses fewer arithmetic operations than any other known
/// implementation on machines with fast multiplication.
/// It uses 12 arithmetic operations, one of which is a multiply.
/// https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation
///
inline uint64_t popcnt64(uint64_t x)
{
  uint64_t m1 = 0x5555555555555555ull;
  uint64_t m2 = 0x3333333333333333ull;
  uint64_t m4 = 0x0F0F0F0F0F0F0F0Full;
  uint64_t h01 = 0x0101010101010101ull;

  x -= (x >> 1) & m1;
  x = (x & m2) + ((x >> 2) & m2);
  x = (x + (x >> 4)) & m4;

  return (x * h01) >> 56;
}

/// Carry-save adder (CSA).
/// @see Chapter 5 in "Hacker's Delight" 2nd edition.
///
inline void CSA(uint64_t& h, uint64_t& l, uint64_t a, uint64_t b, uint64_t c)
{
  uint64_t u = a ^ b;
  h = (a & b) | (u & c);
  l = u ^ c;
}

/// Harley-Seal popcount (3rd iteration).
/// The Harley-Seal popcount algorithm is one of the fastest algorithms
/// for counting 1 bits in an array using only integer operations.
/// This implementation uses only 6.38 instructions per 64-bit word.
/// @see Chapter 5 in "Hacker's Delight" 2nd edition.
///
inline uint64_t popcnt(const uint64_t* data, uint64_t size)
{
  uint64_t cnt = 0;
  uint64_t ones = 0, twos = 0, fours = 0, eights = 0;
  uint64_t twosA, twosB, foursA, foursB;
  uint64_t limit = size - size % 8;
  uint64_t i = 0;

  for(; i < limit; i += 8)
  {
    CSA(twosA, ones, ones, data[i+0], data[i+1]);
    CSA(twosB, ones, ones, data[i+2], data[i+3]);
    CSA(foursA, twos, twos, twosA, twosB);
    CSA(twosA, ones, ones, data[i+4], data[i+5]);
    CSA(twosB, ones, ones, data[i+6], data[i+7]);
    CSA(foursB, twos, twos, twosA, twosB);
    CSA(eights, fours, fours, foursA, foursB);

    cnt += popcnt64(eights);
  }

  cnt *= 8;
  cnt += 4 * popcnt64(fours);
  cnt += 2 * popcnt64(twos);
  cnt += 1 * popcnt64(ones);

  for(; i < size; i++)
    cnt += popcnt64(data[i]);

  return cnt;
}

} // namespace

#elif (defined(__ARM_NEON) || \
       defined(__aarch64__)) && \
       HAS_INCLUDE(<arm_neon.h>)

#include <algorithm>
#include <arm_neon.h>

namespace {

inline uint64x2_t vpadalq(uint64x2_t sum, uint8x16_t t)
{
  return vpadalq_u32(sum, vpaddlq_u16(vpaddlq_u8(t)));
}

/// Count the number of 1 bits in the data array.
/// ARM NEON has a vector popcount instruction but no scalar
/// popcount instruction that's why the ARM popcount function
/// is more complicated than the x86 popcount function.
/// This function has been copied from:
/// https://github.com/kimwalisch/libpopcnt
///
inline uint64_t popcnt(const uint64_t* data, uint64_t size)
{
  uint64_t i = 0;
  uint64_t cnt = 0;

  if (size >= 8)
  {
    const uint8_t* data8 = (const uint8_t*) data;
    uint64_t bytes = size * sizeof(uint64_t);
    uint64_t chunk_size = 64;
    uint64_t iters = bytes / chunk_size;

    uint64x2_t sum = vcombine_u64(vcreate_u64(0), vcreate_u64(0));
    uint8x16_t zero = vcombine_u8(vcreate_u8(0), vcreate_u8(0));

    do
    {
      uint8x16_t t0 = zero;
      uint8x16_t t1 = zero;
      uint8x16_t t2 = zero;
      uint8x16_t t3 = zero;

      // After every 31 iterations we need to add the
      // temporary sums (t0, t1, t2, t3) to the total sum.
      // We must ensure that the temporary sums <= 255
      // and 31 * 8 bits = 248 which is OK.
      uint64_t limit = std::min(i + 31, iters);

      // Each iteration processes 64 bytes
      for (; i < limit; i++)
      {
        uint8x16x4_t input = vld4q_u8(data8);
        data8 += chunk_size;

        t0 = vaddq_u8(t0, vcntq_u8(input.val[0]));
        t1 = vaddq_u8(t1, vcntq_u8(input.val[1]));
        t2 = vaddq_u8(t2, vcntq_u8(input.val[2]));
        t3 = vaddq_u8(t3, vcntq_u8(input.val[3]));
      }

      sum = vpadalq(sum, t0);
      sum = vpadalq(sum, t1);
      sum = vpadalq(sum, t2);
      sum = vpadalq(sum, t3);
    }
    while (i < iters);

    uint64_t tmp[2];
    vst1q_u64(tmp, sum);
    cnt += tmp[0];
    cnt += tmp[1];

    uint64_t bytes_processed = iters * chunk_size;
    i = bytes_processed / sizeof(uint64_t);
  }

  // Process the remaining bytes
  for (; i < size; i++)
    cnt += popcnt64(data[i]);

  return cnt;
}

} // namespace

#else

namespace {

/// Used for all CPUs except ARM
inline uint64_t popcnt(const uint64_t* data, uint64_t size)
{
  uint64_t cnt = 0;

  for (uint64_t i = 0; i < size; i++)
    cnt += popcnt64(data[i]);

  return cnt;
}

} // namespace

#endif

#endif // POPCNT_HPP
<commit_msg>Simplify popcnt<commit_after>///
/// @file  popcnt.hpp
/// @brief Functions to count the number of 1 bits inside
///        an array or a 64-bit word.
///
/// Copyright (C) 2020 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef POPCNT_HPP
#define POPCNT_HPP

#include <stdint.h>

#if defined(__has_include)
  #define HAS_INCLUDE(header) __has_include(header)
#else
  // If the __has_include() macro does not exist
  // we assume that the header file exists.
  #define HAS_INCLUDE(header) 1
#endif

#if defined(ENABLE_POPCNT)

#if !defined(__has_builtin)
  #define __has_builtin(x) 0
#endif

// GCC & Clang
#if defined(__GNUC__) || \
    __has_builtin(__builtin_popcountll)

namespace {

inline uint64_t popcnt64(uint64_t x)
{
  return __builtin_popcountll(x);
}

} // namespace

#elif defined(_MSC_VER) && \
      defined(_WIN64) && \
      HAS_INCLUDE(<nmmintrin.h>)

#include <nmmintrin.h>

namespace {

inline uint64_t popcnt64(uint64_t x)
{
  return _mm_popcnt_u64(x);
}

} // namespace

#elif defined(_MSC_VER) && \
      defined(_WIN32) && \
      HAS_INCLUDE(<nmmintrin.h>)

#include <nmmintrin.h>

namespace {

inline uint64_t popcnt64(uint64_t x)
{
  return _mm_popcnt_u32((uint32_t) x) +
         _mm_popcnt_u32((uint32_t)(x >> 32));
}

} // namespace

#else

// Since hardware popcount support is very important for
// performance in primecount we want the compilation to fail in
// case the compiler does not select any of the implementations
// above (which use compiler intrinsics to enable hardware
// popcount support). This way we can be relatively sure that
// when primecount has been compiled successfully, primecount
// will have hardware popcount support.
#error "No fast popcount function implemented for this compiler!"

#endif
#endif

namespace {

#if !defined(ENABLE_POPCNT)

/// This uses fewer arithmetic operations than any other known
/// implementation on machines with fast multiplication.
/// It uses 12 arithmetic operations, one of which is a multiply.
/// https://en.wikipedia.org/wiki/Hamming_weight#Efficient_implementation
///
inline uint64_t popcnt64(uint64_t x)
{
  uint64_t m1 = 0x5555555555555555ull;
  uint64_t m2 = 0x3333333333333333ull;
  uint64_t m4 = 0x0F0F0F0F0F0F0F0Full;
  uint64_t h01 = 0x0101010101010101ull;

  x -= (x >> 1) & m1;
  x = (x & m2) + ((x >> 2) & m2);
  x = (x + (x >> 4)) & m4;

  return (x * h01) >> 56;
}

#endif

inline uint64_t popcnt(const uint64_t* data, uint64_t size)
{
  uint64_t cnt = 0;

  for (uint64_t i = 0; i < size; i++)
    cnt += popcnt64(data[i]);

  return cnt;
}

} // namespace

#endif // POPCNT_HPP
<|endoftext|>


<commit_before>/*
   Copyright 2014 The Trustees of Princeton University

   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 "driver.h"

// start up the driver
int driver_init( void** driver_state ) {

   return 0;
}

// shut down the driver
int driver_shutdown( void* driver_state ) {

   return 0;
}

// connect
int connect_dataset_block( struct AG_connection_context* ag_ctx, void* driver_state, void** driver_connection_state ) {

   char* request_path = AG_driver_get_request_path( ag_ctx );
   char* url = AG_driver_get_query_string( ag_ctx );

   struct curl_connection_context* curl_ctx = CALLOC_LIST( struct curl_connection_context, 1 );

   curl_ctx->request_path = request_path;
   curl_ctx->url = url;

   *driver_connection_state = curl_ctx;

   return 0;
}

// free a connection context
int close_dataset_block( void* driver_connection_state ) {

   struct curl_connection_context* curl_ctx = (struct curl_connection_context*)driver_connection_state;

   if( curl_ctx != NULL ) {
      if( curl_ctx->request_path != NULL ) {
         free( curl_ctx->request_path );
         curl_ctx->request_path = NULL;
      }

      if( curl_ctx->url != NULL ) {
         free( curl_ctx->url );
         curl_ctx->url = NULL;
      }

      free( curl_ctx );
   }
   return 0;
}

// dummy header function; seems to be needed to stat files
static size_t curl_null_header( void* ptr, size_t size, size_t nmemb, void* data ) {
   return size * nmemb;
}

// write function to fill our buffer, and abort if we get too much
static size_t curl_write_block( void* ptr, size_t size, size_t nmemb, void* data ) {

   struct curl_write_context* write_ctx = (struct curl_write_context*)data;

   size_t total_available = size * nmemb;

   // have space?
   if( write_ctx->num_written + total_available <= write_ctx->buf_len ) {

      memcpy( write_ctx->buf + write_ctx->num_written, ptr, total_available );
      write_ctx->num_written += total_available;

      return total_available;
   }
   else {
      // not enough space
      errorf("Not enough space (have %zu bytes, but need at least %zu)\n", write_ctx->buf_len, write_ctx->num_written + total_available );
      return 0;
   }
}

// find out how big a file is, and when it was modified
// return 0 on success
// return negative errno on failure, or if errno was not set, return positive curl status code on failure
static int curl_stat_file( CURL* curl, char const* url, struct AG_driver_publish_info* pub_info ) {

   // set up the curl handle
   curl_easy_setopt( curl, CURLOPT_URL, url );
   curl_easy_setopt( curl, CURLOPT_NOBODY, 1L );
   curl_easy_setopt( curl, CURLOPT_FILETIME, 1L );
   curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, curl_null_header );
   curl_easy_setopt( curl, CURLOPT_HEADER, 0L );

   int rc = curl_easy_perform( curl );

   if( rc != 0 ) {
      // error of some sort
      long oserr = 0;

      curl_easy_getinfo( curl, CURLINFO_OS_ERRNO, &oserr );

      if( oserr != 0 ) {
         errorf("curl_easy_perform(%s) rc = %d, errno = %ld\n", url, rc, -oserr );
         return (int)(-oserr);
      }
      else {
         errorf("curl_easy_perform(%s) rc = %d\n", url, rc );
         return rc;
      }
   }

   // success! get info
   long filetime = -1;
   double filesize = -1.0;

   int filetime_rc = curl_easy_getinfo( curl, CURLINFO_FILETIME, &filetime );
   int filesize_rc = curl_easy_getinfo( curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize );

   if( (filetime_rc != 0 || filetime < 0) || (filesize_rc != 0 || filesize <= 0.0 ) ) {

      // failed to get data
      rc = -ENODATA;

      if( filetime_rc != 0 ) {
         errorf("curl_easy_getinfo( CURLINFO_FILETIME ) rc = %d\n", filetime_rc );
         rc = filetime_rc;
      }
      if( filesize_rc != 0 ) {
         errorf("curl_easy_getinfo( CURLINFO_CONTENT_LENGTH_DOWNLOAD ) rc = %d\n", filesize_rc );
         rc = filesize_rc;
      }

      return rc;
   }

   // fill in our pubinfo
   pub_info->size = (off_t)filesize;

   // set the time to now
   pub_info->mtime_sec = (int64_t)filetime;
   pub_info->mtime_nsec = 0;

   return 0;
}

// get a block of data, and get its pubinfo while we're at it
// return the number of bytes received on success
// return negative errno on failure, or if errno was not set, return positive curl status code on failure
static int curl_download_block( CURL* curl, char const* url, uint64_t block_id, char* buf, uint64_t block_size, struct AG_driver_publish_info* pub_info ) {

   struct curl_write_context write_ctx;
   memset( &write_ctx, 0, sizeof(struct curl_write_context) );

   write_ctx.num_written = 0;
   write_ctx.buf = buf;
   write_ctx.buf_len = block_size;

   // set up the curl handle
   curl_easy_setopt( curl, CURLOPT_URL, url );
   curl_easy_setopt( curl, CURLOPT_FILETIME, 1L );
   curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, curl_null_header );
   curl_easy_setopt( curl, CURLOPT_HEADER, 0L );
   curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curl_write_block );
   curl_easy_setopt( curl, CURLOPT_WRITEDATA, &write_ctx );

   // add range requirement
   // format: X-Y, where X and Y are 64-bit byte indices.  Y is inclusive
   size_t range_maxlen = 20 + 1 + 20 + 1;  // NOTE: 19 < log10( UINT64_MAX ) < 20
   char range_buf[20 + 1 + 20 + 1];
   snprintf( range_buf, range_maxlen, "%" PRIu64 "-%" PRIu64, block_id * block_size, (block_id + 1) * block_size - 1 );

   curl_easy_setopt( curl, CURLOPT_RANGE, range_buf );

   int rc = curl_easy_perform( curl );

   if( rc != 0 ) {
      // error of some sort
      long oserr = 0;

      curl_easy_getinfo( curl, CURLINFO_OS_ERRNO, &oserr );

      if( oserr != 0 ) {
         errorf("curl_easy_perform(%s) rc = %d, errno = %ld\n", url, rc, -oserr );
         return (int)(-oserr);
      }
      else {
         if( rc == CURLE_BAD_DOWNLOAD_RESUME ) {
            // EOF
            errorf("WARN: Block %" PRIu64 " is off the end of the file\n", block_id );

            write_ctx.num_written = 0;
         }
         else {
            errorf("curl_easy_perform(%s) rc = %d\n", url, rc );
            return -rc;
         }
      }
   }

   // success! opportunistically try to get metadata info
   long filetime = -1;
   double filesize = -1.0;

   int filetime_rc = curl_easy_getinfo( curl, CURLINFO_FILETIME, &filetime );
   int filesize_rc = curl_easy_getinfo( curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize );

   // opportunistically got metadata?
   if( filetime_rc == 0 && filetime > 0 && filesize_rc == 0 && filesize >= 1.0 ) {

      // fill in our pubinfo
      pub_info->size = (off_t)filesize;
      pub_info->mtime_sec = (int64_t)filetime;
      pub_info->mtime_nsec = 0;
   }

   return write_ctx.num_written;
}

// get the publish information for a dataset
int curl_get_pubinfo( char const* request_path, char const* url, struct AG_driver_publish_info* pubinfo ) {

   // maybe we've cached it?
   char* info_path = md_fullpath( request_path, "curl-info", NULL );

   char* pubinfo_buf = NULL;
   size_t pubinfo_buflen = 0;

   int rc = AG_driver_cache_get_chunk( info_path, &pubinfo_buf, &pubinfo_buflen );

   if( rc == 0 ) {

      if( pubinfo_buflen == sizeof(struct AG_driver_publish_info) ) {
         // success!
         memcpy( pubinfo, pubinfo_buf, sizeof(struct AG_driver_publish_info) );
         free( pubinfo_buf );

         AG_driver_cache_promote_chunk( info_path );
         free( info_path );
         return 0;
      }
      else {
         errorf("WARN: got invalid data for %s\n", info_path );
         AG_driver_cache_evict_chunk( info_path );

         rc = 0;
         free( pubinfo_buf );
         pubinfo_buf = NULL;
      }
   }

   // miss
   CURL* curl = curl_easy_init();

   // not cached
   rc = curl_stat_file( curl, url, pubinfo );
   if( rc != 0 ) {
      errorf("ERR: curl_stat_file(%s, %s) rc = %d\n", info_path, url, rc );
   }
   else {
      // success! cache it

      char* cache_chunk = CALLOC_LIST( char, sizeof(struct AG_driver_publish_info) );
      memcpy( cache_chunk, &pubinfo, sizeof(struct AG_driver_publish_info) );

      AG_driver_cache_put_chunk_async( info_path, cache_chunk, sizeof(struct AG_driver_publish_info) );

      dbprintf("Got pubinfo for %s: { size = %jd, mtime_sec = %" PRId64 ", mtime_nsec = %" PRId32 " }\n", request_path, pubinfo->size, pubinfo->mtime_sec, pubinfo->mtime_nsec );
   }

   curl_easy_cleanup( curl );

   free( info_path );

   return rc;
}

// get data for a block.
// get it from the cache, if possible.
// otherwise, download it and serve it back
ssize_t get_dataset_block( struct AG_connection_context* ag_ctx, uint64_t block_id, char* block_buf, size_t buf_len, void* driver_connection_state ) {

   struct curl_connection_context* curl_ctx = (struct curl_connection_context*)driver_connection_state;

   // opportunistically get pubinfo
   struct AG_driver_publish_info pubinfo;
   memset( &pubinfo, 0, sizeof(struct AG_driver_publish_info) );

   pubinfo.size = -1;
   pubinfo.mtime_sec = -1;

   CURL* curl = curl_easy_init();

   // no data cached; go get it
   int num_read = curl_download_block( curl, curl_ctx->url, block_id, block_buf, buf_len, &pubinfo );

   if( num_read < 0 ) {

      errorf("curl_download_block(%s, %" PRIu64 ") rc = %d\n", curl_ctx->url, block_id, num_read );
   }
   else {

      // got data!
      // did we get any pubinfo?
      if( pubinfo.mtime_sec > 0 && pubinfo.size > 0 ) {

         dbprintf("Update cached publish info for %s\n", curl_ctx->request_path );

         // update the cache
         char* info_path = md_fullpath( curl_ctx->request_path, "curl-info", NULL );

         char* cache_chunk = CALLOC_LIST( char, sizeof(struct AG_driver_publish_info) );
         memcpy( cache_chunk, &pubinfo, sizeof(struct AG_driver_publish_info) );

         AG_driver_cache_put_chunk_async( info_path, cache_chunk, sizeof(struct AG_driver_publish_info) );

         free( info_path );
      }
   }

   curl_easy_cleanup( curl );

   return num_read;
}

// prepare to publish--fill in pub_info
int stat_dataset( char const* path, struct AG_map_info* map_info, struct AG_driver_publish_info* pub_info, void* driver_state ) {

   char* url = AG_driver_map_info_get_query_string( map_info );

   dbprintf("stat %s (url: %s)\n", path, url );

   if( url == NULL ) {

      // it's a directory.
      // timestamp is simply now
      pub_info->size = 4096;

      struct timespec ts;
      clock_gettime( CLOCK_REALTIME, &ts );

      pub_info->mtime_sec = ts.tv_sec;
      pub_info->mtime_nsec = ts.tv_nsec;

      return 0;
   }

   int rc = 0;

   // it's a file.
   // get information from upstream?
   char* get_from_upstream = AG_driver_get_config_var( AG_CURL_DRIVER_CONFIG_CHECK_UPSTREAM );
   if( get_from_upstream != NULL ) {

      free( get_from_upstream );

      // get the info from upstream
      int rc = curl_get_pubinfo( path, url, pub_info );

      if( rc != 0 ) {
         errorf("curl_get_pubinfo(%s, %s) rc = %d\n", path, url, rc );
      }

   }
   else {
      // get locally
      pub_info->size = -1;

      struct timespec ts;
      clock_gettime( CLOCK_REALTIME, &ts );

      pub_info->mtime_sec = ts.tv_sec;
      pub_info->mtime_nsec = ts.tv_nsec;
   }

   free( url );
   return rc;
}

// handle dataset reversion
int reversion_dataset( char const* path, struct AG_map_info* mi, void* driver_state ) {

   // evict the associated cached publish info
   char* info_path = md_fullpath( path, "curl-info", NULL );

   AG_driver_cache_evict_chunk( info_path );

   free( info_path );
   return 0;
}

// query type: curl
char* get_query_type(void) {
   return strdup("curl");
}
<commit_msg>Don't bother re-obtaining publish information in the CURL driver when we fetch a block.  The AG shouldn't evict the cached data until it is time to reversion.<commit_after>/*
   Copyright 2014 The Trustees of Princeton University

   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 "driver.h"

// start up the driver
int driver_init( void** driver_state ) {

   return 0;
}

// shut down the driver
int driver_shutdown( void* driver_state ) {

   return 0;
}

// connect
int connect_dataset_block( struct AG_connection_context* ag_ctx, void* driver_state, void** driver_connection_state ) {

   char* request_path = AG_driver_get_request_path( ag_ctx );
   char* url = AG_driver_get_query_string( ag_ctx );

   struct curl_connection_context* curl_ctx = CALLOC_LIST( struct curl_connection_context, 1 );

   curl_ctx->request_path = request_path;
   curl_ctx->url = url;

   *driver_connection_state = curl_ctx;

   return 0;
}

// free a connection context
int close_dataset_block( void* driver_connection_state ) {

   struct curl_connection_context* curl_ctx = (struct curl_connection_context*)driver_connection_state;

   if( curl_ctx != NULL ) {
      if( curl_ctx->request_path != NULL ) {
         free( curl_ctx->request_path );
         curl_ctx->request_path = NULL;
      }

      if( curl_ctx->url != NULL ) {
         free( curl_ctx->url );
         curl_ctx->url = NULL;
      }

      free( curl_ctx );
   }
   return 0;
}

// dummy header function; seems to be needed to stat files
static size_t curl_null_header( void* ptr, size_t size, size_t nmemb, void* data ) {
   return size * nmemb;
}

// write function to fill our buffer, and abort if we get too much
static size_t curl_write_block( void* ptr, size_t size, size_t nmemb, void* data ) {

   struct curl_write_context* write_ctx = (struct curl_write_context*)data;

   size_t total_available = size * nmemb;

   // have space?
   if( write_ctx->num_written + total_available <= write_ctx->buf_len ) {

      memcpy( write_ctx->buf + write_ctx->num_written, ptr, total_available );
      write_ctx->num_written += total_available;

      return total_available;
   }
   else {
      // not enough space
      errorf("Not enough space (have %zu bytes, but need at least %zu)\n", write_ctx->buf_len, write_ctx->num_written + total_available );
      return 0;
   }
}

// find out how big a file is, and when it was modified
// return 0 on success
// return negative errno on failure, or if errno was not set, return positive curl status code on failure
static int curl_stat_file( CURL* curl, char const* url, struct AG_driver_publish_info* pub_info ) {

   dbprintf("stat %s\n", url );

   // set up the curl handle
   curl_easy_setopt( curl, CURLOPT_URL, url );
   curl_easy_setopt( curl, CURLOPT_NOBODY, 1L );
   curl_easy_setopt( curl, CURLOPT_FILETIME, 1L );
   curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, curl_null_header );
   curl_easy_setopt( curl, CURLOPT_HEADER, 0L );

   int rc = curl_easy_perform( curl );

   if( rc != 0 ) {
      // error of some sort
      long oserr = 0;

      curl_easy_getinfo( curl, CURLINFO_OS_ERRNO, &oserr );

      if( oserr != 0 ) {
         errorf("curl_easy_perform(%s) rc = %d, errno = %ld\n", url, rc, -oserr );
         return (int)(-oserr);
      }
      else {
         errorf("curl_easy_perform(%s) rc = %d\n", url, rc );
         return rc;
      }
   }

   // success! get info
   long filetime = -1;
   double filesize = -1.0;

   int filetime_rc = curl_easy_getinfo( curl, CURLINFO_FILETIME, &filetime );
   int filesize_rc = curl_easy_getinfo( curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize );

   if( (filetime_rc != 0 || filetime < 0) || (filesize_rc != 0 || filesize <= 0.0 ) ) {

      // failed to get data
      rc = -ENODATA;

      if( filetime_rc != 0 ) {
         errorf("curl_easy_getinfo( CURLINFO_FILETIME ) rc = %d\n", filetime_rc );
         rc = filetime_rc;
      }
      if( filesize_rc != 0 ) {
         errorf("curl_easy_getinfo( CURLINFO_CONTENT_LENGTH_DOWNLOAD ) rc = %d\n", filesize_rc );
         rc = filesize_rc;
      }

      return rc;
   }

   // fill in our pubinfo
   pub_info->size = (off_t)filesize;

   // set the time to now
   pub_info->mtime_sec = (int64_t)filetime;
   pub_info->mtime_nsec = 0;

   return 0;
}

// get a block of data, and get its pubinfo while we're at it
// return the number of bytes received on success
// return negative errno on failure, or if errno was not set, return positive curl status code on failure
static int curl_download_block( CURL* curl, char const* url, uint64_t block_id, char* buf, uint64_t block_size, struct AG_driver_publish_info* pub_info ) {

   dbprintf("Get block %s\n", url );

   struct curl_write_context write_ctx;
   memset( &write_ctx, 0, sizeof(struct curl_write_context) );

   write_ctx.num_written = 0;
   write_ctx.buf = buf;
   write_ctx.buf_len = block_size;

   // set up the curl handle
   curl_easy_setopt( curl, CURLOPT_URL, url );
   curl_easy_setopt( curl, CURLOPT_FILETIME, 1L );
   curl_easy_setopt( curl, CURLOPT_HEADERFUNCTION, curl_null_header );
   curl_easy_setopt( curl, CURLOPT_HEADER, 0L );
   curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, curl_write_block );
   curl_easy_setopt( curl, CURLOPT_WRITEDATA, &write_ctx );

   // add range requirement
   // format: X-Y, where X and Y are 64-bit byte indices.  Y is inclusive
   size_t range_maxlen = 20 + 1 + 20 + 1;  // NOTE: 19 < log10( UINT64_MAX ) < 20
   char range_buf[20 + 1 + 20 + 1];
   snprintf( range_buf, range_maxlen, "%" PRIu64 "-%" PRIu64, block_id * block_size, (block_id + 1) * block_size - 1 );

   curl_easy_setopt( curl, CURLOPT_RANGE, range_buf );

   int rc = curl_easy_perform( curl );

   if( rc != 0 ) {
      // error of some sort
      long oserr = 0;

      curl_easy_getinfo( curl, CURLINFO_OS_ERRNO, &oserr );

      if( oserr != 0 ) {
         errorf("curl_easy_perform(%s) rc = %d, errno = %ld\n", url, rc, -oserr );
         return (int)(-oserr);
      }
      else {
         if( rc == CURLE_BAD_DOWNLOAD_RESUME ) {
            // EOF
            errorf("WARN: Block %" PRIu64 " is off the end of the file\n", block_id );

            write_ctx.num_written = 0;
         }
         else {
            errorf("curl_easy_perform(%s) rc = %d\n", url, rc );
            return -rc;
         }
      }
   }

   // success! opportunistically try to get metadata info
   long filetime = -1;
   double filesize = -1.0;

   int filetime_rc = curl_easy_getinfo( curl, CURLINFO_FILETIME, &filetime );
   int filesize_rc = curl_easy_getinfo( curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &filesize );

   // opportunistically got metadata?
   if( filetime_rc == 0 && filetime > 0 && filesize_rc == 0 && filesize >= 1.0 ) {

      // fill in our pubinfo
      pub_info->size = (off_t)filesize;
      pub_info->mtime_sec = (int64_t)filetime;
      pub_info->mtime_nsec = 0;
   }

   return write_ctx.num_written;
}

// get the publish information for a dataset
int curl_get_pubinfo( char const* request_path, char const* url, struct AG_driver_publish_info* pubinfo ) {

   // maybe we've cached it?
   char* info_path = md_fullpath( request_path, "curl-info", NULL );

   char* pubinfo_buf = NULL;
   size_t pubinfo_buflen = 0;

   int rc = AG_driver_cache_get_chunk( info_path, &pubinfo_buf, &pubinfo_buflen );

   if( rc == 0 ) {

      if( pubinfo_buflen == sizeof(struct AG_driver_publish_info) ) {
         // success!
         memcpy( pubinfo, pubinfo_buf, sizeof(struct AG_driver_publish_info) );
         free( pubinfo_buf );

         AG_driver_cache_promote_chunk( info_path );
         free( info_path );
         return 0;
      }
      else {
         errorf("WARN: got invalid data for %s\n", info_path );
         AG_driver_cache_evict_chunk( info_path );

         rc = 0;
         free( pubinfo_buf );
         pubinfo_buf = NULL;
      }
   }

   // miss
   CURL* curl = curl_easy_init();

   // not cached
   rc = curl_stat_file( curl, url, pubinfo );
   if( rc != 0 ) {
      errorf("ERR: curl_stat_file(%s, %s) rc = %d\n", info_path, url, rc );
   }
   else {
      // success! cache it

      char* cache_chunk = CALLOC_LIST( char, sizeof(struct AG_driver_publish_info) );
      memcpy( cache_chunk, &pubinfo, sizeof(struct AG_driver_publish_info) );

      AG_driver_cache_put_chunk_async( info_path, cache_chunk, sizeof(struct AG_driver_publish_info) );

      dbprintf("Got pubinfo for %s: { size = %jd, mtime_sec = %" PRId64 ", mtime_nsec = %" PRId32 " }\n", request_path, pubinfo->size, pubinfo->mtime_sec, pubinfo->mtime_nsec );
   }

   curl_easy_cleanup( curl );

   free( info_path );

   return rc;
}

// get data for a block.
// get it from the cache, if possible.
// otherwise, download it and serve it back
ssize_t get_dataset_block( struct AG_connection_context* ag_ctx, uint64_t block_id, char* block_buf, size_t buf_len, void* driver_connection_state ) {

   struct curl_connection_context* curl_ctx = (struct curl_connection_context*)driver_connection_state;

   // opportunistically get pubinfo
   struct AG_driver_publish_info pubinfo;
   memset( &pubinfo, 0, sizeof(struct AG_driver_publish_info) );

   pubinfo.size = -1;
   pubinfo.mtime_sec = -1;

   CURL* curl = curl_easy_init();

   // no data cached; go get it
   int num_read = curl_download_block( curl, curl_ctx->url, block_id, block_buf, buf_len, &pubinfo );

   if( num_read < 0 ) {

      errorf("curl_download_block(%s, %" PRIu64 ") rc = %d\n", curl_ctx->url, block_id, num_read );
   }

   curl_easy_cleanup( curl );

   return num_read;
}

// prepare to publish--fill in pub_info
int stat_dataset( char const* path, struct AG_map_info* map_info, struct AG_driver_publish_info* pub_info, void* driver_state ) {

   char* url = AG_driver_map_info_get_query_string( map_info );

   dbprintf("stat %s (url: %s)\n", path, url );

   if( url == NULL ) {

      // it's a directory.
      // timestamp is simply now
      pub_info->size = 4096;

      struct timespec ts;
      clock_gettime( CLOCK_REALTIME, &ts );

      pub_info->mtime_sec = ts.tv_sec;
      pub_info->mtime_nsec = ts.tv_nsec;

      return 0;
   }

   int rc = 0;

   // it's a file.
   // get information from upstream?
   char* get_from_upstream = AG_driver_get_config_var( AG_CURL_DRIVER_CONFIG_CHECK_UPSTREAM );
   if( get_from_upstream != NULL ) {

      free( get_from_upstream );

      // get the info from upstream
      int rc = curl_get_pubinfo( path, url, pub_info );

      if( rc != 0 ) {
         errorf("curl_get_pubinfo(%s, %s) rc = %d\n", path, url, rc );
      }

   }
   else {
      // get locally
      pub_info->size = -1;

      struct timespec ts;
      clock_gettime( CLOCK_REALTIME, &ts );

      pub_info->mtime_sec = ts.tv_sec;
      pub_info->mtime_nsec = ts.tv_nsec;
   }

   free( url );
   return rc;
}

// handle dataset reversion
int reversion_dataset( char const* path, struct AG_map_info* mi, void* driver_state ) {

   // evict the associated cached publish info
   char* info_path = md_fullpath( path, "curl-info", NULL );

   AG_driver_cache_evict_chunk( info_path );

   free( info_path );
   return 0;
}

// query type: curl
char* get_query_type(void) {
   return strdup("curl");
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 offa
//
// 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.

#include "danek/internal/ToString.h"
#include "danek/internal/ConfigItem.h"
#include "danek/internal/ConfigScope.h"
#include "danek/internal/UidIdentifierProcessor.h"
#include <algorithm>
#include <iterator>
#include <sstream>

namespace danek
{

    namespace
    {
        std::string indent(std::size_t level)
        {
            constexpr std::size_t indentCount = 4;
            return std::string(level * indentCount, ' ');
        }

        void replaceInplace(std::string& input, const std::string& str, const std::string& replacement)
        {
            if( str.empty() == false )
            {
                std::size_t pos = 0;

                while( ( pos = input.find(str, pos) ) != std::string::npos )
                {
                    input.replace(pos, str.size(), replacement);
                    pos += replacement.size();
                }
            }
        }

        std::string escape(const std::string& str)
        {
            auto output = str;

            replaceInplace(output, "%", "%%");
            replaceInplace(output, "\t", "%t");
            replaceInplace(output, "\n", "%n");
            replaceInplace(output, "\"", "%\"");

            return "\"" + output + "\"";
        }

        std::string expandUid(const std::string& name, bool expand)
        {
            if( expand == false )
            {
                UidIdentifierProcessor uidIdProc;
                StringBuffer nameBuf;
                return uidIdProc.unexpand(name.c_str(), nameBuf);
            }
            return name;
        }

        void appendConfType(std::stringstream& stream, const ConfigScope& scope, ConfType type, bool expandUid, std::size_t indentLevel)
        {
            auto names = scope.listLocallyScopedNames(type, false, { });
            std::sort(names.begin(), names.end());

            for( std::size_t i=0; i<names.size(); ++i )
            {
                const auto item = scope.findItem(names[i]);
                stream << toString(*item, item->name(), expandUid, indentLevel);
            }
        }

    }

    std::string toString(const ConfigItem& item, const std::string& name, bool expandUidNames, std::size_t indentLevel)
    {
        const std::string nameStr = expandUid(name, expandUidNames);
        std::stringstream os;

        os << indent(indentLevel);

        switch(item.type())
        {
            case ConfType::String:
                os << nameStr << " = " << escape(item.stringVal()) << ";\n";
                break;
            case ConfType::List:
                {
                    os << nameStr << " = [";
                    const auto values = item.listVal();

                    if( values.empty() == false )
                    {
                        using OItr = std::ostream_iterator<std::string>;
                        std::transform(values.cbegin(), std::prev(values.cend()), OItr{os, ", "}, escape);
                        std::transform(std::prev(values.cend()), values.cend(), OItr{os}, escape);
                    }

                    os << "];\n";
                }
                break;
            case ConfType::Scope:
                {
                    os << nameStr << " {\n";
                    StringBuffer buffer;
                    os << toString(*(item.scopeVal()), expandUidNames, indentLevel + 1);
                    os << indent(indentLevel) << buffer.str() << "}\n";
                }
                break;
            default:
                break;
        }

        return os.str();
    }

    std::string toString(const ConfigScope& scope, bool expandUidNames, std::size_t indentLevel)
    {
        std::stringstream ss;
        appendConfType(ss, scope, ConfType::Variables, expandUidNames, indentLevel);
        appendConfType(ss, scope, ConfType::Scope, expandUidNames, indentLevel);

        return ss.str();
    }

}
<commit_msg>Useless code removed.<commit_after>// Copyright (c) 2017 offa
//
// 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.

#include "danek/internal/ToString.h"
#include "danek/internal/ConfigItem.h"
#include "danek/internal/ConfigScope.h"
#include "danek/internal/UidIdentifierProcessor.h"
#include <algorithm>
#include <iterator>
#include <sstream>

namespace danek
{

    namespace
    {
        std::string indent(std::size_t level)
        {
            constexpr std::size_t indentCount = 4;
            return std::string(level * indentCount, ' ');
        }

        void replaceInplace(std::string& input, const std::string& str, const std::string& replacement)
        {
            if( str.empty() == false )
            {
                std::size_t pos = 0;

                while( ( pos = input.find(str, pos) ) != std::string::npos )
                {
                    input.replace(pos, str.size(), replacement);
                    pos += replacement.size();
                }
            }
        }

        std::string escape(const std::string& str)
        {
            auto output = str;

            replaceInplace(output, "%", "%%");
            replaceInplace(output, "\t", "%t");
            replaceInplace(output, "\n", "%n");
            replaceInplace(output, "\"", "%\"");

            return "\"" + output + "\"";
        }

        std::string expandUid(const std::string& name, bool expand)
        {
            if( expand == false )
            {
                UidIdentifierProcessor uidIdProc;
                StringBuffer nameBuf;
                return uidIdProc.unexpand(name.c_str(), nameBuf);
            }
            return name;
        }

        void appendConfType(std::stringstream& stream, const ConfigScope& scope, ConfType type, bool expandUid, std::size_t indentLevel)
        {
            auto names = scope.listLocallyScopedNames(type, false, { });
            std::sort(names.begin(), names.end());

            for( std::size_t i=0; i<names.size(); ++i )
            {
                const auto item = scope.findItem(names[i]);
                stream << toString(*item, item->name(), expandUid, indentLevel);
            }
        }

    }

    std::string toString(const ConfigItem& item, const std::string& name, bool expandUidNames, std::size_t indentLevel)
    {
        const std::string nameStr = expandUid(name, expandUidNames);
        std::stringstream os;

        os << indent(indentLevel);

        switch(item.type())
        {
            case ConfType::String:
                os << nameStr << " = " << escape(item.stringVal()) << ";\n";
                break;
            case ConfType::List:
                {
                    os << nameStr << " = [";
                    const auto values = item.listVal();

                    if( values.empty() == false )
                    {
                        using OItr = std::ostream_iterator<std::string>;
                        std::transform(values.cbegin(), std::prev(values.cend()), OItr{os, ", "}, escape);
                        std::transform(std::prev(values.cend()), values.cend(), OItr{os}, escape);
                    }

                    os << "];\n";
                }
                break;
            case ConfType::Scope:
                {
                    os << nameStr << " {\n";
                    os << toString(*(item.scopeVal()), expandUidNames, indentLevel + 1);
                    os << indent(indentLevel) << "}\n";
                }
                break;
            default:
                break;
        }

        return os.str();
    }

    std::string toString(const ConfigScope& scope, bool expandUidNames, std::size_t indentLevel)
    {
        std::stringstream ss;
        appendConfType(ss, scope, ConfType::Variables, expandUidNames, indentLevel);
        appendConfType(ss, scope, ConfType::Scope, expandUidNames, indentLevel);

        return ss.str();
    }

}
<|endoftext|>


<commit_before>
#include "MainWindow.h"

#include <y/io/File.h>

#include <yave/framegraph/renderers.h>

using namespace editor;

int test_graph() {
	Instance instance(DebugParams::debug());
	Device device(instance);
	Scene scene;

	{
		FrameGraph graph(&device);

		auto& gbuffer = render_gbuffer(graph, SceneView(scene), math::Vec2ui(512));

		{
			CmdBufferRecorder rec(device.create_disposable_cmd_buffer());
			std::move(graph).render(rec, gbuffer.color);
			RecordedCmdBuffer cmd(std::move(rec));
			device.queue(vk::QueueFlagBits::eGraphics).submit<SyncSubmit>(std::move(cmd));
		}
	}

	return 4;
}

void test_mat_data() {
	struct Tester : NonCopyable {
		usize& i;

		Tester(usize& p) : i(p) {
		}

		~Tester() {
			++i;
		}
	};

	usize i = 0;
	{
		MaterialData data;

		{
			auto test = std::make_shared<Tester>(i);
		}
		if(i != 1) {
			y_fatal("Tester not deleted.");
		}

		{
			auto test = std::make_shared<Tester>(i);
			data.keep_alive(test);
		}
		if(i != 1) {
			y_fatal("Tester deleted.");
		}
	}
	if(i != 2) {
		y_fatal("Tester not deleted.");
	}
	log_msg("Test = OK");
}

int main(int argc, char** argv) {
	perf::set_output(std::move(io::File::create("perfdump.json").unwrap()));

	/*log_msg("Testing frame graph");

	return test_graph();*/

	bool debug = true;
	for(std::string_view arg : core::ArrayView<const char*>(argv, argc)) {
		if(arg == "--nodebug") {
			log_msg("Vulkan debugging disabled", Log::Warning);
			debug = false;
		}
	}

	Instance instance(debug ? DebugParams::debug() : DebugParams::none());
	Device device(instance);

	EditorContext ctx(&device);

	MainWindow window(&ctx);
	window.exec();

	return 0;
}
<commit_msg>Fixed compilation failure<commit_after>
#include "MainWindow.h"

#include <y/io/File.h>

#include <yave/framegraph/renderers.h>

using namespace editor;

int test_graph() {
	Instance instance(DebugParams::debug());
	Device device(instance);
	Scene scene;

	{
		FrameGraph graph(&device);

		auto& gbuffer = render_gbuffer(graph, SceneView(scene), math::Vec2ui(512));

		{
			CmdBufferRecorder rec(device.create_disposable_cmd_buffer());
			std::move(graph).render(rec, gbuffer.color);
			RecordedCmdBuffer cmd(std::move(rec));
			device.queue(vk::QueueFlagBits::eGraphics).submit<SyncSubmit>(std::move(cmd));
		}
	}

	return 4;
}

int main(int argc, char** argv) {
	perf::set_output(std::move(io::File::create("perfdump.json").unwrap()));

	/*log_msg("Testing frame graph");

	return test_graph();*/

	bool debug = true;
	for(std::string_view arg : core::ArrayView<const char*>(argv, argc)) {
		if(arg == "--nodebug") {
			log_msg("Vulkan debugging disabled", Log::Warning);
			debug = false;
		}
	}

	Instance instance(debug ? DebugParams::debug() : DebugParams::none());
	Device device(instance);

	EditorContext ctx(&device);

	MainWindow window(&ctx);
	window.exec();

	return 0;
}
<|endoftext|>