<commit_before>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - General Reacting Incompressible Navier-Stokes
//
// Copyright (C) 2014-2015 Paul T. Bauman, Roy H. Stogner
// Copyright (C) 2010-2013 The PECOS Development Team
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Version 2.1 GNU Lesser General
// Public License as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA
//
//-----------------------------------------------------------------------el-

// This class
#include "grins/velocity_penalty.h"

// GRINS
#include "grins/inc_nav_stokes_macro.h"
#include "grins/postprocessed_quantities.h"

// libMesh
#include "libmesh/quadrature.h"

namespace GRINS
{

  template<class Mu>
  VelocityPenalty<Mu>::VelocityPenalty( const std::string& physics_name, const GetPot& input )
    : VelocityPenaltyBase<Mu>(physics_name, input)
  {
    return;
  }

  template<class Mu>
  VelocityPenalty<Mu>::~VelocityPenalty()
  {
    return;
  }

  template<class Mu>
  void VelocityPenalty<Mu>::init_context( AssemblyContext& context )
  {
    context.get_element_fe(this->_flow_vars.u_var())->get_xyz();
    context.get_element_fe(this->_flow_vars.u_var())->get_phi();

    return;
  }

  template<class Mu>
  void VelocityPenalty<Mu>::register_postprocessing_vars( const GetPot& input,
                                                          PostProcessedQuantities<libMesh::Real>& postprocessing )
  {
    std::string section = "Physics/"+velocity_penalty+"/output_vars";

    if( input.have_variable(section) )
      {
        unsigned int n_vars = input.vector_variable_size(section);

        for( unsigned int v = 0; v < n_vars; v++ )
          {
            std::string name = input(section,"DIE!",v);

            if( name == std::string("velocity_penalty") )
              {
                _velocity_penalty_x_index = postprocessing.register_quantity( std::string("vel_penalty_x") );

                _velocity_penalty_y_index = postprocessing.register_quantity( std::string("vel_penalty_y") );

                _velocity_penalty_z_index = postprocessing.register_quantity( std::string("vel_penalty_z") );
              }
            else if( name == std::string("velocity_penalty_base") )
              {
                _velocity_penalty_base_x_index = postprocessing.register_quantity( std::string("vel_penalty_base_x") );

                _velocity_penalty_base_y_index = postprocessing.register_quantity( std::string("vel_penalty_base_y") );

                _velocity_penalty_base_z_index = postprocessing.register_quantity( std::string("vel_penalty_base_z") );
              }
            else
              {
                std::cerr << "Error: Invalue output_vars value for "+this->_physics_name << std::endl
                          << "       Found " << name << std::endl
                          << "       Acceptable values are: velocity_penalty" << std::endl
                          << "                              velocity_penalty_base" << std::endl;
                libmesh_error();
              }
          }
      }

    return;
  }

  template<class Mu>
  void VelocityPenalty<Mu>::element_time_derivative( bool compute_jacobian,
					         AssemblyContext& context,
					         CachedValues& /* cache */ )
  {
#ifdef GRINS_USE_GRVY_TIMERS
    this->_timer->BeginTimer("VelocityPenalty::element_time_derivative");
#endif

    // Element Jacobian * quadrature weights for interior integration
    const std::vector<libMesh::Real> &JxW =
      context.get_element_fe(this->_flow_vars.u_var())->get_JxW();

    // The shape functions at interior quadrature points.
    const std::vector<std::vector<libMesh::Real> >& u_phi =
      context.get_element_fe(this->_flow_vars.u_var())->get_phi();

    const std::vector<libMesh::Point>& u_qpoint =
      context.get_element_fe(this->_flow_vars.u_var())->get_xyz();

    // The number of local degrees of freedom in each variable
    const unsigned int n_u_dofs = context.get_dof_indices(this->_flow_vars.u_var()).size();

    // The subvectors and submatrices we need to fill:
    libMesh::DenseSubMatrix<libMesh::Number> &Kuu = context.get_elem_jacobian(this->_flow_vars.u_var(), this->_flow_vars.u_var()); // R_{u},{u}
    libMesh::DenseSubMatrix<libMesh::Number> &Kuv = context.get_elem_jacobian(this->_flow_vars.u_var(), this->_flow_vars.v_var()); // R_{u},{v}
    libMesh::DenseSubMatrix<libMesh::Number> &Kvu = context.get_elem_jacobian(this->_flow_vars.v_var(), this->_flow_vars.u_var()); // R_{v},{u}
    libMesh::DenseSubMatrix<libMesh::Number> &Kvv = context.get_elem_jacobian(this->_flow_vars.v_var(), this->_flow_vars.v_var()); // R_{v},{v}

    libMesh::DenseSubMatrix<libMesh::Number>* Kwu = NULL;
    libMesh::DenseSubMatrix<libMesh::Number>* Kwv = NULL;
    libMesh::DenseSubMatrix<libMesh::Number>* Kww = NULL;
    libMesh::DenseSubMatrix<libMesh::Number>* Kuw = NULL;
    libMesh::DenseSubMatrix<libMesh::Number>* Kvw = NULL;

    libMesh::DenseSubVector<libMesh::Number> &Fu = context.get_elem_residual(this->_flow_vars.u_var()); // R_{u}
    libMesh::DenseSubVector<libMesh::Number> &Fv = context.get_elem_residual(this->_flow_vars.v_var()); // R_{v}
    libMesh::DenseSubVector<libMesh::Number>* Fw = NULL;

    if( this->_dim == 3 )
      {
        Kuw = &context.get_elem_jacobian(this->_flow_vars.u_var(), this->_flow_vars.w_var()); // R_{u},{w}
        Kvw = &context.get_elem_jacobian(this->_flow_vars.v_var(), this->_flow_vars.w_var()); // R_{v},{w}

        Kwu = &context.get_elem_jacobian(this->_flow_vars.w_var(), this->_flow_vars.u_var()); // R_{w},{u}
        Kwv = &context.get_elem_jacobian(this->_flow_vars.w_var(), this->_flow_vars.v_var()); // R_{w},{v}
        Kww = &context.get_elem_jacobian(this->_flow_vars.w_var(), this->_flow_vars.w_var()); // R_{w},{w}
        Fw  = &context.get_elem_residual(this->_flow_vars.w_var()); // R_{w}
      }

    unsigned int n_qpoints = context.get_element_qrule().n_points();

    for (unsigned int qp=0; qp != n_qpoints; qp++)
      {
        // Compute the solution at the old Newton iterate.
        libMesh::Number u, v;
        u = context.interior_value(this->_flow_vars.u_var(), qp);
        v = context.interior_value(this->_flow_vars.v_var(), qp);

        libMesh::NumberVectorValue U(u,v);
        if (this->_dim == 3)
          U(2) = context.interior_value(this->_flow_vars.w_var(), qp); // w

        libMesh::NumberVectorValue F;
        libMesh::NumberTensorValue dFdU;
        libMesh::NumberTensorValue* dFdU_ptr =
          compute_jacobian ? &dFdU : NULL;
        if (!this->compute_force(u_qpoint[qp], context.time, U, F, dFdU_ptr))
          continue;

        const libMesh::Real jac = JxW[qp];

        for (unsigned int i=0; i != n_u_dofs; i++)
          {
            const libMesh::Number jac_i = jac * u_phi[i][qp];

            Fu(i) += F(0)*jac_i;

            Fv(i) += F(1)*jac_i;
            if( this->_dim == 3 )
              {
                (*Fw)(i) += F(2)*jac_i;
              }

	    if( compute_jacobian )
              {
                for (unsigned int j=0; j != n_u_dofs; j++)
                  {
                    const libMesh::Number jac_ij = context.get_elem_solution_derivative() * jac_i * u_phi[j][qp];
                    Kuu(i,j) += jac_ij * dFdU(0,0);
                    Kuv(i,j) += jac_ij * dFdU(0,1);
                    Kvu(i,j) += jac_ij * dFdU(1,0);
                    Kvv(i,j) += jac_ij * dFdU(1,1);

                    if( this->_dim == 3 )
                      {
                        (*Kuw)(i,j) += jac_ij * dFdU(0,2);
                        (*Kvw)(i,j) += jac_ij * dFdU(1,2);

                        (*Kwu)(i,j) += jac_ij * dFdU(2,0);
                        (*Kwv)(i,j) += jac_ij * dFdU(2,1);
                        (*Kww)(i,j) += jac_ij * dFdU(2,2);
                      }
                  }
              }
          }
      }

#ifdef GRINS_USE_GRVY_TIMERS
    this->_timer->EndTimer("VelocityPenalty::element_time_derivative");
#endif

    return;
  }

  template<class Mu>
  void VelocityPenalty<Mu>::compute_postprocessed_quantity( unsigned int quantity_index,
                                                            const AssemblyContext& context,
                                                            const libMesh::Point& point,
                                                            libMesh::Real& value )
  {
    libMesh::DenseVector<libMesh::Number> output_vec(3);

    if( quantity_index == this->_velocity_penalty_x_index )
      {
        (*this->normal_vector_function)(point, context.time, output_vec);

        value = output_vec(0);
      }
    else if( quantity_index == this->_velocity_penalty_y_index )
      {
        (*this->normal_vector_function)(point, context.time, output_vec);

        value = output_vec(1);
      }
    else if( quantity_index == this->_velocity_penalty_z_index )
      {
        (*this->normal_vector_function)(point, context.time, output_vec);

        value = output_vec(2);
      }
    else if( quantity_index == this->_velocity_penalty_base_x_index )
      {
        (*this->base_velocity_function)(point, context.time, output_vec);

        value = output_vec(0);
      }
    else if( quantity_index == this->_velocity_penalty_base_y_index )
      {
        (*this->base_velocity_function)(point, context.time, output_vec);

        value = output_vec(1);
      }
    else if( quantity_index == this->_velocity_penalty_base_z_index )
      {
        (*this->base_velocity_function)(point, context.time, output_vec);

        value = output_vec(2);
      }

    return;
  }

} // namespace GRINS
// Instantiate
INSTANTIATE_INC_NS_SUBCLASS(VelocityPenalty);
<commit_msg>VelocityPenalty2 distinct postprocessing names<commit_after>//-----------------------------------------------------------------------bl-
//--------------------------------------------------------------------------
//
// GRINS - General Reacting Incompressible Navier-Stokes
//
// Copyright (C) 2014-2015 Paul T. Bauman, Roy H. Stogner
// Copyright (C) 2010-2013 The PECOS Development Team
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the Version 2.1 GNU Lesser General
// Public License as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc. 51 Franklin Street, Fifth Floor,
// Boston, MA  02110-1301  USA
//
//-----------------------------------------------------------------------el-

// This class
#include "grins/velocity_penalty.h"

// GRINS
#include "grins/inc_nav_stokes_macro.h"
#include "grins/postprocessed_quantities.h"

// libMesh
#include "libmesh/quadrature.h"

namespace GRINS
{

  template<class Mu>
  VelocityPenalty<Mu>::VelocityPenalty( const std::string& physics_name, const GetPot& input )
    : VelocityPenaltyBase<Mu>(physics_name, input)
  {
    return;
  }

  template<class Mu>
  VelocityPenalty<Mu>::~VelocityPenalty()
  {
    return;
  }

  template<class Mu>
  void VelocityPenalty<Mu>::init_context( AssemblyContext& context )
  {
    context.get_element_fe(this->_flow_vars.u_var())->get_xyz();
    context.get_element_fe(this->_flow_vars.u_var())->get_phi();

    return;
  }

