<commit_before>// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#ifndef MFEM_NONLINEARFORM
#define MFEM_NONLINEARFORM

#include "../config/config.hpp"
#include "nonlininteg.hpp"
#include "nonlinearform_ext.hpp"
#include "bilinearform.hpp"
#include "gridfunc.hpp"

namespace mfem
{

class NonlinearForm : public Operator
{
protected:
   /// The assembly level.
   AssemblyLevel assembly;

   /** Extension for supporting Partial Assembly (PA) or
       Matrix Free assembly (MF). */
   NonlinearFormExtension *ext;

   /// FE space on which the form lives.
   FiniteElementSpace *fes; // not owned

   /// Set of Domain Integrators to be assembled (added).
   Array<NonlinearFormIntegrator*> dnfi; // owned

   /// Set of interior face Integrators to be assembled (added).
   Array<NonlinearFormIntegrator*> fnfi; // owned

   /// Set of boundary face Integrators to be assembled (added).
   Array<NonlinearFormIntegrator*> bfnfi; // owned
   Array<Array<int>*>              bfnfi_marker; // not owned

   mutable SparseMatrix *Grad, *cGrad; // owned

   /// A list of all essential true dofs
   Array<int> ess_tdof_list;

   /// Counter for updates propagated from the FiniteElementSpace.
   long sequence;

   /// Auxiliary Vector%s
   mutable Vector aux1, aux2;

   /// Pointer to the prolongation matrix of fes, may be NULL.
   const Operator *P; // not owned
   /// The result of dynamic-casting P to SparseMatrix pointer.
   const SparseMatrix *cP; // not owned

   bool Serial() const { return (!P || cP); }
   const Vector &Prolongate(const Vector &x) const;

public:
   /// Construct a NonlinearForm on the given FiniteElementSpace, @a f.
   /** As an Operator, the NonlinearForm has input and output size equal to the
       number of true degrees of freedom, i.e. f->GetTrueVSize(). */
   NonlinearForm(FiniteElementSpace *f)
      : Operator(f->GetTrueVSize()), assembly(AssemblyLevel::NONE),
        ext(NULL), fes(f), Grad(NULL), cGrad(NULL),
        sequence(f->GetSequence()), P(f->GetProlongationMatrix()),
        cP(dynamic_cast<const SparseMatrix*>(P))
   { }

   /// Set the desired assembly level. The default is AssemblyLevel::NONE.
   /** This method must be called before assembly. */
   void SetAssemblyLevel(AssemblyLevel assembly_level);

   FiniteElementSpace *FESpace() { return fes; }
   const FiniteElementSpace *FESpace() const { return fes; }

   /// Adds new Domain Integrator.
   void AddDomainIntegrator(NonlinearFormIntegrator *nlfi)
   { dnfi.Append(nlfi); }

   /// Access all integrators added with AddDomainIntegrator().
   Array<NonlinearFormIntegrator*> *GetDNFI() { return &dnfi; }
   const Array<NonlinearFormIntegrator*> *GetDNFI() const { return &dnfi; }

   /// Adds new Interior Face Integrator.
   void AddInteriorFaceIntegrator(NonlinearFormIntegrator *nlfi)
   { fnfi.Append(nlfi); }

   /// Adds new Boundary Face Integrator.
   void AddBdrFaceIntegrator(NonlinearFormIntegrator *nlfi)
   { bfnfi.Append(nlfi); bfnfi_marker.Append(NULL); }

   /** @brief Adds new Boundary Face Integrator, restricted to specific boundary
       attributes. */
   void AddBdrFaceIntegrator(NonlinearFormIntegrator *nfi,
                             Array<int> &bdr_marker)
   { bfnfi.Append(nfi); bfnfi_marker.Append(&bdr_marker); }

   /// Specify essential boundary conditions.
   /** This method calls FiniteElementSpace::GetEssentialTrueDofs() and stores
       the result internally for use by other methods. If the @a rhs pointer is
       not NULL, its essential true dofs will be set to zero. This makes it
       "compatible" with the output vectors from the Mult() method which also
       have zero entries at the essential true dofs. */
   void SetEssentialBC(const Array<int> &bdr_attr_is_ess, Vector *rhs = NULL);

   /// (DEPRECATED) Specify essential boundary conditions.
   /** @deprecated Use either SetEssentialBC() or SetEssentialTrueDofs(). */
   MFEM_DEPRECATED void SetEssentialVDofs(const Array<int> &ess_vdofs_list);

   /// Specify essential boundary conditions.
   void SetEssentialTrueDofs(const Array<int> &ess_tdof_list)
   { ess_tdof_list.Copy(this->ess_tdof_list); }

   /// Return a (read-only) list of all essential true dofs.
   const Array<int> &GetEssentialTrueDofs() const { return ess_tdof_list; }

   /// Compute the enery corresponding to the state @a x.
   /** In general, @a x may have non-homogeneous essential boundary values.

       The state @a x must be a "GridFunction size" vector, i.e. its size must
       be fes->GetVSize(). */
   double GetGridFunctionEnergy(const Vector &x) const;

   /// Compute the enery corresponding to the state @a x.
   /** In general, @a x may have non-homogeneous essential boundary values.

       The state @a x must be a true-dof vector. */
   virtual double GetEnergy(const Vector &x) const
   { return GetGridFunctionEnergy(Prolongate(x)); }

   /// Evaluate the action of the NonlinearForm.
   /** The input essential dofs in @a x will, generally, be non-zero. However,
       the output essential dofs in @a y will always be set to zero.

       Both the input and the output vectors, @a x and @a y, must be true-dof
       vectors, i.e. their size must be fes->GetTrueVSize(). */
   virtual void Mult(const Vector &x, Vector &y) const;

   /** @brief Compute the gradient Operator of the NonlinearForm corresponding
       to the state @a x. */
   /** Any previously specified essential boundary conditions will be
       automatically imposed on the gradient operator.

       The returned object is valid until the next call to this method or the
       destruction of this object.

       In general, @a x may have non-homogeneous essential boundary values.

       The state @a x must be a true-dof vector. */
   virtual Operator &GetGradient(const Vector &x) const;

   /// Update the NonlinearForm to propagate updates of the associated FE space.
   /** After calling this method, the essential boundary conditions need to be
       set again. */
   virtual void Update();

   /// Setup the NonlinearForm
   virtual void Setup();

   /// Get the finite element space prolongation matrix
   virtual const Operator *GetProlongation() const { return P; }
   /// Get the finite element space restriction matrix
   virtual const Operator *GetRestriction() const
   { return fes->GetRestrictionMatrix(); }

   /** @brief Destroy the NoninearForm including the owned
       NonlinearFormIntegrator%s and gradient Operator. */
   virtual ~NonlinearForm();
};

/** @brief A class representing a general block nonlinear operator defined on
    the Cartesian product of multiple FiniteElementSpace%s. */
class BlockNonlinearForm : public Operator
{
protected:
   /// FE spaces on which the form lives.
   Array<FiniteElementSpace*> fes;

   /// Set of Domain Integrators to be assembled (added).
   Array<BlockNonlinearFormIntegrator*> dnfi;

   /// Set of interior face Integrators to be assembled (added).
   Array<BlockNonlinearFormIntegrator*> fnfi;

   /// Set of Boundary Face Integrators to be assembled (added).
   Array<BlockNonlinearFormIntegrator*> bfnfi;
   Array<Array<int>*>           bfnfi_marker;

   /** Auxiliary block-vectors for wrapping input and output vectors or holding
       GridFunction-like block-vector data (e.g. in parallel). */
   mutable BlockVector xs, ys;

   mutable Array2D<SparseMatrix*> Grads;
   mutable BlockOperator *BlockGrad;

   // A list of the offsets
   Array<int> block_offsets;
   Array<int> block_trueOffsets;

   // Essential vdofs: one list of vdofs for each space in 'fes'
   Array<Array<int> *> ess_vdofs;

   /// Specialized version of GetEnergy() for BlockVectors
   double GetEnergyBlocked(const BlockVector &bx) const;

   /// Specialized version of Mult() for BlockVector%s
   void MultBlocked(const BlockVector &bx, BlockVector &by) const;

   /// Specialized version of GetGradient() for BlockVector
   Operator &GetGradientBlocked(const BlockVector &bx) const;

public:
   /// Construct an empty BlockNonlinearForm. Initialize with SetSpaces().
   BlockNonlinearForm();

   /// Construct a BlockNonlinearForm on the given set of FiniteElementSpace%s.
   BlockNonlinearForm(Array<FiniteElementSpace *> &f);

   /// Return the @a k-th FE space of the BlockNonlinearForm.
   FiniteElementSpace *FESpace(int k) { return fes[k]; }
   /// Return the @a k-th FE space of the BlockNonlinearForm (const version).
   const FiniteElementSpace *FESpace(int k) const { return fes[k]; }

   /// (Re)initialize the BlockNonlinearForm.
   /** After a call to SetSpaces(), the essential b.c. must be set again. */
   void SetSpaces(Array<FiniteElementSpace *> &f);

   /// Return the regular dof offsets.
   const Array<int> &GetBlockOffsets() const { return block_offsets; }
   /// Return the true-dof offsets.
   const Array<int> &GetBlockTrueOffsets() const { return block_trueOffsets; }

   /// Adds new Domain Integrator.
   void AddDomainIntegrator(BlockNonlinearFormIntegrator *nlfi)
   { dnfi.Append(nlfi); }

   /// Adds new Interior Face Integrator.
   void AddInteriorFaceIntegrator(BlockNonlinearFormIntegrator *nlfi)
   { fnfi.Append(nlfi); }

   /// Adds new Boundary Face Integrator.
   void AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nlfi)
   { bfnfi.Append(nlfi); bfnfi_marker.Append(NULL); }

   /** @brief Adds new Boundary Face Integrator, restricted to specific boundary
       attributes. */
   void AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nlfi,
                             Array<int> &bdr_marker);

   virtual void SetEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
                               Array<Vector *> &rhs);

   virtual double GetEnergy(const Vector &x) const;

   virtual void Mult(const Vector &x, Vector &y) const;

   virtual Operator &GetGradient(const Vector &x) const;

   /// Destructor.
   virtual ~BlockNonlinearForm();
};

}

#endif
<commit_msg>Removing `deprecated` flag from `NonlinearForm::SetEssentialVdofs` but left a gentler suggestion to use another method if possible.<commit_after>// Copyright (c) 2010-2020, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.

#ifndef MFEM_NONLINEARFORM
#define MFEM_NONLINEARFORM

#include "../config/config.hpp"
#include "nonlininteg.hpp"
#include "nonlinearform_ext.hpp"
#include "bilinearform.hpp"
#include "gridfunc.hpp"

namespace mfem
{

class NonlinearForm : public Operator
{
protected:
   /// The assembly level.
   AssemblyLevel assembly;

   /** Extension for supporting Partial Assembly (PA) or
       Matrix Free assembly (MF). */
   NonlinearFormExtension *ext;

