<commit_before>#include "stdafx.h"
#include "ContextEnumeratorTest.h"
#include "ContextEnumerator.h"

TEST_F(ContextEnumeratorTest, DegenerateEnumeration) {
  size_t ct = 0;
  for(const auto& cur : ContextEnumerator(std::shared_ptr<CoreContext>(nullptr))) {
    ASSERT_TRUE(!!cur.get()) << "Context enumerator incorrectly enumerated a null context pointer";
    ct++;
  }
  ASSERT_EQ(0UL, ct) << "An empty enumerator unexpectedly enumerated one or more entries";
}

TEST_F(ContextEnumeratorTest, TrivialEnumeration) {
  size_t ct = 0;
  for(const auto& cur : ContextEnumerator(AutoCurrentContext()))
    ct++;
  ASSERT_EQ(1UL, ct) << "Context enumerator failed to enumerate a context with no children";
}

TEST_F(ContextEnumeratorTest, VerifySimpleEnumeration) {
  // Create a pair of descendant contexts, verify we don't accidentally hit these when enumerating children
  AutoCreateContext outer;
  AutoCreateContext inner;
  CurrentContextPusher pshr(inner);

  // Add a few children to the current context:
  AutoCreateContext c1;
  AutoCreateContext c2;
  AutoCreateContext c3;
  AutoCreateContext c4;

  std::unordered_set<std::shared_ptr<CoreContext>> allCtxts;
  allCtxts.insert(AutoCurrentContext());
  allCtxts.insert(c1);
  allCtxts.insert(c2);
  allCtxts.insert(c3);
  allCtxts.insert(c4);

  // Create the enumerator, verify we get the expected count:
  size_t nContexts = 0;
  for(const std::shared_ptr<CoreContext>& cur : CurrentContextEnumerator()) {
    ASSERT_TRUE(!!allCtxts.count(cur)) << "Context enumerator accidentally enumerated a context not rooted in the specified parent";

    allCtxts.erase(cur);
    nContexts++;
  }

  ASSERT_EQ(5UL, nContexts) << "Context enumerator did not encounter the number of expected children";
  ASSERT_TRUE(allCtxts.empty()) << "Context enumerator did not encounter all children as expected";
}

TEST_F(ContextEnumeratorTest, SimpleRemovalInterference) {
  static const size_t nChildren = 5;

  // Create a few contexts which we intend to destroy as we go along:
  std::unordered_set<std::shared_ptr<CoreContext>> contexts;
  for(size_t i = nChildren; i--;)
    contexts.insert(AutoCreateContext());

  // Also add ourselves:
  contexts.insert(AutoCurrentContext());

  // Enumerate contexts, and remove them from the set:
  size_t nRemoved = 0;
  for(const auto& cur : CurrentContextEnumerator()) {
    ASSERT_TRUE(!!contexts.count(cur)) << "Failed to find a context enumerated by the context enumerator";
    contexts.erase(cur);
    nRemoved++;
  }

  // Verify we got the number removed we expected:
  ASSERT_EQ(nChildren + 1UL, nRemoved) << "Context enumerator did not remove the expected number of children";
}

TEST_F(ContextEnumeratorTest, ComplexRemovalInterference) {
  static const size_t nChildren = 50;

  // This time we use a vector, and we pop from the back of the vector unpredictably:
  std::vector<std::shared_ptr<CoreContext>> children(nChildren);
  for(size_t i = nChildren; i--;)
    children.push_back(AutoCreateContext());

  // Shuffle the collection to prevent the order here from being equivalent to the order in the context
  std::random_shuffle(children.begin(), children.end());

  // These are the elements actually encountered in the enumeration, held here to prevent expiration in the
  // event that we enumerate a context which should have already been evicted
  std::unordered_set<std::shared_ptr<CoreContext>> enumerated;

  // These are the elements eliminated from the vector.  By the time we're done the should all be expired.
  std::vector<std::weak_ptr<CoreContext>> eliminated;

  // Go through the enumeration, the totals should line up by the time we're done:
  for(const auto& cur : CurrentContextEnumerator()) {
    enumerated.insert(cur);

    // Pull off the last element:
    auto removed = children.back();
    children.pop_back();

    // If we haven't enumerated this element already, we want to mark it as eliminated-before-enumerated
    if(!enumerated.count(removed))
      eliminated.push_back(removed);
  }

  // Now verify that nothing we eliminated was enumerated:
  for(auto& cur : eliminated)
    ASSERT_TRUE(cur.expired()) << "Found an element that was iterated after it should have been unreachable";
}<commit_msg>Adding sibling traversal tests<commit_after>#include "stdafx.h"
#include "ContextEnumeratorTest.h"
#include "ContextEnumerator.h"

TEST_F(ContextEnumeratorTest, DegenerateEnumeration) {
  size_t ct = 0;
  for(const auto& cur : ContextEnumerator(std::shared_ptr<CoreContext>(nullptr))) {
    ASSERT_TRUE(!!cur.get()) << "Context enumerator incorrectly enumerated a null context pointer";
    ct++;
  }
  ASSERT_EQ(0UL, ct) << "An empty enumerator unexpectedly enumerated one or more entries";
}

TEST_F(ContextEnumeratorTest, TrivialEnumeration) {
  size_t ct = 0;
  for(const auto& cur : ContextEnumerator(AutoCurrentContext()))
    ct++;
  ASSERT_EQ(1UL, ct) << "Context enumerator failed to enumerate a context with no children";
}

TEST_F(ContextEnumeratorTest, TwoContextEnumerationTest) {
  AutoCreateContext ctxt1;
  AutoCreateContext ctxt2;

  size_t ct = 0;
  for(const auto& cur : ContextEnumerator(ctxt1))
    ct++;
  ASSERT_EQ(1UL, ct) << "An attempt to enumerate a context with a sibling did not correctly enumerate one context";
}

TEST_F(ContextEnumeratorTest, VerifySimpleEnumeration) {
  // Create a pair of descendant contexts, verify we don't accidentally hit these when enumerating children
  AutoCreateContext outer;
  AutoCreateContext inner;
  CurrentContextPusher pshr(inner);

  // Add a few children to the current context:
  AutoCreateContext c1;
  AutoCreateContext c2;
  AutoCreateContext c3;
  AutoCreateContext c4;

  std::unordered_set<std::shared_ptr<CoreContext>> allCtxts;
  allCtxts.insert(AutoCurrentContext());
  allCtxts.insert(c1);
  allCtxts.insert(c2);
  allCtxts.insert(c3);
  allCtxts.insert(c4);

  // Create the enumerator, verify we get the expected count:
  size_t nContexts = 0;
  for(const std::shared_ptr<CoreContext>& cur : CurrentContextEnumerator()) {
    ASSERT_TRUE(!!allCtxts.count(cur)) << "Context enumerator accidentally enumerated a context not rooted in the specified parent";

    allCtxts.erase(cur);
    nContexts++;
  }

  ASSERT_EQ(5UL, nContexts) << "Context enumerator did not encounter the number of expected children";
  ASSERT_TRUE(allCtxts.empty()) << "Context enumerator did not encounter all children as expected";
}

TEST_F(ContextEnumeratorTest, SimpleRemovalInterference) {
  static const size_t nChildren = 5;

  // Create a few contexts which we intend to destroy as we go along:
  std::unordered_set<std::shared_ptr<CoreContext>> contexts;
  for(size_t i = nChildren; i--;)
    contexts.insert(AutoCreateContext());

  // Also add ourselves:
  contexts.insert(AutoCurrentContext());

  // Enumerate contexts, and remove them from the set:
  size_t nRemoved = 0;
  for(const auto& cur : CurrentContextEnumerator()) {
    ASSERT_TRUE(!!contexts.count(cur)) << "Failed to find a context enumerated by the context enumerator";
    contexts.erase(cur);
    nRemoved++;
  }

  // Verify we got the number removed we expected:
  ASSERT_EQ(nChildren + 1UL, nRemoved) << "Context enumerator did not remove the expected number of children";
}

TEST_F(ContextEnumeratorTest, ComplexRemovalInterference) {
  static const size_t nChildren = 50;

  // This time we use a vector, and we pop from the back of the vector unpredictably:
  std::vector<std::shared_ptr<CoreContext>> children(nChildren);
  for(size_t i = nChildren; i--;)
    children.push_back(AutoCreateContext());

  // Shuffle the collection to prevent the order here from being equivalent to the order in the context
  std::random_shuffle(children.begin(), children.end());

  // These are the elements actually encountered in the enumeration, held here to prevent expiration in the
  // event that we enumerate a context which should have already been evicted
  std::unordered_set<std::shared_ptr<CoreContext>> enumerated;

  // These are the elements eliminated from the vector.  By the time we're done the should all be expired.
  std::vector<std::weak_ptr<CoreContext>> eliminated;

  // Go through the enumeration, the totals should line up by the time we're done:
  for(const auto& cur : CurrentContextEnumerator()) {
    enumerated.insert(cur);

    // Pull off the last element:
    auto removed = children.back();
    children.pop_back();

    // If we haven't enumerated this element already, we want to mark it as eliminated-before-enumerated
    if(!enumerated.count(removed))
      eliminated.push_back(removed);
  }

  // Now verify that nothing we eliminated was enumerated:
  for(auto& cur : eliminated)
    ASSERT_TRUE(cur.expired()) << "Found an element that was iterated after it should have been unreachable";
}<|endoftext|>


<commit_before>/*
 * pv.cpp
 *
 */

#include <columns/buildandrun.hpp>
#include <connections/CopyConn.hpp>
#include <connections/HyPerConn.hpp>
#include <normalizers/NormalizeBase.hpp>

int runparamsfile(int argc, char ** argv, char const * paramsfile);

int main(int argc, char * argv[]) {
   int rank = 0;
#ifdef PV_USE_MPI
   MPI_Init(&argc, &argv);
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
#endif // PV_USE_MPI
   int status = PV_SUCCESS;

   if (status == PV_SUCCESS) { status = runparamsfile(argc, argv, "input/CopyConnInitializeTest.params"); }
   if (status == PV_SUCCESS) { status = runparamsfile(argc, argv, "input/CopyConnInitializeNonsharedTest.params"); }
   if (status == PV_SUCCESS) { status = runparamsfile(argc, argv, "input/CopyConnPlasticTest.params"); }
   if (status == PV_SUCCESS) { status = runparamsfile(argc, argv, "input/CopyConnPlasticNonsharedTest.params"); }

#ifdef PV_USE_MPI
   MPI_Finalize();
#endif // PV_USE_MPI
   return status;
}