  template<class Mu>
  void VelocityPenalty<Mu>::register_postprocessing_vars( const GetPot& input,
                                                          PostProcessedQuantities<libMesh::Real>& postprocessing )
  {
    std::string section = "Physics/"+this->_physics_name+"/output_vars";

    std::string vel_penalty = "vel_penalty";
    if (this->_physics_name == "VelocityPenalty2")
      vel_penalty += 2;

    if( input.have_variable(section) )
      {
        unsigned int n_vars = input.vector_variable_size(section);

        for( unsigned int v = 0; v < n_vars; v++ )
          {
            std::string name = input(section,"DIE!",v);

            if( name == std::string("velocity_penalty") )
              {
                _velocity_penalty_x_index =
                  postprocessing.register_quantity( vel_penalty+"_x" );

                _velocity_penalty_y_index =
                  postprocessing.register_quantity( vel_penalty+"_y" );

                _velocity_penalty_z_index =
                  postprocessing.register_quantity( vel_penalty+"_z" );
              }
            else if( name == std::string("velocity_penalty_base") )
              {
                _velocity_penalty_base_x_index =
                  postprocessing.register_quantity( vel_penalty+"_base_x" );

                _velocity_penalty_base_y_index =
                  postprocessing.register_quantity( vel_penalty+"_base_y" );

                _velocity_penalty_base_z_index =
                  postprocessing.register_quantity( vel_penalty+"_base_z" );
              }
            else
              {
                std::cerr << "Error: Invalue output_vars value for "+this->_physics_name << std::endl
                          << "       Found " << name << std::endl
                          << "       Acceptable values are: velocity_penalty" << std::endl
                          << "                              velocity_penalty_base" << std::endl;
                libmesh_error();
              }
          }
      }

    return;
  }

  template<class Mu>
  void VelocityPenalty<Mu>::element_time_derivative( bool compute_jacobian,
					         AssemblyContext& context,
					         CachedValues& /* cache */ )
  {
#ifdef GRINS_USE_GRVY_TIMERS
    this->_timer->BeginTimer("VelocityPenalty::element_time_derivative");
#endif

    // Element Jacobian * quadrature weights for interior integration
    const std::vector<libMesh::Real> &JxW =
      context.get_element_fe(this->_flow_vars.u_var())->get_JxW();

    // The shape functions at interior quadrature points.
    const std::vector<std::vector<libMesh::Real> >& u_phi =
      context.get_element_fe(this->_flow_vars.u_var())->get_phi();

    const std::vector<libMesh::Point>& u_qpoint =
      context.get_element_fe(this->_flow_vars.u_var())->get_xyz();

    // The number of local degrees of freedom in each variable
    const unsigned int n_u_dofs = context.get_dof_indices(this->_flow_vars.u_var()).size();

    // The subvectors and submatrices we need to fill:
    libMesh::DenseSubMatrix<libMesh::Number> &Kuu = context.get_elem_jacobian(this->_flow_vars.u_var(), this->_flow_vars.u_var()); // R_{u},{u}
    libMesh::DenseSubMatrix<libMesh::Number> &Kuv = context.get_elem_jacobian(this->_flow_vars.u_var(), this->_flow_vars.v_var()); // R_{u},{v}
    libMesh::DenseSubMatrix<libMesh::Number> &Kvu = context.get_elem_jacobian(this->_flow_vars.v_var(), this->_flow_vars.u_var()); // R_{v},{u}
    libMesh::DenseSubMatrix<libMesh::Number> &Kvv = context.get_elem_jacobian(this->_flow_vars.v_var(), this->_flow_vars.v_var()); // R_{v},{v}

    libMesh::DenseSubMatrix<libMesh::Number>* Kwu = NULL;
    libMesh::DenseSubMatrix<libMesh::Number>* Kwv = NULL;
    libMesh::DenseSubMatrix<libMesh::Number>* Kww = NULL;
    libMesh::DenseSubMatrix<libMesh::Number>* Kuw = NULL;
    libMesh::DenseSubMatrix<libMesh::Number>* Kvw = NULL;

    libMesh::DenseSubVector<libMesh::Number> &Fu = context.get_elem_residual(this->_flow_vars.u_var()); // R_{u}
    libMesh::DenseSubVector<libMesh::Number> &Fv = context.get_elem_residual(this->_flow_vars.v_var()); // R_{v}
    libMesh::DenseSubVector<libMesh::Number>* Fw = NULL;

    if( this->_dim == 3 )
      {
        Kuw = &context.get_elem_jacobian(this->_flow_vars.u_var(), this->_flow_vars.w_var()); // R_{u},{w}
        Kvw = &context.get_elem_jacobian(this->_flow_vars.v_var(), this->_flow_vars.w_var()); // R_{v},{w}

        Kwu = &context.get_elem_jacobian(this->_flow_vars.w_var(), this->_flow_vars.u_var()); // R_{w},{u}
        Kwv = &context.get_elem_jacobian(this->_flow_vars.w_var(), this->_flow_vars.v_var()); // R_{w},{v}
        Kww = &context.get_elem_jacobian(this->_flow_vars.w_var(), this->_flow_vars.w_var()); // R_{w},{w}
        Fw  = &context.get_elem_residual(this->_flow_vars.w_var()); // R_{w}
      }

    unsigned int n_qpoints = context.get_element_qrule().n_points();

    for (unsigned int qp=0; qp != n_qpoints; qp++)
      {
        // Compute the solution at the old Newton iterate.
        libMesh::Number u, v;
        u = context.interior_value(this->_flow_vars.u_var(), qp);
        v = context.interior_value(this->_flow_vars.v_var(), qp);

        libMesh::NumberVectorValue U(u,v);
        if (this->_dim == 3)
          U(2) = context.interior_value(this->_flow_vars.w_var(), qp); // w

        libMesh::NumberVectorValue F;
        libMesh::NumberTensorValue dFdU;
        libMesh::NumberTensorValue* dFdU_ptr =
          compute_jacobian ? &dFdU : NULL;
        if (!this->compute_force(u_qpoint[qp], context.time, U, F, dFdU_ptr))
          continue;

        const libMesh::Real jac = JxW[qp];

        for (unsigned int i=0; i != n_u_dofs; i++)
          {
            const libMesh::Number jac_i = jac * u_phi[i][qp];

            Fu(i) += F(0)*jac_i;

            Fv(i) += F(1)*jac_i;
            if( this->_dim == 3 )
              {
                (*Fw)(i) += F(2)*jac_i;
              }

	    if( compute_jacobian )
              {
                for (unsigned int j=0; j != n_u_dofs; j++)
                  {
                    const libMesh::Number jac_ij = context.get_elem_solution_derivative() * jac_i * u_phi[j][qp];
                    Kuu(i,j) += jac_ij * dFdU(0,0);
                    Kuv(i,j) += jac_ij * dFdU(0,1);
                    Kvu(i,j) += jac_ij * dFdU(1,0);
                    Kvv(i,j) += jac_ij * dFdU(1,1);

                    if( this->_dim == 3 )
                      {
                        (*Kuw)(i,j) += jac_ij * dFdU(0,2);
                        (*Kvw)(i,j) += jac_ij * dFdU(1,2);

                        (*Kwu)(i,j) += jac_ij * dFdU(2,0);
                        (*Kwv)(i,j) += jac_ij * dFdU(2,1);
                        (*Kww)(i,j) += jac_ij * dFdU(2,2);
                      }
                  }
              }
          }
      }

#ifdef GRINS_USE_GRVY_TIMERS
    this->_timer->EndTimer("VelocityPenalty::element_time_derivative");
#endif

    return;
  }

  template<class Mu>
  void VelocityPenalty<Mu>::compute_postprocessed_quantity( unsigned int quantity_index,
                                                            const AssemblyContext& context,
                                                            const libMesh::Point& point,
                                                            libMesh::Real& value )
  {
    libMesh::DenseVector<libMesh::Number> output_vec(3);

    if( quantity_index == this->_velocity_penalty_x_index )
      {
        (*this->normal_vector_function)(point, context.time, output_vec);

        value = output_vec(0);
      }
    else if( quantity_index == this->_velocity_penalty_y_index )
      {
        (*this->normal_vector_function)(point, context.time, output_vec);

        value = output_vec(1);
      }
    else if( quantity_index == this->_velocity_penalty_z_index )
      {
        (*this->normal_vector_function)(point, context.time, output_vec);

        value = output_vec(2);
      }
    else if( quantity_index == this->_velocity_penalty_base_x_index )
      {
        (*this->base_velocity_function)(point, context.time, output_vec);

        value = output_vec(0);
      }
    else if( quantity_index == this->_velocity_penalty_base_y_index )
      {
        (*this->base_velocity_function)(point, context.time, output_vec);

        value = output_vec(1);
      }
    else if( quantity_index == this->_velocity_penalty_base_z_index )
      {
        (*this->base_velocity_function)(point, context.time, output_vec);

        value = output_vec(2);
      }

    return;
  }

} // namespace GRINS
// Instantiate
INSTANTIATE_INC_NS_SUBCLASS(VelocityPenalty);
<|endoftext|>


<commit_before>#ifndef DOMUserDataHandler_HEADER_GUARD_
#define DOMUserDataHandler_HEADER_GUARD_

/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id$
 */

#include <xercesc/util/XercesDefs.hpp>
#include <xercesc/dom/DOMNode.hpp>

/**
 * When associating an object to a key on a node using setUserData
 *  the application can provide a handler that gets called when the node the
 * object is associated to is being cloned or imported. This can be used by
 * the application to implement various behaviors regarding the data it
 * associates to the DOM nodes. This interface defines that handler.
 *
See also the Document Object Model (DOM) Level 3 Core Specification.
 * @since DOM Level 3
 */
class CDOM_EXPORT DOMUserDataHandler {

protected:
    // -----------------------------------------------------------------------
    //  Hidden constructors
    // -----------------------------------------------------------------------
    /** @name Hidden constructors */
    //@{
    DOMUserDataHandler() {};
    DOMUserDataHandler(const DOMUserDataHandler &other) {};
    DOMUserDataHandler & operator = (const DOMUserDataHandler &other) {return *this;};
    //@}

public:
    // -----------------------------------------------------------------------
    //  All constructors are hidden, just the destructor is available
    // -----------------------------------------------------------------------
    /** @name Destructor */
    //@{
    /**
     * Destructor
     *
     */
    virtual ~DOMUserDataHandler() {};
    //@}

    // -----------------------------------------------------------------------
    //  Class Types
    // -----------------------------------------------------------------------
    /** @name Public Constants */
    //@{
    /**
     * Operation Type
     *
     *
NODE_CLONED:
     * The node is cloned.

     *
     *
NODE_IMPORTED
     * The node is imported.

     *
     *
NODE_DELETED
     * The node is deleted.

     *
     *
NODE_RENAMED
     * The node is renamed.
     * @since DOM Level 3
     */
    enum DOMOperationType {
        NODE_CLONED               = 1,
        NODE_IMPORTED             = 2,
        NODE_DELETED              = 3,
        NODE_RENAMED              = 4
    };
    //@}

    // -----------------------------------------------------------------------
    //  Virtual DOMUserDataHandler interface
    // -----------------------------------------------------------------------
    /** @name Functions introduced in DOM Level 3 */
    //@{
    /**
     * This method is called whenever the node for which this handler is
     * registered is imported or cloned.
     * @param operation Specifies the type of operation that is being
     *   performed on the node.
     * @param key Specifies the key for which this handler is being called.
     * @param data Specifies the data for which this handler is being called.
     * @param src Specifies the node being cloned, imported, or renamed.
     * @param dst Specifies the node newly created.
     * @since DOM Level 3
     */
    virtual void handle(DOMOperationType operation,
                        const XMLCh* const key,
                        void* data,
                        const DOMNode* src,
                        const DOMNode* dst) = 0;

    //@}

};

#endif

<commit_msg>DOM L3: API Documentation Update, add phrase "experimental"<commit_after>#ifndef DOMUserDataHandler_HEADER_GUARD_
#define DOMUserDataHandler_HEADER_GUARD_

