<commit_before>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.22 $  $Author: trey $  $Date: 2007-03-05 08:57:08 $

 @file    zmdpSolve.cc
 @brief   No brief

 Copyright (c) 2002-2006, Trey Smith.  All rights reserved.

 Licensed under the Apache License, Version 2.0 (the "License"); you may
 not use this file except in compliance with the License.  You may
 obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 implied.  See the License for the specific language governing
 permissions and limitations under the License.

 ***************************************************************************/

#include <assert.h>
#include <sys/time.h>
#include <getopt.h>
#include <signal.h>

#include <iostream>
#include <fstream>

#include "zmdpCommonTime.h"
#include "solverUtils.h"
#include "zmdpMainConfig.h"

#include "zmdpMainConfig.cc" // embed default config file

using namespace std;
using namespace MatrixUtils;
using namespace zmdp;

bool userTerminatedG = false;

void sigIntHandler(int sig) {
  userTerminatedG = true;

  printf("*** received SIGINT, user pressed control-C ***\n"
	 "terminating run and writing output policy as soon as the solver returns control\n");
  fflush(stdout);
}

void setSignalHandler(int sig, void (*handler)(int)) {
  struct sigaction act;
  memset (&act, 0, sizeof(act));
  act.sa_handler = handler;
  act.sa_flags = SA_RESTART;
  if (-1 == sigaction (sig, &act, NULL)) {
    cerr << "ERROR: unable to set handler for signal "
         << sig << endl;
    exit(EXIT_FAILURE);
  }
}

void doSolve(const ZMDPConfig& config, SolverParams& p)
{
  init_matrix_utils();
  StopWatch run;

  printf("%05d reading model file and allocating data structures\n",
	 (int) run.elapsedTime());
  SolverObjects so;
  constructSolverObjects(so, p, config);

  // initialize the solver
  printf("%05d calculating initial heuristics\n",
	 (int) run.elapsedTime());
  so.solver->planInit(so.sim->getModel(), &config);
  printf("%05d finished initialization, beginning to improve policy\n",
	 (int) run.elapsedTime());

  setSignalHandler(SIGINT, &sigIntHandler);

  double lastPrintTime = -1000;
  bool reachedTargetPrecision = false;
  bool reachedTimeout = false;
  int numSolverCalls = 0;
  while (!(reachedTargetPrecision || reachedTimeout || userTerminatedG)) {
    // make a call to the solver
    reachedTargetPrecision =
      so.solver->planFixedTime(so.sim->getModel()->getInitialState(),
			       /* maxTime = */ -1, p.terminateRegretBound);
    numSolverCalls++;

    // check timeout
    double elapsed = run.elapsedTime();
    if (elapsed >= p.terminateWallclockSeconds) {
      reachedTimeout = true;
    }

    // print a progress update every 10 seconds
    if ((elapsed - lastPrintTime > 10)
	|| reachedTargetPrecision || reachedTimeout || userTerminatedG) {
      ValueInterval intv = so.solver->getValueAt(so.sim->getModel()->getInitialState());
      printf("%05d %6d calls to solver, bounds [%8.4f .. %8.4f], regret <= %g\n",
	     (int) elapsed, numSolverCalls, intv.l, intv.u, (intv.u - intv.l));
      lastPrintTime = elapsed;
    }
  }

  // say why the run ended
  if (reachedTargetPrecision) {
    printf("%05d terminating run; reached target regret bound of %g\n",
	   (int) run.elapsedTime(), p.terminateRegretBound);
  } else if (reachedTimeout) {
    printf("%05d terminating run; passed specified timeout of %g seconds\n",
	   (int) run.elapsedTime(), p.terminateWallclockSeconds);
  } else {
    printf("%05d terminating run; caught SIGINT from user\n",
	   (int) run.elapsedTime());
  }

  // write out a policy
  if (NULL == p.policyOutputFile) {
    printf("%05d (not outputting policy)\n", (int) run.elapsedTime());
  } else {
    printf("%05d writing policy to '%s'\n", (int) run.elapsedTime(), p.policyOutputFile);
    so.bounds->writePolicy(p.policyOutputFile);
  }

  // finish up logging (if any, according to params specified in the config file)
  printf("%05d finishing logging (e.g., writing qValuesOutputFile if it was requested)\n",
	 (int) run.elapsedTime());
  so.solver->finishLogging();

  printf("%05d done\n", (int) run.elapsedTime());
}

void usage(const char* cmdName) {
  cerr <<
    "usage: " << cmdName << " OPTIONS <model>\n"
    "  -h or --help           Print this help\n"
    "  --version              Print version information (CFLAGS used at compilation)\n"
    "  -c or --config <file>  Specify a config file to read options from\n"
    "  --genConfig <file>     Generate an example config file and exit\n"
    "\n"
    "  zmdpSolve generates an output policy for a search strategy and\n"
    "  problem you select.  It runs the search strategy in an anytime\n"
    "  fashion, periodically printing bounds on the value of the initial\n"
    "  state to console so that you can monitor progress.  When the run ends,\n"
    "  the final policy is output to the file you specify.  There are several\n"
    "  options for how to end the run: you can specify a desired regret bound\n"
    "  for the output solution, specify a fixed timeout, or just use ctrl-C to\n"
    "  interrupt the algorithm when you are satisfied (it will output the final\n"
    "  policy before exiting).\n"
    "\n"
    "  ZMDP gets configuration information from three places: (1) Default values\n"
    "  are embedded in the binary at compile time.  (2) If you specify a config file\n"
    "  using the --config option, any fields set in that file override the defaults.\n"
    "  (3) You can override individual fields in the config file from the command line.\n"
    "  For instance, if the config file specifies 'searchStrategy frtdp' you can override\n"
    "  with '--searchStrategy hsvi' at the command line.\n"
    "\n"
    "  To generate an example config file (including default values and comments describing\n"
    "  all the parameters), use the '--genConfig <file>' option.\n"
    "\n"
    "For convenience, there are also some abbreviations:\n"
    "  -s = --searchStrategy\n"
    "  -t = --modelType\n"
    "  -o = --policyOutputFile\n"
    "  -f = --useFastModelParser 1\n"
    "  -p = --terminateRegretBound\n"
    "\n"
    "Examples:\n"
    "  " << cmdName << " RockSample_4_4.pomdp\n"
    "  " << cmdName << " large-b.racetrack\n"
    "  " << cmdName << " -t 60 -o my.policy RockSample_4_4.pomdp\n"
    "  " << cmdName << " -s lrtdp RockSample_4_4.pomdp\n"
    "  " << cmdName << " -f RockSample_5_7.pomdp\n"
    "\n"
;
  exit(-1);
}

