<commit_before>#include "omp.h"

inline
kth_cv_svm::kth_cv_svm(const std::string in_path,
		       const std::string in_actionNames,
		       const field<std::string> in_all_people,
		       const int in_scale_factor,
		       const int in_shift,
		       const int in_scene, //only for kth
		       const int in_dim
):path(in_path), actionNames(in_actionNames), all_people (in_all_people), scale_factor(in_scale_factor), shift(in_shift), total_scenes(in_scene), dim(in_dim)
{
  actions.load( actionNames );

}

// Log-Euclidean Distance

inline
void
kth_cv_svm::logEucl()
{
  //logEucl_distances();
  logEucl_CV(); //cross validation;

}

inline
void
kth_cv_svm::logEucl_CV()
{

  int n_actions = actions.n_rows;
  int n_peo =  all_people.n_rows;

  //int n_test = n_peo*n_actions*total_scenes - 1; // - person13_handclapping_d3
  int n_test = (n_peo-1)*n_actions*total_scenes; // - person13_handclapping_d3
  int n_dim = n_test;
  int sc = 1; // = total scenes
  fvec dist_vector;

  float acc=0;
  vec real_labels;
  vec est_labels;
  field<std::string> test_video_list(n_peo*n_actions);

  real_labels.zeros(n_peo*n_actions);
  est_labels.zeros(n_peo*n_actions);

  int j =0;

  for (int pe_ts=0; pe_ts<n_peo; ++pe_ts)
  {

    fmat training_data;
    fvec lab;
    training_data.zeros(n_dim,n_test);
    lab.zeros(n_test);
    int k=0;
    for (int pe_tr=0; pe_tr<n_peo; ++pe_tr)
    {
      if(pe_tr!=pe_ts)

	for (int act=0; act<n_actions; act++)
	{

	    std::stringstream load_vec_dist;
	    load_vec_dist << path << "./classification/kth-svm/logEucl/dist_vector_" << all_people (pe_tr) << "_" << actions(act) << ".h5" ;
	    dist_vector.load( load_vec_dist.str() );
	    training_data.col(k) = dist_vector;
	    lab(k) = act;
	    ++k;
	}
    }

    //Training the model with OpenCV
    cout << "Using SVM to classify " << all_people (pe_ts) << endl;
    //cout << "Preparing data to train the data" << endl;
    cv::Mat cvMatTraining(n_test, n_dim, CV_32FC1);
    float fl_labels[n_test] ;

      for (uword m=0; m<n_test; ++m)
      {
	for (uword d=0; d<n_dim; ++d)
	{
	  cvMatTraining.at<float>(m,d) = training_data(d,m);
	  //cout << " OpenCV: " << cvMatTraining.at<float>(m,d) << " - Arma: " <<training_data(d,m);

	}

	fl_labels[m] = lab(m);
	//cout <<" OpenCVLabel: " <<  fl_labels[m] << " ArmaLabel: " << labels(m) << endl;

      }

      cv::Mat cvMatLabels(n_test, 1, CV_32FC1,fl_labels );

      //cout << "Setting parameters" << endl;
      CvSVMParams params;
      params.svm_type    = CvSVM::C_SVC;
      params.kernel_type = CvSVM::LINEAR;
      //params.gamma = 1;
      params.term_crit   = cvTermCriteria(CV_TERMCRIT_ITER,  (int)1e7, 1e-6);
      //params.term_crit   = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);

      // Train the SVM
      //cout << "Training" << endl;
      CvSVM SVM;
      SVM.train( cvMatTraining , cvMatLabels, cv::Mat(), cv::Mat(), params);

    //   y luego si for act=0:total_act
    //acc para este run
    //ACC = [ACC acc];

      for (int act_ts =0; act_ts<n_actions; ++act_ts)
      {
	 vec test_dist;
	 std::stringstream load_vec_dist;
	 load_vec_dist << path << "./classification/kth-svm/logEucl/dist_vector_" << all_people (pe_ts) << "_" << actions(act_ts) << ".h5" ;
	 test_dist.load( load_vec_dist.str() );
	 //cout << all_people (pe_ts) << "_" << actions(act_ts) << endl;

	 cv::Mat cvMatTesting_onevideo(1, n_dim, CV_32FC1);

	 for (uword d=0; d<n_dim; ++d)
	 {
	   cvMatTesting_onevideo.at<float>(0,d) = test_dist(d);

	}

	float response = SVM.predict(cvMatTesting_onevideo, true);

	//cout << "response " << response << endl;
	real_labels(j) = act_ts;
	est_labels(j) = response;
	test_video_list(j) = load_vec_dist.str();
	j++;

	if (response == act_ts)  {
	  acc++;

	}

      }

      ///cambiar nombres
      real_labels.save("svm_LogEucl_real_labels.dat", raw_ascii);
      est_labels.save("svm_LogEucl_est_labels.dat", raw_ascii);
      test_video_list.save("svm_LogEucl_test_video_list.dat", raw_ascii);
      //getchar();

  }
  cout << "Performance: " << acc*100/(n_peo*n_actions) << " %" << endl;
}

///

inline
void
kth_cv_svm::logEucl_distances()
{

  int n_actions = actions.n_rows;
  int n_peo =  all_people.n_rows;

  //int n_test = n_peo*n_actions*total_scenes - 1; // - person13_handclapping_d3
  int n_test = n_peo*n_actions*total_scenes; // - person13_handclapping_d3

  int k=0;
  int sc = 1; // = total scenes

  mat peo_act(n_test,2);

  for (int pe = 0; pe< n_peo; ++pe)
  {
    for (int act=0; act<n_actions; ++act)
    {
      peo_act (k,0) = pe;
      peo_act (k,1) = act;
      k++;
    }
  }

  std::stringstream load_sub_path;
  load_sub_path  << path << "cov_matrices/kth-one-cov-mat-dim" << dim << "/sc" << sc << "/scale" << scale_factor << "-shift"<< shift ;

  //omp_set_num_threads(8); //Use only 8 processors
  #pragma omp parallel for
  for (int n = 0; n< n_test; ++n)
  {
    int pe  = peo_act (n,0);
    int act = peo_act (n,1);

    int tid=omp_get_thread_num();
    vec dist_video_i;

    #pragma omp critical
    cout<< "Processor " << tid <<" doing "<<  all_people (pe) << "_" << actions(act) << endl;

    std::stringstream load_cov;
    load_cov << load_sub_path.str() << "/LogMcov_" << all_people (pe) << "_" << actions(act) << "_dim" << dim  << ".h5";

    //#pragma omp critical
    //cout << load_cov_seg.str() << endl;

    dist_video_i = dist_logEucl_one_video( pe, load_sub_path.str(), load_cov.str());

    //save dist_video_i person, action
    std::stringstream save_vec_dist;
    save_vec_dist << "./kth-svm/logEucl/dist_vector_" << all_people (pe) << "_" << actions(act) << ".h5" ;

    #pragma omp critical
    dist_video_i.save(save_vec_dist.str(), hdf5_binary);

  }

}

inline
vec
kth_cv_svm::dist_logEucl_one_video(int pe_test, std::string load_sub_path, std::string load_cov)
{
  //wall_clock timer;
  //timer.tic();

  mat logMtest_cov;
  logMtest_cov.load(load_cov);

  int n_actions = actions.n_rows;
  int n_peo =  all_people.n_rows;

  //double dist;
  double tmp_dist;
  vec dist;
  int num_dist = (n_peo-1)*n_actions;
  dist.zeros(num_dist);
  tmp_dist = datum::inf;

  double est_lab;
  int sc =1;
  int k=0;

  for (int pe_tr = 0; pe_tr< n_peo; ++pe_tr)
  {
    if (pe_tr!= pe_test)
    {

      //cout << " " << all_people (pe_tr);

      for (int act=0; act<n_actions; ++act)
      {

	std::stringstream load_cov_tr;
	load_cov_tr << load_sub_path << "/LogMcov_" <<  all_people (pe_tr) << "_" << actions(act) << "_dim" << dim  << ".h5";
	mat logMtrain_cov;
	logMtrain_cov.load( load_cov_tr.str() );
	tmp_dist = norm( logMtest_cov - logMtrain_cov, "fro");
	dist(k) = tmp_dist;
	++k;
      }
    }
  }

  return dist;

}

<commit_msg>Test svm_LogEucl. Linear SVM<commit_after>#include "omp.h"

inline
kth_cv_svm::kth_cv_svm(const std::string in_path,
		       const std::string in_actionNames,
		       const field<std::string> in_all_people,
		       const int in_scale_factor,
		       const int in_shift,
		       const int in_scene, //only for kth
		       const int in_dim
):path(in_path), actionNames(in_actionNames), all_people (in_all_people), scale_factor(in_scale_factor), shift(in_shift), total_scenes(in_scene), dim(in_dim)
{
  actions.load( actionNames );

}

// Log-Euclidean Distance

inline
void
kth_cv_svm::logEucl()
{
  //logEucl_distances();
  logEucl_CV(); //cross validation;

}

inline
void
kth_cv_svm::logEucl_CV()
{

  int n_actions = actions.n_rows;
  int n_peo =  all_people.n_rows;

  //int n_test = n_peo*n_actions*total_scenes - 1; // - person13_handclapping_d3
  int n_test = (n_peo-1)*n_actions*total_scenes; // - person13_handclapping_d3
  int n_dim = n_test;
  int sc = 1; // = total scenes
  fvec dist_vector;

  float acc=0;
  vec real_labels;
  vec est_labels;
  field<std::string> test_video_list(n_peo*n_actions);

  real_labels.zeros(n_peo*n_actions);
  est_labels.zeros(n_peo*n_actions);

  int j =0;

  for (int pe_ts=0; pe_ts<n_peo; ++pe_ts)
  {

    fmat training_data;
    fvec lab;
    training_data.zeros(n_dim,n_test);
    lab.zeros(n_test);
    int k=0;
    for (int pe_tr=0; pe_tr<n_peo; ++pe_tr)
    {
      if(pe_tr!=pe_ts)

	for (int act=0; act<n_actions; act++)
	{

	    std::stringstream load_vec_dist;
	    load_vec_dist << path << "./classification/kth-svm/logEucl/dist_vector_" << all_people (pe_tr) << "_" << actions(act) << ".h5" ;
	    dist_vector.load( load_vec_dist.str() );
	    training_data.col(k) = dist_vector;
	    lab(k) = act;
	    ++k;
	}
    }

    //Training the model with OpenCV
    cout << "Using SVM to classify " << all_people (pe_ts) << endl;
    //cout << "Preparing data to train the data" << endl;
    cv::Mat cvMatTraining(n_test, n_dim, CV_32FC1);
    float fl_labels[n_test] ;

      for (uword m=0; m<n_test; ++m)
      {
	for (uword d=0; d<n_dim; ++d)
	{
	  cvMatTraining.at<float>(m,d) = training_data(d,m);
	  //cout << " OpenCV: " << cvMatTraining.at<float>(m,d) << " - Arma: " <<training_data(d,m);

	}

	fl_labels[m] = lab(m);
	//cout <<" OpenCVLabel: " <<  fl_labels[m] << " ArmaLabel: " << labels(m) << endl;

      }

      cv::Mat cvMatLabels(n_test, 1, CV_32FC1,fl_labels );

      //cout << "Setting parameters" << endl;
      CvSVMParams params;
      params.svm_type    = CvSVM::C_SVC;
      params.kernel_type = CvSVM::LINEAR;
      //params.gamma = 1;
      params.term_crit   = cvTermCriteria(CV_TERMCRIT_ITER,  (int)1e7, 1e-6);
      //params.term_crit   = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);

      // Train the SVM
      //cout << "Training" << endl;
      CvSVM SVM;
      SVM.train( cvMatTraining , cvMatLabels, cv::Mat(), cv::Mat(), params);

    //   y luego si for act=0:total_act
    //acc para este run
    //ACC = [ACC acc];

      for (int act_ts =0; act_ts<n_actions; ++act_ts)
      {
	 vec test_dist;
	 std::stringstream load_vec_dist;
	 load_vec_dist << path << "./classification/kth-svm/logEucl/dist_vector_" << all_people (pe_ts) << "_" << actions(act_ts) << ".h5" ;
	 test_dist.load( load_vec_dist.str() );
	 //cout << all_people (pe_ts) << "_" << actions(act_ts) << endl;

	 cv::Mat cvMatTesting_onevideo(1, n_dim, CV_32FC1);

	 for (uword d=0; d<n_dim; ++d)
	 {
	   cvMatTesting_onevideo.at<float>(0,d) = test_dist(d);

	}

	float response = SVM.predict(cvMatTesting_onevideo, true);

	//cout << "response " << response << endl;
	real_labels(j) = act_ts;
	est_labels(j) = response;
	test_video_list(j) = load_vec_dist.str();
	j++;

	if (response == act_ts)  {
	  acc++;

	}

      }

      ///cambiar nombres
      real_labels.save("./svm_results/svm_LogEucl_real_labels.dat", raw_ascii);
      est_labels.save("./svm_results/svm_LogEucl_est_labels.dat", raw_ascii);
      test_video_list.save("./svm_results/svm_LogEucl_test_video_list.dat", raw_ascii);
      //getchar();

  }
  cout << "Performance: " << acc*100/(n_peo*n_actions) << " %" << endl;
}

