<commit_before>#include "mfem.hpp"
#include <fstream>
#include <iostream>

#include "multigrid.hpp"
#include "eigenvalue.hpp"

using namespace std;
using namespace mfem;

class PoissonMultigridOperator : public MultigridOperator
{
private:
   HypreBoomerAMG* amg;
   Array<ParBilinearForm*> forms;
   bool partialAssembly;

   Operator* ConstructOperator(ParFiniteElementSpace* fespace, const Array<int>& essentialDofs)
   {
      ParBilinearForm* form = new ParBilinearForm(fespace);
      if (NumLevels() != 0 && partialAssembly)
      {
         form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
      }
      ConstantCoefficient one(1.0);
      form->AddDomainIntegrator(new DiffusionIntegrator(one));
      form->Assemble();

      OperatorPtr opr;

      if (NumLevels() != 0 && partialAssembly)
      {
         opr.SetType(Operator::ANY_TYPE);
      }
      else
      {
         opr.SetType(Operator::Hypre_ParCSR);
      }

      form->FormSystemMatrix(essentialDofs, opr);
      opr.SetOperatorOwner(false);

      forms.Append(form);

      return opr.Ptr();
   }

   Solver* ConstructCoarseSolver(Operator* opr)
   {
      HypreParMatrix& hypreCoarseMat = dynamic_cast<HypreParMatrix&>(*opr);
      amg = new HypreBoomerAMG(hypreCoarseMat);
      amg->SetPrintLevel(-1);
      HyprePCG *coarseSolver = new HyprePCG(hypreCoarseMat);
      coarseSolver->SetTol(1e-8);
      coarseSolver->SetMaxIter(500);
      coarseSolver->SetPrintLevel(-1);
      coarseSolver->SetPreconditioner(*amg);
      return coarseSolver;
   }

   Solver* ConstructSmoother(ParFiniteElementSpace* fespace, Operator* opr, const Array<int>& essentialDofs)
   {
      Solver* smoother = nullptr;

      if (partialAssembly)
      {
         Vector diag(fespace->GetTrueVSize());
         forms.Last()->AssembleDiagonal(diag);

         Vector ev(opr->Width());
         OperatorJacobiSmoother invDiagOperator(diag, essentialDofs, 1.0);
         ProductOperator diagPrecond(&invDiagOperator, opr, false, false);
         double estLargestEigenvalue = PowerMethod::EstimateLargestEigenvalue(MPI_COMM_WORLD, diagPrecond, ev, 10, 1e-8);
         smoother = new OperatorChebyshevSmoother(opr, diag, essentialDofs, 3, estLargestEigenvalue);
      }
      else
      {
         smoother = new HypreSmoother(static_cast<HypreParMatrix&>(*opr));
      }

      return smoother;
   }

public:
   PoissonMultigridOperator(ParFiniteElementSpace* fespace, const Array<int>& essentialDofs, bool partialAssembly_)
      : MultigridOperator(), amg(nullptr), partialAssembly(partialAssembly_)
   {
      Operator* coarseOpr = ConstructOperator(fespace, essentialDofs);
      Solver* coarseSolver = ConstructCoarseSolver(coarseOpr);

      AddCoarseLevel(coarseOpr, coarseSolver, false, true);
   }

   ~PoissonMultigridOperator()
   {
      delete amg;
      MFEM_FORALL(i, forms.Size(), delete forms[i]; );
   }

   void AddLevel(ParFiniteElementSpace* lFEspace, ParFiniteElementSpace* hFEspace, const Array<int>& essentialDofs)
   {
      Operator* opr = ConstructOperator(hFEspace, essentialDofs);
      Solver* smoother = ConstructSmoother(hFEspace, opr, essentialDofs);
      Operator* P = new TrueTransferOperator(*lFEspace, *hFEspace);
      MultigridOperator::AddLevel(opr, smoother, P, partialAssembly, true, true);
   }

   void FormLinearSystem(const Array<int> &ess_tdof_list,
                         Vector &x, Vector &b,
                         Vector &X, Vector &B,
                         int copy_interior = 0)
   {
      OperatorPtr dummy;
      forms.Last()->FormLinearSystem(ess_tdof_list, x, b, dummy, X, B, copy_interior);
   }

   void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x)
   {
      forms.Last()->RecoverFEMSolution(X, b, x);
   }
};

int main(int argc, char *argv[])
{
   int num_procs, myid;
   MPI_Init(&argc, &argv);
   MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
   MPI_Comm_rank(MPI_COMM_WORLD, &myid);

   // 1. Parse command-line options.
   const char *mesh_file = "../../data/fichera.mesh";
   int ref_levels = 2;
   int mg_levels = 2;
   int order = 1;
   const char *basis_type = "G"; // Gauss-Lobatto
   bool visualization = 1;
   bool partialAssembly = false;
   bool pMultigrid = false;

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&order, "-o", "--order",
                  "Order of the finite element spaces");
   args.AddOption(&ref_levels, "-r", "--refine",
                  "Number of times to refine the initial mesh uniformly;"
                  "This mesh will be the coarse mesh in the multigrid hierarchy");
   args.AddOption(&mg_levels, "-l", "--levels",
                  "Number of levels in the multigrid hierarchy;");
   args.AddOption(&basis_type, "-b", "--basis-type",
                  "Basis: G - Gauss-Lobatto, P - Positive, U - Uniform");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.AddOption(&partialAssembly, "-pa", "--partialassembly", "-no-pa",
                  "--no-partialassembly",
                  "Enable or disable GLVis visualization.");
   args.AddOption(&pMultigrid, "-pmg", "--pmultigrid", "-no-pmg",
                  "--no-pmultigrid",
                  "Enable or disable GLVis visualization.");
   args.Parse();
   if (!args.Good())
   {
      if (myid == 0)
      {
         args.PrintUsage(cout);
      }
      MPI_Finalize();
      return 1;
   }

   if (myid == 0)
   {
      args.PrintOptions(cout);
   }

   // See class BasisType in fem/fe_coll.hpp for available basis types
   int basis = BasisType::GetType(basis_type[0]);
   if (myid == 0)
   {
      cout << "Using " << BasisType::Name(basis) << " basis ..." << endl;
   }

   // 2. Read the mesh from the given mesh file. We can handle triangular,
   //    quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
   //    the same code.
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   // Mesh *mesh = new Mesh(1, 1, 1, Element::HEXAHEDRON, true, 1.0, 1.0, 1.0, false);
   // Mesh *mesh = new Mesh(1, 1, Element::QUADRILATERAL, true, 1.0, 1.0, false);
   int dim = mesh->Dimension();

   Array<int> ess_bdr(mesh->bdr_attributes.Max());
   ess_bdr = 1;

   // Initial refinements of the input grid
   for (int i = 0; i < ref_levels; ++i)
   {
      mesh->UniformRefinement();
   }

   ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
   delete mesh;
   mesh = nullptr;

   Array<FiniteElementCollection*> feCollectons;
   feCollectons.Append(new H1_FECollection(order, dim, basis));

   // Set up coarse grid finite element space
   ParFiniteElementSpace* fespace = new ParFiniteElementSpace(pmesh, feCollectons.Last());
   HYPRE_Int size = fespace->GlobalTrueVSize();

   if (myid == 0)
   {
      cout << "Number of finite element unknowns on level 0: " << size << "; FE order: " << order << endl;
   }

   Array<Array<int>*> essentialTrueDoFs;
   essentialTrueDoFs.Append(new Array<int>());
   fespace->GetEssentialTrueDofs(ess_bdr, *essentialTrueDoFs.Last());

   ParSpaceHierarchy* spaceHierarchy = new ParSpaceHierarchy(pmesh, fespace, true, true);
   PoissonMultigridOperator* oprMultigrid = new PoissonMultigridOperator(fespace, *essentialTrueDoFs.Last(), partialAssembly);

   for(int level = 1; level < mg_levels; ++level)
   {
      int newOrder = order;

      if (pMultigrid)
      {
         newOrder = std::pow(2, level);
         feCollectons.Append(new H1_FECollection(newOrder, dim, basis));
         spaceHierarchy->AddOrderRefinedLevel(feCollectons.Last());
      }
      else
      {
         spaceHierarchy->AddUniformlyRefinedLevel();
      }

      size = spaceHierarchy->GetFinestFESpace().GlobalTrueVSize();
      if (myid == 0)
      {
         cout << "Number of finite element unknowns on level " << level << ": " << size << "; FE order: " << newOrder << endl;
      }

      essentialTrueDoFs.Append(new Array<int>());
      spaceHierarchy->GetFinestFESpace().GetEssentialTrueDofs(ess_bdr, *essentialTrueDoFs.Last());

      oprMultigrid->AddLevel(&spaceHierarchy->GetFESpaceAtLevel(level - 1), &spaceHierarchy->GetFinestFESpace(), *essentialTrueDoFs.Last());
   }

   ParGridFunction x(&spaceHierarchy->GetFinestFESpace());
   x = 0.0;

   if (myid == 0)
   {
      cout << "Assembling rhs..." << flush;
   }
   tic_toc.Clear();
   tic_toc.Start();
   ParLinearForm* b = new ParLinearForm(&spaceHierarchy->GetFinestFESpace());
   ConstantCoefficient one(1.0);
   b->AddDomainIntegrator(new DomainLFIntegrator(one));
   b->Assemble();
   tic_toc.Stop();
   if (myid == 0)
   {
      cout << "\t\t\t\tdone, " << tic_toc.RealTime() << "s." << endl;
   }

   Vector X, B;
   oprMultigrid->FormLinearSystem(*essentialTrueDoFs.Last(), x, *b, X, B);

   Vector r(X.Size());
   MultigridSolver* mgCycle = new MultigridSolver(oprMultigrid, MultigridSolver::CycleType::VCYCLE, 3, 3);

   // HypreParMatrix& hypreMat = dynamic_cast<HypreParMatrix&>(*oprMultigrid.GetOperatorAtFinestLevel());
   // HypreBoomerAMG *amg = new HypreBoomerAMG(hypreMat);
   // HyprePCG *pcg = new HyprePCG(hypreMat);
   // pcg->SetTol(1e-8);
   // pcg->SetMaxIter(500);
   // pcg->SetPrintLevel(2);
   // pcg->SetPreconditioner(*amg);
   // pcg->Mult(B, X);

   // CGSolver pcg(MPI_COMM_WORLD);
   // pcg.SetPrintLevel(1);
   // pcg.SetMaxIter(10);
   // pcg.SetRelTol(1e-5);
   // pcg.SetAbsTol(0.0);
   // pcg.SetOperator(*oprMultigrid.GetOperatorAtFinestLevel());
   // pcg.SetPreconditioner(mgCycle);
   // pcg.Mult(B, X);

   oprMultigrid->Mult(X, r);
   subtract(B, r, r);

   double beginRes = sqrt(InnerProduct(MPI_COMM_WORLD, r, r));
   double prevRes = beginRes;
   const int printWidth = 11;

   if (myid == 0)
   {
      cout << std::setw(3) << "It";
      cout << std::setw(printWidth) << "Absres";
      cout << std::setw(printWidth) << "Relres";
      cout << std::setw(printWidth) << "Conv";
      cout << std::setw(printWidth) << "Time [s]" << endl;

      cout << std::setw(3) << 0;
      cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << beginRes;
      cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << 1.0;
      cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << 0.0;
      cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << 0.0 << endl;
   }

   for (int iter = 0; iter < 10; ++iter)
   {
      tic_toc.Clear();
      tic_toc.Start();
      mgCycle->Mult(B, X);
      tic_toc.Stop();

      oprMultigrid->Mult(X, r);
      subtract(B, r, r);

      double res = sqrt(InnerProduct(MPI_COMM_WORLD, r, r));
      if (myid == 0)
      {
         cout << std::setw(3) << iter + 1;
         cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << res;
         cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << res/beginRes;
         cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << res/prevRes;
         cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << tic_toc.RealTime() << endl;
      }

      if (res < 1e-10 * beginRes)
      {
         break;
      }

      prevRes = res;
   }

   oprMultigrid->RecoverFEMSolution(X, *b, x);

   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock << "parallel " << num_procs << " " << myid << "\n";
      sol_sock.precision(8);
      sol_sock << "solution\n" << spaceHierarchy->GetFinestMesh() << x << flush;
   }

   delete b;
   delete mgCycle;
   delete oprMultigrid;
   delete spaceHierarchy;

   MFEM_FORALL(i, essentialTrueDoFs.Size(), delete essentialTrueDoFs[i]; );
   MFEM_FORALL(i, feCollectons.Size(), delete feCollectons[i]; );

   MPI_Finalize();

   return 0;
}<commit_msg>Added parameter to compare BoomerAMG to MultigridSolver<commit_after>#include "mfem.hpp"
#include <fstream>
#include <iostream>