   /// FE space on which the form lives.
   FiniteElementSpace *fes; // not owned

   /// Set of Domain Integrators to be assembled (added).
   Array<NonlinearFormIntegrator*> dnfi; // owned

   /// Set of interior face Integrators to be assembled (added).
   Array<NonlinearFormIntegrator*> fnfi; // owned

   /// Set of boundary face Integrators to be assembled (added).
   Array<NonlinearFormIntegrator*> bfnfi; // owned
   Array<Array<int>*>              bfnfi_marker; // not owned

   mutable SparseMatrix *Grad, *cGrad; // owned

   /// A list of all essential true dofs
   Array<int> ess_tdof_list;

   /// Counter for updates propagated from the FiniteElementSpace.
   long sequence;

   /// Auxiliary Vector%s
   mutable Vector aux1, aux2;

   /// Pointer to the prolongation matrix of fes, may be NULL.
   const Operator *P; // not owned
   /// The result of dynamic-casting P to SparseMatrix pointer.
   const SparseMatrix *cP; // not owned

   bool Serial() const { return (!P || cP); }
   const Vector &Prolongate(const Vector &x) const;

public:
   /// Construct a NonlinearForm on the given FiniteElementSpace, @a f.
   /** As an Operator, the NonlinearForm has input and output size equal to the
       number of true degrees of freedom, i.e. f->GetTrueVSize(). */
   NonlinearForm(FiniteElementSpace *f)
      : Operator(f->GetTrueVSize()), assembly(AssemblyLevel::NONE),
        ext(NULL), fes(f), Grad(NULL), cGrad(NULL),
        sequence(f->GetSequence()), P(f->GetProlongationMatrix()),
        cP(dynamic_cast<const SparseMatrix*>(P))
   { }

   /// Set the desired assembly level. The default is AssemblyLevel::NONE.
   /** This method must be called before assembly. */
   void SetAssemblyLevel(AssemblyLevel assembly_level);

   FiniteElementSpace *FESpace() { return fes; }
   const FiniteElementSpace *FESpace() const { return fes; }

   /// Adds new Domain Integrator.
   void AddDomainIntegrator(NonlinearFormIntegrator *nlfi)
   { dnfi.Append(nlfi); }

   /// Access all integrators added with AddDomainIntegrator().
   Array<NonlinearFormIntegrator*> *GetDNFI() { return &dnfi; }
   const Array<NonlinearFormIntegrator*> *GetDNFI() const { return &dnfi; }

   /// Adds new Interior Face Integrator.
   void AddInteriorFaceIntegrator(NonlinearFormIntegrator *nlfi)
   { fnfi.Append(nlfi); }

   /// Adds new Boundary Face Integrator.
   void AddBdrFaceIntegrator(NonlinearFormIntegrator *nlfi)
   { bfnfi.Append(nlfi); bfnfi_marker.Append(NULL); }

   /** @brief Adds new Boundary Face Integrator, restricted to specific boundary
       attributes. */
   void AddBdrFaceIntegrator(NonlinearFormIntegrator *nfi,
                             Array<int> &bdr_marker)
   { bfnfi.Append(nfi); bfnfi_marker.Append(&bdr_marker); }

   /// Specify essential boundary conditions.
   /** This method calls FiniteElementSpace::GetEssentialTrueDofs() and stores
       the result internally for use by other methods. If the @a rhs pointer is
       not NULL, its essential true dofs will be set to zero. This makes it
       "compatible" with the output vectors from the Mult() method which also
       have zero entries at the essential true dofs. */
   void SetEssentialBC(const Array<int> &bdr_attr_is_ess, Vector *rhs = NULL);

   /// Specify essential boundary conditions.
   /** Use either SetEssentialBC() or SetEssentialTrueDofs() if possible. */
   void SetEssentialVDofs(const Array<int> &ess_vdofs_list);

   /// Specify essential boundary conditions.
   void SetEssentialTrueDofs(const Array<int> &ess_tdof_list)
   { ess_tdof_list.Copy(this->ess_tdof_list); }

   /// Return a (read-only) list of all essential true dofs.
   const Array<int> &GetEssentialTrueDofs() const { return ess_tdof_list; }

   /// Compute the enery corresponding to the state @a x.
   /** In general, @a x may have non-homogeneous essential boundary values.

       The state @a x must be a "GridFunction size" vector, i.e. its size must
       be fes->GetVSize(). */
   double GetGridFunctionEnergy(const Vector &x) const;

   /// Compute the enery corresponding to the state @a x.
   /** In general, @a x may have non-homogeneous essential boundary values.

       The state @a x must be a true-dof vector. */
   virtual double GetEnergy(const Vector &x) const
   { return GetGridFunctionEnergy(Prolongate(x)); }

   /// Evaluate the action of the NonlinearForm.
   /** The input essential dofs in @a x will, generally, be non-zero. However,
       the output essential dofs in @a y will always be set to zero.

       Both the input and the output vectors, @a x and @a y, must be true-dof
       vectors, i.e. their size must be fes->GetTrueVSize(). */
   virtual void Mult(const Vector &x, Vector &y) const;

   /** @brief Compute the gradient Operator of the NonlinearForm corresponding
       to the state @a x. */
   /** Any previously specified essential boundary conditions will be
       automatically imposed on the gradient operator.

       The returned object is valid until the next call to this method or the
       destruction of this object.

       In general, @a x may have non-homogeneous essential boundary values.

       The state @a x must be a true-dof vector. */
   virtual Operator &GetGradient(const Vector &x) const;

   /// Update the NonlinearForm to propagate updates of the associated FE space.
   /** After calling this method, the essential boundary conditions need to be
       set again. */
   virtual void Update();

   /// Setup the NonlinearForm
   virtual void Setup();

   /// Get the finite element space prolongation matrix
   virtual const Operator *GetProlongation() const { return P; }
   /// Get the finite element space restriction matrix
   virtual const Operator *GetRestriction() const
   { return fes->GetRestrictionMatrix(); }

   /** @brief Destroy the NoninearForm including the owned
       NonlinearFormIntegrator%s and gradient Operator. */
   virtual ~NonlinearForm();
};

/** @brief A class representing a general block nonlinear operator defined on
    the Cartesian product of multiple FiniteElementSpace%s. */
class BlockNonlinearForm : public Operator
{
protected:
   /// FE spaces on which the form lives.
   Array<FiniteElementSpace*> fes;

   /// Set of Domain Integrators to be assembled (added).
   Array<BlockNonlinearFormIntegrator*> dnfi;

   /// Set of interior face Integrators to be assembled (added).
   Array<BlockNonlinearFormIntegrator*> fnfi;

   /// Set of Boundary Face Integrators to be assembled (added).
   Array<BlockNonlinearFormIntegrator*> bfnfi;
   Array<Array<int>*>           bfnfi_marker;

   /** Auxiliary block-vectors for wrapping input and output vectors or holding
       GridFunction-like block-vector data (e.g. in parallel). */
   mutable BlockVector xs, ys;

   mutable Array2D<SparseMatrix*> Grads;
   mutable BlockOperator *BlockGrad;

   // A list of the offsets
   Array<int> block_offsets;
   Array<int> block_trueOffsets;

   // Essential vdofs: one list of vdofs for each space in 'fes'
   Array<Array<int> *> ess_vdofs;

   /// Specialized version of GetEnergy() for BlockVectors
   double GetEnergyBlocked(const BlockVector &bx) const;

   /// Specialized version of Mult() for BlockVector%s
   void MultBlocked(const BlockVector &bx, BlockVector &by) const;

   /// Specialized version of GetGradient() for BlockVector
   Operator &GetGradientBlocked(const BlockVector &bx) const;

public:
   /// Construct an empty BlockNonlinearForm. Initialize with SetSpaces().
   BlockNonlinearForm();

   /// Construct a BlockNonlinearForm on the given set of FiniteElementSpace%s.
   BlockNonlinearForm(Array<FiniteElementSpace *> &f);

   /// Return the @a k-th FE space of the BlockNonlinearForm.
   FiniteElementSpace *FESpace(int k) { return fes[k]; }
   /// Return the @a k-th FE space of the BlockNonlinearForm (const version).
   const FiniteElementSpace *FESpace(int k) const { return fes[k]; }

   /// (Re)initialize the BlockNonlinearForm.
   /** After a call to SetSpaces(), the essential b.c. must be set again. */
   void SetSpaces(Array<FiniteElementSpace *> &f);

   /// Return the regular dof offsets.
   const Array<int> &GetBlockOffsets() const { return block_offsets; }
   /// Return the true-dof offsets.
   const Array<int> &GetBlockTrueOffsets() const { return block_trueOffsets; }

   /// Adds new Domain Integrator.
   void AddDomainIntegrator(BlockNonlinearFormIntegrator *nlfi)
   { dnfi.Append(nlfi); }

   /// Adds new Interior Face Integrator.
   void AddInteriorFaceIntegrator(BlockNonlinearFormIntegrator *nlfi)
   { fnfi.Append(nlfi); }

   /// Adds new Boundary Face Integrator.
   void AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nlfi)
   { bfnfi.Append(nlfi); bfnfi_marker.Append(NULL); }

   /** @brief Adds new Boundary Face Integrator, restricted to specific boundary
       attributes. */
   void AddBdrFaceIntegrator(BlockNonlinearFormIntegrator *nlfi,
                             Array<int> &bdr_marker);

   virtual void SetEssentialBC(const Array<Array<int> *>&bdr_attr_is_ess,
                               Array<Vector *> &rhs);

   virtual double GetEnergy(const Vector &x) const;

   virtual void Mult(const Vector &x, Vector &y) const;

   virtual Operator &GetGradient(const Vector &x) const;

   /// Destructor.
   virtual ~BlockNonlinearForm();
};

}

#endif
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

// Based on http://beej.us/guide/bgnet/examples/pack2.c

#pragma once

#include <cmath>
#include <cstdint>

