<commit_before>#include <gsl/gsl_vector.h>
#include <gsl/gsl_multimin.h>
#include "counts.h"
#include "backward.h"
#include "util.h"
#include "logger.h"

// Prefix for sqrt-transformed parameters
#define TransformedParamPrefix "$x"

// GSL multidimensional optimization parameters
#define StepSize 0.1
#define LineSearchTolerance 1e-4
#define EpsilonAbsolute 1e-3
#define MaxIterations 100

// log levels
#define ParamTransformLogLevel 9
#define ObjectiveFunctionLogLevel 8
#define OptimizationParamsLogLevel 7

MachineCounts::MachineCounts()
{ }

MachineCounts::MachineCounts (const EvaluatedMachine& machine) {
  init (machine);
}

MachineCounts::MachineCounts (const EvaluatedMachine& machine, const SeqPair& seqPair)
{
  init (machine);
  (void) add (machine, seqPair);
}

MachineCounts::MachineCounts (const EvaluatedMachine& machine, const SeqPairList& seqPairList, const list<Envelope>& envelopes)
{
  init (machine);
  auto env = envelopes.begin();
  for (const auto& seqPair: seqPairList.seqPairs)
    (void) add (machine, seqPair, env == envelopes.end() ? Envelope(seqPair) : *(env++));
}

void MachineCounts::init (const EvaluatedMachine& machine) {
  loglike = 0;
  count = vguard<vguard<double> > (machine.nStates());
  for (StateIndex s = 0; s < machine.nStates(); ++s)
    count[s].resize (machine.state[s].nTransitions, 0.);
}

double MachineCounts::add (const EvaluatedMachine& machine, const SeqPair& seqPair) {
  const Envelope env (seqPair);
  return add (machine, seqPair, env);
}

double MachineCounts::add (const EvaluatedMachine& machine, const SeqPair& seqPair, const Envelope& env) {
  const ForwardMatrix forward (machine, seqPair, env);
  const BackwardMatrix backward (machine, seqPair, env);
  backward.getCounts (forward, *this);
  const double result = forward.logLike();
  loglike += result;
  return result;
}

MachineCounts& MachineCounts::operator+= (const MachineCounts& counts) {
  for (StateIndex s = 0; s < count.size(); ++s)
    for (size_t t = 0; t < count[s].size(); ++t)
      count[s][t] += counts.count[s][t];
  return *this;
}

void MachineCounts::writeJson (ostream& outs) const {
  vguard<string> s;
  for (const auto& c: count)
    s.push_back (string("[") + to_string_join (c, ",") + "]");
  outs << "[" << join (s, ",\n ") << "]" << endl;
}

void MachineCounts::writeParamCountsJson (ostream& outs, const Machine& machine, const ParamAssign& prob) const {
  const auto pc = paramCounts (machine, prob);
  outs << "{";
  size_t n = 0;
  for (const auto& name_count: pc)
    outs << (n++ ? "," : "") << "\"" << escaped_str(name_count.first) << "\":" << name_count.second;
  outs << "}";
}

map<string,double> MachineCounts::paramCounts (const Machine& machine, const ParamAssign& prob) const {
  map<string,double> paramCount;
  Assert (count.size() == machine.state.size(), "Number of states mismatch");
  for (StateIndex s = 0; s < machine.nStates(); ++s) {
    auto transIter = machine.state[s].trans.begin();
    Assert (count[s].size() == machine.state[s].trans.size(), "State size mismatch");
    for (auto& c: count[s]) {
      auto& trans = *(transIter++);
      const auto transParams = WeightAlgebra::params (trans.weight, ParamDefs());
      const double w = WeightAlgebra::eval (trans.weight, prob.defs);
      for (auto& p: transParams) {
	const auto deriv = WeightAlgebra::deriv (trans.weight, ParamDefs(), p);
	paramCount[p] += c * WeightAlgebra::eval (deriv, prob.defs) * WeightAlgebra::asDouble (prob.defs.at(p)) / w;
      }
    }
  }
  return paramCount;
}

WeightExpr makeSquareFunc (const string& trParam) {
  WeightExpr tr = WeightAlgebra::param (trParam);
  return WeightAlgebra::multiply (tr, tr);
}

WeightExpr makeExpFunc (const string& trParam) {
  return WeightAlgebra::expOf (WeightAlgebra::minus (makeSquareFunc (trParam)));
}

MachineObjective::MachineObjective (const Machine& machine, const MachineCounts& counts, const Constraints& cons, const Params& constants) :
  constraints (machine.cons.combine (cons)),
  constantDefs (machine.defs.combine (constants).defs),
  objective (WeightAlgebra::zero())
{
  for (StateIndex s = 0; s < machine.state.size(); ++s) {
    EvaluatedMachineState::TransIndex t = 0;
    for (TransList::const_iterator iter = machine.state[s].trans.begin();
	 iter != machine.state[s].trans.end(); ++iter, ++t) {
      const WeightExpr term = WeightAlgebra::multiply (WeightAlgebra::doubleConstant (counts.count[s][t]),
						       WeightAlgebra::logOf ((*iter).weight));
      objective = WeightAlgebra::subtract (objective, term);
    }
  }

  // p_i = (1 - exp(-x_i^2)) \prod_{k=1}^{i-1} exp(-x_k^2)
  const set<string> p = WeightAlgebra::params (objective, ParamDefs());
  int trIdx = 0;
  auto makeTransformedParamName = [&] (const string& param) -> string {
    string trParam;
    do
      trParam = string(TransformedParamPrefix) + to_string(++trIdx);
    while (p.count(trParam));
    transformedParamIndex[param] = transformedParam.size();
    transformedParam.push_back (trParam);
    return trParam;
  };
  for (const auto& c: constraints.norm) {
    WeightExpr notPrev = WeightAlgebra::one();
    for (size_t n = 0; n < c.size(); ++n) {
      const string& cParam = c[n];
      if (n + 1 == c.size())
	paramTransformDefs[cParam] = notPrev;
      else {
	const string trParam = makeTransformedParamName (cParam);
	WeightExpr notThis = makeExpFunc (trParam);
	paramTransformDefs[cParam] = WeightAlgebra::multiply (notPrev, WeightAlgebra::negate (notThis));
	notPrev = WeightAlgebra::multiply (notPrev, notThis);
      }
    }
  }

  for (const auto& pParam: constraints.prob)
    paramTransformDefs[pParam] = makeExpFunc (makeTransformedParamName (pParam));

  for (const auto& rParam: constraints.rate)
    paramTransformDefs[rParam] = makeSquareFunc (makeTransformedParamName (rParam));

  if (LoggingThisAt(ParamTransformLogLevel))
    for (const auto& p_d: paramTransformDefs)
      LogThisAt(ParamTransformLogLevel,"Mapping " << p_d.first << " to " << WeightAlgebra::toString (p_d.second, ParamDefs()) << endl);

  allDefs = constantDefs;
  allDefs.insert (paramTransformDefs.begin(), paramTransformDefs.end());

  deriv.reserve (transformedParam.size());
  for (const auto& p: transformedParam)
    deriv.push_back (WeightAlgebra::deriv (objective, allDefs, p));

  LogThisAt (ObjectiveFunctionLogLevel, toString());
}

string MachineObjective::toString() const {
  string s = string("E = ") + WeightAlgebra::toString(objective,allDefs) + "\n";
  for (size_t n = 0; n < transformedParam.size(); ++n)
    s += "dE/d" + transformedParam[n] + " = " + WeightAlgebra::toString(deriv[n],allDefs) + "\n";
  return s;
}

Params gsl_vector_to_params (const gsl_vector *v, const MachineObjective& ml) {
  Params p;
  p.defs = ml.allDefs;
  for (size_t n = 0; n < ml.transformedParam.size(); ++n)
    p.defs[ml.transformedParam[n]] = WeightExpr (WeightAlgebra::doubleConstant (gsl_vector_get (v, n)));
  return p;
}

double gsl_machine_objective (const gsl_vector *v, void *voidML)
{
  const MachineObjective& ml (*((MachineObjective*)voidML));
  const Params pv = gsl_vector_to_params (v, ml);

  const double f = WeightAlgebra::eval (ml.objective, pv.defs);

  LogThisAt (OptimizationParamsLogLevel, JsonLoader<Params>::toJsonString(pv) << endl);
  LogThisAt (ObjectiveFunctionLogLevel, "gsl_machine_objective(" << to_string_join(gsl_vector_to_stl(v)) << ") = " << f << endl);

  return f;
}

void gsl_machine_objective_deriv (const gsl_vector *v, void *voidML, gsl_vector *df)
{
  const MachineObjective& ml (*((MachineObjective*)voidML));
  const Params pv = gsl_vector_to_params (v, ml);

  for (size_t n = 0; n < ml.transformedParam.size(); ++n)
    gsl_vector_set (df, n, WeightAlgebra::eval (ml.deriv[n], pv.defs));

  const vguard<double> v_stl = gsl_vector_to_stl(v), df_stl = gsl_vector_to_stl(df);
  LogThisAt (ObjectiveFunctionLogLevel, "gsl_machine_objective_deriv(" << to_string_join(v_stl) << ") = (" << to_string_join(df_stl) << ")" << endl);
}

void gsl_machine_objective_with_deriv (const gsl_vector *x, void *voidML, double *f, gsl_vector *df)
{
  *f = gsl_machine_objective (x, voidML);
  gsl_machine_objective_deriv (x, voidML, df);
}

Params MachineObjective::optimize (const Params& seed) const {
  gsl_vector *v;
  gsl_multimin_function_fdf func;
  func.n = transformedParam.size();
  func.f = gsl_machine_objective;
  func.df = gsl_machine_objective_deriv;
  func.fdf = gsl_machine_objective_with_deriv;
  func.params = (void*) this;

  gsl_vector* x = gsl_vector_alloc (func.n);
  // p_i = (1 - z_i) \prod_{k=1}^{i-1} z_k
  // where z_i = exp(-x_i^2)
  // \prod_{k=1}^{i-1} z_k = 1 - \sum_{k=1}^{i-1} p_k
  // so z_i = 1 - p_i / (1 - \sum_{k=1}^{i-1} p_k)
  // x_i = sqrt(-log z_i)
  for (const auto& c: constraints.norm) {
    double pSum = 0;
    for (size_t n = 0; n + 1 < c.size(); ++n) {
      const string& cParam = c[n];
      const double p = WeightAlgebra::asDouble (seed.defs.at(cParam));
      const double z = 1 - p / (1 - pSum);
      const double val = sqrt (-log (z));
      pSum += p;
      gsl_vector_set (x, transformedParamIndex.at(cParam), val);
      LogThisAt(ParamTransformLogLevel,"Setting " << transformedParam[transformedParamIndex.at(cParam)] << " to " << val << endl);
    }
  }
  for (const auto& pParam: constraints.prob) {
      const double p = WeightAlgebra::asDouble (seed.defs.at(pParam));
      const double val = sqrt (-log (p));
      gsl_vector_set (x, transformedParamIndex.at(pParam), val);
      LogThisAt(ParamTransformLogLevel,"Setting " << transformedParam[transformedParamIndex.at(pParam)] << " to " << val << endl);
  }
  for (const auto& rParam: constraints.rate) {
      const double r = WeightAlgebra::asDouble (seed.defs.at(rParam));
      const double val = sqrt (r);
      gsl_vector_set (x, transformedParamIndex.at(rParam), val);
      LogThisAt(ParamTransformLogLevel,"Setting " << transformedParam[transformedParamIndex.at(rParam)] << " to " << val << endl);
  }

  const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_vector_bfgs2;
  gsl_multimin_fdfminimizer *s = gsl_multimin_fdfminimizer_alloc (T, func.n);

  gsl_multimin_fdfminimizer_set (s, &func, x, StepSize, LineSearchTolerance);

  size_t iter = 0;
  int status;
  do
    {
      iter++;
      status = gsl_multimin_fdfminimizer_iterate (s);

      const vguard<double> x_stl = gsl_vector_to_stl(s->x);
      LogThisAt (OptimizationParamsLogLevel, "iteration #" << iter << ": x=(" << to_string_join(x_stl) << ")" << endl);

      if (status)
        break;

      status = gsl_multimin_test_gradient (s->gradient, EpsilonAbsolute);
    }
  while (status == GSL_CONTINUE && iter < MaxIterations);

  const Params finalTransformedParams = gsl_vector_to_params (s->x, *this);
  Params finalParams = seed;
  for (const auto& pt: paramTransformDefs)
    finalParams.defs[pt.first] = WeightAlgebra::doubleConstant (WeightAlgebra::eval (pt.second, finalTransformedParams.defs));

  gsl_multimin_fdfminimizer_free (s);
  gsl_vector_free (x);

  return finalParams;
}
<commit_msg>fixed compile error (no for real this time)<commit_after>#include <gsl/gsl_vector.h>
#include <gsl/gsl_multimin.h>
#include "counts.h"
#include "backward.h"
#include "util.h"
#include "logger.h"