/*
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2002 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. 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.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xerces" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache\@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 APACHE SOFTWARE FOUNDATION OR
 * ITS 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.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation, and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com .  For more information
 * on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */

/*
 * $Id$
 */

#include <xercesc/util/XercesDefs.hpp>
#include <xercesc/dom/DOMNode.hpp>

/**
 * When associating an object to a key on a node using setUserData
 *  the application can provide a handler that gets called when the node the
 * object is associated to is being cloned or imported. This can be used by
 * the application to implement various behaviors regarding the data it
 * associates to the DOM nodes. This interface defines that handler.
 *
 *
"Experimental - subject to change"

 *
 *
See also the Document Object Model (DOM) Level 3 Core Specification.
 * @since DOM Level 3
 */
class CDOM_EXPORT DOMUserDataHandler {

protected:
    // -----------------------------------------------------------------------
    //  Hidden constructors
    // -----------------------------------------------------------------------
    /** @name Hidden constructors */
    //@{
    DOMUserDataHandler() {};
    DOMUserDataHandler(const DOMUserDataHandler &other) {};
    DOMUserDataHandler & operator = (const DOMUserDataHandler &other) {return *this;};
    //@}

public:
    // -----------------------------------------------------------------------
    //  All constructors are hidden, just the destructor is available
    // -----------------------------------------------------------------------
    /** @name Destructor */
    //@{
    /**
     * Destructor
     *
     */
    virtual ~DOMUserDataHandler() {};
    //@}

    // -----------------------------------------------------------------------
    //  Class Types
    // -----------------------------------------------------------------------
    /** @name Public Constants */
    //@{
    /**
     * Operation Type
     *
     *
NODE_CLONED:
     * The node is cloned.

     *
     *
NODE_IMPORTED
     * The node is imported.

     *
     *
NODE_DELETED
     * The node is deleted.

     *
     *
NODE_RENAMED
     * The node is renamed.
     *
     *
"Experimental - subject to change"

     *
     * @since DOM Level 3
     */
    enum DOMOperationType {
        NODE_CLONED               = 1,
        NODE_IMPORTED             = 2,
        NODE_DELETED              = 3,
        NODE_RENAMED              = 4
    };
    //@}

    // -----------------------------------------------------------------------
    //  Virtual DOMUserDataHandler interface
    // -----------------------------------------------------------------------
    /** @name Functions introduced in DOM Level 3 */
    //@{
    /**
     * This method is called whenever the node for which this handler is
     * registered is imported or cloned.
     *
     *
"Experimental - subject to change"

     *
     * @param operation Specifies the type of operation that is being
     *   performed on the node.
     * @param key Specifies the key for which this handler is being called.
     * @param data Specifies the data for which this handler is being called.
     * @param src Specifies the node being cloned, imported, or renamed.
     * @param dst Specifies the node newly created.
     * @since DOM Level 3
     */
    virtual void handle(DOMOperationType operation,
                        const XMLCh* const key,
                        void* data,
                        const DOMNode* src,
                        const DOMNode* dst) = 0;

    //@}

};

#endif

<|endoftext|>


<commit_before>#include "arch/arm/ARMInterceptRouting.h"
#include "arch/arm/ARMInstructionRelocation.h"

#include "vm_core/modules/assembler/assembler-arm.h"
#include "vm_core/modules/codegen/codegen-arm.h"
#include "vm_core/objects/code.h"

using namespace zz::arm;

// arm branch meta info
#define ARM_TINY_REDIRECT_SIZE 4
#define ARM_B_XXX_RANGE (1 << 25) // signed
#define ARM_FULL_REDIRECT_SIZE 8

// thumb branch meta info
#define THUMB1_TINY_REDIRECT_SIZE 2
#define THUMB2_TINY_REDIRECT_SIZE 4
#define THUMB1_B_XXX_RANGE (1 << 10) // signed
#define THUMB2_B_XXX_RANGE (1 << 23) // signed
#define THUMB_FULL_REDIRECT_SIZE 8

static bool is_thumb2(uint32_t inst) {
  uint16_t inst1, inst2;
  inst1 = inst & 0x0000ffff;
  inst2 = (inst & 0xffff0000) >> 16;
  // refer: Top level T32 instruction set encoding
  uint32_t op0 = bits(inst1, 13, 15);

  if (op0 == 0b111) {
    return true;
  }
  return false;
}

InterceptRouting *InterceptRouting::New(HookEntry *entry) {
  return reinterpret_cast<InterceptRouting *>(new ARMInterceptRouting(entry));
}

void ARMInterceptRouting::prepare_thumb() {
  uintptr_t src_address     = (uintptr_t)entry_->target_address;
  Interceptor *interceptor  = Interceptor::SharedInstance();
  MemoryRegion *region      = NULL;
  uword aligned_src_address = ThumbAlign(src_address);

  uint32_t inst = *(uint32_t *)aligned_src_address;
  if (interceptor->options().enable_arm_arm64_b_branch) {
    DLOG("%s", "[*] Enable b branch maybe cause crash, if crashed, please disable it.\n");
    // If the first instuction is thumb1(2 bytes), the first choice for use is thumb1 b-xxx, else use thumb2 b-xxx
    if (!is_thumb2(inst)) {
      // Try allocate a code cave for fast-forward-transfer-trampoline
      region = CodeChunk::AllocateCodeCave(aligned_src_address, THUMB1_B_XXX_RANGE, ARM_FULL_REDIRECT_SIZE);
      if (region) {
        DLOG("%s", "[*] Use Thumb1 B-xxx Branch\n");
        branch_type_  = Thumb1_B_Branch;
        relocate_size = 2;
      }
    }
    // Otherwith condisider the thumb2(4 bytes) b-xxx
    // Try allocate a code cave for fast-forward-transfer-trampoline
    if (!region) {
      region = CodeChunk::AllocateCodeCave(aligned_src_address, THUMB2_B_XXX_RANGE, ARM_FULL_REDIRECT_SIZE);
      if (region) {
        DLOG("%s", "[*] Use Thumb2 B-xxx Branch\n");
        branch_type_  = Thumb2_B_Branch;
        relocate_size = 4;
      } else
        DLOG("%s", "[!] Can't find any cove cave, change to ldr branch");
    }
  }

  if (region)
    fast_forward_region = region;
  else {
    DLOG("%s", "[*] Use Thumb2 Ldr Branch\n");
    branch_type_  = Thumb2_LDR_Branch;
    relocate_size = 8;
  }
}

void ARMInterceptRouting::prepare_arm() {
  uintptr_t src_address    = (uintptr_t)entry_->target_address;
  Interceptor *interceptor = Interceptor::SharedInstance();
  MemoryRegion *region     = NULL;
  if (interceptor->options().enable_arm_arm64_b_branch) {
    region = CodeChunk::AllocateCodeCave(src_address, ARM_B_XXX_RANGE, ARM_FULL_REDIRECT_SIZE);
    if (region) {
      DLOG("%s", "[*] Use ARM B-xxx Branch\n");
      branch_type_  = ARM_B_Branch;
      relocate_size = 4;
    } else {
      // Can't find any code cave, change to ldr branch
      DLOG("%s", "[!] Can't find any cove cave, change to ldr branch");
    }
  }
  if (region)
    fast_forward_region = region;
  else {
    DLOG("%s", "[*] Use ARM LDR Branch\n");
    branch_type_  = ARM_LDR_Branch;
    relocate_size = 8;
  }
}

// Determined if use B_Branch or LDR_Branch, and backup the origin instrutions
void ARMInterceptRouting::Prepare() {
  uintptr_t src_address = (uintptr_t)entry_->target_address;

  // set instruction running state
  execute_state_ = ARMExecuteState;
  if (src_address % 2) {
    execute_state_ = ThumbExecuteState;
  }

  // Determined which branch_type_, and corrreponding trampoline size
  if (execute_state_ == ThumbExecuteState) {
    prepare_thumb();
  } else {
    prepare_arm();
  }

  // Gen the relocated code
  Code *code;
  code                              = GenRelocateCode(src_address, &relocate_size);
  entry_->relocated_origin_function = (void *)code->raw_instruction_start();
  // If Thumb Execute, code snippet address should be odd.
  if (execute_state_ == ThumbExecuteState) {
    entry_->relocated_origin_function = (void *)((uint32_t)entry_->relocated_origin_function + 1);
  }
  DLOG("[*] Relocate origin (prologue) instruction at %p.\n", (void *)code->raw_instruction_start());

  // backup original prologue
  memcpy(entry_->origin_instructions.data, entry_->target_address, relocate_size);
  entry_->origin_instructions.size    = relocate_size;
  entry_->origin_instructions.address = entry_->target_address;
}

// Add pre_call(prologue) handler before running the origin function,
void ARMInterceptRouting::BuildPreCallRouting() {
  // create closure trampoline jump to prologue_routing_dispath with the `entry_` data
  ClosureTrampolineEntry *cte = ClosureTrampoline::CreateClosureTrampoline(entry_, (void *)prologue_routing_dispatch);
  entry_->prologue_dispatch_bridge = cte->address;

  // build the fast forward trampoline jump to the normal routing(prologue_routing_dispatch).
  if (branch_type_ == ARM_B_Branch || branch_type_ == Thumb1_B_Branch || branch_type_ == Thumb2_B_Branch) {
    DLOG("%s", "[*] Fast forward to Pre-ClosureTrampoline\n");
    BuildFastForwardTrampoline();
  }

  DLOG("[*] create pre call closure trampoline to 'prologue_routing_dispatch' at %p\n", cte->address);
}

// Add post_call(epilogue) handler before `Return` of the origin function, as implementation is replace the origin `Return Address` of the function.
void ARMInterceptRouting::BuildPostCallRouting() {
  // create closure trampoline jump to prologue_routing_dispath with the `entry_` data
  ClosureTrampolineEntry *closure_trampoline_entry =
      ClosureTrampoline::CreateClosureTrampoline(entry_, (void *)epilogue_routing_dispatch);
  entry_->epilogue_dispatch_bridge = closure_trampoline_entry->address;

  DLOG("[*] create post call closure trampoline to 'prologue_routing_dispatch' at %p\n",
       closure_trampoline_entry->address);
}

void ARMInterceptRouting::BuildReplaceRouting() {
  // build the fast forward trampoline jump to the normal routing(prologue_routing_dispatch).
  if (branch_type_ == ARM_B_Branch || branch_type_ == Thumb1_B_Branch || branch_type_ == Thumb2_B_Branch) {
    DLOG("%s", "[*] Fast forward to ReplaceCall\n");
    BuildFastForwardTrampoline();
  }
}

static Code *build_arm_fast_forward_trampoline(uintptr_t address, MemoryRegion *region) {
  CustomThumbTurboAssembler thumb_turbo_assembler_;
#define _ thumb_turbo_assembler_.

  _ t2_ldr(pc, MemOperand(pc, 0));
  _ Emit((int32_t)address);

  // Patch
  CodeChunk::MemoryOperationError err;
  err = CodeChunk::PatchCodeBuffer((void *)region->pointer(), thumb_turbo_assembler_.GetCodeBuffer());
  CHECK_EQ(err, CodeChunk::kMemoryOperationSuccess);
  Code *code = Code::FinalizeFromAddress((uintptr_t)region->pointer(), thumb_turbo_assembler_.CodeSize());
}

static Code *build_thumb_fast_forward_trampoline(uintptr_t address, MemoryRegion *region) {
  TurboAssembler turbo_assembler_;
  CodeGen codegen(&turbo_assembler_);
  codegen.LiteralLdrBranch(address);

  // Patch
  CodeChunk::MemoryOperationError err;
  err = CodeChunk::PatchCodeBuffer((void *)region->pointer(), turbo_assembler_.GetCodeBuffer());
  CHECK_EQ(err, CodeChunk::kMemoryOperationSuccess);
  Code *code = Code::FinalizeFromAddress((uintptr_t)region->pointer(), turbo_assembler_.CodeSize());
}