int main(int argc, char **argv) {
  SolverParams p;

  ostringstream outs;
  if (zmdpDebugLevelG >= 1) {
    // save arguments for debug printout later
    for (int i=1; i < argc; i++) {
      outs << argv[i] << " ";
    }
  }

  bool argsOnly = false;
  const char* configFileName = NULL;
  ZMDPConfig commandLineConfig;

  p.cmdName = argv[0];
  for (int argi=1; argi < argc; argi++) {
    std::string args = argv[argi];
    if (!argsOnly && '-' == args[0]) {
      if (args == "--") {
	argsOnly = true;
      } else if (args == "-h" || args == "--help") {
	usage(argv[0]);
      } else if (args == "--version") {
	cout << "CFLAGS = " << CFLAGS << endl;
	exit(EXIT_SUCCESS);
      } else if (args == "-c" || args == "--config") {
	if (++argi == argc) {
	  fprintf(stderr, "ERROR: found -c option without argument (use -h for help)\n");
	  exit(EXIT_FAILURE);
	}
	configFileName = argv[argi];
      } else if (args == "--genConfig") {
	if (++argi == argc) {
	  fprintf(stderr, "ERROR: found --genConfig option without argument (use -h for help)\n");
	  exit(EXIT_FAILURE);
	}
	embedWriteToFile(argv[argi], defaultConfig);
	printf("wrote config file with default settings to %s\n", argv[argi]);
	exit(EXIT_SUCCESS);
      } else {
	// replace abbreviations
	if (args == "-s") {
	  args = "--searchStrategy";
	} else if (args == "-t") {
	  args = "--modelType";
	} else if (args == "-f") {
	  commandLineConfig.setBool("useFastModelParser", true);
	  continue;
	} else if (args == "-p") {
	  args = "--terminateRegretBound";
	} else if (args == "-o") {
	  args = "--policyOutputFile";
	}

	if (args.find("--") != 0) {
	  fprintf(stderr, "ERROR: found unknown option '%s' (use -h for help)\n",
		  args.c_str());
	  exit(EXIT_FAILURE);
	} else {
	  if (++argi == argc) {
	    fprintf(stderr, "ERROR: found %s option without argument (-h for help)\n",
		    args.c_str());
	    exit(EXIT_FAILURE);
	  }
	  commandLineConfig.setString(args.substr(2), argv[argi]);
	}
      }
    } else {
      if (NULL == p.probName) {
	p.probName = argv[argi];
      } else {
	fprintf(stderr, "ERROR: expected exactly 1 argument (use -h for help)\n");
	exit(EXIT_FAILURE);
      }
    }
  }
  if (NULL == p.probName) {
    fprintf(stderr, "ERROR: expected exactly 1 argument (use -h for help)\n");
    exit(EXIT_FAILURE);
  }

  // config step 1: read defaults embedded in binary
  ZMDPConfig config;
  config.readFromString("<defaultConfig>", defaultConfig.data);

  // config step 2: overwrite defaults with values specified in config file
  // (signal an error if any new unexpected fields are defined)
  config.setOverWriteOnlyMode(true);
  if (NULL != configFileName) {
    config.readFromFile(configFileName);
  }

  // config step 3: overwrite with values specified on command line
  config.readFromConfig(commandLineConfig);

  if (zmdpDebugLevelG >= 1) {
    printf("CFLAGS = %s\n", CFLAGS);
    printf("ARGS = %s\n", outs.str().c_str());
    fflush(stdout);
  }

  p.setValues(config);
  doSolve(config, p);

  return 0;
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.21  2006/11/08 16:36:05  trey
 * renamed useFastPomdpParser to useFastModelParser
 *
 * Revision 1.20  2006/11/07 20:09:23  trey
 * now call finishLogging() in zmdpSolve (not just in zmdpBenchmark)
 *
 * Revision 1.19  2006/10/30 20:22:19  trey
 * removed redundant setting of policyOutputFile; this is now done in solverUtils.cc
 *
 * Revision 1.18  2006/10/30 20:00:15  trey
 * USE_DEBUG_PRINT replaced with a run-time config parameter "debugLevel"
 *
 * Revision 1.17  2006/10/25 02:44:28  trey
 * updated usage summary, removed obsolete -v option
 *
 * Revision 1.16  2006/10/25 02:07:30  trey
 * removed old debug statement
 *
 * Revision 1.15  2006/10/24 19:10:28  trey
 * moved some parameter logic to solverUtils
 *
 * Revision 1.14  2006/10/24 02:11:20  trey
 * changes to match Solver API change
 *
 * Revision 1.13  2006/10/18 18:05:56  trey
 * now propagating config data structure to lower levels so config fields can be used to control more parts of the system
 *
 * Revision 1.12  2006/10/17 19:18:47  trey
 * centralized proper handling of negative terminateWallclockSeconds values
 *
 * Revision 1.11  2006/10/16 17:33:04  trey
 * removed obsolete OutputParams struct
 *
 * Revision 1.10  2006/10/16 05:49:37  trey
 * renamed outputPolicyFile -> policyOutputFile
 *
 * Revision 1.9  2006/10/15 23:46:02  trey
 * switched to new config mechanism
 *
 * Revision 1.8  2006/10/03 03:17:26  trey
 * added --max-horizon parameter
 *
 * Revision 1.7  2006/06/15 16:08:37  trey
 * restructured so zmdpBenchmark can output policies
 *
 * Revision 1.6  2006/06/01 16:49:54  trey
 * moved installation of the SIGINT handler to after initialization, so an interrupt during initialization causes the solver to exit immediately
 *
 * Revision 1.5  2006/04/28 20:33:54  trey
 * added handling of SIGINT
 *
 * Revision 1.4  2006/04/28 17:57:41  trey
 * changed to use apache license
 *
 * Revision 1.3  2006/04/27 23:42:54  trey
 * improved error diagnostic for policy output
 *
 * Revision 1.2  2006/04/27 23:19:14  trey
 * removed unnecessary #include of Interleave.h
 *
 * Revision 1.1  2006/04/27 23:07:24  trey
 * initial check-in
 *
 *
 ***************************************************************************/
<commit_msg>added assertion<commit_after>/********** tell emacs we use -*- c++ -*- style comments *******************
 $Revision: 1.23 $  $Author: trey $  $Date: 2007-03-25 21:40:14 $

 @file    zmdpSolve.cc
 @brief   No brief

 Copyright (c) 2002-2006, Trey Smith.  All rights reserved.

 Licensed under the Apache License, Version 2.0 (the "License"); you may
 not use this file except in compliance with the License.  You may
 obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 implied.  See the License for the specific language governing
 permissions and limitations under the License.

 ***************************************************************************/

#include <assert.h>
#include <sys/time.h>
#include <getopt.h>
#include <signal.h>

#include <iostream>
#include <fstream>

#include "zmdpCommonTime.h"
#include "solverUtils.h"
#include "zmdpMainConfig.h"

#include "zmdpMainConfig.cc" // embed default config file

using namespace std;
using namespace MatrixUtils;
using namespace zmdp;

bool userTerminatedG = false;

void sigIntHandler(int sig) {
  userTerminatedG = true;

  printf("*** received SIGINT, user pressed control-C ***\n"
	 "terminating run and writing output policy as soon as the solver returns control\n");
  fflush(stdout);
}

void setSignalHandler(int sig, void (*handler)(int)) {
  struct sigaction act;
  memset (&act, 0, sizeof(act));
  act.sa_handler = handler;
  act.sa_flags = SA_RESTART;
  if (-1 == sigaction (sig, &act, NULL)) {
    cerr << "ERROR: unable to set handler for signal "
         << sig << endl;
    exit(EXIT_FAILURE);
  }
}

void doSolve(const ZMDPConfig& config, SolverParams& p)
{
  init_matrix_utils();
  StopWatch run;

  printf("%05d reading model file and allocating data structures\n",
	 (int) run.elapsedTime());
  SolverObjects so;
  constructSolverObjects(so, p, config);

  // initialize the solver
  printf("%05d calculating initial heuristics\n",
	 (int) run.elapsedTime());
  so.solver->planInit(so.sim->getModel(), &config);
  printf("%05d finished initialization, beginning to improve policy\n",
	 (int) run.elapsedTime());

  setSignalHandler(SIGINT, &sigIntHandler);

  double lastPrintTime = -1000;
  bool reachedTargetPrecision = false;
  bool reachedTimeout = false;
  int numSolverCalls = 0;
  while (!(reachedTargetPrecision || reachedTimeout || userTerminatedG)) {
    // make a call to the solver
    reachedTargetPrecision =
      so.solver->planFixedTime(so.sim->getModel()->getInitialState(),
			       /* maxTime = */ -1, p.terminateRegretBound);
    numSolverCalls++;

    // check timeout
    double elapsed = run.elapsedTime();
    if (elapsed >= p.terminateWallclockSeconds) {
      reachedTimeout = true;
    }

    // print a progress update every 10 seconds
    if ((elapsed - lastPrintTime > 10)
	|| reachedTargetPrecision || reachedTimeout || userTerminatedG) {
      ValueInterval intv = so.solver->getValueAt(so.sim->getModel()->getInitialState());
      printf("%05d %6d calls to solver, bounds [%8.4f .. %8.4f], regret <= %g\n",
	     (int) elapsed, numSolverCalls, intv.l, intv.u, (intv.u - intv.l));
      lastPrintTime = elapsed;
    }
  }

  // say why the run ended
  if (reachedTargetPrecision) {
    printf("%05d terminating run; reached target regret bound of %g\n",
	   (int) run.elapsedTime(), p.terminateRegretBound);
  } else if (reachedTimeout) {
    printf("%05d terminating run; passed specified timeout of %g seconds\n",
	   (int) run.elapsedTime(), p.terminateWallclockSeconds);
  } else {
    printf("%05d terminating run; caught SIGINT from user\n",
	   (int) run.elapsedTime());
  }

  // write out a policy
  if (NULL == p.policyOutputFile) {
    printf("%05d (not outputting policy)\n", (int) run.elapsedTime());
  } else {
    printf("%05d writing policy to '%s'\n", (int) run.elapsedTime(), p.policyOutputFile);
    assert(so.bounds->lowerBound != NULL);
    so.bounds->writePolicy(p.policyOutputFile);
  }

  // finish up logging (if any, according to params specified in the config file)
  printf("%05d finishing logging (e.g., writing qValuesOutputFile if it was requested)\n",
	 (int) run.elapsedTime());
  so.solver->finishLogging();

  printf("%05d done\n", (int) run.elapsedTime());
}

void usage(const char* cmdName) {
  cerr <<
    "usage: " << cmdName << " OPTIONS <model>\n"
    "  -h or --help           Print this help\n"
    "  --version              Print version information (CFLAGS used at compilation)\n"
    "  -c or --config <file>  Specify a config file to read options from\n"
    "  --genConfig <file>     Generate an example config file and exit\n"
    "\n"
    "  zmdpSolve generates an output policy for a search strategy and\n"
    "  problem you select.  It runs the search strategy in an anytime\n"
    "  fashion, periodically printing bounds on the value of the initial\n"
    "  state to console so that you can monitor progress.  When the run ends,\n"
    "  the final policy is output to the file you specify.  There are several\n"
    "  options for how to end the run: you can specify a desired regret bound\n"
    "  for the output solution, specify a fixed timeout, or just use ctrl-C to\n"
    "  interrupt the algorithm when you are satisfied (it will output the final\n"
    "  policy before exiting).\n"
    "\n"
    "  ZMDP gets configuration information from three places: (1) Default values\n"
    "  are embedded in the binary at compile time.  (2) If you specify a config file\n"
    "  using the --config option, any fields set in that file override the defaults.\n"
    "  (3) You can override individual fields in the config file from the command line.\n"
    "  For instance, if the config file specifies 'searchStrategy frtdp' you can override\n"
    "  with '--searchStrategy hsvi' at the command line.\n"
    "\n"
    "  To generate an example config file (including default values and comments describing\n"
    "  all the parameters), use the '--genConfig <file>' option.\n"
    "\n"
    "For convenience, there are also some abbreviations:\n"
    "  -s = --searchStrategy\n"
    "  -t = --modelType\n"
    "  -o = --policyOutputFile\n"
    "  -f = --useFastModelParser 1\n"
    "  -p = --terminateRegretBound\n"
    "\n"
    "Examples:\n"
    "  " << cmdName << " RockSample_4_4.pomdp\n"
    "  " << cmdName << " large-b.racetrack\n"
    "  " << cmdName << " -t 60 -o my.policy RockSample_4_4.pomdp\n"
    "  " << cmdName << " -s lrtdp RockSample_4_4.pomdp\n"
    "  " << cmdName << " -f RockSample_5_7.pomdp\n"
    "\n"
;
  exit(-1);
}

int main(int argc, char **argv) {
  SolverParams p;

  ostringstream outs;
  if (zmdpDebugLevelG >= 1) {
    // save arguments for debug printout later
    for (int i=1; i < argc; i++) {
      outs << argv[i] << " ";
    }
  }

  bool argsOnly = false;
  const char* configFileName = NULL;
  ZMDPConfig commandLineConfig;

  p.cmdName = argv[0];
  for (int argi=1; argi < argc; argi++) {
    std::string args = argv[argi];
    if (!argsOnly && '-' == args[0]) {
      if (args == "--") {
	argsOnly = true;
      } else if (args == "-h" || args == "--help") {
	usage(argv[0]);
      } else if (args == "--version") {
	cout << "CFLAGS = " << CFLAGS << endl;
	exit(EXIT_SUCCESS);
      } else if (args == "-c" || args == "--config") {
	if (++argi == argc) {
	  fprintf(stderr, "ERROR: found -c option without argument (use -h for help)\n");
	  exit(EXIT_FAILURE);
	}
	configFileName = argv[argi];
      } else if (args == "--genConfig") {
	if (++argi == argc) {
	  fprintf(stderr, "ERROR: found --genConfig option without argument (use -h for help)\n");
	  exit(EXIT_FAILURE);
	}
	embedWriteToFile(argv[argi], defaultConfig);
	printf("wrote config file with default settings to %s\n", argv[argi]);
	exit(EXIT_SUCCESS);
      } else {
	// replace abbreviations
	if (args == "-s") {
	  args = "--searchStrategy";
	} else if (args == "-t") {
	  args = "--modelType";
	} else if (args == "-f") {
	  commandLineConfig.setBool("useFastModelParser", true);
	  continue;
	} else if (args == "-p") {
	  args = "--terminateRegretBound";
	} else if (args == "-o") {
	  args = "--policyOutputFile";
	}

	if (args.find("--") != 0) {
	  fprintf(stderr, "ERROR: found unknown option '%s' (use -h for help)\n",
		  args.c_str());
	  exit(EXIT_FAILURE);
	} else {
	  if (++argi == argc) {
	    fprintf(stderr, "ERROR: found %s option without argument (-h for help)\n",
		    args.c_str());
	    exit(EXIT_FAILURE);
	  }
	  commandLineConfig.setString(args.substr(2), argv[argi]);
	}
      }
    } else {
      if (NULL == p.probName) {
	p.probName = argv[argi];
      } else {
	fprintf(stderr, "ERROR: expected exactly 1 argument (use -h for help)\n");
	exit(EXIT_FAILURE);
      }
    }
  }
  if (NULL == p.probName) {
    fprintf(stderr, "ERROR: expected exactly 1 argument (use -h for help)\n");
    exit(EXIT_FAILURE);
  }

  // config step 1: read defaults embedded in binary
  ZMDPConfig config;
  config.readFromString("<defaultConfig>", defaultConfig.data);

  // config step 2: overwrite defaults with values specified in config file
  // (signal an error if any new unexpected fields are defined)
  config.setOverWriteOnlyMode(true);
  if (NULL != configFileName) {
    config.readFromFile(configFileName);
  }

  // config step 3: overwrite with values specified on command line
  config.readFromConfig(commandLineConfig);

  if (zmdpDebugLevelG >= 1) {
    printf("CFLAGS = %s\n", CFLAGS);
    printf("ARGS = %s\n", outs.str().c_str());
    fflush(stdout);
  }

  p.setValues(config);
  doSolve(config, p);

  return 0;
}

/***************************************************************************
 * REVISION HISTORY:
 * $Log: not supported by cvs2svn $
 * Revision 1.22  2007/03/05 08:57:08  trey
 * modified how zmdpMainConfig is used so binaries built in other directories can embed the default config
 *
 * Revision 1.21  2006/11/08 16:36:05  trey
 * renamed useFastPomdpParser to useFastModelParser
 *
 * Revision 1.20  2006/11/07 20:09:23  trey
 * now call finishLogging() in zmdpSolve (not just in zmdpBenchmark)
 *
 * Revision 1.19  2006/10/30 20:22:19  trey
 * removed redundant setting of policyOutputFile; this is now done in solverUtils.cc
 *
 * Revision 1.18  2006/10/30 20:00:15  trey
 * USE_DEBUG_PRINT replaced with a run-time config parameter "debugLevel"
 *
 * Revision 1.17  2006/10/25 02:44:28  trey
 * updated usage summary, removed obsolete -v option
 *
 * Revision 1.16  2006/10/25 02:07:30  trey
 * removed old debug statement
 *
 * Revision 1.15  2006/10/24 19:10:28  trey
 * moved some parameter logic to solverUtils
 *
 * Revision 1.14  2006/10/24 02:11:20  trey
 * changes to match Solver API change
 *
 * Revision 1.13  2006/10/18 18:05:56  trey
 * now propagating config data structure to lower levels so config fields can be used to control more parts of the system
 *
 * Revision 1.12  2006/10/17 19:18:47  trey
 * centralized proper handling of negative terminateWallclockSeconds values
 *
 * Revision 1.11  2006/10/16 17:33:04  trey
 * removed obsolete OutputParams struct
 *
 * Revision 1.10  2006/10/16 05:49:37  trey
 * renamed outputPolicyFile -> policyOutputFile
 *
 * Revision 1.9  2006/10/15 23:46:02  trey
 * switched to new config mechanism
 *
 * Revision 1.8  2006/10/03 03:17:26  trey
 * added --max-horizon parameter
 *
 * Revision 1.7  2006/06/15 16:08:37  trey
 * restructured so zmdpBenchmark can output policies
 *
 * Revision 1.6  2006/06/01 16:49:54  trey
 * moved installation of the SIGINT handler to after initialization, so an interrupt during initialization causes the solver to exit immediately
 *
 * Revision 1.5  2006/04/28 20:33:54  trey
 * added handling of SIGINT
 *
 * Revision 1.4  2006/04/28 17:57:41  trey
 * changed to use apache license
 *
 * Revision 1.3  2006/04/27 23:42:54  trey
 * improved error diagnostic for policy output
 *
 * Revision 1.2  2006/04/27 23:19:14  trey
 * removed unnecessary #include of Interleave.h
 *
 * Revision 1.1  2006/04/27 23:07:24  trey
 * initial check-in
 *
 *
 ***************************************************************************/
<|endoftext|>


<commit_before>/*
 * concurrent_linked_list_test.cpp
 *
 * Testing the concurrent_linked_list.h implementation.
 *
 */

#include <algorithm>
#include <iostream>

#include <cstdlib>
#include <cilk/cilk.h>

// Included from util directory.
#include <check_sort.h>
#include "concurrent_linked_list.h"

// Function repeated mallocs an array of size 100, increments the pth
// element of that array, and then frees the array.
void repeated_malloc_free(unsigned int n, int p) {
    int z = 0;

    for(unsigned int i = 0; i < n; i++) {
        int* f;
        f = (int*)malloc(sizeof(int)*100);
        f[p]++;
        z += f[p];
        free(f);
    }

    std::cout << "z is " << z << "\n";
}

// Checks whether the list L contains the elements [0, 1, ..R-1].
bool check_list_insert(ConcurrentLinkedList* L, int R) {
    int final_size = 0;
    long long* a = L->get_keys(&final_size);
    std::sort(a, a+final_size);
    check_sort(a, final_size);
    assert(final_size == R);

    delete [] a;
    return true;
}

// Tries to insert elements [0, 1, .. R-1] into the list.
void all_list_insert(ConcurrentLinkedList* L, int R) {

    int statusCounts[OP_LAST];
    for (int i = OP_NULL; i < OP_LAST; i++) {
        statusCounts[i] = 0;
    }

    for (int i = 0; i < R; i++) {
        LOpStatus code = OP_FAILED;
        void* return_val;
        void* i_as_ptr = reinterpret_cast<void*>(std::size_t(i));
        // Retry insert until return code is not OP_FAILED.
        while (code == OP_FAILED) {
            return_val = L->insert_if_absent(i,
                                             i_as_ptr,
                                             &code);
            statusCounts[code]++;
        }
        assert(return_val == i_as_ptr);
    }

    std::cout << "Final code results: ";
    for (int i = OP_NULL; i < OP_LAST; i++) {
        std::cout << "(" << i << ", " << statusCounts[i] << ")   ";
    }
    std::cout << "\n";
}

// Tries to insert n random elements into L, with each element being
// chosen uniformly from the range [0 .. R-1].
void test_list_insert(ConcurrentLinkedList* L, int R, int n) {

    int statusCounts[OP_LAST];
    for (int i = OP_NULL; i < OP_LAST; i++) {
        statusCounts[i] = 0;
    }

    std::cout << "Test list insert: R = " << R << ", n = " << n << "\n";

    for (int i = 0; i < n; i++) {
        int rand_num = rand() % R;
        void* rand_as_ptr = reinterpret_cast<void*>(std::size_t(rand_num));
        LOpStatus code = OP_FAILED;
        void* return_val;

        while (code == OP_FAILED) {
            return_val = L->insert_if_absent(rand_num,
                                             rand_as_ptr,
                                             &code);
            statusCounts[code]++;
        }
        assert(return_val == rand_as_ptr);
    }

    std::cout << "Code results: ";
    for (int i = OP_NULL; i < OP_LAST; i++) {
        std::cout << "(" << i << ", " << statusCounts[i] << ")  ";
    }
    std::cout << "\n";
}

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

     int R = 10000;
     if (argc >= 2) {
       R = atoi(argv[1]);
     }
     std::cout << "Value of R: " << R << "\n";

     ConcurrentLinkedList* L = new ConcurrentLinkedList();
     std::cout << "An empty linked list\n";
     L->print_list();

     for (int i = 0; i < 20; i++) {
       cilk_spawn test_list_insert(L, R, 5*R);
     }
     cilk_sync;

     all_list_insert(L, R);
     check_list_insert(L, R);

     std::cout << "Deleting list: \n";
     delete L;

     std::cout << "Done with delete\n";
     std::cout << "PASSED\n";
     return 0;
}