// Given one params file, runparamsfile builds and runs, but then before deleting the HyPerCol,
// it looks for a connection named "OriginalConn" and one named "CopyConn",
// grabs the normalization strengths of each, and tests whether the weights divided by the strength
// are equal to within roundoff error.
// (Technically, it tests whether (original weight)*(copy strength) and (copy weight)*(original strength)
// are within 1.0e-6 in absolute value.  This is reasonable if the weights and strengths are order-of-magnitude 1.0)
//
// Note that this check makes assumptions on the normalization method, although normalizeSum, normalizeL2 and normalizeMax all satisfy them.
int runparamsfile(int argc, char ** argv, char const * paramsfile) {
   //
   // Process input arguments.  The only input arguments allowed are --require-return and -t.
   // The parameter file is hardcoded below.
   //
   int rank = 0;
#ifdef PV_USE_MPI
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
#endif // PV_USE_MPI
   bool argerr = false;
   int reqrtn = 0;
   int usethreads = 0;
   int threadargno = -1;
   for (int k=1; k<argc; k++) {
      if (!strcmp(argv[k], "--require-return")) {
         reqrtn = 1;
      }
      else if (!strcmp(argv[k], "-t")) {
         usethreads = 1;
         if (k<argc-1 && argv[k+1][0] != '-') {
            k++;
            threadargno = k;
         }
      }
      else {
         argerr = true;
         break;
      }
   }
   if (argerr) {
      if (rank==0) {
         fprintf(stderr, "%s: run without input arguments (except for --require-return, if desired); the necessary arguments are hardcoded.\n", argv[0]);
      }
      MPI_Barrier(MPI_COMM_WORLD);
      return PV_FAILURE;
   }

   assert(reqrtn==0 || reqrtn==1);
   assert(usethreads==0 || usethreads==1);
   size_t cl_argc = 3+reqrtn+usethreads+(threadargno>0);
   char ** cl_args = (char **) malloc((cl_argc+1)*sizeof(char *));
   assert(cl_args!=NULL);
   int cl_arg = 0;
   cl_args[cl_arg++] = strdup(argv[0]);
   cl_args[cl_arg++] = strdup("-p");
   cl_args[cl_arg++] = strdup(paramsfile);
   if (reqrtn) {
      cl_args[cl_arg++] = strdup("--require-return");
   }
   if (usethreads) {
      cl_args[cl_arg++] = strdup("-t");
      if (threadargno>0) {
         cl_args[cl_arg++] = strdup(argv[threadargno]);
      }
   }
   assert(cl_arg==cl_argc);
   cl_args[cl_arg] = NULL;
   int status = PV_SUCCESS;
   HyPerCol * hc = build((int) cl_argc, cl_args);
   if (hc != NULL) {
      status = hc->run();
      if( status != PV_SUCCESS ) {
         if (rank==0) {
            fprintf(stderr, "%s: running with params file %s returned error %d.\n", cl_args[0], paramsfile, status);
         }
      }
   }
   else {
      status = PV_FAILURE;
   }

   for (size_t arg=0; arg<cl_argc; arg++) {
       free(cl_args[arg]);
   }
   free(cl_args);

   if (status != PV_SUCCESS) {
      delete hc;
      return status;
   }

   HyPerConn * origConn = dynamic_cast<HyPerConn *>(hc->getConnFromName("OriginalConn"));
   if (origConn==NULL) {
      if (rank==0) {
         fprintf(stderr, "Unable to find connection named \"OriginalConn\" in params file \"%s\".\n", paramsfile);
      }
      status = PV_FAILURE;
   }
   CopyConn * copyConn = dynamic_cast<CopyConn *>(hc->getConnFromName("CopyConn"));
   if (origConn==NULL) {
      if (rank==0) {
         fprintf(stderr, "Unable to find connection named \"CopyConn\" in params file \"%s\".\n", paramsfile);
      }
      status = PV_FAILURE;
   }
   if (status != PV_SUCCESS) {
      delete hc;
      return status;
   }

   NormalizeBase * origNormalizer = origConn->getNormalizer();
   assert(origNormalizer);
   float origStrength = origNormalizer->getStrength();

   NormalizeBase * copyNormalizer = copyConn->getNormalizer();
   assert(copyNormalizer);
   float copyStrength = copyNormalizer->getStrength();

   int origNumPatches = origConn->getNumDataPatches();
   int copyNumPatches = copyConn->getNumDataPatches();
   assert(origNumPatches==copyNumPatches);
   int origNxp = origConn->xPatchSize();
   int copyNxp = copyConn->xPatchSize();
   assert(origNxp==copyNxp);
   int origNyp = origConn->yPatchSize();
   int copyNyp = copyConn->yPatchSize();
   assert(origNyp==copyNyp);
   int origNfp = origConn->fPatchSize();
   int copyNfp = copyConn->fPatchSize();
   assert(origNfp==copyNfp);
   int origNxpShrunken = origConn->getNxpShrunken();
   int copyNxpShrunken = copyConn->getNxpShrunken();
   assert(origNxpShrunken==copyNxpShrunken);
   int origNypShrunken = origConn->getNypShrunken();
   int copyNypShrunken = copyConn->getNypShrunken();
   assert(origNypShrunken==copyNypShrunken);
   int origNumArbors = origConn->numberOfAxonalArborLists();
   int copyNumArbors = copyConn->numberOfAxonalArborLists();
   assert(origNumArbors==copyNumArbors);

   int xstart = (origNxp-origNxpShrunken)/2;
   int ystart = (origNyp-origNypShrunken)/2;
   for (int arbor=0; arbor<origNumArbors; arbor++) {
      for (int patchindex=0; patchindex<origNumPatches; patchindex++) {
         for (int y=ystart; y<ystart+origNypShrunken; y++) {
            for (int x=xstart; x<xstart+origNxpShrunken; x++) {
               for (int f=0; f<origNfp; f++) {
                  int indexinpatch = kIndex(x,y,f,origNxp,origNyp,origNfp);
                  pvwdata_t origWeight = origConn->get_wDataHead(arbor, patchindex)[indexinpatch];
                  pvwdata_t copyWeight = copyConn->get_wDataHead(arbor, patchindex)[indexinpatch];
                  float discrep = fabsf(origWeight*copyStrength - copyWeight*origStrength);
                  if (discrep > 1e-6) {
                     fprintf(stderr, "Rank %d: arbor %d, patchindex %d, x=%d, y=%d, f=%d: discrepancy of %g\n",
                           hc->columnId(), arbor, patchindex, x, y, f, discrep);
                     status = PV_FAILURE;
                  }
               }
            }
         }
      }
   }

   delete hc;

   return status;
}
<commit_msg>Updates CopyConnTest for removal of nxpShrunken and nypShrunken<commit_after>/*
 * pv.cpp
 *
 */

#include <columns/buildandrun.hpp>
#include <connections/CopyConn.hpp>
#include <connections/HyPerConn.hpp>
#include <normalizers/NormalizeBase.hpp>

int runparamsfile(int argc, char ** argv, char const * paramsfile);

int main(int argc, char * argv[]) {
   int rank = 0;
#ifdef PV_USE_MPI
   MPI_Init(&argc, &argv);
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
#endif // PV_USE_MPI
   int status = PV_SUCCESS;

   if (status == PV_SUCCESS) { status = runparamsfile(argc, argv, "input/CopyConnInitializeTest.params"); }
   if (status == PV_SUCCESS) { status = runparamsfile(argc, argv, "input/CopyConnInitializeNonsharedTest.params"); }
   if (status == PV_SUCCESS) { status = runparamsfile(argc, argv, "input/CopyConnPlasticTest.params"); }
   if (status == PV_SUCCESS) { status = runparamsfile(argc, argv, "input/CopyConnPlasticNonsharedTest.params"); }

#ifdef PV_USE_MPI
   MPI_Finalize();
#endif // PV_USE_MPI
   return status;
}

// Given one params file, runparamsfile builds and runs, but then before deleting the HyPerCol,
// it looks for a connection named "OriginalConn" and one named "CopyConn",
// grabs the normalization strengths of each, and tests whether the weights divided by the strength
// are equal to within roundoff error.
// (Technically, it tests whether (original weight)*(copy strength) and (copy weight)*(original strength)
// are within 1.0e-6 in absolute value.  This is reasonable if the weights and strengths are order-of-magnitude 1.0)
//
// Note that this check makes assumptions on the normalization method, although normalizeSum, normalizeL2 and normalizeMax all satisfy them.
int runparamsfile(int argc, char ** argv, char const * paramsfile) {
   //
   // Process input arguments.  The only input arguments allowed are --require-return and -t.
   // The parameter file is hardcoded below.
   //
   int rank = 0;
#ifdef PV_USE_MPI
   MPI_Comm_rank(MPI_COMM_WORLD, &rank);
#endif // PV_USE_MPI
   bool argerr = false;
   int reqrtn = 0;
   int usethreads = 0;
   int threadargno = -1;
   for (int k=1; k<argc; k++) {
      if (!strcmp(argv[k], "--require-return")) {
         reqrtn = 1;
      }
      else if (!strcmp(argv[k], "-t")) {
         usethreads = 1;
         if (k<argc-1 && argv[k+1][0] != '-') {
            k++;
            threadargno = k;
         }
      }
      else {
         argerr = true;
         break;
      }
   }
   if (argerr) {
      if (rank==0) {
         fprintf(stderr, "%s: run without input arguments (except for --require-return, if desired); the necessary arguments are hardcoded.\n", argv[0]);
      }
      MPI_Barrier(MPI_COMM_WORLD);
      return PV_FAILURE;
   }

   assert(reqrtn==0 || reqrtn==1);
   assert(usethreads==0 || usethreads==1);
   size_t cl_argc = 3+reqrtn+usethreads+(threadargno>0);
   char ** cl_args = (char **) malloc((cl_argc+1)*sizeof(char *));
   assert(cl_args!=NULL);
   int cl_arg = 0;
   cl_args[cl_arg++] = strdup(argv[0]);
   cl_args[cl_arg++] = strdup("-p");
   cl_args[cl_arg++] = strdup(paramsfile);
   if (reqrtn) {
      cl_args[cl_arg++] = strdup("--require-return");
   }
   if (usethreads) {
      cl_args[cl_arg++] = strdup("-t");
      if (threadargno>0) {
         cl_args[cl_arg++] = strdup(argv[threadargno]);
      }
   }
   assert(cl_arg==cl_argc);
   cl_args[cl_arg] = NULL;
   int status = PV_SUCCESS;
   HyPerCol * hc = build((int) cl_argc, cl_args);
   if (hc != NULL) {
      status = hc->run();
      if( status != PV_SUCCESS ) {
         if (rank==0) {
            fprintf(stderr, "%s: running with params file %s returned error %d.\n", cl_args[0], paramsfile, status);
         }
      }
   }
   else {
      status = PV_FAILURE;
   }

   for (size_t arg=0; arg<cl_argc; arg++) {
       free(cl_args[arg]);
   }
   free(cl_args);

   if (status != PV_SUCCESS) {
      delete hc;
      return status;
   }

   HyPerConn * origConn = dynamic_cast<HyPerConn *>(hc->getConnFromName("OriginalConn"));
   if (origConn==NULL) {
      if (rank==0) {
         fprintf(stderr, "Unable to find connection named \"OriginalConn\" in params file \"%s\".\n", paramsfile);
      }
      status = PV_FAILURE;
   }
   CopyConn * copyConn = dynamic_cast<CopyConn *>(hc->getConnFromName("CopyConn"));
   if (origConn==NULL) {
      if (rank==0) {
         fprintf(stderr, "Unable to find connection named \"CopyConn\" in params file \"%s\".\n", paramsfile);
      }
      status = PV_FAILURE;
   }
   if (status != PV_SUCCESS) {
      delete hc;
      return status;
   }

   NormalizeBase * origNormalizer = origConn->getNormalizer();
   assert(origNormalizer);
   float origStrength = origNormalizer->getStrength();

   NormalizeBase * copyNormalizer = copyConn->getNormalizer();
   assert(copyNormalizer);
   float copyStrength = copyNormalizer->getStrength();

   int origNumPatches = origConn->getNumDataPatches();
   int copyNumPatches = copyConn->getNumDataPatches();
   assert(origNumPatches==copyNumPatches);
   int origNxp = origConn->xPatchSize();
   int copyNxp = copyConn->xPatchSize();
   assert(origNxp==copyNxp);
   int origNyp = origConn->yPatchSize();
   int copyNyp = copyConn->yPatchSize();
   assert(origNyp==copyNyp);
   int origNfp = origConn->fPatchSize();
   int copyNfp = copyConn->fPatchSize();
   assert(origNfp==copyNfp);
   int origNumArbors = origConn->numberOfAxonalArborLists();
   int copyNumArbors = copyConn->numberOfAxonalArborLists();
   assert(origNumArbors==copyNumArbors);

   for (int arbor=0; arbor<origNumArbors; arbor++) {
      for (int patchindex=0; patchindex<origNumPatches; patchindex++) {
         for (int y=0; y<origNyp; y++) {
            for (int x=0; x<origNxp; x++) {
               for (int f=0; f<origNfp; f++) {
                  int indexinpatch = kIndex(x,y,f,origNxp,origNyp,origNfp);
                  pvwdata_t origWeight = origConn->get_wDataHead(arbor, patchindex)[indexinpatch];
                  pvwdata_t copyWeight = copyConn->get_wDataHead(arbor, patchindex)[indexinpatch];
                  float discrep = fabsf(origWeight*copyStrength - copyWeight*origStrength);
                  if (discrep > 1e-6) {
                     fprintf(stderr, "Rank %d: arbor %d, patchindex %d, x=%d, y=%d, f=%d: discrepancy of %g\n",
                           hc->columnId(), arbor, patchindex, x, y, f, discrep);
                     status = PV_FAILURE;
                  }
               }
            }
         }
      }
   }

   delete hc;

   return status;
}
<|endoftext|>