#include "multigrid.hpp"
#include "eigenvalue.hpp"

using namespace std;
using namespace mfem;

class PoissonMultigridOperator : public MultigridOperator
{
private:
   HypreBoomerAMG* amg;
   Array<ParBilinearForm*> forms;
   bool partialAssembly;

   Operator* ConstructOperator(ParFiniteElementSpace* fespace, const Array<int>& essentialDofs)
   {
      ParBilinearForm* form = new ParBilinearForm(fespace);
      if (NumLevels() != 0 && partialAssembly)
      {
         form->SetAssemblyLevel(AssemblyLevel::PARTIAL);
      }
      ConstantCoefficient one(1.0);
      form->AddDomainIntegrator(new DiffusionIntegrator(one));
      form->Assemble();

      OperatorPtr opr;

      if (NumLevels() != 0 && partialAssembly)
      {
         opr.SetType(Operator::ANY_TYPE);
      }
      else
      {
         opr.SetType(Operator::Hypre_ParCSR);
      }

      form->FormSystemMatrix(essentialDofs, opr);
      opr.SetOperatorOwner(false);

      forms.Append(form);

      return opr.Ptr();
   }

   Solver* ConstructCoarseSolver(Operator* opr)
   {
      HypreParMatrix& hypreCoarseMat = dynamic_cast<HypreParMatrix&>(*opr);
      amg = new HypreBoomerAMG(hypreCoarseMat);
      amg->SetPrintLevel(-1);
      HyprePCG *coarseSolver = new HyprePCG(hypreCoarseMat);
      coarseSolver->SetTol(1e-8);
      coarseSolver->SetMaxIter(500);
      coarseSolver->SetPrintLevel(-1);
      coarseSolver->SetPreconditioner(*amg);
      return coarseSolver;
   }

   Solver* ConstructSmoother(ParFiniteElementSpace* fespace, Operator* opr, const Array<int>& essentialDofs)
   {
      Solver* smoother = nullptr;

      if (partialAssembly)
      {
         Vector diag(fespace->GetTrueVSize());
         forms.Last()->AssembleDiagonal(diag);

         Vector ev(opr->Width());
         OperatorJacobiSmoother invDiagOperator(diag, essentialDofs, 1.0);
         ProductOperator diagPrecond(&invDiagOperator, opr, false, false);
         double estLargestEigenvalue = PowerMethod::EstimateLargestEigenvalue(MPI_COMM_WORLD, diagPrecond, ev, 10, 1e-8);
         smoother = new OperatorChebyshevSmoother(opr, diag, essentialDofs, 3, estLargestEigenvalue);
      }
      else
      {
         smoother = new HypreSmoother(static_cast<HypreParMatrix&>(*opr));
      }

      return smoother;
   }

public:
   PoissonMultigridOperator(ParFiniteElementSpace* fespace, const Array<int>& essentialDofs, bool partialAssembly_)
      : MultigridOperator(), amg(nullptr), partialAssembly(partialAssembly_)
   {
      Operator* coarseOpr = ConstructOperator(fespace, essentialDofs);
      Solver* coarseSolver = ConstructCoarseSolver(coarseOpr);

      AddCoarseLevel(coarseOpr, coarseSolver, false, true);
   }

   ~PoissonMultigridOperator()
   {
      delete amg;
      MFEM_FORALL(i, forms.Size(), delete forms[i]; );
   }

   void AddLevel(ParFiniteElementSpace* lFEspace, ParFiniteElementSpace* hFEspace, const Array<int>& essentialDofs)
   {
      Operator* opr = ConstructOperator(hFEspace, essentialDofs);
      Solver* smoother = ConstructSmoother(hFEspace, opr, essentialDofs);
      Operator* P = new TrueTransferOperator(*lFEspace, *hFEspace);
      MultigridOperator::AddLevel(opr, smoother, P, partialAssembly, true, true);
   }

   void FormLinearSystem(const Array<int> &ess_tdof_list,
                         Vector &x, Vector &b,
                         Vector &X, Vector &B,
                         int copy_interior = 0)
   {
      OperatorPtr dummy;
      forms.Last()->FormLinearSystem(ess_tdof_list, x, b, dummy, X, B, copy_interior);
   }

   void RecoverFEMSolution(const Vector &X, const Vector &b, Vector &x)
   {
      forms.Last()->RecoverFEMSolution(X, b, x);
   }
};