// If BranchType is B_Branch and the branch_range of `B` is not enough, build the transfer to forward the b branch, if
void ARMInterceptRouting::BuildFastForwardTrampoline() {
  uint32_t forward_address;
  Code *code;
  if (entry_->type == kFunctionInlineHook) {
    forward_address = (uintptr_t)entry_->replace_call;
  } else if (entry_->type == kDynamicBinaryInstrumentation) {
    forward_address = (uintptr_t)entry_->prologue_dispatch_bridge;
  } else if (entry_->type == kFunctionWrapper) {
    forward_address = (uintptr_t)entry_->prologue_dispatch_bridge;
  } else {
    UNREACHABLE();
    exit(-1);
  }
  if (execute_state_ == ThumbExecuteState) {
    code = build_thumb_fast_forward_trampoline(forward_address, fast_forward_region);
  } else {
    code = build_arm_fast_forward_trampoline(forward_address, fast_forward_region);
  }
  entry_->fast_forward_trampoline = (void *)code->raw_instruction_start();
}

// Add dbi_call handler before running the origin instructions
void ARMInterceptRouting::BuildDynamicBinaryInstrumentationRouting() {
  // create closure trampoline jump to prologue_routing_dispath with the `entry_` data
  ClosureTrampolineEntry *closure_trampoline_entry =
      ClosureTrampoline::CreateClosureTrampoline(entry_, (void *)prologue_routing_dispatch);
  entry_->prologue_dispatch_bridge = closure_trampoline_entry->address;

  if (branch_type_ == ARM_B_Branch || branch_type_ == Thumb1_B_Branch || branch_type_ == Thumb2_B_Branch) {
    BuildFastForwardTrampoline();
  }
  DLOG("create dynamic binary instrumentation call closure trampoline to 'prologue_dispatch_bridge' %p\n",
       closure_trampoline_entry->address);
}

// alias Active
void ARMInterceptRouting::Commit() { Active(); }

// active arm intercept routing
void ARMInterceptRouting::active_arm_intercept_routing() {
  TurboAssembler turbo_assembler_;
#define _ turbo_assembler_.

  uintptr_t target_address = (uintptr_t)entry_->target_address;

  if (branch_type_ == ARM_B_Branch) {
    _ b((int32_t)entry_->fast_forward_trampoline - (int32_t)target_address - ARM_PC_OFFSET);
  } else {
    CodeGen codegen(&turbo_assembler_);
    // check if enable "fast forward trampoline"
    if (entry_->fast_forward_trampoline)
      codegen.LiteralLdrBranch((uint64_t)entry_->fast_forward_trampoline);
    else
      codegen.LiteralLdrBranch((uint64_t)entry_->prologue_dispatch_bridge);
  }

  // Patch
  CodeChunk::MemoryOperationError err;
  err = CodeChunk::PatchCodeBuffer((void *)target_address, turbo_assembler_.GetCodeBuffer());
  CHECK_EQ(err, CodeChunk::kMemoryOperationSuccess);
  Code::FinalizeFromAddress(target_address, turbo_assembler_.CodeSize());
}

// active thumb intercept routing
void ARMInterceptRouting::active_thumb_intercept_routing() {
  CustomThumbTurboAssembler thumb_turbo_assembler_;
#define _ thumb_turbo_assembler_.

  uintptr_t target_address         = (uintptr_t)entry_->target_address;
  uintptr_t aligned_target_address = ThumbAlign(target_address);

  if (branch_type_ == ARMInterceptRouting::Thumb1_B_Branch) {
    _ t1_b((int32_t)entry_->fast_forward_trampoline - (int32_t)aligned_target_address - Thumb_PC_OFFSET);
  } else if (branch_type_ == ARMInterceptRouting::Thumb2_B_Branch) {
    _ t2_b((int32_t)entry_->fast_forward_trampoline - (int32_t)aligned_target_address - Thumb_PC_OFFSET);
  } else if (branch_type_ == ARMInterceptRouting::Thumb2_LDR_Branch) {
    // Check if needed pc align, (relative pc instructions needed 4 align)
    if (aligned_target_address % 4)
      _ t2_ldr(pc, MemOperand(pc, 2));
    else {
      _ t2_ldr(pc, MemOperand(pc, 0));
    }

    // check if enable "fast forward trampoline"
    if (entry_->fast_forward_trampoline)
      _ Emit((int32_t)entry_->fast_forward_trampoline);
    else if (entry_->prologue_dispatch_bridge)
      _ Emit((int32_t)entry_->prologue_dispatch_bridge);
    else {
      if (entry_->type == kFunctionInlineHook)
        _ Emit((int32_t)entry_->replace_call);
    }
  } else {
    UNREACHABLE();
  }
  // Patch
  CodeChunk::MemoryOperationError err;
  err = CodeChunk::PatchCodeBuffer((void *)aligned_target_address, thumb_turbo_assembler_.GetCodeBuffer());
  CHECK_EQ(err, CodeChunk::kMemoryOperationSuccess);
  Code::FinalizeFromAddress(aligned_target_address, thumb_turbo_assembler_.CodeSize());
}

// Active routing, will patch the origin insturctions, and forward to our custom routing.
void ARMInterceptRouting::Active() {
  if (execute_state_ == ARMExecuteState) {
    DLOG("[*] Active the routing at %p\n", entry_->target_address);
    active_arm_intercept_routing();
  } else {
    DLOG("[*] Active the routing at %p\n", entry_->target_address);
    active_thumb_intercept_routing();
  }
}
<commit_msg>[bug-fix] fix build fast forward trampoline<commit_after>#include "arch/arm/ARMInterceptRouting.h"
#include "arch/arm/ARMInstructionRelocation.h"

#include "vm_core/modules/assembler/assembler-arm.h"
#include "vm_core/modules/codegen/codegen-arm.h"
#include "vm_core/objects/code.h"

using namespace zz::arm;

// arm branch meta info
#define ARM_TINY_REDIRECT_SIZE 4
#define ARM_B_XXX_RANGE (1 << 25) // signed
#define ARM_FULL_REDIRECT_SIZE 8

// thumb branch meta info
#define THUMB1_TINY_REDIRECT_SIZE 2
#define THUMB2_TINY_REDIRECT_SIZE 4
#define THUMB1_B_XXX_RANGE (1 << 10) // signed
#define THUMB2_B_XXX_RANGE (1 << 23) // signed
#define THUMB_FULL_REDIRECT_SIZE 8

static bool is_thumb2(uint32_t inst) {
  uint16_t inst1, inst2;
  inst1 = inst & 0x0000ffff;
  inst2 = (inst & 0xffff0000) >> 16;
  // refer: Top level T32 instruction set encoding
  uint32_t op0 = bits(inst1, 13, 15);

  if (op0 == 0b111) {
    return true;
  }
  return false;
}

InterceptRouting *InterceptRouting::New(HookEntry *entry) {
  return reinterpret_cast<InterceptRouting *>(new ARMInterceptRouting(entry));
}

void ARMInterceptRouting::prepare_thumb() {
  uintptr_t src_address     = (uintptr_t)entry_->target_address;
  Interceptor *interceptor  = Interceptor::SharedInstance();
  MemoryRegion *region      = NULL;
  uword aligned_src_address = ThumbAlign(src_address);

  uint32_t inst = *(uint32_t *)aligned_src_address;
  if (interceptor->options().enable_arm_arm64_b_branch) {
    DLOG("%s", "[*] Enable b branch maybe cause crash, if crashed, please disable it.\n");
    // If the first instuction is thumb1(2 bytes), the first choice for use is thumb1 b-xxx, else use thumb2 b-xxx
    if (!is_thumb2(inst)) {
      // Try allocate a code cave for fast-forward-transfer-trampoline
      region = CodeChunk::AllocateCodeCave(aligned_src_address, THUMB1_B_XXX_RANGE, ARM_FULL_REDIRECT_SIZE);
      if (region) {
        DLOG("%s", "[*] Use Thumb1 B-xxx Branch\n");
        branch_type_  = Thumb1_B_Branch;
        relocate_size = 2;
      }
    }
    // Otherwith condisider the thumb2(4 bytes) b-xxx
    // Try allocate a code cave for fast-forward-transfer-trampoline
    if (!region) {
      region = CodeChunk::AllocateCodeCave(aligned_src_address, THUMB2_B_XXX_RANGE, ARM_FULL_REDIRECT_SIZE);
      if (region) {
        DLOG("%s", "[*] Use Thumb2 B-xxx Branch\n");
        branch_type_  = Thumb2_B_Branch;
        relocate_size = 4;
      } else
        DLOG("%s", "[!] Can't find any cove cave, change to ldr branch");
    }
  }

  if (region)
    fast_forward_region = region;
  else {
    DLOG("%s", "[*] Use Thumb2 Ldr Branch\n");
    branch_type_  = Thumb2_LDR_Branch;
    relocate_size = 8;
  }
}

void ARMInterceptRouting::prepare_arm() {
  uintptr_t src_address    = (uintptr_t)entry_->target_address;
  Interceptor *interceptor = Interceptor::SharedInstance();
  MemoryRegion *region     = NULL;
  if (interceptor->options().enable_arm_arm64_b_branch) {
    region = CodeChunk::AllocateCodeCave(src_address, ARM_B_XXX_RANGE, ARM_FULL_REDIRECT_SIZE);
    if (region) {
      DLOG("%s", "[*] Use ARM B-xxx Branch\n");
      branch_type_  = ARM_B_Branch;
      relocate_size = 4;
    } else {
      // Can't find any code cave, change to ldr branch
      DLOG("%s", "[!] Can't find any cove cave, change to ldr branch");
    }
  }
  if (region)
    fast_forward_region = region;
  else {
    DLOG("%s", "[*] Use ARM LDR Branch\n");
    branch_type_  = ARM_LDR_Branch;
    relocate_size = 8;
  }
}

// Determined if use B_Branch or LDR_Branch, and backup the origin instrutions
void ARMInterceptRouting::Prepare() {
  uintptr_t src_address = (uintptr_t)entry_->target_address;

  // set instruction running state
  execute_state_ = ARMExecuteState;
  if (src_address % 2) {
    execute_state_ = ThumbExecuteState;
  }

  // Determined which branch_type_, and corrreponding trampoline size
  if (execute_state_ == ThumbExecuteState) {
    prepare_thumb();
  } else {
    prepare_arm();
  }

  // Gen the relocated code
  Code *code;
  code                              = GenRelocateCode(src_address, &relocate_size);
  entry_->relocated_origin_function = (void *)code->raw_instruction_start();
  // If Thumb Execute, code snippet address should be odd.
  if (execute_state_ == ThumbExecuteState) {
    entry_->relocated_origin_function = (void *)((uint32_t)entry_->relocated_origin_function + 1);
  }
  DLOG("[*] Relocate origin (prologue) instruction at %p.\n", (void *)code->raw_instruction_start());

  // backup original prologue
  memcpy(entry_->origin_instructions.data, entry_->target_address, relocate_size);
  entry_->origin_instructions.size    = relocate_size;
  entry_->origin_instructions.address = entry_->target_address;
}

// Add pre_call(prologue) handler before running the origin function,
void ARMInterceptRouting::BuildPreCallRouting() {
  // create closure trampoline jump to prologue_routing_dispath with the `entry_` data
  ClosureTrampolineEntry *cte = ClosureTrampoline::CreateClosureTrampoline(entry_, (void *)prologue_routing_dispatch);
  entry_->prologue_dispatch_bridge = cte->address;

  // build the fast forward trampoline jump to the normal routing(prologue_routing_dispatch).
  if (branch_type_ == ARM_B_Branch || branch_type_ == Thumb1_B_Branch || branch_type_ == Thumb2_B_Branch) {
    DLOG("%s", "[*] Fast forward to Pre-ClosureTrampoline\n");
    BuildFastForwardTrampoline();
  }

  DLOG("[*] create pre call closure trampoline to 'prologue_routing_dispatch' at %p\n", cte->address);
}