namespace caf::detail {

template <class T>
struct ieee_754_trait;

template <>
struct ieee_754_trait<float> {
  static constexpr uint32_t bits = 32;   // number of bits
  static constexpr uint32_t expbits = 8; // bits used for exponent
  static constexpr float zero = 0.0f;    // the value 0
  static constexpr float p5 = 0.5f;      // the value 0.5
  using packed_type = uint32_t;          // unsigned integer type
  using signed_packed_type = int32_t;    // signed integer type
  using float_type = float;              // floating point type
};

template <>
struct ieee_754_trait<uint32_t> : ieee_754_trait<float> {};

template <>
struct ieee_754_trait<double> {
  static constexpr uint64_t bits = 64;
  static constexpr uint64_t expbits = 11;
  static constexpr double zero = 0.0;
  static constexpr double p5 = 0.5;
  using packed_type = uint64_t;
  using signed_packed_type = int64_t;
  using float_type = double;
};

template <>
struct ieee_754_trait<uint64_t> : ieee_754_trait<double> {};

template <class T>
typename ieee_754_trait<T>::packed_type pack754(T f) {
  using trait = ieee_754_trait<T>;
  using result_type = typename trait::packed_type;
  // filter special type
  if (std::fabs(f) <= trait::zero) {
    return 0; // only true if f equals +0 or -0
  }
  auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
  // check sign and begin normalization
  result_type sign;
  T fnorm;
  if (f < 0) {
    sign = 1;
    fnorm = -f;
  } else {
    sign = 0;
    fnorm = f;
  }
  // get the normalized form of f and track the exponent
  typename ieee_754_trait<T>::packed_type shift = 0;
  while (fnorm >= static_cast<T>(2)) {
    fnorm /= static_cast<T>(2);
    ++shift;
  }
  while (fnorm < static_cast<T>(1)) {
    fnorm *= static_cast<T>(2);
    --shift;
  }
  fnorm = fnorm - static_cast<T>(1);
  // calculate 2^significandbits
  auto pownum = static_cast<T>(result_type{1} << significandbits);
  // calculate the binary form (non-float) of the significand data
  auto significand = static_cast<result_type>(fnorm * (pownum + trait::p5));
  // get the biased exponent
  auto exp = shift + ((1 << (trait::expbits - 1)) - 1); // shift + bias
  // return the final answer
  return (sign << (trait::bits - 1))
         | (exp << (trait::bits - trait::expbits - 1)) | significand;
}

template <class T>
typename ieee_754_trait<T>::float_type unpack754(T i) {
  using trait = ieee_754_trait<T>;
  using signed_type = typename trait::signed_packed_type;
  using result_type = typename trait::float_type;
  if (i == 0)
    return trait::zero;
  auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
  // pull the significand: mask, convert back to float + add the one back on
  auto result = static_cast<result_type>(i & ((T{1} << significandbits) - 1));
  result /= static_cast<result_type>(T{1} << significandbits);
  result += static_cast<result_type>(1);
  // deal with the exponent
  auto si = static_cast<signed_type>(i);
  auto bias = (1 << (trait::expbits - 1)) - 1;
  auto pownum = static_cast<signed_type>(1) << trait::expbits;
  auto shift
    = static_cast<signed_type>(((si >> significandbits) & (pownum - 1)) - bias);
  while (shift > 0) {
    result *= static_cast<result_type>(2);
    --shift;
  }
  while (shift < 0) {
    result /= static_cast<result_type>(2);
    ++shift;
  }
  // sign it
  result *= ((i >> (trait::bits - 1)) & 1) ? -1 : 1;
  return result;
}

} // namespace caf::detail
<commit_msg>Fix packing/unpacking of NaN and infinite values<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

// Based on http://beej.us/guide/bgnet/examples/pack2.c

#pragma once

#include <cmath>
#include <cstdint>
#include <limits>

namespace caf::detail {

template <class T>
struct ieee_754_trait;

template <>
struct ieee_754_trait<float> {
  static constexpr uint32_t bits = 32;                 // number of bits
  static constexpr uint32_t expbits = 8;               // bits used for exponent
  static constexpr float zero = 0.0f;                  // the value 0
  static constexpr float p5 = 0.5f;                    // the value 0.5
  static constexpr uint32_t packed_pzero = 0x00000000; // positive zero
  static constexpr uint32_t packed_nzero = 0x80000000; // negative zero
  static constexpr uint32_t packed_nan = 0xFFFFFFFF;   // not-a-number
  static constexpr uint32_t packed_pinf = 0xFF800000;  // positive infinity
  static constexpr uint32_t packed_ninf = 0x7F800000;  // negative infinity
  using packed_type = uint32_t;                        // unsigned integer type
  using signed_packed_type = int32_t;                  // signed integer type
  using float_type = float;                            // floating point type
};

template <>
struct ieee_754_trait<uint32_t> : ieee_754_trait<float> {};

template <>
struct ieee_754_trait<double> {
  static constexpr uint64_t bits = 64;
  static constexpr uint64_t expbits = 11;
  static constexpr double zero = 0.0;
  static constexpr double p5 = 0.5;
  static constexpr uint64_t packed_pzero = 0x0000000000000000ull;
  static constexpr uint64_t packed_nzero = 0x8000000000000000ull;
  static constexpr uint64_t packed_nan = 0xFFFFFFFFFFFFFFFFull;
  static constexpr uint64_t packed_pinf = 0xFFF0000000000000ull;
  static constexpr uint64_t packed_ninf = 0x7FF0000000000000ull;
  using packed_type = uint64_t;
  using signed_packed_type = int64_t;
  using float_type = double;
};

template <>
struct ieee_754_trait<uint64_t> : ieee_754_trait<double> {};

template <class T>
typename ieee_754_trait<T>::packed_type pack754(T f) {
  using trait = ieee_754_trait<T>;
  using result_type = typename trait::packed_type;
  // filter special cases
  if (std::isnan(f)) {
    return trait::packed_nan;
  }
  if (std::isinf(f)) {
    return std::signbit(f) ? trait::packed_ninf : trait::packed_pinf;
  }
  if (std::fabs(f) <= trait::zero) { // only true if f equals +0 or -0
    return std::signbit(f) ? trait::packed_nzero : trait::packed_pzero;
  }
  auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
  // check sign and begin normalization
  result_type sign;
  T fnorm;
  if (f < 0) {
    sign = 1;
    fnorm = -f;
  } else {
    sign = 0;
    fnorm = f;
  }
  // get the normalized form of f and track the exponent
  typename ieee_754_trait<T>::packed_type shift = 0;
  while (fnorm >= static_cast<T>(2)) {
    fnorm /= static_cast<T>(2);
    ++shift;
  }
  while (fnorm < static_cast<T>(1)) {
    fnorm *= static_cast<T>(2);
    --shift;
  }
  fnorm = fnorm - static_cast<T>(1);
  // calculate 2^significandbits
  auto pownum = static_cast<T>(result_type{1} << significandbits);
  // calculate the binary form (non-float) of the significand data
  auto significand = static_cast<result_type>(fnorm * (pownum + trait::p5));
  // get the biased exponent
  auto exp = shift + ((1 << (trait::expbits - 1)) - 1); // shift + bias
  // return the final answer
  return (sign << (trait::bits - 1))
         | (exp << (trait::bits - trait::expbits - 1)) | significand;
}

template <class T>
typename ieee_754_trait<T>::float_type unpack754(T i) {
  using trait = ieee_754_trait<T>;
  using signed_type = typename trait::signed_packed_type;
  using result_type = typename trait::float_type;
  using limits = std::numeric_limits<result_type>;
  switch (i) {
    case trait::packed_pzero:
      return trait::zero;
    case trait::packed_nzero:
      return -trait::zero;
    case trait::packed_pinf:
      return limits::infinity();
    case trait::packed_ninf:
      return -limits::infinity();
    case trait::packed_nan:
      return limits::quiet_NaN();
    default:
      // carry on
      break;
  }
  auto significandbits = trait::bits - trait::expbits - 1; // -1 for sign bit
  // pull the significand: mask, convert back to float + add the one back on
  auto result = static_cast<result_type>(i & ((T{1} << significandbits) - 1));
  result /= static_cast<result_type>(T{1} << significandbits);
  result += static_cast<result_type>(1);
  // deal with the exponent
  auto si = static_cast<signed_type>(i);
  auto bias = (1 << (trait::expbits - 1)) - 1;
  auto pownum = static_cast<signed_type>(1) << trait::expbits;
  auto shift
    = static_cast<signed_type>(((si >> significandbits) & (pownum - 1)) - bias);
  while (shift > 0) {
    result *= static_cast<result_type>(2);
    --shift;
  }
  while (shift < 0) {
    result /= static_cast<result_type>(2);
    ++shift;
  }
  // sign it
  result *= ((i >> (trait::bits - 1)) & 1) ? -1 : 1;
  return result;
}

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


<commit_before>/*
 * Copyright (C) 2008 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 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 St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "contextc.h"
#include "service.h"
#include "property.h"
#include "group.h"
#include "logging.h"
#include "sconnect.h"
#include "listeners.h"
#include "loggingfeatures.h"

#include <QCoreApplication>

namespace ContextProvider {
}

using namespace ContextProvider;

/*!
    \page CApi C Api

    \brief The libcontextprovider library offers a simple plain-C API to be used
    from any C program.

    \section Usage

    \code
    context_provider_init(DBUS_BUS_SESSION, "org.test.provider");

    context_provider_install_key("Battery.OnBattery", 0, NULL, NULL);
    context_provider_install_key("Battery.ChargePercentage", 0, NULL, NULL);

    context_provider_set_boolean("Battery.OnBattery", 1);
    context_provider_set_integer("Battery.ChargePercentage", 55);
    \endcode

    The above code snippet starts a service at "org.test.provider" with two
    keys ("Battery.OnBattery", "Battery.ChargePercentage") and sets their
    respective values.

    Note: If the provider used other D-Bus bindings than QDBus, the
    service name ("org.test.provider") needs to be unique, i.e., the
    provider process should not register it itself.

    \section Callbacks

    The context_provider_install_key function and context_provider_install_group function
    take arguments specyfying the callback function. The callback is executed when the
    subscription status changes for the key or the key group (first subscriber
    appears or last subscriber goes away). Basing on this info the provider can
    manage it's resources. It's okay also to use the callback function to actually
    set the value.

    \code
    void callback_function_example(int subscribed, void* user_data)
    {
        if (subscribed) {
            // First subscriber appeared.
            // Start ie. pooling the data.
        } else {
            // Last subscribed disappeared.
            // Stop  pooling data, release resources.
        }
    }
    \endcode
*/

static Service *cService;

QList<Listener*> *listeners = NULL;

char argv[] = "libcontextprovider";
char* p= argv;
int argc = 1;
QCoreApplication* app = 0;

/// Initializes and starts the service with a given \a bus_type and a \a bus_name.
/// The \a bus_type can be DBUS_BUS_SESSION or DBUS_BUS_SYSTEM. This function can be
/// called only once till a matching context_provider_stop is called.
int context_provider_init (DBusBusType bus_type, const char* bus_name)
{
    contextDebug() << F_C << bus_name;

    if (QCoreApplication::instance() == 0) {
        // No QCoreApplication created, so crate one.
        // This will also create an event dispatcher (QEventDispatcherGlibPrivate.)
        app = new QCoreApplication(argc, &p);
    }

    if (cService != NULL) {
        contextCritical() << "Service already initialize. You can only initialize one service with C API";
        return 0;
    }

    cService = new Service(bus_type == DBUS_BUS_SESSION
                           ? QDBusConnection::SessionBus
                           : QDBusConnection::SystemBus,
                           bus_name);
    listeners = new QList<Listener*>;

    return 1;
}

/// Stops the currently started service with context_provider_init. After calling
/// this function a new service can be started by calling context_provider_init.
void context_provider_stop (void)
{
    contextDebug() << F_C;
    if (cService) {
        contextDebug() << "Stopping service";

        // Delete all listeners
        if (listeners) {
            Q_FOREACH (Listener *listener, *listeners)
                delete listener;
        }
        delete listeners; listeners = NULL;

        delete cService;
        cService = NULL;
    }
    // FIXME: Do we need to do something with the QCoreApplication we might have constructed?
}

/// Installs (adds) a \a key to be provided by the service. The callback function \a
/// subscription_changed_cb will be called with the passed user data \a subscription_changed_cb_target
/// when the status of the subscription changes -- when the first subscriber appears or the
/// last subscriber disappears. The \a clear_values_on_subscribe when enabled will automatically
/// clear (set to null/undetermined) the group keys on first subscribe.
void context_provider_install_key (const char* key,
                                   int clear_values_on_subscribe,
                                   ContextProviderSubscriptionChangedCallback subscription_changed_cb,
                                   void* subscription_changed_cb_target)
{
    contextDebug() << F_C << key;

    if (! cService) {
        contextCritical() << "Can't install key:" << key << "because no service started.";
        return;
    }

    listeners->append(new PropertyListener(*cService, key,
                                           clear_values_on_subscribe,
                                           subscription_changed_cb, subscription_changed_cb_target));
    cService->restart();
}

/// Installs (adds) a \a key_group to be provided by the service. The \a key_group is a NULL-terminated
/// array containing the keys. The callback function \a subscription_changed_cb will be called with the
/// passed user data \a subscription_changed_cb_target when the status of the subscription changes --
/// when the first subscriber appears or the last subscriber disappears. The \a clear_values_on_subscribe
/// when enabled will automatically clear (set to null/undetermined) the group keys on first subscribe.
void context_provider_install_group (char* const * key_group,
                                     int clear_values_on_subscribe,
                                     ContextProviderSubscriptionChangedCallback subscription_changed_cb,
                                     void* subscription_changed_cb_target)
{
    contextDebug() << F_C;

    if (! cService) {
        contextCritical() << "Can't install key group because no service started.";
        return;
    }

    QStringList keys;
    int i = 0;
    while(key_group[i] != NULL) {
        QString key = QString(key_group[i]);

        keys << key;
        i++;
    }

    listeners->append(new GroupListener(*cService, keys,
                                        clear_values_on_subscribe,
                                        subscription_changed_cb, subscription_changed_cb_target));
    cService->restart();
}

/// Sets the \a key to a specified integer \a value.
void context_provider_set_integer (const char* key, int value)
{
    contextDebug() << F_C << key << value;
    if (cService)
        cService->setValue(key, value);
}

/// Sets the \a key to a specified double \a value.
void context_provider_set_double (const char* key, double value)
{
    contextDebug() << F_C << key << value;
    if (cService)
        cService->setValue(key, value);
}

/// Sets the \a key to a specified boolean \a value.
void context_provider_set_boolean (const char* key, int value)
{
    contextDebug() << F_C << key << value;
    if (cService)
        cService->setValue(key, (value != 0));
}

/// Sets the \a key to a specified string \a value.
void context_provider_set_string (const char* key, const char* value)
{
    contextDebug() << F_C << key << value;
    if (cService)
        cService->setValue(key, value);
}

/// Sets the \a key to NULL. In other words - unsets the key.
void context_provider_set_null (const char* key)
{
    contextDebug() << F_C << key;
    if (cService)
        cService->setValue(key, QVariant());
}

/// Sets the value of \a key to the specified \a map.  If \a free_map is TRUE,
/// frees the map, which becomes invalid afterwards.
///
/// \sa context_provider_map_new, context_provider_map_set_*
void context_provider_set_map (const char* key, void* map, int free_map)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    cService->setValue(key, QVariant(*qvm));
    if (free_map)
        delete qvm;
}