// Prefix for sqrt-transformed parameters
#define TransformedParamPrefix "$x"

// GSL multidimensional optimization parameters
#define StepSize 0.1
#define LineSearchTolerance 1e-4
#define EpsilonAbsolute 1e-3
#define MaxIterations 100

// log levels
#define ParamTransformLogLevel 9
#define ObjectiveFunctionLogLevel 8
#define OptimizationParamsLogLevel 7

MachineCounts::MachineCounts()
{ }

MachineCounts::MachineCounts (const EvaluatedMachine& machine) {
  init (machine);
}

MachineCounts::MachineCounts (const EvaluatedMachine& machine, const SeqPair& seqPair)
{
  init (machine);
  (void) add (machine, seqPair);
}

MachineCounts::MachineCounts (const EvaluatedMachine& machine, const SeqPairList& seqPairList, const list<Envelope>& envelopes)
{
  init (machine);
  auto env = envelopes.begin();
  for (const auto& seqPair: seqPairList.seqPairs)
    (void) add (machine, seqPair, env == envelopes.end() ? Envelope(seqPair) : *(env++));
}

void MachineCounts::init (const EvaluatedMachine& machine) {
  loglike = 0;
  count = vguard<vguard<double> > (machine.nStates());
  for (StateIndex s = 0; s < machine.nStates(); ++s)
    count[s].resize (machine.state[s].nTransitions, 0.);
}

double MachineCounts::add (const EvaluatedMachine& machine, const SeqPair& seqPair) {
  const Envelope env (seqPair);
  return add (machine, seqPair, env);
}

double MachineCounts::add (const EvaluatedMachine& machine, const SeqPair& seqPair, const Envelope& env) {
  const ForwardMatrix forward (machine, seqPair, env);
  const BackwardMatrix backward (machine, seqPair, env);
  backward.getCounts (forward, *this);
  const double result = forward.logLike();
  loglike += result;
  return result;
}

MachineCounts& MachineCounts::operator+= (const MachineCounts& counts) {
  for (StateIndex s = 0; s < count.size(); ++s)
    for (size_t t = 0; t < count[s].size(); ++t)
      count[s][t] += counts.count[s][t];
  return *this;
}

void MachineCounts::writeJson (ostream& outs) const {
  vguard<string> s;
  for (const auto& c: count)
    s.push_back (string("[") + to_string_join (c, ",") + "]");
  outs << "[" << join (s, ",\n ") << "]" << endl;
}

void MachineCounts::writeParamCountsJson (ostream& outs, const Machine& machine, const ParamAssign& prob) const {
  const auto pc = paramCounts (machine, prob);
  outs << "{";
  size_t n = 0;
  for (const auto& name_count: pc)
    outs << (n++ ? "," : "") << "\"" << escaped_str(name_count.first) << "\":" << name_count.second;
  outs << "}";
}

map<string,double> MachineCounts::paramCounts (const Machine& machine, const ParamAssign& prob) const {
  map<string,double> paramCount;
  Assert (count.size() == machine.state.size(), "Number of states mismatch");
  for (StateIndex s = 0; s < machine.nStates(); ++s) {
    auto transIter = machine.state[s].trans.begin();
    Assert (count[s].size() == machine.state[s].trans.size(), "State size mismatch");
    for (auto& c: count[s]) {
      auto& trans = *(transIter++);
      const auto transParams = WeightAlgebra::params (trans.weight, ParamDefs());
      const double w = WeightAlgebra::eval (trans.weight, prob.defs);
      for (auto& p: transParams) {
	const auto deriv = WeightAlgebra::deriv (trans.weight, ParamDefs(), p);
	paramCount[p] += c * WeightAlgebra::eval (deriv, prob.defs) * WeightAlgebra::asDouble (prob.defs.at(p)) / w;
      }
    }
  }
  return paramCount;
}

WeightExpr makeSquareFunc (const string& trParam) {
  WeightExpr tr = WeightAlgebra::param (trParam);
  return WeightAlgebra::multiply (tr, tr);
}

WeightExpr makeExpFunc (const string& trParam) {
  return WeightAlgebra::expOf (WeightAlgebra::minus (makeSquareFunc (trParam)));
}

MachineObjective::MachineObjective (const Machine& machine, const MachineCounts& counts, const Constraints& cons, const Params& constants) :
  constraints (machine.cons.combine (cons)),
  constantDefs (machine.funcs.combine (constants).defs),
  objective (WeightAlgebra::zero())
{
  for (StateIndex s = 0; s < machine.state.size(); ++s) {
    EvaluatedMachineState::TransIndex t = 0;
    for (TransList::const_iterator iter = machine.state[s].trans.begin();
	 iter != machine.state[s].trans.end(); ++iter, ++t) {
      const WeightExpr term = WeightAlgebra::multiply (WeightAlgebra::doubleConstant (counts.count[s][t]),
						       WeightAlgebra::logOf ((*iter).weight));
      objective = WeightAlgebra::subtract (objective, term);
    }
  }

  // p_i = (1 - exp(-x_i^2)) \prod_{k=1}^{i-1} exp(-x_k^2)
  const set<string> p = WeightAlgebra::params (objective, ParamDefs());
  int trIdx = 0;
  auto makeTransformedParamName = [&] (const string& param) -> string {
    string trParam;
    do
      trParam = string(TransformedParamPrefix) + to_string(++trIdx);
    while (p.count(trParam));
    transformedParamIndex[param] = transformedParam.size();
    transformedParam.push_back (trParam);
    return trParam;
  };
  for (const auto& c: constraints.norm) {
    WeightExpr notPrev = WeightAlgebra::one();
    for (size_t n = 0; n < c.size(); ++n) {
      const string& cParam = c[n];
      if (n + 1 == c.size())
	paramTransformDefs[cParam] = notPrev;
      else {
	const string trParam = makeTransformedParamName (cParam);
	WeightExpr notThis = makeExpFunc (trParam);
	paramTransformDefs[cParam] = WeightAlgebra::multiply (notPrev, WeightAlgebra::negate (notThis));
	notPrev = WeightAlgebra::multiply (notPrev, notThis);
      }
    }
  }

  for (const auto& pParam: constraints.prob)
    paramTransformDefs[pParam] = makeExpFunc (makeTransformedParamName (pParam));

  for (const auto& rParam: constraints.rate)
    paramTransformDefs[rParam] = makeSquareFunc (makeTransformedParamName (rParam));

  if (LoggingThisAt(ParamTransformLogLevel))
    for (const auto& p_d: paramTransformDefs)
      LogThisAt(ParamTransformLogLevel,"Mapping " << p_d.first << " to " << WeightAlgebra::toString (p_d.second, ParamDefs()) << endl);

  allDefs = constantDefs;
  allDefs.insert (paramTransformDefs.begin(), paramTransformDefs.end());

  deriv.reserve (transformedParam.size());
  for (const auto& p: transformedParam)
    deriv.push_back (WeightAlgebra::deriv (objective, allDefs, p));

  LogThisAt (ObjectiveFunctionLogLevel, toString());
}

string MachineObjective::toString() const {
  string s = string("E = ") + WeightAlgebra::toString(objective,allDefs) + "\n";
  for (size_t n = 0; n < transformedParam.size(); ++n)
    s += "dE/d" + transformedParam[n] + " = " + WeightAlgebra::toString(deriv[n],allDefs) + "\n";
  return s;
}

Params gsl_vector_to_params (const gsl_vector *v, const MachineObjective& ml) {
  Params p;
  p.defs = ml.allDefs;
  for (size_t n = 0; n < ml.transformedParam.size(); ++n)
    p.defs[ml.transformedParam[n]] = WeightExpr (WeightAlgebra::doubleConstant (gsl_vector_get (v, n)));
  return p;
}

double gsl_machine_objective (const gsl_vector *v, void *voidML)
{
  const MachineObjective& ml (*((MachineObjective*)voidML));
  const Params pv = gsl_vector_to_params (v, ml);

  const double f = WeightAlgebra::eval (ml.objective, pv.defs);

  LogThisAt (OptimizationParamsLogLevel, JsonLoader<Params>::toJsonString(pv) << endl);
  LogThisAt (ObjectiveFunctionLogLevel, "gsl_machine_objective(" << to_string_join(gsl_vector_to_stl(v)) << ") = " << f << endl);

  return f;
}

void gsl_machine_objective_deriv (const gsl_vector *v, void *voidML, gsl_vector *df)
{
  const MachineObjective& ml (*((MachineObjective*)voidML));
  const Params pv = gsl_vector_to_params (v, ml);

  for (size_t n = 0; n < ml.transformedParam.size(); ++n)
    gsl_vector_set (df, n, WeightAlgebra::eval (ml.deriv[n], pv.defs));

  const vguard<double> v_stl = gsl_vector_to_stl(v), df_stl = gsl_vector_to_stl(df);
  LogThisAt (ObjectiveFunctionLogLevel, "gsl_machine_objective_deriv(" << to_string_join(v_stl) << ") = (" << to_string_join(df_stl) << ")" << endl);
}

void gsl_machine_objective_with_deriv (const gsl_vector *x, void *voidML, double *f, gsl_vector *df)
{
  *f = gsl_machine_objective (x, voidML);
  gsl_machine_objective_deriv (x, voidML, df);
}

Params MachineObjective::optimize (const Params& seed) const {
  gsl_vector *v;
  gsl_multimin_function_fdf func;
  func.n = transformedParam.size();
  func.f = gsl_machine_objective;
  func.df = gsl_machine_objective_deriv;
  func.fdf = gsl_machine_objective_with_deriv;
  func.params = (void*) this;

  gsl_vector* x = gsl_vector_alloc (func.n);
  // p_i = (1 - z_i) \prod_{k=1}^{i-1} z_k
  // where z_i = exp(-x_i^2)
  // \prod_{k=1}^{i-1} z_k = 1 - \sum_{k=1}^{i-1} p_k
  // so z_i = 1 - p_i / (1 - \sum_{k=1}^{i-1} p_k)
  // x_i = sqrt(-log z_i)
  for (const auto& c: constraints.norm) {
    double pSum = 0;
    for (size_t n = 0; n + 1 < c.size(); ++n) {
      const string& cParam = c[n];
      const double p = WeightAlgebra::asDouble (seed.defs.at(cParam));
      const double z = 1 - p / (1 - pSum);
      const double val = sqrt (-log (z));
      pSum += p;
      gsl_vector_set (x, transformedParamIndex.at(cParam), val);
      LogThisAt(ParamTransformLogLevel,"Setting " << transformedParam[transformedParamIndex.at(cParam)] << " to " << val << endl);
    }
  }
  for (const auto& pParam: constraints.prob) {
      const double p = WeightAlgebra::asDouble (seed.defs.at(pParam));
      const double val = sqrt (-log (p));
      gsl_vector_set (x, transformedParamIndex.at(pParam), val);
      LogThisAt(ParamTransformLogLevel,"Setting " << transformedParam[transformedParamIndex.at(pParam)] << " to " << val << endl);
  }
  for (const auto& rParam: constraints.rate) {
      const double r = WeightAlgebra::asDouble (seed.defs.at(rParam));
      const double val = sqrt (r);
      gsl_vector_set (x, transformedParamIndex.at(rParam), val);
      LogThisAt(ParamTransformLogLevel,"Setting " << transformedParam[transformedParamIndex.at(rParam)] << " to " << val << endl);
  }

  const gsl_multimin_fdfminimizer_type *T = gsl_multimin_fdfminimizer_vector_bfgs2;
  gsl_multimin_fdfminimizer *s = gsl_multimin_fdfminimizer_alloc (T, func.n);

  gsl_multimin_fdfminimizer_set (s, &func, x, StepSize, LineSearchTolerance);

  size_t iter = 0;
  int status;
  do
    {
      iter++;
      status = gsl_multimin_fdfminimizer_iterate (s);

      const vguard<double> x_stl = gsl_vector_to_stl(s->x);
      LogThisAt (OptimizationParamsLogLevel, "iteration #" << iter << ": x=(" << to_string_join(x_stl) << ")" << endl);

      if (status)
        break;

      status = gsl_multimin_test_gradient (s->gradient, EpsilonAbsolute);
    }
  while (status == GSL_CONTINUE && iter < MaxIterations);

  const Params finalTransformedParams = gsl_vector_to_params (s->x, *this);
  Params finalParams = seed;
  for (const auto& pt: paramTransformDefs)
    finalParams.defs[pt.first] = WeightAlgebra::doubleConstant (WeightAlgebra::eval (pt.second, finalTransformedParams.defs));

  gsl_multimin_fdfminimizer_free (s);
  gsl_vector_free (x);

  return finalParams;
}
<|endoftext|>