<commit_msg>Temporary quick hacks to eliminate Clang compiler warnings<commit_after>/*
 * concurrent_linked_list_test.cpp
 *
 * Testing the concurrent_linked_list.h implementation.
 *
 */

#include <algorithm>
#include <iostream>

#include <cstdlib>
#include <cilk/cilk.h>

// Included from util directory.
#include <check_sort.h>
#include "concurrent_linked_list.h"

// Function repeated mallocs an array of size 100, increments the pth
// element of that array, and then frees the array.
void repeated_malloc_free(unsigned int n, int p) {
    int z = 0;

    for(unsigned int i = 0; i < n; i++) {
        int* f;
        f = (int*)malloc(sizeof(int)*100);
        f[p]++;
        z += f[p];
        free(f);
    }

    std::cout << "z is " << z << "\n";
}

// Checks whether the list L contains the elements [0, 1, ..R-1].
bool check_list_insert(ConcurrentLinkedList* L, int R) {
    int final_size = 0;
    long long* a = L->get_keys(&final_size);
    std::sort(a, a+final_size);
    check_sort(a, final_size);
    assert(final_size == R);

    delete [] a;
    return true;
}

// Tries to insert elements [0, 1, .. R-1] into the list.
void all_list_insert(ConcurrentLinkedList* L, int R) {

    int statusCounts[OP_LAST];
    for (int i = OP_NULL; i < OP_LAST; i++) {
        statusCounts[i] = 0;
    }

    for (int i = 0; i < R; i++) {
        LOpStatus code = OP_FAILED;
        void* return_val;
        void* i_as_ptr = reinterpret_cast<void*>(std::size_t(i));
        // Retry insert until return code is not OP_FAILED.
        while (code == OP_FAILED) {
            // Cast the int to a larger type.
            // Yes, we really meant to convert an int into a void*
            return_val = L->insert_if_absent(i,
                                             i_as_ptr,
                                             &code);
            statusCounts[code]++;
        }
        assert(return_val == i_as_ptr);
    }

    std::cout << "Final code results: ";
    for (int i = OP_NULL; i < OP_LAST; i++) {
        std::cout << "(" << i << ", " << statusCounts[i] << ")   ";
    }
    std::cout << "\n";
}

// Tries to insert n random elements into L, with each element being
// chosen uniformly from the range [0 .. R-1].
void test_list_insert(ConcurrentLinkedList* L, int R, int n) {

    int statusCounts[OP_LAST];
    for (int i = OP_NULL; i < OP_LAST; i++) {
        statusCounts[i] = 0;
    }

    std::cout << "Test list insert: R = " << R << ", n = " << n << "\n";

    for (int i = 0; i < n; i++) {
        int rand_num = rand() % R;
        void* rand_as_ptr = reinterpret_cast<void*>(std::size_t(rand_num));
        LOpStatus code = OP_FAILED;
        void* return_val;

        while (code == OP_FAILED) {
            return_val = L->insert_if_absent(rand_num,
                                             rand_as_ptr,
                                             &code);
            statusCounts[code]++;
        }
        assert(return_val == rand_as_ptr);
    }

    std::cout << "Code results: ";
    for (int i = OP_NULL; i < OP_LAST; i++) {
        std::cout << "(" << i << ", " << statusCounts[i] << ")  ";
    }
    std::cout << "\n";
}

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

     int R = 10000;
     if (argc >= 2) {
       R = atoi(argv[1]);
     }
     std::cout << "Value of R: " << R << "\n";

     ConcurrentLinkedList* L = new ConcurrentLinkedList();
     std::cout << "An empty linked list\n";
     L->print_list();

     for (int i = 0; i < 20; i++) {
       cilk_spawn test_list_insert(L, R, 5*R);
     }
     cilk_sync;

     all_list_insert(L, R);
     check_list_insert(L, R);

     std::cout << "Deleting list: \n";
     delete L;

     std::cout << "Done with delete\n";
     std::cout << "PASSED\n";
     return 0;
}

<|endoftext|>


<commit_before>#include "ext_metaobject.h"
#include "util.h"
#include "ext_pointer.h"
#include "rubyclass.h"
#include "signalforwarder.h"
#include <QtCore/QMetaObject>
#include <QtCore/QMetaMethod>
#include <QtCore/QMetaProperty>
#include <QtCore/QMetaEnum>
#include <QtCore/QVariant>
#include <array>
#include <QtCore/QDebug>
#include <QtQml/QQmlEngine>

namespace RubyQml {
namespace Ext {

namespace {

RubyValue idListToArray(const QList<ID> &xs)
{
    return protect([&] {
        auto ary = rb_ary_new();
        for (ID id : xs) {
            rb_ary_push(ary, ID2SYM(id));
        }
        return ary;
    });
}

}

MetaObject::MetaObject(RubyValue self) :
    self(self)
{
    setMetaObject(&QObject::staticMetaObject);
}

RubyValue MetaObject::className() const
{
    return rb_str_new_cstr(mMetaObject->className());
}

RubyValue MetaObject::methodNames() const
{
    return idListToArray(mMethodHash.keys());
}

RubyValue MetaObject::isPublic(RubyValue name) const
{
    auto methods = findMethods(name);
    return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Public);
}
RubyValue MetaObject::isProtected(RubyValue name) const
{
    auto methods = findMethods(name);
    return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Protected);
}
RubyValue MetaObject::isPrivate(RubyValue name) const
{
    auto methods = findMethods(name);
    return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Private);
}
RubyValue MetaObject::isSignal(RubyValue name) const
{
    auto methods = findMethods(name);
    return RubyValue::from(mMetaObject->method(methods.first()).methodType() == QMetaMethod::Signal);
}

class MethodInvoker
{
public:
    MethodInvoker(RubyValue args, const QMetaMethod &method) :
        mArgs(args), mMethod(method) {}

    bool isArgsCompatible() const
    {
        int count = RARRAY_LEN(VALUE(mArgs));
        if (mMethod.parameterCount() != count) {
            return false;
        }
        for (int i = 0; i < count; ++i) {
            auto metaType = mMethod.parameterType(i);
            RubyValue arg = RARRAY_AREF(VALUE(mArgs), i);
            if (!arg.isConvertibleTo(metaType)) {
                return false;
            }
        }
        return true;
    }

    RubyValue invoke(QObject *obj)
    {
        std::array<QVariant, 10> argVariants;
        std::array<QGenericArgument, 10> args;
        for (int i = 0; i < mMethod.parameterCount(); ++i) {
            auto metaType = mMethod.parameterType(i);
            argVariants[i] = RubyValue(RARRAY_AREF(VALUE(mArgs), i)).toVariant(metaType);
            args[i] = QGenericArgument(QMetaType::typeName(metaType), argVariants[i].data());
        }

        int returnType = mMethod.returnType();
        if (returnType == QMetaType::UnknownType) {
            fail("QML::MethodError", "unknown return metatype");
        }
        bool voidReturning = (returnType == QMetaType::Void);
        QVariant returnValue;
        if (!voidReturning) {
            returnValue = QVariant(returnType, QMetaType::create(returnType));
        }

        bool result;
        withoutGvl([&] {
            result = mMethod.invoke(
                        obj,
                        Qt::DirectConnection,
                        QGenericReturnArgument(QMetaType::typeName(returnType), returnValue.data()),
                        args[0],args[1],args[2],args[3],args[4],
                        args[5],args[6],args[7],args[8],args[9]);
        });

        if (!result) {
            QString error;
            QDebug(&error) << "failed to call method" << mMethod.methodSignature();
            fail("QML::MethodError", error);
        }
        if (voidReturning) {
            return Qnil;
        } else {
            auto ret = RubyValue::from(returnValue);
            static auto objectBaseClass = RubyClass::fromPath("QML::QtObjectBase");
            if (ret.isKindOf(objectBaseClass)) {
                auto pointer = wrapperRubyClass<Pointer>().unwrap(ret.send("pointer"));
                pointer->preferManaged(true);
            }
            return ret;
        }
    }
private:
    RubyValue mArgs;
    QMetaMethod mMethod;
};

RubyValue MetaObject::invokeMethod(RubyValue object, RubyValue methodName, RubyValue args) const
{
    checkThread();

    auto methodIndexes = findMethods(methodName);

    protect([&] {
        args = rb_check_array_type(args);
    });
    auto obj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
    for (int i : methodIndexes) {
        MethodInvoker invoker(args, mMetaObject->method(i));
        if (invoker.isArgsCompatible()) {
            return invoker.invoke(obj);
        }
    }
    protect([&] {
        auto to_class = rb_funcall(ID2SYM(rb_intern("class")), rb_intern("to_proc"), 0);
        auto classes = rb_funcall_with_block(args, rb_intern("map"), 0, nullptr, to_class);
        auto classes_str = rb_funcall(classes, rb_intern("to_s"), 0);

        rb_raise(rb_path2class("QML::MethodError"),
                 "method mismatch (%s with params %s in %s)",
                 mMetaObject->method(methodIndexes.first()).name().data(),
                 StringValueCStr(classes_str),
                 mMetaObject->className());
    });
    return Qnil;
}