///

inline
void
kth_cv_svm::logEucl_distances()
{

  int n_actions = actions.n_rows;
  int n_peo =  all_people.n_rows;

  //int n_test = n_peo*n_actions*total_scenes - 1; // - person13_handclapping_d3
  int n_test = n_peo*n_actions*total_scenes; // - person13_handclapping_d3

  int k=0;
  int sc = 1; // = total scenes

  mat peo_act(n_test,2);

  for (int pe = 0; pe< n_peo; ++pe)
  {
    for (int act=0; act<n_actions; ++act)
    {
      peo_act (k,0) = pe;
      peo_act (k,1) = act;
      k++;
    }
  }

  std::stringstream load_sub_path;
  load_sub_path  << path << "cov_matrices/kth-one-cov-mat-dim" << dim << "/sc" << sc << "/scale" << scale_factor << "-shift"<< shift ;

  //omp_set_num_threads(8); //Use only 8 processors
  #pragma omp parallel for
  for (int n = 0; n< n_test; ++n)
  {
    int pe  = peo_act (n,0);
    int act = peo_act (n,1);

    int tid=omp_get_thread_num();
    vec dist_video_i;

    #pragma omp critical
    cout<< "Processor " << tid <<" doing "<<  all_people (pe) << "_" << actions(act) << endl;

    std::stringstream load_cov;
    load_cov << load_sub_path.str() << "/LogMcov_" << all_people (pe) << "_" << actions(act) << "_dim" << dim  << ".h5";

    //#pragma omp critical
    //cout << load_cov_seg.str() << endl;

    dist_video_i = dist_logEucl_one_video( pe, load_sub_path.str(), load_cov.str());

    //save dist_video_i person, action
    std::stringstream save_vec_dist;
    save_vec_dist << "./kth-svm/logEucl/dist_vector_" << all_people (pe) << "_" << actions(act) << ".h5" ;

    #pragma omp critical
    dist_video_i.save(save_vec_dist.str(), hdf5_binary);

  }

}

inline
vec
kth_cv_svm::dist_logEucl_one_video(int pe_test, std::string load_sub_path, std::string load_cov)
{
  //wall_clock timer;
  //timer.tic();

  mat logMtest_cov;
  logMtest_cov.load(load_cov);

  int n_actions = actions.n_rows;
  int n_peo =  all_people.n_rows;

  //double dist;
  double tmp_dist;
  vec dist;
  int num_dist = (n_peo-1)*n_actions;
  dist.zeros(num_dist);
  tmp_dist = datum::inf;

  double est_lab;
  int sc =1;
  int k=0;

  for (int pe_tr = 0; pe_tr< n_peo; ++pe_tr)
  {
    if (pe_tr!= pe_test)
    {

      //cout << " " << all_people (pe_tr);

      for (int act=0; act<n_actions; ++act)
      {

	std::stringstream load_cov_tr;
	load_cov_tr << load_sub_path << "/LogMcov_" <<  all_people (pe_tr) << "_" << actions(act) << "_dim" << dim  << ".h5";
	mat logMtrain_cov;
	logMtrain_cov.load( load_cov_tr.str() );
	tmp_dist = norm( logMtest_cov - logMtrain_cov, "fro");
	dist(k) = tmp_dist;
	++k;
      }
    }
  }

  return dist;

}

<|endoftext|>


<commit_before>// Jonathan Eastep, Harshad Kasture, Jason Miller, Chris Celio, Charles Gruenwald,
// Nathan Beckmann, David Wentzlaff, James Psota
// 10.12.08
//
// Carbon Computer Simulator
//
// This simulator models future multi-core computers with thousands of cores.
// It runs on today's x86 multicores and will scale as more and more cores
// and better inter-core communications mechanisms become available.
// The simulator provides a platform for research in processor architecture,
// compilers, network interconnect topologies, and some OS.
//
// The simulator runs on top of Intel's Pin dynamic binary instrumention engine.
// Application code in the absence of instrumentation runs more or less
// natively and is thus high performance. When instrumentation is used, models
// can be hot-swapped or dynamically enabled and disabled as desired so that
// performance tracks the level of simulation detail needed.

#include <iostream>
#include <assert.h>
#include <set>
#include <sys/syscall.h>
#include <unistd.h>

#include "pin.H"
#include "log.h"
#include "run_models.h"
#include "analysis.h"
#include "routine_replace.h"

// FIXME: This list could probably be trimmed down a lot.
#include "simulator.h"
#include "core_manager.h"
#include "core.h"
#include "syscall_model.h"
#include "thread_manager.h"
#include "config_file.hpp"
#include "handle_args.h"

config::ConfigFile *cfg;

INT32 usage()
{
   cerr << "This tool implements a multicore simulator." << endl;
   cerr << KNOB_BASE::StringKnobSummary() << endl;

   return -1;
}

void routineCallback(RTN rtn, void *v)
{
   string rtn_name = RTN_Name(rtn);
   bool did_func_replace = replaceUserAPIFunction(rtn, rtn_name);

   if (!did_func_replace)
      replaceInstruction(rtn, rtn_name);
}

// syscall model wrappers

void SyscallEntry(THREADID threadIndex, CONTEXT *ctxt, SYSCALL_STANDARD std, void *v)
{
   Core *core = Sim()->getCoreManager()->getCurrentCore();

   if (core)
   {
      UInt8 syscall_number = (UInt8) PIN_GetSyscallNumber(ctxt, std);
      SyscallMdl::syscall_args_t args;
      args.arg0 = PIN_GetSyscallArgument(ctxt, std, 0);
      args.arg1 = PIN_GetSyscallArgument(ctxt, std, 1);
      args.arg2 = PIN_GetSyscallArgument(ctxt, std, 2);
      args.arg3 = PIN_GetSyscallArgument(ctxt, std, 3);
      args.arg4 = PIN_GetSyscallArgument(ctxt, std, 4);
      args.arg5 = PIN_GetSyscallArgument(ctxt, std, 5);
      UInt8 new_syscall = core->getSyscallMdl()->runEnter(syscall_number, args);
      PIN_SetSyscallNumber(ctxt, std, new_syscall);
   }
}

void SyscallExit(THREADID threadIndex, CONTEXT *ctxt, SYSCALL_STANDARD std, void *v)
{
   Core *core = Sim()->getCoreManager()->getCurrentCore();

   if (core)
   {
      carbon_reg_t old_return =
#ifdef TARGET_IA32E
      PIN_GetContextReg(ctxt, REG_RAX);
#else
      PIN_GetContextReg(ctxt, REG_EAX);
#endif

      carbon_reg_t syscall_return = core->getSyscallMdl()->runExit(old_return);

#ifdef TARGET_IA32E
      PIN_SetContextReg(ctxt, REG_RAX, syscall_return);
#else
      PIN_SetContextReg(ctxt, REG_EAX, syscall_return);
#endif
   }
}

void ApplicationStart()
{
}

void ApplicationExit(int, void*)
{
   LOG_PRINT("Application exit.");
   Simulator::release();
   delete cfg;
}

void SimSpawnThreadSpawner(CONTEXT *ctx, AFUNPTR fp_main)
{
   // Get the function for the thread spawner
   PIN_LockClient();
   AFUNPTR thread_spawner;
   IMG img = IMG_FindByAddress((ADDRINT)fp_main);
   RTN rtn = RTN_FindByName(img, "CarbonSpawnThreadSpawner");
   thread_spawner = RTN_Funptr(rtn);
   PIN_UnlockClient();

   // Get the address of the thread spawner
   int res;
   PIN_CallApplicationFunction(ctx,
         PIN_ThreadId(),
         CALLINGSTD_DEFAULT,
         thread_spawner,
         PIN_PARG(int), &res,
         PIN_PARG_END());

}

int CarbonMain(CONTEXT *ctx, AFUNPTR fp_main, int argc, char *argv[])
{
   ApplicationStart();

   SimSpawnThreadSpawner(ctx, fp_main);

   if (Config::getSingleton()->getCurrentProcessNum() == 0)
   {
      LOG_PRINT("Calling main()...");

      Sim()->getCoreManager()->initializeThread(0);

      // call main()
      int res;
      PIN_CallApplicationFunction(ctx,
                                  PIN_ThreadId(),
                                  CALLINGSTD_DEFAULT,
                                  fp_main,
                                  PIN_PARG(int), &res,
                                  PIN_PARG(int), argc,
                                  PIN_PARG(char**), argv,
                                  PIN_PARG_END());
   }
   else
   {
      LOG_PRINT("Waiting for main process to finish...");
      while (!Sim()->finished())
         usleep(100);
      LOG_PRINT("Finished!");
   }

   LOG_PRINT("Leaving CarbonMain...");

   return 0;
}

int main(int argc, char *argv[])
{

   // Global initialization
   PIN_InitSymbols();
   PIN_Init(argc,argv);

   string_vec args;

   // Set the default config path if it isn't
   // overwritten on the command line.
   std::string config_path = "carbon_sim.cfg";

   parse_args(args, config_path, argc, argv);

   cfg = new config::ConfigFile();
   cfg->load(config_path);

   handle_args(args, *cfg);

   Simulator::setConfig(cfg);

   Simulator::allocate();
   Sim()->start();

   // Instrumentation
   LOG_PRINT("Start of instrumentation.");
   RTN_AddInstrumentFunction(routineCallback, 0);

   if(cfg->getBool("general/enable_syscall_modeling"))
   {
       PIN_AddSyscallEntryFunction(SyscallEntry, 0);
       PIN_AddSyscallExitFunction(SyscallExit, 0);
   }

   PIN_AddFiniFunction(ApplicationExit, 0);

   // Just in case ... might not be strictly necessary
   Transport::getSingleton()->barrier();

   // Never returns
   LOG_PRINT("Running program...");
   PIN_StartProgram();

   return 0;
}
<commit_msg>[print] Printing out all the routines<commit_after>// Jonathan Eastep, Harshad Kasture, Jason Miller, Chris Celio, Charles Gruenwald,
// Nathan Beckmann, David Wentzlaff, James Psota
// 10.12.08
//
// Carbon Computer Simulator
//
// This simulator models future multi-core computers with thousands of cores.
// It runs on today's x86 multicores and will scale as more and more cores
// and better inter-core communications mechanisms become available.
// The simulator provides a platform for research in processor architecture,
// compilers, network interconnect topologies, and some OS.
//
// The simulator runs on top of Intel's Pin dynamic binary instrumention engine.
// Application code in the absence of instrumentation runs more or less
// natively and is thus high performance. When instrumentation is used, models
// can be hot-swapped or dynamically enabled and disabled as desired so that
// performance tracks the level of simulation detail needed.

#include <iostream>
#include <assert.h>
#include <set>
#include <sys/syscall.h>
#include <unistd.h>

#include "pin.H"
#include "log.h"
#include "run_models.h"
#include "analysis.h"
#include "routine_replace.h"

// FIXME: This list could probably be trimmed down a lot.
#include "simulator.h"
#include "core_manager.h"
#include "core.h"
#include "syscall_model.h"
#include "thread_manager.h"
#include "config_file.hpp"
#include "handle_args.h"

config::ConfigFile *cfg;

INT32 usage()
{
   cerr << "This tool implements a multicore simulator." << endl;
   cerr << KNOB_BASE::StringKnobSummary() << endl;

   return -1;
}