<commit_before><commit_msg>Zero-fill tab drag icon pixel buffer.<commit_after><|endoftext|>


<commit_before>// The libMesh Finite Element Library.
// Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

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

// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// library configuration
#include "libmesh_config.h"

// C++ includes
#include <algorithm> // for std::min
#include <map>       // for std::multimap
#include <sstream>   // for std::ostringstream

// Local includes
#include "boundary_info.h"
#include "elem.h"
#include "mesh_base.h"
#include "parallel.h"
#include "partitioner.h"
#include "point_locator_base.h"
#include "threads.h"

namespace libMesh
{

// ------------------------------------------------------------
// MeshBase class member functions
MeshBase::MeshBase (unsigned int d) :
  boundary_info  (new BoundaryInfo(*this)),
  _n_parts       (1),
  _dim           (d),
  _is_prepared   (false),
  _point_locator (NULL),
  _partitioner   (NULL),
  _skip_partitioning(false)
{
  libmesh_assert (LIBMESH_DIM <= 3);
  libmesh_assert (LIBMESH_DIM >= _dim);
  libmesh_assert (libMesh::initialized());
}

MeshBase::MeshBase (const MeshBase& other_mesh) :
  boundary_info  (new BoundaryInfo(*this)),
  _n_parts       (other_mesh._n_parts),
  _dim           (other_mesh._dim),
  _is_prepared   (other_mesh._is_prepared),
  _point_locator (NULL),
  _partitioner   (other_mesh._partitioner->clone()),
  _skip_partitioning(other_mesh._skip_partitioning)
{
}

MeshBase::~MeshBase()
{
  this->clear();

  libmesh_assert (!libMesh::closed());
}

void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements)
{
  parallel_only();

  // Renumber the nodes and elements so that they in contiguous
  // blocks.  By default, skip_renumber_nodes_and_elements is false,
  // however we may skip this step by passing
  // skip_renumber_nodes_and_elements==true to this function.
  //
  // Instances where you if prepare_for_use() should not renumber the nodes
  // and elements include reading in e.g. an xda/r or gmv file. In
  // this case, the ordering of the nodes may depend on an accompanying
  // solution, and the node ordering cannot be changed.
  if(!skip_renumber_nodes_and_elements)
    this->renumber_nodes_and_elements();

  // Let all the elements find their neighbors
  this->find_neighbors();

  // Partition the mesh.
  this->partition();

  // If we're using ParallelMesh, we'll want it parallelized.
  this->delete_remote_elements();

  if(!skip_renumber_nodes_and_elements)
    this->renumber_nodes_and_elements();

  // Reset our PointLocator.  This needs to happen any time the elements
  // in the underlying elements in the mesh have changed, so we do it here.
  this->clear_point_locator();

  // The mesh is now prepared for use.
  _is_prepared = true;
}

void MeshBase::clear ()
{
  // Reset the number of partitions
  _n_parts = 1;

  // Reset the _is_prepared flag
  _is_prepared = false;

  // Clear boundary information
  this->boundary_info->clear();

  // Clear our point locator.
  this->clear_point_locator();
}

unsigned int MeshBase::n_subdomains() const
{
  // This requires an inspection on every processor
  parallel_only();

  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  std::set<unsigned int> subdomain_ids;

  for (; el!=end; ++el)
    subdomain_ids.insert((*el)->subdomain_id());

  // Some subdomains may only live on other processors
  Parallel::set_union(subdomain_ids, 0);

  unsigned int n_sbd_ids = subdomain_ids.size();
  Parallel::broadcast(n_sbd_ids);

  return n_sbd_ids;
}

unsigned int MeshBase::n_nodes_on_proc (const unsigned int proc_id) const
{
  // We're either counting a processor's nodes or unpartitioned
  // nodes
  libmesh_assert (proc_id < libMesh::n_processors() ||
	  proc_id == DofObject::invalid_processor_id);

  return static_cast<unsigned int>(std::distance (this->pid_nodes_begin(proc_id),
						  this->pid_nodes_end  (proc_id)));
}

unsigned int MeshBase::n_elem_on_proc (const unsigned int proc_id) const
{
  // We're either counting a processor's elements or unpartitioned
  // elements
  libmesh_assert (proc_id < libMesh::n_processors() ||
	  proc_id == DofObject::invalid_processor_id);

  return static_cast<unsigned int>(std::distance (this->pid_elements_begin(proc_id),
						  this->pid_elements_end  (proc_id)));
}

unsigned int MeshBase::n_active_elem_on_proc (const unsigned int proc_id) const
{
  libmesh_assert (proc_id < libMesh::n_processors());
  return static_cast<unsigned int>(std::distance (this->active_pid_elements_begin(proc_id),
						  this->active_pid_elements_end  (proc_id)));
}

unsigned int MeshBase::n_sub_elem () const
{
  unsigned int ne=0;

  const_element_iterator       el  = this->elements_begin();
  const const_element_iterator end = this->elements_end();

  for (; el!=end; ++el)
    ne += (*el)->n_sub_elem();

  return ne;
}

unsigned int MeshBase::n_active_sub_elem () const
{
  unsigned int ne=0;

  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  for (; el!=end; ++el)
    ne += (*el)->n_sub_elem();

  return ne;
}

std::string MeshBase::get_info() const
{
  std::ostringstream out;

  out << " Mesh Information:"                                  << '\n'
      << "  mesh_dimension()="    << this->mesh_dimension()    << '\n'
      << "  spatial_dimension()=" << this->spatial_dimension() << '\n'
      << "  n_nodes()="           << this->n_nodes()           << '\n'
      << "    n_local_nodes()="   << this->n_local_nodes()     << '\n'
      << "  n_elem()="            << this->n_elem()            << '\n'
      << "    n_local_elem()="    << this->n_local_elem()      << '\n'
#ifdef LIBMESH_ENABLE_AMR
      << "    n_active_elem()="   << this->n_active_elem()     << '\n'
#endif
      << "  n_subdomains()="      << this->n_subdomains()      << '\n'
      << "  n_partitions()="      << this->n_partitions()      << '\n'
      << "  n_processors()="      << libMesh::n_processors()   << '\n'
      << "  n_threads()="         << libMesh::n_threads()      << '\n'
      << "  processor_id()="      << this->processor_id()      << '\n';

  return out.str();
}

void MeshBase::print_info(std::ostream& os) const
{
  os << this->get_info()
     << std::endl;
}

std::ostream& operator << (std::ostream& os, const MeshBase& m)
{
  m.print_info(os);
  return os;
}

void MeshBase::partition (const unsigned int n_parts)
{
  if(!skip_partitioning())
  {
    if (partitioner().get()) // "NULL" means don't partition
      partitioner()->partition (*this, n_parts);
  }
}

unsigned int MeshBase::recalculate_n_partitions()
{
  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  unsigned int max_proc_id=0;

  for (; el!=end; ++el)
    max_proc_id = std::max(max_proc_id, static_cast<unsigned int>((*el)->processor_id()));

  // The number of partitions is one more than the max processor ID.
  _n_parts = max_proc_id+1;

  Parallel::max(_n_parts);

  return _n_parts;
}

const PointLocatorBase& MeshBase::point_locator () const
{
  libmesh_deprecated();

  if (_point_locator.get() == NULL)
    {
      // PointLocator construction may not be safe within threads
      libmesh_assert(!Threads::in_threads);

      _point_locator.reset (PointLocatorBase::build(TREE, *this).release());
    }

  return *_point_locator;
}

AutoPtr<PointLocatorBase> MeshBase::sub_point_locator () const
{
  if (_point_locator.get() == NULL)
    {
      // PointLocator construction may not be safe within threads
      libmesh_assert(!Threads::in_threads);

      _point_locator.reset (PointLocatorBase::build(TREE, *this).release());
    }

  return PointLocatorBase::build(TREE, *this, _point_locator.get());
}

void MeshBase::clear_point_locator ()
{
  _point_locator.reset(NULL);
}

} // namespace libMesh

<commit_msg>Bugfix to make sure that after prepare_for_use, a manually partitioned mesh has a correct n_partitions() count even if it's not partitioned by a Partitioner.<commit_after>// The libMesh Finite Element Library.
// Copyright (C) 2002-2012 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

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

// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// library configuration
#include "libmesh_config.h"

// C++ includes
#include <algorithm> // for std::min
#include <map>       // for std::multimap
#include <sstream>   // for std::ostringstream

// Local includes
#include "boundary_info.h"
#include "elem.h"
#include "mesh_base.h"
#include "parallel.h"
#include "partitioner.h"
#include "point_locator_base.h"
#include "threads.h"