RubyValue MetaObject::connectSignal(RubyValue object, RubyValue signalName, RubyValue proc) const
{
    auto id = signalName.toID();
    auto obj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();

    proc = proc.send("to_proc");

    auto methodIndexes = mMethodHash.values(id);
    std::reverse(methodIndexes.begin(), methodIndexes.end());

    for (int i : methodIndexes) {
        auto method = mMetaObject->method(i);
        if (method.methodType() != QMetaMethod::Signal) {
            continue;
        }
        new SignalForwarder(obj, method, proc);
        return Qnil;
    }
    protect([&] {
        rb_raise(rb_path2class("QML::MethodError"),
                 "signal not found (%s in %s)",
                 rb_id2name(id), mMetaObject->className());
    });
    return Qnil;
}

RubyValue MetaObject::propertyNames() const
{
    return idListToArray(mPropertyHash.keys());
}

RubyValue MetaObject::getProperty(RubyValue object, RubyValue name) const
{
    checkThread();

    auto metaProperty = mMetaObject->property(findProperty(name));

    auto qobj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
    QVariant result;
    withoutGvl([&] {
        result = metaProperty.read(qobj);
    });
    return RubyValue::from(result);
}

RubyValue MetaObject::setProperty(RubyValue object, RubyValue name, RubyValue newValue) const
{
    checkThread();

    auto metaProperty = mMetaObject->property(findProperty(name));
    if (!newValue.isConvertibleTo(metaProperty.userType())) {
        protect([&] {
            rb_raise(rb_path2class("QML::PropertyError"),
                     "type mismatch (%s for %s)",
                     rb_obj_classname(newValue), metaProperty.typeName());
        });
    }

    auto qobj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
    auto variant = newValue.to<QVariant>();
    QVariant result;
    withoutGvl([&] {
        metaProperty.write(qobj, variant);
        result = metaProperty.read(qobj);
    });
    return RubyValue::from(result);
}

RubyValue MetaObject::notifySignal(RubyValue name) const
{
    auto metaProperty = mMetaObject->property(findProperty(name));
    auto signal = metaProperty.notifySignal();
    if (signal.isValid()) {
        return ID2SYM(rb_intern(metaProperty.notifySignal().name()));
    }
    else {
        return Qnil;
    }
}

void MetaObject::checkThread() const
{
    if (rb_thread_current() != rb_thread_main()) {
        fail("QML::InvalidThreadError", "Qt object accessed from non-main thread");
    }
}

QList<int> MetaObject::findMethods(RubyValue name) const
{
    auto id = name.toID();
    auto methodIndexes = mMethodHash.values(id);
    if (methodIndexes.size() == 0) {
        protect([&] {
            rb_raise(rb_path2class("QML::MethodError"),
                     "method not found (%s in %s)",
                     rb_id2name(id),
                     mMetaObject->className());
        });
    }
    return methodIndexes;
}

int MetaObject::findProperty(RubyValue name) const
{
    auto id = name.toID();
    if (!mPropertyHash.contains(id)) {
        protect([&] {
            rb_raise(rb_path2class("QML::PropertyError"),
                     "property not found (%s in %s)",
                     rb_id2name(id), mMetaObject->className());
        });
    }
    return mPropertyHash[id];
}

RubyValue MetaObject::enumerators() const
{
    QHash<QByteArray, QHash<QByteArray, int>> enums;

    for (int i = mMetaObject->enumeratorOffset(); i < mMetaObject->enumeratorCount(); ++i) {
        auto metaEnum = mMetaObject->enumerator(i);
        if (metaEnum.isFlag()) {
            continue;
        }

        QHash<QByteArray, int> enumHash;
        for (int j = 0; j < metaEnum.keyCount(); ++j) {
            enumHash[metaEnum.key(j)] = metaEnum.value(j);
        }
        enums[metaEnum.name()] = enumHash;
    }

    return RubyValue::from(enums);
}

RubyValue MetaObject::superClass() const
{
    auto superclass = mMetaObject->superClass();
    if (!superclass) {
        return Qnil;
    }
    return fromMetaObject(superclass);
}

RubyValue MetaObject::isEqual(RubyValue other) const
{
    return RubyValue::from(mMetaObject == wrapperRubyClass<MetaObject>().unwrap(other)->mMetaObject);
}

RubyValue MetaObject::hash() const
{
    return RubyValue::from(reinterpret_cast<size_t>(mMetaObject)).send("hash");
}

void MetaObject::setMetaObject(const QMetaObject *metaObject)
{
    int methodCount = metaObject->methodCount() - metaObject->methodOffset();

    QMultiHash<ID, int> methodHash;
    QHash<ID, int> propertyHash;

    for (int i = 0; i < methodCount; ++i) {

        auto index = i + metaObject->methodOffset();
        auto method = metaObject->method(index);

        if (method.methodType() == QMetaMethod::Constructor) {
            continue;
        }

        methodHash.insert(rb_intern(method.name()), index);
    }

    int propertyCount = metaObject->propertyCount() - metaObject->propertyOffset();

    for (int i = 0; i < propertyCount; ++i) {
        auto index = i + metaObject->propertyOffset();
        auto property = metaObject->property(index);
        propertyHash[rb_intern(property.name())] = index;
    }

    int enumCount = metaObject->enumeratorCount() - metaObject->enumeratorOffset();
    for (int i = 0; i < enumCount; ++i) {
        auto index = i + metaObject->enumeratorOffset();
        auto metaEnum = metaObject->enumerator(index);
        auto typeName = QByteArray(metaObject->className()) + "::" + QByteArray(metaEnum.name());
        auto metaType = QMetaType::type(typeName);
        if (metaType != QMetaType::UnknownType) {
            RubyValue::addEnumeratorMetaType(metaType);
        }
    }

    mMetaObject = metaObject;
    mMethodHash = methodHash;
    mPropertyHash = propertyHash;
}

RubyValue MetaObject::fromMetaObject(const QMetaObject *metaObject)
{
    auto klass = wrapperRubyClass<MetaObject>();
    auto value = klass.newInstance();
    klass.unwrap(value)->setMetaObject(metaObject);
    return value;
}

void MetaObject::defineClass()
{
    WrapperRubyClass<MetaObject> klass(RubyModule::fromPath("QML"), "MetaObject");

    klass.defineMethod("name", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::className));

    klass.defineMethod("method_names", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::methodNames));
    klass.defineMethod("public?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isPublic));
    klass.defineMethod("protected?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isProtected));
    klass.defineMethod("private?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isPrivate));
    klass.defineMethod("signal?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isSignal));

    klass.defineMethod("invoke_method", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::invokeMethod));
    klass.defineMethod("connect_signal", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::connectSignal));

    klass.defineMethod("property_names", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::propertyNames));
    klass.defineMethod("get_property", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::getProperty));
    klass.defineMethod("set_property", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::setProperty));
    klass.defineMethod("notify_signal", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::notifySignal));

    klass.defineMethod("enumerators", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::enumerators));

    klass.defineMethod("super_class", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::superClass));

    klass.defineMethod("==", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isEqual));
    klass.defineMethod("hash", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::hash));

    klass.aliasMethod("eql?", "==");
}

} // namespace Ext
} // namespace RubyQml
<commit_msg>[C++] Fix memory leak in MetaObject::invokeMethod<commit_after>#include "ext_metaobject.h"
#include "util.h"
#include "ext_pointer.h"
#include "rubyclass.h"
#include "signalforwarder.h"
#include <QtCore/QMetaObject>
#include <QtCore/QMetaMethod>
#include <QtCore/QMetaProperty>
#include <QtCore/QMetaEnum>
#include <QtCore/QVariant>
#include <array>
#include <QtCore/QDebug>
#include <QtQml/QQmlEngine>

namespace RubyQml {
namespace Ext {

namespace {

RubyValue idListToArray(const QList<ID> &xs)
{
    return protect([&] {
        auto ary = rb_ary_new();
        for (ID id : xs) {
            rb_ary_push(ary, ID2SYM(id));
        }
        return ary;
    });
}

}

MetaObject::MetaObject(RubyValue self) :
    self(self)
{
    setMetaObject(&QObject::staticMetaObject);
}

RubyValue MetaObject::className() const
{
    return rb_str_new_cstr(mMetaObject->className());
}

RubyValue MetaObject::methodNames() const
{
    return idListToArray(mMethodHash.keys());
}

RubyValue MetaObject::isPublic(RubyValue name) const
{
    auto methods = findMethods(name);
    return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Public);
}
RubyValue MetaObject::isProtected(RubyValue name) const
{
    auto methods = findMethods(name);
    return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Protected);
}
RubyValue MetaObject::isPrivate(RubyValue name) const
{
    auto methods = findMethods(name);
    return RubyValue::from(mMetaObject->method(methods.first()).access() == QMetaMethod::Private);
}
RubyValue MetaObject::isSignal(RubyValue name) const
{
    auto methods = findMethods(name);
    return RubyValue::from(mMetaObject->method(methods.first()).methodType() == QMetaMethod::Signal);
}

class MethodInvoker
{
public:
    MethodInvoker(RubyValue args, const QMetaMethod &method) :
        mArgs(args), mMethod(method) {}

    bool isArgsCompatible() const
    {
        int count = RARRAY_LEN(VALUE(mArgs));
        if (mMethod.parameterCount() != count) {
            return false;
        }
        for (int i = 0; i < count; ++i) {
            auto metaType = mMethod.parameterType(i);
            RubyValue arg = RARRAY_AREF(VALUE(mArgs), i);
            if (!arg.isConvertibleTo(metaType)) {
                return false;
            }
        }
        return true;
    }

    RubyValue invoke(QObject *obj)
    {
        std::array<QVariant, 10> argVariants;
        std::array<QGenericArgument, 10> args;
        for (int i = 0; i < mMethod.parameterCount(); ++i) {
            auto metaType = mMethod.parameterType(i);
            argVariants[i] = RubyValue(RARRAY_AREF(VALUE(mArgs), i)).toVariant(metaType);
            args[i] = QGenericArgument(QMetaType::typeName(metaType), argVariants[i].data());
        }

        int returnType = mMethod.returnType();
        if (returnType == QMetaType::UnknownType) {
            fail("QML::MethodError", "unknown return metatype");
        }
        bool voidReturning = (returnType == QMetaType::Void);
        QVariant returnValue;
        if (!voidReturning) {
            auto data = QMetaType::create(returnType);
            returnValue = QVariant(returnType, data);
            QMetaType::destroy(returnType, data);
        }

        bool result;
        withoutGvl([&] {
            result = mMethod.invoke(
                        obj,
                        Qt::DirectConnection,
                        QGenericReturnArgument(QMetaType::typeName(returnType), returnValue.data()),
                        args[0],args[1],args[2],args[3],args[4],
                        args[5],args[6],args[7],args[8],args[9]);
        });

        if (!result) {
            QString error;
            QDebug(&error) << "failed to call method" << mMethod.methodSignature();
            fail("QML::MethodError", error);
        }
        if (voidReturning) {
            return Qnil;
        } else {
            auto ret = RubyValue::from(returnValue);
            static auto objectBaseClass = RubyClass::fromPath("QML::QtObjectBase");
            if (ret.isKindOf(objectBaseClass)) {
                auto pointer = wrapperRubyClass<Pointer>().unwrap(ret.send("pointer"));
                pointer->preferManaged(true);
            }
            return ret;
        }
    }
private:
    RubyValue mArgs;
    QMetaMethod mMethod;
};

RubyValue MetaObject::invokeMethod(RubyValue object, RubyValue methodName, RubyValue args) const
{
    checkThread();

    auto methodIndexes = findMethods(methodName);

    protect([&] {
        args = rb_check_array_type(args);
    });
    auto obj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
    for (int i : methodIndexes) {
        MethodInvoker invoker(args, mMetaObject->method(i));
        if (invoker.isArgsCompatible()) {
            return invoker.invoke(obj);
        }
    }
    protect([&] {
        auto to_class = rb_funcall(ID2SYM(rb_intern("class")), rb_intern("to_proc"), 0);
        auto classes = rb_funcall_with_block(args, rb_intern("map"), 0, nullptr, to_class);
        auto classes_str = rb_funcall(classes, rb_intern("to_s"), 0);

        rb_raise(rb_path2class("QML::MethodError"),
                 "method mismatch (%s with params %s in %s)",
                 mMetaObject->method(methodIndexes.first()).name().data(),
                 StringValueCStr(classes_str),
                 mMetaObject->className());
    });
    return Qnil;
}