int main(int argc, char *argv[])
{
   int num_procs, myid;
   MPI_Init(&argc, &argv);
   MPI_Comm_size(MPI_COMM_WORLD, &num_procs);
   MPI_Comm_rank(MPI_COMM_WORLD, &myid);

   // 1. Parse command-line options.
   const char *mesh_file = "../../data/fichera.mesh";
   int ref_levels = 2;
   int mg_levels = 2;
   int order = 1;
   const char *basis_type = "G"; // Gauss-Lobatto
   bool visualization = 1;
   bool partialAssembly = false;
   bool pMultigrid = false;
   bool boomerAMG = false;

   OptionsParser args(argc, argv);
   args.AddOption(&mesh_file, "-m", "--mesh",
                  "Mesh file to use.");
   args.AddOption(&order, "-o", "--order",
                  "Order of the finite element spaces");
   args.AddOption(&ref_levels, "-r", "--refine",
                  "Number of times to refine the initial mesh uniformly;"
                  "This mesh will be the coarse mesh in the multigrid hierarchy");
   args.AddOption(&mg_levels, "-l", "--levels",
                  "Number of levels in the multigrid hierarchy;");
   args.AddOption(&basis_type, "-b", "--basis-type",
                  "Basis: G - Gauss-Lobatto, P - Positive, U - Uniform");
   args.AddOption(&visualization, "-vis", "--visualization", "-no-vis",
                  "--no-visualization",
                  "Enable or disable GLVis visualization.");
   args.AddOption(&partialAssembly, "-pa", "--partialassembly", "-no-pa",
                  "--no-partialassembly",
                  "Enable or disable partial assembly.");
   args.AddOption(&pMultigrid, "-pmg", "--pmultigrid", "-no-pmg",
                  "--no-pmultigrid",
                  "Enable or p multigrid.");
   args.AddOption(&boomerAMG, "-boomeramg", "--boomeramg", "-no-boomeramg",
                  "--no-boomeramg",
                  "Enable or disable usage of BoomerAMG at finest level");
   args.Parse();
   if (!args.Good())
   {
      if (myid == 0)
      {
         args.PrintUsage(cout);
      }
      MPI_Finalize();
      return 1;
   }

   if (myid == 0)
   {
      args.PrintOptions(cout);
   }

   // See class BasisType in fem/fe_coll.hpp for available basis types
   int basis = BasisType::GetType(basis_type[0]);
   if (myid == 0)
   {
      cout << "Using " << BasisType::Name(basis) << " basis ..." << endl;
   }

   // 2. Read the mesh from the given mesh file. We can handle triangular,
   //    quadrilateral, tetrahedral, hexahedral, surface and volume meshes with
   //    the same code.
   Mesh *mesh = new Mesh(mesh_file, 1, 1);
   // Mesh *mesh = new Mesh(1, 1, 1, Element::HEXAHEDRON, true, 1.0, 1.0, 1.0, false);
   // Mesh *mesh = new Mesh(1, 1, Element::QUADRILATERAL, true, 1.0, 1.0, false);
   int dim = mesh->Dimension();

   Array<int> ess_bdr(mesh->bdr_attributes.Max());
   ess_bdr = 1;

   // Initial refinements of the input grid
   for (int i = 0; i < ref_levels; ++i)
   {
      mesh->UniformRefinement();
   }

   ParMesh *pmesh = new ParMesh(MPI_COMM_WORLD, *mesh);
   delete mesh;
   mesh = nullptr;

   Array<FiniteElementCollection*> feCollectons;
   feCollectons.Append(new H1_FECollection(order, dim, basis));

   // Set up coarse grid finite element space
   ParFiniteElementSpace* fespace = new ParFiniteElementSpace(pmesh, feCollectons.Last());
   HYPRE_Int size = fespace->GlobalTrueVSize();

   if (myid == 0)
   {
      cout << "Number of finite element unknowns on level 0: " << size << "; FE order: " << order << endl;
   }

   Array<Array<int>*> essentialTrueDoFs;
   essentialTrueDoFs.Append(new Array<int>());
   fespace->GetEssentialTrueDofs(ess_bdr, *essentialTrueDoFs.Last());

   ParSpaceHierarchy* spaceHierarchy = new ParSpaceHierarchy(pmesh, fespace, true, true);
   PoissonMultigridOperator* oprMultigrid = new PoissonMultigridOperator(fespace, *essentialTrueDoFs.Last(), partialAssembly);

   for(int level = 1; level < mg_levels; ++level)
   {
      tic_toc.Clear();
      tic_toc.Start();
      int newOrder = order;

      if (pMultigrid)
      {
         newOrder = std::pow(2, level);
         feCollectons.Append(new H1_FECollection(newOrder, dim, basis));
         spaceHierarchy->AddOrderRefinedLevel(feCollectons.Last());
      }
      else
      {
         spaceHierarchy->AddUniformlyRefinedLevel();
      }

      size = spaceHierarchy->GetFinestFESpace().GlobalTrueVSize();
      if (myid == 0)
      {
         cout << "Number of finite element unknowns on level " << level << ": " << size << "; FE order: " << newOrder << endl;
      }

      essentialTrueDoFs.Append(new Array<int>());
      spaceHierarchy->GetFinestFESpace().GetEssentialTrueDofs(ess_bdr, *essentialTrueDoFs.Last());

      oprMultigrid->AddLevel(&spaceHierarchy->GetFESpaceAtLevel(level - 1), &spaceHierarchy->GetFinestFESpace(), *essentialTrueDoFs.Last());
      tic_toc.Stop();
      if (myid == 0)
      {
         cout << "Assembly time on level " << level << ": " << tic_toc.RealTime() << "s" << endl;
      }
   }

   ParGridFunction x(&spaceHierarchy->GetFinestFESpace());
   x = 0.0;

   if (myid == 0)
   {
      cout << "Assembling rhs..." << flush;
   }
   tic_toc.Clear();
   tic_toc.Start();
   ParLinearForm* b = new ParLinearForm(&spaceHierarchy->GetFinestFESpace());
   ConstantCoefficient one(1.0);
   b->AddDomainIntegrator(new DomainLFIntegrator(one));
   b->Assemble();
   tic_toc.Stop();
   if (myid == 0)
   {
      cout << "\t\t\t\tdone, " << tic_toc.RealTime() << "s." << endl;
   }

   Vector X, B;
   oprMultigrid->FormLinearSystem(*essentialTrueDoFs.Last(), x, *b, X, B);

   Vector r(X.Size());
   MultigridSolver* mgCycle = new MultigridSolver(oprMultigrid, MultigridSolver::CycleType::VCYCLE, 3, 3);

   tic_toc.Clear();
   tic_toc.Start();
   if (boomerAMG)
   {
      HypreParMatrix& hypreMat = dynamic_cast<HypreParMatrix&>(*oprMultigrid->GetOperatorAtFinestLevel());
      HypreBoomerAMG *amg = new HypreBoomerAMG(hypreMat);
      HyprePCG *pcg = new HyprePCG(hypreMat);
      pcg->SetPrintLevel(2);
      pcg->SetMaxIter(100);
      pcg->SetTol(1e-8);
      pcg->SetPreconditioner(*amg);
      pcg->Mult(B, X);
   }
   else
   {
      CGSolver pcg(MPI_COMM_WORLD);
      pcg.SetPrintLevel(1);
      pcg.SetMaxIter(100);
      pcg.SetRelTol(1e-8);
      pcg.SetAbsTol(0.0);
      pcg.SetOperator(*oprMultigrid->GetOperatorAtFinestLevel());
      pcg.SetPreconditioner(*mgCycle);
      pcg.Mult(B, X);
   }
   tic_toc.Stop();

   if (myid == 0)
   {
      cout << "Time to solution: " << tic_toc.RealTime() << "s" << endl;
   }

   // oprMultigrid->Mult(X, r);
   // subtract(B, r, r);

   // double beginRes = sqrt(InnerProduct(MPI_COMM_WORLD, r, r));
   // double prevRes = beginRes;
   // const int printWidth = 11;

   // if (myid == 0)
   // {
   //    cout << std::setw(3) << "It";
   //    cout << std::setw(printWidth) << "Absres";
   //    cout << std::setw(printWidth) << "Relres";
   //    cout << std::setw(printWidth) << "Conv";
   //    cout << std::setw(printWidth) << "Time [s]" << endl;

   //    cout << std::setw(3) << 0;
   //    cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << beginRes;
   //    cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << 1.0;
   //    cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << 0.0;
   //    cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << 0.0 << endl;
   // }

   // for (int iter = 0; iter < 10; ++iter)
   // {
   //    tic_toc.Clear();
   //    tic_toc.Start();
   //    mgCycle->Mult(B, X);
   //    tic_toc.Stop();

   //    oprMultigrid->Mult(X, r);
   //    subtract(B, r, r);

   //    double res = sqrt(InnerProduct(MPI_COMM_WORLD, r, r));
   //    if (myid == 0)
   //    {
   //       cout << std::setw(3) << iter + 1;
   //       cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << res;
   //       cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << res/beginRes;
   //       cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << res/prevRes;
   //       cout << std::scientific << std::setprecision(3) << std::setw(printWidth) << tic_toc.RealTime() << endl;
   //    }

   //    if (res < 1e-10 * beginRes)
   //    {
   //       break;
   //    }

   //    prevRes = res;
   // }

   oprMultigrid->RecoverFEMSolution(X, *b, x);

   if (visualization)
   {
      char vishost[] = "localhost";
      int  visport   = 19916;
      socketstream sol_sock(vishost, visport);
      sol_sock << "parallel " << num_procs << " " << myid << "\n";
      sol_sock.precision(8);
      sol_sock << "solution\n" << spaceHierarchy->GetFinestMesh() << x << flush;
   }

   delete b;
   delete mgCycle;
   delete oprMultigrid;
   delete spaceHierarchy;

   MFEM_FORALL(i, essentialTrueDoFs.Size(), delete essentialTrueDoFs[i]; );
   MFEM_FORALL(i, feCollectons.Size(), delete feCollectons[i]; );

   MPI_Finalize();

   return 0;
}<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Open Source Robotics Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

#ifndef __IGN_TRANSPORT_SUBSCRIPTIONHANDLER_HH_INCLUDED__
#define __IGN_TRANSPORT_SUBSCRIPTIONHANDLER_HH_INCLUDED__

#include <google/protobuf/message.h>
#include <functional>
#include <memory>
#include <string>
#include "ignition/transport/TransportTypes.hh"

namespace ignition
{
  namespace transport
  {
    /// \class ISubscriptionHandler SubscriptionHandler.hh
    /// \brief Interface class used to manage generic protobub messages.
    class ISubscriptionHandler
    {
      /// \brief Destructor
      public: virtual ~ISubscriptionHandler()
      {
      }

      /// \brief Executes the local callback registered for this handler.
      /// \param[in] _topic Topic to be passed to the callback.
      /// \param[in] _msg Protobuf message received.
      /// \return 0 when success.
      public: virtual int RunLocalCallback(const std::string &_topic,
                                           const transport::ProtoMsg &_msg) = 0;

      /// \brief Executes the callback registered for this handler.
      /// \param[in] _topic Topic to be passed to the callback.
      /// \param[in] _data Serialized data received. The data will be used
      /// to compose a specific protobuf message and will be passed to the
      /// callback function.
      /// \return 0 when success.
      public: virtual int RunCallback(const std::string &_topic,
                                      const std::string &_data) = 0;
    };

    /// \class SubscriptionHandler SubscriptionHandler.hh
    /// \brief It creates subscription handlers for each specific protobuf
    /// message used.
    template <typename T> class SubscriptionHandler
      : public ISubscriptionHandler
    {
      /// \brief Create a specific protobuf message given its serialized data.
      /// \param[in] _data The serialized data.
      /// \return Pointer to the specific protobuf message.
      public: std::shared_ptr<T> CreateMsg(const char *_data)
      {
        // Instantiate a specific protobuf message
        std::shared_ptr<T> msgPtr(new T());

        // Create the message using some serialized data
        msgPtr->ParseFromString(_data);

        return msgPtr;
      }

      /// \brief Set the callback for this handler.
      /// \param[in] _cb The callback.
      public: void SetCallback(
        const std::function<void(const std::string &, const T &)> &_cb)
      {
        this->cb = _cb;
      }

      // Documentation inherited.
      public: int RunLocalCallback(const std::string &_topic,
                                   const transport::ProtoMsg &_msg)
      {
        // Execute the callback (if existing)
        if (this->cb)
        {
          auto msgPtr = google::protobuf::down_cast<const T*>(&_msg);
          this->cb(_topic, *msgPtr);
          return 0;
        }
        else
        {
          std::cerr << "Callback is NULL" << std::endl;
          return -1;
        }
      }

      // Documentation inherited
      public: int RunCallback(const std::string &_topic,
                              const std::string &_data)
      {
        // Instantiate the specific protobuf message associated to this topic.
        auto msg = this->CreateMsg(_data.c_str());

        // Execute the callback (if existing)
        if (this->cb)
        {
          this->cb(_topic, *msg);
          return 0;
        }
        else
        {
          std::cerr << "Callback is NULL" << std::endl;
          return -1;
        }
      }

      /// \brief Callback to the function registered for this handler.
      private: std::function<void(const std::string &, const T &)> cb;
    };
  }
}

#endif
<commit_msg>Adding missing include.<commit_after>/*
 * Copyright (C) 2014 Open Source Robotics Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
*/

#ifndef __IGN_TRANSPORT_SUBSCRIPTIONHANDLER_HH_INCLUDED__
#define __IGN_TRANSPORT_SUBSCRIPTIONHANDLER_HH_INCLUDED__

#include <google/protobuf/message.h>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include "ignition/transport/TransportTypes.hh"

namespace ignition
{
  namespace transport
  {
    /// \class ISubscriptionHandler SubscriptionHandler.hh
    /// \brief Interface class used to manage generic protobub messages.
    class ISubscriptionHandler
    {
      /// \brief Destructor
      public: virtual ~ISubscriptionHandler()
      {
      }

      /// \brief Executes the local callback registered for this handler.
      /// \param[in] _topic Topic to be passed to the callback.
      /// \param[in] _msg Protobuf message received.
      /// \return 0 when success.
      public: virtual int RunLocalCallback(const std::string &_topic,
                                           const transport::ProtoMsg &_msg) = 0;

      /// \brief Executes the callback registered for this handler.
      /// \param[in] _topic Topic to be passed to the callback.
      /// \param[in] _data Serialized data received. The data will be used
      /// to compose a specific protobuf message and will be passed to the
      /// callback function.
      /// \return 0 when success.
      public: virtual int RunCallback(const std::string &_topic,
                                      const std::string &_data) = 0;
    };