<commit_before>#if defined(__APPLE__)

#include "apple_fsnotifier.h"
#include <codecvt>
#include <locale>
#include <string>

using namespace std;

// Utility wrapper to adapt locale-bound facets for wstring convert
// See https://en.cppreference.com/w/cpp/locale/codecvt
// TODO Understand what this does
template <class Facet>
struct deletable_facet : Facet {
    template <class... Args>
    deletable_facet(Args&&... args)
        : Facet(std::forward<Args>(args)...) {
    }
    ~deletable_facet() {
    }
};

WatchPoint::WatchPoint(Server* server, CFRunLoopRef runLoop, const u16string& path, long latencyInMillis) {
    CFStringRef cfPath = CFStringCreateWithCharacters(NULL, (UniChar*) path.c_str(), path.length());
    if (cfPath == nullptr) {
        throw FileWatcherException("Could not allocate CFString for path");
    }
    CFMutableArrayRef pathArray = CFArrayCreateMutable(NULL, 1, NULL);
    if (pathArray == NULL) {
        throw FileWatcherException("Could not allocate array to store roots to watch");
    }
    CFArrayAppendValue(pathArray, cfPath);

    FSEventStreamContext context = {
        0,                 // version, must be 0
        (void*) server,    // info
        NULL,              // retain
        NULL,              // release
        NULL               // copyDescription
    };
    FSEventStreamRef watcherStream = FSEventStreamCreate(
        NULL,
        &handleEventsCallback,
        &context,
        pathArray,
        kFSEventStreamEventIdSinceNow,
        latencyInMillis / 1000.0,
        kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagWatchRoot);
    CFRelease(pathArray);
    // TODO Why releasing this before the stream is created causes a crash?
    CFRelease(cfPath);
    if (watcherStream == NULL) {
        throw FileWatcherException("Could not create FSEventStreamCreate to track changes");
    }
    FSEventStreamScheduleWithRunLoop(watcherStream, runLoop, kCFRunLoopDefaultMode);
    FSEventStreamStart(watcherStream);
    this->watcherStream = watcherStream;
}

WatchPoint::~WatchPoint() {
    // Reading the Apple docs it seems we should call FSEventStreamFlushSync() here.
    // But doing so produces this log:
    //
    //     2020-02-17 23:02 java[50430] (FSEvents.framework) FSEventStreamFlushSync(): failed assertion '(SInt64)last_id > 0LL'
    //
    // According to this comment we should not use flush at all, and it's probably broken:
    // https://github.com/nodejs/node/issues/854#issuecomment-294892950
    // As the comment mentions, even Watchman doesn't flush:
    // https://github.com/facebook/watchman/blob/b397e00cf566f361282a456122eef4e909f26182/watcher/fsevents.cpp#L276-L285
    // FSEventStreamFlushSync(watcherStream);
    FSEventStreamStop(watcherStream);
    FSEventStreamInvalidate(watcherStream);
    FSEventStreamRelease(watcherStream);
}

//
// Server
//

Server::Server(JNIEnv* env, jobject watcherCallback, jobjectArray rootsToWatch, long latencyInMillis)
    : rootsToWatch(rootsToWatch)
    , latencyInMillis(latencyInMillis)
    , AbstractServer(env, watcherCallback) {
    // TODO Would be nice to inline this in AbstractServer(), but doing so results in pure virtual call
    startThread();
}

Server::~Server() {
    if (threadLoop != NULL) {
        CFRunLoopStop(threadLoop);
    }

    if (watcherThread.joinable()) {
        watcherThread.join();
    }
}

void Server::runLoop(JNIEnv* env, function<void(exception_ptr)> notifyStarted) {
    try {
        CFRunLoopRef threadLoop = CFRunLoopGetCurrent();
        this->threadLoop = threadLoop;

        int count = env->GetArrayLength(rootsToWatch);
        for (int i = 0; i < count; i++) {
            jstring javaPath = (jstring) env->GetObjectArrayElement(rootsToWatch, i);
            jsize javaPathLength = env->GetStringLength(javaPath);
            const jchar* javaPathChars = env->GetStringCritical(javaPath, nullptr);
            if (javaPathChars == NULL) {
                throw FileWatcherException("Could not get Java string character");
            }
            u16string path((char16_t*) javaPathChars, javaPathLength);
            env->ReleaseStringCritical(javaPath, javaPathChars);
            watchPoints.emplace_back(this, threadLoop, path, latencyInMillis);
        }

        notifyStarted(nullptr);
    } catch (...) {
        notifyStarted(current_exception());
    }

    CFRunLoopRun();
}

static void handleEventsCallback(
    ConstFSEventStreamRef streamRef,
    void* clientCallBackInfo,
    size_t numEvents,
    void* eventPaths,
    const FSEventStreamEventFlags eventFlags[],
    const FSEventStreamEventId eventIds[]) {
    Server* server = (Server*) clientCallBackInfo;
    server->handleEvents(numEvents, (char**) eventPaths, eventFlags, eventIds);
}

void Server::handleEvents(
    size_t numEvents,
    char** eventPaths,
    const FSEventStreamEventFlags eventFlags[],
    const FSEventStreamEventId eventIds[]) {
    JNIEnv* env = getThreadEnv();

    for (int i = 0; i < numEvents; i++) {
        handleEvent(env, eventPaths[i], eventFlags[i]);
    }
}