RubyValue MetaObject::connectSignal(RubyValue object, RubyValue signalName, RubyValue proc) const
{
    auto id = signalName.toID();
    auto obj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();

    proc = proc.send("to_proc");

    auto methodIndexes = mMethodHash.values(id);
    std::reverse(methodIndexes.begin(), methodIndexes.end());

    for (int i : methodIndexes) {
        auto method = mMetaObject->method(i);
        if (method.methodType() != QMetaMethod::Signal) {
            continue;
        }
        new SignalForwarder(obj, method, proc);
        return Qnil;
    }
    protect([&] {
        rb_raise(rb_path2class("QML::MethodError"),
                 "signal not found (%s in %s)",
                 rb_id2name(id), mMetaObject->className());
    });
    return Qnil;
}

RubyValue MetaObject::propertyNames() const
{
    return idListToArray(mPropertyHash.keys());
}

RubyValue MetaObject::getProperty(RubyValue object, RubyValue name) const
{
    checkThread();

    auto metaProperty = mMetaObject->property(findProperty(name));

    auto qobj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
    QVariant result;
    withoutGvl([&] {
        result = metaProperty.read(qobj);
    });
    return RubyValue::from(result);
}

RubyValue MetaObject::setProperty(RubyValue object, RubyValue name, RubyValue newValue) const
{
    checkThread();

    auto metaProperty = mMetaObject->property(findProperty(name));
    if (!newValue.isConvertibleTo(metaProperty.userType())) {
        protect([&] {
            rb_raise(rb_path2class("QML::PropertyError"),
                     "type mismatch (%s for %s)",
                     rb_obj_classname(newValue), metaProperty.typeName());
        });
    }

    auto qobj = wrapperRubyClass<Pointer>().unwrap(object)->fetchQObject();
    auto variant = newValue.to<QVariant>();
    QVariant result;
    withoutGvl([&] {
        metaProperty.write(qobj, variant);
        result = metaProperty.read(qobj);
    });
    return RubyValue::from(result);
}

RubyValue MetaObject::notifySignal(RubyValue name) const
{
    auto metaProperty = mMetaObject->property(findProperty(name));
    auto signal = metaProperty.notifySignal();
    if (signal.isValid()) {
        return ID2SYM(rb_intern(metaProperty.notifySignal().name()));
    }
    else {
        return Qnil;
    }
}

void MetaObject::checkThread() const
{
    if (rb_thread_current() != rb_thread_main()) {
        fail("QML::InvalidThreadError", "Qt object accessed from non-main thread");
    }
}

QList<int> MetaObject::findMethods(RubyValue name) const
{
    auto id = name.toID();
    auto methodIndexes = mMethodHash.values(id);
    if (methodIndexes.size() == 0) {
        protect([&] {
            rb_raise(rb_path2class("QML::MethodError"),
                     "method not found (%s in %s)",
                     rb_id2name(id),
                     mMetaObject->className());
        });
    }
    return methodIndexes;
}

int MetaObject::findProperty(RubyValue name) const
{
    auto id = name.toID();
    if (!mPropertyHash.contains(id)) {
        protect([&] {
            rb_raise(rb_path2class("QML::PropertyError"),
                     "property not found (%s in %s)",
                     rb_id2name(id), mMetaObject->className());
        });
    }
    return mPropertyHash[id];
}

RubyValue MetaObject::enumerators() const
{
    QHash<QByteArray, QHash<QByteArray, int>> enums;

    for (int i = mMetaObject->enumeratorOffset(); i < mMetaObject->enumeratorCount(); ++i) {
        auto metaEnum = mMetaObject->enumerator(i);
        if (metaEnum.isFlag()) {
            continue;
        }

        QHash<QByteArray, int> enumHash;
        for (int j = 0; j < metaEnum.keyCount(); ++j) {
            enumHash[metaEnum.key(j)] = metaEnum.value(j);
        }
        enums[metaEnum.name()] = enumHash;
    }

    return RubyValue::from(enums);
}

RubyValue MetaObject::superClass() const
{
    auto superclass = mMetaObject->superClass();
    if (!superclass) {
        return Qnil;
    }
    return fromMetaObject(superclass);
}

RubyValue MetaObject::isEqual(RubyValue other) const
{
    return RubyValue::from(mMetaObject == wrapperRubyClass<MetaObject>().unwrap(other)->mMetaObject);
}

RubyValue MetaObject::hash() const
{
    return RubyValue::from(reinterpret_cast<size_t>(mMetaObject)).send("hash");
}

void MetaObject::setMetaObject(const QMetaObject *metaObject)
{
    int methodCount = metaObject->methodCount() - metaObject->methodOffset();

    QMultiHash<ID, int> methodHash;
    QHash<ID, int> propertyHash;

    for (int i = 0; i < methodCount; ++i) {

        auto index = i + metaObject->methodOffset();
        auto method = metaObject->method(index);

        if (method.methodType() == QMetaMethod::Constructor) {
            continue;
        }

        methodHash.insert(rb_intern(method.name()), index);
    }

    int propertyCount = metaObject->propertyCount() - metaObject->propertyOffset();

    for (int i = 0; i < propertyCount; ++i) {
        auto index = i + metaObject->propertyOffset();
        auto property = metaObject->property(index);
        propertyHash[rb_intern(property.name())] = index;
    }

    int enumCount = metaObject->enumeratorCount() - metaObject->enumeratorOffset();
    for (int i = 0; i < enumCount; ++i) {
        auto index = i + metaObject->enumeratorOffset();
        auto metaEnum = metaObject->enumerator(index);
        auto typeName = QByteArray(metaObject->className()) + "::" + QByteArray(metaEnum.name());
        auto metaType = QMetaType::type(typeName);
        if (metaType != QMetaType::UnknownType) {
            RubyValue::addEnumeratorMetaType(metaType);
        }
    }

    mMetaObject = metaObject;
    mMethodHash = methodHash;
    mPropertyHash = propertyHash;
}

RubyValue MetaObject::fromMetaObject(const QMetaObject *metaObject)
{
    auto klass = wrapperRubyClass<MetaObject>();
    auto value = klass.newInstance();
    klass.unwrap(value)->setMetaObject(metaObject);
    return value;
}

void MetaObject::defineClass()
{
    WrapperRubyClass<MetaObject> klass(RubyModule::fromPath("QML"), "MetaObject");

    klass.defineMethod("name", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::className));

    klass.defineMethod("method_names", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::methodNames));
    klass.defineMethod("public?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isPublic));
    klass.defineMethod("protected?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isProtected));
    klass.defineMethod("private?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isPrivate));
    klass.defineMethod("signal?", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isSignal));

    klass.defineMethod("invoke_method", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::invokeMethod));
    klass.defineMethod("connect_signal", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::connectSignal));

    klass.defineMethod("property_names", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::propertyNames));
    klass.defineMethod("get_property", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::getProperty));
    klass.defineMethod("set_property", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::setProperty));
    klass.defineMethod("notify_signal", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::notifySignal));

    klass.defineMethod("enumerators", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::enumerators));

    klass.defineMethod("super_class", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::superClass));

    klass.defineMethod("==", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::isEqual));
    klass.defineMethod("hash", RUBYQML_MEMBER_FUNCTION_INFO(&MetaObject::hash));

    klass.aliasMethod("eql?", "==");
}

} // namespace Ext
} // namespace RubyQml
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPCellDataToPointData.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkPCellDataToPointData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkPolyData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkUnstructuredGrid.h"
#include "vtkObjectFactory.h"

vtkCxxRevisionMacro(vtkPCellDataToPointData, "1.8");
vtkStandardNewMacro(vtkPCellDataToPointData);

//----------------------------------------------------------------------------
vtkPCellDataToPointData::vtkPCellDataToPointData()
{
  this->PieceInvariant = 1;
}

//----------------------------------------------------------------------------
int vtkPCellDataToPointData::RequestData(
  vtkInformation* request,
  vtkInformationVector** inputVector ,
  vtkInformationVector* outputVector)
{
  vtkInformation* info = outputVector->GetInformationObject(0);
  vtkDataSet *output = vtkDataSet::SafeDownCast(
    info->Get(vtkDataObject::DATA_OBJECT()));
  if (!output) {return 0;}

  if ( !this->Superclass::RequestData(request, inputVector, outputVector) )
    {
    return 0;
    }

  // Remove the extra (now ivalid) ghost cells.
  // This is only necessary fro unstructured data.
  if (this->PieceInvariant)
    {
    vtkInformation* outInfo = outputVector->GetInformationObject(0);
    int ghostLevel = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS());
    vtkPolyData *pd = vtkPolyData::SafeDownCast(output);
    vtkUnstructuredGrid *ug = vtkUnstructuredGrid::SafeDownCast(output);
    if (pd)
      {
      pd->RemoveGhostCells(ghostLevel+1);
      }
    if (ug)
      {
      ug->RemoveGhostCells(ghostLevel+1);
      }
    }
  return 1;
}

//--------------------------------------------------------------------------
int vtkPCellDataToPointData::RequestUpdateExtent(
  vtkInformation*,
  vtkInformationVector** inputVector,
  vtkInformationVector* outputVector)
{
  if (this->PieceInvariant == 0)
    {
    // I believe the default input update extent
    // is set to the input update extent.
    return 1;
    }

  vtkInformation* opInfo = this->GetOutputPortInformation(0);
  int extentType = opInfo->Get(vtkDataObject::DATA_EXTENT_TYPE());
  vtkInformation* outInfo = outputVector->GetInformationObject(0);
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);

  int piece, numPieces, ghostLevel;
  int* wholeExt;
  int* upExt;
  int ext[6];

  int isInputPiecesExtent = 1;
  if (extentType == VTK_3D_EXTENT &&
      inInfo->Has(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()))
    {
    isInputPiecesExtent = 0;
    }
  if (isInputPiecesExtent)
    {
    piece = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
    numPieces = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
    ghostLevel = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS()) + 1;
    inInfo->Set(
      vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), piece);
    inInfo->Set(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(), numPieces);
    inInfo->Set(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(),
      ghostLevel);
    return 1;
    }
  else
    {
    wholeExt = inInfo->Get(
      vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
    upExt = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT());
    memcpy(ext, upExt, 6*sizeof(int));
    for (int i = 0; i < 3; ++i)
      {
      --ext[i*2];
      if (ext[i*2] < wholeExt[i*2])
        {
        ext[i*2] = wholeExt[i*2];
        }
      ++ext[i*2+1];
      if (ext[i*2+1] > wholeExt[i*2+1])
        {
        ext[i*2+1] = wholeExt[i*2+1];
        }
      }
    inInfo->Set(
      vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), ext, 6);
    return 1;
    }
  return 0;
}

//----------------------------------------------------------------------------
void vtkPCellDataToPointData::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "PieceInvariant: "
     << this->PieceInvariant << "\n";
}
<commit_msg>ENH:Removed warnings<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPCellDataToPointData.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkPCellDataToPointData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkPolyData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkUnstructuredGrid.h"
#include "vtkObjectFactory.h"

vtkCxxRevisionMacro(vtkPCellDataToPointData, "1.9");
vtkStandardNewMacro(vtkPCellDataToPointData);

//----------------------------------------------------------------------------
vtkPCellDataToPointData::vtkPCellDataToPointData()
{
  this->PieceInvariant = 1;
}

//----------------------------------------------------------------------------
int vtkPCellDataToPointData::RequestData(
  vtkInformation* request,
  vtkInformationVector** inputVector ,
  vtkInformationVector* outputVector)
{
  vtkInformation* info = outputVector->GetInformationObject(0);
  vtkDataSet *output = vtkDataSet::SafeDownCast(
    info->Get(vtkDataObject::DATA_OBJECT()));
  if (!output) {return 0;}

  if ( !this->Superclass::RequestData(request, inputVector, outputVector) )
    {
    return 0;
    }

  // Remove the extra (now ivalid) ghost cells.
  // This is only necessary fro unstructured data.
  if (this->PieceInvariant)
    {
    vtkInformation* outInfo = outputVector->GetInformationObject(0);
    int ghostLevel = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS());
    vtkPolyData *pd = vtkPolyData::SafeDownCast(output);
    vtkUnstructuredGrid *ug = vtkUnstructuredGrid::SafeDownCast(output);
    if (pd)
      {
      pd->RemoveGhostCells(ghostLevel+1);
      }
    if (ug)
      {
      ug->RemoveGhostCells(ghostLevel+1);
      }
    }
  return 1;
}