// Add post_call(epilogue) handler before `Return` of the origin function, as implementation is replace the origin `Return Address` of the function.
void ARMInterceptRouting::BuildPostCallRouting() {
  // create closure trampoline jump to prologue_routing_dispath with the `entry_` data
  ClosureTrampolineEntry *closure_trampoline_entry =
      ClosureTrampoline::CreateClosureTrampoline(entry_, (void *)epilogue_routing_dispatch);
  entry_->epilogue_dispatch_bridge = closure_trampoline_entry->address;

  DLOG("[*] create post call closure trampoline to 'prologue_routing_dispatch' at %p\n",
       closure_trampoline_entry->address);
}

void ARMInterceptRouting::BuildReplaceRouting() {
  // build the fast forward trampoline jump to the normal routing(prologue_routing_dispatch).
  if (branch_type_ == ARM_B_Branch || branch_type_ == Thumb1_B_Branch || branch_type_ == Thumb2_B_Branch) {
    DLOG("%s", "[*] Fast forward to ReplaceCall\n");
    BuildFastForwardTrampoline();
  }
}

static Code *build_thumb_fast_forward_trampoline(uintptr_t address, MemoryRegion *region) {
  CustomThumbTurboAssembler thumb_turbo_assembler_;
#define _ thumb_turbo_assembler_.

  _ t2_ldr(pc, MemOperand(pc, 0));
  _ Emit((int32_t)address);

  // Patch
  CodeChunk::MemoryOperationError err;
  err = CodeChunk::PatchCodeBuffer((void *)region->pointer(), thumb_turbo_assembler_.GetCodeBuffer());
  CHECK_EQ(err, CodeChunk::kMemoryOperationSuccess);
  Code *code = Code::FinalizeFromAddress((uintptr_t)region->pointer(), thumb_turbo_assembler_.CodeSize());
  return code;
}

static Code *build_arm_fast_forward_trampoline(uintptr_t address, MemoryRegion *region) {
  TurboAssembler turbo_assembler_;
  CodeGen codegen(&turbo_assembler_);
  codegen.LiteralLdrBranch(address);

  // Patch
  CodeChunk::MemoryOperationError err;
  err = CodeChunk::PatchCodeBuffer((void *)region->pointer(), turbo_assembler_.GetCodeBuffer());
  CHECK_EQ(err, CodeChunk::kMemoryOperationSuccess);
  Code *code = Code::FinalizeFromAddress((uintptr_t)region->pointer(), turbo_assembler_.CodeSize());
  return code;
}

// If BranchType is B_Branch and the branch_range of `B` is not enough, build the transfer to forward the b branch, if
void ARMInterceptRouting::BuildFastForwardTrampoline() {
  uint32_t forward_address;
  Code *code;
  if (entry_->type == kFunctionInlineHook) {
    forward_address = (uintptr_t)entry_->replace_call;
  } else if (entry_->type == kDynamicBinaryInstrumentation) {
    forward_address = (uintptr_t)entry_->prologue_dispatch_bridge;
  } else if (entry_->type == kFunctionWrapper) {
    forward_address = (uintptr_t)entry_->prologue_dispatch_bridge;
  } else {
    UNREACHABLE();
    exit(-1);
  }
  if (execute_state_ == ThumbExecuteState) {
    code = build_thumb_fast_forward_trampoline(forward_address, fast_forward_region);
  } else {
    code = build_arm_fast_forward_trampoline(forward_address, fast_forward_region);
  }
  entry_->fast_forward_trampoline = (void *)code->raw_instruction_start();
}

// Add dbi_call handler before running the origin instructions
void ARMInterceptRouting::BuildDynamicBinaryInstrumentationRouting() {
  // create closure trampoline jump to prologue_routing_dispath with the `entry_` data
  ClosureTrampolineEntry *closure_trampoline_entry =
      ClosureTrampoline::CreateClosureTrampoline(entry_, (void *)prologue_routing_dispatch);
  entry_->prologue_dispatch_bridge = closure_trampoline_entry->address;

  if (branch_type_ == ARM_B_Branch || branch_type_ == Thumb1_B_Branch || branch_type_ == Thumb2_B_Branch) {
    BuildFastForwardTrampoline();
  }
  DLOG("create dynamic binary instrumentation call closure trampoline to 'prologue_dispatch_bridge' %p\n",
       closure_trampoline_entry->address);
}

// alias Active
void ARMInterceptRouting::Commit() { Active(); }

// active arm intercept routing
void ARMInterceptRouting::active_arm_intercept_routing() {
  TurboAssembler turbo_assembler_;
#define _ turbo_assembler_.

  uintptr_t target_address = (uintptr_t)entry_->target_address;

  if (branch_type_ == ARM_B_Branch) {
    _ b((int32_t)entry_->fast_forward_trampoline - (int32_t)target_address - ARM_PC_OFFSET);
  } else {
    CodeGen codegen(&turbo_assembler_);
    // check if enable "fast forward trampoline"
    if (entry_->fast_forward_trampoline)
      codegen.LiteralLdrBranch((uint64_t)entry_->fast_forward_trampoline);
    else
      codegen.LiteralLdrBranch((uint64_t)entry_->prologue_dispatch_bridge);
  }

  // Patch
  CodeChunk::MemoryOperationError err;
  err = CodeChunk::PatchCodeBuffer((void *)target_address, turbo_assembler_.GetCodeBuffer());
  CHECK_EQ(err, CodeChunk::kMemoryOperationSuccess);
  Code::FinalizeFromAddress(target_address, turbo_assembler_.CodeSize());
}

// active thumb intercept routing
void ARMInterceptRouting::active_thumb_intercept_routing() {
  CustomThumbTurboAssembler thumb_turbo_assembler_;
#define _ thumb_turbo_assembler_.

  uintptr_t target_address         = (uintptr_t)entry_->target_address;
  uintptr_t aligned_target_address = ThumbAlign(target_address);

  if (branch_type_ == ARMInterceptRouting::Thumb1_B_Branch) {
    _ t1_b((int32_t)entry_->fast_forward_trampoline - (int32_t)aligned_target_address - Thumb_PC_OFFSET);
  } else if (branch_type_ == ARMInterceptRouting::Thumb2_B_Branch) {
    _ t2_b((int32_t)entry_->fast_forward_trampoline - (int32_t)aligned_target_address - Thumb_PC_OFFSET);
  } else if (branch_type_ == ARMInterceptRouting::Thumb2_LDR_Branch) {
    // Check if needed pc align, (relative pc instructions needed 4 align)
    if (aligned_target_address % 4)
      _ t2_ldr(pc, MemOperand(pc, 2));
    else {
      _ t2_ldr(pc, MemOperand(pc, 0));
    }

    // check if enable "fast forward trampoline"
    if (entry_->fast_forward_trampoline)
      _ Emit((int32_t)entry_->fast_forward_trampoline);
    else if (entry_->prologue_dispatch_bridge)
      _ Emit((int32_t)entry_->prologue_dispatch_bridge);
    else {
      if (entry_->type == kFunctionInlineHook)
        _ Emit((int32_t)entry_->replace_call);
    }
  } else {
    UNREACHABLE();
  }
  // Patch
  CodeChunk::MemoryOperationError err;
  err = CodeChunk::PatchCodeBuffer((void *)aligned_target_address, thumb_turbo_assembler_.GetCodeBuffer());
  CHECK_EQ(err, CodeChunk::kMemoryOperationSuccess);
  Code::FinalizeFromAddress(aligned_target_address, thumb_turbo_assembler_.CodeSize());
}

// Active routing, will patch the origin insturctions, and forward to our custom routing.
void ARMInterceptRouting::Active() {
  if (execute_state_ == ARMExecuteState) {
    DLOG("[*] Active the routing at %p\n", entry_->target_address);
    active_arm_intercept_routing();
  } else {
    DLOG("[*] Active the routing at %p\n", entry_->target_address);
    active_thumb_intercept_routing();
  }
}
<|endoftext|>


<commit_before>#include "YObjectManager.h"

/** C++ **/
#include <cassert>

/** YDA */
#include "YObject.cpp"

YObjectManager::YObjectManager()
{
    m_started = false;
}

YObjectManager::~YObjectManager()
{

}

void YObjectManager::add(YObject* a_object)
{
    assert(a_object != nullptr);

    m_objects.push_back(a_object);
}

void YObjectManager::remove(YObject* a_object)
{
    m_objects.erase(std::remove(m_objects.begin(),
                                m_objects.end(),
                                a_object),
                    m_objects.end());
}

void YObjectManager::updateRender()
{
    if (m_started == false)
    {
        m_started = true;

        struct
        {
            bool operator()(YObject* a_first, YObject* a_second) const
            {
                return a_first->position().z() < a_second->position().z();
            }
        } Comparator;

        m_update = [&](SDL_Event* a_event, float a_deltaT)
        {
            std::sort(m_objects.begin(),
                      m_objects.end(),
                      Comparator);

            for (YObject* object: m_objects)
            {
                object->processEvents(a_event);
                object->update(a_deltaT);
            }
        };

        m_render = [&](SDL_Renderer* a_renderer)
        {
            for (YObject* object: m_objects)
            {
                object->render(a_renderer);
            }
        };
    }
}

YMain::FunctionUpdate* YObjectManager::updater()
{
    return &m_update;
}

YMain::FunctionRender* YObjectManager::renderer()
{
    return &m_render;
}<commit_msg>Adds updateRender method on YObjectManager constructor<commit_after>#include "YObjectManager.h"

/** C++ **/
#include <cassert>

/** YDA */
#include "YObject.cpp"

YObjectManager::YObjectManager()
{
    m_started = false;
    updateRender();
}

YObjectManager::~YObjectManager()
{

}

void YObjectManager::add(YObject* a_object)
{
    assert(a_object != nullptr);

    m_objects.push_back(a_object);
}

void YObjectManager::remove(YObject* a_object)
{
    m_objects.erase(std::remove(m_objects.begin(),
                                m_objects.end(),
                                a_object),
                    m_objects.end());
}

void YObjectManager::updateRender()
{
    if (m_started == false)
    {
        m_started = true;

        struct
        {
            bool operator()(YObject* a_first, YObject* a_second) const
            {
                return a_first->position().z() < a_second->position().z();
            }
        } Comparator;

        m_update = [&](SDL_Event* a_event, float a_deltaT)
        {
            std::sort(m_objects.begin(),
                      m_objects.end(),
                      Comparator);

            for (YObject* object: m_objects)
            {
                object->processEvents(a_event);
                object->update(a_deltaT);
            }
        };

        m_render = [&](SDL_Renderer* a_renderer)
        {
            for (YObject* object: m_objects)
            {
                object->render(a_renderer);
            }
        };
    }
}

YMain::FunctionUpdate* YObjectManager::updater()
{
    return &m_update;
}

YMain::FunctionRender* YObjectManager::renderer()
{
    return &m_render;
}<|endoftext|>


<commit_before>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include "caf/byte_buffer.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/has_after_reading.hpp"
#include "caf/fwd.hpp"
#include "caf/logger.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/handshake_worker.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/net/stream_oriented_layer_ptr.hpp"
#include "caf/net/stream_socket.hpp"
#include "caf/net/stream_transport.hpp"
#include "caf/sec.hpp"
#include "caf/settings.hpp"
#include "caf/span.hpp"
#include "caf/tag/io_event_oriented.hpp"
#include "caf/tag/stream_oriented.hpp"

CAF_PUSH_WARNINGS
#include <openssl/err.h>
#include <openssl/ssl.h>
CAF_POP_WARNINGS