/// Creates an opaque map for use with the context_provider_map_set_* family
/// of functions.  Free it with context_provider_map_free().
void *context_provider_map_new ()
{
    return new QVariantMap();
}

/// Free the \a map created by context_provider_map_new().
void context_provider_map_free (void* map)
{
    delete reinterpret_cast<QVariantMap *>(map);
}

/// Sets \a key to the integer \a value in \a map.
void context_provider_map_set_integer(void* map, const char* key, int value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    qvm->insert(key, QVariant(value));
}

/// Sets \a key to the double \a value in \a map.
void context_provider_map_set_double (void* map, const char* key, double value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    qvm->insert(key, QVariant(value));
}

/// Sets \a key to the boolean \a value in \a map.
void context_provider_map_set_boolean(void* map, const char* key, int value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    qvm->insert(key, QVariant(value != 0));
}

/// Sets \a key to the string \a value in \a map.
void context_provider_map_set_string (void* map, const char* key, const char* value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    qvm->insert(key, QVariant(value));
}

/// Sets \a key to the map \a value in \a map.  NOTE: \a value is freed, and
/// becomes invalid after this call.
void context_provider_map_set_map (void* map, const char* key, void* value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    QVariantMap *other = reinterpret_cast<QVariantMap *>(value);
    qvm->insert(key, QVariant(*other));
    delete other;
}

/// Sets \a key to the list \a value in \a map.  NOTE: \a value is freed, and
/// becomes invalid after this call.
void context_provider_map_set_list(void* map, const char* key, void* value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    QVariantList *list = reinterpret_cast<QVariantList *>(value);
    qvm->insert(key, QVariant(*list));
    delete list;
}

/// Sets the value of \a key to the specified \a list.  If \a free_list is
/// TRUE, the list is freed.
///
/// \sa context_provider_list_new, context_provider_list_add_*
void context_provider_set_list(const char* key, void* list, int free_list)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    cService->setValue(key, QVariant(*qvl));
    if (free_list)
        delete qvl;
}

/// Creates an opaque list for use with the context_provider_list_add_* family
/// of functions.  Free it with context_provider_list_free().
void *context_provider_list_new()
{
    return new QVariantList();
}

/// Frees the list created by context_provider_list_new().  Use
/// context_provider_list_free() to free it.
void context_provider_list_free(void* list)
{
    delete reinterpret_cast<QVariantList *>(list);
}

/// Appends the integer \a value to \a list.
void context_provider_list_add_integer(void* list, int value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    qvl->append(QVariant(value));
}

/// Appends the double \a value to \a list.
void context_provider_list_add_double(void* list, double value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    qvl->append(QVariant(value));
}

/// Appends the boolean \a value to \a list.
void context_provider_list_add_boolean(void* list, int value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    qvl->append(QVariant(value != 0));
}

/// Appends the string \a value to \a list.
void context_provider_list_add_string(void* list, const char* value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    qvl->append(QVariant(value));
}

/// Appends the specified map (\a value) to \a list.  NOTE: \a value is freed
/// and becomes invalid after this call.
void context_provider_list_add_map(void* list, void* value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(value);
    qvl->append(QVariant(*qvm));
    delete qvm;
}

/// Appends the specified list \a value to \a list.  NOTE: \a value is freed,
/// and becomes invalid after this call.
void context_provider_list_add_list(void* list, void* value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    QVariantList *vl = reinterpret_cast<QVariantList *>(value);
    qvl->append(QVariant(*vl));
    delete vl;
}
<commit_msg>Limiting the visibility of the variables needed by the C API.<commit_after>/*
 * Copyright (C) 2008 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 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 St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "contextc.h"
#include "service.h"
#include "property.h"
#include "group.h"
#include "logging.h"
#include "sconnect.h"
#include "listeners.h"
#include "loggingfeatures.h"

#include <QCoreApplication>

namespace ContextProvider {
}

using namespace ContextProvider;

/*!
    \page CApi C Api

    \brief The libcontextprovider library offers a simple plain-C API to be used
    from any C program.

    \section Usage

    \code
    context_provider_init(DBUS_BUS_SESSION, "org.test.provider");

    context_provider_install_key("Battery.OnBattery", 0, NULL, NULL);
    context_provider_install_key("Battery.ChargePercentage", 0, NULL, NULL);

    context_provider_set_boolean("Battery.OnBattery", 1);
    context_provider_set_integer("Battery.ChargePercentage", 55);
    \endcode

    The above code snippet starts a service at "org.test.provider" with two
    keys ("Battery.OnBattery", "Battery.ChargePercentage") and sets their
    respective values.

    Note: If the provider used other D-Bus bindings than QDBus, the
    service name ("org.test.provider") needs to be unique, i.e., the
    provider process should not register it itself.

    \section Callbacks

    The context_provider_install_key function and context_provider_install_group function
    take arguments specyfying the callback function. The callback is executed when the
    subscription status changes for the key or the key group (first subscriber
    appears or last subscriber goes away). Basing on this info the provider can
    manage it's resources. It's okay also to use the callback function to actually
    set the value.

    \code
    void callback_function_example(int subscribed, void* user_data)
    {
        if (subscribed) {
            // First subscriber appeared.
            // Start ie. pooling the data.
        } else {
            // Last subscribed disappeared.
            // Stop  pooling data, release resources.
        }
    }
    \endcode
*/

static Service *cService;
static QList<Listener*> *listeners = NULL;

/// Initializes and starts the service with a given \a bus_type and a \a bus_name.
/// The \a bus_type can be DBUS_BUS_SESSION or DBUS_BUS_SYSTEM. This function can be
/// called only once till a matching context_provider_stop is called.
int context_provider_init (DBusBusType bus_type, const char* bus_name)
{
    contextDebug() << F_C << bus_name;

    static char arg[] = "libcontextprovider";
    static char* p = arg;
    static int argc = 1;
    static QCoreApplication* app = 0;

    if (QCoreApplication::instance() == 0) {
        // No QCoreApplication created, so crate one.
        // This will also create an event dispatcher (QEventDispatcherGlibPrivate.)
        app = new QCoreApplication(argc, &p);
    }

    if (cService != NULL) {
        contextCritical() << "Service already initialized. You can only initialize one service with C API";
        return 0;
    }

    cService = new Service(bus_type == DBUS_BUS_SESSION
                           ? QDBusConnection::SessionBus
                           : QDBusConnection::SystemBus,
                           bus_name);
    listeners = new QList<Listener*>;

    return 1;
}

/// Stops the currently started service with context_provider_init. After calling
/// this function a new service can be started by calling context_provider_init.
void context_provider_stop (void)
{
    contextDebug() << F_C;
    if (cService) {
        contextDebug() << "Stopping service";

        // Delete all listeners
        if (listeners) {
            Q_FOREACH (Listener *listener, *listeners)
                delete listener;
        }
        delete listeners; listeners = NULL;

        delete cService;
        cService = NULL;
    }
    // We cannot destroy the QCoreApplication we might have created; it must
    // be kept alive in case the provider calls init again.
}