//--------------------------------------------------------------------------
int vtkPCellDataToPointData::RequestUpdateExtent(
  vtkInformation*,
  vtkInformationVector** inputVector,
  vtkInformationVector* outputVector)
{
  if (this->PieceInvariant == 0)
    {
    // I believe the default input update extent
    // is set to the input update extent.
    return 1;
    }

  vtkInformation* opInfo = this->GetOutputPortInformation(0);
  int extentType = opInfo->Get(vtkDataObject::DATA_EXTENT_TYPE());
  vtkInformation* outInfo = outputVector->GetInformationObject(0);
  vtkInformation* inInfo = inputVector[0]->GetInformationObject(0);

  int piece, numPieces, ghostLevel;
  int* wholeExt;
  int* upExt;
  int ext[6];

  int isInputPiecesExtent = 1;
  if (extentType == VTK_3D_EXTENT &&
      inInfo->Has(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT()))
    {
    isInputPiecesExtent = 0;
    }
  if (isInputPiecesExtent)
    {
    piece = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER());
    numPieces = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES());
    ghostLevel = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS()) + 1;
    inInfo->Set(
      vtkStreamingDemandDrivenPipeline::UPDATE_PIECE_NUMBER(), piece);
    inInfo->Set(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_PIECES(), numPieces);
    inInfo->Set(
      vtkStreamingDemandDrivenPipeline::UPDATE_NUMBER_OF_GHOST_LEVELS(),
      ghostLevel);
    }
  else
    {
    wholeExt = inInfo->Get(
      vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
    upExt = outInfo->Get(
      vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT());
    memcpy(ext, upExt, 6*sizeof(int));
    for (int i = 0; i < 3; ++i)
      {
      --ext[i*2];
      if (ext[i*2] < wholeExt[i*2])
        {
        ext[i*2] = wholeExt[i*2];
        }
      ++ext[i*2+1];
      if (ext[i*2+1] > wholeExt[i*2+1])
        {
        ext[i*2+1] = wholeExt[i*2+1];
        }
      }
    inInfo->Set(
      vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), ext, 6);
    }
  return 1;
}

//----------------------------------------------------------------------------
void vtkPCellDataToPointData::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "PieceInvariant: "
     << this->PieceInvariant << "\n";
}
<|endoftext|>


<commit_before>#include <zmq.hpp>
#include <msgpack.hpp>
#include <string>
#include <iostream>

#include "testTypes.hpp"

int main()
{
	//  Prepare our context and socket
	zmq::context_t context(1);
	zmq::socket_t socket(context, ZMQ_REQ);

	std::cout << "Connecting to hello world server" << std::endl;
	socket.connect("tcp://localhost:8080");

	//  Do 10 requests, waiting each time for a response
	for (int request_nbr = 0; request_nbr != 10; request_nbr++) {

		// create my class
		TestClass test(request_nbr, "TEST");

		// pack a message up
		msgpack::sbuffer sbuf;
		msgpack::pack(sbuf, test);

		zmq::message_t request(sbuf.size());
		std::memcpy(request.data(), sbuf.data(), sbuf.size());

		std::cout << "Sending Hello " << request_nbr << "" << std::endl;
		socket.send(request);

		//  Get the reply.
		zmq::message_t reply;
		socket.recv(&reply);
		std::cout << "Received World " << request_nbr << std::endl;
	}
	return 0;
}<commit_msg>use i because its better<commit_after>#include <zmq.hpp>
#include <msgpack.hpp>
#include <string>
#include <iostream>

#include "testTypes.hpp"

int main()
{
	//  Prepare our context and socket
	zmq::context_t context(1);
	zmq::socket_t socket(context, ZMQ_REQ);

	std::cout << "Connecting to hello world server" << std::endl;
	socket.connect("tcp://localhost:8080");

	//  Do 10 requests, waiting each time for a response
	for (int i = 0; i < 10; i++) {

		// create my class
		TestClass test(i, "TEST");

		// pack a message up
		msgpack::sbuffer sbuf;
		msgpack::pack(sbuf, test);

		zmq::message_t request(sbuf.size());
		std::memcpy(request.data(), sbuf.data(), sbuf.size());

		std::cout << "Sending Hello " << i << "" << std::endl;
		socket.send(request);

		//  Get the reply.
		zmq::message_t reply;
		socket.recv(&reply);
		std::cout << "Received World " << i << std::endl;
	}
	return 0;
}<|endoftext|>


<commit_before>/*

Copyright (c) 2017 Maksim Galkin

This file is subject to the terms and conditions of the MIT license,
distributed with this code package in the LICENSE file, also available at:
https://github.com/yacoder/allo-cpp/blob/master/LICENSE

*/

#define CATCH_CONFIG_MAIN
#include "catch.hpp"

#include "allo.hpp"

#include <array>
#include <cstdint>
#include <string>
#include <vector>

TEST_CASE("Counting strategy can count", "[counting-strategy]")
{
   allo::strategies::counting_strategy counting_strategy;

   REQUIRE(counting_strategy.number_of_allocations() == 0);
   REQUIRE(counting_strategy.number_of_deallocations() == 0);

   REQUIRE(counting_strategy.allocate<void>(100) == nullptr);

   REQUIRE(counting_strategy.number_of_allocations() == 1);
   REQUIRE(counting_strategy.number_of_deallocations() == 0);

   REQUIRE(counting_strategy.deallocate<void>(&counting_strategy, 1) == false);

   REQUIRE(counting_strategy.number_of_allocations() == 1);
   REQUIRE(counting_strategy.number_of_deallocations() == 1);
}

TEST_CASE("Never-look-back strategy allocates until out of buffer", "[never-look-back-strategy]")
{
   std::array<uint8_t, 3> buffer{};
   auto* buffer_begin = buffer.data();
   auto* buffer_end = buffer.data() + buffer.size();
   allo::strategies::never_look_back_strategy never_look_back_strategy(buffer_begin, buffer_end);

   auto* first_byte = never_look_back_strategy.allocate<uint8_t>(1);
   REQUIRE(first_byte == buffer.data());

   auto* too_large = never_look_back_strategy.allocate<uint8_t>(3);
   REQUIRE(too_large == nullptr);

   auto* last_two_bytes = never_look_back_strategy.allocate<uint8_t>(2);
   REQUIRE(last_two_bytes == buffer.data() + 1);

   // should run out of buffer by now
   REQUIRE(never_look_back_strategy.allocate<uint8_t>(1) == nullptr);

   // strategy must not recognize other pointers
   REQUIRE(never_look_back_strategy.deallocate<uint8_t>(nullptr, 1) == false);
   REQUIRE(never_look_back_strategy.deallocate<uint8_t>(buffer_end, 1) == false);

   // strategy must recognize the pointers it allocated
   REQUIRE(never_look_back_strategy.deallocate<uint8_t>(first_byte, 1) == true);
   REQUIRE(never_look_back_strategy.deallocate<uint8_t>(last_two_bytes, 2) == true);

   // even after we have deallocated everything, we can't allocate again fom it
   REQUIRE(never_look_back_strategy.allocate<uint8_t>(1) == nullptr);
}

TEST_CASE("Counting allocator works with standard vector", "[counting-allocator][counting-strategy][vector]")
{
   using allocator_t = allo::counting_allocator<int>;
   allo::strategies::counting_strategy counting_strategy;

#if !defined(_ITERATOR_DEBUG_LEVEL) || _ITERATOR_DEBUG_LEVEL == 0
   const int expect_allocations = 1;
#else
   // MS STL allocates some proxy conatiner stuff
   const int expect_allocations = 2;
#endif

   {
      allocator_t allocator(counting_strategy);
      std::vector<int, allocator_t> v(allocator);
      v.push_back(1);
      REQUIRE(counting_strategy.number_of_allocations() == expect_allocations);
      REQUIRE(counting_strategy.number_of_deallocations() == 0);
   }

   REQUIRE(counting_strategy.number_of_allocations() == expect_allocations);
   REQUIRE(counting_strategy.number_of_deallocations() == expect_allocations);
}

TEST_CASE("Never-look-back strategy works with standard vector",
          "[never-look-back-strategy][never-look-back-allocator][vector]")
{
   const int N = 2048;
   std::array<uint8_t, N> buffer{};
   auto* buffer_begin = buffer.data();
   auto* buffer_end = buffer.data() + buffer.size();

   allo::strategies::never_look_back_strategy never_look_back_strategy(buffer_begin, buffer_end);

   // We will use counting allocator to make sure no allocations fall through to std allocator.
   allo::strategies::counting_strategy counting_strategy;

   {
      allo::counting_allocator<uint16_t> counting_allocator(counting_strategy);
      allo::never_look_back_allocator<uint16_t, decltype(counting_allocator)> allocator(never_look_back_strategy,
                                                                                counting_allocator);
      std::vector<uint16_t, decltype(allocator)> v(allocator);
      v.reserve(100);
      v.push_back(42);

      REQUIRE(v[0] == 42);

#if !defined(_ITERATOR_DEBUG_LEVEL) || _ITERATOR_DEBUG_LEVEL == 0
      REQUIRE(*reinterpret_cast<uint16_t*>(&buffer[0]) == 42);
#else
// (MSVC) debug vector allocates extra stuff, we won't test it.
#endif

      REQUIRE(counting_strategy.number_of_allocations() == 0);
      REQUIRE(counting_strategy.number_of_deallocations() == 0);
   }

   REQUIRE(counting_strategy.number_of_allocations() == 0);
   REQUIRE(counting_strategy.number_of_deallocations() == 0);
}

TEST_CASE("Wrapped map works as a map", "[wrapped-map]")
{
   allo::containers::wrapped_map<uint16_t, std::string> wrapped_map(1000);
   auto& m = wrapped_map.unwrap();

   m[42] = "hello, world!";

   REQUIRE(m.size() == 1);
   REQUIRE(m[42] == "hello, world!");
}

TEST_CASE("Wrapped map must not abandon memory if it had to allocate outside private pool", "[wrapped-map][abandon]")
{
   // We will use counting allocator to make sure no allocations fall through to std allocator.
   allo::strategies::counting_strategy counting_strategy;

   {
      allo::counting_allocator<uint16_t> counting_allocator(counting_strategy);

      // Create with empty private pool.
      // So in the end we must get as many deallocations as allocations.
      allo::containers::wrapped_map<uint16_t, uint16_t, allo::counting_allocator> wrapped_map(0, counting_allocator);
      auto& map = wrapped_map.unwrap();

      static_assert(allo::is_abandonable<std::remove_reference<decltype(map)>::type>::type::value,
                    "map must be abandonable in this test");

      map[42] = 1984;

      REQUIRE(map.size() == 1);
      REQUIRE(map[42] == 1984);
   }

   REQUIRE(counting_strategy.number_of_allocations() > 0);
   REQUIRE(counting_strategy.number_of_deallocations() == counting_strategy.number_of_allocations());
}

TEST_CASE("Wrapped map must not abandon memory if one of the types is not trivially destructible",
          "[wrapped-map][abandon]")
{
   uint16_t destructors_called = 0;
   struct non_trivial
   {
      non_trivial(uint16_t& destructors_called)
          : m_destructors_called(destructors_called)
      {
      }

      ~non_trivial() { ++m_destructors_called; }

      uint16_t& m_destructors_called;
   };

   {
      allo::containers::wrapped_map<uint16_t, non_trivial> wrapped_map(1024);
      auto& map = wrapped_map.unwrap();

      static_assert(!allo::is_abandonable<std::remove_reference<decltype(map)>::type>::type::value,
                    "map must be not abandonable in this test");

      map.emplace(42, destructors_called);

      REQUIRE(destructors_called == 0);
      REQUIRE(map.size() == 1);
   }

   REQUIRE(destructors_called == 1);
}

namespace
{

struct non_trivial_but_abandonable
{
   non_trivial_but_abandonable(uint16_t& destructors_called)
       : m_destructors_called(destructors_called)
   {
   }

   ~non_trivial_but_abandonable() { ++m_destructors_called; }

   uint16_t& m_destructors_called;
};

} // end of unnamed namespace

template<>
struct allo::is_abandonable<non_trivial_but_abandonable> : std::true_type
{
};