    /// \class SubscriptionHandler SubscriptionHandler.hh
    /// \brief It creates subscription handlers for each specific protobuf
    /// message used.
    template <typename T> class SubscriptionHandler
      : public ISubscriptionHandler
    {
      /// \brief Create a specific protobuf message given its serialized data.
      /// \param[in] _data The serialized data.
      /// \return Pointer to the specific protobuf message.
      public: std::shared_ptr<T> CreateMsg(const char *_data)
      {
        // Instantiate a specific protobuf message
        std::shared_ptr<T> msgPtr(new T());

        // Create the message using some serialized data
        msgPtr->ParseFromString(_data);

        return msgPtr;
      }

      /// \brief Set the callback for this handler.
      /// \param[in] _cb The callback.
      public: void SetCallback(
        const std::function<void(const std::string &, const T &)> &_cb)
      {
        this->cb = _cb;
      }

      // Documentation inherited.
      public: int RunLocalCallback(const std::string &_topic,
                                   const transport::ProtoMsg &_msg)
      {
        // Execute the callback (if existing)
        if (this->cb)
        {
          auto msgPtr = google::protobuf::down_cast<const T*>(&_msg);
          this->cb(_topic, *msgPtr);
          return 0;
        }
        else
        {
          std::cerr << "Callback is NULL" << std::endl;
          return -1;
        }
      }

      // Documentation inherited
      public: int RunCallback(const std::string &_topic,
                              const std::string &_data)
      {
        // Instantiate the specific protobuf message associated to this topic.
        auto msg = this->CreateMsg(_data.c_str());

        // Execute the callback (if existing)
        if (this->cb)
        {
          this->cb(_topic, *msg);
          return 0;
        }
        else
        {
          std::cerr << "Callback is NULL" << std::endl;
          return -1;
        }
      }

      /// \brief Callback to the function registered for this handler.
      private: std::function<void(const std::string &, const T &)> cb;
    };
  }
}

#endif
<|endoftext|>


<commit_before>//===------------------------ UnitTestRunner.cpp --------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// TO ADD A NEW TEST, search in this file for [new_tests]
//
// Provides a mechanism for doing faux unit tests.  The idea is to emulate the
// basic function of calling a function and validating its results/effects.
//
// This is done via the test_specification instruction.  Using one or more
// instances of it in your function, you can specify which test (i.e. UnitTest
// subclass) should be run and what arguments should be provided to it.  The
// test grabs the arguments it expects out of the test::Arguments instance it is
// provided.  It calls some function or functions.  It then prints out
// interesting results.  These results can then be FileCheck'd.
//
// CASE STUDY:
// Here's an example of how it works:
// 1) A test test/SILOptimizer/interesting_functionality.sil runs this pass:
//     // RUN: %target-sil-opt -unit-test-runner %s 2>&1 | %FileCheck %s
// 2) A function in interesting_functionality.sil contains the
//    test_specification instruction.
//      sil @f : $() -> () {
//      ...
//      test_specification "my-neato-utility 43 @trace[2] @function[other_fun]"
//      ...
//      }
// 3) UnitTestRunner sees the name "my-neato-utility", instantiates the
//    corresponding UnitTest subclass MyNeatoUtilityTest, and calls ::invoke()
//    on it, passing an test::Arguments that contains
//      (43 : unsigned long, someValue : SILValue, other_fun : SILFunction *)
//
// 4) MyNeatoUtilityTest calls takeUInt(), takeValue(), and takeFunction() on
//    the test::Arguments instance.
//      auto count = arguments.takeUInt();
//      auto target = arguments.takeValue();
//      auto callee = arguments.takeFunction();
// 5) MyNeatoUtilityTest calls myNeatoUtility, passing these values along.
//      myNeatoUtility(count, target, callee);
// 6) MyNeatoUtilityTest then dumps out the current function, which was modified
//    in the process.
//      getFunction()->dump();
// 7) The test file test/SILOptimizer/interesting_functionality.sil matches the
//    expected contents of the modified function:
//    // CHECK-LABEL: sil @f
//    // CHECK-NOT:     function_ref @other_fun
//
// [new_tests] TESTING MORE FUNCTIONALITY:
//
// 1) Add a new UnitTest subclass.
//    - Add a constructor: NewTest(UnitTestRunner *pass) : UnitTest(pass) {}
//    - Implement invoke: void invoke(test::Arguments &arguments) override {...}
//    - Call the take{TYPE} methods to get the arguments you need.
//    - Call your function with those arguments.
// 2) Add a new ADD_UNIT_TEST_SUBCLASS line.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/Type.h"
#include "swift/Basic/TaggedUnion.h"
#include "swift/SIL/PrunedLiveness.h"
#include "swift/SIL/SILArgumentArrayRef.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILBridging.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
#include "swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
#include "swift/SILOptimizer/Utils/ParseTestSpecification.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include <iterator>
#include <memory>

using namespace swift;
using namespace swift::test;

namespace {

class UnitTestRunner;

class UnitTest {
  UnitTestRunner *pass;

protected:
  template <typename Analysis>
  Analysis *getAnalysis();
  UnitTestRunner *getPass() { return pass; }
  SILFunction *getFunction();

public:
  UnitTest(UnitTestRunner *pass) : pass(pass){};
  virtual ~UnitTest() {}
  virtual void invoke(Arguments &arguments) = 0;
};

// Arguments:
// - string: list of characters, each of which specifies subsequent arguments
//           - F: function
//           - B: block
//           - I: instruction
//           - V: value
//           - O: operand
//           - b: boolean
//           - u: unsigned
//           - s: string
// - ...
// - an argument of the type specified in the initial string
// - ...
// Dumps:
// - for each argument (after the initial string)
//   - its type
//   - something to identify the instance (mostly this means calling dump)
struct TestSpecificationTest : UnitTest {
  TestSpecificationTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto expectedFields = arguments.takeString();
    for (auto expectedField : expectedFields) {
      switch (expectedField) {
      case 'F': {
        auto *function = arguments.takeFunction();
        llvm::errs() << "function: " << function->getName() << "\n";
        break;
      }
      case 'B': {
        auto *block = arguments.takeBlock();
        llvm::errs() << "block:\n";
        block->dump();
        break;
      }
      case 'I': {
        auto *instruction = arguments.takeInstruction();
        llvm::errs() << "instruction: ";
        instruction->dump();
        break;
      }
      case 'V': {
        auto value = arguments.takeValue();
        llvm::errs() << "value: ";
        value->dump();
        break;
      }
      case 'O': {
        auto *operand = arguments.takeOperand();
        llvm::errs() << "operand: ";
        operand->print(llvm::errs());
        break;
      }
      case 'u': {
        auto u = arguments.takeUInt();
        llvm::errs() << "uint: " << u << "\n";
        break;
      }
      case 'b': {
        auto b = arguments.takeBool();
        llvm::errs() << "bool: " << b << "\n";
        break;
      }
      case 's': {
        auto s = arguments.takeString();
        llvm::errs() << "string: " << s << "\n";
        break;
      }
      default:
        llvm_unreachable("unknown field type was expected?!");
      }
    }
  }
};

// Arguments:
// - bool: pruneDebug
// - bool: maximizeLifetimes
// - SILValue: value to canonicalize
// Dumps:
// - function after value canonicalization
struct CanonicalizeOSSALifetimeTest : UnitTest {
  CanonicalizeOSSALifetimeTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
    auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
    DominanceInfo *domTree = dominanceAnalysis->get(getFunction());
    auto pruneDebug = arguments.takeBool();
    auto maximizeLifetimes = arguments.takeBool();
    InstructionDeleter deleter;
    CanonicalizeOSSALifetime canonicalizer(pruneDebug, maximizeLifetimes, accessBlockAnalysis,
                                           domTree, deleter);
    auto value = arguments.takeValue();
    canonicalizer.canonicalizeValueLifetime(value);
    getFunction()->dump();
  }
};

// Arguments:
// - SILValue: phi
// Dumps:
// - function
// - the adjacent phis
struct VisitAdjacentReborrowsOfPhiTest : UnitTest {
  VisitAdjacentReborrowsOfPhiTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    getFunction()->dump();
    visitAdjacentReborrowsOfPhi(cast<SILPhiArgument>(arguments.takeValue()),
                                [](auto *argument) -> bool {
                                  argument->dump();
                                  return true;
                                });
  }
};

// Arguments:
// - variadic list of - instruction: a last user
// Dumps:
// - the insertion points
struct PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest : UnitTest {
  PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest(
      UnitTestRunner *pass)
      : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    PrunedLivenessBoundary boundary;
    while (arguments.hasUntaken()) {
      boundary.lastUsers.push_back(arguments.takeInstruction());
    }
    boundary.visitInsertionPoints(
        [](SILBasicBlock::iterator point) { point->dump(); });
  }
};

// Arguments: NONE
// Dumps:
// - the function
struct DumpFunction : UnitTest {
  DumpFunction(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override { getFunction()->dump(); }
};

// Arguments: NONE
// Dumps: the index of the self argument of the current function
struct FunctionGetSelfArgumentIndex : UnitTest {
  FunctionGetSelfArgumentIndex(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto index =
        SILFunction_getSelfArgumentIndex(BridgedFunction{getFunction()});
    llvm::errs() << "self argument index = " << index << "\n";
  }
};

// Arguments:
// - instruction
// Dumps:
// - instruction
// - whether it's a deinit barrier
struct IsDeinitBarrierTest : UnitTest {
  IsDeinitBarrierTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto *instruction = arguments.takeInstruction();
    auto *analysis = getAnalysis<BasicCalleeAnalysis>();
    auto isBarrier = isDeinitBarrier(instruction, analysis);
    instruction->dump();
    auto *boolString = isBarrier ? "true" : "false";
    llvm::errs() << boolString << "\n";
  }
};

struct ShrinkBorrowScopeTest : UnitTest {
  ShrinkBorrowScopeTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto instruction = arguments.takeValue();
    auto expected = arguments.takeBool();
    auto *bbi = cast<BeginBorrowInst>(instruction);
    auto *analysis = getAnalysis<BasicCalleeAnalysis>();
    SmallVector<CopyValueInst *, 4> modifiedCopyValueInsts;
    InstructionDeleter deleter(
        InstModCallbacks().onDelete([&](auto *instruction) {
          llvm::errs() << "DELETED:\n";
          instruction->dump();
        }));
    auto shrunk =
        shrinkBorrowScope(*bbi, deleter, analysis, modifiedCopyValueInsts);
    unsigned index = 0;
    for (auto *cvi : modifiedCopyValueInsts) {
      auto expectedCopy = arguments.takeValue();
      llvm::errs() << "rewritten copy " << index << ":\n";
      llvm::errs() << "expected:\n";
      expectedCopy->print(llvm::errs());
      llvm::errs() << "got:\n";
      cvi->dump();
      assert(cvi == expectedCopy);
      ++index;
    }
    assert(expected == shrunk && "didn't shrink expectedly!?");
  }
};

/// [new_tests] Add the new UnitTest subclass above this line.