#if OPENSSL_VERSION_NUMBER < 0x10100000L
#  error "OpenSSL version too old. CAF::net requires at least OpenSSL 1.1"
#endif

#include <string>
#include <string_view>

// -- small wrappers to help working with OpenSSL ------------------------------

namespace caf::net::openssl {

/// Dispatches to the proper OpenSSL `free` function for each OpenSSL type.
struct deleter {
  void operator()(SSL_CTX* ptr) const {
    SSL_CTX_free(ptr);
  }

  void operator()(SSL* ptr) const {
    SSL_free(ptr);
  }
};

/// A smart pointer to an `SSL_CTX` structure.
/// @note technically, SSL structures are reference counted and we could use
///       `intrusive_ptr` instead. However, we have no need for shared ownership
///       semantics here and use `unique_ptr` for simplicity.
using ctx_ptr = std::unique_ptr<SSL_CTX, deleter>;

/// A smart pointer to an `SSL` structure.
/// @note technically, SSL structures are reference counted and we could use
///       `intrusive_ptr` instead. However, we have no need for shared ownership
///       semantics here and use `unique_ptr` for simplicity.
using conn_ptr = std::unique_ptr<SSL, deleter>;

/// Convenience function for creating an OpenSSL context for given method.
inline ctx_ptr make_ctx(const SSL_METHOD* method) {
  if (auto ptr = SSL_CTX_new(method))
    return ctx_ptr{ptr};
  else
    CAF_RAISE_ERROR("SSL_CTX_new failed");
}

/// Fetches a string representation for the last OpenSSL errors.
std::string fetch_error_str() {
  auto cb = [](const char* cstr, size_t len, void* vptr) -> int {
    auto& str = *reinterpret_cast<std::string*>(vptr);
    if (str.empty()) {
      str.assign(cstr, len);
    } else {
      str += "; ";
      auto view = std::string_view{cstr, len};
      str.insert(str.end(), view.begin(), view.end());
    }
    return 1;
  };
  std::string result;
  ERR_print_errors_cb(cb, &result);
  return result;
}

///  Loads the certificate into the SSL context.
inline error certificate_pem_file(const ctx_ptr& ctx, const std::string& path) {
  auto cstr = path.c_str();
  if (SSL_CTX_use_certificate_file(ctx.get(), cstr, SSL_FILETYPE_PEM) > 0) {
    return none;
  } else {
    return make_error(sec::invalid_argument, fetch_error_str());
  }
}

///  Loads the private key into the SSL context.
inline error private_key_pem_file(const ctx_ptr& ctx, const std::string& path) {
  auto cstr = path.c_str();
  if (SSL_CTX_use_PrivateKey_file(ctx.get(), cstr, SSL_FILETYPE_PEM) > 0) {
    return none;
  } else {
    return make_error(sec::invalid_argument, fetch_error_str());
  }
}

/// Convenience function for creating a new SSL structure from given context.
inline conn_ptr make_conn(const ctx_ptr& ctx) {
  if (auto ptr = SSL_new(ctx.get()))
    return conn_ptr{ptr};
  else
    CAF_RAISE_ERROR("SSL_new failed");
}

/// Convenience function for creating a new SSL structure from given context and
/// binding the given socket to it.
inline conn_ptr make_conn(const ctx_ptr& ctx, stream_socket fd) {
  auto ptr = make_conn(ctx);
  if (SSL_set_fd(ptr.get(), fd.id))
    return ptr;
  else
    CAF_RAISE_ERROR("SSL_set_fd failed");
}

/// Manages an OpenSSL connection.
class policy {
public:
  // -- constructors, destructors, and assignment operators --------------------

  policy() = delete;

  policy(const policy&) = delete;

  policy& operator=(const policy&) = delete;

  policy(policy&&) = default;

  policy& operator=(policy&&) = default;

  policy(ctx_ptr ctx, conn_ptr conn)
    : ctx_(std::move(ctx)), conn_(std::move(conn)) {
    // nop
  }

  // -- factories --------------------------------------------------------------

  /// Creates a policy from an SSL method and socket.
  static policy make(const SSL_METHOD* method, stream_socket fd) {
    auto ctx = make_ctx(method);
    auto conn = make_conn(ctx, fd);
    return policy{std::move(ctx), std::move(conn)};
  }

  /// Creates a policy from an SSL context and socket.
  static policy make(ctx_ptr ctx, stream_socket fd) {
    auto conn = make_conn(ctx, fd);
    return policy{std::move(ctx), std::move(conn)};
  }

  // -- properties -------------------------------------------------------------

  SSL_CTX* ctx() {
    return ctx_.get();
  }

  SSL* conn() {
    return conn_.get();
  }

  // -- OpenSSL settings -------------------------------------------------------

  ///  Loads the certificate into the SSL connection object.
  error certificate_pem_file(const std::string& path) {
    auto cstr = path.c_str();
    if (SSL_use_certificate_file(conn(), cstr, SSL_FILETYPE_PEM) > 0) {
      return none;
    } else {
      return make_error(sec::invalid_argument, fetch_error_str());
    }
  }

  ///  Loads the private key into the SSL connection object.
  error private_key_pem_file(const std::string& path) {
    auto cstr = path.c_str();
    if (SSL_use_PrivateKey_file(conn(), cstr, SSL_FILETYPE_PEM) > 0) {
      return none;
    } else {
      return make_error(sec::invalid_argument, fetch_error_str());
    }
  }

  // -- interface functions for the stream transport ---------------------------

  /// Fetches a string representation for the last error.
  std::string fetch_error_str() {
    return openssl::fetch_error_str();
  }

  /// Reads data from the SSL connection into the buffer.
  ptrdiff_t read(stream_socket, span<byte> buf) {
    return SSL_read(conn_.get(), buf.data(), static_cast<int>(buf.size()));
  }

  /// Writes data from the buffer to the SSL connection.
  ptrdiff_t write(stream_socket, span<const byte> buf) {
    return SSL_write(conn_.get(), buf.data(), static_cast<int>(buf.size()));
  }

  /// Performs a TLS/SSL handshake with the server.
  ptrdiff_t connect(stream_socket) {
    return SSL_connect(conn_.get());
  }

  /// Waits for the client to performs a TLS/SSL handshake.
  ptrdiff_t accept(stream_socket) {
    return SSL_accept(conn_.get());
  }

  /// Returns the last SSL error.
  stream_transport_error last_error(stream_socket fd, ptrdiff_t ret) {
    switch (SSL_get_error(conn_.get(), static_cast<int>(ret))) {
      case SSL_ERROR_NONE:
      case SSL_ERROR_WANT_ACCEPT:
      case SSL_ERROR_WANT_CONNECT:
        // For all of these, OpenSSL docs say to do the operation again later.
        return stream_transport_error::temporary;
      case SSL_ERROR_SYSCALL:
        // Need to consult errno, which we just leave to the default policy.
        return default_stream_transport_policy::last_error(fd, ret);
      case SSL_ERROR_WANT_READ:
        return stream_transport_error::want_read;
      case SSL_ERROR_WANT_WRITE:
        return stream_transport_error::want_write;
      default:
        // Errors like SSL_ERROR_WANT_X509_LOOKUP are technically temporary, but
        // we do not configure any callbacks. So seeing this is a red flag.
        return stream_transport_error::permanent;
    }
  }

  /// Graceful shutdown.
  void notify_close() {
    SSL_shutdown(conn_.get());
  }

  /// Returns the number of bytes that are buffered internally and that are
  /// available for immediate read.
  size_t buffered() {
    return static_cast<size_t>(SSL_pending(conn_.get()));
  }

private:
  /// Our SSL context.
  openssl::ctx_ptr ctx_;

  /// Our SSL connection data.
  openssl::conn_ptr conn_;
};

/// Asynchronously starts the TLS/SSL client handshake.
/// @param fd A connected stream socket.
/// @param mpx Pointer to the multiplexer for managing the asynchronous events.
/// @param pol The OpenSSL policy with properly configured SSL/TSL method.
/// @param on_success The callback for creating a @ref socket_manager after
///                   successfully connecting to the server.
/// @param on_error The callback for unexpected errors.
/// @pre `fd != invalid_socket`
/// @pre `mpx != nullptr`
template <class Socket, class OnSuccess, class OnError>
void async_connect(Socket fd, multiplexer* mpx, policy pol,
                   OnSuccess on_success, OnError on_error) {
  using res_t = decltype(on_success(fd, mpx, std::move(pol)));
  using err_t = decltype(on_error(error{}));
  static_assert(std::is_convertible_v<res_t, socket_manager_ptr>,
                "on_success must return a socket_manager_ptr");
  static_assert(std::is_same_v<err_t, void>,
                "on_error may not return anything");
  using factory_t = default_handshake_worker_factory<OnSuccess, OnError>;
  using worker_t = handshake_worker<false, Socket, policy, factory_t>;
  auto factory = factory_t{std::move(on_success), std::move(on_error)};
  auto mgr = make_counted<worker_t>(fd, mpx, std::move(pol),
                                    std::move(factory));
  mpx->init(mgr);
}

/// Asynchronously starts the TLS/SSL server handshake.
/// @param fd A connected stream socket.
/// @param mpx Pointer to the multiplexer for managing the asynchronous events.
/// @param pol The OpenSSL policy with properly configured SSL/TSL method.
/// @param on_success The callback for creating a @ref socket_manager after
///                   successfully connecting to the client.
/// @param on_error The callback for unexpected errors.
/// @pre `fd != invalid_socket`
/// @pre `mpx != nullptr`
template <class Socket, class OnSuccess, class OnError>
void async_accept(Socket fd, multiplexer* mpx, policy pol, OnSuccess on_success,
                  OnError on_error) {
  using res_t = decltype(on_success(fd, mpx, std::move(pol)));
  using err_t = decltype(on_error(error{}));
  static_assert(std::is_convertible_v<res_t, socket_manager_ptr>,
                "on_success must return a socket_manager_ptr");
  static_assert(std::is_same_v<err_t, void>,
                "on_error may not return anything");
  using factory_t = default_handshake_worker_factory<OnSuccess, OnError>;
  using worker_t = handshake_worker<true, Socket, policy, factory_t>;
  auto factory = factory_t{std::move(on_success), std::move(on_error)};
  auto mgr = make_counted<worker_t>(fd, mpx, std::move(pol),
                                    std::move(factory));
  mpx->init(mgr);
}

} // namespace caf::net::openssl

namespace caf::net {

/// Implements a stream_transport that manages a stream socket with encrypted
/// communication over OpenSSL.
template <class UpperLayer>
class openssl_transport
  : public stream_transport_base<openssl::policy, UpperLayer> {
public:
  // -- member types -----------------------------------------------------------

  using super = stream_transport_base<openssl::policy, UpperLayer>;

  // -- constructors, destructors, and assignment operators --------------------

  template <class... Ts>
  openssl_transport(openssl::ctx_ptr ctx, openssl::conn_ptr conn, Ts&&... xs)
    : super(openssl::policy{std::move(ctx), std::move(conn)},
            std::forward<Ts>(xs)...) {
    // nop
  }

  template <class... Ts>
  openssl_transport(openssl::policy policy, Ts&&... xs)
    : super(std::move(policy), std::forward<Ts>(xs)...) {
    // nop
  }
};

} // namespace caf::net
<commit_msg>Make the OpenSSL policy copyable<commit_after>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include "caf/byte_buffer.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/has_after_reading.hpp"
#include "caf/fwd.hpp"
#include "caf/logger.hpp"
#include "caf/net/fwd.hpp"
#include "caf/net/handshake_worker.hpp"
#include "caf/net/multiplexer.hpp"
#include "caf/net/receive_policy.hpp"
#include "caf/net/stream_oriented_layer_ptr.hpp"
#include "caf/net/stream_socket.hpp"
#include "caf/net/stream_transport.hpp"
#include "caf/sec.hpp"
#include "caf/settings.hpp"
#include "caf/span.hpp"
#include "caf/tag/io_event_oriented.hpp"
#include "caf/tag/stream_oriented.hpp"