namespace libMesh
{

// ------------------------------------------------------------
// MeshBase class member functions
MeshBase::MeshBase (unsigned int d) :
  boundary_info  (new BoundaryInfo(*this)),
  _n_parts       (1),
  _dim           (d),
  _is_prepared   (false),
  _point_locator (NULL),
  _partitioner   (NULL),
  _skip_partitioning(false)
{
  libmesh_assert (LIBMESH_DIM <= 3);
  libmesh_assert (LIBMESH_DIM >= _dim);
  libmesh_assert (libMesh::initialized());
}

MeshBase::MeshBase (const MeshBase& other_mesh) :
  boundary_info  (new BoundaryInfo(*this)),
  _n_parts       (other_mesh._n_parts),
  _dim           (other_mesh._dim),
  _is_prepared   (other_mesh._is_prepared),
  _point_locator (NULL),
  _partitioner   (other_mesh._partitioner->clone()),
  _skip_partitioning(other_mesh._skip_partitioning)
{
}

MeshBase::~MeshBase()
{
  this->clear();

  libmesh_assert (!libMesh::closed());
}

void MeshBase::prepare_for_use (const bool skip_renumber_nodes_and_elements)
{
  parallel_only();

  // Renumber the nodes and elements so that they in contiguous
  // blocks.  By default, skip_renumber_nodes_and_elements is false,
  // however we may skip this step by passing
  // skip_renumber_nodes_and_elements==true to this function.
  //
  // Instances where you if prepare_for_use() should not renumber the nodes
  // and elements include reading in e.g. an xda/r or gmv file. In
  // this case, the ordering of the nodes may depend on an accompanying
  // solution, and the node ordering cannot be changed.
  if(!skip_renumber_nodes_and_elements)
    this->renumber_nodes_and_elements();

  // Let all the elements find their neighbors
  this->find_neighbors();

  // Partition the mesh.
  this->partition();

  // If we're using ParallelMesh, we'll want it parallelized.
  this->delete_remote_elements();

  if(!skip_renumber_nodes_and_elements)
    this->renumber_nodes_and_elements();

  // Reset our PointLocator.  This needs to happen any time the elements
  // in the underlying elements in the mesh have changed, so we do it here.
  this->clear_point_locator();

  // The mesh is now prepared for use.
  _is_prepared = true;
}

void MeshBase::clear ()
{
  // Reset the number of partitions
  _n_parts = 1;

  // Reset the _is_prepared flag
  _is_prepared = false;

  // Clear boundary information
  this->boundary_info->clear();

  // Clear our point locator.
  this->clear_point_locator();
}

unsigned int MeshBase::n_subdomains() const
{
  // This requires an inspection on every processor
  parallel_only();

  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  std::set<unsigned int> subdomain_ids;

  for (; el!=end; ++el)
    subdomain_ids.insert((*el)->subdomain_id());

  // Some subdomains may only live on other processors
  Parallel::set_union(subdomain_ids, 0);

  unsigned int n_sbd_ids = subdomain_ids.size();
  Parallel::broadcast(n_sbd_ids);

  return n_sbd_ids;
}

unsigned int MeshBase::n_nodes_on_proc (const unsigned int proc_id) const
{
  // We're either counting a processor's nodes or unpartitioned
  // nodes
  libmesh_assert (proc_id < libMesh::n_processors() ||
	  proc_id == DofObject::invalid_processor_id);

  return static_cast<unsigned int>(std::distance (this->pid_nodes_begin(proc_id),
						  this->pid_nodes_end  (proc_id)));
}

unsigned int MeshBase::n_elem_on_proc (const unsigned int proc_id) const
{
  // We're either counting a processor's elements or unpartitioned
  // elements
  libmesh_assert (proc_id < libMesh::n_processors() ||
	  proc_id == DofObject::invalid_processor_id);

  return static_cast<unsigned int>(std::distance (this->pid_elements_begin(proc_id),
						  this->pid_elements_end  (proc_id)));
}

unsigned int MeshBase::n_active_elem_on_proc (const unsigned int proc_id) const
{
  libmesh_assert (proc_id < libMesh::n_processors());
  return static_cast<unsigned int>(std::distance (this->active_pid_elements_begin(proc_id),
						  this->active_pid_elements_end  (proc_id)));
}

unsigned int MeshBase::n_sub_elem () const
{
  unsigned int ne=0;

  const_element_iterator       el  = this->elements_begin();
  const const_element_iterator end = this->elements_end();

  for (; el!=end; ++el)
    ne += (*el)->n_sub_elem();

  return ne;
}

unsigned int MeshBase::n_active_sub_elem () const
{
  unsigned int ne=0;

  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  for (; el!=end; ++el)
    ne += (*el)->n_sub_elem();

  return ne;
}

std::string MeshBase::get_info() const
{
  std::ostringstream out;

  out << " Mesh Information:"                                  << '\n'
      << "  mesh_dimension()="    << this->mesh_dimension()    << '\n'
      << "  spatial_dimension()=" << this->spatial_dimension() << '\n'
      << "  n_nodes()="           << this->n_nodes()           << '\n'
      << "    n_local_nodes()="   << this->n_local_nodes()     << '\n'
      << "  n_elem()="            << this->n_elem()            << '\n'
      << "    n_local_elem()="    << this->n_local_elem()      << '\n'
#ifdef LIBMESH_ENABLE_AMR
      << "    n_active_elem()="   << this->n_active_elem()     << '\n'
#endif
      << "  n_subdomains()="      << this->n_subdomains()      << '\n'
      << "  n_partitions()="      << this->n_partitions()      << '\n'
      << "  n_processors()="      << libMesh::n_processors()   << '\n'
      << "  n_threads()="         << libMesh::n_threads()      << '\n'
      << "  processor_id()="      << this->processor_id()      << '\n';

  return out.str();
}

void MeshBase::print_info(std::ostream& os) const
{
  os << this->get_info()
     << std::endl;
}

std::ostream& operator << (std::ostream& os, const MeshBase& m)
{
  m.print_info(os);
  return os;
}

void MeshBase::partition (const unsigned int n_parts)
{
  if(!skip_partitioning())
  {
    if (partitioner().get()) // "NULL" means don't partition
      partitioner()->partition (*this, n_parts);
  }
  else
  {
    // Make sure our cached n_partitions() is still correct
    this->recalculate_n_partitions();
  }
}

unsigned int MeshBase::recalculate_n_partitions()
{
  const_element_iterator       el  = this->active_elements_begin();
  const const_element_iterator end = this->active_elements_end();

  unsigned int max_proc_id=0;

  for (; el!=end; ++el)
    max_proc_id = std::max(max_proc_id, static_cast<unsigned int>((*el)->processor_id()));

  // The number of partitions is one more than the max processor ID.
  _n_parts = max_proc_id+1;

  Parallel::max(_n_parts);

  return _n_parts;
}

const PointLocatorBase& MeshBase::point_locator () const
{
  libmesh_deprecated();

  if (_point_locator.get() == NULL)
    {
      // PointLocator construction may not be safe within threads
      libmesh_assert(!Threads::in_threads);

      _point_locator.reset (PointLocatorBase::build(TREE, *this).release());
    }

  return *_point_locator;
}

AutoPtr<PointLocatorBase> MeshBase::sub_point_locator () const
{
  if (_point_locator.get() == NULL)
    {
      // PointLocator construction may not be safe within threads
      libmesh_assert(!Threads::in_threads);

      _point_locator.reset (PointLocatorBase::build(TREE, *this).release());
    }

  return PointLocatorBase::build(TREE, *this, _point_locator.get());
}

void MeshBase::clear_point_locator ()
{
  _point_locator.reset(NULL);
}

} // namespace libMesh

<|endoftext|>


<commit_before>/// \file rtmp_analyser.cpp
/// Debugging tool for RTMP data.
#include <iostream>
#include <fstream>
#include <string>

#include <cstdlib>

#include <mist/flv_tag.h>
#include <mist/amf.h>
#include <mist/rtmpchunks.h>
#include <mist/config.h>

#define DETAIL_RECONSTRUCT 1
#define DETAIL_EXPLICIT 2
#define DETAIL_VERBOSE 4

///\brief Holds everything unique to the analysers.
namespace Analysers {
  ///\brief Debugging tool for RTMP data.
  ///
  ///Expects RTMP data of one side of the conversation through stdin, outputs human-readable information to stderr.
  ///
  ///Will output FLV file to stdout, if available.
  ///
  ///Automatically skips the handshake data.
  ///\param conf The configuration parsed from the commandline.
  ///\return The return code of the analyser.
  int analyseRTMP(Util::Config conf){
    int Detail = conf.getInteger("detail");
    if (Detail > 0){
      fprintf(stderr, "Detail level set:\n");
      if (Detail & DETAIL_RECONSTRUCT){
        fprintf(stderr, " - Will reconstuct FLV file to stdout\n");
        std::cout.write(FLV::Header, 13);
      }
      if (Detail & DETAIL_EXPLICIT){
        fprintf(stderr, " - Will list explicit video/audio data information\n");
      }
      if (Detail & DETAIL_VERBOSE){
        fprintf(stderr, " - Will list verbose chunk information\n");
      }
    }

    std::string inbuffer;
    inbuffer.reserve(3073);
    while (std::cin.good() && inbuffer.size() < 3073){
      inbuffer += std::cin.get();
    } //read all of std::cin to temp
    inbuffer.erase(0, 3073); //strip the handshake part
    RTMPStream::Chunk next;
    FLV::Tag F; //FLV holder
    AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER);
    AMF::Object3 amf3data("empty", AMF::AMF3_DDV_CONTAINER);
    unsigned int read_in = 0;