class UnitTestRunner : public SILFunctionTransform {
  void printTestLifetime(bool begin, unsigned testIndex, unsigned testCount,
                         StringRef name, ArrayRef<StringRef> components) {
    StringRef word = begin ? "begin" : "end";
    llvm::errs() << word << " running test " << testIndex + 1 << " of "
                 << testCount << " on " << getFunction()->getName() << ": "
                 << name << " with: ";
    for (unsigned long index = 0, size = components.size(); index < size;
         ++index) {
      llvm::errs() << components[index];
      if (index != size - 1) {
        llvm::errs() << ", ";
      }
    }
    llvm::errs() << "\n";
  }

  template <typename Doit>
  void withTest(StringRef name, Doit doit) {
#define ADD_UNIT_TEST_SUBCLASS(STRING, SUBCLASS)                               \
  if (name == STRING) {                                                        \
    SUBCLASS it{this};                                                         \
    doit(&it);                                                                 \
    return;                                                                    \
  }

    ADD_UNIT_TEST_SUBCLASS("dump-function", DumpFunction)

    ADD_UNIT_TEST_SUBCLASS("test-specification-parsing", TestSpecificationTest)
    ADD_UNIT_TEST_SUBCLASS("canonicalize-ossa-lifetime",
                           CanonicalizeOSSALifetimeTest)
    ADD_UNIT_TEST_SUBCLASS("visit-adjacent-reborrows-of-phi",
                           VisitAdjacentReborrowsOfPhiTest)
    ADD_UNIT_TEST_SUBCLASS("function-get-self-argument-index",
                           FunctionGetSelfArgumentIndex)
    ADD_UNIT_TEST_SUBCLASS(
        "pruned-liveness-boundary-with-list-of-last-users-insertion-points",
        PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest)
    ADD_UNIT_TEST_SUBCLASS("shrink-borrow-scope", ShrinkBorrowScopeTest)
    ADD_UNIT_TEST_SUBCLASS("is-deinit-barrier", IsDeinitBarrierTest)
    /// [new_tests] Add the new mapping from string to subclass above this line.

#undef ADD_UNIT_TEST_SUBCLASS
  }

  void runTest(StringRef name, Arguments &arguments) {
    withTest(name, [&](auto *test) { test->invoke(arguments); });
  }

  void run() override {
    llvm::SmallVector<std::string, 2> testSpecifications;
    getTestSpecifications(getFunction(), testSpecifications);
    Arguments arguments;
    SmallVector<StringRef, 4> components;
    for (unsigned long index = 0, size = testSpecifications.size();
         index < size; ++index) {
      components.clear();
      arguments.clear();
      auto testSpecification = testSpecifications[index];
      test::parseTestArgumentsFromSpecification(
          getFunction(), testSpecification, arguments, components);
      auto name = arguments.takeString();
      ArrayRef<StringRef> argumentStrings = components;
      argumentStrings = argumentStrings.drop_front();
      printTestLifetime(/*begin=*/true, /*index=*/index, /*size=*/size, name,
                        argumentStrings);
      runTest(name, arguments);
      printTestLifetime(/*begin=*/false, /*index=*/index, /*size=*/size, name,
                        argumentStrings);
    }
  }
  friend class UnitTest;
};

template <typename Analysis>
Analysis *UnitTest::getAnalysis() {
  return pass->getAnalysis<Analysis>();
}

SILFunction *UnitTest::getFunction() { return getPass()->getFunction(); }

} // anonymous namespace

//===----------------------------------------------------------------------===//
//                           Top Level Entry Point
//===----------------------------------------------------------------------===//

SILTransform *swift::createUnitTestRunner() { return new UnitTestRunner(); }
<commit_msg>[Gardening] Tweaked comment.<commit_after>//===------------------------ UnitTestRunner.cpp --------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// TO ADD A NEW TEST, search in this file for [new_tests]
//
// Provides a mechanism for doing faux unit tests.  The idea is to emulate the
// basic functionality of calling a function and validating its results/effects.
//
// This is done via the test_specification instruction.  Using one or more
// instances of it in your function, you can specify which test (i.e. UnitTest
// subclass) should be run and what arguments should be provided to it.  The
// test grabs the arguments it expects out of the test::Arguments instance it is
// provided.  It calls some function or functions.  It then prints out
// interesting results.  These results can then be FileCheck'd.
//
// CASE STUDY:
// Here's an example of how it works:
// 1) A test test/SILOptimizer/interesting_functionality.sil runs this pass:
//     // RUN: %target-sil-opt -unit-test-runner %s 2>&1 | %FileCheck %s
// 2) A function in interesting_functionality.sil contains the
//    test_specification instruction.
//      sil @f : $() -> () {
//      ...
//      test_specification "my-neato-utility 43 @trace[2] @function[other_fun]"
//      ...
//      }
// 3) UnitTestRunner sees the name "my-neato-utility", instantiates the
//    corresponding UnitTest subclass MyNeatoUtilityTest, and calls ::invoke()
//    on it, passing an test::Arguments that contains
//      (43 : unsigned long, someValue : SILValue, other_fun : SILFunction *)
//
// 4) MyNeatoUtilityTest calls takeUInt(), takeValue(), and takeFunction() on
//    the test::Arguments instance.
//      auto count = arguments.takeUInt();
//      auto target = arguments.takeValue();
//      auto callee = arguments.takeFunction();
// 5) MyNeatoUtilityTest calls myNeatoUtility, passing these values along.
//      myNeatoUtility(count, target, callee);
// 6) MyNeatoUtilityTest then dumps out the current function, which was modified
//    in the process.
//      getFunction()->dump();
// 7) The test file test/SILOptimizer/interesting_functionality.sil matches the
//    expected contents of the modified function:
//    // CHECK-LABEL: sil @f
//    // CHECK-NOT:     function_ref @other_fun
//
// [new_tests] TESTING MORE FUNCTIONALITY:
//
// 1) Add a new UnitTest subclass.
//    - Add a constructor: NewTest(UnitTestRunner *pass) : UnitTest(pass) {}
//    - Implement invoke: void invoke(test::Arguments &arguments) override {...}
//    - Call the take{TYPE} methods to get the arguments you need.
//    - Call your function with those arguments.
// 2) Add a new ADD_UNIT_TEST_SUBCLASS line.
//
//===----------------------------------------------------------------------===//

#include "swift/AST/Type.h"
#include "swift/Basic/TaggedUnion.h"
#include "swift/SIL/PrunedLiveness.h"
#include "swift/SIL/SILArgumentArrayRef.h"
#include "swift/SIL/SILBasicBlock.h"
#include "swift/SIL/SILBridging.h"
#include "swift/SIL/SILFunction.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SILOptimizer/Analysis/BasicCalleeAnalysis.h"
#include "swift/SILOptimizer/PassManager/Passes.h"
#include "swift/SILOptimizer/PassManager/Transforms.h"
#include "swift/SILOptimizer/Utils/CanonicalizeBorrowScope.h"
#include "swift/SILOptimizer/Utils/CanonicalizeOSSALifetime.h"
#include "swift/SILOptimizer/Utils/InstOptUtils.h"
#include "swift/SILOptimizer/Utils/InstructionDeleter.h"
#include "swift/SILOptimizer/Utils/ParseTestSpecification.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include <iterator>
#include <memory>

using namespace swift;
using namespace swift::test;

namespace {

class UnitTestRunner;

class UnitTest {
  UnitTestRunner *pass;

protected:
  template <typename Analysis>
  Analysis *getAnalysis();
  UnitTestRunner *getPass() { return pass; }
  SILFunction *getFunction();

public:
  UnitTest(UnitTestRunner *pass) : pass(pass){};
  virtual ~UnitTest() {}
  virtual void invoke(Arguments &arguments) = 0;
};

// Arguments:
// - string: list of characters, each of which specifies subsequent arguments
//           - F: function
//           - B: block
//           - I: instruction
//           - V: value
//           - O: operand
//           - b: boolean
//           - u: unsigned
//           - s: string
// - ...
// - an argument of the type specified in the initial string
// - ...
// Dumps:
// - for each argument (after the initial string)
//   - its type
//   - something to identify the instance (mostly this means calling dump)
struct TestSpecificationTest : UnitTest {
  TestSpecificationTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto expectedFields = arguments.takeString();
    for (auto expectedField : expectedFields) {
      switch (expectedField) {
      case 'F': {
        auto *function = arguments.takeFunction();
        llvm::errs() << "function: " << function->getName() << "\n";
        break;
      }
      case 'B': {
        auto *block = arguments.takeBlock();
        llvm::errs() << "block:\n";
        block->dump();
        break;
      }
      case 'I': {
        auto *instruction = arguments.takeInstruction();
        llvm::errs() << "instruction: ";
        instruction->dump();
        break;
      }
      case 'V': {
        auto value = arguments.takeValue();
        llvm::errs() << "value: ";
        value->dump();
        break;
      }
      case 'O': {
        auto *operand = arguments.takeOperand();
        llvm::errs() << "operand: ";
        operand->print(llvm::errs());
        break;
      }
      case 'u': {
        auto u = arguments.takeUInt();
        llvm::errs() << "uint: " << u << "\n";
        break;
      }
      case 'b': {
        auto b = arguments.takeBool();
        llvm::errs() << "bool: " << b << "\n";
        break;
      }
      case 's': {
        auto s = arguments.takeString();
        llvm::errs() << "string: " << s << "\n";
        break;
      }
      default:
        llvm_unreachable("unknown field type was expected?!");
      }
    }
  }
};

// Arguments:
// - bool: pruneDebug
// - bool: maximizeLifetimes
// - SILValue: value to canonicalize
// Dumps:
// - function after value canonicalization
struct CanonicalizeOSSALifetimeTest : UnitTest {
  CanonicalizeOSSALifetimeTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto *accessBlockAnalysis = getAnalysis<NonLocalAccessBlockAnalysis>();
    auto *dominanceAnalysis = getAnalysis<DominanceAnalysis>();
    DominanceInfo *domTree = dominanceAnalysis->get(getFunction());
    auto pruneDebug = arguments.takeBool();
    auto maximizeLifetimes = arguments.takeBool();
    InstructionDeleter deleter;
    CanonicalizeOSSALifetime canonicalizer(pruneDebug, maximizeLifetimes, accessBlockAnalysis,
                                           domTree, deleter);
    auto value = arguments.takeValue();
    canonicalizer.canonicalizeValueLifetime(value);
    getFunction()->dump();
  }
};

// Arguments:
// - SILValue: phi
// Dumps:
// - function
// - the adjacent phis
struct VisitAdjacentReborrowsOfPhiTest : UnitTest {
  VisitAdjacentReborrowsOfPhiTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    getFunction()->dump();
    visitAdjacentReborrowsOfPhi(cast<SILPhiArgument>(arguments.takeValue()),
                                [](auto *argument) -> bool {
                                  argument->dump();
                                  return true;
                                });
  }
};