void printRtnName(ADDRINT rtn_address)
{
   // printf ("RTN: Name = %s, Address = 0x%x\n", RTN_FindNameByAddress(rtn_address).c_str(), rtn_address);
}

void routineCallback(RTN rtn, void *v)
{
   RTN_Open(rtn);

   string rtn_name = RTN_Name(rtn);
   ADDRINT rtn_address = RTN_Address(rtn);

   RTN_InsertCall(rtn, IPOINT_BEFORE,
         AFUNPTR(printRtnName),
         IARG_ADDRINT, rtn_address,
         IARG_END);

   replaceUserAPIFunction(rtn, rtn_name);

   RTN_Close(rtn);
   // bool did_func_replace = replaceUserAPIFunction(rtn, rtn_name);

   // We dont need this for now
   // if (!did_func_replace)
   //   replaceInstruction(rtn, rtn_name);
}

// syscall model wrappers

void SyscallEntry(THREADID threadIndex, CONTEXT *ctxt, SYSCALL_STANDARD std, void *v)
{
   Core *core = Sim()->getCoreManager()->getCurrentCore();

   if (core)
   {
      UInt8 syscall_number = (UInt8) PIN_GetSyscallNumber(ctxt, std);
      SyscallMdl::syscall_args_t args;
      args.arg0 = PIN_GetSyscallArgument(ctxt, std, 0);
      args.arg1 = PIN_GetSyscallArgument(ctxt, std, 1);
      args.arg2 = PIN_GetSyscallArgument(ctxt, std, 2);
      args.arg3 = PIN_GetSyscallArgument(ctxt, std, 3);
      args.arg4 = PIN_GetSyscallArgument(ctxt, std, 4);
      args.arg5 = PIN_GetSyscallArgument(ctxt, std, 5);
      UInt8 new_syscall = core->getSyscallMdl()->runEnter(syscall_number, args);
      PIN_SetSyscallNumber(ctxt, std, new_syscall);
   }
}

void SyscallExit(THREADID threadIndex, CONTEXT *ctxt, SYSCALL_STANDARD std, void *v)
{
   Core *core = Sim()->getCoreManager()->getCurrentCore();

   if (core)
   {
      carbon_reg_t old_return =
#ifdef TARGET_IA32E
      PIN_GetContextReg(ctxt, REG_RAX);
#else
      PIN_GetContextReg(ctxt, REG_EAX);
#endif

      carbon_reg_t syscall_return = core->getSyscallMdl()->runExit(old_return);

#ifdef TARGET_IA32E
      PIN_SetContextReg(ctxt, REG_RAX, syscall_return);
#else
      PIN_SetContextReg(ctxt, REG_EAX, syscall_return);
#endif
   }
}

void ApplicationStart()
{
}

void ApplicationExit(int, void*)
{
   LOG_PRINT("Application exit.");
   Simulator::release();
   delete cfg;
}

void SimSpawnThreadSpawner(CONTEXT *ctx, AFUNPTR fp_main)
{
   // Get the function for the thread spawner
   PIN_LockClient();
   AFUNPTR thread_spawner;
   IMG img = IMG_FindByAddress((ADDRINT)fp_main);
   RTN rtn = RTN_FindByName(img, "CarbonSpawnThreadSpawner");
   thread_spawner = RTN_Funptr(rtn);
   PIN_UnlockClient();

   // Get the address of the thread spawner
   int res;
   PIN_CallApplicationFunction(ctx,
         PIN_ThreadId(),
         CALLINGSTD_DEFAULT,
         thread_spawner,
         PIN_PARG(int), &res,
         PIN_PARG_END());

}

int CarbonMain(CONTEXT *ctx, AFUNPTR fp_main, int argc, char *argv[])
{
   ApplicationStart();

   SimSpawnThreadSpawner(ctx, fp_main);

   if (Config::getSingleton()->getCurrentProcessNum() == 0)
   {
      LOG_PRINT("Calling main()...");

      Sim()->getCoreManager()->initializeThread(0);

      // call main()
      int res;
      PIN_CallApplicationFunction(ctx,
                                  PIN_ThreadId(),
                                  CALLINGSTD_DEFAULT,
                                  fp_main,
                                  PIN_PARG(int), &res,
                                  PIN_PARG(int), argc,
                                  PIN_PARG(char**), argv,
                                  PIN_PARG_END());
   }
   else
   {
      LOG_PRINT("Waiting for main process to finish...");
      while (!Sim()->finished())
         usleep(100);
      LOG_PRINT("Finished!");
   }

   LOG_PRINT("Leaving CarbonMain...");

   return 0;
}