CAF_PUSH_WARNINGS
#include <openssl/err.h>
#include <openssl/ssl.h>
CAF_POP_WARNINGS

#if OPENSSL_VERSION_NUMBER < 0x10100000L
#  error "OpenSSL version too old. CAF::net requires at least OpenSSL 1.1"
#endif

#include <string>
#include <string_view>

// -- small wrappers to help working with OpenSSL ------------------------------

namespace caf {

template<>
struct intrusive_ptr_access<SSL_CTX> {
public:
  static void add_ref(SSL_CTX* ptr) noexcept {
    SSL_CTX_up_ref(ptr);
  }

  static void release(SSL_CTX* ptr) noexcept {
    SSL_CTX_free(ptr);
  }
};

template <>
struct intrusive_ptr_access<SSL> {
public:
  static void add_ref(SSL* ptr) noexcept {
    SSL_up_ref(ptr);
  }

  static void release(SSL* ptr) noexcept {
    SSL_free(ptr);
  }
};

} // namespace caf

namespace caf::net::openssl {

/// A smart pointer to an `SSL_CTX` structure.
/// @note technically, SSL structures are reference counted and we could use
///       `intrusive_ptr` instead. However, we have no need for shared ownership
///       semantics here and use `unique_ptr` for simplicity.
using ctx_ptr = intrusive_ptr<SSL_CTX>;

/// A smart pointer to an `SSL` structure.
/// @note technically, SSL structures are reference counted and we could use
///       `intrusive_ptr` instead. However, we have no need for shared ownership
///       semantics here and use `unique_ptr` for simplicity.
using conn_ptr = intrusive_ptr<SSL>;

/// Convenience function for creating an OpenSSL context for given method.
inline ctx_ptr make_ctx(const SSL_METHOD* method) {
  if (auto ptr = SSL_CTX_new(method))
    return ctx_ptr{ptr, false};
  else
    CAF_RAISE_ERROR("SSL_CTX_new failed");
}

/// Fetches a string representation for the last OpenSSL errors.
std::string fetch_error_str() {
  auto cb = [](const char* cstr, size_t len, void* vptr) -> int {
    auto& str = *reinterpret_cast<std::string*>(vptr);
    if (str.empty()) {
      str.assign(cstr, len);
    } else {
      str += "; ";
      auto view = std::string_view{cstr, len};
      str.insert(str.end(), view.begin(), view.end());
    }
    return 1;
  };
  std::string result;
  ERR_print_errors_cb(cb, &result);
  return result;
}

///  Loads the certificate into the SSL context.
inline error certificate_pem_file(const ctx_ptr& ctx, const std::string& path) {
  auto cstr = path.c_str();
  if (SSL_CTX_use_certificate_file(ctx.get(), cstr, SSL_FILETYPE_PEM) > 0) {
    return none;
  } else {
    return make_error(sec::invalid_argument, fetch_error_str());
  }
}

///  Loads the private key into the SSL context.
inline error private_key_pem_file(const ctx_ptr& ctx, const std::string& path) {
  auto cstr = path.c_str();
  if (SSL_CTX_use_PrivateKey_file(ctx.get(), cstr, SSL_FILETYPE_PEM) > 0) {
    return none;
  } else {
    return make_error(sec::invalid_argument, fetch_error_str());
  }
}

/// Convenience function for creating a new SSL structure from given context.
inline conn_ptr make_conn(const ctx_ptr& ctx) {
  if (auto ptr = SSL_new(ctx.get()))
    return conn_ptr{ptr, false};
  else
    CAF_RAISE_ERROR("SSL_new failed");
}

/// Convenience function for creating a new SSL structure from given context and
/// binding the given socket to it.
inline conn_ptr make_conn(const ctx_ptr& ctx, stream_socket fd) {
  auto ptr = make_conn(ctx);
  if (SSL_set_fd(ptr.get(), fd.id))
    return ptr;
  else
    CAF_RAISE_ERROR("SSL_set_fd failed");
}

/// Manages an OpenSSL connection.
class policy {
public:
  // -- constructors, destructors, and assignment operators --------------------

  policy() = delete;

  policy(const policy&) = default;

  policy& operator=(const policy&) = default;

  policy(policy&&) = default;

  policy& operator=(policy&&) = default;

  policy(ctx_ptr ctx, conn_ptr conn)
    : ctx_(std::move(ctx)), conn_(std::move(conn)) {
    // nop
  }

  // -- factories --------------------------------------------------------------

  /// Creates a policy from an SSL method and socket.
  static policy make(const SSL_METHOD* method, stream_socket fd) {
    auto ctx = make_ctx(method);
    auto conn = make_conn(ctx, fd);
    return policy{std::move(ctx), std::move(conn)};
  }

  /// Creates a policy from an SSL context and socket.
  static policy make(ctx_ptr ctx, stream_socket fd) {
    auto conn = make_conn(ctx, fd);
    return policy{std::move(ctx), std::move(conn)};
  }

  // -- properties -------------------------------------------------------------

  SSL_CTX* ctx() {
    return ctx_.get();
  }

  SSL* conn() {
    return conn_.get();
  }

  // -- OpenSSL settings -------------------------------------------------------

  ///  Loads the certificate into the SSL connection object.
  error certificate_pem_file(const std::string& path) {
    auto cstr = path.c_str();
    if (SSL_use_certificate_file(conn(), cstr, SSL_FILETYPE_PEM) > 0) {
      return none;
    } else {
      return make_error(sec::invalid_argument, fetch_error_str());
    }
  }

  ///  Loads the private key into the SSL connection object.
  error private_key_pem_file(const std::string& path) {
    auto cstr = path.c_str();
    if (SSL_use_PrivateKey_file(conn(), cstr, SSL_FILETYPE_PEM) > 0) {
      return none;
    } else {
      return make_error(sec::invalid_argument, fetch_error_str());
    }
  }

  // -- interface functions for the stream transport ---------------------------

  /// Fetches a string representation for the last error.
  std::string fetch_error_str() {
    return openssl::fetch_error_str();
  }

  /// Reads data from the SSL connection into the buffer.
  ptrdiff_t read(stream_socket, span<byte> buf) {
    return SSL_read(conn_.get(), buf.data(), static_cast<int>(buf.size()));
  }

  /// Writes data from the buffer to the SSL connection.
  ptrdiff_t write(stream_socket, span<const byte> buf) {
    return SSL_write(conn_.get(), buf.data(), static_cast<int>(buf.size()));
  }

  /// Performs a TLS/SSL handshake with the server.
  ptrdiff_t connect(stream_socket) {
    return SSL_connect(conn_.get());
  }

  /// Waits for the client to performs a TLS/SSL handshake.
  ptrdiff_t accept(stream_socket) {
    return SSL_accept(conn_.get());
  }

  /// Returns the last SSL error.
  stream_transport_error last_error(stream_socket fd, ptrdiff_t ret) {
    switch (SSL_get_error(conn_.get(), static_cast<int>(ret))) {
      case SSL_ERROR_NONE:
      case SSL_ERROR_WANT_ACCEPT:
      case SSL_ERROR_WANT_CONNECT:
        // For all of these, OpenSSL docs say to do the operation again later.
        return stream_transport_error::temporary;
      case SSL_ERROR_SYSCALL:
        // Need to consult errno, which we just leave to the default policy.
        return default_stream_transport_policy::last_error(fd, ret);
      case SSL_ERROR_WANT_READ:
        return stream_transport_error::want_read;
      case SSL_ERROR_WANT_WRITE:
        return stream_transport_error::want_write;
      default:
        // Errors like SSL_ERROR_WANT_X509_LOOKUP are technically temporary, but
        // we do not configure any callbacks. So seeing this is a red flag.
        return stream_transport_error::permanent;
    }
  }

  /// Graceful shutdown.
  void notify_close() {
    SSL_shutdown(conn_.get());
  }

  /// Returns the number of bytes that are buffered internally and that are
  /// available for immediate read.
  size_t buffered() {
    return static_cast<size_t>(SSL_pending(conn_.get()));
  }

private:
  /// Our SSL context.
  openssl::ctx_ptr ctx_;

  /// Our SSL connection data.
  openssl::conn_ptr conn_;
};

/// Asynchronously starts the TLS/SSL client handshake.
/// @param fd A connected stream socket.
/// @param mpx Pointer to the multiplexer for managing the asynchronous events.
/// @param pol The OpenSSL policy with properly configured SSL/TSL method.
/// @param on_success The callback for creating a @ref socket_manager after
///                   successfully connecting to the server.
/// @param on_error The callback for unexpected errors.
/// @pre `fd != invalid_socket`
/// @pre `mpx != nullptr`
template <class Socket, class OnSuccess, class OnError>
void async_connect(Socket fd, multiplexer* mpx, policy pol,
                   OnSuccess on_success, OnError on_error) {
  using res_t = decltype(on_success(fd, mpx, std::move(pol)));
  using err_t = decltype(on_error(error{}));
  static_assert(std::is_convertible_v<res_t, socket_manager_ptr>,
                "on_success must return a socket_manager_ptr");
  static_assert(std::is_same_v<err_t, void>,
                "on_error may not return anything");
  using factory_t = default_handshake_worker_factory<OnSuccess, OnError>;
  using worker_t = handshake_worker<false, Socket, policy, factory_t>;
  auto factory = factory_t{std::move(on_success), std::move(on_error)};
  auto mgr = make_counted<worker_t>(fd, mpx, std::move(pol),
                                    std::move(factory));
  mpx->init(mgr);
}

/// Asynchronously starts the TLS/SSL server handshake.
/// @param fd A connected stream socket.
/// @param mpx Pointer to the multiplexer for managing the asynchronous events.
/// @param pol The OpenSSL policy with properly configured SSL/TSL method.
/// @param on_success The callback for creating a @ref socket_manager after
///                   successfully connecting to the client.
/// @param on_error The callback for unexpected errors.
/// @pre `fd != invalid_socket`
/// @pre `mpx != nullptr`
template <class Socket, class OnSuccess, class OnError>
void async_accept(Socket fd, multiplexer* mpx, policy pol, OnSuccess on_success,
                  OnError on_error) {
  using res_t = decltype(on_success(fd, mpx, std::move(pol)));
  using err_t = decltype(on_error(error{}));
  static_assert(std::is_convertible_v<res_t, socket_manager_ptr>,
                "on_success must return a socket_manager_ptr");
  static_assert(std::is_same_v<err_t, void>,
                "on_error may not return anything");
  using factory_t = default_handshake_worker_factory<OnSuccess, OnError>;
  using worker_t = handshake_worker<true, Socket, policy, factory_t>;
  auto factory = factory_t{std::move(on_success), std::move(on_error)};
  auto mgr = make_counted<worker_t>(fd, mpx, std::move(pol),
                                    std::move(factory));
  mpx->init(mgr);
}

} // namespace caf::net::openssl

namespace caf::net {

/// Implements a stream_transport that manages a stream socket with encrypted
/// communication over OpenSSL.
template <class UpperLayer>
class openssl_transport
  : public stream_transport_base<openssl::policy, UpperLayer> {
public:
  // -- member types -----------------------------------------------------------

  using super = stream_transport_base<openssl::policy, UpperLayer>;

  // -- constructors, destructors, and assignment operators --------------------

  template <class... Ts>
  openssl_transport(openssl::ctx_ptr ctx, openssl::conn_ptr conn, Ts&&... xs)
    : super(openssl::policy{std::move(ctx), std::move(conn)},
            std::forward<Ts>(xs)...) {
    // nop
  }

  template <class... Ts>
  openssl_transport(openssl::policy policy, Ts&&... xs)
    : super(std::move(policy), std::forward<Ts>(xs)...) {
    // nop
  }
};

} // namespace caf::net
<|endoftext|>