void Server::handleEvent(JNIEnv* env, char* path, FSEventStreamEventFlags flags) {
    log_fine(env, "Event flags: 0x%x for %s", flags, path);

    jint type;
    if (IS_SET(flags, kFSEventStreamEventFlagHistoryDone)) {
        return;
    } else if (IS_ANY_SET(flags,
                   kFSEventStreamEventFlagRootChanged
                       | kFSEventStreamEventFlagMount
                       | kFSEventStreamEventFlagUnmount
                       | kFSEventStreamEventFlagMustScanSubDirs)) {
        type = FILE_EVENT_INVALIDATE;
    } else if (IS_SET(flags, kFSEventStreamEventFlagItemRenamed)) {
        if (IS_SET(flags, kFSEventStreamEventFlagItemCreated)) {
            type = FILE_EVENT_REMOVED;
        } else {
            type = FILE_EVENT_CREATED;
        }
    } else if (IS_SET(flags, kFSEventStreamEventFlagItemModified)) {
        type = FILE_EVENT_MODIFIED;
    } else if (IS_SET(flags, kFSEventStreamEventFlagItemRemoved)) {
        type = FILE_EVENT_REMOVED;
    } else if (IS_ANY_SET(flags,
                   kFSEventStreamEventFlagItemInodeMetaMod    // file locked
                       | kFSEventStreamEventFlagItemFinderInfoMod
                       | kFSEventStreamEventFlagItemChangeOwner
                       | kFSEventStreamEventFlagItemXattrMod)) {
        type = FILE_EVENT_MODIFIED;
    } else if (IS_SET(flags, kFSEventStreamEventFlagItemCreated)) {
        type = FILE_EVENT_CREATED;
    } else {
        log_warning(env, "Unknown event 0x%x for %s", flags, path);
        type = FILE_EVENT_UNKNOWN;
    }

    log_fine(env, "Changed: %s %d", path, type);
    // TODO Can we extract this to some static state? It should only be used from the server thread
    wstring_convert<deletable_facet<codecvt<char16_t, char, mbstate_t>>, char16_t> conv16;
    u16string pathStr = conv16.from_bytes(path);
    reportChange(env, type, pathStr);
}

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_OsxFileEventFunctions_startWatching(JNIEnv* env, jclass target, jobjectArray paths, long latencyInMillis, jobject javaCallback) {
    Server* server;
    try {
        server = new Server(env, javaCallback, paths, latencyInMillis);
    } catch (const exception& e) {
        log_severe(env, "Caught exception: %s", e.what());
        jclass exceptionClass = env->FindClass("net/rubygrapefruit/platform/NativeException");
        assert(exceptionClass != NULL);
        jint ret = env->ThrowNew(exceptionClass, e.what());
        assert(ret == 0);
        return NULL;
    }

    jclass clsWatcher = env->FindClass("net/rubygrapefruit/platform/internal/jni/OsxFileEventFunctions$WatcherImpl");
    assert(clsWatcher != NULL);
    jmethodID constructor = env->GetMethodID(clsWatcher, "<init>", "(Ljava/lang/Object;)V");
    assert(constructor != NULL);
    return env->NewObject(clsWatcher, constructor, env->NewDirectByteBuffer(server, sizeof(server)));
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_OsxFileEventFunctions_stopWatching(JNIEnv* env, jclass target, jobject detailsObj) {
    Server* server = (Server*) env->GetDirectBufferAddress(detailsObj);
    assert(server != NULL);
    delete server;
}

#endif
<commit_msg>Unregister watch points first<commit_after>#if defined(__APPLE__)

#include "apple_fsnotifier.h"
#include <codecvt>
#include <locale>
#include <string>

using namespace std;

// Utility wrapper to adapt locale-bound facets for wstring convert
// See https://en.cppreference.com/w/cpp/locale/codecvt
// TODO Understand what this does
template <class Facet>
struct deletable_facet : Facet {
    template <class... Args>
    deletable_facet(Args&&... args)
        : Facet(std::forward<Args>(args)...) {
    }
    ~deletable_facet() {
    }
};

WatchPoint::WatchPoint(Server* server, CFRunLoopRef runLoop, const u16string& path, long latencyInMillis) {
    CFStringRef cfPath = CFStringCreateWithCharacters(NULL, (UniChar*) path.c_str(), path.length());
    if (cfPath == nullptr) {
        throw FileWatcherException("Could not allocate CFString for path");
    }
    CFMutableArrayRef pathArray = CFArrayCreateMutable(NULL, 1, NULL);
    if (pathArray == NULL) {
        throw FileWatcherException("Could not allocate array to store roots to watch");
    }
    CFArrayAppendValue(pathArray, cfPath);

    FSEventStreamContext context = {
        0,                 // version, must be 0
        (void*) server,    // info
        NULL,              // retain
        NULL,              // release
        NULL               // copyDescription
    };
    FSEventStreamRef watcherStream = FSEventStreamCreate(
        NULL,
        &handleEventsCallback,
        &context,
        pathArray,
        kFSEventStreamEventIdSinceNow,
        latencyInMillis / 1000.0,
        kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagWatchRoot);
    CFRelease(pathArray);
    // TODO Why releasing this before the stream is created causes a crash?
    CFRelease(cfPath);
    if (watcherStream == NULL) {
        throw FileWatcherException("Could not create FSEventStreamCreate to track changes");
    }
    FSEventStreamScheduleWithRunLoop(watcherStream, runLoop, kCFRunLoopDefaultMode);
    FSEventStreamStart(watcherStream);
    this->watcherStream = watcherStream;
}

WatchPoint::~WatchPoint() {
    // Reading the Apple docs it seems we should call FSEventStreamFlushSync() here.
    // But doing so produces this log:
    //
    //     2020-02-17 23:02 java[50430] (FSEvents.framework) FSEventStreamFlushSync(): failed assertion '(SInt64)last_id > 0LL'
    //
    // According to this comment we should not use flush at all, and it's probably broken:
    // https://github.com/nodejs/node/issues/854#issuecomment-294892950
    // As the comment mentions, even Watchman doesn't flush:
    // https://github.com/facebook/watchman/blob/b397e00cf566f361282a456122eef4e909f26182/watcher/fsevents.cpp#L276-L285
    // FSEventStreamFlushSync(watcherStream);
    FSEventStreamStop(watcherStream);
    FSEventStreamInvalidate(watcherStream);
    FSEventStreamRelease(watcherStream);
}

//
// Server
//

Server::Server(JNIEnv* env, jobject watcherCallback, jobjectArray rootsToWatch, long latencyInMillis)
    : rootsToWatch(rootsToWatch)
    , latencyInMillis(latencyInMillis)
    , AbstractServer(env, watcherCallback) {
    // TODO Would be nice to inline this in AbstractServer(), but doing so results in pure virtual call
    startThread();
}

Server::~Server() {
    watchPoints.clear();

    if (threadLoop != NULL && CFRunLoopIsWaiting(threadLoop)) {
        CFRunLoopStop(threadLoop);
    }

    if (watcherThread.joinable()) {
        watcherThread.join();
    }
}

void Server::runLoop(JNIEnv* env, function<void(exception_ptr)> notifyStarted) {
    try {
        CFRunLoopRef threadLoop = CFRunLoopGetCurrent();
        this->threadLoop = threadLoop;

        int count = env->GetArrayLength(rootsToWatch);
        for (int i = 0; i < count; i++) {
            jstring javaPath = (jstring) env->GetObjectArrayElement(rootsToWatch, i);
            jsize javaPathLength = env->GetStringLength(javaPath);
            const jchar* javaPathChars = env->GetStringCritical(javaPath, nullptr);
            if (javaPathChars == NULL) {
                throw FileWatcherException("Could not get Java string character");
            }
            u16string path((char16_t*) javaPathChars, javaPathLength);
            env->ReleaseStringCritical(javaPath, javaPathChars);
            watchPoints.emplace_back(this, threadLoop, path, latencyInMillis);
        }

        notifyStarted(nullptr);
    } catch (...) {
        notifyStarted(current_exception());
    }

    CFRunLoopRun();
}

static void handleEventsCallback(
    ConstFSEventStreamRef streamRef,
    void* clientCallBackInfo,
    size_t numEvents,
    void* eventPaths,
    const FSEventStreamEventFlags eventFlags[],
    const FSEventStreamEventId eventIds[]) {
    Server* server = (Server*) clientCallBackInfo;
    server->handleEvents(numEvents, (char**) eventPaths, eventFlags, eventIds);
}

void Server::handleEvents(
    size_t numEvents,
    char** eventPaths,
    const FSEventStreamEventFlags eventFlags[],
    const FSEventStreamEventId eventIds[]) {
    JNIEnv* env = getThreadEnv();

    for (int i = 0; i < numEvents; i++) {
        handleEvent(env, eventPaths[i], eventFlags[i]);
    }
}

void Server::handleEvent(JNIEnv* env, char* path, FSEventStreamEventFlags flags) {
    log_fine(env, "Event flags: 0x%x for %s", flags, path);

    jint type;
    if (IS_SET(flags, kFSEventStreamEventFlagHistoryDone)) {
        return;
    } else if (IS_ANY_SET(flags,
                   kFSEventStreamEventFlagRootChanged
                       | kFSEventStreamEventFlagMount
                       | kFSEventStreamEventFlagUnmount
                       | kFSEventStreamEventFlagMustScanSubDirs)) {
        type = FILE_EVENT_INVALIDATE;
    } else if (IS_SET(flags, kFSEventStreamEventFlagItemRenamed)) {
        if (IS_SET(flags, kFSEventStreamEventFlagItemCreated)) {
            type = FILE_EVENT_REMOVED;
        } else {
            type = FILE_EVENT_CREATED;
        }
    } else if (IS_SET(flags, kFSEventStreamEventFlagItemModified)) {
        type = FILE_EVENT_MODIFIED;
    } else if (IS_SET(flags, kFSEventStreamEventFlagItemRemoved)) {
        type = FILE_EVENT_REMOVED;
    } else if (IS_ANY_SET(flags,
                   kFSEventStreamEventFlagItemInodeMetaMod    // file locked
                       | kFSEventStreamEventFlagItemFinderInfoMod
                       | kFSEventStreamEventFlagItemChangeOwner
                       | kFSEventStreamEventFlagItemXattrMod)) {
        type = FILE_EVENT_MODIFIED;
    } else if (IS_SET(flags, kFSEventStreamEventFlagItemCreated)) {
        type = FILE_EVENT_CREATED;
    } else {
        log_warning(env, "Unknown event 0x%x for %s", flags, path);
        type = FILE_EVENT_UNKNOWN;
    }

    log_fine(env, "Changed: %s %d", path, type);
    // TODO Can we extract this to some static state? It should only be used from the server thread
    wstring_convert<deletable_facet<codecvt<char16_t, char, mbstate_t>>, char16_t> conv16;
    u16string pathStr = conv16.from_bytes(path);
    reportChange(env, type, pathStr);
}

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_OsxFileEventFunctions_startWatching(JNIEnv* env, jclass target, jobjectArray paths, long latencyInMillis, jobject javaCallback) {
    Server* server;
    try {
        server = new Server(env, javaCallback, paths, latencyInMillis);
    } catch (const exception& e) {
        log_severe(env, "Caught exception: %s", e.what());
        jclass exceptionClass = env->FindClass("net/rubygrapefruit/platform/NativeException");
        assert(exceptionClass != NULL);
        jint ret = env->ThrowNew(exceptionClass, e.what());
        assert(ret == 0);
        return NULL;
    }

    jclass clsWatcher = env->FindClass("net/rubygrapefruit/platform/internal/jni/OsxFileEventFunctions$WatcherImpl");
    assert(clsWatcher != NULL);
    jmethodID constructor = env->GetMethodID(clsWatcher, "<init>", "(Ljava/lang/Object;)V");
    assert(constructor != NULL);
    return env->NewObject(clsWatcher, constructor, env->NewDirectByteBuffer(server, sizeof(server)));
}

JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_OsxFileEventFunctions_stopWatching(JNIEnv* env, jclass target, jobject detailsObj) {
    Server* server = (Server*) env->GetDirectBufferAddress(detailsObj);
    assert(server != NULL);
    delete server;
}

#endif
<|endoftext|>


<commit_before>#include "file-browser-manager.h"

#include "seafile-applet.h"
#include "ui/main-window.h"
#include <QApplication>
#include <QDesktopWidget>

#include "file-browser-dialog.h"

namespace {
}

FileBrowserManager *FileBrowserManager::instance_ = NULL;

FileBrowserDialog *FileBrowserManager::openOrActivateDialog(const Account &account, const ServerRepo &repo)
{
    FileBrowserDialog *dialog = getDialog(account, repo.id);
    if (dialog == NULL) {
        dialog = new FileBrowserDialog(account, repo, seafApplet->mainWindow());
        QRect screen = QApplication::desktop()->screenGeometry();
        dialog->setAttribute(Qt::WA_DeleteOnClose, true);
        dialog->show();
        dialog->move(screen.center() - dialog->rect().center());
        dialogs_.push_back(dialog);
    }
    dialog->raise();
    return dialog;
}

FileBrowserDialog *FileBrowserManager::getDialog(const Account &account, const QString &repo_id)
{
    // remove all QWeakPointer which is NULL alreadly;
    for (int i = dialogs_.size() - 1; i >= 0; i--)
        if (dialogs_[i].isNull())
          dialogs_.removeAt(i);

    // search and find if dialog registered
    for (int i = 0; i < dialogs_.size() ; i++)
        if (dialogs_[i].data()->account_ == account &&
            dialogs_[i].data()->repo_.id == repo_id) {
            return dialogs_[i].data();
        }
    // not found
    return NULL;
}

<commit_msg>fix a bug where the program forgets to activate the filebrowserdialog<commit_after>#include "file-browser-manager.h"

#include "seafile-applet.h"
#include "ui/main-window.h"
#include <QApplication>
#include <QDesktopWidget>

#include "file-browser-dialog.h"

namespace {
}

FileBrowserManager *FileBrowserManager::instance_ = NULL;

FileBrowserDialog *FileBrowserManager::openOrActivateDialog(const Account &account, const ServerRepo &repo)
{
    FileBrowserDialog *dialog = getDialog(account, repo.id);
    if (dialog == NULL) {
        dialog = new FileBrowserDialog(account, repo, seafApplet->mainWindow());
        QRect screen = QApplication::desktop()->screenGeometry();
        dialog->setAttribute(Qt::WA_DeleteOnClose, true);
        dialog->show();
        dialog->move(screen.center() - dialog->rect().center());
        dialogs_.push_back(dialog);
    }
    dialog->raise();
    dialog->activateWindow();
    return dialog;
}

FileBrowserDialog *FileBrowserManager::getDialog(const Account &account, const QString &repo_id)
{
    // remove all QWeakPointer which is NULL alreadly;
    for (int i = dialogs_.size() - 1; i >= 0; i--)
        if (dialogs_[i].isNull())
          dialogs_.removeAt(i);

    // search and find if dialog registered
    for (int i = 0; i < dialogs_.size() ; i++)
        if (dialogs_[i].data()->account_ == account &&
            dialogs_[i].data()->repo_.id == repo_id) {
            return dialogs_[i].data();
        }
    // not found
    return NULL;
}

<|endoftext|>


<commit_before>#include "UIElement.h"
#include "UIController.h"

using namespace ci;
using namespace ci::app;
using namespace std;
using namespace MinimalUI;

int UIElement::DEFAULT_HEIGHT = 36;

UIElement::UIElement( UIController *aUIController, const std::string &aName, const std::string &aParamString )
	: mParent( aUIController ), mName( aName ), mParams( ci::JsonTree( aParamString ) )
{
	// attach mouse callbacks
	mCbMouseDown = mParent->getWindow()->getSignalMouseDown().connect( mParent->getDepth(), std::bind( &UIElement::mouseDown, this, std::placeholders::_1 ) );
	mCbMouseUp = mParent->getWindow()->getSignalMouseUp().connect( mParent->getDepth(), std::bind( &UIElement::mouseUp, this, std::placeholders::_1 ) );
	mCbMouseDrag = mParent->getWindow()->getSignalMouseDrag().connect( mParent->getDepth(), std::bind( &UIElement::mouseDrag, this, std::placeholders::_1 ) );

	// initialize some variables
	mActive = false;

	// parse params that are common to all UIElements
	mGroup = hasParam( "group" ) ? getParam<string>( "group" ) : "";
	mIcon = hasParam( "icon" ) ? getParam<bool>( "icon" ) : false;
	mLocked = hasParam( "locked" ) ? getParam<bool>( "locked" ) : false;
	mClear = hasParam( "clear" ) ? getParam<bool>( "clear" ) : true;

	// JSON doesn't support hex literals...
	// TODO: there's got to be a better way to do this, esp considering I need hex versions of default values set above...
	std::stringstream str;
	string strValue;
	uint32_t hexValue;
	if ( hasParam( "nameColor" ) )
	{
		strValue = getParam<string>( "nameColor" );
		str << strValue;
		str >> std::hex >> hexValue;
		mNameColor = ColorA::hexA( hexValue );
	}
	else
	{
		mNameColor = UIController::DEFAULT_NAME_COLOR;
	}
	if ( hasParam( "backgroundColor" ) )
	{
		strValue = getParam<string>( "backgroundColor" );
		str.clear();
		str << strValue;
		str >> std::hex >> hexValue;
		mBackgroundColor = ColorA::hexA( hexValue );
	}
	else
	{
		mBackgroundColor = UIController::DEFAULT_BACKGROUND_COLOR;
	}

	if ( hasParam( "justification" ) ) {
		string justification = getParam<string>( "justification" );
		if ( justification == "left" ) {
			mAlignment = TextBox::LEFT;
		} else if ( justification == "right" ) {
			mAlignment = TextBox::RIGHT;
		} else {
			mAlignment = TextBox::CENTER;
		}
	} else {
		mAlignment = TextBox::CENTER;
	}

	if ( hasParam( "style" ) ) {
		mFont = mParent->getFont( getParam<string>( "style" ) );
	} else if ( mIcon ) {
		mFont = mParent->getFont( "icon" );
	} else {
		mFont = mParent->getFont( "label" );
	}

	if ( hasParam( "backgroundImage" ) ) {
		mBackgroundTexture = gl::Texture( loadImage( loadAsset( getParam<string>( "backgroundImage" ) ) ) );
	}
}

void UIElement::offsetInsertPosition()
{
	if ( mClear ) {
		mParent->resetInsertPosition( mBounds.getHeight() + UIController::DEFAULT_MARGIN_SMALL );
	} else {
		mParent->offsetInsertPosition( Vec2i( mBounds.getWidth() + UIController::DEFAULT_MARGIN_SMALL, 0 ) );
	}
}

void UIElement::setPositionAndBounds()
{
	mPosition = mParent->getInsertPosition();
	mBounds = Area( mPosition, mPosition + mSize );

	// offset the insert position
	offsetInsertPosition();
}

void UIElement::mouseDown( MouseEvent &event )
{
	if ( mParent->isVisible() && !mLocked && mBounds.contains( event.getPos() - mParent->getPosition() ) ) {
		mActive = true;
		handleMouseDown( event.getPos() - mParent->getPosition(), event.isRight() );
		event.setHandled();
	}
}

void UIElement::mouseUp( MouseEvent &event )
{
	if ( mParent->isVisible() && !mLocked && mActive ) {
		mActive = false;
		handleMouseUp( event.getPos() - mParent->getPosition() );
		//		event.setHandled(); // maybe?
	}
}

void UIElement::mouseDrag( MouseEvent &event )
{
	if ( mParent->isVisible() && !mLocked && mActive ) {
		handleMouseDrag( event.getPos() - mParent->getPosition() );
	}
}

void UIElement::renderNameTexture()
{
	TextBox textBox = TextBox().size( Vec2i( mSize.x, TextBox::GROW ) ).font( mFont ).color( mNameColor ).alignment( mAlignment ).text( mName );
	mNameTexture = textBox.render();
}

void UIElement::drawLabel()
{
	gl::pushMatrices();
	if ( mBackgroundTexture ) gl::draw( mBackgroundTexture, mBounds );
	// intentional truncation
	Vec2i offset = mBounds.getCenter() - mNameTexture.getSize() / 2;
	gl::translate( offset );
	gl::color( Color::white() );
	gl::draw( mNameTexture );
	gl::popMatrices();
}<commit_msg>set white color before rendering background, was too dull<commit_after>#include "UIElement.h"
#include "UIController.h"

using namespace ci;
using namespace ci::app;
using namespace std;
using namespace MinimalUI;

int UIElement::DEFAULT_HEIGHT = 36;

UIElement::UIElement( UIController *aUIController, const std::string &aName, const std::string &aParamString )
	: mParent( aUIController ), mName( aName ), mParams( ci::JsonTree( aParamString ) )
{
	// attach mouse callbacks
	mCbMouseDown = mParent->getWindow()->getSignalMouseDown().connect( mParent->getDepth(), std::bind( &UIElement::mouseDown, this, std::placeholders::_1 ) );
	mCbMouseUp = mParent->getWindow()->getSignalMouseUp().connect( mParent->getDepth(), std::bind( &UIElement::mouseUp, this, std::placeholders::_1 ) );
	mCbMouseDrag = mParent->getWindow()->getSignalMouseDrag().connect( mParent->getDepth(), std::bind( &UIElement::mouseDrag, this, std::placeholders::_1 ) );

	// initialize some variables
	mActive = false;

	// parse params that are common to all UIElements
	mGroup = hasParam( "group" ) ? getParam<string>( "group" ) : "";
	mIcon = hasParam( "icon" ) ? getParam<bool>( "icon" ) : false;
	mLocked = hasParam( "locked" ) ? getParam<bool>( "locked" ) : false;
	mClear = hasParam( "clear" ) ? getParam<bool>( "clear" ) : true;

	// JSON doesn't support hex literals...
	// TODO: there's got to be a better way to do this, esp considering I need hex versions of default values set above...
	std::stringstream str;
	string strValue;
	uint32_t hexValue;
	if ( hasParam( "nameColor" ) )
	{
		strValue = getParam<string>( "nameColor" );
		str << strValue;
		str >> std::hex >> hexValue;
		mNameColor = ColorA::hexA( hexValue );
	}
	else
	{
		mNameColor = UIController::DEFAULT_NAME_COLOR;
	}
	if ( hasParam( "backgroundColor" ) )
	{
		strValue = getParam<string>( "backgroundColor" );
		str.clear();
		str << strValue;
		str >> std::hex >> hexValue;
		mBackgroundColor = ColorA::hexA( hexValue );
	}
	else
	{
		mBackgroundColor = UIController::DEFAULT_BACKGROUND_COLOR;
	}

	if ( hasParam( "justification" ) ) {
		string justification = getParam<string>( "justification" );
		if ( justification == "left" ) {
			mAlignment = TextBox::LEFT;
		} else if ( justification == "right" ) {
			mAlignment = TextBox::RIGHT;
		} else {
			mAlignment = TextBox::CENTER;
		}
	} else {
		mAlignment = TextBox::CENTER;
	}

	if ( hasParam( "style" ) ) {
		mFont = mParent->getFont( getParam<string>( "style" ) );
	} else if ( mIcon ) {
		mFont = mParent->getFont( "icon" );
	} else {
		mFont = mParent->getFont( "label" );
	}

	if ( hasParam( "backgroundImage" ) ) {
		mBackgroundTexture = gl::Texture( loadImage( loadAsset( getParam<string>( "backgroundImage" ) ) ) );
	}
}

void UIElement::offsetInsertPosition()
{
	if ( mClear ) {
		mParent->resetInsertPosition( mBounds.getHeight() + UIController::DEFAULT_MARGIN_SMALL );
	} else {
		mParent->offsetInsertPosition( Vec2i( mBounds.getWidth() + UIController::DEFAULT_MARGIN_SMALL, 0 ) );
	}
}

void UIElement::setPositionAndBounds()
{
	mPosition = mParent->getInsertPosition();
	mBounds = Area( mPosition, mPosition + mSize );

	// offset the insert position
	offsetInsertPosition();
}

void UIElement::mouseDown( MouseEvent &event )
{
	if ( mParent->isVisible() && !mLocked && mBounds.contains( event.getPos() - mParent->getPosition() ) ) {
		mActive = true;
		handleMouseDown( event.getPos() - mParent->getPosition(), event.isRight() );
		event.setHandled();
	}
}

void UIElement::mouseUp( MouseEvent &event )
{
	if ( mParent->isVisible() && !mLocked && mActive ) {
		mActive = false;
		handleMouseUp( event.getPos() - mParent->getPosition() );
		//		event.setHandled(); // maybe?
	}
}

void UIElement::mouseDrag( MouseEvent &event )
{
	if ( mParent->isVisible() && !mLocked && mActive ) {
		handleMouseDrag( event.getPos() - mParent->getPosition() );
	}
}

void UIElement::renderNameTexture()
{
	TextBox textBox = TextBox().size( Vec2i( mSize.x, TextBox::GROW ) ).font( mFont ).color( mNameColor ).alignment( mAlignment ).text( mName );
	mNameTexture = textBox.render();
}

void UIElement::drawLabel()
{
	gl::pushMatrices();
	gl::color( Color::white() );
	if ( mBackgroundTexture ) gl::draw( mBackgroundTexture, mBounds );
	// intentional truncation
	Vec2i offset = mBounds.getCenter() - mNameTexture.getSize() / 2;
	gl::translate( offset );
	gl::draw( mNameTexture );
	gl::popMatrices();
}<|endoftext|>


<commit_before>#ifndef __NBA_NODELOCALSTORAGE_HH__
#define __NBA_NODELOCALSTORAGE_HH__

#include "log.hh"

#include <rte_memory.h>
#include <rte_malloc.h>
#include <rte_rwlock.h>
#include <rte_spinlock.h>

#include <unordered_map>
#include <string>

namespace nba {

class NodeLocalStorage {
    /**
     * NodeLocalStorage is a node-version of thread local storage.
     * Elements can use this to store per-node information, such as
     * routing tables.  The storage unit is mapped with a string key
     * for convenience.  The keys from different elements must not
     * overlap as this implementation does not have separate namespaces
     * for each element instances.
     *
     * The elements must use the methods of this class only on
     * initialization steps, and must NOT use them in the data-path.
     * They must keep the pointers and rwlocks retrieved during
     * initialization step to use them in the data-path.
     * It is optional for elements to use rwlocks.  It is recommended
     * to use them only if the storage needs to be updated inside the
     * data-path.
     * TODO: add multiple types of storage, such as RCU-backed ones.
     *
     * alloc() method must be called inside initialize_per_node() method
     * of elements, and get_alloc() / get_rwlock() methods should be
     * called inside configure() method which is called per thread ( =
     * per element instance).
     */
public:
    NodeLocalStorage(unsigned node_id)
    {
        _node_id = node_id;
        for (int i = 0; i < NBA_MAX_NODELOCALSTORAGE_ENTRIES; i++) {
            _pointers[i] = NULL;
            //_rwlocks[i] = NULL;
        }
        rte_spinlock_init(&_node_lock);
    }

    virtual ~NodeLocalStorage()
    {
        // TODO: free all existing entries.
    }

    int alloc(const char *key, size_t size)
    {
        rte_spinlock_lock(&_node_lock);
        size_t kid = _keys.size();
        assert(kid < NBA_MAX_NODELOCALSTORAGE_ENTRIES);
        _keys.insert(std::pair<std::string, int>(key, kid));

        void *ptr = rte_malloc_socket("nls_alloc", size, 64, _node_id);
        //void *ptr = new char*[size];
        assert(ptr != NULL);
        memset(ptr, 0xcd, size);
        size_t real_size = 0;
        //assert(0 == rte_malloc_validate(ptr, &real_size));
        _pointers[kid] = ptr;
        RTE_LOG(DEBUG, ELEM, "NLS[%u]: malloc req size %'lu bytes, real size %'lu bytes\n", _node_id, size, real_size);

        //rte_rwlock_t *rwlock = (rte_rwlock_t *) rte_malloc_socket("nls_lock", sizeof(rte_rwlock_t), 64, _node_id);
        //assert(rwlock != NULL);
        //rte_rwlock_init(rwlock);
        //_rwlocks[kid] = rwlock;

        rte_spinlock_unlock(&_node_lock);
        return kid;
    }

    void* get_alloc(const char *key)
    {
        rte_spinlock_lock(&_node_lock);
        assert(_keys.find(key) != _keys.end());
        int kid = _keys[key];
        void *ptr = _pointers[kid];
        rte_spinlock_unlock(&_node_lock);
        return ptr;
    }

    rte_rwlock_t *get_rwlock(const char *key)
    {
        rte_spinlock_lock(&_node_lock);
        assert(_keys.find(key) != _keys.end());
        int kid = _keys[key];
        //rte_rwlock_t *lock = _rwlocks[kid];
        rte_spinlock_unlock(&_node_lock);
        //return lock;
        return nullptr;
    }

    void free(const char *key)
    {
        rte_spinlock_lock(&_node_lock);
        assert(_keys.find(key) != _keys.end());
        int kid = _keys[key];
        void *ptr = _pointers[kid];
        rte_free(ptr);
        //delete (char*)ptr;
        //rte_rwlock_t *rwlock = _rwlocks[kid];
        //rte_free(rwlock);
        _pointers[kid] = NULL;
        rte_spinlock_unlock(&_node_lock);
        // TODO: remove entry from _pointers, _rwlocks, and _keys.
        // But we do not implement it because usually node-local
        // storage is alloccated-once and used-forever.
    }

protected:
    unsigned _node_id;
    //rte_rwlock_t *_rwlocks[NBA_MAX_NODELOCALSTORAGE_ENTRIES];
    void *_pointers[NBA_MAX_NODELOCALSTORAGE_ENTRIES];
    std::unordered_map<std::string, int> _keys;
    rte_spinlock_t _node_lock;
};
}

#endif

// vim: ts=8 sts=4 sw=4 et
<commit_msg>Explicit cache line size in lib/nodelocalstorage.<commit_after>#ifndef __NBA_NODELOCALSTORAGE_HH__
#define __NBA_NODELOCALSTORAGE_HH__

#include "log.hh"

#include <rte_memory.h>
#include <rte_malloc.h>
#include <rte_rwlock.h>
#include <rte_spinlock.h>

#include <unordered_map>
#include <string>

namespace nba {

class NodeLocalStorage {
    /**
     * NodeLocalStorage is a node-version of thread local storage.
     * Elements can use this to store per-node information, such as
     * routing tables.  The storage unit is mapped with a string key
     * for convenience.  The keys from different elements must not
     * overlap as this implementation does not have separate namespaces
     * for each element instances.
     *
     * The elements must use the methods of this class only on
     * initialization steps, and must NOT use them in the data-path.
     * They must keep the pointers and rwlocks retrieved during
     * initialization step to use them in the data-path.
     * It is optional for elements to use rwlocks.  It is recommended
     * to use them only if the storage needs to be updated inside the
     * data-path.
     * TODO: add multiple types of storage, such as RCU-backed ones.
     *
     * alloc() method must be called inside initialize_per_node() method
     * of elements, and get_alloc() / get_rwlock() methods should be
     * called inside configure() method which is called per thread ( =
     * per element instance).
     */
public:
    NodeLocalStorage(unsigned node_id)
    {
        _node_id = node_id;
        for (int i = 0; i < NBA_MAX_NODELOCALSTORAGE_ENTRIES; i++) {
            _pointers[i] = NULL;
            //_rwlocks[i] = NULL;
        }
        rte_spinlock_init(&_node_lock);
    }

    virtual ~NodeLocalStorage()
    {
        // TODO: free all existing entries.
    }

    int alloc(const char *key, size_t size)
    {
        rte_spinlock_lock(&_node_lock);
        size_t kid = _keys.size();
        assert(kid < NBA_MAX_NODELOCALSTORAGE_ENTRIES);
        _keys.insert(std::pair<std::string, int>(key, kid));

        void *ptr = rte_malloc_socket("nls_alloc", size, CACHE_LINE_SIZE, _node_id);
        //void *ptr = new char*[size];
        assert(ptr != NULL);
        memset(ptr, 0xcd, size);
        size_t real_size = 0;
        //assert(0 == rte_malloc_validate(ptr, &real_size));
        _pointers[kid] = ptr;
        RTE_LOG(DEBUG, ELEM, "NLS[%u]: malloc req size %'lu bytes, real size %'lu bytes\n", _node_id, size, real_size);

        //rte_rwlock_t *rwlock = (rte_rwlock_t *) rte_malloc_socket("nls_lock", sizeof(rte_rwlock_t), 64, _node_id);
        //assert(rwlock != NULL);
        //rte_rwlock_init(rwlock);
        //_rwlocks[kid] = rwlock;

        rte_spinlock_unlock(&_node_lock);
        return kid;
    }

    void* get_alloc(const char *key)
    {
        rte_spinlock_lock(&_node_lock);
        assert(_keys.find(key) != _keys.end());
        int kid = _keys[key];
        void *ptr = _pointers[kid];
        rte_spinlock_unlock(&_node_lock);
        return ptr;
    }

    rte_rwlock_t *get_rwlock(const char *key)
    {
        rte_spinlock_lock(&_node_lock);
        assert(_keys.find(key) != _keys.end());
        int kid = _keys[key];
        //rte_rwlock_t *lock = _rwlocks[kid];
        rte_spinlock_unlock(&_node_lock);
        //return lock;
        return nullptr;
    }

    void free(const char *key)
    {
        rte_spinlock_lock(&_node_lock);
        assert(_keys.find(key) != _keys.end());
        int kid = _keys[key];
        void *ptr = _pointers[kid];
        rte_free(ptr);
        //delete (char*)ptr;
        //rte_rwlock_t *rwlock = _rwlocks[kid];
        //rte_free(rwlock);
        _pointers[kid] = NULL;
        rte_spinlock_unlock(&_node_lock);
        // TODO: remove entry from _pointers, _rwlocks, and _keys.
        // But we do not implement it because usually node-local
        // storage is alloccated-once and used-forever.
    }

protected:
    unsigned _node_id;
    //rte_rwlock_t *_rwlocks[NBA_MAX_NODELOCALSTORAGE_ENTRIES];
    void *_pointers[NBA_MAX_NODELOCALSTORAGE_ENTRIES];
    std::unordered_map<std::string, int> _keys;
    rte_spinlock_t _node_lock;
};
}

#endif

// vim: ts=8 sts=4 sw=4 et
<|endoftext|>


<commit_before>/*
 * \file Loads the CPP Core Guidelines support libraries and configures it's behavior.
 */

#ifndef LIB_GSL_HPP
#define LIB_GSL_HPP

#define GSL_THROW_ON_CONTRACT_VIOLATION //throw exception when contract is violated (instead of std::terminate)!

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-tautological-constant-compare"
#include <gsl/gsl_assert>
#include <gsl/gsl_byte>
#include <gsl/span>
#pragma GCC diagnostic pop

#include "protobuf.hpp"

namespace Molch {
	inline unsigned char* byte_to_uchar(std::byte* byte) {
		return reinterpret_cast<unsigned char*>(byte); //NOLINT
	}

	inline const unsigned char* byte_to_uchar(const std::byte* byte) {
		return reinterpret_cast<const unsigned char*>(byte); //NOLINT
	}

	constexpr unsigned char byte_to_uchar(const std::byte byte) {
		return static_cast<unsigned char>(byte);
	}

	inline std::byte* uchar_to_byte(unsigned char* character) {
		return reinterpret_cast<std::byte*>(character); //NOLINT
	}

	inline const std::byte* uchar_to_byte(const unsigned char* character) {
		return reinterpret_cast<const std::byte*>(character); //NOLINT
	}

	constexpr std::byte uchar_to_byte(const unsigned char character) {
		return static_cast<std::byte>(character);
	}

	inline char* byte_to_char(std::byte* pointer) noexcept {
		return reinterpret_cast<char*>(pointer); //NOLINT
	}

	inline const char* byte_to_char(const std::byte* pointer) noexcept {
		return reinterpret_cast<const char*>(pointer); //NOLINT
	}

	inline const std::byte* char_to_byte(const char* pointer) noexcept {
		return reinterpret_cast<const std::byte*>(pointer); //NOLINT
	}

	inline std::byte* char_to_byte(char* pointer) noexcept {
		return reinterpret_cast<std::byte*>(pointer); //NOLINT
	}

	template <class ElementType,std::ptrdiff_t length = -1>
	class span : public gsl::span<ElementType,length> {
	private:
		using base_class = gsl::span<ElementType,length>;

	public:
		//make base class constructors available
		using base_class::base_class;

		constexpr span() : base_class{nullptr, static_cast<ptrdiff_t>(0)} {}

		constexpr span(ElementType* pointer, size_t count)
			: base_class{pointer, gsl::narrow<ptrdiff_t>(count)} {}

		constexpr span(gsl::span<ElementType> gsl_span) : base_class{gsl_span} {}

		constexpr size_t size() const {
			return gsl::narrow<size_t>(this->base_class::size());
		}

		span(const ProtobufCBinaryData data) : base_class{uchar_to_byte(data.data), gsl::narrow<ptrdiff_t>(data.len)} {}
	};
}

#endif /* LIB_GSL_HPP */
<commit_msg>Molch::span: Add subspan overload with size_t<commit_after>/*
 * \file Loads the CPP Core Guidelines support libraries and configures it's behavior.
 */

#ifndef LIB_GSL_HPP
#define LIB_GSL_HPP

#define GSL_THROW_ON_CONTRACT_VIOLATION //throw exception when contract is violated (instead of std::terminate)!

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wno-tautological-constant-compare"
#include <gsl/gsl_assert>
#include <gsl/gsl_byte>
#include <gsl/span>
#pragma GCC diagnostic pop

#include "protobuf.hpp"

namespace Molch {
	inline unsigned char* byte_to_uchar(std::byte* byte) {
		return reinterpret_cast<unsigned char*>(byte); //NOLINT
	}

	inline const unsigned char* byte_to_uchar(const std::byte* byte) {
		return reinterpret_cast<const unsigned char*>(byte); //NOLINT
	}

	constexpr unsigned char byte_to_uchar(const std::byte byte) {
		return static_cast<unsigned char>(byte);
	}

	inline std::byte* uchar_to_byte(unsigned char* character) {
		return reinterpret_cast<std::byte*>(character); //NOLINT
	}

	inline const std::byte* uchar_to_byte(const unsigned char* character) {
		return reinterpret_cast<const std::byte*>(character); //NOLINT
	}

	constexpr std::byte uchar_to_byte(const unsigned char character) {
		return static_cast<std::byte>(character);
	}

	inline char* byte_to_char(std::byte* pointer) noexcept {
		return reinterpret_cast<char*>(pointer); //NOLINT
	}

	inline const char* byte_to_char(const std::byte* pointer) noexcept {
		return reinterpret_cast<const char*>(pointer); //NOLINT
	}

	inline const std::byte* char_to_byte(const char* pointer) noexcept {
		return reinterpret_cast<const std::byte*>(pointer); //NOLINT
	}

	inline std::byte* char_to_byte(char* pointer) noexcept {
		return reinterpret_cast<std::byte*>(pointer); //NOLINT
	}

	template <class ElementType,std::ptrdiff_t length = -1>
	class span : public gsl::span<ElementType,length> {
	private:
		using base_class = gsl::span<ElementType,length>;

	public:
		//make base class constructors available
		using base_class::base_class;

		constexpr span() : base_class{nullptr, static_cast<ptrdiff_t>(0)} {}

		constexpr span(ElementType* pointer, size_t count)
			: base_class{pointer, gsl::narrow<ptrdiff_t>(count)} {}

		constexpr span(gsl::span<ElementType> gsl_span) : base_class{gsl_span} {}

		constexpr size_t size() const {
			return gsl::narrow<size_t>(this->base_class::size());
		}

		span(const ProtobufCBinaryData data) : base_class{uchar_to_byte(data.data), gsl::narrow<ptrdiff_t>(data.len)} {}

		const span subspan(size_t offset, size_t size) const {
			const auto signed_offset = gsl::narrow_cast<ptrdiff_t>(offset);
			const auto signed_size = gsl::narrow_cast<ptrdiff_t>(size);
			return this->base_class::subspan(signed_offset, signed_size);
		}

		const span subspan(size_t offset) const {
			const auto signed_offset = gsl::narrow_cast<ptrdiff_t>(offset);
			return this->base_class::subspan(signed_offset);
		}
	};
}

#endif /* LIB_GSL_HPP */
<|endoftext|>


<commit_before>
#include <unistd.h>
#include <sstream>

#include "tclap/CmdLine.h"
#include "beanstalk.hpp"
#include "alpr.h"
#include "openalpr/simpleini/simpleini.h"
#include "openalpr/cjson.h"
#include "support/tinythread.h"
#include "videobuffer.h"
#include "uuid.h"
#include <curl/curl.h>

#include <log4cplus/logger.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>
#include <log4cplus/consoleappender.h>
#include <log4cplus/fileappender.h>

// prototypes
void streamRecognitionThread(void* arg);
bool writeToQueue(std::string jsonResult);
bool uploadPost(std::string url, std::string data);
void dataUploadThread(void* arg);

// Constants
const std::string DEFAULT_LOG_FILE_PATH="/var/log/openalpr.log";
const std::string WTS_CONFIG_FILE_PATH="/etc/openalpr/wts.conf";

const std::string BEANSTALK_QUEUE_HOST="127.0.0.1";
const int BEANSTALK_PORT=11300;
const std::string BEANSTALK_TUBE_NAME="alprd";

struct CaptureThreadData
{
  std::string stream_url;
  std::string site_id;
  int camera_id;

  std::string config_file;
  std::string country_code;
  std::string output_image_folder;
};

struct UploadThreadData
{
  std::string upload_url;
};

bool daemon_active;

static log4cplus::Logger logger;

int main( int argc, const char** argv )
{
  daemon_active = true;

  bool noDaemon = false;
  std::string logFile;
  int topn;

  std::string configFile;
  std::string country;

  TCLAP::CmdLine cmd("OpenAlpr Daemon", ' ', Alpr::getVersion());

  TCLAP::ValueArg<std::string> countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe).  Default=us",false, "us" ,"country_code");
  TCLAP::ValueArg<std::string> configFileArg("","config","Path to the openalpr.conf file.",false, "" ,"config_file");
  TCLAP::ValueArg<int> topNArg("n","topn","Max number of possible plate numbers to return.  Default=10",false, 10 ,"topN");
  TCLAP::ValueArg<std::string> logFileArg("l","log","Log file to write to.  Default=" + DEFAULT_LOG_FILE_PATH,false, DEFAULT_LOG_FILE_PATH ,"topN");

  TCLAP::SwitchArg daemonOffSwitch("f","foreground","Set this flag for debugging.  Disables forking the process as a daemon and runs in the foreground.  Default=off", cmd, false);

  try
  {

    cmd.add( topNArg );
    cmd.add( configFileArg );
    cmd.add( logFileArg );

    if (cmd.parse( argc, argv ) == false)
    {
      // Error occured while parsing.  Exit now.
      return 1;
    }

    country = countryCodeArg.getValue();
    configFile = configFileArg.getValue();
    logFile = logFileArg.getValue();
    topn = topNArg.getValue();
    noDaemon = daemonOffSwitch.getValue();
  }
  catch (TCLAP::ArgException &e)    // catch any exceptions
  {
    std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
    return 1;
  }

  log4cplus::BasicConfigurator config;
  config.configure();

  if (noDaemon == false)
  {
    // Fork off into a separate daemon
    daemon(0, 0);

    log4cplus::SharedAppenderPtr myAppender(new log4cplus::FileAppender(logFile));
    myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in daemon mode.");
  }
  else
  {
    //log4cplus::SharedAppenderPtr myAppender(new log4cplus::ConsoleAppender());
    //myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    //logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in the foreground.");
  }

  CSimpleIniA ini;
  ini.SetMultiKey();

  ini.LoadFile(WTS_CONFIG_FILE_PATH.c_str());

  std::vector<std::string> stream_urls;

  CSimpleIniA::TNamesDepend values;
  ini.GetAllValues("daemon", "stream", values);

  // sort the values into the original load order
  values.sort(CSimpleIniA::Entry::LoadOrder());

  // output all of the items
  CSimpleIniA::TNamesDepend::const_iterator i;
  for (i = values.begin(); i != values.end(); ++i) {
      stream_urls.push_back(i->pItem);
  }

  if (stream_urls.size() == 0)
  {
    LOG4CPLUS_FATAL(logger, "No video streams defined in the configuration.");
    return 1;
  }

  std::string imageFolder = ini.GetValue("daemon", "image_folder", "/tmp/");
  std::string upload_url = ini.GetValue("daemon", "upload_address", "");
  std::string site_id = ini.GetValue("daemon", "site_id", "");

  LOG4CPLUS_INFO(logger, "Using: " << imageFolder << " for storing valid plate images");

  for (int i = 0; i < stream_urls.size(); i++)
  {
    CaptureThreadData* tdata = new CaptureThreadData();
    tdata->stream_url = stream_urls[i];
    tdata->camera_id = i + 1;
    tdata->config_file = configFile;
    tdata->output_image_folder = imageFolder;
    tdata->country_code = country;
    tdata->site_id = site_id;

    tthread::thread* t = new tthread::thread(streamRecognitionThread, (void*) tdata);
  }

  // Kick off the data upload thread
  UploadThreadData* udata = new UploadThreadData();
  udata->upload_url = upload_url;
  tthread::thread* t = new tthread::thread(dataUploadThread, (void*) udata );

  while (daemon_active)
  {
    usleep(30000);
  }

}