/// Installs (adds) a \a key to be provided by the service. The callback function \a
/// subscription_changed_cb will be called with the passed user data \a subscription_changed_cb_target
/// when the status of the subscription changes -- when the first subscriber appears or the
/// last subscriber disappears. The \a clear_values_on_subscribe when enabled will automatically
/// clear (set to null/undetermined) the group keys on first subscribe.
void context_provider_install_key (const char* key,
                                   int clear_values_on_subscribe,
                                   ContextProviderSubscriptionChangedCallback subscription_changed_cb,
                                   void* subscription_changed_cb_target)
{
    contextDebug() << F_C << key;

    if (! cService) {
        contextCritical() << "Can't install key:" << key << "because no service started.";
        return;
    }

    listeners->append(new PropertyListener(*cService, key,
                                           clear_values_on_subscribe,
                                           subscription_changed_cb, subscription_changed_cb_target));
    cService->restart();
}

/// Installs (adds) a \a key_group to be provided by the service. The \a key_group is a NULL-terminated
/// array containing the keys. The callback function \a subscription_changed_cb will be called with the
/// passed user data \a subscription_changed_cb_target when the status of the subscription changes --
/// when the first subscriber appears or the last subscriber disappears. The \a clear_values_on_subscribe
/// when enabled will automatically clear (set to null/undetermined) the group keys on first subscribe.
void context_provider_install_group (char* const * key_group,
                                     int clear_values_on_subscribe,
                                     ContextProviderSubscriptionChangedCallback subscription_changed_cb,
                                     void* subscription_changed_cb_target)
{
    contextDebug() << F_C;

    if (! cService) {
        contextCritical() << "Can't install key group because no service started.";
        return;
    }

    QStringList keys;
    int i = 0;
    while(key_group[i] != NULL) {
        QString key = QString(key_group[i]);

        keys << key;
        i++;
    }

    listeners->append(new GroupListener(*cService, keys,
                                        clear_values_on_subscribe,
                                        subscription_changed_cb, subscription_changed_cb_target));
    cService->restart();
}

/// Sets the \a key to a specified integer \a value.
void context_provider_set_integer (const char* key, int value)
{
    contextDebug() << F_C << key << value;
    if (cService)
        cService->setValue(key, value);
}

/// Sets the \a key to a specified double \a value.
void context_provider_set_double (const char* key, double value)
{
    contextDebug() << F_C << key << value;
    if (cService)
        cService->setValue(key, value);
}

/// Sets the \a key to a specified boolean \a value.
void context_provider_set_boolean (const char* key, int value)
{
    contextDebug() << F_C << key << value;
    if (cService)
        cService->setValue(key, (value != 0));
}

/// Sets the \a key to a specified string \a value.
void context_provider_set_string (const char* key, const char* value)
{
    contextDebug() << F_C << key << value;
    if (cService)
        cService->setValue(key, value);
}

/// Sets the \a key to NULL. In other words - unsets the key.
void context_provider_set_null (const char* key)
{
    contextDebug() << F_C << key;
    if (cService)
        cService->setValue(key, QVariant());
}

/// Sets the value of \a key to the specified \a map.  If \a free_map is TRUE,
/// frees the map, which becomes invalid afterwards.
///
/// \sa context_provider_map_new, context_provider_map_set_*
void context_provider_set_map (const char* key, void* map, int free_map)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    cService->setValue(key, QVariant(*qvm));
    if (free_map)
        delete qvm;
}

/// Creates an opaque map for use with the context_provider_map_set_* family
/// of functions.  Free it with context_provider_map_free().
void *context_provider_map_new ()
{
    return new QVariantMap();
}

/// Free the \a map created by context_provider_map_new().
void context_provider_map_free (void* map)
{
    delete reinterpret_cast<QVariantMap *>(map);
}

/// Sets \a key to the integer \a value in \a map.
void context_provider_map_set_integer(void* map, const char* key, int value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    qvm->insert(key, QVariant(value));
}

/// Sets \a key to the double \a value in \a map.
void context_provider_map_set_double (void* map, const char* key, double value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    qvm->insert(key, QVariant(value));
}

/// Sets \a key to the boolean \a value in \a map.
void context_provider_map_set_boolean(void* map, const char* key, int value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    qvm->insert(key, QVariant(value != 0));
}

/// Sets \a key to the string \a value in \a map.
void context_provider_map_set_string (void* map, const char* key, const char* value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    qvm->insert(key, QVariant(value));
}

/// Sets \a key to the map \a value in \a map.  NOTE: \a value is freed, and
/// becomes invalid after this call.
void context_provider_map_set_map (void* map, const char* key, void* value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    QVariantMap *other = reinterpret_cast<QVariantMap *>(value);
    qvm->insert(key, QVariant(*other));
    delete other;
}

/// Sets \a key to the list \a value in \a map.  NOTE: \a value is freed, and
/// becomes invalid after this call.
void context_provider_map_set_list(void* map, const char* key, void* value)
{
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(map);
    QVariantList *list = reinterpret_cast<QVariantList *>(value);
    qvm->insert(key, QVariant(*list));
    delete list;
}

/// Sets the value of \a key to the specified \a list.  If \a free_list is
/// TRUE, the list is freed.
///
/// \sa context_provider_list_new, context_provider_list_add_*
void context_provider_set_list(const char* key, void* list, int free_list)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    cService->setValue(key, QVariant(*qvl));
    if (free_list)
        delete qvl;
}

/// Creates an opaque list for use with the context_provider_list_add_* family
/// of functions.  Free it with context_provider_list_free().
void *context_provider_list_new()
{
    return new QVariantList();
}

/// Frees the list created by context_provider_list_new().  Use
/// context_provider_list_free() to free it.
void context_provider_list_free(void* list)
{
    delete reinterpret_cast<QVariantList *>(list);
}

/// Appends the integer \a value to \a list.
void context_provider_list_add_integer(void* list, int value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    qvl->append(QVariant(value));
}

/// Appends the double \a value to \a list.
void context_provider_list_add_double(void* list, double value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    qvl->append(QVariant(value));
}

/// Appends the boolean \a value to \a list.
void context_provider_list_add_boolean(void* list, int value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    qvl->append(QVariant(value != 0));
}

/// Appends the string \a value to \a list.
void context_provider_list_add_string(void* list, const char* value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    qvl->append(QVariant(value));
}

/// Appends the specified map (\a value) to \a list.  NOTE: \a value is freed
/// and becomes invalid after this call.
void context_provider_list_add_map(void* list, void* value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    QVariantMap *qvm = reinterpret_cast<QVariantMap *>(value);
    qvl->append(QVariant(*qvm));
    delete qvm;
}

/// Appends the specified list \a value to \a list.  NOTE: \a value is freed,
/// and becomes invalid after this call.
void context_provider_list_add_list(void* list, void* value)
{
    QVariantList *qvl = reinterpret_cast<QVariantList *>(list);
    QVariantList *vl = reinterpret_cast<QVariantList *>(value);
    qvl->append(QVariant(*vl));
    delete vl;
}
<|endoftext|>


<commit_before>/***************************************************************************
                          kopetetransfermanager.cpp  -  description
                             -------------------
    begin                : Sat Aug 3 2002
    copyright            : (C) 2002 by nbetcher
    email                : nbetcher@kde.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kopetetransfermanager.h"
#include "kopetefileconfirmdialog.h"

#include "kopetecontactlist.h"
#include "kopeteprotocol.h"
#include "kopetemetacontact.h"
#include "kopetecontact.h"

#include <klocale.h>
#include <kpushbutton.h>
#include <kdialogbase.h>
#include <qpainter.h>
#include <qpixmap.h>
#include <qfontmetrics.h>

/***************************
 *  KopeteFileTransferInfo *
 ***************************/

KopeteFileTransferInfo::KopeteFileTransferInfo(  KopeteContact *contact, const QString& file, const unsigned long size, const QString &recipient, KopeteTransferDirection di, const unsigned int id, void *internalId)
{
	mContact = contact;
	mFile = file;
	mId = id;
	mSize = size;
	mRecipient = recipient;
	m_intId= internalId;
	mDirection= di;
}

/***************************
 *     KopeteTransfer      *
 ***************************/

KopeteTransfer::KopeteTransfer( const KopeteFileTransferInfo &kfti, QObject *parent, const char *name)
	: QObject(parent, name),
	  QListViewItem( KopeteTransferManager::transferManager()->mListView),
	  mInfo( kfti )
{
//	if (!kfti)
//		kfti = new KopeteFileTransferInfo(0L, QString("Unknown"), 0, QString("Unknown"), 0); // icky
	setText(0, kfti.file());
	setText(1, kfti.recipient());
	setText(2, KGlobal::locale()->formatNumber( kfti.size() ));
	setText(3, i18n("Waiting"));
	listView()->setColumnWidth(4, 150);
	slotPercentCompleted(0);
}

void KopeteTransfer::slotPercentCompleted(unsigned int percent)
{
	if (percent == 100)
		setText(3, i18n("Finished"));
	else
		setText(3, i18n("Transferring"));

	KopeteTransferManager::transferManager()->paintProgressBar(this, percent);
}

void KopeteTransfer::setError(KopeteTransferError error)
{
	QString errorString;
	switch (error)
	{
		case CanceledLocal:
			errorString = i18n("Aborted");
			emit transferCanceled();
			break;
		case CanceledRemote:
			errorString = i18n("Remote user aborted");
			break;
		case Timeout:
			errorString = i18n("Connection timed out");
			break;
		case Other:
		default:
			errorString = i18n("Unknown error occurred");
			break;
	}
	setText(3, errorString);
}

/***************************
 *  KopeteTransferManager  *
 ***************************/

KopeteTransferManager* KopeteTransferManager::s_transferManager = 0L;

KopeteTransferManager* KopeteTransferManager::transferManager()
{
	if( !s_transferManager )
		s_transferManager = new KopeteTransferManager( 0L );

	//FIXME: the transfer manager is never deleted!
	//	we can't add a parent because it is a widget
	//	and it shouldn't appaers in it

	return s_transferManager;
}

KopeteTransferManager::KopeteTransferManager( QWidget *parent )
	: KopeteFileTransferUI( parent )
{
	nextID = 0;
	hide();
	connect(cmdAbort, SIGNAL(clicked()), this, SLOT(slotAbortClicked()));
	connect(cmdRmComplete, SIGNAL(clicked()), this, SLOT(slotClearFinished()));
}

void KopeteTransferManager::paintProgressBar(QListViewItem *item, const int currentPercent)
{
	int width = (item->listView()->columnWidth(4) -4);
	QPixmap pixmap(width, (item->height() - 4));
	pixmap.fill(QColor(255,255,255));
	QPainter p(&pixmap);
	int fillWidth = static_cast<int>(width * ( (double)currentPercent/100 ) );

	// This makes the percentage bar colored coded according to how far it is in the transfer
	QColor blendColor;
	blendColor.setHsv(currentPercent, 255, 204); // Oh, I know, I'm good :)
	p.fillRect(0, 0, fillWidth, item->height(), blendColor);

	// The following is to align and draw the percentage text in the center
	QString percentText = QString::number( currentPercent ) + QString::fromLatin1( "%" );
	int textWidth = (item->listView()->fontMetrics().width(percentText) / 2);
	int newWidth = ( width / 2 );
	int startPosY = static_cast<int>(((double)item->height() /2) + 2);
	int startPosX = newWidth - textWidth;
	p.drawText(startPosX, startPosY, percentText);

	p.end();
	item->setPixmap(4, pixmap);
}