// Arguments:
// - variadic list of - instruction: a last user
// Dumps:
// - the insertion points
struct PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest : UnitTest {
  PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest(
      UnitTestRunner *pass)
      : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    PrunedLivenessBoundary boundary;
    while (arguments.hasUntaken()) {
      boundary.lastUsers.push_back(arguments.takeInstruction());
    }
    boundary.visitInsertionPoints(
        [](SILBasicBlock::iterator point) { point->dump(); });
  }
};

// Arguments: NONE
// Dumps:
// - the function
struct DumpFunction : UnitTest {
  DumpFunction(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override { getFunction()->dump(); }
};

// Arguments: NONE
// Dumps: the index of the self argument of the current function
struct FunctionGetSelfArgumentIndex : UnitTest {
  FunctionGetSelfArgumentIndex(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto index =
        SILFunction_getSelfArgumentIndex(BridgedFunction{getFunction()});
    llvm::errs() << "self argument index = " << index << "\n";
  }
};

// Arguments:
// - instruction
// Dumps:
// - instruction
// - whether it's a deinit barrier
struct IsDeinitBarrierTest : UnitTest {
  IsDeinitBarrierTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto *instruction = arguments.takeInstruction();
    auto *analysis = getAnalysis<BasicCalleeAnalysis>();
    auto isBarrier = isDeinitBarrier(instruction, analysis);
    instruction->dump();
    auto *boolString = isBarrier ? "true" : "false";
    llvm::errs() << boolString << "\n";
  }
};

struct ShrinkBorrowScopeTest : UnitTest {
  ShrinkBorrowScopeTest(UnitTestRunner *pass) : UnitTest(pass) {}
  void invoke(Arguments &arguments) override {
    auto instruction = arguments.takeValue();
    auto expected = arguments.takeBool();
    auto *bbi = cast<BeginBorrowInst>(instruction);
    auto *analysis = getAnalysis<BasicCalleeAnalysis>();
    SmallVector<CopyValueInst *, 4> modifiedCopyValueInsts;
    InstructionDeleter deleter(
        InstModCallbacks().onDelete([&](auto *instruction) {
          llvm::errs() << "DELETED:\n";
          instruction->dump();
        }));
    auto shrunk =
        shrinkBorrowScope(*bbi, deleter, analysis, modifiedCopyValueInsts);
    unsigned index = 0;
    for (auto *cvi : modifiedCopyValueInsts) {
      auto expectedCopy = arguments.takeValue();
      llvm::errs() << "rewritten copy " << index << ":\n";
      llvm::errs() << "expected:\n";
      expectedCopy->print(llvm::errs());
      llvm::errs() << "got:\n";
      cvi->dump();
      assert(cvi == expectedCopy);
      ++index;
    }
    assert(expected == shrunk && "didn't shrink expectedly!?");
  }
};

/// [new_tests] Add the new UnitTest subclass above this line.

class UnitTestRunner : public SILFunctionTransform {
  void printTestLifetime(bool begin, unsigned testIndex, unsigned testCount,
                         StringRef name, ArrayRef<StringRef> components) {
    StringRef word = begin ? "begin" : "end";
    llvm::errs() << word << " running test " << testIndex + 1 << " of "
                 << testCount << " on " << getFunction()->getName() << ": "
                 << name << " with: ";
    for (unsigned long index = 0, size = components.size(); index < size;
         ++index) {
      llvm::errs() << components[index];
      if (index != size - 1) {
        llvm::errs() << ", ";
      }
    }
    llvm::errs() << "\n";
  }

  template <typename Doit>
  void withTest(StringRef name, Doit doit) {
#define ADD_UNIT_TEST_SUBCLASS(STRING, SUBCLASS)                               \
  if (name == STRING) {                                                        \
    SUBCLASS it{this};                                                         \
    doit(&it);                                                                 \
    return;                                                                    \
  }

    ADD_UNIT_TEST_SUBCLASS("dump-function", DumpFunction)

    ADD_UNIT_TEST_SUBCLASS("test-specification-parsing", TestSpecificationTest)
    ADD_UNIT_TEST_SUBCLASS("canonicalize-ossa-lifetime",
                           CanonicalizeOSSALifetimeTest)
    ADD_UNIT_TEST_SUBCLASS("visit-adjacent-reborrows-of-phi",
                           VisitAdjacentReborrowsOfPhiTest)
    ADD_UNIT_TEST_SUBCLASS("function-get-self-argument-index",
                           FunctionGetSelfArgumentIndex)
    ADD_UNIT_TEST_SUBCLASS(
        "pruned-liveness-boundary-with-list-of-last-users-insertion-points",
        PrunedLivenessBoundaryWithListOfLastUsersInsertionPointsTest)
    ADD_UNIT_TEST_SUBCLASS("shrink-borrow-scope", ShrinkBorrowScopeTest)
    ADD_UNIT_TEST_SUBCLASS("is-deinit-barrier", IsDeinitBarrierTest)
    /// [new_tests] Add the new mapping from string to subclass above this line.

#undef ADD_UNIT_TEST_SUBCLASS
  }

  void runTest(StringRef name, Arguments &arguments) {
    withTest(name, [&](auto *test) { test->invoke(arguments); });
  }

  void run() override {
    llvm::SmallVector<std::string, 2> testSpecifications;
    getTestSpecifications(getFunction(), testSpecifications);
    Arguments arguments;
    SmallVector<StringRef, 4> components;
    for (unsigned long index = 0, size = testSpecifications.size();
         index < size; ++index) {
      components.clear();
      arguments.clear();
      auto testSpecification = testSpecifications[index];
      test::parseTestArgumentsFromSpecification(
          getFunction(), testSpecification, arguments, components);
      auto name = arguments.takeString();
      ArrayRef<StringRef> argumentStrings = components;
      argumentStrings = argumentStrings.drop_front();
      printTestLifetime(/*begin=*/true, /*index=*/index, /*size=*/size, name,
                        argumentStrings);
      runTest(name, arguments);
      printTestLifetime(/*begin=*/false, /*index=*/index, /*size=*/size, name,
                        argumentStrings);
    }
  }
  friend class UnitTest;
};

template <typename Analysis>
Analysis *UnitTest::getAnalysis() {
  return pass->getAnalysis<Analysis>();
}

SILFunction *UnitTest::getFunction() { return getPass()->getFunction(); }

} // anonymous namespace

//===----------------------------------------------------------------------===//
//                           Top Level Entry Point
//===----------------------------------------------------------------------===//

SILTransform *swift::createUnitTestRunner() { return new UnitTestRunner(); }
<|endoftext|>


<commit_before>/*
** This file is part of libyuni, a cross-platform C++ framework (http://libyuni.org).
**
** This Source Code Form is subject to the terms of the Mozilla Public License
** v.2.0. If a copy of the MPL was not distributed with this file, You can
** obtain one at http://mozilla.org/MPL/2.0/.
**
** github: https://github.com/libyuni/libyuni/
** gitlab: https://gitlab.com/libyuni/libyuni/ (mirror)
*/
#pragma once
#include "traits.h"

namespace Yuni
{
namespace Private
{
namespace CStringImpl
{

	template<uint ChunkSizeT, bool ExpandableT>
	inline Data<ChunkSizeT,ExpandableT>::Data() :
		size(),
		capacity(),
		data(NULL)
	{}

	template<uint ChunkSizeT, bool ExpandableT>
	Data<ChunkSizeT,ExpandableT>::Data(const Data<ChunkSizeT,ExpandableT>& rhs) :
		size(rhs.size),
		capacity(rhs.size),
		data(NULL)
	{
		if (size)
		{
			if (chunkSize != 0)
			{
				capacity += static_cast<uint>(zeroTerminated);
				data = reinterpret_cast<C*>(::malloc(sizeof(C) * static_cast<uint>(capacity)));
				YUNI_MEMCPY(const_cast<void*>(static_cast<const void*>(data)), static_cast<uint>(capacity), rhs.data, sizeof(C) * size);
				if (static_cast<uint>(zeroTerminated))
					(const_cast<char*>(data))[size] = C();
			}
			else
			{
				// this string is a string adapter
				data = rhs.data;
			}
		}
	}

	# ifdef YUNI_HAS_CPP_MOVE
	template<uint ChunkSizeT, bool ExpandableT>
	inline Data<ChunkSizeT,ExpandableT>::Data(Data&& rhs) :
		size(rhs.size),
		capacity(rhs.size),
		data(rhs.data)
	{
		rhs.size = 0;
		rhs.capacity = 0;
		rhs.data = nullptr;
	}
	# endif

	template<uint ChunkSizeT, bool ExpandableT>
	inline Data<ChunkSizeT,ExpandableT>::~Data()
	{
		// Release the internal buffer if allocated
		// The string is a string adapter only if the chunk size if null
		// When the string is an adapter, the variable is const
		if (chunkSize != 0)
			::free(const_cast<void*>(static_cast<const void*>(data)));
	}