TEST_CASE("Wrapped map must abandon memory if the types are abandonable and all allocations were private",
          "[wrapped-map][abandon]")
{
   uint16_t destructors_called = 0;

   {
      allo::containers::wrapped_map<uint16_t, non_trivial_but_abandonable> wrapped_map(1024);
      auto& map = wrapped_map.unwrap();

      static_assert(allo::is_abandonable<std::remove_reference<decltype(map)>::type>::type::value,
                    "map must be abandonable in this test");

      map.emplace(42, destructors_called);

      REQUIRE(destructors_called == 0);
      REQUIRE(map.size() == 1);
   }

   REQUIRE(destructors_called == 0);
}
<commit_msg>more adjustments for travis-ci builds<commit_after>/*

Copyright (c) 2017 Maksim Galkin

This file is subject to the terms and conditions of the MIT license,
distributed with this code package in the LICENSE file, also available at:
https://github.com/yacoder/allo-cpp/blob/master/LICENSE

*/

#define CATCH_CONFIG_MAIN
#include "catch.hpp"

#include "allo.hpp"

#include <array>
#include <cstdint>
#include <string>
#include <vector>

TEST_CASE("Counting strategy can count", "[counting-strategy]")
{
   allo::strategies::counting_strategy counting_strategy;

   REQUIRE(counting_strategy.number_of_allocations() == 0);
   REQUIRE(counting_strategy.number_of_deallocations() == 0);

   REQUIRE(counting_strategy.allocate<void>(100) == nullptr);

   REQUIRE(counting_strategy.number_of_allocations() == 1);
   REQUIRE(counting_strategy.number_of_deallocations() == 0);

   REQUIRE(counting_strategy.deallocate<void>(&counting_strategy, 1) == false);

   REQUIRE(counting_strategy.number_of_allocations() == 1);
   REQUIRE(counting_strategy.number_of_deallocations() == 1);
}

TEST_CASE("Never-look-back strategy allocates until out of buffer", "[never-look-back-strategy]")
{
   std::array<uint8_t, 3> buffer{};
   auto* buffer_begin = buffer.data();
   auto* buffer_end = buffer.data() + buffer.size();
   allo::strategies::never_look_back_strategy never_look_back_strategy(buffer_begin, buffer_end);

   auto* first_byte = never_look_back_strategy.allocate<uint8_t>(1);
   REQUIRE(first_byte == buffer.data());

   auto* too_large = never_look_back_strategy.allocate<uint8_t>(3);
   REQUIRE(too_large == nullptr);

   auto* last_two_bytes = never_look_back_strategy.allocate<uint8_t>(2);
   REQUIRE(last_two_bytes == buffer.data() + 1);

   // should run out of buffer by now
   REQUIRE(never_look_back_strategy.allocate<uint8_t>(1) == nullptr);

   // strategy must not recognize other pointers
   REQUIRE(never_look_back_strategy.deallocate<uint8_t>(nullptr, 1) == false);
   REQUIRE(never_look_back_strategy.deallocate<uint8_t>(buffer_end, 1) == false);

   // strategy must recognize the pointers it allocated
   REQUIRE(never_look_back_strategy.deallocate<uint8_t>(first_byte, 1) == true);
   REQUIRE(never_look_back_strategy.deallocate<uint8_t>(last_two_bytes, 2) == true);

   // even after we have deallocated everything, we can't allocate again fom it
   REQUIRE(never_look_back_strategy.allocate<uint8_t>(1) == nullptr);
}

TEST_CASE("Counting allocator works with standard vector", "[counting-allocator][counting-strategy][vector]")
{
   using allocator_t = allo::counting_allocator<int>;
   allo::strategies::counting_strategy counting_strategy;

#if !defined(_ITERATOR_DEBUG_LEVEL) || _ITERATOR_DEBUG_LEVEL == 0
   const int expect_allocations = 1;
#else
   // MS STL allocates some proxy conatiner stuff
   const int expect_allocations = 2;
#endif

   {
      allocator_t allocator(counting_strategy);
      std::vector<int, allocator_t> v(allocator);
      v.push_back(1);
      REQUIRE(counting_strategy.number_of_allocations() == expect_allocations);
      REQUIRE(counting_strategy.number_of_deallocations() == 0);
   }

   REQUIRE(counting_strategy.number_of_allocations() == expect_allocations);
   REQUIRE(counting_strategy.number_of_deallocations() == expect_allocations);
}

TEST_CASE("Never-look-back strategy works with standard vector",
          "[never-look-back-strategy][never-look-back-allocator][vector]")
{
   const int N = 2048;
   std::array<uint8_t, N> buffer{};
   auto* buffer_begin = buffer.data();
   auto* buffer_end = buffer.data() + buffer.size();

   allo::strategies::never_look_back_strategy never_look_back_strategy(buffer_begin, buffer_end);

   // We will use counting allocator to make sure no allocations fall through to std allocator.
   allo::strategies::counting_strategy counting_strategy;

   {
      allo::counting_allocator<uint16_t> counting_allocator(counting_strategy);
      allo::never_look_back_allocator<uint16_t, decltype(counting_allocator)> allocator(never_look_back_strategy,
                                                                                counting_allocator);
      std::vector<uint16_t, decltype(allocator)> v(allocator);
      v.reserve(100);
      v.push_back(42);

      REQUIRE(v[0] == 42);

#if !defined(_ITERATOR_DEBUG_LEVEL) || _ITERATOR_DEBUG_LEVEL == 0
      REQUIRE(*reinterpret_cast<uint16_t*>(&buffer[0]) == 42);
#else
// (MSVC) debug vector allocates extra stuff, we won't test it.
#endif

      REQUIRE(counting_strategy.number_of_allocations() == 0);
      REQUIRE(counting_strategy.number_of_deallocations() == 0);
   }

   REQUIRE(counting_strategy.number_of_allocations() == 0);
   REQUIRE(counting_strategy.number_of_deallocations() == 0);
}

TEST_CASE("Wrapped map works as a map", "[wrapped-map]")
{
   allo::containers::wrapped_map<uint16_t, std::string> wrapped_map(1000);
   auto& m = wrapped_map.unwrap();

   m[42] = "hello, world!";

   REQUIRE(m.size() == 1);
   REQUIRE(m[42] == "hello, world!");
}

TEST_CASE("Wrapped map must not abandon memory if it had to allocate outside private pool", "[wrapped-map][abandon]")
{
   // We will use counting allocator to make sure no allocations fall through to std allocator.
   allo::strategies::counting_strategy counting_strategy;

   {
      allo::counting_allocator<uint16_t> counting_allocator(counting_strategy);

      // Create with empty private pool.
      // So in the end we must get as many deallocations as allocations.
      allo::containers::wrapped_map<uint16_t, uint16_t, allo::counting_allocator> wrapped_map(0, counting_allocator);
      auto& map = wrapped_map.unwrap();

      static_assert(allo::is_abandonable<std::remove_reference<decltype(map)>::type>::type::value,
                    "map must be abandonable in this test");

      map[42] = 1984;

      REQUIRE(map.size() == 1);
      REQUIRE(map[42] == 1984);
   }

   REQUIRE(counting_strategy.number_of_allocations() > 0);
   REQUIRE(counting_strategy.number_of_deallocations() == counting_strategy.number_of_allocations());
}

TEST_CASE("Wrapped map must not abandon memory if one of the types is not trivially destructible",
          "[wrapped-map][abandon]")
{
   uint16_t destructors_called = 0;
   struct non_trivial
   {
      non_trivial(uint16_t& destructors_called)
          : m_destructors_called(destructors_called)
      {
      }

      ~non_trivial() { ++m_destructors_called; }

      uint16_t& m_destructors_called;
   };

   {
      allo::containers::wrapped_map<uint16_t, non_trivial> wrapped_map(1024);
      auto& map = wrapped_map.unwrap();

      static_assert(!allo::is_abandonable<std::remove_reference<decltype(map)>::type>::type::value,
                    "map must be not abandonable in this test");

      map.emplace(42, destructors_called);

      REQUIRE(destructors_called == 0);
      REQUIRE(map.size() == 1);
   }

   REQUIRE(destructors_called == 1);
}

namespace
{

struct non_trivial_but_abandonable
{
   non_trivial_but_abandonable(uint16_t& destructors_called)
       : m_destructors_called(destructors_called)
   {
   }

   ~non_trivial_but_abandonable() { ++m_destructors_called; }

   uint16_t& m_destructors_called;
};

} // end of unnamed namespace

namespace allo
{

template <> struct is_abandonable<non_trivial_but_abandonable> : std::true_type
{
};

} // end of namespace allo

TEST_CASE("Wrapped map must abandon memory if the types are abandonable and all allocations were private",
          "[wrapped-map][abandon]")
{
   uint16_t destructors_called = 0;

   {
      allo::containers::wrapped_map<uint16_t, non_trivial_but_abandonable> wrapped_map(1024);
      auto& map = wrapped_map.unwrap();

      static_assert(allo::is_abandonable<std::remove_reference<decltype(map)>::type>::type::value,
                    "map must be abandonable in this test");

      map.emplace(42, destructors_called);

      REQUIRE(destructors_called == 0);
      REQUIRE(map.size() == 1);
   }

   REQUIRE(destructors_called == 0);
}
<|endoftext|>


<commit_before>//
//  ZX8081.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 08/06/2017.
//  Copyright 2017 Thomas Harte. All rights reserved.
//

#include "ZX8081.hpp"

using namespace Storage::Data::ZX8081;

static uint16_t short_at(std::size_t address, const std::vector<uint8_t> &data) {
	return static_cast<uint16_t>(data[address] | (data[address + 1] << 8));
}

static std::shared_ptr<File> ZX80FileFromData(const std::vector<uint8_t> &data) {
	// Does this look like a ZX80 file?

	if(data.size() < 0x28) return nullptr;

//	uint16_t next_line_number = short_at(0x2, data);
//	uint16_t first_visible_line = short_at(0x13, data);

	uint16_t vars = short_at(0x8, data);
	uint16_t end_of_file = short_at(0xa, data);
	uint16_t display_address = short_at(0xc, data);

	// check that the end of file is contained within the supplied data
	if(static_cast<size_t>(end_of_file - 0x4000) > data.size()) return nullptr;

	// check for the proper ordering of buffers
	if(vars > end_of_file) return nullptr;
	if(end_of_file > display_address) return nullptr;

	// TODO: does it make sense to inspect the tokenised BASIC?
	// It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76],
	// but I'm as yet unable to find documentation of the tokens.

	// TODO: check that the line numbers declared above exist (?)

	std::shared_ptr<File> file(new File);
	file->data = data;
	file->isZX81 = false;
	return file;
}

static std::shared_ptr<File> ZX81FileFromData(const std::vector<uint8_t> &data) {
	// Does this look like a ZX81 file?

	// Look for a file name.
	std::size_t data_pointer = 0;
	std::vector<uint8_t> name_data;
	std::size_t c = 11;
	while(c < data.size() && c--) {
		name_data.push_back(data[data_pointer] & 0x3f);
		if(data[data_pointer] & 0x80) break;
		data_pointer++;
	}
	if(!c) return nullptr;
	data_pointer++;

	if(data.size() < data_pointer + 0x405e - 0x4009) return nullptr;

//	if(data[data_pointer]) return nullptr;

	uint16_t vars = short_at(data_pointer + 0x4010 - 0x4009, data);
	uint16_t end_of_file = short_at(data_pointer + 0x4014 - 0x4009, data);
//	uint16_t display_address = short_at(0x400c - 0x4009, data);

	// check that the end of file is contained within the supplied data
	if(data_pointer + end_of_file - 0x4009 > data.size()) return nullptr;

	// check for the proper ordering of buffers
//	if(vars > end_of_file) return nullptr;
//	if(end_of_file > display_address) return nullptr;

	// TODO: does it make sense to inspect the tokenised BASIC?
	// It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76],
	// but I'm as yet unable to find documentation of the tokens.

	// TODO: check that the line numbers declared above exist (?)

	std::shared_ptr<File> file(new File);
	file->name = StringFromData(name_data, true);
	file->data = data;
	file->isZX81 = true;
	return file;
}

std::shared_ptr<File> Storage::Data::ZX8081::FileFromData(const std::vector<uint8_t> &data) {
	std::shared_ptr<Storage::Data::ZX8081::File> result = ZX81FileFromData(data);
	if(result) return result;
	return ZX80FileFromData(data);
}

// MARK: - String conversion