KopeteTransfer* KopeteTransferManager::addTransfer(  KopeteContact *contact, const QString& file, const unsigned long size, const QString &recipient , KopeteFileTransferInfo::KopeteTransferDirection di)
{
//	if (nextID != 0)
		nextID++;
	KopeteFileTransferInfo info(contact, file, size, recipient,di,  nextID);
	KopeteTransfer *trans = new KopeteTransfer(info, this, "KopeteTransfer");
	connect(trans, SIGNAL(done(KopeteTransfer *)), this, SIGNAL(done(KopeteTransfer *))); // Just for handiness
	mTransfersMap.insert(nextID, trans);
	mListView->insertItem(trans);
	show();
	return trans;
}

int KopeteTransferManager::askIncomingTransfer(  KopeteContact *contact, const QString& file, const unsigned long size, const QString& description, void *internalId)
{
//	if (nextID != 0)
		nextID++;
	KopeteFileTransferInfo info(contact, file, size, contact->metaContact()->displayName(), KopeteFileTransferInfo::Incoming , nextID , internalId);

	KopeteFileConfirmDialog *diag= new KopeteFileConfirmDialog(info, description , this )  ;

	connect( diag, SIGNAL( accepted(const KopeteFileTransferInfo&, const QString&)) , this, SLOT( slotAccepted(const KopeteFileTransferInfo&, const QString&) ) );
	connect( diag, SIGNAL( refused(const KopeteFileTransferInfo&)) , this, SIGNAL( refused(const KopeteFileTransferInfo&) ) );
	diag->show();
	return nextID;
}

void KopeteTransferManager::removeTransfer( unsigned int id )
{
	KopeteTransfer *trans = mTransfersMap[id];
	mTransfersMap.remove(id);
	mListView->takeItem(trans);
	if (mListView->childCount() == 0)
		hide();
	delete trans;
}

void KopeteTransferManager::slotAbortClicked()
{
	QListViewItem *curr = mListView->currentItem();
	if (!curr) return;
	KopeteTransfer *trans = dynamic_cast<KopeteTransfer *>(curr);
	if (!trans) return;
	trans->setError(KopeteTransfer::CanceledLocal);
	emit canceled(trans);
}

void KopeteTransferManager::slotClearFinished()
{
	for (QListViewItem *it = mListView->firstChild(); it != 0L; it = it->itemBelow())
	{
		if (it->text(3) != i18n("Transferring"))
		{
			KopeteTransfer *trans = dynamic_cast<KopeteTransfer *>(it);
			if (!trans) continue;
			emit canceled(trans);
			delete trans;
		}
	}
}

void KopeteTransferManager::slotAccepted(const KopeteFileTransferInfo& info, const QString& filename)
{
	KopeteTransfer *trans = new KopeteTransfer(info, this, "KopeteTransfer");
	connect(trans, SIGNAL(done(KopeteTransfer *)), this, SIGNAL(done(KopeteTransfer *))); // Just for handiness
	mTransfersMap.insert(info.transferId(), trans);
	mListView->insertItem(trans);
	show();
	emit accepted(trans,filename);
}

#include "kopetetransfermanager.moc"

<commit_msg>another compile fix, same error as before<commit_after>/***************************************************************************
                          kopetetransfermanager.cpp  -  description
                             -------------------
    begin                : Sat Aug 3 2002
    copyright            : (C) 2002 by nbetcher
    email                : nbetcher@kde.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "kopetetransfermanager.h"
#include "kopetefileconfirmdialog.h"

#include "kopetecontactlist.h"
#include "kopeteprotocol.h"
#include "kopetemetacontact.h"
#include "kopetecontact.h"

#include <klocale.h>
#include <kpushbutton.h>
#include <kdialogbase.h>
#include <qpainter.h>
#include <qpixmap.h>
#include <qfontmetrics.h>

/***************************
 *  KopeteFileTransferInfo *
 ***************************/

KopeteFileTransferInfo::KopeteFileTransferInfo(  KopeteContact *contact, const QString& file, const unsigned long size, const QString &recipient, KopeteTransferDirection di, const unsigned int id, void *internalId)
{
	mContact = contact;
	mFile = file;
	mId = id;
	mSize = size;
	mRecipient = recipient;
	m_intId= internalId;
	mDirection= di;
}

/***************************
 *     KopeteTransfer      *
 ***************************/

KopeteTransfer::KopeteTransfer( const KopeteFileTransferInfo &kfti, QObject *parent, const char *name)
	: QObject(parent, name),
	  QListViewItem( KopeteTransferManager::transferManager()->mListView),
	  mInfo( kfti )
{
//	if (!kfti)
//		kfti = new KopeteFileTransferInfo(0L, QString("Unknown"), 0, QString("Unknown"), 0); // icky
	setText(0, kfti.file());
	setText(1, kfti.recipient());
	setText( 2, KGlobal::locale()->formatNumber( long( kfti.size() ) ) );
	setText(3, i18n("Waiting"));
	listView()->setColumnWidth(4, 150);
	slotPercentCompleted(0);
}

void KopeteTransfer::slotPercentCompleted(unsigned int percent)
{
	if (percent == 100)
		setText(3, i18n("Finished"));
	else
		setText(3, i18n("Transferring"));

	KopeteTransferManager::transferManager()->paintProgressBar(this, percent);
}

void KopeteTransfer::setError(KopeteTransferError error)
{
	QString errorString;
	switch (error)
	{
		case CanceledLocal:
			errorString = i18n("Aborted");
			emit transferCanceled();
			break;
		case CanceledRemote:
			errorString = i18n("Remote user aborted");
			break;
		case Timeout:
			errorString = i18n("Connection timed out");
			break;
		case Other:
		default:
			errorString = i18n("Unknown error occurred");
			break;
	}
	setText(3, errorString);
}

/***************************
 *  KopeteTransferManager  *
 ***************************/

KopeteTransferManager* KopeteTransferManager::s_transferManager = 0L;

KopeteTransferManager* KopeteTransferManager::transferManager()
{
	if( !s_transferManager )
		s_transferManager = new KopeteTransferManager( 0L );

	//FIXME: the transfer manager is never deleted!
	//	we can't add a parent because it is a widget
	//	and it shouldn't appaers in it

	return s_transferManager;
}

KopeteTransferManager::KopeteTransferManager( QWidget *parent )
	: KopeteFileTransferUI( parent )
{
	nextID = 0;
	hide();
	connect(cmdAbort, SIGNAL(clicked()), this, SLOT(slotAbortClicked()));
	connect(cmdRmComplete, SIGNAL(clicked()), this, SLOT(slotClearFinished()));
}

void KopeteTransferManager::paintProgressBar(QListViewItem *item, const int currentPercent)
{
	int width = (item->listView()->columnWidth(4) -4);
	QPixmap pixmap(width, (item->height() - 4));
	pixmap.fill(QColor(255,255,255));
	QPainter p(&pixmap);
	int fillWidth = static_cast<int>(width * ( (double)currentPercent/100 ) );

	// This makes the percentage bar colored coded according to how far it is in the transfer
	QColor blendColor;
	blendColor.setHsv(currentPercent, 255, 204); // Oh, I know, I'm good :)
	p.fillRect(0, 0, fillWidth, item->height(), blendColor);

	// The following is to align and draw the percentage text in the center
	QString percentText = QString::number( currentPercent ) + QString::fromLatin1( "%" );
	int textWidth = (item->listView()->fontMetrics().width(percentText) / 2);
	int newWidth = ( width / 2 );
	int startPosY = static_cast<int>(((double)item->height() /2) + 2);
	int startPosX = newWidth - textWidth;
	p.drawText(startPosX, startPosY, percentText);

	p.end();
	item->setPixmap(4, pixmap);
}

KopeteTransfer* KopeteTransferManager::addTransfer(  KopeteContact *contact, const QString& file, const unsigned long size, const QString &recipient , KopeteFileTransferInfo::KopeteTransferDirection di)
{
//	if (nextID != 0)
		nextID++;
	KopeteFileTransferInfo info(contact, file, size, recipient,di,  nextID);
	KopeteTransfer *trans = new KopeteTransfer(info, this, "KopeteTransfer");
	connect(trans, SIGNAL(done(KopeteTransfer *)), this, SIGNAL(done(KopeteTransfer *))); // Just for handiness
	mTransfersMap.insert(nextID, trans);
	mListView->insertItem(trans);
	show();
	return trans;
}

int KopeteTransferManager::askIncomingTransfer(  KopeteContact *contact, const QString& file, const unsigned long size, const QString& description, void *internalId)
{
//	if (nextID != 0)
		nextID++;
	KopeteFileTransferInfo info(contact, file, size, contact->metaContact()->displayName(), KopeteFileTransferInfo::Incoming , nextID , internalId);

	KopeteFileConfirmDialog *diag= new KopeteFileConfirmDialog(info, description , this )  ;

	connect( diag, SIGNAL( accepted(const KopeteFileTransferInfo&, const QString&)) , this, SLOT( slotAccepted(const KopeteFileTransferInfo&, const QString&) ) );
	connect( diag, SIGNAL( refused(const KopeteFileTransferInfo&)) , this, SIGNAL( refused(const KopeteFileTransferInfo&) ) );
	diag->show();
	return nextID;
}

void KopeteTransferManager::removeTransfer( unsigned int id )
{
	KopeteTransfer *trans = mTransfersMap[id];
	mTransfersMap.remove(id);
	mListView->takeItem(trans);
	if (mListView->childCount() == 0)
		hide();
	delete trans;
}

void KopeteTransferManager::slotAbortClicked()
{
	QListViewItem *curr = mListView->currentItem();
	if (!curr) return;
	KopeteTransfer *trans = dynamic_cast<KopeteTransfer *>(curr);
	if (!trans) return;
	trans->setError(KopeteTransfer::CanceledLocal);
	emit canceled(trans);
}

void KopeteTransferManager::slotClearFinished()
{
	for (QListViewItem *it = mListView->firstChild(); it != 0L; it = it->itemBelow())
	{
		if (it->text(3) != i18n("Transferring"))
		{
			KopeteTransfer *trans = dynamic_cast<KopeteTransfer *>(it);
			if (!trans) continue;
			emit canceled(trans);
			delete trans;
		}
	}
}