	# ifdef YUNI_HAS_CPP_MOVE
	template<uint ChunkSizeT, bool ExpandableT>
	inline Data<ChunkSizeT,ExpandableT>& Data<ChunkSizeT,ExpandableT>::operator = (Data&& rhs)
	{
		// Release the internal buffer if allocated
		// The string is a string adapter only if the chunk size if null
		// When the string is an adapter, the variable is const
		if (chunkSize != 0)
			::free(const_cast<void*>(static_cast<const void*>(data)));

		size = rhs.size;
		capacity = rhs.capacity;
		data = rhs.data;

		rhs.size = 0;
		rhs.capacity = 0;
		rhs.data = nullptr;
		return *this;
	}
	# endif

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::adapt(const char* const cstring)
	{
		data = const_cast<char*>(cstring);
		capacity = size = (data ? static_cast<Size>(::strlen(data)) : 0);
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::adapt(const char* const cstring, Size length)
	{
		data = const_cast<char*>(cstring);
		capacity = size = length;
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::clear()
	{
		if (static_cast<uint>(zeroTerminated))
		{
			if (size)
			{
				size = 0;
				*(const_cast<char*>(data)) = C();
			}
		}
		else
			size = 0;
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::forgetContent()
	{
		capacity = 0;
		data = nullptr; // forget me !
		if (static_cast<uint>(zeroTerminated))
		{
			if (size)
			{
				size = 0;
				*(const_cast<char*>(data)) = C();
			}
		}
		else
			size = 0;
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::shrink()
	{
		if (data)
		{
			if (size)
			{
				capacity = size + static_cast<uint>(zeroTerminated);
				data = reinterpret_cast<char*>(::realloc(const_cast<char*>(data), static_cast<uint>(capacity)));
			}
			else
			{
				capacity = 0;
				::free(const_cast<void*>(static_cast<const void*>(data)));
				data = nullptr;
			}
		}
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::insert(Size offset, const C* const buffer, const Size len)
	{
		// Reserving enough space to insert the buffer
		reserve(len + size + static_cast<uint>(zeroTerminated));
		// Move the existing block of data
		(void)::memmove(const_cast<char*>(data) + sizeof(C) * (offset + len),
			const_cast<char*>(data) + sizeof(C) * (offset), sizeof(C) * (size - offset));
		// Copying the given buffer
		YUNI_MEMCPY(const_cast<char*>(data) + sizeof(C) * (offset), static_cast<uint>(capacity), buffer, sizeof(C) * len);
		// Updating the size
		size += len;
		// zero-terminated
		if (static_cast<uint>(zeroTerminated))
			(const_cast<char*>(data))[size] = C();
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::put(const C rhs)
	{
		// Making sure that we have enough space
		reserve(size + 1 + static_cast<uint>(zeroTerminated));
		// Raw copy
		(const_cast<char*>(data))[size] = rhs;
		// New size
		++size;
		if (static_cast<uint>(zeroTerminated))
			(const_cast<char*>(data))[size] = C();
	}

	template<uint ChunkSizeT, bool ExpandableT>
	void
	Data<ChunkSizeT,ExpandableT>::reserve(Size mincapacity)
	{
		if (adapter)
			return;
		mincapacity += static_cast<uint>(zeroTerminated);
		if (static_cast<uint>(capacity) < mincapacity)
		{
			// This loop may be a little faster than the following replacement code :
			// static_cast<uint>(capacity) = minstatic_cast<uint>(capacity) + minstatic_cast<uint>(capacity) % chunkSize;
			// Especially when chunkSize is not a power of 2
			Size newcapacity = static_cast<uint>(capacity);
			do
			{
				// Increase the static_cast<uint>(capacity) until we have enough space
				newcapacity += chunkSize;
			}
			while (newcapacity < mincapacity);

			// Realloc the internal buffer
			C* newdata = reinterpret_cast<C*>(::realloc(const_cast<char*>(data), (sizeof(C) * newcapacity)));
			// The returned value can be NULL
			if (!newdata)
				throw "Yuni::CString: Impossible to realloc";
			{
				data = newdata;
				capacity = newcapacity;
				if (static_cast<uint>(zeroTerminated))
					(const_cast<char*>(data))[size] = C();
			}
		}
	}

	template<uint ChunkSizeT>
	typename Data<ChunkSizeT,false>::Size
	Data<ChunkSizeT,false>::assignWithoutChecking(const C* const block,
		Size blockSize)
	{
		// We have to trunk the size if we are outer limits
		// This condition is a little faster than the folowing replacement code :
		// blockSize = Math::Min<Size>(blockSize, static_cast<uint>(capacity) - size);
		//
		// We have better performance if the two cases have their own code block
		// (perhaps because of the constant values)
		//
		if (blockSize > static_cast<uint>(capacity))
		{
			// The new blocksize is actually the static_cast<uint>(capacity) itself
			// The static_cast<uint>(capacity) can not be null
			// Raw copy
			YUNI_MEMCPY(const_cast<char*>(data), static_cast<uint>(capacity), block, sizeof(C) * static_cast<uint>(capacity));
			// New size
			size = static_cast<uint>(capacity);
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[static_cast<uint>(capacity)] = C();
			return static_cast<uint>(capacity);
		}
		// else
		{
			// Raw copy
			YUNI_MEMCPY(const_cast<char*>(data), static_cast<uint>(capacity), block, sizeof(C) * blockSize);
			// New size
			size = blockSize;
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[size] = C();
			return blockSize;
		}
	}

	template<uint ChunkSizeT>
	typename Data<ChunkSizeT,false>::Size
	Data<ChunkSizeT,false>::appendWithoutChecking(const C* const block, Size blockSize)
	{
		// We have to trunk the size if we are outer limits
		// This condition is a little faster than the folowing replacement code :
		// blockSize = Math::Min<Size>(blockSize, static_cast<uint>(capacity) - size);
		//
		// We have better performance if the two cases have their own code block
		// (perhaps because of the constant values)
		//
		if (blockSize + size > static_cast<uint>(capacity))
		{
			// Computing the new block size to copy
			blockSize = static_cast<uint>(capacity) - size;
			// The performance are a bit better if we interupt the process sooner
			if (!blockSize)
				return 0;
			// Raw copy
			YUNI_MEMCPY(data + size * sizeof(C), static_cast<uint>(capacity), block, sizeof(C) * blockSize);
			// New size
			size = static_cast<uint>(capacity);
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[static_cast<uint>(capacity)] = C();
			return blockSize;
		}
		// else
		{
			// Raw copy
			YUNI_MEMCPY(data + size * sizeof(C), static_cast<uint>(capacity), block, sizeof(C) * blockSize);
			// New size
			size += blockSize;
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[size] = C();
			return blockSize;
		}
	}

	template<uint ChunkSizeT>
	inline typename Data<ChunkSizeT,false>::Size
	Data<ChunkSizeT,false>::assignWithoutChecking(const C c)
	{
		data[0] = c;
		size = 1;
		if (static_cast<uint>(zeroTerminated))
			(const_cast<char*>(data))[1] = C();
		return 1;
	}

	template<uint ChunkSizeT>
	typename Data<ChunkSizeT,false>::Size
	Data<ChunkSizeT,false>::appendWithoutChecking(const C c)
	{
		if (YUNI_UNLIKELY(size == static_cast<uint>(capacity)))
			return 0;

		data[size] = c;
		++size;
		if (static_cast<uint>(zeroTerminated))
			(const_cast<char*>(data))[size] = C();
		return 1;
	}

	template<uint ChunkSizeT>
	void
	Data<ChunkSizeT,false>::put(const C rhs)
	{
		// Making sure that we have enough space
		if (size != static_cast<uint>(capacity))
		{
			// Raw copy
			data[size] = rhs;
			// New size
			++size;
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[size] = C();
		}
	}

	template<uint ChunkSizeT>
	void Data<ChunkSizeT,false>::swap(Data<ChunkSizeT,false>& rhs)
	{
		Data<ChunkSizeT,false> tmp(*this);
		assignWithoutChecking(rhs.data, rhs.size);
		rhs.assignWithoutChecking(tmp.data, tmp.size);
	}

} // namespace CStringImpl
} // namespace Private
} // namespace Yuni
<commit_msg>String::forgetContent: fixed invalid write to null pointer<commit_after>/*
** This file is part of libyuni, a cross-platform C++ framework (http://libyuni.org).
**
** This Source Code Form is subject to the terms of the Mozilla Public License
** v.2.0. If a copy of the MPL was not distributed with this file, You can
** obtain one at http://mozilla.org/MPL/2.0/.
**
** github: https://github.com/libyuni/libyuni/
** gitlab: https://gitlab.com/libyuni/libyuni/ (mirror)
*/
#pragma once
#include "traits.h"

namespace Yuni
{
namespace Private
{
namespace CStringImpl
{

	template<uint ChunkSizeT, bool ExpandableT>
	inline Data<ChunkSizeT,ExpandableT>::Data() :
		size(),
		capacity(),
		data(NULL)
	{}

	template<uint ChunkSizeT, bool ExpandableT>
	Data<ChunkSizeT,ExpandableT>::Data(const Data<ChunkSizeT,ExpandableT>& rhs) :
		size(rhs.size),
		capacity(rhs.size),
		data(NULL)
	{
		if (size)
		{
			if (chunkSize != 0)
			{
				capacity += static_cast<uint>(zeroTerminated);
				data = reinterpret_cast<C*>(::malloc(sizeof(C) * static_cast<uint>(capacity)));
				YUNI_MEMCPY(const_cast<void*>(static_cast<const void*>(data)), static_cast<uint>(capacity), rhs.data, sizeof(C) * size);
				if (static_cast<uint>(zeroTerminated))
					(const_cast<char*>(data))[size] = C();
			}
			else
			{
				// this string is a string adapter
				data = rhs.data;
			}
		}
	}

	# ifdef YUNI_HAS_CPP_MOVE
	template<uint ChunkSizeT, bool ExpandableT>
	inline Data<ChunkSizeT,ExpandableT>::Data(Data&& rhs) :
		size(rhs.size),
		capacity(rhs.size),
		data(rhs.data)
	{
		rhs.size = 0;
		rhs.capacity = 0;
		rhs.data = nullptr;
	}
	# endif

	template<uint ChunkSizeT, bool ExpandableT>
	inline Data<ChunkSizeT,ExpandableT>::~Data()
	{
		// Release the internal buffer if allocated
		// The string is a string adapter only if the chunk size if null
		// When the string is an adapter, the variable is const
		if (chunkSize != 0)
			::free(const_cast<void*>(static_cast<const void*>(data)));
	}

	# ifdef YUNI_HAS_CPP_MOVE
	template<uint ChunkSizeT, bool ExpandableT>
	inline Data<ChunkSizeT,ExpandableT>& Data<ChunkSizeT,ExpandableT>::operator = (Data&& rhs)
	{
		// Release the internal buffer if allocated
		// The string is a string adapter only if the chunk size if null
		// When the string is an adapter, the variable is const
		if (chunkSize != 0)
			::free(const_cast<void*>(static_cast<const void*>(data)));

		size = rhs.size;
		capacity = rhs.capacity;
		data = rhs.data;

		rhs.size = 0;
		rhs.capacity = 0;
		rhs.data = nullptr;
		return *this;
	}
	# endif

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::adapt(const char* const cstring)
	{
		data = const_cast<char*>(cstring);
		capacity = size = (data ? static_cast<Size>(::strlen(data)) : 0);
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::adapt(const char* const cstring, Size length)
	{
		data = const_cast<char*>(cstring);
		capacity = size = length;
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::clear()
	{
		if (static_cast<uint>(zeroTerminated))
		{
			if (size)
			{
				size = 0;
				*(const_cast<char*>(data)) = C();
			}
		}
		else
			size = 0;
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::forgetContent()
	{
		capacity = 0;
		if (static_cast<uint>(zeroTerminated))
		{
			if (size)
			{
				size = 0;
				*(const_cast<char*>(data)) = C();
			}
		}
		else
			size = 0;
		data = nullptr; // forget me !
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::shrink()
	{
		if (data)
		{
			if (size)
			{
				capacity = size + static_cast<uint>(zeroTerminated);
				data = reinterpret_cast<char*>(::realloc(const_cast<char*>(data), static_cast<uint>(capacity)));
			}
			else
			{
				capacity = 0;
				::free(const_cast<void*>(static_cast<const void*>(data)));
				data = nullptr;
			}
		}
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::insert(Size offset, const C* const buffer, const Size len)
	{
		// Reserving enough space to insert the buffer
		reserve(len + size + static_cast<uint>(zeroTerminated));
		// Move the existing block of data
		(void)::memmove(const_cast<char*>(data) + sizeof(C) * (offset + len),
			const_cast<char*>(data) + sizeof(C) * (offset), sizeof(C) * (size - offset));
		// Copying the given buffer
		YUNI_MEMCPY(const_cast<char*>(data) + sizeof(C) * (offset), static_cast<uint>(capacity), buffer, sizeof(C) * len);
		// Updating the size
		size += len;
		// zero-terminated
		if (static_cast<uint>(zeroTerminated))
			(const_cast<char*>(data))[size] = C();
	}

	template<uint ChunkSizeT, bool ExpandableT>
	inline void
	Data<ChunkSizeT,ExpandableT>::put(const C rhs)
	{
		// Making sure that we have enough space
		reserve(size + 1 + static_cast<uint>(zeroTerminated));
		// Raw copy
		(const_cast<char*>(data))[size] = rhs;
		// New size
		++size;
		if (static_cast<uint>(zeroTerminated))
			(const_cast<char*>(data))[size] = C();
	}

	template<uint ChunkSizeT, bool ExpandableT>
	void
	Data<ChunkSizeT,ExpandableT>::reserve(Size mincapacity)
	{
		if (adapter)
			return;
		mincapacity += static_cast<uint>(zeroTerminated);
		if (static_cast<uint>(capacity) < mincapacity)
		{
			// This loop may be a little faster than the following replacement code :
			// static_cast<uint>(capacity) = minstatic_cast<uint>(capacity) + minstatic_cast<uint>(capacity) % chunkSize;
			// Especially when chunkSize is not a power of 2
			Size newcapacity = static_cast<uint>(capacity);
			do
			{
				// Increase the static_cast<uint>(capacity) until we have enough space
				newcapacity += chunkSize;
			}
			while (newcapacity < mincapacity);

			// Realloc the internal buffer
			C* newdata = reinterpret_cast<C*>(::realloc(const_cast<char*>(data), (sizeof(C) * newcapacity)));
			// The returned value can be NULL
			if (!newdata)
				throw "Yuni::CString: Impossible to realloc";
			{
				data = newdata;
				capacity = newcapacity;
				if (static_cast<uint>(zeroTerminated))
					(const_cast<char*>(data))[size] = C();
			}
		}
	}

	template<uint ChunkSizeT>
	typename Data<ChunkSizeT,false>::Size
	Data<ChunkSizeT,false>::assignWithoutChecking(const C* const block,
		Size blockSize)
	{
		// We have to trunk the size if we are outer limits
		// This condition is a little faster than the folowing replacement code :
		// blockSize = Math::Min<Size>(blockSize, static_cast<uint>(capacity) - size);
		//
		// We have better performance if the two cases have their own code block
		// (perhaps because of the constant values)
		//
		if (blockSize > static_cast<uint>(capacity))
		{
			// The new blocksize is actually the static_cast<uint>(capacity) itself
			// The static_cast<uint>(capacity) can not be null
			// Raw copy
			YUNI_MEMCPY(const_cast<char*>(data), static_cast<uint>(capacity), block, sizeof(C) * static_cast<uint>(capacity));
			// New size
			size = static_cast<uint>(capacity);
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[static_cast<uint>(capacity)] = C();
			return static_cast<uint>(capacity);
		}
		// else
		{
			// Raw copy
			YUNI_MEMCPY(const_cast<char*>(data), static_cast<uint>(capacity), block, sizeof(C) * blockSize);
			// New size
			size = blockSize;
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[size] = C();
			return blockSize;
		}
	}

	template<uint ChunkSizeT>
	typename Data<ChunkSizeT,false>::Size
	Data<ChunkSizeT,false>::appendWithoutChecking(const C* const block, Size blockSize)
	{
		// We have to trunk the size if we are outer limits
		// This condition is a little faster than the folowing replacement code :
		// blockSize = Math::Min<Size>(blockSize, static_cast<uint>(capacity) - size);
		//
		// We have better performance if the two cases have their own code block
		// (perhaps because of the constant values)
		//
		if (blockSize + size > static_cast<uint>(capacity))
		{
			// Computing the new block size to copy
			blockSize = static_cast<uint>(capacity) - size;
			// The performance are a bit better if we interupt the process sooner
			if (!blockSize)
				return 0;
			// Raw copy
			YUNI_MEMCPY(data + size * sizeof(C), static_cast<uint>(capacity), block, sizeof(C) * blockSize);
			// New size
			size = static_cast<uint>(capacity);
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[static_cast<uint>(capacity)] = C();
			return blockSize;
		}
		// else
		{
			// Raw copy
			YUNI_MEMCPY(data + size * sizeof(C), static_cast<uint>(capacity), block, sizeof(C) * blockSize);
			// New size
			size += blockSize;
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[size] = C();
			return blockSize;
		}
	}

	template<uint ChunkSizeT>
	inline typename Data<ChunkSizeT,false>::Size
	Data<ChunkSizeT,false>::assignWithoutChecking(const C c)
	{
		data[0] = c;
		size = 1;
		if (static_cast<uint>(zeroTerminated))
			(const_cast<char*>(data))[1] = C();
		return 1;
	}

	template<uint ChunkSizeT>
	typename Data<ChunkSizeT,false>::Size
	Data<ChunkSizeT,false>::appendWithoutChecking(const C c)
	{
		if (YUNI_UNLIKELY(size == static_cast<uint>(capacity)))
			return 0;

		data[size] = c;
		++size;
		if (static_cast<uint>(zeroTerminated))
			(const_cast<char*>(data))[size] = C();
		return 1;
	}

	template<uint ChunkSizeT>
	void
	Data<ChunkSizeT,false>::put(const C rhs)
	{
		// Making sure that we have enough space
		if (size != static_cast<uint>(capacity))
		{
			// Raw copy
			data[size] = rhs;
			// New size
			++size;
			if (static_cast<uint>(zeroTerminated))
				(const_cast<char*>(data))[size] = C();
		}
	}

	template<uint ChunkSizeT>
	void Data<ChunkSizeT,false>::swap(Data<ChunkSizeT,false>& rhs)
	{
		Data<ChunkSizeT,false> tmp(*this);
		assignWithoutChecking(rhs.data, rhs.size);
		rhs.assignWithoutChecking(tmp.data, tmp.size);
	}

} // namespace CStringImpl
} // namespace Private
} // namespace Yuni
<|endoftext|>


<commit_before>#include "gl/mesh_loader.hpp"
#include "core/debug/assert.hpp"
#include "core/core.hpp"
#include "core/resource_manager.hpp"

namespace tz::gl
{
	tz::gl::IndexedMesh load_mesh(const std::string& filename)
	{
		std::string full_path = tz::core::res().get_path() + filename;
		tz::ext::assimp::Scene scene{full_path};
		topaz_assert(scene.size_meshes() == 1, "tz::gl::load_mesh(", full_path, "): File must contain exactly one mesh to be loaded this way. This file contains ", scene.size_meshes(), " meshes...");
		const aiMesh* ass_mesh = scene.get_mesh();

		tz::gl::IndexedMesh mesh;

		// Chuck in all those vertices
		for(unsigned int i = 0; i < ass_mesh->mNumVertices; i++)
		{
			tz::gl::Vertex v;
			const aiVector3D& pos = ass_mesh->mVertices[i];
			v.position = {{pos.x, pos.y, pos.z}};

			if(ass_mesh->HasTextureCoords(i))
			{
				const aiVector3D* texcoord = ass_mesh->mTextureCoords[i];
				v.texcoord = {{texcoord->x, texcoord->y}};
			}

			const aiVector3D& norm = ass_mesh->mNormals[i];
			v.normal = {{norm.x, norm.y, norm.z}};

			const aiVector3D& tang = ass_mesh->mTangents[i];
			v.tangent = {{tang.x, tang.y, tang.z}};

			const aiVector3D& bitang = ass_mesh->mBitangents[i];
			v.bitangent = {{bitang.x, bitang.y, bitang.z}};

			mesh.vertices.push_back(std::move(v));
		}

		for(unsigned int i = 0; i < ass_mesh->mNumFaces; i++)
		{
			const aiFace& face = ass_mesh->mFaces[i];
			mesh.indices.push_back(face.mIndices[0]);
			mesh.indices.push_back(face.mIndices[1]);
			mesh.indices.push_back(face.mIndices[2]);
		}

		return mesh;
	}
}<commit_msg>* Fixed an issue where texcoords were not being imported properly from mesh_loader<commit_after>#include "gl/mesh_loader.hpp"
#include "core/debug/assert.hpp"
#include "core/core.hpp"
#include "core/resource_manager.hpp"

namespace tz::gl
{
	tz::gl::IndexedMesh load_mesh(const std::string& filename)
	{
		std::string full_path = tz::core::res().get_path() + filename;
		tz::ext::assimp::Scene scene{full_path};
		topaz_assert(scene.size_meshes() == 1, "tz::gl::load_mesh(", full_path, "): File must contain exactly one mesh to be loaded this way. This file contains ", scene.size_meshes(), " meshes...");
		const aiMesh* ass_mesh = scene.get_mesh();

		tz::gl::IndexedMesh mesh;
		#if TOPAZ_DEBUG
		std::vector<tz::Vec2> debug_texcoord_list;
		#endif

		// Chuck in all those vertices
		for(unsigned int i = 0; i < ass_mesh->mNumVertices; i++)
		{
			tz::gl::Vertex v;
			const aiVector3D& pos = ass_mesh->mVertices[i];
			v.position = {{pos.x, pos.y, pos.z}};

			if(ass_mesh->HasTextureCoords(0))
			{
				aiVector3D texcoord = ass_mesh->mTextureCoords[0][i];
				v.texcoord = {{texcoord.x, texcoord.y}};
				#if TOPAZ_DEBUG
				debug_texcoord_list.push_back(v.texcoord);
				#endif
			}

			const aiVector3D& norm = ass_mesh->mNormals[i];
			v.normal = {{norm.x, norm.y, norm.z}};

			const aiVector3D& tang = ass_mesh->mTangents[i];
			v.tangent = {{tang.x, tang.y, tang.z}};

			const aiVector3D& bitang = ass_mesh->mBitangents[i];
			v.bitangent = {{bitang.x, bitang.y, bitang.z}};

			mesh.vertices.push_back(std::move(v));
		}

		for(unsigned int i = 0; i < ass_mesh->mNumFaces; i++)
		{
			const aiFace& face = ass_mesh->mFaces[i];
			mesh.indices.push_back(face.mIndices[0]);
			mesh.indices.push_back(face.mIndices[1]);
			mesh.indices.push_back(face.mIndices[2]);
		}

		return mesh;
	}
}<|endoftext|>