std::wstring Storage::Data::ZX8081::StringFromData(const std::vector<uint8_t> &data, bool is_zx81) {
	std::wstring string;

	wchar_t zx80_map[64] = {
		' ',	u'\u2598',	u'\u259d',	u'\u2580',	u'\u2596',	u'\u258c',	u'\u259e',	u'\u259b',	u'\u2588',	u'\u2584',	u'\u2580',	'"',	u'\u00a3',	'$',	':',	'?',
		'(',	')',		'>',		'<',		'=',		'+',		'-',		'*',		'/',		';',		',',		'.',	'0',		'1',	'2',	'3',
		'4',	'5',		'6',		'7',		'8',		'9',		'A',		'B',		'C',		'D',		'E',		'F',	'G',		'H',	'I',	'J',
		'K',	'L',		'M',		'N',		'O',		'P',		'Q',		'R',		'S',		'T',		'U',		'V',	'W',		'X',	'Y',	'Z'
	};
	// TODO: the block character conversions shown here are in the wrong order
	wchar_t zx81_map[64] = {
		' ',	u'\u2598',	u'\u259d',	u'\u2580',	u'\u2596',	u'\u258c',	u'\u259e',	u'\u259b',	u'\u2588',	u'\u2584',	u'\u2580',	'"',	u'\u00a3',	'$',	':',	'?',
		'(',	')',		'-',		'+',		'*',		'/',		'=',		'>',		'<',		';',		',',		'.',	'0',		'1',	'2',	'3',
		'4',	'5',		'6',		'7',		'8',		'9',		'A',		'B',		'C',		'D',		'E',		'F',	'G',		'H',	'I',	'J',
		'K',	'L',		'M',		'N',		'O',		'P',		'Q',		'R',		'S',		'T',		'U',		'V',	'W',		'X',	'Y',	'Z'
	};
	wchar_t *map = is_zx81 ? zx81_map : zx80_map;

	for(uint8_t byte : data) {
		string.push_back(map[byte & 0x3f]);
	}

	return string;
}

std::vector<uint8_t> Storage::Data::ZX8081::DataFromString(const std::wstring &string, bool is_zx81) {
	std::vector<uint8_t> data;

	// TODO

	return data;
}
<commit_msg>Removes unused calculation of `vars`.<commit_after>//
//  ZX8081.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 08/06/2017.
//  Copyright 2017 Thomas Harte. All rights reserved.
//

#include "ZX8081.hpp"

using namespace Storage::Data::ZX8081;

static uint16_t short_at(std::size_t address, const std::vector<uint8_t> &data) {
	return static_cast<uint16_t>(data[address] | (data[address + 1] << 8));
}

static std::shared_ptr<File> ZX80FileFromData(const std::vector<uint8_t> &data) {
	// Does this look like a ZX80 file?

	if(data.size() < 0x28) return nullptr;

//	uint16_t next_line_number = short_at(0x2, data);
//	uint16_t first_visible_line = short_at(0x13, data);

	uint16_t vars = short_at(0x8, data);
	uint16_t end_of_file = short_at(0xa, data);
	uint16_t display_address = short_at(0xc, data);

	// check that the end of file is contained within the supplied data
	if(static_cast<size_t>(end_of_file - 0x4000) > data.size()) return nullptr;

	// check for the proper ordering of buffers
	if(vars > end_of_file) return nullptr;
	if(end_of_file > display_address) return nullptr;

	// TODO: does it make sense to inspect the tokenised BASIC?
	// It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76],
	// but I'm as yet unable to find documentation of the tokens.

	// TODO: check that the line numbers declared above exist (?)

	std::shared_ptr<File> file(new File);
	file->data = data;
	file->isZX81 = false;
	return file;
}

static std::shared_ptr<File> ZX81FileFromData(const std::vector<uint8_t> &data) {
	// Does this look like a ZX81 file?

	// Look for a file name.
	std::size_t data_pointer = 0;
	std::vector<uint8_t> name_data;
	std::size_t c = 11;
	while(c < data.size() && c--) {
		name_data.push_back(data[data_pointer] & 0x3f);
		if(data[data_pointer] & 0x80) break;
		data_pointer++;
	}
	if(!c) return nullptr;
	data_pointer++;

	if(data.size() < data_pointer + 0x405e - 0x4009) return nullptr;

//	if(data[data_pointer]) return nullptr;

//	uint16_t vars = short_at(data_pointer + 0x4010 - 0x4009, data);
	uint16_t end_of_file = short_at(data_pointer + 0x4014 - 0x4009, data);
//	uint16_t display_address = short_at(0x400c - 0x4009, data);

	// check that the end of file is contained within the supplied data
	if(data_pointer + end_of_file - 0x4009 > data.size()) return nullptr;

	// check for the proper ordering of buffers
//	if(vars > end_of_file) return nullptr;
//	if(end_of_file > display_address) return nullptr;

	// TODO: does it make sense to inspect the tokenised BASIC?
	// It starts at 0x4028 and proceeds as [16-bit line number] [tokens] [0x76],
	// but I'm as yet unable to find documentation of the tokens.

	// TODO: check that the line numbers declared above exist (?)

	std::shared_ptr<File> file(new File);
	file->name = StringFromData(name_data, true);
	file->data = data;
	file->isZX81 = true;
	return file;
}

std::shared_ptr<File> Storage::Data::ZX8081::FileFromData(const std::vector<uint8_t> &data) {
	std::shared_ptr<Storage::Data::ZX8081::File> result = ZX81FileFromData(data);
	if(result) return result;
	return ZX80FileFromData(data);
}

// MARK: - String conversion

std::wstring Storage::Data::ZX8081::StringFromData(const std::vector<uint8_t> &data, bool is_zx81) {
	std::wstring string;

	wchar_t zx80_map[64] = {
		' ',	u'\u2598',	u'\u259d',	u'\u2580',	u'\u2596',	u'\u258c',	u'\u259e',	u'\u259b',	u'\u2588',	u'\u2584',	u'\u2580',	'"',	u'\u00a3',	'$',	':',	'?',
		'(',	')',		'>',		'<',		'=',		'+',		'-',		'*',		'/',		';',		',',		'.',	'0',		'1',	'2',	'3',
		'4',	'5',		'6',		'7',		'8',		'9',		'A',		'B',		'C',		'D',		'E',		'F',	'G',		'H',	'I',	'J',
		'K',	'L',		'M',		'N',		'O',		'P',		'Q',		'R',		'S',		'T',		'U',		'V',	'W',		'X',	'Y',	'Z'
	};
	// TODO: the block character conversions shown here are in the wrong order
	wchar_t zx81_map[64] = {
		' ',	u'\u2598',	u'\u259d',	u'\u2580',	u'\u2596',	u'\u258c',	u'\u259e',	u'\u259b',	u'\u2588',	u'\u2584',	u'\u2580',	'"',	u'\u00a3',	'$',	':',	'?',
		'(',	')',		'-',		'+',		'*',		'/',		'=',		'>',		'<',		';',		',',		'.',	'0',		'1',	'2',	'3',
		'4',	'5',		'6',		'7',		'8',		'9',		'A',		'B',		'C',		'D',		'E',		'F',	'G',		'H',	'I',	'J',
		'K',	'L',		'M',		'N',		'O',		'P',		'Q',		'R',		'S',		'T',		'U',		'V',	'W',		'X',	'Y',	'Z'
	};
	wchar_t *map = is_zx81 ? zx81_map : zx80_map;

	for(uint8_t byte : data) {
		string.push_back(map[byte & 0x3f]);
	}

	return string;
}

std::vector<uint8_t> Storage::Data::ZX8081::DataFromString(const std::wstring &string, bool is_zx81) {
	std::vector<uint8_t> data;

	// TODO

	return data;
}
<|endoftext|>


<commit_before>#include "LinuxHardware.h"

#include <unistd.h>
#include <cpuid.h>
#include <thread>

namespace Red
{
	std::string LinuxSystemHardware::GetOSName()
	{
		return "Linux";
	}

	uint8_t LinuxSystemHardware::GetArchitecture()
	{
#if defined(__x86_64__) || defined(__ppc64__)
		return 64;
#else
		return 32;
#endif
	}

	std::string LinuxSystemHardware::GetCPUVendor()
	{
		int Data[4] = { 0 };
		char Vendor[13] = { 0 };

		if (__get_cpuid(0, &Data[0], &Data[1], &Data[2], &Data[3]) != 0)
		{
			// CPU vendor is stored in EBX EDX ECX (ASCII)
			memcpy(Vendor + 0, &Data[1], 4);  // EBX
			memcpy(Vendor + 4, &Data[3], 4);  // ECX
			memcpy(Vendor + 8, &Data[2], 4);  // EDX
		}

		else
		{
			return "Error";
		}

		Vendor[12] = '\0';

		return Vendor;
	}

	std::string LinuxSystemHardware::GetCPUBrand()
	{
		int Data[4] = { 0 };
		char Brand[49] = { 0 };

		if (__get_cpuid(0x80000002, &Data[0], &Data[1], &Data[2], &Data[3]) != 0)
		{
			memcpy(Brand + 0, &Data[0], 4);
			memcpy(Brand + 4, &Data[1], 4);
			memcpy(Brand + 8, &Data[2], 4);
			memcpy(Brand + 12, &Data[3], 4);
		}

		else
		{
			return "Error";
		}

		if (__get_cpuid(0x80000003, &Data[0], &Data[1], &Data[2], &Data[3]) != 0)
		{
			memcpy(Brand + 16, &Data[0], 4);
			memcpy(Brand + 20, &Data[1], 4);
			memcpy(Brand + 24, &Data[2], 4);
			memcpy(Brand + 28, &Data[3], 4);
		}

		else
		{
			return "Error";
		}

		if (__get_cpuid(0x80000004, &Data[0], &Data[1], &Data[2], &Data[3]) != 0)
		{
			memcpy(Brand + 32, &Data[0], 4);
			memcpy(Brand + 36, &Data[1], 4);
			memcpy(Brand + 40, &Data[2], 4);
			memcpy(Brand + 44, &Data[3], 4);
		}

		else
		{
			return "Error";
		}

		Brand[48] = '\0';

		return Brand;
	}

	unsigned int LinuxSystemHardware::GetCPUCoreCount()
	{
		return std::thread::hardware_concurrency();
	}

	unsigned long int LinuxSystemHardware::GetPhysicalMemory()
	{
		return (sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE));
	}
}  // namespace Red<commit_msg>Added Linux Hardware Memory Dependency<commit_after>#include "LinuxHardware.h"

#include <memory>
#include <unistd.h>
#include <cpuid.h>
#include <thread>

namespace Red
{
	std::string LinuxSystemHardware::GetOSName()
	{
		return "Linux";
	}

	uint8_t LinuxSystemHardware::GetArchitecture()
	{
#if defined(__x86_64__) || defined(__ppc64__)
		return 64;
#else
		return 32;
#endif
	}

	std::string LinuxSystemHardware::GetCPUVendor()
	{
		int Data[4] = { 0 };
		char Vendor[13] = { 0 };

		if (__get_cpuid(0, &Data[0], &Data[1], &Data[2], &Data[3]) != 0)
		{
			// CPU vendor is stored in EBX EDX ECX (ASCII)
			memcpy(Vendor + 0, &Data[1], 4);  // EBX
			memcpy(Vendor + 4, &Data[3], 4);  // ECX
			memcpy(Vendor + 8, &Data[2], 4);  // EDX
		}

		else
		{
			return "Error";
		}

		Vendor[12] = '\0';

		return Vendor;
	}

	std::string LinuxSystemHardware::GetCPUBrand()
	{
		int Data[4] = { 0 };
		char Brand[49] = { 0 };

		if (__get_cpuid(0x80000002, &Data[0], &Data[1], &Data[2], &Data[3]) != 0)
		{
			memcpy(Brand + 0, &Data[0], 4);
			memcpy(Brand + 4, &Data[1], 4);
			memcpy(Brand + 8, &Data[2], 4);
			memcpy(Brand + 12, &Data[3], 4);
		}

		else
		{
			return "Error";
		}

		if (__get_cpuid(0x80000003, &Data[0], &Data[1], &Data[2], &Data[3]) != 0)
		{
			memcpy(Brand + 16, &Data[0], 4);
			memcpy(Brand + 20, &Data[1], 4);
			memcpy(Brand + 24, &Data[2], 4);
			memcpy(Brand + 28, &Data[3], 4);
		}

		else
		{
			return "Error";
		}

		if (__get_cpuid(0x80000004, &Data[0], &Data[1], &Data[2], &Data[3]) != 0)
		{
			memcpy(Brand + 32, &Data[0], 4);
			memcpy(Brand + 36, &Data[1], 4);
			memcpy(Brand + 40, &Data[2], 4);
			memcpy(Brand + 44, &Data[3], 4);
		}

		else
		{
			return "Error";
		}

		Brand[48] = '\0';

		return Brand;
	}

	unsigned int LinuxSystemHardware::GetCPUCoreCount()
	{
		return std::thread::hardware_concurrency();
	}

	unsigned long int LinuxSystemHardware::GetPhysicalMemory()
	{
		return (sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGE_SIZE));
	}
}  // namespace Red
<|endoftext|>