void streamRecognitionThread(void* arg)
{
  CaptureThreadData* tdata = (CaptureThreadData*) arg;

  LOG4CPLUS_INFO(logger, "country: " << tdata->country_code << " -- config file: " << tdata->config_file );
  LOG4CPLUS_INFO(logger, "Stream " << tdata->camera_id << ": " << tdata->stream_url);

  Alpr alpr(tdata->country_code, tdata->config_file);

  int framenum = 0;

  VideoBuffer videoBuffer;

  videoBuffer.connect(tdata->stream_url, 5);

  cv::Mat latestFrame;

  std::vector<uchar> buffer;

  LOG4CPLUS_INFO(logger, "Daemon active: " << daemon_active);

  while (daemon_active)
  {
    int response = videoBuffer.getLatestFrame(&latestFrame);

    if (response != -1)
    {
      cv::imencode(".bmp", latestFrame, buffer );
      std::vector<AlprResult> results = alpr.recognize(buffer);

      if (results.size() > 0)
      {
	// Create a UUID for the image
	std::string uuid = newUUID();

	// Save the image to disk (using the UUID)
	std::stringstream ss;
	ss << tdata->output_image_folder << "/" << uuid << ".jpg";

	cv::imwrite(ss.str(), latestFrame);

	// Update the JSON content to include UUID and camera ID
	cJSON *root;

	root=cJSON_CreateObject();

	std::string json = alpr.toJson(results);

	cJSON *array = cJSON_Parse(json.c_str());
	cJSON_AddStringToObject(root,	"uuid",		uuid.c_str());
	cJSON_AddNumberToObject(root,	"camera_id",	tdata->camera_id);
	cJSON_AddStringToObject(root, "site_id", 	tdata->site_id.c_str());
	cJSON_AddNumberToObject(root,	"img_width",	latestFrame.cols);
	cJSON_AddNumberToObject(root,	"img_height",	latestFrame.rows);
	cJSON_AddItemToObject(root, 	"results", 	array);

	char *out;
	out=cJSON_PrintUnformatted(root);
	cJSON_Delete(root);

	std::string response(out);

	free(out);

	// Push the results to the Beanstalk queue
	writeToQueue(response);
      }
    }

    usleep(10000);
  }

  videoBuffer.disconnect();

  LOG4CPLUS_INFO(logger, "Video processing ended");

  delete tdata;
}