<commit_before>/**
 * Copyright 2012 FAU (Friedrich Alexander University of Erlangen-Nuremberg)
 *
 * 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 "i6engine/rpg/npc/NPCManager.h"

#include "i6engine/rpg/npc/NPC.h"
#include "i6engine/rpg/npc/NPCParser.h"

#include "i6engine/api/EngineController.h"
#include "i6engine/api/facades/ScriptingFacade.h"

namespace i6engine {
namespace rpg {
namespace npc {

	NPCManager::NPCManager() : _npcs(), _running(true), _jobID() {
		_jobID = api::EngineController::GetSingleton().registerTimer(10000, boost::bind(&NPCManager::checkNPCs, this), true, core::JobPriorities::Prio_Medium);
	}

	NPCManager::~NPCManager() {
		_running = false;
		api::EngineController::GetSingleton().removeTimerID(_jobID);
	}

	void NPCManager::createNPC(const std::string & identifier, const Vec3 & pos, const Quaternion & rot, bool player) {
		api::objects::GOTemplate tpl = NPCParser::GetSingletonPtr()->getTemplate(identifier);
		api::attributeMap paramsPSC;
		pos.insertInMap("pos", paramsPSC);
		rot.insertInMap("rot", paramsPSC);
		tpl._components.push_back(i6engine::api::objects::GOTemplateComponent("PhysicalState", paramsPSC, "", false, false));

		if (!player) {
			api::attributeMap paramsDC;
			paramsDC.insert(std::make_pair("ident", identifier));
			tpl._components.push_back(api::objects::GOTemplateComponent("DialogChecker", paramsDC, "", false, false));
		} else {
			api::attributeMap paramsTPC;
			paramsTPC.insert(std::make_pair("ident", identifier));
			tpl._components.push_back(api::objects::GOTemplateComponent("ThirdPersonControl", paramsTPC, "", false, false));
		}

		NPC * n = new NPC(tpl, player);
		_npcs.insert(std::make_pair(identifier, n));
	}

	NPC * NPCManager::getNPC(const std::string & identifier) const {
		auto it = _npcs.find(identifier);
		if (it != _npcs.end()) {
			return it->second;
		}
		return nullptr;
	}

	bool NPCManager::checkNPCs() {
		for (auto & p : _npcs) {
			p.second->workQueue();
		}
		return _running;
	}

} /* namespace npc */
} /* namespace rpg */
} /* namespace i6engine */
<commit_msg>ISIXE-909 fixed compile error with boost bind<commit_after>/**
 * Copyright 2012 FAU (Friedrich Alexander University of Erlangen-Nuremberg)
 *
 * 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 "i6engine/rpg/npc/NPCManager.h"

#include "i6engine/rpg/npc/NPC.h"
#include "i6engine/rpg/npc/NPCParser.h"

#include "i6engine/api/EngineController.h"
#include "i6engine/api/facades/ScriptingFacade.h"

#include "boost/bind.hpp"

namespace i6engine {
namespace rpg {
namespace npc {

	NPCManager::NPCManager() : _npcs(), _running(true), _jobID() {
		_jobID = api::EngineController::GetSingleton().registerTimer(10000, boost::bind(&NPCManager::checkNPCs, this), true, core::JobPriorities::Prio_Medium);
	}

	NPCManager::~NPCManager() {
		_running = false;
		api::EngineController::GetSingleton().removeTimerID(_jobID);
	}

	void NPCManager::createNPC(const std::string & identifier, const Vec3 & pos, const Quaternion & rot, bool player) {
		api::objects::GOTemplate tpl = NPCParser::GetSingletonPtr()->getTemplate(identifier);
		api::attributeMap paramsPSC;
		pos.insertInMap("pos", paramsPSC);
		rot.insertInMap("rot", paramsPSC);
		tpl._components.push_back(i6engine::api::objects::GOTemplateComponent("PhysicalState", paramsPSC, "", false, false));

		if (!player) {
			api::attributeMap paramsDC;
			paramsDC.insert(std::make_pair("ident", identifier));
			tpl._components.push_back(api::objects::GOTemplateComponent("DialogChecker", paramsDC, "", false, false));
		} else {
			api::attributeMap paramsTPC;
			paramsTPC.insert(std::make_pair("ident", identifier));
			tpl._components.push_back(api::objects::GOTemplateComponent("ThirdPersonControl", paramsTPC, "", false, false));
		}

		NPC * n = new NPC(tpl, player);
		_npcs.insert(std::make_pair(identifier, n));
	}

	NPC * NPCManager::getNPC(const std::string & identifier) const {
		auto it = _npcs.find(identifier);
		if (it != _npcs.end()) {
			return it->second;
		}
		return nullptr;
	}

	bool NPCManager::checkNPCs() {
		for (auto & p : _npcs) {
			p.second->workQueue();
		}
		return _running;
	}

} /* namespace npc */
} /* namespace rpg */
} /* namespace i6engine */
<|endoftext|>


<commit_before>// Qt includes
#include <QDateTime>

#include "LinearColorSmoothing.h"

LinearColorSmoothing::LinearColorSmoothing(
		LedDevice * ledDevice,
		double ledUpdateFrequency_hz,
		int settlingTime_ms,
		unsigned updateDelay) :
	QObject(),
	LedDevice(),
	_ledDevice(ledDevice),
	_updateInterval(1000 / ledUpdateFrequency_hz),
	_settlingTime(settlingTime_ms),
	_timer(),
	_outputDelay(updateDelay)
{
	_timer.setSingleShot(false);
	_timer.setInterval(_updateInterval);

	connect(&_timer, SIGNAL(timeout()), this, SLOT(updateLeds()));
}

LinearColorSmoothing::~LinearColorSmoothing()
{
	delete _ledDevice;
}

int LinearColorSmoothing::write(const std::vector<ColorRgb> &ledValues)
{
	// received a new target color
	if (_previousValues.empty())
	{
		// not initialized yet
		_targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime;
		_targetValues = ledValues;

		_previousTime = QDateTime::currentMSecsSinceEpoch();
		_previousValues = ledValues;
		_timer.start();
	}
	else
	{
		_targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime;
		memcpy(_targetValues.data(), ledValues.data(), ledValues.size() * sizeof(ColorRgb));
	}

	return 0;
}

int LinearColorSmoothing::switchOff()
{
	// We will keep updating the leds (but with pure-black)

	// Clear the smoothing parameters
	std::fill(_targetValues.begin(), _targetValues.end(), ColorRgb::BLACK);
	_targetTime = 0;

	// Erase the output-queue
	for (unsigned i=0; i<_outputQueue.size(); ++i)
	{
		_outputQueue.push_back(_targetValues);
		_outputQueue.pop_front();
	}

	return 0;
}

void LinearColorSmoothing::updateLeds()
{
	int64_t now = QDateTime::currentMSecsSinceEpoch();
	int deltaTime = _targetTime - now;

	if (deltaTime < 0)
	{
		memcpy(_previousValues.data(), _targetValues.data(), _targetValues.size() * sizeof(ColorRgb));
		_previousTime = now;

		queueColors(_previousValues);
	}
	else
	{
		float k = 1.0f - 1.0f * deltaTime / (_targetTime - _previousTime);

		for (size_t i = 0; i < _previousValues.size(); ++i)
		{
			ColorRgb & prev = _previousValues[i];
			ColorRgb & target = _targetValues[i];

			prev.red   += k * (target.red   - prev.red);
			prev.green += k * (target.green - prev.green);
			prev.blue  += k * (target.blue  - prev.blue);
		}
		_previousTime = now;

		queueColors(_previousValues);
	}
}

void LinearColorSmoothing::queueColors(const std::vector<ColorRgb> & ledColors)
{
	if (_outputDelay == 0)
	{
		// No output delay => immediate write
		_ledDevice->write(ledColors);
	}
	else
	{
		// Push the new colors in the delay-buffer
		_outputQueue.push_back(ledColors);
		// If the delay-buffer is filled pop the front and write to device
		if (_outputQueue.size() > _outputDelay)
		{
			_ledDevice->write(_outputQueue.front());
			_outputQueue.pop_front();
		}
	}
}
<commit_msg>Added debug statement to standard out<commit_after>// Qt includes
#include <QDateTime>

#include "LinearColorSmoothing.h"

LinearColorSmoothing::LinearColorSmoothing(
		LedDevice * ledDevice,
		double ledUpdateFrequency_hz,
		int settlingTime_ms,
		unsigned updateDelay) :
	QObject(),
	LedDevice(),
	_ledDevice(ledDevice),
	_updateInterval(1000 / ledUpdateFrequency_hz),
	_settlingTime(settlingTime_ms),
	_timer(),
	_outputDelay(updateDelay)
{
	_timer.setSingleShot(false);
	_timer.setInterval(_updateInterval);

	connect(&_timer, SIGNAL(timeout()), this, SLOT(updateLeds()));

	std::cout << "Created linear-smoothing(interval_ms=" << _updateInterval << ";settlingTime_ms=" << settlingTime_ms << ";updateDelay=" << _outputDelay << std::endl;
}

LinearColorSmoothing::~LinearColorSmoothing()
{
	delete _ledDevice;
}

int LinearColorSmoothing::write(const std::vector<ColorRgb> &ledValues)
{
	// received a new target color
	if (_previousValues.empty())
	{
		// not initialized yet
		_targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime;
		_targetValues = ledValues;

		_previousTime = QDateTime::currentMSecsSinceEpoch();
		_previousValues = ledValues;
		_timer.start();
	}
	else
	{
		_targetTime = QDateTime::currentMSecsSinceEpoch() + _settlingTime;
		memcpy(_targetValues.data(), ledValues.data(), ledValues.size() * sizeof(ColorRgb));
	}

	return 0;
}

int LinearColorSmoothing::switchOff()
{
	// We will keep updating the leds (but with pure-black)

	// Clear the smoothing parameters
	std::fill(_targetValues.begin(), _targetValues.end(), ColorRgb::BLACK);
	_targetTime = 0;

	// Erase the output-queue
	for (unsigned i=0; i<_outputQueue.size(); ++i)
	{
		_outputQueue.push_back(_targetValues);
		_outputQueue.pop_front();
	}

	return 0;
}

void LinearColorSmoothing::updateLeds()
{
	int64_t now = QDateTime::currentMSecsSinceEpoch();
	int deltaTime = _targetTime - now;

	if (deltaTime < 0)
	{
		memcpy(_previousValues.data(), _targetValues.data(), _targetValues.size() * sizeof(ColorRgb));
		_previousTime = now;

		queueColors(_previousValues);
	}
	else
	{
		float k = 1.0f - 1.0f * deltaTime / (_targetTime - _previousTime);

		for (size_t i = 0; i < _previousValues.size(); ++i)
		{
			ColorRgb & prev = _previousValues[i];
			ColorRgb & target = _targetValues[i];

			prev.red   += k * (target.red   - prev.red);
			prev.green += k * (target.green - prev.green);
			prev.blue  += k * (target.blue  - prev.blue);
		}
		_previousTime = now;

		queueColors(_previousValues);
	}
}

void LinearColorSmoothing::queueColors(const std::vector<ColorRgb> & ledColors)
{
	if (_outputDelay == 0)
	{
		// No output delay => immediate write
		_ledDevice->write(ledColors);
	}
	else
	{
		// Push the new colors in the delay-buffer
		_outputQueue.push_back(ledColors);
		// If the delay-buffer is filled pop the front and write to device
		if (_outputQueue.size() > _outputDelay)
		{
			_ledDevice->write(_outputQueue.front());
			_outputQueue.pop_front();
		}
	}
}
<|endoftext|>