    while (std::cin.good() || inbuffer.size()){
      if (next.Parse(inbuffer)){
        if (Detail & DETAIL_VERBOSE){
          fprintf(stderr, "Chunk info: [%#2X] CS ID %u, timestamp %u, len %u, type ID %u, Stream ID %u\n", next.headertype, next.cs_id, next.timestamp,
              next.len, next.msg_type_id, next.msg_stream_id);
        }
        switch (next.msg_type_id){
          case 0: //does not exist
            fprintf(stderr, "Error chunk @ %lu - CS%i, T%i, L%i, LL%i, MID%i\n", read_in-inbuffer.size(), next.cs_id, next.timestamp, next.real_len, next.len_left, next.msg_stream_id);
            return 0;
            break; //happens when connection breaks unexpectedly
          case 1: //set chunk size
            RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str());
            fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max);
            break;
          case 2: //abort message - we ignore this one
            fprintf(stderr, "CTRL: Abort message: %i\n", ntohl(*(int*)next.data.c_str()));
            //4 bytes of stream id to drop
            break;
          case 3: //ack
            RTMPStream::snd_window_at = ntohl(*(int*)next.data.c_str());
            fprintf(stderr, "CTRL: Acknowledgement: %i\n", RTMPStream::snd_window_at);
            break;
          case 4: {
            short int ucmtype = ntohs(*(short int*)next.data.c_str());
            switch (ucmtype){
              case 0:
                fprintf(stderr, "CTRL: User control message: stream begin %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 1:
                fprintf(stderr, "CTRL: User control message: stream EOF %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 2:
                fprintf(stderr, "CTRL: User control message: stream dry %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 3:
                fprintf(stderr, "CTRL: User control message: setbufferlen %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 4:
                fprintf(stderr, "CTRL: User control message: streamisrecorded %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 6:
                fprintf(stderr, "CTRL: User control message: pingrequest %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 7:
                fprintf(stderr, "CTRL: User control message: pingresponse %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 31:
              case 32:
                //don't know, but not interesting anyway
                break;
              default:
                fprintf(stderr, "CTRL: User control message: UNKNOWN %hu - %u\n", ucmtype, ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
            }
          }
            break;
          case 5: //window size of other end
            RTMPStream::rec_window_size = ntohl(*(int*)next.data.c_str());
            RTMPStream::rec_window_at = RTMPStream::rec_cnt;
            fprintf(stderr, "CTRL: Window size: %i\n", RTMPStream::rec_window_size);
            break;
          case 6:
            RTMPStream::snd_window_size = ntohl(*(int*)next.data.c_str());
            //4 bytes window size, 1 byte limit type (ignored)
            fprintf(stderr, "CTRL: Set peer bandwidth: %i\n", RTMPStream::snd_window_size);
            break;
          case 8:
          case 9:
            if (Detail & (DETAIL_EXPLICIT | DETAIL_RECONSTRUCT)){
              F.ChunkLoader(next);
              if (Detail & DETAIL_EXPLICIT){
                std::cerr << "[" << F.tagTime() << "+" << F.offset() << "] " << F.tagType() << std::endl;
              }
              if (Detail & DETAIL_RECONSTRUCT){
                std::cout.write(F.data, F.len);
              }
            }
            break;
          case 15:
            fprintf(stderr, "Received AFM3 data message\n");
            break;
          case 16:
            fprintf(stderr, "Received AFM3 shared object\n");
            break;
          case 17: {
            fprintf(stderr, "Received AFM3 command message:\n");
            char soort = next.data[0];
            next.data = next.data.substr(1);
            if (soort == 0){
              amfdata = AMF::parse(next.data);
              std::cerr << amfdata.Print() << std::endl;
            }else{
              amf3data = AMF::parse3(next.data);
              amf3data.Print();
            }
          }
            break;
          case 18: {
            fprintf(stderr, "Received AFM0 data message (metadata):\n");
            amfdata = AMF::parse(next.data);
            amfdata.Print();
            if (Detail & DETAIL_RECONSTRUCT){
              F.ChunkLoader(next);
              std::cout.write(F.data, F.len);
            }
          }
            break;
          case 19:
            fprintf(stderr, "Received AFM0 shared object\n");
            break;
          case 20: { //AMF0 command message
            fprintf(stderr, "Received AFM0 command message:\n");
            amfdata = AMF::parse(next.data);
            std::cerr << amfdata.Print() << std::endl;
          }
            break;
          case 22:
            if (Detail & DETAIL_RECONSTRUCT){
              std::cout << next.data;
            }
            break;
          default:
            fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n");
            return 1;
            break;
        } //switch for type of chunk
      }else{ //if chunk parsed
        if (std::cin.good()){
          char newchar = std::cin.get();
          if (std::cin.good()){
            inbuffer += newchar;
            ++read_in;
          }
        }else{
          inbuffer.clear();
        }
      }
    }//while std::cin.good()
    fprintf(stderr, "No more readable data\n");
    return 0;
  }
}

int main(int argc, char ** argv){
  Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION);
  conf.addOption("detail",
      JSON::fromString(
          "{\"arg_num\":1, \"arg\":\"integer\", \"default\":0, \"help\":\"Bitmask, 1 = Reconstruct, 2 = Explicit media info, 4 = Verbose chunks\"}"));
  conf.parseArgs(argc, argv);

  return Analysers::analyseRTMP(conf);
} //main
<commit_msg>Improved RTMP analyzer speed and precision.<commit_after>/// \file rtmp_analyser.cpp
/// Debugging tool for RTMP data.
#include <iostream>
#include <fstream>
#include <string>

#include <cstdlib>

#include <mist/flv_tag.h>
#include <mist/amf.h>
#include <mist/rtmpchunks.h>
#include <mist/config.h>

#define DETAIL_RECONSTRUCT 1
#define DETAIL_EXPLICIT 2
#define DETAIL_VERBOSE 4

///\brief Holds everything unique to the analysers.
namespace Analysers {
  ///\brief Debugging tool for RTMP data.
  ///
  ///Expects RTMP data of one side of the conversation through stdin, outputs human-readable information to stderr.
  ///
  ///Will output FLV file to stdout, if available.
  ///
  ///Automatically skips the handshake data.
  ///\param conf The configuration parsed from the commandline.
  ///\return The return code of the analyser.
  int analyseRTMP(Util::Config conf){
    int Detail = conf.getInteger("detail");
    if (Detail > 0){
      fprintf(stderr, "Detail level set:\n");
      if (Detail & DETAIL_RECONSTRUCT){
        fprintf(stderr, " - Will reconstuct FLV file to stdout\n");
        std::cout.write(FLV::Header, 13);
      }
      if (Detail & DETAIL_EXPLICIT){
        fprintf(stderr, " - Will list explicit video/audio data information\n");
      }
      if (Detail & DETAIL_VERBOSE){
        fprintf(stderr, " - Will list verbose chunk information\n");
      }
    }

    std::string inbuffer;
    inbuffer.reserve(3073);
    while (std::cin.good() && inbuffer.size() < 3073){
      inbuffer += std::cin.get();
    } //read all of std::cin to temp
    inbuffer.erase(0, 3073); //strip the handshake part
    RTMPStream::rec_cnt += 3073;
    RTMPStream::Chunk next;
    FLV::Tag F; //FLV holder
    AMF::Object amfdata("empty", AMF::AMF0_DDV_CONTAINER);
    AMF::Object3 amf3data("empty", AMF::AMF3_DDV_CONTAINER);
    unsigned int read_in = 0;

    while (std::cin.good() || inbuffer.size()){
      if (next.Parse(inbuffer)){
        if (Detail & DETAIL_VERBOSE){
          fprintf(stderr, "Chunk info: [%#2X] CS ID %u, timestamp %u, len %u, type ID %u, Stream ID %u\n", next.headertype, next.cs_id, next.timestamp,
              next.len, next.msg_type_id, next.msg_stream_id);
        }
        switch (next.msg_type_id){
          case 0: //does not exist
            fprintf(stderr, "Error chunk @ %lu - CS%i, T%i, L%i, LL%i, MID%i\n", read_in-inbuffer.size(), next.cs_id, next.timestamp, next.real_len, next.len_left, next.msg_stream_id);
            return 0;
            break; //happens when connection breaks unexpectedly
          case 1: //set chunk size
            RTMPStream::chunk_rec_max = ntohl(*(int*)next.data.c_str());
            fprintf(stderr, "CTRL: Set chunk size: %i\n", RTMPStream::chunk_rec_max);
            break;
          case 2: //abort message - we ignore this one
            fprintf(stderr, "CTRL: Abort message: %i\n", ntohl(*(int*)next.data.c_str()));
            //4 bytes of stream id to drop
            break;
          case 3: //ack
            RTMPStream::snd_window_at = ntohl(*(int*)next.data.c_str());
            fprintf(stderr, "CTRL: Acknowledgement: %i\n", RTMPStream::snd_window_at);
            break;
          case 4: {
            short int ucmtype = ntohs(*(short int*)next.data.c_str());
            switch (ucmtype){
              case 0:
                fprintf(stderr, "CTRL: User control message: stream begin %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 1:
                fprintf(stderr, "CTRL: User control message: stream EOF %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 2:
                fprintf(stderr, "CTRL: User control message: stream dry %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 3:
                fprintf(stderr, "CTRL: User control message: setbufferlen %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 4:
                fprintf(stderr, "CTRL: User control message: streamisrecorded %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 6:
                fprintf(stderr, "CTRL: User control message: pingrequest %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 7:
                fprintf(stderr, "CTRL: User control message: pingresponse %u\n", ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
              case 31:
              case 32:
                //don't know, but not interesting anyway
                break;
              default:
                fprintf(stderr, "CTRL: User control message: UNKNOWN %hu - %u\n", ucmtype, ntohl(*(unsigned int*)(next.data.c_str()+2)));
                break;
            }
          }
            break;
          case 5: //window size of other end
            RTMPStream::rec_window_size = ntohl(*(int*)next.data.c_str());
            RTMPStream::rec_window_at = RTMPStream::rec_cnt;
            fprintf(stderr, "CTRL: Window size: %i\n", RTMPStream::rec_window_size);
            break;
          case 6:
            RTMPStream::snd_window_size = ntohl(*(int*)next.data.c_str());
            //4 bytes window size, 1 byte limit type (ignored)
            fprintf(stderr, "CTRL: Set peer bandwidth: %i\n", RTMPStream::snd_window_size);
            break;
          case 8:
          case 9:
            if (Detail & (DETAIL_EXPLICIT | DETAIL_RECONSTRUCT)){
              F.ChunkLoader(next);
              if (Detail & DETAIL_EXPLICIT){
                std::cerr << "[" << F.tagTime() << "+" << F.offset() << "] " << F.tagType() << std::endl;
              }
              if (Detail & DETAIL_RECONSTRUCT){
                std::cout.write(F.data, F.len);
              }
            }
            break;
          case 15:
            fprintf(stderr, "Received AFM3 data message\n");
            break;
          case 16:
            fprintf(stderr, "Received AFM3 shared object\n");
            break;
          case 17: {
            fprintf(stderr, "Received AFM3 command message:\n");
            char soort = next.data[0];
            next.data = next.data.substr(1);
            if (soort == 0){
              amfdata = AMF::parse(next.data);
              std::cerr << amfdata.Print() << std::endl;
            }else{
              amf3data = AMF::parse3(next.data);
              amf3data.Print();
            }
          }
            break;
          case 18: {
            fprintf(stderr, "Received AFM0 data message (metadata):\n");
            amfdata = AMF::parse(next.data);
            amfdata.Print();
            if (Detail & DETAIL_RECONSTRUCT){
              F.ChunkLoader(next);
              std::cout.write(F.data, F.len);
            }
          }
            break;
          case 19:
            fprintf(stderr, "Received AFM0 shared object\n");
            break;
          case 20: { //AMF0 command message
            fprintf(stderr, "Received AFM0 command message:\n");
            amfdata = AMF::parse(next.data);
            std::cerr << amfdata.Print() << std::endl;
          }
            break;
          case 22:
            if (Detail & DETAIL_RECONSTRUCT){
              std::cout << next.data;
            }
            break;
          default:
            fprintf(stderr, "Unknown chunk received! Probably protocol corruption, stopping parsing of incoming data.\n");
            return 1;
            break;
        } //switch for type of chunk
      }else{ //if chunk parsed
        if (std::cin.good()){
          unsigned int charCount = 0;
          inbuffer.reserve(4096);
          while (std::cin.good() && charCount < 4096){
            char newchar = std::cin.get();
            if (std::cin.good()){
              inbuffer += newchar;
              ++read_in;
              ++charCount;
            }
          }
        }else{
          inbuffer.clear();
        }
      }
    }//while std::cin.good()
    fprintf(stderr, "No more readable data\n");
    return 0;
  }
}

int main(int argc, char ** argv){
  Util::Config conf = Util::Config(argv[0], PACKAGE_VERSION);
  conf.addOption("detail",
      JSON::fromString(
          "{\"arg_num\":1, \"arg\":\"integer\", \"default\":0, \"help\":\"Bitmask, 1 = Reconstruct, 2 = Explicit media info, 4 = Verbose chunks\"}"));
  conf.parseArgs(argc, argv);

  return Analysers::analyseRTMP(conf);
} //main
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkAndroidCodec.h"
#include "SkAnimatedImage.h"
#include "SkCanvas.h"
#include "SkCodec.h"
#include "SkCodecPriv.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
        SkISize scaledSize, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
    if (!codec) {
        return nullptr;
    }

    SkISize decodeSize = scaledSize;
    auto decodeInfo = codec->getInfo();
    if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP
            && scaledSize.width()  < decodeInfo.width()
            && scaledSize.height() < decodeInfo.height()) {
        // libwebp can decode to arbitrary smaller sizes.
        decodeInfo = decodeInfo.makeWH(decodeSize.width(), decodeSize.height());
    }

    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, std::move(postProcess)));
    if (!image->fActiveFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    return image;
}

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
    if (!codec) {
        return nullptr;
    }

    const auto decodeInfo = codec->getInfo();
    const auto scaledSize = decodeInfo.dimensions();
    const auto cropRect   = SkIRect::MakeSize(scaledSize);
    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, nullptr));

    if (!image->fActiveFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    SkASSERT(image->fSimple);
    return image;
}

SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec, SkISize scaledSize,
        SkImageInfo decodeInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess)
    : fCodec(std::move(codec))
    , fScaledSize(scaledSize)
    , fDecodeInfo(decodeInfo)
    , fCropRect(cropRect)
    , fPostProcess(std::move(postProcess))
    , fFrameCount(fCodec->codec()->getFrameCount())
    , fSimple(fScaledSize == fDecodeInfo.dimensions() && !fPostProcess
              && fCropRect == fDecodeInfo.bounds())
    , fFinished(false)
    , fRepetitionCount(fCodec->codec()->getRepetitionCount())
    , fRepetitionsCompleted(0)
{
    if (!fActiveFrame.fBitmap.tryAllocPixels(fDecodeInfo)) {
        return;
    }

    if (!fSimple) {
        fMatrix = SkMatrix::MakeTrans(-fCropRect.fLeft, -fCropRect.fTop);
        float scaleX = (float) fScaledSize.width()  / fDecodeInfo.width();
        float scaleY = (float) fScaledSize.height() / fDecodeInfo.height();
        fMatrix.preConcat(SkMatrix::MakeScale(scaleX, scaleY));
    }
    this->decodeNextFrame();
}

SkAnimatedImage::~SkAnimatedImage() { }

SkRect SkAnimatedImage::onGetBounds() {
    return SkRect::MakeIWH(fCropRect.width(), fCropRect.height());
}

SkAnimatedImage::Frame::Frame()
    : fIndex(SkCodec::kNone)
{}

bool SkAnimatedImage::Frame::copyTo(Frame* dst) const {
    if (dst->fBitmap.getPixels()) {
        dst->fBitmap.setAlphaType(fBitmap.alphaType());
    } else if (!dst->fBitmap.tryAllocPixels(fBitmap.info())) {
        return false;
    }

    memcpy(dst->fBitmap.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize());
    dst->fIndex = fIndex;
    dst->fDisposalMethod = fDisposalMethod;
    return true;
}

void SkAnimatedImage::reset() {
    fFinished = false;
    fRepetitionsCompleted = 0;
    if (fActiveFrame.fIndex == 0) {
        // Already showing the first frame.
        return;
    }

    if (fRestoreFrame.fIndex == 0) {
        SkTSwap(fActiveFrame, fRestoreFrame);
        // Now we're showing the first frame.
        return;
    }

    fActiveFrame.fIndex = SkCodec::kNone;
    this->decodeNextFrame();
}

static bool is_restore_previous(SkCodecAnimation::DisposalMethod dispose) {
    return SkCodecAnimation::DisposalMethod::kRestorePrevious == dispose;
}

int SkAnimatedImage::computeNextFrame(int current, bool* animationEnded) {
    SkASSERT(animationEnded != nullptr);
    *animationEnded = false;

    const int frameToDecode = current + 1;
    if (frameToDecode == fFrameCount - 1) {
        // Final frame. Check to determine whether to stop.
        fRepetitionsCompleted++;
        if (fRepetitionCount != SkCodec::kRepetitionCountInfinite
                && fRepetitionsCompleted > fRepetitionCount) {
            *animationEnded = true;
        }
    } else if (frameToDecode == fFrameCount) {
        return 0;
    }
    return frameToDecode;
}

double SkAnimatedImage::finish() {
    fFinished = true;
    fCurrentFrameDuration = kFinished;
    return kFinished;
}

int SkAnimatedImage::decodeNextFrame() {
    if (fFinished) {
        return kFinished;
    }

    bool animationEnded = false;
    int frameToDecode = this->computeNextFrame(fActiveFrame.fIndex, &animationEnded);

    SkCodec::FrameInfo frameInfo;
    if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
        if (!frameInfo.fFullyReceived) {
            SkCodecPrintf("Frame %i not fully received\n", frameToDecode);
            return this->finish();
        }

        fCurrentFrameDuration = frameInfo.fDuration;
    } else {
        animationEnded = true;
        if (0 == frameToDecode) {
            // Static image. This is okay.
            frameInfo.fRequiredFrame = SkCodec::kNone;
            frameInfo.fAlphaType = fCodec->getInfo().alphaType();
            // These fields won't be read.
            frameInfo.fDuration = INT_MAX;
            frameInfo.fFullyReceived = true;
            fCurrentFrameDuration = kFinished;
        } else {
            SkCodecPrintf("Error getting frameInfo for frame %i\n",
                          frameToDecode);
            return this->finish();
        }
    }

    if (frameToDecode == fActiveFrame.fIndex) {
        if (animationEnded) {
            return this->finish();
        }
        return fCurrentFrameDuration;
    }

    if (frameToDecode == fRestoreFrame.fIndex) {
        SkTSwap(fActiveFrame, fRestoreFrame);
        if (animationEnded) {
            return this->finish();
        }
        return fCurrentFrameDuration;
    }

    // The following code makes an effort to avoid overwriting a frame that will
    // be used again. If frame |i| is_restore_previous, frame |i+1| will not
    // depend on frame |i|, so do not overwrite frame |i-1|, which may be needed
    // for frame |i+1|.
    // We could be even smarter about which frames to save by looking at the
    // entire dependency chain.
    SkCodec::Options options;
    options.fFrameIndex = frameToDecode;
    if (frameInfo.fRequiredFrame == SkCodec::kNone) {
        if (is_restore_previous(frameInfo.fDisposalMethod)) {
            // frameToDecode will be discarded immediately after drawing, so
            // do not overwrite a frame which could possibly be used in the
            // future.
            if (fActiveFrame.fIndex != SkCodec::kNone &&
                    !is_restore_previous(fActiveFrame.fDisposalMethod)) {
                SkTSwap(fActiveFrame, fRestoreFrame);
            }
        }
    } else {
        auto validPriorFrame = [&frameInfo, &frameToDecode](const Frame& frame) {
            if (SkCodec::kNone == frame.fIndex || is_restore_previous(frame.fDisposalMethod)) {
                return false;
            }

            return frame.fIndex >= frameInfo.fRequiredFrame && frame.fIndex < frameToDecode;
        };
        if (validPriorFrame(fActiveFrame)) {
            if (is_restore_previous(frameInfo.fDisposalMethod)) {
                // fActiveFrame is a good frame to use for this one, but we
                // don't want to overwrite it.
                fActiveFrame.copyTo(&fRestoreFrame);
            }
            options.fPriorFrame = fActiveFrame.fIndex;
        } else if (validPriorFrame(fRestoreFrame)) {
            if (!is_restore_previous(frameInfo.fDisposalMethod)) {
                SkTSwap(fActiveFrame, fRestoreFrame);
            } else if (!fRestoreFrame.copyTo(&fActiveFrame)) {
                SkCodecPrintf("Failed to restore frame\n");
                return this->finish();
            }
            options.fPriorFrame = fActiveFrame.fIndex;
        }
    }

    auto alphaType = kOpaque_SkAlphaType == frameInfo.fAlphaType ?
                     kOpaque_SkAlphaType : kPremul_SkAlphaType;
    SkBitmap* dst = &fActiveFrame.fBitmap;
    if (dst->getPixels()) {
        SkAssertResult(dst->setAlphaType(alphaType));
    } else {
        auto info = fDecodeInfo.makeAlphaType(alphaType);
        if (!dst->tryAllocPixels(info)) {
            return this->finish();
        }
    }

    auto result = fCodec->codec()->getPixels(dst->info(), dst->getPixels(), dst->rowBytes(),
                                             &options);
    if (result != SkCodec::kSuccess) {
        SkCodecPrintf("error %i, frame %i of %i\n", result, frameToDecode, fFrameCount);
        return this->finish();
    }

    fActiveFrame.fIndex = frameToDecode;
    fActiveFrame.fDisposalMethod = frameInfo.fDisposalMethod;

    if (animationEnded) {
        return this->finish();
    }
    return fCurrentFrameDuration;
}

void SkAnimatedImage::onDraw(SkCanvas* canvas) {
    if (fSimple) {
        canvas->drawBitmap(fActiveFrame.fBitmap, 0, 0);
        return;
    }

    SkRect bounds = this->getBounds();
    if (fPostProcess) {
        canvas->saveLayer(&bounds, nullptr);
    }
    {
        SkAutoCanvasRestore acr(canvas, fPostProcess);
        canvas->concat(fMatrix);
        SkPaint paint;
        paint.setBlendMode(SkBlendMode::kSrc);
        paint.setFilterQuality(kLow_SkFilterQuality);
        canvas->drawBitmap(fActiveFrame.fBitmap, 0, 0, &paint);
    }
    if (fPostProcess) {
        canvas->drawPicture(fPostProcess);
        canvas->restore();
    }
}

void SkAnimatedImage::setRepetitionCount(int newCount) {
    fRepetitionCount = newCount;
}
<commit_msg>Address MSAN bug in SkAnimatedImage::decodeNextFrame<commit_after>/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkAndroidCodec.h"
#include "SkAnimatedImage.h"
#include "SkCanvas.h"
#include "SkCodec.h"
#include "SkCodecPriv.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec,
        SkISize scaledSize, SkIRect cropRect, sk_sp<SkPicture> postProcess) {
    if (!codec) {
        return nullptr;
    }

    SkISize decodeSize = scaledSize;
    auto decodeInfo = codec->getInfo();
    if (codec->getEncodedFormat() == SkEncodedImageFormat::kWEBP
            && scaledSize.width()  < decodeInfo.width()
            && scaledSize.height() < decodeInfo.height()) {
        // libwebp can decode to arbitrary smaller sizes.
        decodeInfo = decodeInfo.makeWH(decodeSize.width(), decodeSize.height());
    }

    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, std::move(postProcess)));
    if (!image->fActiveFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    return image;
}

sk_sp<SkAnimatedImage> SkAnimatedImage::Make(std::unique_ptr<SkAndroidCodec> codec) {
    if (!codec) {
        return nullptr;
    }

    const auto decodeInfo = codec->getInfo();
    const auto scaledSize = decodeInfo.dimensions();
    const auto cropRect   = SkIRect::MakeSize(scaledSize);
    auto image = sk_sp<SkAnimatedImage>(new SkAnimatedImage(std::move(codec), scaledSize,
                decodeInfo, cropRect, nullptr));

    if (!image->fActiveFrame.fBitmap.getPixels()) {
        // tryAllocPixels failed.
        return nullptr;
    }

    SkASSERT(image->fSimple);
    return image;
}

SkAnimatedImage::SkAnimatedImage(std::unique_ptr<SkAndroidCodec> codec, SkISize scaledSize,
        SkImageInfo decodeInfo, SkIRect cropRect, sk_sp<SkPicture> postProcess)
    : fCodec(std::move(codec))
    , fScaledSize(scaledSize)
    , fDecodeInfo(decodeInfo)
    , fCropRect(cropRect)
    , fPostProcess(std::move(postProcess))
    , fFrameCount(fCodec->codec()->getFrameCount())
    , fSimple(fScaledSize == fDecodeInfo.dimensions() && !fPostProcess
              && fCropRect == fDecodeInfo.bounds())
    , fFinished(false)
    , fRepetitionCount(fCodec->codec()->getRepetitionCount())
    , fRepetitionsCompleted(0)
{
    if (!fActiveFrame.fBitmap.tryAllocPixels(fDecodeInfo)) {
        return;
    }

    if (!fSimple) {
        fMatrix = SkMatrix::MakeTrans(-fCropRect.fLeft, -fCropRect.fTop);
        float scaleX = (float) fScaledSize.width()  / fDecodeInfo.width();
        float scaleY = (float) fScaledSize.height() / fDecodeInfo.height();
        fMatrix.preConcat(SkMatrix::MakeScale(scaleX, scaleY));
    }
    this->decodeNextFrame();
}

SkAnimatedImage::~SkAnimatedImage() { }

SkRect SkAnimatedImage::onGetBounds() {
    return SkRect::MakeIWH(fCropRect.width(), fCropRect.height());
}

SkAnimatedImage::Frame::Frame()
    : fIndex(SkCodec::kNone)
{}

bool SkAnimatedImage::Frame::copyTo(Frame* dst) const {
    if (dst->fBitmap.getPixels()) {
        dst->fBitmap.setAlphaType(fBitmap.alphaType());
    } else if (!dst->fBitmap.tryAllocPixels(fBitmap.info())) {
        return false;
    }

    memcpy(dst->fBitmap.getPixels(), fBitmap.getPixels(), fBitmap.computeByteSize());
    dst->fIndex = fIndex;
    dst->fDisposalMethod = fDisposalMethod;
    return true;
}

void SkAnimatedImage::reset() {
    fFinished = false;
    fRepetitionsCompleted = 0;
    if (fActiveFrame.fIndex == 0) {
        // Already showing the first frame.
        return;
    }

    if (fRestoreFrame.fIndex == 0) {
        SkTSwap(fActiveFrame, fRestoreFrame);
        // Now we're showing the first frame.
        return;
    }

    fActiveFrame.fIndex = SkCodec::kNone;
    this->decodeNextFrame();
}

static bool is_restore_previous(SkCodecAnimation::DisposalMethod dispose) {
    return SkCodecAnimation::DisposalMethod::kRestorePrevious == dispose;
}

int SkAnimatedImage::computeNextFrame(int current, bool* animationEnded) {
    SkASSERT(animationEnded != nullptr);
    *animationEnded = false;

    const int frameToDecode = current + 1;
    if (frameToDecode == fFrameCount - 1) {
        // Final frame. Check to determine whether to stop.
        fRepetitionsCompleted++;
        if (fRepetitionCount != SkCodec::kRepetitionCountInfinite
                && fRepetitionsCompleted > fRepetitionCount) {
            *animationEnded = true;
        }
    } else if (frameToDecode == fFrameCount) {
        return 0;
    }
    return frameToDecode;
}

double SkAnimatedImage::finish() {
    fFinished = true;
    fCurrentFrameDuration = kFinished;
    return kFinished;
}

int SkAnimatedImage::decodeNextFrame() {
    if (fFinished) {
        return kFinished;
    }

    bool animationEnded = false;
    int frameToDecode = this->computeNextFrame(fActiveFrame.fIndex, &animationEnded);

    SkCodec::FrameInfo frameInfo;
    if (fCodec->codec()->getFrameInfo(frameToDecode, &frameInfo)) {
        if (!frameInfo.fFullyReceived) {
            SkCodecPrintf("Frame %i not fully received\n", frameToDecode);
            return this->finish();
        }

        fCurrentFrameDuration = frameInfo.fDuration;
    } else {
        animationEnded = true;
        if (0 == frameToDecode) {
            // Static image. This is okay.
            frameInfo.fRequiredFrame = SkCodec::kNone;
            frameInfo.fAlphaType = fCodec->getInfo().alphaType();
            frameInfo.fDisposalMethod = SkCodecAnimation::DisposalMethod::kKeep;
            // These fields won't be read.
            frameInfo.fDuration = INT_MAX;
            frameInfo.fFullyReceived = true;
            fCurrentFrameDuration = kFinished;
        } else {
            SkCodecPrintf("Error getting frameInfo for frame %i\n",
                          frameToDecode);
            return this->finish();
        }
    }

    if (frameToDecode == fActiveFrame.fIndex) {
        if (animationEnded) {
            return this->finish();
        }
        return fCurrentFrameDuration;
    }

    if (frameToDecode == fRestoreFrame.fIndex) {
        SkTSwap(fActiveFrame, fRestoreFrame);
        if (animationEnded) {
            return this->finish();
        }
        return fCurrentFrameDuration;
    }

    // The following code makes an effort to avoid overwriting a frame that will
    // be used again. If frame |i| is_restore_previous, frame |i+1| will not
    // depend on frame |i|, so do not overwrite frame |i-1|, which may be needed
    // for frame |i+1|.
    // We could be even smarter about which frames to save by looking at the
    // entire dependency chain.
    SkCodec::Options options;
    options.fFrameIndex = frameToDecode;
    if (frameInfo.fRequiredFrame == SkCodec::kNone) {
        if (is_restore_previous(frameInfo.fDisposalMethod)) {
            // frameToDecode will be discarded immediately after drawing, so
            // do not overwrite a frame which could possibly be used in the
            // future.
            if (fActiveFrame.fIndex != SkCodec::kNone &&
                    !is_restore_previous(fActiveFrame.fDisposalMethod)) {
                SkTSwap(fActiveFrame, fRestoreFrame);
            }
        }
    } else {
        auto validPriorFrame = [&frameInfo, &frameToDecode](const Frame& frame) {
            if (SkCodec::kNone == frame.fIndex || is_restore_previous(frame.fDisposalMethod)) {
                return false;
            }

            return frame.fIndex >= frameInfo.fRequiredFrame && frame.fIndex < frameToDecode;
        };
        if (validPriorFrame(fActiveFrame)) {
            if (is_restore_previous(frameInfo.fDisposalMethod)) {
                // fActiveFrame is a good frame to use for this one, but we
                // don't want to overwrite it.
                fActiveFrame.copyTo(&fRestoreFrame);
            }
            options.fPriorFrame = fActiveFrame.fIndex;
        } else if (validPriorFrame(fRestoreFrame)) {
            if (!is_restore_previous(frameInfo.fDisposalMethod)) {
                SkTSwap(fActiveFrame, fRestoreFrame);
            } else if (!fRestoreFrame.copyTo(&fActiveFrame)) {
                SkCodecPrintf("Failed to restore frame\n");
                return this->finish();
            }
            options.fPriorFrame = fActiveFrame.fIndex;
        }
    }

    auto alphaType = kOpaque_SkAlphaType == frameInfo.fAlphaType ?
                     kOpaque_SkAlphaType : kPremul_SkAlphaType;
    SkBitmap* dst = &fActiveFrame.fBitmap;
    if (dst->getPixels()) {
        SkAssertResult(dst->setAlphaType(alphaType));
    } else {
        auto info = fDecodeInfo.makeAlphaType(alphaType);
        if (!dst->tryAllocPixels(info)) {
            return this->finish();
        }
    }

    auto result = fCodec->codec()->getPixels(dst->info(), dst->getPixels(), dst->rowBytes(),
                                             &options);
    if (result != SkCodec::kSuccess) {
        SkCodecPrintf("error %i, frame %i of %i\n", result, frameToDecode, fFrameCount);
        return this->finish();
    }

    fActiveFrame.fIndex = frameToDecode;
    fActiveFrame.fDisposalMethod = frameInfo.fDisposalMethod;

    if (animationEnded) {
        return this->finish();
    }
    return fCurrentFrameDuration;
}

void SkAnimatedImage::onDraw(SkCanvas* canvas) {
    if (fSimple) {
        canvas->drawBitmap(fActiveFrame.fBitmap, 0, 0);
        return;
    }

    SkRect bounds = this->getBounds();
    if (fPostProcess) {
        canvas->saveLayer(&bounds, nullptr);
    }
    {
        SkAutoCanvasRestore acr(canvas, fPostProcess);
        canvas->concat(fMatrix);
        SkPaint paint;
        paint.setBlendMode(SkBlendMode::kSrc);
        paint.setFilterQuality(kLow_SkFilterQuality);
        canvas->drawBitmap(fActiveFrame.fBitmap, 0, 0, &paint);
    }
    if (fPostProcess) {
        canvas->drawPicture(fPostProcess);
        canvas->restore();
    }
}

void SkAnimatedImage::setRepetitionCount(int newCount) {
    fRepetitionCount = newCount;
}
<|endoftext|>


<commit_before>#include <LightGBM/application.h>

#include <LightGBM/utils/common.h>
#include <LightGBM/utils/text_reader.h>

#include <LightGBM/network.h>
#include <LightGBM/dataset.h>
#include <LightGBM/boosting.h>
#include <LightGBM/objective_function.h>
#include <LightGBM/metric.h>

#include "predictor.hpp"

#include <omp.h>

#include <cstdio>
#include <ctime>

#include <chrono>
#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

namespace LightGBM {

Application::Application(int argc, char** argv)
  :train_data_(nullptr), boosting_(nullptr), objective_fun_(nullptr) {
  LoadParameters(argc, argv);
  // set number of threads for openmp
  if (config_.num_threads > 0) {
    omp_set_num_threads(config_.num_threads);
  }
}

Application::~Application() {
  if (train_data_ != nullptr) { delete train_data_; }
  for (auto& data : valid_datas_) {
    if (data != nullptr) { delete data; }
  }
  valid_datas_.clear();
  for (auto& metric : train_metric_) {
    if (metric != nullptr) { delete metric; }
  }
  for (auto& metric : valid_metrics_) {
    for (auto& sub_metric : metric) {
      if (sub_metric != nullptr) { delete sub_metric; }
    }
  }
  valid_metrics_.clear();
  if (boosting_ != nullptr) { delete boosting_; }
  if (objective_fun_ != nullptr) { delete objective_fun_; }
  if (config_.is_parallel) {
    Network::Dispose();
  }
}

void Application::LoadParameters(int argc, char** argv) {
  std::unordered_map<std::string, std::string> params;
  for (int i = 0; i < argc; ++i) {
    std::vector<std::string> tmp_strs = Common::Split(argv[i], '=');
    if (tmp_strs.size() == 2) {
      std::string key = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[0]));
      std::string value = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[1]));
      if (key.size() <= 0) {
        continue;
      }
      params[key] = value;
    }
  }
  // check for alias
  ParameterAlias::KeyAliasTransform(&params);
  // read parameters from config file
  if (params.count("config_file") > 0) {
    TextReader<size_t> config_reader(params["config_file"].c_str());
    config_reader.ReadAllLines();
    if (config_reader.Lines().size() > 0) {
      for (auto& line : config_reader.Lines()) {
        line = Common::Trim(line);
        // skip comment
        if (line.size() == 0 || line[0] == '#') {
          continue;
        }
        std::vector<std::string> tmp_strs = Common::Split(line.c_str(), '=');
        if (tmp_strs.size() == 2) {
          std::string key = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[0]));
          std::string value = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[1]));
          if (key.size() <= 0) {
            continue;
          }
          // Command line have higher priority
          if (params.count(key) == 0) {
            params[key] = value;
          }
        }
      }
    } else {
      Log::Stdout("config file: %s doesn't exist, will ignore",
                                params["config_file"].c_str());
    }
  }
  // check for alias again
  ParameterAlias::KeyAliasTransform(&params);
  // load configs
  config_.Set(params);
  Log::Stdout("finished load parameters");
}