bool writeToQueue(std::string jsonResult)
{

  Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);
  client.use(BEANSTALK_TUBE_NAME);

  int id = client.put(jsonResult);

  if (id <= 0)
    return false;

  LOG4CPLUS_INFO(logger, "put job id: " << id );

  return true;
}

void dataUploadThread(void* arg)
{

  /* In windows, this will init the winsock stuff */
  curl_global_init(CURL_GLOBAL_ALL);

  UploadThreadData* udata = (UploadThreadData*) arg;

  Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);

  client.watch(BEANSTALK_TUBE_NAME);

  while(daemon_active)
  {
    Beanstalk::Job job;

    client.reserve(job);

    if (job.id() > 0)
    {
      LOG4CPLUS_DEBUG(logger, job.body() );
      if (uploadPost(udata->upload_url, job.body()))
      {
	client.del(job.id());
	LOG4CPLUS_INFO(logger, "Job: " << job.id() << " successfully uploaded" );
      }
      else
      {
	client.release(job);
	LOG4CPLUS_WARN(logger, "Job: " << job.id() << " failed to upload.  Will retry." );
      }
    }

    usleep(10000);
  }

  curl_global_cleanup();
}

bool uploadPost(std::string url, std::string data)
{
  bool success = true;
  CURL *curl;
  CURLcode res;

  /* get a curl handle */
  curl = curl_easy_init();
  if(curl) {
    /* First set the URL that is about to receive our POST. This URL can
       just as well be a https:// URL if that is what should receive the
       data. */
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* Now specify the POST data */
    //char* escaped_data = curl_easy_escape(curl, data.c_str(), data.length());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
    //curl_free(escaped_data);

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
    {
      success = false;
    }

    /* always cleanup */
    curl_easy_cleanup(curl);
  }

  return success;

}<commit_msg>Using rolling log file with max size<commit_after>
#include <unistd.h>
#include <sstream>