int main(int argc, char *argv[])
{

   // Global initialization
   PIN_InitSymbols();
   PIN_Init(argc,argv);

   string_vec args;

   // Set the default config path if it isn't
   // overwritten on the command line.
   std::string config_path = "carbon_sim.cfg";

   parse_args(args, config_path, argc, argv);

   cfg = new config::ConfigFile();
   cfg->load(config_path);

   handle_args(args, *cfg);

   Simulator::setConfig(cfg);

   Simulator::allocate();
   Sim()->start();

   // Instrumentation
   LOG_PRINT("Start of instrumentation.");
   RTN_AddInstrumentFunction(routineCallback, 0);

   if(cfg->getBool("general/enable_syscall_modeling"))
   {
       PIN_AddSyscallEntryFunction(SyscallEntry, 0);
       PIN_AddSyscallExitFunction(SyscallExit, 0);
   }

   PIN_AddFiniFunction(ApplicationExit, 0);

   // Just in case ... might not be strictly necessary
   Transport::getSingleton()->barrier();

   // Never returns
   LOG_PRINT("Running program...");
   PIN_StartProgram();

   return 0;
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "arrow/compute/kernels/sort_to_indices.h"

#include <algorithm>
#include <limits>
#include <numeric>
#include <vector>

#include "arrow/builder.h"
#include "arrow/compute/context.h"
#include "arrow/compute/expression.h"
#include "arrow/compute/logical_type.h"
#include "arrow/type_traits.h"
#include "arrow/visitor_inline.h"

namespace arrow {

class Array;

namespace compute {

/// \brief UnaryKernel implementing SortToIndices operation
class ARROW_EXPORT SortToIndicesKernel : public UnaryKernel {
 protected:
  std::shared_ptr<DataType> type_;

 public:
  /// \brief UnaryKernel interface
  ///
  /// delegates to subclasses via SortToIndices()
  Status Call(FunctionContext* ctx, const Datum& values, Datum* offsets) override = 0;

  /// \brief output type of this kernel
  std::shared_ptr<DataType> out_type() const override { return uint64(); }

  /// \brief single-array implementation
  virtual Status SortToIndices(FunctionContext* ctx, const std::shared_ptr<Array>& values,
                               std::shared_ptr<Array>* offsets) = 0;

  /// \brief factory for SortToIndicesKernel
  ///
  /// \param[in] value_type constructed SortToIndicesKernel will support sorting
  ///            values of this type
  /// \param[out] out created kernel
  static Status Make(const std::shared_ptr<DataType>& value_type,
                     std::unique_ptr<SortToIndicesKernel>* out);
};

template <typename ArrayType>
bool CompareValues(const ArrayType& array, uint64_t lhs, uint64_t rhs) {
  return array.Value(lhs) < array.Value(rhs);
}

template <typename ArrayType>
bool CompareViews(const ArrayType& array, uint64_t lhs, uint64_t rhs) {
  return array.GetView(lhs) < array.GetView(rhs);
}

template <typename ArrowType, typename Comparator>
class CompareSorter {
  using ArrayType = typename TypeTraits<ArrowType>::ArrayType;

 public:
  explicit CompareSorter(Comparator compare) : compare_(compare) {}

  void Sort(int64_t* indices_begin, int64_t* indices_end, const ArrayType& values) {
    std::iota(indices_begin, indices_end, 0);

    auto nulls_begin = indices_end;
    if (values.null_count()) {
      nulls_begin =
          std::stable_partition(indices_begin, indices_end,
                                [&values](uint64_t ind) { return !values.IsNull(ind); });
    }
    std::stable_sort(indices_begin, nulls_begin,
                     [&values, this](uint64_t left, uint64_t right) {
                       return compare_(values, left, right);
                     });
  }

 private:
  Comparator compare_;
};

template <typename ArrowType>
class CountSorter {
  using ArrayType = typename TypeTraits<ArrowType>::ArrayType;
  using c_type = typename ArrowType::c_type;

 public:
  CountSorter() = default;

  explicit CountSorter(c_type min, c_type max) { SetMinMax(min, max); }

  // Assume: max >= min && (max - min) < 4Gi
  void SetMinMax(c_type min, c_type max) {
    min_ = min;
    value_range_ = static_cast<uint32_t>(max - min) + 1;
  }

  void Sort(int64_t* indices_begin, int64_t* indices_end, const ArrayType& values) {
    // 32bit counter performs much better than 64bit one
    if (values.length() < (1LL << 32)) {
      SortInternal<uint32_t>(indices_begin, indices_end, values);
    } else {
      SortInternal<uint64_t>(indices_begin, indices_end, values);
    }
  }

 private:
  c_type min_{0};
  uint32_t value_range_{0};

  template <typename CounterType>
  void SortInternal(int64_t* indices_begin, int64_t* indices_end,
                    const ArrayType& values) {
    const uint32_t value_range = value_range_;

    // first slot reserved for prefix sum, last slot for null value
    std::vector<CounterType> counts(1 + value_range + 1);

    struct UpdateCounts {
      Status VisitNull() {
        ++counts[value_range];
        return Status::OK();
      }

      Status VisitValue(c_type v) {
        ++counts[v - min];
        return Status::OK();
      }

      CounterType* counts;
      const uint32_t value_range;
      c_type min;
    };
    {
      UpdateCounts update_counts{&counts[1], value_range, min_};
      ARROW_CHECK_OK(ArrayDataVisitor<ArrowType>().Visit(*values.data(), &update_counts));
    }

    for (uint32_t i = 1; i <= value_range; ++i) {
      counts[i] += counts[i - 1];
    }

    struct OutputIndices {
      Status VisitNull() {
        out_indices[counts[value_range]++] = index++;
        return Status::OK();
      }

      Status VisitValue(c_type v) {
        out_indices[counts[v - min]++] = index++;
        return Status::OK();
      }

      CounterType* counts;
      const uint32_t value_range;
      c_type min;
      int64_t* out_indices;
      int64_t index;
    };
    {
      OutputIndices output_indices{&counts[0], value_range, min_, indices_begin, 0};
      ARROW_CHECK_OK(
          ArrayDataVisitor<ArrowType>().Visit(*values.data(), &output_indices));
    }
  }
};

// Sort integers with counting sort or comparison based sorting algorithm
// - Use O(n) counting sort if values are in a small range
// - Use O(nlogn) std::stable_sort otherwise
template <typename ArrowType, typename Comparator>
class CountOrCompareSorter {
  using ArrayType = typename TypeTraits<ArrowType>::ArrayType;
  using c_type = typename ArrowType::c_type;

 public:
  explicit CountOrCompareSorter(Comparator compare) : compare_sorter_(compare) {}

  void Sort(int64_t* indices_begin, int64_t* indices_end, const ArrayType& values) {
    if (values.length() >= countsort_min_len_ && values.length() > values.null_count()) {
      struct MinMaxScanner {
        Status VisitNull() { return Status::OK(); }

        Status VisitValue(c_type v) {
          min = std::min(min, v);
          max = std::max(max, v);
          return Status::OK();
        }

        c_type min{std::numeric_limits<c_type>::max()};
        c_type max{std::numeric_limits<c_type>::min()};
      };

      ArrayDataVisitor<ArrowType> visitor;
      MinMaxScanner minmax_scanner;
      ARROW_CHECK_OK(visitor.Visit(*values.data(), &minmax_scanner));

      // For signed int32/64, (max - min) may overflow and trigger UBSAN.
      // Cast to largest unsigned type(uint64_t) before substraction.
      const uint64_t min = static_cast<uint64_t>(minmax_scanner.min);
      const uint64_t max = static_cast<uint64_t>(minmax_scanner.max);
      if ((max - min) <= countsort_max_range_) {
        count_sorter_.SetMinMax(minmax_scanner.min, minmax_scanner.max);
        count_sorter_.Sort(indices_begin, indices_end, values);
        return;
      }
    }

    compare_sorter_.Sort(indices_begin, indices_end, values);
  }

 private:
  CompareSorter<ArrowType, Comparator> compare_sorter_;
  CountSorter<ArrowType> count_sorter_;

  // Cross point to prefer counting sort than stl::stable_sort(merge sort)
  // - array to be sorted is longer than "count_min_len_"
  // - value range (max-min) is within "count_max_range_"
  //
  // The optimal setting depends heavily on running CPU. Below setting is
  // conservative to adapt to various hardware and keep code simple.
  // It's possible to decrease array-len and/or increase value-range to cover
  // more cases, or setup a table for best array-len/value-range combinations.
  // See https://issues.apache.org/jira/browse/ARROW-1571 for detailed analysis.
  static const uint32_t countsort_min_len_ = 1024;
  static const uint32_t countsort_max_range_ = 4096;
};

template <typename ArrowType, typename Sorter>
class SortToIndicesKernelImpl : public SortToIndicesKernel {
  using ArrayType = typename TypeTraits<ArrowType>::ArrayType;

 public:
  explicit SortToIndicesKernelImpl(Sorter sorter) : sorter_(sorter) {}

  Status SortToIndices(FunctionContext* ctx, const std::shared_ptr<Array>& values,
                       std::shared_ptr<Array>* offsets) {
    return SortToIndicesImpl(ctx, std::static_pointer_cast<ArrayType>(values), offsets);
  }

  Status Call(FunctionContext* ctx, const Datum& values, Datum* offsets) {
    if (!values.is_array()) {
      return Status::Invalid("SortToIndicesKernel expects array values");
    }
    auto values_array = values.make_array();
    std::shared_ptr<Array> offsets_array;
    RETURN_NOT_OK(this->SortToIndices(ctx, values_array, &offsets_array));
    *offsets = offsets_array;
    return Status::OK();
  }

  std::shared_ptr<DataType> out_type() const { return type_; }

 private:
  Sorter sorter_;

  Status SortToIndicesImpl(FunctionContext* ctx, const std::shared_ptr<ArrayType>& values,
                           std::shared_ptr<Array>* offsets) {
    std::shared_ptr<Buffer> indices_buf;
    int64_t buf_size = values->length() * sizeof(uint64_t);
    RETURN_NOT_OK(AllocateBuffer(ctx->memory_pool(), buf_size, &indices_buf));

    int64_t* indices_begin = reinterpret_cast<int64_t*>(indices_buf->mutable_data());
    int64_t* indices_end = indices_begin + values->length();

    sorter_.Sort(indices_begin, indices_end, *values.get());
    *offsets = std::make_shared<UInt64Array>(values->length(), indices_buf);
    return Status::OK();
  }
};

template <typename ArrowType, typename Comparator,
          typename Sorter = CompareSorter<ArrowType, Comparator>>
SortToIndicesKernelImpl<ArrowType, Sorter>* MakeCompareKernel(Comparator comparator) {
  return new SortToIndicesKernelImpl<ArrowType, Sorter>(Sorter(comparator));
}

template <typename ArrowType, typename Sorter = CountSorter<ArrowType>>
SortToIndicesKernelImpl<ArrowType, Sorter>* MakeCountKernel(int min, int max) {
  return new SortToIndicesKernelImpl<ArrowType, Sorter>(Sorter(min, max));
}

template <typename ArrowType, typename Comparator,
          typename Sorter = CountOrCompareSorter<ArrowType, Comparator>>
SortToIndicesKernelImpl<ArrowType, Sorter>* MakeCountOrCompareKernel(
    Comparator comparator) {
  return new SortToIndicesKernelImpl<ArrowType, Sorter>(Sorter(comparator));
}

Status SortToIndicesKernel::Make(const std::shared_ptr<DataType>& value_type,
                                 std::unique_ptr<SortToIndicesKernel>* out) {
  SortToIndicesKernel* kernel;
  switch (value_type->id()) {
    case Type::UINT8:
      kernel = MakeCountKernel<UInt8Type>(0, 255);
      break;
    case Type::INT8:
      kernel = MakeCountKernel<Int8Type>(-128, 127);
      break;
    case Type::UINT16:
      kernel = MakeCountOrCompareKernel<UInt16Type>(CompareValues<UInt16Array>);
      break;
    case Type::INT16:
      kernel = MakeCountOrCompareKernel<Int16Type>(CompareValues<Int16Array>);
      break;
    case Type::UINT32:
      kernel = MakeCountOrCompareKernel<UInt32Type>(CompareValues<UInt32Array>);
      break;
    case Type::INT32:
      kernel = MakeCountOrCompareKernel<Int32Type>(CompareValues<Int32Array>);
      break;
    case Type::UINT64:
      kernel = MakeCountOrCompareKernel<UInt64Type>(CompareValues<UInt64Array>);
      break;
    case Type::INT64:
      kernel = MakeCountOrCompareKernel<Int64Type>(CompareValues<Int64Array>);
      break;
    case Type::FLOAT:
      kernel = MakeCompareKernel<FloatType>(CompareValues<FloatArray>);
      break;
    case Type::DOUBLE:
      kernel = MakeCompareKernel<DoubleType>(CompareValues<DoubleArray>);
      break;
    case Type::BINARY:
      kernel = MakeCompareKernel<BinaryType>(CompareViews<BinaryArray>);
      break;
    case Type::STRING:
      kernel = MakeCompareKernel<StringType>(CompareViews<StringArray>);
      break;
    default:
      return Status::NotImplemented("Sorting of ", *value_type, " arrays");
  }
  out->reset(kernel);
  return Status::OK();
}

Status SortToIndices(FunctionContext* ctx, const Datum& values, Datum* offsets) {
  std::unique_ptr<SortToIndicesKernel> kernel;
  RETURN_NOT_OK(SortToIndicesKernel::Make(values.type(), &kernel));
  return kernel->Call(ctx, values, offsets);
}

Status SortToIndices(FunctionContext* ctx, const Array& values,
                     std::shared_ptr<Array>* offsets) {
  Datum offsets_datum;
  RETURN_NOT_OK(SortToIndices(ctx, Datum(values.data()), &offsets_datum));
  *offsets = offsets_datum.make_array();
  return Status::OK();
}

}  // namespace compute
}  // namespace arrow
<commit_msg>ARROW-7992: [C++] Fix MSVC warning (#6525)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "arrow/compute/kernels/sort_to_indices.h"

#include <algorithm>
#include <limits>
#include <numeric>
#include <vector>

#include "arrow/builder.h"
#include "arrow/compute/context.h"
#include "arrow/compute/expression.h"
#include "arrow/compute/logical_type.h"
#include "arrow/type_traits.h"
#include "arrow/visitor_inline.h"

namespace arrow {

class Array;

namespace compute {

/// \brief UnaryKernel implementing SortToIndices operation
class ARROW_EXPORT SortToIndicesKernel : public UnaryKernel {
 protected:
  std::shared_ptr<DataType> type_;

 public:
  /// \brief UnaryKernel interface
  ///
  /// delegates to subclasses via SortToIndices()
  Status Call(FunctionContext* ctx, const Datum& values, Datum* offsets) override = 0;

  /// \brief output type of this kernel
  std::shared_ptr<DataType> out_type() const override { return uint64(); }

  /// \brief single-array implementation
  virtual Status SortToIndices(FunctionContext* ctx, const std::shared_ptr<Array>& values,
                               std::shared_ptr<Array>* offsets) = 0;

  /// \brief factory for SortToIndicesKernel
  ///
  /// \param[in] value_type constructed SortToIndicesKernel will support sorting
  ///            values of this type
  /// \param[out] out created kernel
  static Status Make(const std::shared_ptr<DataType>& value_type,
                     std::unique_ptr<SortToIndicesKernel>* out);
};

template <typename ArrayType>
bool CompareValues(const ArrayType& array, uint64_t lhs, uint64_t rhs) {
  return array.Value(lhs) < array.Value(rhs);
}

template <typename ArrayType>
bool CompareViews(const ArrayType& array, uint64_t lhs, uint64_t rhs) {
  return array.GetView(lhs) < array.GetView(rhs);
}

template <typename ArrowType, typename Comparator>
class CompareSorter {
  using ArrayType = typename TypeTraits<ArrowType>::ArrayType;

 public:
  explicit CompareSorter(Comparator compare) : compare_(compare) {}

  void Sort(int64_t* indices_begin, int64_t* indices_end, const ArrayType& values) {
    std::iota(indices_begin, indices_end, 0);

    auto nulls_begin = indices_end;
    if (values.null_count()) {
      nulls_begin =
          std::stable_partition(indices_begin, indices_end,
                                [&values](uint64_t ind) { return !values.IsNull(ind); });
    }
    std::stable_sort(indices_begin, nulls_begin,
                     [&values, this](uint64_t left, uint64_t right) {
                       return compare_(values, left, right);
                     });
  }

 private:
  Comparator compare_;
};

template <typename ArrowType>
class CountSorter {
  using ArrayType = typename TypeTraits<ArrowType>::ArrayType;
  using c_type = typename ArrowType::c_type;

 public:
  CountSorter() = default;

  explicit CountSorter(c_type min, c_type max) { SetMinMax(min, max); }

  // Assume: max >= min && (max - min) < 4Gi
  void SetMinMax(c_type min, c_type max) {
    min_ = min;
    value_range_ = static_cast<uint32_t>(max - min) + 1;
  }

  void Sort(int64_t* indices_begin, int64_t* indices_end, const ArrayType& values) {
    // 32bit counter performs much better than 64bit one
    if (values.length() < (1LL << 32)) {
      SortInternal<uint32_t>(indices_begin, indices_end, values);
    } else {
      SortInternal<uint64_t>(indices_begin, indices_end, values);
    }
  }

 private:
  c_type min_{0};
  uint32_t value_range_{0};

  template <typename CounterType>
  void SortInternal(int64_t* indices_begin, int64_t* indices_end,
                    const ArrayType& values) {
    const uint32_t value_range = value_range_;

    // first slot reserved for prefix sum, last slot for null value
    std::vector<CounterType> counts(1 + value_range + 1);

    struct UpdateCounts {
      Status VisitNull() {
        ++counts[value_range];
        return Status::OK();
      }

      Status VisitValue(c_type v) {
        ++counts[v - min];
        return Status::OK();
      }

      CounterType* counts;
      const uint32_t value_range;
      c_type min;
    };
    {
      UpdateCounts update_counts{&counts[1], value_range, min_};
      ARROW_CHECK_OK(ArrayDataVisitor<ArrowType>().Visit(*values.data(), &update_counts));
    }

    for (uint32_t i = 1; i <= value_range; ++i) {
      counts[i] += counts[i - 1];
    }

    struct OutputIndices {
      Status VisitNull() {
        out_indices[counts[value_range]++] = index++;
        return Status::OK();
      }

      Status VisitValue(c_type v) {
        out_indices[counts[v - min]++] = index++;
        return Status::OK();
      }

      CounterType* counts;
      const uint32_t value_range;
      c_type min;
      int64_t* out_indices;
      int64_t index;
    };
    {
      OutputIndices output_indices{&counts[0], value_range, min_, indices_begin, 0};
      ARROW_CHECK_OK(
          ArrayDataVisitor<ArrowType>().Visit(*values.data(), &output_indices));
    }
  }
};

// Sort integers with counting sort or comparison based sorting algorithm
// - Use O(n) counting sort if values are in a small range
// - Use O(nlogn) std::stable_sort otherwise
template <typename ArrowType, typename Comparator>
class CountOrCompareSorter {
  using ArrayType = typename TypeTraits<ArrowType>::ArrayType;
  using c_type = typename ArrowType::c_type;

 public:
  explicit CountOrCompareSorter(Comparator compare) : compare_sorter_(compare) {}

  void Sort(int64_t* indices_begin, int64_t* indices_end, const ArrayType& values) {
    if (values.length() >= countsort_min_len_ && values.length() > values.null_count()) {
      struct MinMaxScanner {
        Status VisitNull() { return Status::OK(); }

        Status VisitValue(c_type v) {
          min = std::min(min, v);
          max = std::max(max, v);
          return Status::OK();
        }

        c_type min{std::numeric_limits<c_type>::max()};
        c_type max{std::numeric_limits<c_type>::min()};
      };

      MinMaxScanner minmax_scanner;
      ARROW_CHECK_OK(
          ArrayDataVisitor<ArrowType>().Visit(*values.data(), &minmax_scanner));

      // For signed int32/64, (max - min) may overflow and trigger UBSAN.
      // Cast to largest unsigned type(uint64_t) before substraction.
      const uint64_t min = static_cast<uint64_t>(minmax_scanner.min);
      const uint64_t max = static_cast<uint64_t>(minmax_scanner.max);
      if ((max - min) <= countsort_max_range_) {
        count_sorter_.SetMinMax(minmax_scanner.min, minmax_scanner.max);
        count_sorter_.Sort(indices_begin, indices_end, values);
        return;
      }
    }

    compare_sorter_.Sort(indices_begin, indices_end, values);
  }

 private:
  CompareSorter<ArrowType, Comparator> compare_sorter_;
  CountSorter<ArrowType> count_sorter_;

  // Cross point to prefer counting sort than stl::stable_sort(merge sort)
  // - array to be sorted is longer than "count_min_len_"
  // - value range (max-min) is within "count_max_range_"
  //
  // The optimal setting depends heavily on running CPU. Below setting is
  // conservative to adapt to various hardware and keep code simple.
  // It's possible to decrease array-len and/or increase value-range to cover
  // more cases, or setup a table for best array-len/value-range combinations.
  // See https://issues.apache.org/jira/browse/ARROW-1571 for detailed analysis.
  static const uint32_t countsort_min_len_ = 1024;
  static const uint32_t countsort_max_range_ = 4096;
};

template <typename ArrowType, typename Sorter>
class SortToIndicesKernelImpl : public SortToIndicesKernel {
  using ArrayType = typename TypeTraits<ArrowType>::ArrayType;

 public:
  explicit SortToIndicesKernelImpl(Sorter sorter) : sorter_(sorter) {}

  Status SortToIndices(FunctionContext* ctx, const std::shared_ptr<Array>& values,
                       std::shared_ptr<Array>* offsets) {
    return SortToIndicesImpl(ctx, std::static_pointer_cast<ArrayType>(values), offsets);
  }

  Status Call(FunctionContext* ctx, const Datum& values, Datum* offsets) {
    if (!values.is_array()) {
      return Status::Invalid("SortToIndicesKernel expects array values");
    }
    auto values_array = values.make_array();
    std::shared_ptr<Array> offsets_array;
    RETURN_NOT_OK(this->SortToIndices(ctx, values_array, &offsets_array));
    *offsets = offsets_array;
    return Status::OK();
  }

  std::shared_ptr<DataType> out_type() const { return type_; }

 private:
  Sorter sorter_;

  Status SortToIndicesImpl(FunctionContext* ctx, const std::shared_ptr<ArrayType>& values,
                           std::shared_ptr<Array>* offsets) {
    std::shared_ptr<Buffer> indices_buf;
    int64_t buf_size = values->length() * sizeof(uint64_t);
    RETURN_NOT_OK(AllocateBuffer(ctx->memory_pool(), buf_size, &indices_buf));

    int64_t* indices_begin = reinterpret_cast<int64_t*>(indices_buf->mutable_data());
    int64_t* indices_end = indices_begin + values->length();

    sorter_.Sort(indices_begin, indices_end, *values.get());
    *offsets = std::make_shared<UInt64Array>(values->length(), indices_buf);
    return Status::OK();
  }
};

template <typename ArrowType, typename Comparator,
          typename Sorter = CompareSorter<ArrowType, Comparator>>
SortToIndicesKernelImpl<ArrowType, Sorter>* MakeCompareKernel(Comparator comparator) {
  return new SortToIndicesKernelImpl<ArrowType, Sorter>(Sorter(comparator));
}

template <typename ArrowType, typename Sorter = CountSorter<ArrowType>>
SortToIndicesKernelImpl<ArrowType, Sorter>* MakeCountKernel(int min, int max) {
  return new SortToIndicesKernelImpl<ArrowType, Sorter>(Sorter(min, max));
}

template <typename ArrowType, typename Comparator,
          typename Sorter = CountOrCompareSorter<ArrowType, Comparator>>
SortToIndicesKernelImpl<ArrowType, Sorter>* MakeCountOrCompareKernel(
    Comparator comparator) {
  return new SortToIndicesKernelImpl<ArrowType, Sorter>(Sorter(comparator));
}

Status SortToIndicesKernel::Make(const std::shared_ptr<DataType>& value_type,
                                 std::unique_ptr<SortToIndicesKernel>* out) {
  SortToIndicesKernel* kernel;
  switch (value_type->id()) {
    case Type::UINT8:
      kernel = MakeCountKernel<UInt8Type>(0, 255);
      break;
    case Type::INT8:
      kernel = MakeCountKernel<Int8Type>(-128, 127);
      break;
    case Type::UINT16:
      kernel = MakeCountOrCompareKernel<UInt16Type>(CompareValues<UInt16Array>);
      break;
    case Type::INT16:
      kernel = MakeCountOrCompareKernel<Int16Type>(CompareValues<Int16Array>);
      break;
    case Type::UINT32:
      kernel = MakeCountOrCompareKernel<UInt32Type>(CompareValues<UInt32Array>);
      break;
    case Type::INT32:
      kernel = MakeCountOrCompareKernel<Int32Type>(CompareValues<Int32Array>);
      break;
    case Type::UINT64:
      kernel = MakeCountOrCompareKernel<UInt64Type>(CompareValues<UInt64Array>);
      break;
    case Type::INT64:
      kernel = MakeCountOrCompareKernel<Int64Type>(CompareValues<Int64Array>);
      break;
    case Type::FLOAT:
      kernel = MakeCompareKernel<FloatType>(CompareValues<FloatArray>);
      break;
    case Type::DOUBLE:
      kernel = MakeCompareKernel<DoubleType>(CompareValues<DoubleArray>);
      break;
    case Type::BINARY:
      kernel = MakeCompareKernel<BinaryType>(CompareViews<BinaryArray>);
      break;
    case Type::STRING:
      kernel = MakeCompareKernel<StringType>(CompareViews<StringArray>);
      break;
    default:
      return Status::NotImplemented("Sorting of ", *value_type, " arrays");
  }
  out->reset(kernel);
  return Status::OK();
}

Status SortToIndices(FunctionContext* ctx, const Datum& values, Datum* offsets) {
  std::unique_ptr<SortToIndicesKernel> kernel;
  RETURN_NOT_OK(SortToIndicesKernel::Make(values.type(), &kernel));
  return kernel->Call(ctx, values, offsets);
}

Status SortToIndices(FunctionContext* ctx, const Array& values,
                     std::shared_ptr<Array>* offsets) {
  Datum offsets_datum;
  RETURN_NOT_OK(SortToIndices(ctx, Datum(values.data()), &offsets_datum));
  *offsets = offsets_datum.make_array();
  return Status::OK();
}

}  // namespace compute
}  // namespace arrow
<|endoftext|>


<commit_before>#include <bits/stdc++.h>

using namespace std;

// https://cp-algorithms.com/data_structures/segment_tree.html

const int maxn = 200000;
int tmax[4 * maxn];
int tadd[4 * maxn]; // tadd[i] applies to tmax[i], tadd[2*i+1] and tadd[2*i+2]

void push(int root) {
    tmax[root] += tadd[root];
    tadd[2 * root + 1] += tadd[root];
    tadd[2 * root + 2] += tadd[root];
    tadd[root] = 0;
}

int max(int from, int to, int root = 0, int left = 0, int right = maxn - 1) {
    if (from > right || left > to)
        return INT_MIN;
    if (from <= left && right <= to) {
        return tmax[root] + tadd[root];
    }
    push(root);
    int mid = (left + right) / 2;
    int res = std::max(max(from, to, 2 * root + 1, left, mid),
                       max(from, to, 2 * root + 2, mid + 1, right));
    return res;
}

void add(int from, int to, int delta, int root = 0, int left = 0, int right = maxn - 1) {
    if (from > right || left > to)
        return;
    if (from <= left && right <= to) {
        tadd[root] += delta;
        return;
    }
    push(root); // this push may be omitted for add, but is necessary for other operations such as set
    int mid = (left + right) / 2;
    add(from, to, delta, 2 * root + 1, left, mid);
    add(from, to, delta, 2 * root + 2, mid + 1, right);
    tmax[root] = std::max(tmax[2 * root + 1] + tadd[2 * root + 1], tmax[2 * root + 2] + tadd[2 * root + 2]);
}

// usage example
int main() {
    add(0, 9, 1);
    add(2, 4, 2);
    add(3, 5, 3);

    cout << (6 == max(0, 9)) << endl;
    cout << (6 == tmax[0] + tadd[0]) << endl;
    cout << (1 == max(0, 0)) << endl;
}
<commit_msg>update<commit_after>#include <bits/stdc++.h>

using namespace std;

// https://cp-algorithms.com/data_structures/segment_tree.html

const int maxn = 200000;
int tmax[4 * maxn];
int tadd[4 * maxn]; // tadd[i] applies to tmax[i], tadd[2*i+1] and tadd[2*i+2]

void push(int root) {
    tmax[root] += tadd[root];
    tadd[2 * root + 1] += tadd[root];
    tadd[2 * root + 2] += tadd[root];
    tadd[root] = 0;
}

int max(int from, int to, int root = 0, int left = 0, int right = maxn - 1) {
    if (from > right || left > to)
        return INT_MIN;
    if (from <= left && right <= to) {
        return tmax[root] + tadd[root];
    }
    push(root);
    int mid = (left + right) / 2;
    return std::max(max(from, to, 2 * root + 1, left, mid), max(from, to, 2 * root + 2, mid + 1, right));
}

void add(int from, int to, int delta, int root = 0, int left = 0, int right = maxn - 1) {
    if (from > right || left > to)
        return;
    if (from <= left && right <= to) {
        tadd[root] += delta;
        return;
    }
    push(root); // this push may be omitted for add, but is necessary for other operations such as set
    int mid = (left + right) / 2;
    add(from, to, delta, 2 * root + 1, left, mid);
    add(from, to, delta, 2 * root + 2, mid + 1, right);
    tmax[root] = std::max(tmax[2 * root + 1] + tadd[2 * root + 1], tmax[2 * root + 2] + tadd[2 * root + 2]);
}

// usage example
int main() {
    add(0, 9, 1);
    add(2, 4, 2);
    add(3, 5, 3);

    cout << (6 == max(0, 9)) << endl;
    cout << (6 == tmax[0] + tadd[0]) << endl;
    cout << (1 == max(0, 0)) << endl;
}
<|endoftext|>


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

#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "joynr/ClusterControllerSettings.h"
#include "joynr/ImmutableMessage.h"
#include "joynr/MessagingSettings.h"
#include "joynr/MutableMessage.h"
#include "joynr/PrivateCopyAssign.h"
#include "joynr/Semaphore.h"
#include "joynr/Settings.h"
#include "joynr/TimePoint.h"
#include "libjoynrclustercontroller/mqtt/MosquittoConnection.h"

#include "tests/JoynrTest.h"

using namespace joynr;

class MosquittoConnectionTest : public ::testing::Test
{
public:
    MosquittoConnectionTest() :
        _settings("test-resources/MqttJoynrClusterControllerRuntimeTest.settings"),
        _messagingSettings(_settings),
        _ccSettings(_settings),
        _brokerUrl(_messagingSettings.getBrokerUrl()),
        _mqttKeepAliveTimeSeconds(_messagingSettings.getMqttKeepAliveTimeSeconds()),
        _mqttReconnectDelayTimeSeconds(_messagingSettings.getMqttReconnectDelayTimeSeconds()),
        _mqttReconnectMaxDelayTimeSeconds(_messagingSettings.getMqttReconnectMaxDelayTimeSeconds()),
        _isMqttExponentialBackoffEnabled(_messagingSettings.getMqttExponentialBackoffEnabled())
    {
    }

    ~MosquittoConnectionTest() = default;

protected:
    std::shared_ptr<ImmutableMessage> createMessage(const TimePoint& expiryDate,
            const std::string& recipient,
            const std::string& payload = "")
    {
        MutableMessage mutableMsg;
        mutableMsg.setExpiryDate(expiryDate);
        mutableMsg.setRecipient(recipient);
        mutableMsg.setPayload(payload);
        return mutableMsg.getImmutableMessage();
    }

    static std::string payloadAsString(std::shared_ptr<ImmutableMessage> message)
    {
        const smrf::ByteArrayView& byteArrayView = message->getUnencryptedBody();
        return std::string(
                reinterpret_cast<const char*>(byteArrayView.data()), byteArrayView.size());
    }

    std::shared_ptr<MosquittoConnection> createMosquittoConnection(
            std::shared_ptr<joynr::Semaphore> readyToSendSemaphore,
            const std::string clientId,
            const std::string channelId
            )
    {
        auto mosquittoConnection = std::make_shared<MosquittoConnection>(
                _ccSettings,
                _brokerUrl,
                _mqttKeepAliveTimeSeconds,
                _mqttReconnectDelayTimeSeconds,
                _mqttReconnectMaxDelayTimeSeconds,
                _isMqttExponentialBackoffEnabled,
                clientId);

        // register connection to channelId
        mosquittoConnection->registerChannelId(channelId);
        // connection is established and ready to send messages
        mosquittoConnection->registerReadyToSendChangedCallback([readyToSendSemaphore](bool readyToSend) {
                if (readyToSend) {
                    readyToSendSemaphore->notify();
                }
        });
        return mosquittoConnection;
    }

    Settings _settings;
    MessagingSettings _messagingSettings;
    ClusterControllerSettings _ccSettings;
    const BrokerUrl _brokerUrl;
    std::chrono::seconds _mqttKeepAliveTimeSeconds;
    const std::chrono::seconds _mqttReconnectDelayTimeSeconds, _mqttReconnectMaxDelayTimeSeconds;
    const bool _isMqttExponentialBackoffEnabled;

private:
    DISALLOW_COPY_AND_ASSIGN(MosquittoConnectionTest);
};

TEST_F(MosquittoConnectionTest, generalTest)
{
    // These values remain persisted in the broker
    // use different ones when you need to create other connections
    const std::string clientId1("clientId1");
    const std::string clientId2("clientId2");
    const std::string channelId1("channelId1");
    const std::string channelId2("channelId2");

    // create two MosquittoConnections using different clientIds and channels,
    // both connections connect to the same broker
    auto readyToSendSemaphore1 = std::make_shared<joynr::Semaphore>(0);
    auto mosquittoConnection1 = createMosquittoConnection(
                readyToSendSemaphore1,
                clientId1,
                channelId1
                );
    mosquittoConnection1->registerReceiveCallback([](smrf::ByteVector&& msg) {
            std::ignore = msg;
    });

    // after connection is established, subscription to channel based topic
    // should be established automatically
    mosquittoConnection1->start();
    EXPECT_TRUE(readyToSendSemaphore1->waitFor(std::chrono::seconds(5)));
    EXPECT_TRUE(mosquittoConnection1->isReadyToSend());

    auto readyToSendSemaphore2 = std::make_shared<joynr::Semaphore>(0);
    auto mosquittoConnection2 = createMosquittoConnection(
                readyToSendSemaphore2,
                clientId2,
                channelId2
                );

    auto msgReceived2 = std::make_shared<joynr::Semaphore>(0);
    const std::string recipient2 = "recipient2";
    const std::string payload2 = "payload2";

    mosquittoConnection2->registerReceiveCallback([recipient2, payload2, msgReceived2](smrf::ByteVector&& msg) {
            auto immutableMessage = std::make_shared<ImmutableMessage>(msg);
            EXPECT_EQ(immutableMessage->getRecipient(), recipient2);
            EXPECT_EQ(MosquittoConnectionTest::payloadAsString(immutableMessage), payload2);
            msgReceived2->notify();
    });

    mosquittoConnection2->start();
    EXPECT_TRUE(readyToSendSemaphore2->waitFor(std::chrono::seconds(10)));
    EXPECT_TRUE(mosquittoConnection2->isReadyToSend());

    // create a message and send it via mosquittoConnection1 to mosquittoConnection2
    const std::function<void(const exceptions::JoynrRuntimeException&)>& onFailure = nullptr;
    const auto now = TimePoint::now();
    auto immutableMessage = createMessage(now + 10000, recipient2, payload2);

    std::chrono::milliseconds msgTtlMs = immutableMessage->getExpiryDate().relativeFromNow();
    std::uint32_t msgTtlSec = static_cast<std::uint32_t>(std::ceil(msgTtlMs.count() / 1000.0));

    const smrf::ByteVector& rawMessage = immutableMessage->getSerializedMessage();
    const std::string topic = channelId2 + "/low";
    const int qosLevel = mosquittoConnection1->getMqttQos();
    mosquittoConnection1->publishMessage(topic,
                                         qosLevel,
                                         onFailure,
                                         msgTtlSec,
                                         rawMessage.size(),
                                         rawMessage.data());

    // check the message has been received on mosquittoConnection2
    EXPECT_TRUE(msgReceived2->waitFor(std::chrono::seconds(10)));

    // subscribe and unsubscribe to/from an additional topic
    const std::string additionalTopic("additionalTopic");
    mosquittoConnection1->subscribeToTopic(additionalTopic);
    mosquittoConnection2->subscribeToTopic(additionalTopic);
    std::this_thread::sleep_for(std::chrono::seconds(2));
    mosquittoConnection1->unsubscribeFromTopic(additionalTopic);
    mosquittoConnection2->unsubscribeFromTopic(additionalTopic);
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // cleanup
    mosquittoConnection1->stop();
    mosquittoConnection2->stop();
}
<commit_msg>[C++] Unit tests for MQTT message expiry interval in MosquittoConnection<commit_after>/*
 * #%L
 * %%
 * Copyright (C) 2020 BMW Car IT GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
#include <chrono>
#include <cmath>
#include <ctime>
#include <cstdint>
#include <memory>
#include <string>

#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include "joynr/ClusterControllerSettings.h"
#include "joynr/ImmutableMessage.h"
#include "joynr/MessagingSettings.h"
#include "joynr/MutableMessage.h"
#include "joynr/PrivateCopyAssign.h"
#include "joynr/Semaphore.h"
#include "joynr/Settings.h"
#include "joynr/TimePoint.h"
#include "libjoynrclustercontroller/mqtt/MosquittoConnection.h"

#include "tests/JoynrTest.h"

using namespace joynr;

class MosquittoConnectionTest : public ::testing::Test
{
public:
    MosquittoConnectionTest() :
        _settings("test-resources/MqttJoynrClusterControllerRuntimeTest.settings"),
        _messagingSettings(_settings),
        _ccSettings(_settings),
        _brokerUrl(_messagingSettings.getBrokerUrl()),
        _mqttKeepAliveTimeSeconds(_messagingSettings.getMqttKeepAliveTimeSeconds()),
        _mqttReconnectDelayTimeSeconds(_messagingSettings.getMqttReconnectDelayTimeSeconds()),
        _mqttReconnectMaxDelayTimeSeconds(_messagingSettings.getMqttReconnectMaxDelayTimeSeconds()),
        _isMqttExponentialBackoffEnabled(_messagingSettings.getMqttExponentialBackoffEnabled())
    {
    }

    ~MosquittoConnectionTest() = default;

protected:
    std::shared_ptr<ImmutableMessage> createMessage(const TimePoint& expiryDate,
            const std::string& recipient,
            const std::string& payload = "")
    {
        MutableMessage mutableMsg;
        mutableMsg.setExpiryDate(expiryDate);
        mutableMsg.setRecipient(recipient);
        mutableMsg.setPayload(payload);
        return mutableMsg.getImmutableMessage();
    }

    static std::string payloadAsString(std::shared_ptr<ImmutableMessage> message)
    {
        const smrf::ByteArrayView& byteArrayView = message->getUnencryptedBody();
        return std::string(
                reinterpret_cast<const char*>(byteArrayView.data()), byteArrayView.size());
    }

    std::shared_ptr<MosquittoConnection> createMosquittoConnection(
            std::shared_ptr<joynr::Semaphore> readyToSendSemaphore,
            const std::string clientId,
            const std::string channelId
            )
    {
        auto mosquittoConnection = std::make_shared<MosquittoConnection>(
                _ccSettings,
                _brokerUrl,
                _mqttKeepAliveTimeSeconds,
                _mqttReconnectDelayTimeSeconds,
                _mqttReconnectMaxDelayTimeSeconds,
                _isMqttExponentialBackoffEnabled,
                clientId);

        // register connection to channelId
        mosquittoConnection->registerChannelId(channelId);
        // connection is established and ready to send messages
        mosquittoConnection->registerReadyToSendChangedCallback([readyToSendSemaphore](bool readyToSend) {
                if (readyToSend) {
                    readyToSendSemaphore->notify();
                }
        });
        return mosquittoConnection;
    }

    Settings _settings;
    MessagingSettings _messagingSettings;
    ClusterControllerSettings _ccSettings;
    const BrokerUrl _brokerUrl;
    std::chrono::seconds _mqttKeepAliveTimeSeconds;
    const std::chrono::seconds _mqttReconnectDelayTimeSeconds, _mqttReconnectMaxDelayTimeSeconds;
    const bool _isMqttExponentialBackoffEnabled;

private:
    DISALLOW_COPY_AND_ASSIGN(MosquittoConnectionTest);
};

TEST_F(MosquittoConnectionTest, generalTest)
{
    // These values remain persisted in the broker
    // use different ones when you need to create other connections
    std::srand(std::time(0));
    std::string random = std::to_string(std::rand());
    const std::string clientId1("clientId1-" + random);
    const std::string clientId2("clientId2-" + random);
    const std::string channelId1("channelId1-" + random);
    const std::string channelId2("channelId2-" + random);

    // create two MosquittoConnections using different clientIds and channels,
    // both connections connect to the same broker
    auto readyToSendSemaphore1 = std::make_shared<joynr::Semaphore>(0);
    auto mosquittoConnection1 = createMosquittoConnection(
                readyToSendSemaphore1,
                clientId1,
                channelId1
                );
    mosquittoConnection1->registerReceiveCallback([](smrf::ByteVector&& msg) {
            std::ignore = msg;
            FAIL() << "We do not expect to receive msgs on connection1";
    });

    // after connection is established, subscription to channel based topic
    // should be established automatically
    mosquittoConnection1->start();
    EXPECT_TRUE(readyToSendSemaphore1->waitFor(std::chrono::seconds(5)));
    EXPECT_TRUE(mosquittoConnection1->isReadyToSend());

    auto readyToSendSemaphore2 = std::make_shared<joynr::Semaphore>(0);
    auto mosquittoConnection2 = createMosquittoConnection(
                readyToSendSemaphore2,
                clientId2,
                channelId2
                );

    auto msgReceived2 = std::make_shared<joynr::Semaphore>(0);
    const std::string recipient2 = "recipient2";
    const std::string payload2 = "payload2";

    mosquittoConnection2->registerReceiveCallback([recipient2, payload2, msgReceived2](smrf::ByteVector&& msg) {
            auto immutableMessage = std::make_shared<ImmutableMessage>(msg);
            EXPECT_EQ(immutableMessage->getRecipient(), recipient2);
            EXPECT_EQ(MosquittoConnectionTest::payloadAsString(immutableMessage), payload2);
            msgReceived2->notify();
    });

    mosquittoConnection2->start();
    EXPECT_TRUE(readyToSendSemaphore2->waitFor(std::chrono::seconds(10)));
    EXPECT_TRUE(mosquittoConnection2->isReadyToSend());

    // create a message and send it via mosquittoConnection1 to mosquittoConnection2
    const std::function<void(const exceptions::JoynrRuntimeException&)>& onFailure = nullptr;
    const auto now = TimePoint::now();
    auto immutableMessage = createMessage(now + 10000, recipient2, payload2);

    const std::uint32_t msgTtlSec = 60;
    const smrf::ByteVector& rawMessage = immutableMessage->getSerializedMessage();
    const std::string topic = channelId2 + "/low";
    const int qosLevel = mosquittoConnection1->getMqttQos();
    mosquittoConnection1->publishMessage(topic,
                                         qosLevel,
                                         onFailure,
                                         msgTtlSec,
                                         rawMessage.size(),
                                         rawMessage.data());

    // check the message has been received on mosquittoConnection2
    EXPECT_TRUE(msgReceived2->waitFor(std::chrono::seconds(10)));

    // subscribe and unsubscribe to/from an additional topic
    const std::string additionalTopic("additionalTopic");
    mosquittoConnection1->subscribeToTopic(additionalTopic);
    mosquittoConnection2->subscribeToTopic(additionalTopic);
    std::this_thread::sleep_for(std::chrono::seconds(2));
    mosquittoConnection1->unsubscribeFromTopic(additionalTopic);
    mosquittoConnection2->unsubscribeFromTopic(additionalTopic);
    std::this_thread::sleep_for(std::chrono::seconds(2));

    // cleanup
    mosquittoConnection1->stop();
    mosquittoConnection2->stop();
}

TEST_F(MosquittoConnectionTest, deliverMessageWithinItsExpiryIntervalAfterReconnect)
{
    std::srand(std::time(0));
    std::string random = std::to_string(std::rand());
    const std::string clientId3("clientId3-" + random);
    const std::string clientId4("clientId4-" + random);
    const std::string channelId3("channelId3-" + random);
    const std::string channelId4("channelId4-" + random);

    // connection1
    auto readyToSendSemaphore1 = std::make_shared<joynr::Semaphore>(0);
    auto mosquittoConnection1 = createMosquittoConnection(
                readyToSendSemaphore1,
                clientId3,
                channelId3
                );

    // we do not plan to receive msgs on connection1, but we have to
    // set registerReceiveCallback.
    mosquittoConnection1->registerReceiveCallback([](smrf::ByteVector&& msg) {
            std::ignore = msg;
    });

    mosquittoConnection1->start();
    EXPECT_TRUE(readyToSendSemaphore1->waitFor(std::chrono::seconds(5)));
    EXPECT_TRUE(mosquittoConnection1->isReadyToSend());

    // connection2
    auto readyToSendSemaphore2 = std::make_shared<joynr::Semaphore>(0);
    auto mosquittoConnection2 = createMosquittoConnection(
                readyToSendSemaphore2,
                clientId4,
                channelId4
                );

    const std::string dummyRecipient = "dummyRecipient";
    const std::string dummyPayload = "dummyPayload";
    // check the message has been received on mosquittoConnection2
    auto msgWithExpiryDateReceived = std::make_shared<joynr::Semaphore>(0);
    mosquittoConnection2->registerReceiveCallback([dummyRecipient, dummyPayload, msgWithExpiryDateReceived](smrf::ByteVector&& msg) {
            auto receivedImmutableMessage = std::make_shared<ImmutableMessage>(msg);
            EXPECT_EQ(receivedImmutableMessage->getRecipient(), dummyRecipient);
            EXPECT_EQ(MosquittoConnectionTest::payloadAsString(receivedImmutableMessage), dummyPayload);
            msgWithExpiryDateReceived->notify();
    });

    mosquittoConnection2->start();
    EXPECT_TRUE(readyToSendSemaphore2->waitFor(std::chrono::seconds(10)));
    EXPECT_TRUE(mosquittoConnection2->isReadyToSend());

    mosquittoConnection2->stop();

    // prepare a message and publish it via the broker to connection2 (while it is unavailable)
    const std::function<void(const exceptions::JoynrRuntimeException&)>& onFailure = nullptr;
    const auto now = TimePoint::now();
    const std::int64_t msgDurationMs = 6000; // 1M
    auto immutableMessage = createMessage(now + msgDurationMs, dummyRecipient, dummyPayload);

    std::chrono::milliseconds msgTtlMs = immutableMessage->getExpiryDate().relativeFromNow();
    std::uint32_t msgTtlSec = static_cast<std::uint32_t>(std::ceil(msgTtlMs.count() / 1000.0));

    const smrf::ByteVector& rawMessage = immutableMessage->getSerializedMessage();
    const std::string topic = channelId4 + "/low";
    const int qosLevel = mosquittoConnection1->getMqttQos();

    mosquittoConnection1->publishMessage(topic,
                                         qosLevel,
                                         onFailure,
                                         msgTtlSec,
                                         rawMessage.size(),
                                         rawMessage.data());

    std::this_thread::sleep_for(std::chrono::seconds(2));
    // The broker should keep the msg for 1M
    // start connection2 before message expires at the broker
    mosquittoConnection2->start();
    EXPECT_TRUE(readyToSendSemaphore2->waitFor(std::chrono::seconds(5)));
    EXPECT_TRUE(mosquittoConnection2->isReadyToSend());

    // message still not expired it will be received
    EXPECT_TRUE(msgWithExpiryDateReceived->waitFor(std::chrono::seconds(5)));

    // cleanup
    mosquittoConnection1->stop();
    mosquittoConnection2->stop();
}

TEST_F(MosquittoConnectionTest, noMessageDeliveryWhenExceedingItsExpiryIntervalAfterReconnect)
{
    std::srand(std::time(0));
    std::string random = std::to_string(std::rand());
    const std::string clientId5("clientId5-" + random);
    const std::string clientId6("clientId6-" + random);
    const std::string channelId5("channelId5-" + random);
    const std::string channelId6("channelId6-" + random);

    // connection1
    auto readyToSendSemaphore1 = std::make_shared<joynr::Semaphore>(0);
    auto mosquittoConnection1 = createMosquittoConnection(
                readyToSendSemaphore1,
                clientId5,
                channelId5
                );

    // we do not plan to receive msgs on connection1, but we have to
    // set registerReceiveCallback.
    mosquittoConnection1->registerReceiveCallback([](smrf::ByteVector&& msg) {
            std::ignore = msg;
            FAIL() << "We do not expect to receive msgs on connection1";
    });

    mosquittoConnection1->start();
    EXPECT_TRUE(readyToSendSemaphore1->waitFor(std::chrono::seconds(5)));
    EXPECT_TRUE(mosquittoConnection1->isReadyToSend());

    // connection2
    auto readyToSendSemaphore2 = std::make_shared<joynr::Semaphore>(0);
    auto mosquittoConnection2 = createMosquittoConnection(
                readyToSendSemaphore2,
                clientId6,
                channelId6
                );

    const std::string anotherDummyRecipient = "anotherDummyRecipient";
    const std::string anotherDummyPayload = "anotherDummyPayload";
    // check the message has been received on mosquittoConnection2
    auto msgWithExpiryDateReceived = std::make_shared<joynr::Semaphore>(0);
    mosquittoConnection2->registerReceiveCallback([anotherDummyRecipient, anotherDummyPayload, msgWithExpiryDateReceived](smrf::ByteVector&& msg) {
            msgWithExpiryDateReceived->notify();
    });

    mosquittoConnection2->start();
    EXPECT_TRUE(readyToSendSemaphore2->waitFor(std::chrono::seconds(10)));
    EXPECT_TRUE(mosquittoConnection2->isReadyToSend());

    mosquittoConnection2->stop();

    // prepare a message and publish it via the broker to connection2 (while it is unavailable)
    const std::function<void(const exceptions::JoynrRuntimeException&)>& onFailure = nullptr;
    const auto now = TimePoint::now();
    const std::int64_t msgDurationMs = 3000; // 3sec
    auto immutableMessage = createMessage(now + msgDurationMs, anotherDummyRecipient, anotherDummyPayload);

    std::chrono::milliseconds msgTtlMs = immutableMessage->getExpiryDate().relativeFromNow();
    std::uint32_t msgTtlSec = static_cast<std::uint32_t>(std::ceil(msgTtlMs.count() / 1000.0));

    const smrf::ByteVector& rawMessage = immutableMessage->getSerializedMessage();
    const std::string topic = channelId6 + "/low";
    const int qosLevel = mosquittoConnection1->getMqttQos();

    mosquittoConnection1->publishMessage(topic,
                                         qosLevel,
                                         onFailure,
                                         msgTtlSec,
                                         rawMessage.size(),
                                         rawMessage.data());

    // The broker should keep the msg for 3sec
    std::this_thread::sleep_for(std::chrono::seconds(4));
    // start connection2 after message expired at the broker
    mosquittoConnection2->start();
    EXPECT_TRUE(readyToSendSemaphore2->waitFor(std::chrono::seconds(5)));
    EXPECT_TRUE(mosquittoConnection2->isReadyToSend());

    // message expired nothing will be received
    EXPECT_FALSE(msgWithExpiryDateReceived->waitFor(std::chrono::seconds(3)));

    // cleanup
    mosquittoConnection1->stop();
    mosquittoConnection2->stop();
}
<|endoftext|>


<commit_before>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2013.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#if     qPlatform_Windows
#include    <windows.h>
#elif   qPlatform_POSIX
#include    <unistd.h>
#include    <sys/mman.h>
#endif

#include    "../../Execution/ErrNoException.h"
#include    "../../Execution/Exceptions.h"
#if     qPlatform_Windows
#include    "../../Execution/Platform/Windows/Exception.h"
#include    "../../Execution/Platform/Windows/HRESULTErrorException.h"
#endif
#include    "../../Debug/Trace.h"
#include    "../../IO/FileAccessException.h"
#include    "../../IO/FileBusyException.h"
#include    "../../IO/FileFormatException.h"
#include    "PathName.h"

#include    "MemoryMappedFileReader.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Characters;
using   namespace   Stroika::Foundation::IO;
using   namespace   Stroika::Foundation::IO::FileSystem;
using   namespace   Stroika::Foundation::Memory;

#if     qPlatform_Windows
using   Execution::Platform::Windows::ThrowIfFalseGetLastError;
#endif

/*
 ********************************************************************************
 ***************************** MemoryMappedFileReader ***************************
 ********************************************************************************
 */
MemoryMappedFileReader::MemoryMappedFileReader (const String& fileName)
    : fFileDataStart_ (nullptr)
    , fFileDataEnd_ (nullptr)
#if         qPlatform_Windows
    , fFileHandle_ (INVALID_HANDLE_VALUE)
    , fFileMapping_ (INVALID_HANDLE_VALUE)
#endif
{
#if         qPlatform_POSIX
    int fd = 3;///tmphack....
    AssertNotImplemented (); // chcek results
    size_t fileLength = 3;//size of file - compute
    void* mmap(void*, size_t, int, int, int, __off_t)
    fFileDataStart_ = reinterpret_cast<const Byte*> (::mmap (nullptr, fileLength, PROT_READ, MAP_PRIVATE, fd, 0));
    fFileDataEnd_ = fFileDataStart_ + fileLength;
    ::close (fd);//http://linux.die.net/man/2/mmap says dont need to keep FD open while mmapped
#elif       qPlatform_Windows
    try {
        // FILE_READ_DATA fails on WinME - generates ERROR_INVALID_PARAMETER - so use GENERIC_READ
        fFileHandle_ = ::CreateFile (fileName.AsTString ().c_str (), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
        ThrowIfFalseGetLastError (fFileHandle_ != INVALID_HANDLE_VALUE);
        DWORD   fileSize    =   ::GetFileSize (fFileHandle_, nullptr);
        if (fileSize != 0) {
            fFileMapping_ = ::CreateFileMapping (fFileHandle_, nullptr, PAGE_READONLY, 0, fileSize, 0);
            ThrowIfFalseGetLastError (fFileMapping_ != nullptr);
            fFileDataStart_ = reinterpret_cast<const Byte*> (::MapViewOfFile (fFileMapping_, FILE_MAP_READ, 0, 0, 0));
            ThrowIfFalseGetLastError (fFileDataStart_ != nullptr);
            fFileDataEnd_ = fFileDataStart_ + fileSize;
        }
    }
    catch (...) {
        if (fFileMapping_ != INVALID_HANDLE_VALUE) {
            ::CloseHandle (fFileMapping_);
        }
        if (fFileHandle_ != INVALID_HANDLE_VALUE) {
            ::CloseHandle (fFileHandle_);
        }
        Execution::DoReThrow ();
    }
#else
    AssertNotImplemented ();
#endif
}

MemoryMappedFileReader::~MemoryMappedFileReader ()
{
#if         qPlatform_POSIX
    int res = ::munmap (const_cast<Byte*> (fFileDataStart_), fFileDataEnd_ - fFileDataStart_);
    // check result!
    AssertNotImplemented ();
#elif       qPlatform_Windows
    if (fFileDataStart_ != nullptr) {
        (void)::UnmapViewOfFile (fFileDataStart_);
    }
    if (fFileMapping_ != INVALID_HANDLE_VALUE) {
        ::CloseHandle (fFileMapping_);
    }
    if (fFileHandle_ != INVALID_HANDLE_VALUE) {
        ::CloseHandle (fFileHandle_);
    }
#else
    AssertNotImplemented ();
#endif
}<commit_msg>More POSIX MemoryMappedFileReader fixes (testable - but not yet tested)<commit_after>/*
 * Copyright(c) Sophist Solutions, Inc. 1990-2013.  All rights reserved
 */
#include    "../../StroikaPreComp.h"

#include    <sys/types.h>
#include    <fcntl.h>

#if     qPlatform_Windows
#include    <windows.h>
#elif   qPlatform_POSIX
#include    <unistd.h>
#include    <sys/mman.h>
#endif

#include    "../../Execution/ErrNoException.h"
#include    "../../Execution/Exceptions.h"
#if     qPlatform_Windows
#include    "../../Execution/Platform/Windows/Exception.h"
#include    "../../Execution/Platform/Windows/HRESULTErrorException.h"
#endif
#include    "../../Debug/Trace.h"
#include    "../../IO/FileAccessException.h"
#include    "../../IO/FileBusyException.h"
#include    "../../IO/FileFormatException.h"
#include    "FileSystem.h"
#include    "PathName.h"

#include    "MemoryMappedFileReader.h"

using   namespace   Stroika::Foundation;
using   namespace   Stroika::Foundation::Characters;
using   namespace   Stroika::Foundation::IO;
using   namespace   Stroika::Foundation::IO::FileSystem;
using   namespace   Stroika::Foundation::Memory;

#if     qPlatform_Windows
using   Execution::Platform::Windows::ThrowIfFalseGetLastError;
#endif

/*
 ********************************************************************************
 ***************************** MemoryMappedFileReader ***************************
 ********************************************************************************
 */
MemoryMappedFileReader::MemoryMappedFileReader (const String& fileName)
    : fFileDataStart_ (nullptr)
    , fFileDataEnd_ (nullptr)
#if         qPlatform_Windows
    , fFileHandle_ (INVALID_HANDLE_VALUE)
    , fFileMapping_ (INVALID_HANDLE_VALUE)
#endif
{
#if         qPlatform_POSIX
    int fd = -1;
    Execution::ThrowErrNoIfNegative (fd = open (fileName.AsNarrowSDKString ().c_str (), O_RDONLY));
    size_t fileLength = IO::FileSystem::FileSystem::Default ().GetFileSize (fileName);//
    //WRONG BUT NOT GROSSLY - @todo fix -- AssertNotImplemented (); // size of file - compute -- must check for overlflow and throw...
    fFileDataStart_ = reinterpret_cast<const Byte*> (::mmap (nullptr, fileLength, PROT_READ, MAP_PRIVATE, fd, 0));
    fFileDataEnd_ = fFileDataStart_ + fileLength;
    ::close (fd);//http://linux.die.net/man/2/mmap says dont need to keep FD open while mmapped
#elif       qPlatform_Windows
    try {
        // FILE_READ_DATA fails on WinME - generates ERROR_INVALID_PARAMETER - so use GENERIC_READ
        fFileHandle_ = ::CreateFile (fileName.AsTString ().c_str (), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
        ThrowIfFalseGetLastError (fFileHandle_ != INVALID_HANDLE_VALUE);
        DWORD   fileSize    =   ::GetFileSize (fFileHandle_, nullptr);
        if (fileSize != 0) {
            fFileMapping_ = ::CreateFileMapping (fFileHandle_, nullptr, PAGE_READONLY, 0, fileSize, 0);
            ThrowIfFalseGetLastError (fFileMapping_ != nullptr);
            fFileDataStart_ = reinterpret_cast<const Byte*> (::MapViewOfFile (fFileMapping_, FILE_MAP_READ, 0, 0, 0));
            ThrowIfFalseGetLastError (fFileDataStart_ != nullptr);
            fFileDataEnd_ = fFileDataStart_ + fileSize;
        }
    }
    catch (...) {
        if (fFileMapping_ != INVALID_HANDLE_VALUE) {
            ::CloseHandle (fFileMapping_);
        }
        if (fFileHandle_ != INVALID_HANDLE_VALUE) {
            ::CloseHandle (fFileHandle_);
        }
        Execution::DoReThrow ();
    }
#else
    AssertNotImplemented ();
#endif
}

MemoryMappedFileReader::~MemoryMappedFileReader ()
{
#if         qPlatform_POSIX
    int res = ::munmap (const_cast<Byte*> (fFileDataStart_), fFileDataEnd_ - fFileDataStart_);
    // check result!
    AssertNotImplemented ();
#elif       qPlatform_Windows
    if (fFileDataStart_ != nullptr) {
        (void)::UnmapViewOfFile (fFileDataStart_);
    }
    if (fFileMapping_ != INVALID_HANDLE_VALUE) {
        ::CloseHandle (fFileMapping_);
    }
    if (fFileHandle_ != INVALID_HANDLE_VALUE) {
        ::CloseHandle (fFileHandle_);
    }
#else
    AssertNotImplemented ();
#endif
}
<|endoftext|>


<commit_before>// Joint work by Rob Mill & J B Coe

#include <iostream>
#include <functional>

class A;
class B;
class C;

class AbstractVisitor
{
public:
  virtual void Visit(A& a) = 0;
  virtual void Visit(B& b) = 0;
  virtual void Visit(C& c) = 0;

protected:
  ~AbstractVisitor() {}
};

class Visitable
{
public:
  virtual void Accept(AbstractVisitor& v) = 0;

protected:
  ~Visitable() {}
};

class A : public Visitable
{
public:
  void Accept(AbstractVisitor& v) override { v.Visit(*this); }
};

class B : public Visitable
{
public:
  void Accept(AbstractVisitor& v) override { v.Visit(*this); }
};

class C : public Visitable
{
public:
  void Accept(AbstractVisitor& v) override { v.Visit(*this); }
};

template <typename T, typename F, typename BaseInner, typename ArgsT>
class AddFunction
{
public:
  class Inner : public BaseInner
  {
  public:
    using BaseInner::Visit;

    Inner(ArgsT&& args) : BaseInner(std::move(args.second)), m_f(std::move(args.first)) {}

    void Visit(T& t) final override { m_f(t); }

  private:
    F m_f;
  };

  AddFunction(ArgsT&& args) : m_args(std::move(args)) {}

  template <typename Tadd, typename Fadd>
  AddFunction<Tadd, Fadd, Inner, std::pair<Fadd, ArgsT>> on(Fadd&& f) &&
  {
    return AddFunction<Tadd, Fadd, Inner, std::pair<Fadd, ArgsT>>(
        std::make_pair(std::move(f), std::move(m_args)));
  }

  Inner end_visitor() && { return Inner(std::move(m_args)); }

  ArgsT m_args;
};

template <typename TVisitorBase>
class EmptyVisitor
{
public:
  class Inner : public TVisitorBase
  {
  public:
    using TVisitorBase::Visit;

    Inner(std::nullptr_t) {}
  };

  template <typename Tadd, typename Fadd>
  AddFunction<Tadd, Fadd, Inner, std::pair<Fadd, std::nullptr_t>> on(Fadd&& f) &&
  {
    return AddFunction<Tadd, Fadd, Inner, std::pair<Fadd, std::nullptr_t>>(
        std::make_pair(std::move(f), nullptr));
  }
};

template <typename TVisitorBase>
EmptyVisitor<TVisitorBase> begin_visitor()
{
  return EmptyVisitor<TVisitorBase>();
}

int main()
{
  A a;
  B b;
  C c;

  int iCounter = 0;

  struct CVisitor : public AbstractVisitor
  {
    CVisitor(int& i) : iCounter_(i) {}

    int& iCounter_;

    void Visit(A& a) override final
    {
      std::cout << "I'm an A" << std::endl;
      iCounter_ += 1;
    }

    void Visit(B& a) override final
    {
      std::cout << "I'm a B" << std::endl;
      iCounter_ += 2;
    }

    void Visit(C& a) override final
    {
      std::cout << "I'm a C" << std::endl;
      iCounter_ += 3;
    }
  } visitor(iCounter);

  /*
  auto visitor = begin_visitor<AbstractVisitor>()
                     .on<A>([&iCounter](A& a)
                            {
                              std::cout << "I'm an A" << std::endl;
                              iCounter += 1;
                            })
                     .on<B>([&iCounter](B& a)
                            {
                              std::cout << "I'm a B" << std::endl;
                              iCounter += 2;
                            })
                     .on<C>([&iCounter](C& c)
                            {
                              std::cout << "I'm a C" << std::endl;
                              iCounter += 3;
                            })
                     .end_visitor();
  */

  a.Accept(visitor);
  b.Accept(visitor);
  c.Accept(visitor);

  std::cout << "Count is " << iCounter << std::endl;

  return 0;
}
<commit_msg>make_visitor works<commit_after>// Joint work by Rob Mill & J B Coe

#include <iostream>
#include <functional>

class A;
class B;
class C;

class AbstractVisitor
{
public:
  virtual void Visit(A& a) = 0;
  virtual void Visit(B& b) = 0;
  virtual void Visit(C& c) = 0;

protected:
  ~AbstractVisitor() {}
};

class Visitable
{
public:
  virtual void Accept(AbstractVisitor& v) = 0;

protected:
  ~Visitable() {}
};

class A : public Visitable
{
public:
  void Accept(AbstractVisitor& v) override { v.Visit(*this); }
};

class B : public Visitable
{
public:
  void Accept(AbstractVisitor& v) override { v.Visit(*this); }
};

class C : public Visitable
{
public:
  void Accept(AbstractVisitor& v) override { v.Visit(*this); }
};

template <typename T, typename F, typename BaseInner, typename ArgsT>
class AddFunction
{
public:
  class Inner : public BaseInner
  {
  public:
    using BaseInner::Visit;

    Inner(ArgsT&& args) : BaseInner(std::move(args.second)), m_f(std::move(args.first)) {}

    void Visit(T& t) final override { m_f(t); }

  private:
    F m_f;
  };

  AddFunction(ArgsT&& args) : m_args(std::move(args)) {}

  template <typename Tadd, typename Fadd>
  AddFunction<Tadd, Fadd, Inner, std::pair<Fadd, ArgsT>> on(Fadd&& f) &&
  {
    return AddFunction<Tadd, Fadd, Inner, std::pair<Fadd, ArgsT>>(
        std::make_pair(std::move(f), std::move(m_args)));
  }

  Inner end_visitor() && { return Inner(std::move(m_args)); }

  ArgsT m_args;
};

template <typename TVisitorBase>
class EmptyVisitor
{
public:
  class Inner : public TVisitorBase
  {
  public:
    using TVisitorBase::Visit;

    Inner(std::nullptr_t) {}
  };

  template <typename Tadd, typename Fadd>
  AddFunction<Tadd, Fadd, Inner, std::pair<Fadd, std::nullptr_t>> on(Fadd&& f) &&
  {
    return AddFunction<Tadd, Fadd, Inner, std::pair<Fadd, std::nullptr_t>>(
        std::make_pair(std::move(f), nullptr));
  }
};

template <typename TVisitorBase>
EmptyVisitor<TVisitorBase> begin_visitor()
{
  return EmptyVisitor<TVisitorBase>();
}

int main()
{
  A a;
  B b;
  C c;

  int iCounter = 0;

#ifdef USE_CLASS

  struct CVisitor : public AbstractVisitor
  {
    CVisitor(int& i) : iCounter_(i) {}

    int& iCounter_;

    void Visit(A& a) override final
    {
      std::cout << "I'm an A" << std::endl;
      iCounter_ += 1;
    }

    void Visit(B& a) override final
    {
      std::cout << "I'm a B" << std::endl;
      iCounter_ += 2;
    }

    void Visit(C& a) override final
    {
      std::cout << "I'm a C" << std::endl;
      iCounter_ += 3;
    }
  } visitor(iCounter);
#else
  auto visitor = begin_visitor<AbstractVisitor>()
                     .on<A>([&iCounter](A& a)
                            {
                              std::cout << "I'm an A" << std::endl;
                              iCounter += 1;
                            })
                     .on<B>([&iCounter](B& a)
                            {
                              std::cout << "I'm a B" << std::endl;
                              iCounter += 2;
                            })
                     .on<C>([&iCounter](C& c)
                            {
                              std::cout << "I'm a C" << std::endl;
                              iCounter += 3;
                            })
                     .end_visitor();

#endif

  a.Accept(visitor);
  b.Accept(visitor);
  c.Accept(visitor);

  std::cout << "Count is " << iCounter << std::endl;

  return 0;
}
<|endoftext|>