void KopeteTransferManager::slotAccepted(const KopeteFileTransferInfo& info, const QString& filename)
{
	KopeteTransfer *trans = new KopeteTransfer(info, this, "KopeteTransfer");
	connect(trans, SIGNAL(done(KopeteTransfer *)), this, SIGNAL(done(KopeteTransfer *))); // Just for handiness
	mTransfersMap.insert(info.transferId(), trans);
	mListView->insertItem(trans);
	show();
	emit accepted(trans,filename);
}

#include "kopetetransfermanager.moc"

<|endoftext|>


<commit_before>/* traqnsport.cc
   Mathieu Stefani, 02 July 2017

   TCP transport handling

*/

#include <sys/sendfile.h>
#include <sys/timerfd.h>

#include <pistache/transport.h>
#include <pistache/peer.h>
#include <pistache/tcp.h>
#include <pistache/os.h>

namespace Pistache {

using namespace Polling;

namespace Tcp {

Transport::Transport(const std::shared_ptr<Tcp::Handler>& handler) {
    init(handler);
}

void
Transport::init(const std::shared_ptr<Tcp::Handler>& handler) {
    handler_ = handler;
    handler_->associateTransport(this);
}

std::shared_ptr<Aio::Handler>
Transport::clone() const {
    return std::make_shared<Transport>(handler_->clone());
}

void
Transport::registerPoller(Polling::Epoll& poller) {
    writesQueue.bind(poller);
    timersQueue.bind(poller);
    peersQueue.bind(poller);
    notifier.bind(poller);
}

void
Transport::handleNewPeer(const std::shared_ptr<Tcp::Peer>& peer) {
    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    if (!isInRightThread) {
        PeerEntry entry(peer);
        auto *e = peersQueue.allocEntry(entry);
        peersQueue.push(e);
    } else {
        handlePeer(peer);
    }
}

void
Transport::onReady(const Aio::FdSet& fds) {
    for (const auto& entry: fds) {
        if (entry.getTag() == writesQueue.tag()) {
            handleWriteQueue();
        }
        else if (entry.getTag() == timersQueue.tag()) {
            handleTimerQueue();
        }
        else if (entry.getTag() == peersQueue.tag()) {
            handlePeerQueue();
        }
        else if (entry.getTag() == notifier.tag()) {
            handleNotify();
        }

        else if (entry.isReadable()) {
            auto tag = entry.getTag();
            if (isPeerFd(tag)) {
                auto& peer = getPeer(tag);
                handleIncoming(peer);
            } else if (isTimerFd(tag)) {
                auto it = timers.find(tag.value());
                auto& entry = it->second;
                handleTimer(std::move(entry));
                timers.erase(it);
            }
            else {
                throw std::runtime_error("Unknown fd");
            }

        }
        else if (entry.isWritable()) {
            auto tag = entry.getTag();
            auto fd = tag.value();

            auto it = toWrite.find(fd);
            if (it == std::end(toWrite)) {
                throw std::runtime_error("Assertion Error: could not find write data");
            }

            reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);

            auto& write = it->second;
            asyncWriteImpl(fd, write, Retry);
        }
    }
}

void
Transport::disarmTimer(Fd fd) {
    auto it = timers.find(fd);
    if (it == std::end(timers))
        throw std::runtime_error("Timer has not been armed");

    auto &entry = it->second;
    entry.disable();
}

void
Transport::handleIncoming(const std::shared_ptr<Peer>& peer) {
    char buffer[Const::MaxBuffer];
    memset(buffer, 0, sizeof buffer);

    ssize_t totalBytes = 0;
    int fd = peer->fd();

    for (;;) {

        ssize_t bytes;

        bytes = recv(fd, buffer + totalBytes, Const::MaxBuffer - totalBytes, 0);
        if (bytes == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                if (totalBytes > 0) {
                    handler_->onInput(buffer, totalBytes, peer);
                }
            } else {
                if (errno == ECONNRESET) {
                    handlePeerDisconnection(peer);
                }
                else {
                    throw std::runtime_error(strerror(errno));
                }
            }
            break;
        }
        else if (bytes == 0) {
            handlePeerDisconnection(peer);
            break;
        }

        else {
            totalBytes += bytes;
            if (totalBytes >= Const::MaxBuffer) {
                std::cerr << "Too long packet" << std::endl;
                break;
            }
        }
    }
}

void
Transport::handlePeerDisconnection(const std::shared_ptr<Peer>& peer) {
    handler_->onDisconnection(peer);

    int fd = peer->fd();
    auto it = peers.find(fd);
    if (it == std::end(peers))
        throw std::runtime_error("Could not find peer to erase");

    peers.erase(it);

    close(fd);
}

void
Transport::asyncWriteImpl(Fd fd, Transport::WriteEntry& entry, WriteStatus status) {
    asyncWriteImpl(fd, entry.flags, entry.buffer, std::move(entry.deferred), status);
}

void
Transport::asyncWriteImpl(
        Fd fd, int flags, const BufferHolder& buffer,
        Async::Deferred<ssize_t> deferred, WriteStatus status)
{
    auto cleanUp = [&]() {
        if (buffer.isRaw()) {
            auto raw = buffer.raw();
            if (raw.isOwned) delete[] raw.data;
        }

        if (status == Retry)
            toWrite.erase(fd);
    };

    size_t totalWritten = buffer.offset();
    for (;;) {
        ssize_t bytesWritten = 0;
        auto len = buffer.size() - totalWritten;
        if (buffer.isRaw()) {
            auto raw = buffer.raw();
            auto ptr = raw.data + totalWritten;
            bytesWritten = ::send(fd, ptr, len, flags);
        } else {
            auto file = buffer.fd();
            off_t offset = totalWritten;
            bytesWritten = ::sendfile(fd, file, &offset, len);
        }
        if (bytesWritten < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // save for a future retry with the totalWritten offset.
                if (status == Retry) {
                    toWrite.erase(fd);
                }

                toWrite.insert(
                        std::make_pair(fd,
                            WriteEntry(std::move(deferred), buffer.detach(totalWritten), flags)));

                reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
            }
            else {
                cleanUp();
                deferred.reject(Pistache::Error::system("Could not write data"));
            }
            break;
        }
        else {
            totalWritten += bytesWritten;
            if (totalWritten >= buffer.size()) {
                cleanUp();

                if (buffer.isFile()) {
                    // done with the file buffer, nothing else knows whether to
                    // close it with the way the code is written.
                    ::close(buffer.fd());
                }

                // Cast to match the type of defered template
                // to avoid a BadType exception
                deferred.resolve(static_cast<ssize_t>(totalWritten));
                break;
            }
        }
    }
}

void
Transport::armTimerMs(
        Fd fd, std::chrono::milliseconds value,
        Async::Deferred<uint64_t> deferred) {

    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    TimerEntry entry(fd, value, std::move(deferred));

    if (!isInRightThread) {
        auto *e = timersQueue.allocEntry(std::move(entry));
        timersQueue.push(e);
    } else {
        armTimerMsImpl(std::move(entry));
    }
}

void
Transport::armTimerMsImpl(TimerEntry entry) {

    auto it = timers.find(entry.fd);
    if (it != std::end(timers)) {
        entry.deferred.reject(std::runtime_error("Timer is already armed"));
        return;
    }

    itimerspec spec;
    spec.it_interval.tv_sec = 0;
    spec.it_interval.tv_nsec = 0;

    if (entry.value.count() < 1000) {
        spec.it_value.tv_sec = 0;
        spec.it_value.tv_nsec
            = std::chrono::duration_cast<std::chrono::nanoseconds>(entry.value).count();
    } else {
        spec.it_value.tv_sec
            = std::chrono::duration_cast<std::chrono::seconds>(entry.value).count();
        spec.it_value.tv_nsec = 0;
    }

    int res = timerfd_settime(entry.fd, 0, &spec, 0);
    if (res == -1) {
        entry.deferred.reject(Pistache::Error::system("Could not set timer time"));
        return;
    }

    reactor()->registerFdOneShot(key(), entry.fd, NotifyOn::Read, Polling::Mode::Edge);
    timers.insert(std::make_pair(entry.fd, std::move(entry)));
}

void
Transport::handleWriteQueue() {
    // Let's drain the queue
    for (;;) {
        auto entry = writesQueue.popSafe();
        if (!entry) break;

        auto &write = entry->data();
        asyncWriteImpl(write.peerFd, write);
    }
}

void
Transport::handleTimerQueue() {
    for (;;) {
        auto entry = timersQueue.popSafe();
        if (!entry) break;

        auto &timer = entry->data();
        armTimerMsImpl(std::move(timer));
    }
}

void
Transport::handlePeerQueue() {
    for (;;) {
        auto entry = peersQueue.popSafe();
        if (!entry) break;

        const auto &data = entry->data();
        handlePeer(data.peer);
    }
}

void
Transport::handlePeer(const std::shared_ptr<Peer>& peer) {
    int fd = peer->fd();
    peers.insert(std::make_pair(fd, peer));

    peer->associateTransport(this);

    handler_->onConnection(peer);
    reactor()->registerFd(key(), fd, NotifyOn::Read | NotifyOn::Shutdown, Polling::Mode::Edge);
}

void
Transport::handleNotify() {
    while (this->notifier.tryRead()) ;

    rusage now;

    auto res = getrusage(RUSAGE_THREAD, &now);
    if (res == -1)
        loadRequest_.reject(std::runtime_error("Could not compute usage"));

    loadRequest_.resolve(now);
    loadRequest_.clear();
}

void
Transport::handleTimer(TimerEntry entry) {
    if (entry.isActive()) {
        uint64_t numWakeups;
        int res = ::read(entry.fd, &numWakeups, sizeof numWakeups);
        if (res == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                return;
            else
                entry.deferred.reject(Pistache::Error::system("Could not read timerfd"));
        } else {
            if (res != sizeof(numWakeups)) {
                entry.deferred.reject(Pistache::Error("Read invalid number of bytes for timer fd: "
                            + std::to_string(entry.fd)));
            }
            else {
                entry.deferred.resolve(numWakeups);
            }
        }
    }
}

bool
Transport::isPeerFd(Fd fd) const {
    return peers.find(fd) != std::end(peers);
}

bool
Transport::isTimerFd(Fd fd) const {
    return timers.find(fd) != std::end(timers);
}

bool
Transport::isPeerFd(Polling::Tag tag) const {
    return isPeerFd(tag.value());
}
bool
Transport::isTimerFd(Polling::Tag tag) const {
    return isTimerFd(tag.value());
}

std::shared_ptr<Peer>&
Transport::getPeer(Fd fd)
{
    auto it = peers.find(fd);
    if (it == std::end(peers))
    {
        throw std::runtime_error("No peer found for fd: " + std::to_string(fd));
    }
    return it->second;
}

std::shared_ptr<Peer>&
Transport::getPeer(Polling::Tag tag)
{
    return getPeer(tag.value());
}

} // namespace Tcp
} // namespace Pistache
<commit_msg>Adding additional cleanup to prevent hangs if client disconnects mid-transfer.<commit_after>/* traqnsport.cc
   Mathieu Stefani, 02 July 2017

   TCP transport handling

*/