#include "tclap/CmdLine.h"
#include "beanstalk.hpp"
#include "alpr.h"
#include "openalpr/simpleini/simpleini.h"
#include "openalpr/cjson.h"
#include "support/tinythread.h"
#include "videobuffer.h"
#include "uuid.h"
#include <curl/curl.h>

#include <log4cplus/logger.h>
#include <log4cplus/loggingmacros.h>
#include <log4cplus/configurator.h>
#include <log4cplus/consoleappender.h>
#include <log4cplus/fileappender.h>

// prototypes
void streamRecognitionThread(void* arg);
bool writeToQueue(std::string jsonResult);
bool uploadPost(std::string url, std::string data);
void dataUploadThread(void* arg);

// Constants
const std::string DEFAULT_LOG_FILE_PATH="/var/log/openalpr.log";
const std::string WTS_CONFIG_FILE_PATH="/etc/openalpr/wts.conf";

const std::string BEANSTALK_QUEUE_HOST="127.0.0.1";
const int BEANSTALK_PORT=11300;
const std::string BEANSTALK_TUBE_NAME="alprd";

struct CaptureThreadData
{
  std::string stream_url;
  std::string site_id;
  int camera_id;

  std::string config_file;
  std::string country_code;
  std::string output_image_folder;
};

struct UploadThreadData
{
  std::string upload_url;
};

bool daemon_active;

static log4cplus::Logger logger;

int main( int argc, const char** argv )
{
  daemon_active = true;

  bool noDaemon = false;
  std::string logFile;
  int topn;

  std::string configFile;
  std::string country;

  TCLAP::CmdLine cmd("OpenAlpr Daemon", ' ', Alpr::getVersion());

  TCLAP::ValueArg<std::string> countryCodeArg("c","country","Country code to identify (either us for USA or eu for Europe).  Default=us",false, "us" ,"country_code");
  TCLAP::ValueArg<std::string> configFileArg("","config","Path to the openalpr.conf file.",false, "" ,"config_file");
  TCLAP::ValueArg<int> topNArg("n","topn","Max number of possible plate numbers to return.  Default=10",false, 10 ,"topN");
  TCLAP::ValueArg<std::string> logFileArg("l","log","Log file to write to.  Default=" + DEFAULT_LOG_FILE_PATH,false, DEFAULT_LOG_FILE_PATH ,"topN");

  TCLAP::SwitchArg daemonOffSwitch("f","foreground","Set this flag for debugging.  Disables forking the process as a daemon and runs in the foreground.  Default=off", cmd, false);

  try
  {

    cmd.add( topNArg );
    cmd.add( configFileArg );
    cmd.add( logFileArg );

    if (cmd.parse( argc, argv ) == false)
    {
      // Error occured while parsing.  Exit now.
      return 1;
    }

    country = countryCodeArg.getValue();
    configFile = configFileArg.getValue();
    logFile = logFileArg.getValue();
    topn = topNArg.getValue();
    noDaemon = daemonOffSwitch.getValue();
  }
  catch (TCLAP::ArgException &e)    // catch any exceptions
  {
    std::cerr << "error: " << e.error() << " for arg " << e.argId() << std::endl;
    return 1;
  }

  log4cplus::BasicConfigurator config;
  config.configure();

  if (noDaemon == false)
  {
    // Fork off into a separate daemon
    daemon(0, 0);

    log4cplus::SharedAppenderPtr myAppender(new log4cplus::RollingFileAppender(logFile));
    myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in daemon mode.");
  }
  else
  {
    //log4cplus::SharedAppenderPtr myAppender(new log4cplus::ConsoleAppender());
    //myAppender->setName("alprd_appender");
    // Redirect std out to log file
    logger = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("alprd"));
    //logger.addAppender(myAppender);

    LOG4CPLUS_INFO(logger, "Running OpenALPR daemon in the foreground.");
  }

  CSimpleIniA ini;
  ini.SetMultiKey();

  ini.LoadFile(WTS_CONFIG_FILE_PATH.c_str());

  std::vector<std::string> stream_urls;

  CSimpleIniA::TNamesDepend values;
  ini.GetAllValues("daemon", "stream", values);

  // sort the values into the original load order
  values.sort(CSimpleIniA::Entry::LoadOrder());

  // output all of the items
  CSimpleIniA::TNamesDepend::const_iterator i;
  for (i = values.begin(); i != values.end(); ++i) {
      stream_urls.push_back(i->pItem);
  }

  if (stream_urls.size() == 0)
  {
    LOG4CPLUS_FATAL(logger, "No video streams defined in the configuration.");
    return 1;
  }

  std::string imageFolder = ini.GetValue("daemon", "image_folder", "/tmp/");
  std::string upload_url = ini.GetValue("daemon", "upload_address", "");
  std::string site_id = ini.GetValue("daemon", "site_id", "");

  LOG4CPLUS_INFO(logger, "Using: " << imageFolder << " for storing valid plate images");

  for (int i = 0; i < stream_urls.size(); i++)
  {
    CaptureThreadData* tdata = new CaptureThreadData();
    tdata->stream_url = stream_urls[i];
    tdata->camera_id = i + 1;
    tdata->config_file = configFile;
    tdata->output_image_folder = imageFolder;
    tdata->country_code = country;
    tdata->site_id = site_id;

    tthread::thread* t = new tthread::thread(streamRecognitionThread, (void*) tdata);
  }

  // Kick off the data upload thread
  UploadThreadData* udata = new UploadThreadData();
  udata->upload_url = upload_url;
  tthread::thread* t = new tthread::thread(dataUploadThread, (void*) udata );

  while (daemon_active)
  {
    usleep(30000);
  }

}