void Application::LoadData() {
  auto start_time = std::chrono::high_resolution_clock::now();
  // predition is needed if using input initial model(continued train)
  PredictFunction predict_fun = nullptr;
  Predictor* predictor = nullptr;
  // load init model
  if (config_.io_config.input_model.size() > 0) {
    LoadModel();
    if (boosting_->NumberOfSubModels() > 0) {
      predictor = new Predictor(boosting_, config_.io_config.is_sigmoid);
      predict_fun =
        [&predictor](const std::vector<std::pair<int, double>>& features) {
        return predictor->PredictRawOneLine(features);
      };
    }
  }
  // sync up random seed for data partition
  if (config_.is_parallel_find_bin) {
    config_.io_config.data_random_seed =
       GlobalSyncUpByMin<int>(config_.io_config.data_random_seed);
  }
  train_data_ = new Dataset(config_.io_config.data_filename.c_str(),
                         config_.io_config.input_init_score.c_str(),
                                          config_.io_config.max_bin,
                                 config_.io_config.data_random_seed,
                                 config_.io_config.is_enable_sparse,
                                                       predict_fun);
  // load Training data
  if (config_.is_parallel_find_bin) {
    // load data for parallel training
    train_data_->LoadTrainData(Network::rank(), Network::num_machines(),
                                     config_.io_config.is_pre_partition,
                               config_.io_config.use_two_round_loading);
  } else {
    // load data for single machine
    train_data_->LoadTrainData(config_.io_config.use_two_round_loading);
  }
  // need save binary file
  if (config_.io_config.is_save_binary_file) {
    train_data_->SaveBinaryFile();
  }
  // create training metric
  if (config_.metric_config.is_provide_training_metric) {
    for (auto metric_type : config_.metric_types) {
      Metric* metric =
        Metric::CreateMetric(metric_type, config_.metric_config);
      if (metric == nullptr) { continue; }
      metric->Init("training", train_data_->metadata(),
                              train_data_->num_data());
      train_metric_.push_back(metric);
    }
  }
  // Add validation data, if exists
  for (size_t i = 0; i < config_.io_config.valid_data_filenames.size(); ++i) {
    // add
    valid_datas_.push_back(
      new Dataset(config_.io_config.valid_data_filenames[i].c_str(),
                                          config_.io_config.max_bin,
                                 config_.io_config.data_random_seed,
                                 config_.io_config.is_enable_sparse,
                                                      predict_fun));
    // load validation data like train data
    valid_datas_.back()->LoadValidationData(train_data_,
                config_.io_config.use_two_round_loading);
    // need save binary file
    if (config_.io_config.is_save_binary_file) {
      valid_datas_.back()->SaveBinaryFile();
    }

    // add metric for validation data
    valid_metrics_.emplace_back();
    for (auto metric_type : config_.metric_types) {
      Metric* metric = Metric::CreateMetric(metric_type, config_.metric_config);
      if (metric == nullptr) { continue; }
      metric->Init(config_.io_config.valid_data_filenames[i].c_str(),
                                     valid_datas_.back()->metadata(),
                                    valid_datas_.back()->num_data());
      valid_metrics_.back().push_back(metric);
    }
  }
  if (predictor != nullptr) {
    delete predictor;
  }
  auto end_time = std::chrono::high_resolution_clock::now();
  // output used time on each iteration
  Log::Stdout("Finish loading data, use %f seconds ",
    std::chrono::duration<double, std::milli>(end_time - start_time) * 1e-3);
}