#include <sys/sendfile.h>
#include <sys/timerfd.h>

#include <pistache/transport.h>
#include <pistache/peer.h>
#include <pistache/tcp.h>
#include <pistache/os.h>

namespace Pistache {

using namespace Polling;

namespace Tcp {

Transport::Transport(const std::shared_ptr<Tcp::Handler>& handler) {
    init(handler);
}

void
Transport::init(const std::shared_ptr<Tcp::Handler>& handler) {
    handler_ = handler;
    handler_->associateTransport(this);
}

std::shared_ptr<Aio::Handler>
Transport::clone() const {
    return std::make_shared<Transport>(handler_->clone());
}

void
Transport::registerPoller(Polling::Epoll& poller) {
    writesQueue.bind(poller);
    timersQueue.bind(poller);
    peersQueue.bind(poller);
    notifier.bind(poller);
}

void
Transport::handleNewPeer(const std::shared_ptr<Tcp::Peer>& peer) {
    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    if (!isInRightThread) {
        PeerEntry entry(peer);
        auto *e = peersQueue.allocEntry(entry);
        peersQueue.push(e);
    } else {
        handlePeer(peer);
    }
}

void
Transport::onReady(const Aio::FdSet& fds) {
    for (const auto& entry: fds) {
        if (entry.getTag() == writesQueue.tag()) {
            handleWriteQueue();
        }
        else if (entry.getTag() == timersQueue.tag()) {
            handleTimerQueue();
        }
        else if (entry.getTag() == peersQueue.tag()) {
            handlePeerQueue();
        }
        else if (entry.getTag() == notifier.tag()) {
            handleNotify();
        }

        else if (entry.isReadable()) {
            auto tag = entry.getTag();
            if (isPeerFd(tag)) {
                auto& peer = getPeer(tag);
                handleIncoming(peer);
            } else if (isTimerFd(tag)) {
                auto it = timers.find(tag.value());
                auto& entry = it->second;
                handleTimer(std::move(entry));
                timers.erase(it);
            }
            else {
                throw std::runtime_error("Unknown fd");
            }

        }
        else if (entry.isWritable()) {
            auto tag = entry.getTag();
            auto fd = tag.value();

            auto it = toWrite.find(fd);
            if (it == std::end(toWrite)) {
                throw std::runtime_error("Assertion Error: could not find write data");
            }

            reactor()->modifyFd(key(), fd, NotifyOn::Read, Polling::Mode::Edge);

            auto& write = it->second;
            asyncWriteImpl(fd, write, Retry);
        }
    }
}

void
Transport::disarmTimer(Fd fd) {
    auto it = timers.find(fd);
    if (it == std::end(timers))
        throw std::runtime_error("Timer has not been armed");

    auto &entry = it->second;
    entry.disable();
}

void
Transport::handleIncoming(const std::shared_ptr<Peer>& peer) {
    char buffer[Const::MaxBuffer];
    memset(buffer, 0, sizeof buffer);

    ssize_t totalBytes = 0;
    int fd = peer->fd();

    for (;;) {

        ssize_t bytes;

        bytes = recv(fd, buffer + totalBytes, Const::MaxBuffer - totalBytes, 0);
        if (bytes == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                if (totalBytes > 0) {
                    handler_->onInput(buffer, totalBytes, peer);
                }
            } else {
                if (errno == ECONNRESET) {
                    handlePeerDisconnection(peer);
                }
                else {
                    throw std::runtime_error(strerror(errno));
                }
            }
            break;
        }
        else if (bytes == 0) {
            handlePeerDisconnection(peer);
            break;
        }

        else {
            totalBytes += bytes;
            if (totalBytes >= Const::MaxBuffer) {
                std::cerr << "Too long packet" << std::endl;
                break;
            }
        }
    }
}

void
Transport::handlePeerDisconnection(const std::shared_ptr<Peer>& peer) {
    handler_->onDisconnection(peer);

    int fd = peer->fd();
    auto it = peers.find(fd);
    if (it == std::end(peers))
        throw std::runtime_error("Could not find peer to erase");

    peers.erase(it);
    toWrite.erase(fd);

    close(fd);
}

void
Transport::asyncWriteImpl(Fd fd, Transport::WriteEntry& entry, WriteStatus status) {
    asyncWriteImpl(fd, entry.flags, entry.buffer, std::move(entry.deferred), status);
}

void
Transport::asyncWriteImpl(
        Fd fd, int flags, const BufferHolder& buffer,
        Async::Deferred<ssize_t> deferred, WriteStatus status)
{
    auto cleanUp = [&]() {
        if (buffer.isRaw()) {
            auto raw = buffer.raw();
            if (raw.isOwned) delete[] raw.data;
        }

        if (status == Retry)
            toWrite.erase(fd);
    };

    size_t totalWritten = buffer.offset();
    for (;;) {
        ssize_t bytesWritten = 0;
        auto len = buffer.size() - totalWritten;
        if (buffer.isRaw()) {
            auto raw = buffer.raw();
            auto ptr = raw.data + totalWritten;
            bytesWritten = ::send(fd, ptr, len, flags | MSG_NOSIGNAL);
        } else {
            auto file = buffer.fd();
            off_t offset = totalWritten;
            bytesWritten = ::sendfile(fd, file, &offset, len);
        }
        if (bytesWritten < 0) {
            if (errno == EAGAIN || errno == EWOULDBLOCK) {
                // save for a future retry with the totalWritten offset.
                if (status == Retry) {
                    toWrite.erase(fd);
                }

                toWrite.insert(
                        std::make_pair(fd,
                            WriteEntry(std::move(deferred), buffer.detach(totalWritten), flags)));

                reactor()->modifyFd(key(), fd, NotifyOn::Read | NotifyOn::Write, Polling::Mode::Edge);
            }
            else {
                cleanUp();
                deferred.reject(Pistache::Error::system("Could not write data"));
            }
            break;
        }
        else {
            totalWritten += bytesWritten;
            if (totalWritten >= buffer.size()) {
                cleanUp();

                if (buffer.isFile()) {
                    // done with the file buffer, nothing else knows whether to
                    // close it with the way the code is written.
                    ::close(buffer.fd());
                }

                // Cast to match the type of defered template
                // to avoid a BadType exception
                deferred.resolve(static_cast<ssize_t>(totalWritten));
                break;
            }
        }
    }
}

void
Transport::armTimerMs(
        Fd fd, std::chrono::milliseconds value,
        Async::Deferred<uint64_t> deferred) {

    auto ctx = context();
    const bool isInRightThread = std::this_thread::get_id() == ctx.thread();
    TimerEntry entry(fd, value, std::move(deferred));

    if (!isInRightThread) {
        auto *e = timersQueue.allocEntry(std::move(entry));
        timersQueue.push(e);
    } else {
        armTimerMsImpl(std::move(entry));
    }
}

void
Transport::armTimerMsImpl(TimerEntry entry) {

    auto it = timers.find(entry.fd);
    if (it != std::end(timers)) {
        entry.deferred.reject(std::runtime_error("Timer is already armed"));
        return;
    }

    itimerspec spec;
    spec.it_interval.tv_sec = 0;
    spec.it_interval.tv_nsec = 0;

    if (entry.value.count() < 1000) {
        spec.it_value.tv_sec = 0;
        spec.it_value.tv_nsec
            = std::chrono::duration_cast<std::chrono::nanoseconds>(entry.value).count();
    } else {
        spec.it_value.tv_sec
            = std::chrono::duration_cast<std::chrono::seconds>(entry.value).count();
        spec.it_value.tv_nsec = 0;
    }

    int res = timerfd_settime(entry.fd, 0, &spec, 0);
    if (res == -1) {
        entry.deferred.reject(Pistache::Error::system("Could not set timer time"));
        return;
    }

    reactor()->registerFdOneShot(key(), entry.fd, NotifyOn::Read, Polling::Mode::Edge);
    timers.insert(std::make_pair(entry.fd, std::move(entry)));
}

void
Transport::handleWriteQueue() {
    // Let's drain the queue
    for (;;) {
        auto entry = writesQueue.popSafe();
        if (!entry) break;

        auto &write = entry->data();
        asyncWriteImpl(write.peerFd, write);
    }
}

void
Transport::handleTimerQueue() {
    for (;;) {
        auto entry = timersQueue.popSafe();
        if (!entry) break;

        auto &timer = entry->data();
        armTimerMsImpl(std::move(timer));
    }
}

void
Transport::handlePeerQueue() {
    for (;;) {
        auto entry = peersQueue.popSafe();
        if (!entry) break;

        const auto &data = entry->data();
        handlePeer(data.peer);
    }
}

void
Transport::handlePeer(const std::shared_ptr<Peer>& peer) {
    int fd = peer->fd();
    peers.insert(std::make_pair(fd, peer));

    peer->associateTransport(this);

    handler_->onConnection(peer);
    reactor()->registerFd(key(), fd, NotifyOn::Read | NotifyOn::Shutdown, Polling::Mode::Edge);
}

void
Transport::handleNotify() {
    while (this->notifier.tryRead()) ;

    rusage now;

    auto res = getrusage(RUSAGE_THREAD, &now);
    if (res == -1)
        loadRequest_.reject(std::runtime_error("Could not compute usage"));

    loadRequest_.resolve(now);
    loadRequest_.clear();
}

void
Transport::handleTimer(TimerEntry entry) {
    if (entry.isActive()) {
        uint64_t numWakeups;
        int res = ::read(entry.fd, &numWakeups, sizeof numWakeups);
        if (res == -1) {
            if (errno == EAGAIN || errno == EWOULDBLOCK)
                return;
            else
                entry.deferred.reject(Pistache::Error::system("Could not read timerfd"));
        } else {
            if (res != sizeof(numWakeups)) {
                entry.deferred.reject(Pistache::Error("Read invalid number of bytes for timer fd: "
                            + std::to_string(entry.fd)));
            }
            else {
                entry.deferred.resolve(numWakeups);
            }
        }
    }
}

bool
Transport::isPeerFd(Fd fd) const {
    return peers.find(fd) != std::end(peers);
}

bool
Transport::isTimerFd(Fd fd) const {
    return timers.find(fd) != std::end(timers);
}

bool
Transport::isPeerFd(Polling::Tag tag) const {
    return isPeerFd(tag.value());
}
bool
Transport::isTimerFd(Polling::Tag tag) const {
    return isTimerFd(tag.value());
}

std::shared_ptr<Peer>&
Transport::getPeer(Fd fd)
{
    auto it = peers.find(fd);
    if (it == std::end(peers))
    {
        throw std::runtime_error("No peer found for fd: " + std::to_string(fd));
    }
    return it->second;
}

std::shared_ptr<Peer>&
Transport::getPeer(Polling::Tag tag)
{
    return getPeer(tag.value());
}

} // namespace Tcp
} // namespace Pistache
<|endoftext|>