void streamRecognitionThread(void* arg)
{
  CaptureThreadData* tdata = (CaptureThreadData*) arg;

  LOG4CPLUS_INFO(logger, "country: " << tdata->country_code << " -- config file: " << tdata->config_file );
  LOG4CPLUS_INFO(logger, "Stream " << tdata->camera_id << ": " << tdata->stream_url);

  Alpr alpr(tdata->country_code, tdata->config_file);

  int framenum = 0;

  VideoBuffer videoBuffer;

  videoBuffer.connect(tdata->stream_url, 5);

  cv::Mat latestFrame;

  std::vector<uchar> buffer;

  LOG4CPLUS_INFO(logger, "Daemon active: " << daemon_active);

  while (daemon_active)
  {
    int response = videoBuffer.getLatestFrame(&latestFrame);

    if (response != -1)
    {
      cv::imencode(".bmp", latestFrame, buffer );
      std::vector<AlprResult> results = alpr.recognize(buffer);

      if (results.size() > 0)
      {
	// Create a UUID for the image
	std::string uuid = newUUID();

	// Save the image to disk (using the UUID)
	std::stringstream ss;
	ss << tdata->output_image_folder << "/" << uuid << ".jpg";

	cv::imwrite(ss.str(), latestFrame);

	// Update the JSON content to include UUID and camera ID
	cJSON *root;

	root=cJSON_CreateObject();

	std::string json = alpr.toJson(results);

	cJSON *array = cJSON_Parse(json.c_str());
	cJSON_AddStringToObject(root,	"uuid",		uuid.c_str());
	cJSON_AddNumberToObject(root,	"camera_id",	tdata->camera_id);
	cJSON_AddStringToObject(root, "site_id", 	tdata->site_id.c_str());
	cJSON_AddNumberToObject(root,	"img_width",	latestFrame.cols);
	cJSON_AddNumberToObject(root,	"img_height",	latestFrame.rows);
	cJSON_AddItemToObject(root, 	"results", 	array);

	char *out;
	out=cJSON_PrintUnformatted(root);
	cJSON_Delete(root);

	std::string response(out);

	free(out);

	// Push the results to the Beanstalk queue
	writeToQueue(response);
      }
    }

    usleep(10000);
  }

  videoBuffer.disconnect();

  LOG4CPLUS_INFO(logger, "Video processing ended");

  delete tdata;
}

bool writeToQueue(std::string jsonResult)
{

  Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);
  client.use(BEANSTALK_TUBE_NAME);

  int id = client.put(jsonResult);

  if (id <= 0)
    return false;

  LOG4CPLUS_INFO(logger, "put job id: " << id );

  return true;
}

void dataUploadThread(void* arg)
{

  /* In windows, this will init the winsock stuff */
  curl_global_init(CURL_GLOBAL_ALL);

  UploadThreadData* udata = (UploadThreadData*) arg;

  Beanstalk::Client client(BEANSTALK_QUEUE_HOST, BEANSTALK_PORT);

  client.watch(BEANSTALK_TUBE_NAME);

  while(daemon_active)
  {
    Beanstalk::Job job;

    client.reserve(job);

    if (job.id() > 0)
    {
      LOG4CPLUS_DEBUG(logger, job.body() );
      if (uploadPost(udata->upload_url, job.body()))
      {
	client.del(job.id());
	LOG4CPLUS_INFO(logger, "Job: " << job.id() << " successfully uploaded" );
      }
      else
      {
	client.release(job);
	LOG4CPLUS_WARN(logger, "Job: " << job.id() << " failed to upload.  Will retry." );
      }
    }

    usleep(10000);
  }

  curl_global_cleanup();
}

bool uploadPost(std::string url, std::string data)
{
  bool success = true;
  CURL *curl;
  CURLcode res;

  /* get a curl handle */
  curl = curl_easy_init();
  if(curl) {
    /* First set the URL that is about to receive our POST. This URL can
       just as well be a https:// URL if that is what should receive the
       data. */
    curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    /* Now specify the POST data */
    //char* escaped_data = curl_easy_escape(curl, data.c_str(), data.length());
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
    //curl_free(escaped_data);

    /* Perform the request, res will get the return code */
    res = curl_easy_perform(curl);
    /* Check for errors */
    if(res != CURLE_OK)
    {
      success = false;
    }

    /* always cleanup */
    curl_easy_cleanup(curl);
  }

  return success;

}<|endoftext|>


<commit_before>/*

Copyright (c) 2009, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/allocator.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"

#ifdef TORRENT_WINDOWS
#include <windows.h>
#elif defined TORRENT_BEOS
#include <kernel/OS.h>
#include <stdlib.h> // malloc/free
#else
#include <stdlib.h> // valloc/free
#include <unistd.h> // _SC_PAGESIZE
#endif

#if TORRENT_USE_MEMALIGN || TORRENT_USE_POSIX_MEMALIGN
#include <malloc.h> // memalign
#endif

#ifdef TORRENT_DEBUG_BUFFERS
#include <sys/mman.h>
#include "libtorrent/size_type.hpp"

struct alloc_header
{
	libtorrent::size_type size;
	int magic;
	char stack[3072];
};

#endif

#if defined TORRENT_DEBUG_BUFFERS && (defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050))
	void print_backtrace(char* out, int len);
#endif

namespace libtorrent
{

	int page_size()
	{
		static int s = 0;
		if (s != 0) return s;

#ifdef TORRENT_WINDOWS
		SYSTEM_INFO si;
		GetSystemInfo(&si);
		s = si.dwPageSize;
#elif defined TORRENT_BEOS
		s = B_PAGE_SIZE;
#else
		s = sysconf(_SC_PAGESIZE);
#endif
		// assume the page size is 4 kiB if we
		// fail to query it
		if (s <= 0) s = 4096;
		return s;
	}

	char* page_aligned_allocator::malloc(size_type bytes)
	{
#ifdef TORRENT_DEBUG_BUFFERS
		int page = page_size();
		int num_pages = (bytes + (page-1)) + 2;
		char* ret = (char*)valloc(num_pages * page);
		// make the two surrounding pages non-readable and -writable
		alloc_header* h = (alloc_header*)ret;
		h->size = bytes;
		h->magic = 0x1337;
#if defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
		print_backtrace(h->stack, sizeof(h->stack));
#endif
		mprotect(ret, page, PROT_READ);
		mprotect(ret + (num_pages-1) * page, page, PROT_READ);
//		fprintf(stderr, "malloc: %p head: %p tail: %p size: %d\n", ret + page, ret, ret + page + bytes, int(bytes));

		return ret + page;
#endif

#if TORRENT_USE_POSIX_MEMALIGN
		void* ret;
		if (posix_memalign(&ret, page_size(), bytes) != 0) ret = 0;
		return (char*)ret;
#elif TORRENT_USE_MEMALIGN
		return (char*)memalign(page_size(), bytes);
#elif defined TORRENT_WINDOWS
		return (char*)VirtualAlloc(0, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#elif defined TORRENT_BEOS
		void* ret = 0;
		area_id id = create_area("", &ret, B_ANY_ADDRESS
			, (bytes + page_size() - 1) & (page_size()-1), B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
		if (id < B_OK) return 0;
		return (char*)ret;
#else
		return (char*)valloc(bytes);
#endif
	}

	void page_aligned_allocator::free(char* const block)
	{

#ifdef TORRENT_DEBUG_BUFFERS
		int page = page_size();
		// make the two surrounding pages non-readable and -writable
		mprotect(block - page, page, PROT_READ | PROT_WRITE);
		alloc_header* h = (alloc_header*)(block - page);
		int num_pages = (h->size + (page-1)) + 2;
		TORRENT_ASSERT(h->magic == 0x1337);
		mprotect(block + (num_pages-2) * page, page, PROT_READ | PROT_WRITE);
//		fprintf(stderr, "free: %p head: %p tail: %p size: %d\n", block, block - page, block + h->size, int(h->size));
		h->magic = 0;

#if defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
		print_backtrace(h->stack, sizeof(h->stack));
#endif
		::free(block - page);
		return;
#endif

#ifdef TORRENT_WINDOWS
		VirtualFree(block, 0, MEM_RELEASE);
#elif defined TORRENT_BEOS
		area_id id = area_for(block);
		if (id < B_OK) return;
		delete_area(id);
#else
		::free(block);
#endif
	}

}

<commit_msg>fixed bug in buffer debug code in allocator<commit_after>/*

Copyright (c) 2009, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include "libtorrent/allocator.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/assert.hpp"

#ifdef TORRENT_WINDOWS
#include <windows.h>
#elif defined TORRENT_BEOS
#include <kernel/OS.h>
#include <stdlib.h> // malloc/free
#else
#include <stdlib.h> // valloc/free
#include <unistd.h> // _SC_PAGESIZE
#endif

#if TORRENT_USE_MEMALIGN || TORRENT_USE_POSIX_MEMALIGN
#include <malloc.h> // memalign
#endif

#ifdef TORRENT_DEBUG_BUFFERS
#include <sys/mman.h>
#include "libtorrent/size_type.hpp"

struct alloc_header
{
	libtorrent::size_type size;
	int magic;
	char stack[3072];
};

#endif

#if defined TORRENT_DEBUG_BUFFERS && (defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050))
	void print_backtrace(char* out, int len);
#endif

namespace libtorrent
{

	int page_size()
	{
		static int s = 0;
		if (s != 0) return s;

#ifdef TORRENT_WINDOWS
		SYSTEM_INFO si;
		GetSystemInfo(&si);
		s = si.dwPageSize;
#elif defined TORRENT_BEOS
		s = B_PAGE_SIZE;
#else
		s = sysconf(_SC_PAGESIZE);
#endif
		// assume the page size is 4 kiB if we
		// fail to query it
		if (s <= 0) s = 4096;
		return s;
	}

	char* page_aligned_allocator::malloc(size_type bytes)
	{
#ifdef TORRENT_DEBUG_BUFFERS
		int page = page_size();
		int num_pages = (bytes + (page-1)) / page + 2;
		char* ret = (char*)valloc(num_pages * page);
		// make the two surrounding pages non-readable and -writable
		alloc_header* h = (alloc_header*)ret;
		h->size = bytes;
		h->magic = 0x1337;
#if defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
		print_backtrace(h->stack, sizeof(h->stack));
#endif
		mprotect(ret, page, PROT_READ);
		mprotect(ret + (num_pages-1) * page, page, PROT_READ);
//		fprintf(stderr, "malloc: %p head: %p tail: %p size: %d\n", ret + page, ret, ret + page + bytes, int(bytes));

		return ret + page;
#endif

#if TORRENT_USE_POSIX_MEMALIGN
		void* ret;
		if (posix_memalign(&ret, page_size(), bytes) != 0) ret = 0;
		return (char*)ret;
#elif TORRENT_USE_MEMALIGN
		return (char*)memalign(page_size(), bytes);
#elif defined TORRENT_WINDOWS
		return (char*)VirtualAlloc(0, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#elif defined TORRENT_BEOS
		void* ret = 0;
		area_id id = create_area("", &ret, B_ANY_ADDRESS
			, (bytes + page_size() - 1) & (page_size()-1), B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
		if (id < B_OK) return 0;
		return (char*)ret;
#else
		return (char*)valloc(bytes);
#endif
	}

	void page_aligned_allocator::free(char* const block)
	{

#ifdef TORRENT_DEBUG_BUFFERS
		int page = page_size();
		// make the two surrounding pages non-readable and -writable
		mprotect(block - page, page, PROT_READ | PROT_WRITE);
		alloc_header* h = (alloc_header*)(block - page);
		int num_pages = (h->size + (page-1)) / page + 2;
		TORRENT_ASSERT(h->magic == 0x1337);
		mprotect(block + (num_pages-2) * page, page, PROT_READ | PROT_WRITE);
//		fprintf(stderr, "free: %p head: %p tail: %p size: %d\n", block, block - page, block + h->size, int(h->size));
		h->magic = 0;

#if defined __linux__ || (defined __APPLE__ && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050)
		print_backtrace(h->stack, sizeof(h->stack));
#endif
		::free(block - page);
		return;
#endif

#ifdef TORRENT_WINDOWS
		VirtualFree(block, 0, MEM_RELEASE);
#elif defined TORRENT_BEOS
		area_id id = area_for(block);
		if (id < B_OK) return;
		delete_area(id);
#else
		::free(block);
#endif
	}

}

<|endoftext|>