void Application::InitTrain() {
  if (config_.is_parallel) {
    // need init network
    Network::Init(config_.network_config);
    Log::Stdout("finish network initialization");
    // sync global random seed for feature patition
    if (config_.boosting_type == BoostingType::kGBDT) {
      GBDTConfig* gbdt_config =
        dynamic_cast<GBDTConfig*>(config_.boosting_config);
      gbdt_config->tree_config.feature_fraction_seed =
        GlobalSyncUpByMin<int>(gbdt_config->tree_config.feature_fraction_seed);
      gbdt_config->tree_config.feature_fraction =
        GlobalSyncUpByMin<double>(gbdt_config->tree_config.feature_fraction);
    }
  }
  // create boosting
  boosting_ =
    Boosting::CreateBoosting(config_.boosting_type, config_.boosting_config);
  // create objective function
  objective_fun_ =
    ObjectiveFunction::CreateObjectiveFunction(config_.objective_type,
                                             config_.objective_config);
  // load training data
  LoadData();
  // initialize the objective function
  objective_fun_->Init(train_data_->metadata(), train_data_->num_data());
  // initialize the boosting
  boosting_->Init(train_data_, objective_fun_,
    ConstPtrInVectorWarpper<Metric>(train_metric_),
            config_.io_config.output_model.c_str());
  // add validation data into boosting
  for (size_t i = 0; i < valid_datas_.size(); ++i) {
    boosting_->AddDataset(valid_datas_[i],
      ConstPtrInVectorWarpper<Metric>(valid_metrics_[i]));
  }
  Log::Stdout("finish training init");
}

void Application::Train() {
  Log::Stdout("start train");
  boosting_->Train();
  Log::Stdout("finish train");
}

void Application::Predict() {
  // create predictor
  Predictor predictor(boosting_, config_.io_config.is_sigmoid);
  predictor.Predict(config_.io_config.data_filename.c_str(),
    config_.io_config.data_has_label, config_.io_config.output_result.c_str());
  Log::Stdout("finish predict");
}

void Application::InitPredict() {
  boosting_ =
    Boosting::CreateBoosting(config_.boosting_type, config_.boosting_config);
  LoadModel();
  Log::Stdout("finish predict init");
}

void Application::LoadModel() {
  TextReader<size_t> model_reader(config_.io_config.input_model.c_str());
  model_reader.ReadAllLines();
  std::stringstream ss;
  for (auto& line : model_reader.Lines()) {
    ss << line << '\n';
  }
  boosting_->ModelsFromString(ss.str(), config_.io_config.num_model_predict);
}

template<typename T>
T Application::GlobalSyncUpByMin(T& local) {
  T global = local;
  if (!config_.is_parallel) {
    // not need to sync if not parallel learning
    return global;
  }
  Network::Allreduce(reinterpret_cast<char*>(&local),
                         sizeof(local), sizeof(local),
                     reinterpret_cast<char*>(&global),
              [](const char* src, char* dst, int len) {
    int used_size = 0;
    const int type_size = sizeof(T);
    const T *p1;
    T *p2;
    while (used_size < len) {
      p1 = reinterpret_cast<const T *>(src);
      p2 = reinterpret_cast<T *>(dst);
      if (*p1 < *p2) {
        std::memcpy(dst, src, type_size);
      }
      src += type_size;
      dst += type_size;
      used_size += type_size;
    }
  });
  return global;
}

}  // namespace LightGBM
<commit_msg>better parameter parse logic.<commit_after>#include <LightGBM/application.h>

#include <LightGBM/utils/common.h>
#include <LightGBM/utils/text_reader.h>

#include <LightGBM/network.h>
#include <LightGBM/dataset.h>
#include <LightGBM/boosting.h>
#include <LightGBM/objective_function.h>
#include <LightGBM/metric.h>

#include "predictor.hpp"

#include <omp.h>

#include <cstdio>
#include <ctime>

#include <chrono>
#include <fstream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>

namespace LightGBM {

Application::Application(int argc, char** argv)
  :train_data_(nullptr), boosting_(nullptr), objective_fun_(nullptr) {
  LoadParameters(argc, argv);
  // set number of threads for openmp
  if (config_.num_threads > 0) {
    omp_set_num_threads(config_.num_threads);
  }
}

Application::~Application() {
  if (train_data_ != nullptr) { delete train_data_; }
  for (auto& data : valid_datas_) {
    if (data != nullptr) { delete data; }
  }
  valid_datas_.clear();
  for (auto& metric : train_metric_) {
    if (metric != nullptr) { delete metric; }
  }
  for (auto& metric : valid_metrics_) {
    for (auto& sub_metric : metric) {
      if (sub_metric != nullptr) { delete sub_metric; }
    }
  }
  valid_metrics_.clear();
  if (boosting_ != nullptr) { delete boosting_; }
  if (objective_fun_ != nullptr) { delete objective_fun_; }
  if (config_.is_parallel) {
    Network::Dispose();
  }
}

void Application::LoadParameters(int argc, char** argv) {
  std::unordered_map<std::string, std::string> params;
  for (int i = 1; i < argc; ++i) {
    std::vector<std::string> tmp_strs = Common::Split(argv[i], '=');
    if (tmp_strs.size() == 2) {
      std::string key = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[0]));
      std::string value = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[1]));
      if (key.size() <= 0) {
        continue;
      }
      params[key] = value;
    }
    else {
      Log::Stdout("Warning: unknown parameter in command line: %s", argv[i]);
    }
  }
  // check for alias
  ParameterAlias::KeyAliasTransform(&params);
  // read parameters from config file
  if (params.count("config_file") > 0) {
    TextReader<size_t> config_reader(params["config_file"].c_str());
    config_reader.ReadAllLines();
    if (config_reader.Lines().size() > 0) {
      for (auto& line : config_reader.Lines()) {
        // remove str after #
        if (line.size() > 0 && std::string::npos != line.find_first_of("#")) {
          line.erase(line.find_first_of("#"));
        }
        line = Common::Trim(line);
        if (line.size() == 0) {
          continue;
        }
        std::vector<std::string> tmp_strs = Common::Split(line.c_str(), '=');
        if (tmp_strs.size() == 2) {
          std::string key = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[0]));
          std::string value = Common::RemoveQuotationSymbol(Common::Trim(tmp_strs[1]));
          if (key.size() <= 0) {
            continue;
          }
          // Command line have higher priority
          if (params.count(key) == 0) {
            params[key] = value;
          }
        }
        else {
          Log::Stdout("Warning: unknown parameter in config file: %s", line.c_str());
        }
      }
    } else {
      Log::Stdout("config file: %s doesn't exist, will ignore",
                                params["config_file"].c_str());
    }
  }
  // check for alias again
  ParameterAlias::KeyAliasTransform(&params);
  // load configs
  config_.Set(params);
  Log::Stdout("finished load parameters");
}

void Application::LoadData() {
  auto start_time = std::chrono::high_resolution_clock::now();
  // predition is needed if using input initial model(continued train)
  PredictFunction predict_fun = nullptr;
  Predictor* predictor = nullptr;
  // load init model
  if (config_.io_config.input_model.size() > 0) {
    LoadModel();
    if (boosting_->NumberOfSubModels() > 0) {
      predictor = new Predictor(boosting_, config_.io_config.is_sigmoid);
      predict_fun =
        [&predictor](const std::vector<std::pair<int, double>>& features) {
        return predictor->PredictRawOneLine(features);
      };
    }
  }
  // sync up random seed for data partition
  if (config_.is_parallel_find_bin) {
    config_.io_config.data_random_seed =
       GlobalSyncUpByMin<int>(config_.io_config.data_random_seed);
  }
  train_data_ = new Dataset(config_.io_config.data_filename.c_str(),
                         config_.io_config.input_init_score.c_str(),
                                          config_.io_config.max_bin,
                                 config_.io_config.data_random_seed,
                                 config_.io_config.is_enable_sparse,
                                                       predict_fun);
  // load Training data
  if (config_.is_parallel_find_bin) {
    // load data for parallel training
    train_data_->LoadTrainData(Network::rank(), Network::num_machines(),
                                     config_.io_config.is_pre_partition,
                               config_.io_config.use_two_round_loading);
  } else {
    // load data for single machine
    train_data_->LoadTrainData(config_.io_config.use_two_round_loading);
  }
  // need save binary file
  if (config_.io_config.is_save_binary_file) {
    train_data_->SaveBinaryFile();
  }
  // create training metric
  if (config_.metric_config.is_provide_training_metric) {
    for (auto metric_type : config_.metric_types) {
      Metric* metric =
        Metric::CreateMetric(metric_type, config_.metric_config);
      if (metric == nullptr) { continue; }
      metric->Init("training", train_data_->metadata(),
                              train_data_->num_data());
      train_metric_.push_back(metric);
    }
  }
  // Add validation data, if exists
  for (size_t i = 0; i < config_.io_config.valid_data_filenames.size(); ++i) {
    // add
    valid_datas_.push_back(
      new Dataset(config_.io_config.valid_data_filenames[i].c_str(),
                                          config_.io_config.max_bin,
                                 config_.io_config.data_random_seed,
                                 config_.io_config.is_enable_sparse,
                                                      predict_fun));
    // load validation data like train data
    valid_datas_.back()->LoadValidationData(train_data_,
                config_.io_config.use_two_round_loading);
    // need save binary file
    if (config_.io_config.is_save_binary_file) {
      valid_datas_.back()->SaveBinaryFile();
    }

    // add metric for validation data
    valid_metrics_.emplace_back();
    for (auto metric_type : config_.metric_types) {
      Metric* metric = Metric::CreateMetric(metric_type, config_.metric_config);
      if (metric == nullptr) { continue; }
      metric->Init(config_.io_config.valid_data_filenames[i].c_str(),
                                     valid_datas_.back()->metadata(),
                                    valid_datas_.back()->num_data());
      valid_metrics_.back().push_back(metric);
    }
  }
  if (predictor != nullptr) {
    delete predictor;
  }
  auto end_time = std::chrono::high_resolution_clock::now();
  // output used time on each iteration
  Log::Stdout("Finish loading data, use %f seconds ",
    std::chrono::duration<double, std::milli>(end_time - start_time) * 1e-3);
}

void Application::InitTrain() {
  if (config_.is_parallel) {
    // need init network
    Network::Init(config_.network_config);
    Log::Stdout("finish network initialization");
    // sync global random seed for feature patition
    if (config_.boosting_type == BoostingType::kGBDT) {
      GBDTConfig* gbdt_config =
        dynamic_cast<GBDTConfig*>(config_.boosting_config);
      gbdt_config->tree_config.feature_fraction_seed =
        GlobalSyncUpByMin<int>(gbdt_config->tree_config.feature_fraction_seed);
      gbdt_config->tree_config.feature_fraction =
        GlobalSyncUpByMin<double>(gbdt_config->tree_config.feature_fraction);
    }
  }
  // create boosting
  boosting_ =
    Boosting::CreateBoosting(config_.boosting_type, config_.boosting_config);
  // create objective function
  objective_fun_ =
    ObjectiveFunction::CreateObjectiveFunction(config_.objective_type,
                                             config_.objective_config);
  // load training data
  LoadData();
  // initialize the objective function
  objective_fun_->Init(train_data_->metadata(), train_data_->num_data());
  // initialize the boosting
  boosting_->Init(train_data_, objective_fun_,
    ConstPtrInVectorWarpper<Metric>(train_metric_),
            config_.io_config.output_model.c_str());
  // add validation data into boosting
  for (size_t i = 0; i < valid_datas_.size(); ++i) {
    boosting_->AddDataset(valid_datas_[i],
      ConstPtrInVectorWarpper<Metric>(valid_metrics_[i]));
  }
  Log::Stdout("finish training init");
}

void Application::Train() {
  Log::Stdout("start train");
  boosting_->Train();
  Log::Stdout("finish train");
}

void Application::Predict() {
  // create predictor
  Predictor predictor(boosting_, config_.io_config.is_sigmoid);
  predictor.Predict(config_.io_config.data_filename.c_str(),
    config_.io_config.data_has_label, config_.io_config.output_result.c_str());
  Log::Stdout("finish predict");
}

void Application::InitPredict() {
  boosting_ =
    Boosting::CreateBoosting(config_.boosting_type, config_.boosting_config);
  LoadModel();
  Log::Stdout("finish predict init");
}

void Application::LoadModel() {
  TextReader<size_t> model_reader(config_.io_config.input_model.c_str());
  model_reader.ReadAllLines();
  std::stringstream ss;
  for (auto& line : model_reader.Lines()) {
    ss << line << '\n';
  }
  boosting_->ModelsFromString(ss.str(), config_.io_config.num_model_predict);
}

template<typename T>
T Application::GlobalSyncUpByMin(T& local) {
  T global = local;
  if (!config_.is_parallel) {
    // not need to sync if not parallel learning
    return global;
  }
  Network::Allreduce(reinterpret_cast<char*>(&local),
                         sizeof(local), sizeof(local),
                     reinterpret_cast<char*>(&global),
              [](const char* src, char* dst, int len) {
    int used_size = 0;
    const int type_size = sizeof(T);
    const T *p1;
    T *p2;
    while (used_size < len) {
      p1 = reinterpret_cast<const T *>(src);
      p2 = reinterpret_cast<T *>(dst);
      if (*p1 < *p2) {
        std::memcpy(dst, src, type_size);
      }
      src += type_size;
      dst += type_size;
      used_size += type_size;
    }
  });
  return global;
}

}  // namespace LightGBM
<|endoftext|>