<commit_before>/*
 * Instantiator.cc
 *
 * Copyright (C) 2009, 2014, 2015 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atoms/core/DefineLink.h>
#include <opencog/atoms/core/LambdaLink.h>
#include <opencog/atoms/core/PutLink.h>
#include <opencog/atoms/execution/ExecutionOutputLink.h>
#include <opencog/atoms/execution/EvaluationLink.h>
#include <opencog/atoms/reduct/FoldLink.h>
#include <opencog/query/BindLinkAPI.h>

#include "Instantiator.h"

using namespace opencog;

/// Same as walk tree, except that it handles a handle sequence,
/// instead of a single handle. The returned result is in oset_results.
/// Returns true if the results differ from the input, i.e. if the
/// result of execution/evaluation changed something.
bool Instantiator::walk_sequence(HandleSeq& oset_results, const HandleSeq& expr)
{
	bool changed = false;
	for (const Handle& h : expr)
	{
		Handle hg(walk_tree(h));
		if (hg != h) changed = true;

		// GlobNodes are grounded by a ListLink of everything that
		// the GlobNode matches. Unwrap the list, and insert each
		// of the glob elements in sequence.
		if (_quotation_level == 0 and GLOB_NODE == h->getType() and hg != h)
		{
			LinkPtr lp(LinkCast(hg));
			OC_ASSERT(nullptr != lp, "Expecting glob list");
			for (const Handle& gloe: lp->getOutgoingSet())
			{
				if (NULL != gloe)
					oset_results.emplace_back(gloe);
			}
		}
		else
		{
			// It could be a NULL handle if it's deleted... Just skip
			// over it. We test the pointer here, not the uuid, since
			// the uuid's are all Handle::UNDEFINED until we put them
			// into the atomspace.
			if (NULL != hg)
				oset_results.emplace_back(hg);
		}
	}
	return changed;
}

Handle Instantiator::walk_tree(const Handle& expr)
{
	Type t = expr->getType();

	// Quotation case
	if (QUOTE_LINK == t)
		_quotation_level++;
	else if (UNQUOTE_LINK == t)
		_quotation_level--;

	// Discard the following QuoteLink or UnquoteLink (as it is
	// serving its quoting or unquoting function).
	if (((_avoid_discarding_quotes_level == 0)
		and (_quotation_level == 1 and QUOTE_LINK == t))
		or (_quotation_level == 0 and UNQUOTE_LINK == t)) {
		if (1 != expr->getArity())
			throw InvalidParamException(TRACE_INFO,
			                            "QuoteLink/UnquoteLink has "
			                            "unexpected arity!");
		return walk_tree(expr->getOutgoingAtom(0));
	}

	if (expr->isNode())
	{
		if (_quotation_level > 0)
			return expr;

		// If we are here, we are a Node.
		if (DEFINED_SCHEMA_NODE == t)
		{
			return walk_tree(DefineLink::get_definition(expr));
		}

		if (VARIABLE_NODE != t and GLOB_NODE != t)
			return Handle(expr);

		// If we are here, we found a variable. Look it up. Return a
		// grounding if it has one, otherwise return the variable
		// itself.
		std::map<Handle,Handle>::const_iterator it = _vmap->find(expr);
		if (_vmap->end() == it) return Handle(expr);

		// Not so fast, pardner. VariableNodes can be grounded by
		// links, and those links may be executable. In that case,
		// we have to execute them.

		// halt infinite regress
		if (_halt)
			return Handle(expr);

		_halt = true;
		Handle hgnd(walk_tree(it->second));
		_halt = false;
		return hgnd;
	}

	// -----------------------------------------------------------
	// If we are here, then we have a link. Walk it. In general,
	// links may contain both bound variables, and also free variables.
	// We must be careful to substitute only for free variables, and
	// never for bound ones.

	if (_quotation_level > 0)
		goto mere_recursive_call;

	// Reduce PutLinks. There are two ways to do this: eager execution
	// and lazy execution.  The algos are this:
	//
	//    Eager: first, execute the arguments to the Put, then beta-
	//    reduce, then execute again.
	//
	//    Lazy: beta-reduce first, then execute.  Lazy helps avoid
	//    un-needed executations, and has better control over infinite
	//    recursion. However, unit tests currently fail on it.
	//
	if (PUT_LINK == t)
	{
		PutLinkPtr ppp(PutLinkCast(expr));
		if (nullptr == ppp)
			ppp = createPutLink(*LinkCast(expr));

		if (_eager)
		{
			// Execute the values in the PutLink before doing the
			// beta-reduction. Execute the body only after the
			// beta-reduction has been done.
			Handle pvals = ppp->get_values();
			Handle gargs = walk_tree(pvals);
			if (gargs != pvals)
			{
				HandleSeq groset;
				if (ppp->get_vardecl())
					groset.emplace_back(ppp->get_vardecl());
				groset.emplace_back(ppp->get_body());
				groset.emplace_back(gargs);
				ppp = createPutLink(groset);
			}
		}

		// Step one: beta-reduce.
		Handle red(ppp->reduce());
		// Step two: execute the resulting body.
		Handle rex(walk_tree(red));
		if (nullptr == rex)
			return rex;

		// Step three: XXX this is awkward, but seems to be needed...
		// If the result is evaluatable, then evaluate it. e.g. if the
		// result has a GroundedPredicateNode, we need to run it now.
		// We do, however, ignore the resulting TV, which is also
		// awkward.  I'm confused about how to handle this best.
		// The behavior tree uses this!
		// Anyway, do_evaluate() will throw if rex is not evaluatable.
		if (SET_LINK == rex->getType())
		{
			LinkPtr slp(LinkCast(rex));
			for (const Handle& plo : slp->getOutgoingSet())
			{
				try {
					EvaluationLink::do_evaluate(_as, plo, true);
				}
				catch (const NotEvaluatableException& ex) {}
			}
			return rex;
		}
		try {
			EvaluationLink::do_evaluate(_as, rex, true);
		}
		catch (const NotEvaluatableException& ex) {}
		return rex;
	}

	// ExecutionOutputLinks are not handled by the FunctionLink factory
	// below. This is due to a circular shared-libarary dependency.
	//
	// Even for the case of lazy execution, we still have to do eager
	// execution of the arguments passed to the ExOutLink.  This is
	// because the ExOutLink is a black box, and we cannot guess what
	// it might do.  It would be great if the authors of ExOutLinks
	// did the lazy execution themselves... but this is too much to
	// ask for. So we always eager-evaluate those args.
	if (EXECUTION_OUTPUT_LINK == t)
	{
		// XXX Force syntax checking; normally this would be done in the
		// atomspace factory, but that is currently broken, so do it here.
		ExecutionOutputLinkPtr eolp(ExecutionOutputLinkCast(expr));
		if (nullptr == eolp)
			eolp = createExecutionOutputLink(expr->getOutgoingSet());

		// At this time, the GSN or the DSN is always in position 0
		// of the outgoing set, and the ListLink of arguments is always
		// in position 1.  Someday in the future, there may be a variable
		// declaration; we punt on that.
		Handle sn(eolp->get_schema());
		Handle args(eolp->get_args());

		// Perform substitution on the args, only. Do not discard any quotes.
		_avoid_discarding_quotes_level++;
		args = walk_tree(args);
		_avoid_discarding_quotes_level--;

		// If its a DSN, obtain the correct body for it.
		if (DEFINED_SCHEMA_NODE == sn->getType())
			sn = DefineLink::get_definition(sn);

		// If its an anonymous function link, execute it here.
		if (LAMBDA_LINK == sn->getType())
		{
			LambdaLinkPtr flp(LambdaLinkCast(sn));
			if (NULL == flp)
				flp = createLambdaLink(*LinkCast(sn));

			// Two-step process. First, plug the arguments into the
			// function; i.e. perform beta-reduction. Second, actually
			// execute the result. We execute by just calling walk_tree
			// again.
			Handle body(flp->get_body());
			Variables vars(flp->get_variables());

			const HandleSeq& oset(LinkCast(args)->getOutgoingSet());
			Handle beta_reduced(vars.substitute_nocheck(body, oset));
			return walk_tree(beta_reduced);
		}

		ExecutionOutputLinkPtr geolp(createExecutionOutputLink(sn, args));
		return geolp->execute(_as);
	}

	// Handle DeleteLink's before general FunctionLink's; they
	// work differently.
	if (DELETE_LINK == t)
	{
		HandleSeq oset_results;
		walk_sequence(oset_results, expr->getOutgoingSet());
		for (const Handle& h: oset_results)
		{
			Type ht = h->getType();
			if (VARIABLE_NODE != ht and GLOB_NODE != ht)
				_as->remove_atom(h, true);
		}
		return Handle::UNDEFINED;
	}

	// FoldLink's are a kind-of FunctionLink, but are not currently
	// handled by the FunctionLink factory below.  This should be fixed
	// someday, when the reduct directory is nuked.
	if (classserver().isA(t, FOLD_LINK))
	{
		// A FoldLink never has a variable declaration (at this time).
		// The number of arguments is never fixed, its always variadic.
		if (_eager)
		{
			// Perform substitution on all arguments before applying the
			// function itself.
			HandleSeq oset_results;
			walk_sequence(oset_results, expr->getOutgoingSet());
			Handle hl(FoldLink::factory(t, oset_results));
			FoldLinkPtr flp(FoldLinkCast(hl));
			return flp->execute(_as);
		}
		else
		{
			FoldLinkPtr flp(FoldLink::factory(expr));
			return flp->execute(_as);
		}
	}

	// Fire any other function links, not handled above.
	if (classserver().isA(t, FUNCTION_LINK))
	{
		// At this time, no FunctionLink that is outside of an
		// ExecutionOutputLink ever has a variable declaration.
		// Also, the number of arguments is not fixed, its always variadic.
		// Perform substitution on all arguments before applying the
		// function itself.
		if (_eager)
		{
			HandleSeq oset_results;
			walk_sequence(oset_results, expr->getOutgoingSet());
			FunctionLinkPtr flp(FunctionLink::factory(t, oset_results));
			return flp->execute(_as);
		}
		else
		{
			FunctionLinkPtr flp(FunctionLink::factory(expr));
			return flp->execute(_as);
		}
	}

	// If there is a GetLink, we have to perform the get, and replace
	// it with the results of the get. The get is implemented with the
	// PatternLink::satisfy() method.
	if (classserver().isA(t, GET_LINK))
	{
		if (_eager)
		{
			HandleSeq oset_results;
			_avoid_discarding_quotes_level++;
			walk_sequence(oset_results, expr->getOutgoingSet());
			_avoid_discarding_quotes_level--;

			// We do have to poke the results into the atomspace,
			// else the Get will fail.
			size_t sz = oset_results.size();
			for (size_t i=0; i< sz; i++)
				oset_results[i] = _as->add_atom(oset_results[i]);

			LinkPtr lp(createLink(t, oset_results));

			return satisfying_set(_as, Handle(lp));
		}
		else
		{
			return satisfying_set(_as, expr);
		}
	}

mere_recursive_call:
	// None of the above. Create a duplicate link, but with an outgoing
	// set where the variables have been substituted by their values.
	HandleSeq oset_results;
	bool changed = walk_sequence(oset_results, expr->getOutgoingSet());
	if (changed)
	{
		LinkPtr subl = createLink(t, oset_results, expr->getTruthValue());
		return Handle(_as->add_atom(subl));
	}
	return expr;
}

/**
 * instantiate -- create a grounded expression from an ungrounded one.
 *
 * Given a handle to an ungrounded expression, and a set of groundings,
 * this will create a grounded expression.
 *
 * The set of groundings is to be passed in with the map 'vars', which
 * maps variable names to their groundings -- it maps variable names to
 * atoms that already exist in the atomspace.  This method will then go
 * through all of the variables in the expression, and substitute them
 * with their values, creating a new expression. The new expression is
 * added to the atomspace, and its handle is returned.
 */
Handle Instantiator::instantiate(const Handle& expr,
                                 const std::map<Handle, Handle> &vars)
{
	// throw, not assert, because this is a user error ...
	if (nullptr == expr)
		throw InvalidParamException(TRACE_INFO,
			"Asked to ground a null expression");

	_quotation_level = 0;
	_avoid_discarding_quotes_level = 0;

	_vmap = &vars;

	// The returned handle is not yet in the atomspace. Add it now.
	// We do this here, instead of in walk_tree(), because adding
	// atoms to the atomspace is an expensive process.  We can save
	// some time by doing it just once, right here, in one big batch.
	return _as->add_atom(walk_tree(expr));
}

/* ===================== END OF FILE ===================== */
<commit_msg>Fix a few more un-needed LinkCasts<commit_after>/*
 * Instantiator.cc
 *
 * Copyright (C) 2009, 2014, 2015 Linas Vepstas
 *
 * Author: Linas Vepstas <linasvepstas@gmail.com>  January 2009
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include <opencog/atoms/core/DefineLink.h>
#include <opencog/atoms/core/LambdaLink.h>
#include <opencog/atoms/core/PutLink.h>
#include <opencog/atoms/execution/ExecutionOutputLink.h>
#include <opencog/atoms/execution/EvaluationLink.h>
#include <opencog/atoms/reduct/FoldLink.h>
#include <opencog/query/BindLinkAPI.h>

#include "Instantiator.h"

using namespace opencog;

/// Same as walk tree, except that it handles a handle sequence,
/// instead of a single handle. The returned result is in oset_results.
/// Returns true if the results differ from the input, i.e. if the
/// result of execution/evaluation changed something.
bool Instantiator::walk_sequence(HandleSeq& oset_results, const HandleSeq& expr)
{
	bool changed = false;
	for (const Handle& h : expr)
	{
		Handle hg(walk_tree(h));
		if (hg != h) changed = true;

		// GlobNodes are grounded by a ListLink of everything that
		// the GlobNode matches. Unwrap the list, and insert each
		// of the glob elements in sequence.
		if (_quotation_level == 0 and GLOB_NODE == h->getType() and hg != h)
		{
			for (const Handle& gloe: hg->getOutgoingSet())
			{
				if (NULL != gloe)
					oset_results.emplace_back(gloe);
			}
		}
		else
		{
			// It could be a NULL handle if it's deleted... Just skip
			// over it. We test the pointer here, not the uuid, since
			// the uuid's are all Handle::UNDEFINED until we put them
			// into the atomspace.
			if (NULL != hg)
				oset_results.emplace_back(hg);
		}
	}
	return changed;
}

Handle Instantiator::walk_tree(const Handle& expr)
{
	Type t = expr->getType();

	// Quotation case
	if (QUOTE_LINK == t)
		_quotation_level++;
	else if (UNQUOTE_LINK == t)
		_quotation_level--;

	// Discard the following QuoteLink or UnquoteLink (as it is
	// serving its quoting or unquoting function).
	if (((_avoid_discarding_quotes_level == 0)
		and (_quotation_level == 1 and QUOTE_LINK == t))
		or (_quotation_level == 0 and UNQUOTE_LINK == t)) {
		if (1 != expr->getArity())
			throw InvalidParamException(TRACE_INFO,
			                            "QuoteLink/UnquoteLink has "
			                            "unexpected arity!");
		return walk_tree(expr->getOutgoingAtom(0));
	}

	if (expr->isNode())
	{
		if (_quotation_level > 0)
			return expr;

		// If we are here, we are a Node.
		if (DEFINED_SCHEMA_NODE == t)
		{
			return walk_tree(DefineLink::get_definition(expr));
		}

		if (VARIABLE_NODE != t and GLOB_NODE != t)
			return Handle(expr);

		// If we are here, we found a variable. Look it up. Return a
		// grounding if it has one, otherwise return the variable
		// itself.
		std::map<Handle,Handle>::const_iterator it = _vmap->find(expr);
		if (_vmap->end() == it) return Handle(expr);

		// Not so fast, pardner. VariableNodes can be grounded by
		// links, and those links may be executable. In that case,
		// we have to execute them.

		// halt infinite regress
		if (_halt)
			return Handle(expr);

		_halt = true;
		Handle hgnd(walk_tree(it->second));
		_halt = false;
		return hgnd;
	}

	// -----------------------------------------------------------
	// If we are here, then we have a link. Walk it. In general,
	// links may contain both bound variables, and also free variables.
	// We must be careful to substitute only for free variables, and
	// never for bound ones.

	if (_quotation_level > 0)
		goto mere_recursive_call;

	// Reduce PutLinks. There are two ways to do this: eager execution
	// and lazy execution.  The algos are this:
	//
	//    Eager: first, execute the arguments to the Put, then beta-
	//    reduce, then execute again.
	//
	//    Lazy: beta-reduce first, then execute.  Lazy helps avoid
	//    un-needed executations, and has better control over infinite
	//    recursion. However, unit tests currently fail on it.
	//
	if (PUT_LINK == t)
	{
		PutLinkPtr ppp(PutLinkCast(expr));
		if (nullptr == ppp)
			ppp = createPutLink(*LinkCast(expr));

		if (_eager)
		{
			// Execute the values in the PutLink before doing the
			// beta-reduction. Execute the body only after the
			// beta-reduction has been done.
			Handle pvals = ppp->get_values();
			Handle gargs = walk_tree(pvals);
			if (gargs != pvals)
			{
				HandleSeq groset;
				if (ppp->get_vardecl())
					groset.emplace_back(ppp->get_vardecl());
				groset.emplace_back(ppp->get_body());
				groset.emplace_back(gargs);
				ppp = createPutLink(groset);
			}
		}

		// Step one: beta-reduce.
		Handle red(ppp->reduce());
		// Step two: execute the resulting body.
		Handle rex(walk_tree(red));
		if (nullptr == rex)
			return rex;

		// Step three: XXX this is awkward, but seems to be needed...
		// If the result is evaluatable, then evaluate it. e.g. if the
		// result has a GroundedPredicateNode, we need to run it now.
		// We do, however, ignore the resulting TV, which is also
		// awkward.  I'm confused about how to handle this best.
		// The behavior tree uses this!
		// Anyway, do_evaluate() will throw if rex is not evaluatable.
		if (SET_LINK == rex->getType())
		{
			for (const Handle& plo : rex->getOutgoingSet())
			{
				try {
					EvaluationLink::do_evaluate(_as, plo, true);
				}
				catch (const NotEvaluatableException& ex) {}
			}
			return rex;
		}
		try {
			EvaluationLink::do_evaluate(_as, rex, true);
		}
		catch (const NotEvaluatableException& ex) {}
		return rex;
	}

	// ExecutionOutputLinks are not handled by the FunctionLink factory
	// below. This is due to a circular shared-libarary dependency.
	//
	// Even for the case of lazy execution, we still have to do eager
	// execution of the arguments passed to the ExOutLink.  This is
	// because the ExOutLink is a black box, and we cannot guess what
	// it might do.  It would be great if the authors of ExOutLinks
	// did the lazy execution themselves... but this is too much to
	// ask for. So we always eager-evaluate those args.
	if (EXECUTION_OUTPUT_LINK == t)
	{
		// XXX Force syntax checking; normally this would be done in the
		// atomspace factory, but that is currently broken, so do it here.
		ExecutionOutputLinkPtr eolp(ExecutionOutputLinkCast(expr));
		if (nullptr == eolp)
			eolp = createExecutionOutputLink(expr->getOutgoingSet());

		// At this time, the GSN or the DSN is always in position 0
		// of the outgoing set, and the ListLink of arguments is always
		// in position 1.  Someday in the future, there may be a variable
		// declaration; we punt on that.
		Handle sn(eolp->get_schema());
		Handle args(eolp->get_args());

		// Perform substitution on the args, only. Do not discard any quotes.
		_avoid_discarding_quotes_level++;
		args = walk_tree(args);
		_avoid_discarding_quotes_level--;

		// If its a DSN, obtain the correct body for it.
		if (DEFINED_SCHEMA_NODE == sn->getType())
			sn = DefineLink::get_definition(sn);

		// If its an anonymous function link, execute it here.
		if (LAMBDA_LINK == sn->getType())
		{
			LambdaLinkPtr flp(LambdaLinkCast(sn));
			if (NULL == flp)
				flp = createLambdaLink(*LinkCast(sn));

			// Two-step process. First, plug the arguments into the
			// function; i.e. perform beta-reduction. Second, actually
			// execute the result. We execute by just calling walk_tree
			// again.
			Handle body(flp->get_body());
			Variables vars(flp->get_variables());

			const HandleSeq& oset(args->getOutgoingSet());
			Handle beta_reduced(vars.substitute_nocheck(body, oset));
			return walk_tree(beta_reduced);
		}

		ExecutionOutputLinkPtr geolp(createExecutionOutputLink(sn, args));
		return geolp->execute(_as);
	}

	// Handle DeleteLink's before general FunctionLink's; they
	// work differently.
	if (DELETE_LINK == t)
	{
		HandleSeq oset_results;
		walk_sequence(oset_results, expr->getOutgoingSet());
		for (const Handle& h: oset_results)
		{
			Type ht = h->getType();
			if (VARIABLE_NODE != ht and GLOB_NODE != ht)
				_as->remove_atom(h, true);
		}
		return Handle::UNDEFINED;
	}

	// FoldLink's are a kind-of FunctionLink, but are not currently
	// handled by the FunctionLink factory below.  This should be fixed
	// someday, when the reduct directory is nuked.
	if (classserver().isA(t, FOLD_LINK))
	{
		// A FoldLink never has a variable declaration (at this time).
		// The number of arguments is never fixed, its always variadic.
		if (_eager)
		{
			// Perform substitution on all arguments before applying the
			// function itself.
			HandleSeq oset_results;
			walk_sequence(oset_results, expr->getOutgoingSet());
			FoldLinkPtr flp(FoldLink::factory(t, oset_results));
			return flp->execute(_as);
		}
		else
		{
			FoldLinkPtr flp(FoldLink::factory(expr));
			return flp->execute(_as);
		}
	}

	// Fire any other function links, not handled above.
	if (classserver().isA(t, FUNCTION_LINK))
	{
		// At this time, no FunctionLink that is outside of an
		// ExecutionOutputLink ever has a variable declaration.
		// Also, the number of arguments is not fixed, its always variadic.
		// Perform substitution on all arguments before applying the
		// function itself.
		if (_eager)
		{
			HandleSeq oset_results;
			walk_sequence(oset_results, expr->getOutgoingSet());
			FunctionLinkPtr flp(FunctionLink::factory(t, oset_results));
			return flp->execute(_as);
		}
		else
		{
			FunctionLinkPtr flp(FunctionLink::factory(expr));
			return flp->execute(_as);
		}
	}

	// If there is a GetLink, we have to perform the get, and replace
	// it with the results of the get. The get is implemented with the
	// PatternLink::satisfy() method.
	if (classserver().isA(t, GET_LINK))
	{
		if (_eager)
		{
			HandleSeq oset_results;
			_avoid_discarding_quotes_level++;
			walk_sequence(oset_results, expr->getOutgoingSet());
			_avoid_discarding_quotes_level--;

			// We do have to poke the results into the atomspace,
			// else the Get will fail.
			size_t sz = oset_results.size();
			for (size_t i=0; i< sz; i++)
				oset_results[i] = _as->add_atom(oset_results[i]);

			LinkPtr lp(createLink(t, oset_results));

			return satisfying_set(_as, Handle(lp));
		}
		else
		{
			return satisfying_set(_as, expr);
		}
	}

mere_recursive_call:
	// None of the above. Create a duplicate link, but with an outgoing
	// set where the variables have been substituted by their values.
	HandleSeq oset_results;
	bool changed = walk_sequence(oset_results, expr->getOutgoingSet());
	if (changed)
	{
		LinkPtr subl = createLink(t, oset_results, expr->getTruthValue());
		return Handle(_as->add_atom(subl));
	}
	return expr;
}

/**
 * instantiate -- create a grounded expression from an ungrounded one.
 *
 * Given a handle to an ungrounded expression, and a set of groundings,
 * this will create a grounded expression.
 *
 * The set of groundings is to be passed in with the map 'vars', which
 * maps variable names to their groundings -- it maps variable names to
 * atoms that already exist in the atomspace.  This method will then go
 * through all of the variables in the expression, and substitute them
 * with their values, creating a new expression. The new expression is
 * added to the atomspace, and its handle is returned.
 */
Handle Instantiator::instantiate(const Handle& expr,
                                 const std::map<Handle, Handle> &vars)
{
	// throw, not assert, because this is a user error ...
	if (nullptr == expr)
		throw InvalidParamException(TRACE_INFO,
			"Asked to ground a null expression");

	_quotation_level = 0;
	_avoid_discarding_quotes_level = 0;

	_vmap = &vars;

	// The returned handle is not yet in the atomspace. Add it now.
	// We do this here, instead of in walk_tree(), because adding
	// atoms to the atomspace is an expensive process.  We can save
	// some time by doing it just once, right here, in one big batch.
	return _as->add_atom(walk_tree(expr));
}

/* ===================== END OF FILE ===================== */
<|endoftext|>


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

#include "FontDatabase.h"
#include "FontManager.h"

namespace {

const char* fontList[] =
{
    // LiberationSans
    LIBERATON_TTF "/LiberationSans-BoldItalic.ttf",
    LIBERATON_TTF "/LiberationSans-Italic.ttf",
    LIBERATON_TTF "/LiberationSans-Regular.ttf",
    LIBERATON_TTF "/LiberationSans-Bold.ttf",
    // LiberationSerif
    LIBERATON_TTF "/LiberationSerif-BoldItalic.ttf",
    LIBERATON_TTF "/LiberationSerif-Italic.ttf",
    LIBERATON_TTF "/LiberationSerif-Regular.ttf",
    LIBERATON_TTF "/LiberationSerif-Bold.ttf",
    // LiberationMono
    LIBERATON_TTF "/LiberationMono-BoldItalic.ttf",
    LIBERATON_TTF "/LiberationMono-Italic.ttf",
    LIBERATON_TTF "/LiberationMono-Regular.ttf",
    LIBERATON_TTF "/LiberationMono-Bold.ttf",
#ifdef HAVE_IPA_PGOTHIC
    // IPAPGothic
    HAVE_IPA_PGOTHIC,
#endif
#ifdef HAVE_IPA_PMINCHO
    // IPAPMincho
    HAVE_IPA_PMINCHO,
#endif
#ifdef HAVE_IPA_GOTHIC
    // IPAGothic
    HAVE_IPA_GOTHIC,
#endif
#ifdef HAVE_IPA_MINCHO
    // IPAMincho
    HAVE_IPA_MINCHO,
#endif
#ifdef DEJAVU_TTF
    DEJAVU_TTF "/DejaVuSans-Bold.ttf",
    DEJAVU_TTF "/DejaVuSans-BoldOblique.ttf",
    DEJAVU_TTF "/DejaVuSans-ExtraLight.ttf",
    DEJAVU_TTF "/DejaVuSans-Oblique.ttf",
    DEJAVU_TTF "/DejaVuSans.ttf",
    DEJAVU_TTF "/DejaVuSansMono-Bold.ttf",
    DEJAVU_TTF "/DejaVuSansMono-BoldOblique.ttf",
    DEJAVU_TTF "/DejaVuSansMono-Oblique.ttf",
    DEJAVU_TTF "/DejaVuSansMono.ttf",
    DEJAVU_TTF "/DejaVuSerif-Bold.ttf",
    DEJAVU_TTF "/DejaVuSerif-BoldItalic.ttf",
    DEJAVU_TTF "/DejaVuSerif-Italic.ttf",
    DEJAVU_TTF "/DejaVuSerif.ttf",
#endif
#ifdef HAVE_AEGEAN
    // Aegean
    HAVE_AEGEAN,
#endif
#ifdef HAVE_AHEM
    // Ahem
    HAVE_AHEM,
#endif
};

// Test fonts for CSS 2.1 test suite
const char* testFontList[] =
{
    // Ahem!
    TEST_FONTS "/AhemExtra/AHEM_Ahem!.TTF",
    // MissingNormal
    TEST_FONTS "/AhemExtra/AHEM_MissingNormal.TTF",
    // SmallCaps
    TEST_FONTS "/AhemExtra/AHEM_SmallCaps.TTF",
    // MissingItalicOblique
    TEST_FONTS "/AhemExtra/AHEM_MissingItalicOblique.TTF",
    // White Space
    TEST_FONTS "/AhemExtra/AHEM_WhiteSpace.TTF",
    // cursive
    TEST_FONTS "/AhemExtra/AHEM_cursive.TTF",
    // default
    TEST_FONTS "/AhemExtra/AHEM_default.TTF",
    // fantasy
    TEST_FONTS "/AhemExtra/AHEM_fantasy.TTF",
    // inherit
    TEST_FONTS "/AhemExtra/AHEM_inherit.TTF",
    // initial
    TEST_FONTS "/AhemExtra/AHEM_initial.TTF",
    // monospace
    TEST_FONTS "/AhemExtra/AHEM_monospace.TTF",
    // serif
    TEST_FONTS "/AhemExtra/AHEM_serif.TTF",
    // sans-serif
    TEST_FONTS "/AhemExtra/AHEM_sans-serif.TTF",
    // CSSTest ASCII
    TEST_FONTS "/CSSTest/csstest-ascii.ttf",
    // CSSTest Basic
    TEST_FONTS "/CSSTest/csstest-basic-bold.ttf",
    TEST_FONTS "/CSSTest/csstest-basic-bolditalic.ttf",
    TEST_FONTS "/CSSTest/csstest-basic-italic.ttf",
    TEST_FONTS "/CSSTest/csstest-basic-regular.ttf",
    // CSSTest Fallback
    TEST_FONTS "/CSSTest/csstest-fallback.ttf",
    // small-caps 1in CSSTest FamilyName Funky
    TEST_FONTS "/CSSTest/csstest-familyname-funkyA.ttf",
    // x-large CSSTest FamilyName Funky
    TEST_FONTS "/CSSTest/csstest-familyname-funkyB.ttf",
    // 12px CSSTest FamilyName Funky
    TEST_FONTS "/CSSTest/csstest-familyname-funkyC.ttf",
    // CSSTest FamilyName
    TEST_FONTS "/CSSTest/csstest-familyname.ttf",
    TEST_FONTS "/CSSTest/csstest-familyname-bold.ttf",
    // CSSTest Verify
    TEST_FONTS "/CSSTest/csstest-verify.ttf",
    // CSSTest Weights
    TEST_FONTS "/CSSTest/csstest-weights-100.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-1479-w1.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-1479-w4.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-1479-w7.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-1479-w9.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-15-w1.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-15-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-200.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-24-w2.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-24-w4.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-2569-w2.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-2569-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-2569-w6.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-2569-w9.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-258-w2.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-258-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-258-w8.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-300.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-3589-w3.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-3589-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-3589-w8.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-3589-w9.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-400.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-47-w4.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-47-w7.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-500.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-600.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-700.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-800.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-900.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w1.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w2.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w3.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w4.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w6.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w7.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w8.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w9.ttf",
    TEST_FONTS "/CSSTest/csstest-weights.ttf",
};

}

void FontDatabase::loadBaseFonts(FontManager* manager)
{
    for (auto i = fontList; i < &fontList[sizeof fontList / sizeof fontList[0]]; ++i)
        manager->loadFont(*i);
}

void FontDatabase::loadTestFonts(FontManager* manager)
{
    for (auto i = testFontList; i < &testFontList[sizeof testFontList / sizeof testFontList[0]]; ++i)
        manager->loadFont(*i);
}
<commit_msg>(FontDatabase::loadBaseFonts) : Do not throw even if a font face is not found.<commit_after>/*
 * Copyright 2010-2012 Esrille Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "FontDatabase.h"
#include "FontManager.h"

namespace {

const char* fontList[] =
{
    // LiberationSans
    LIBERATON_TTF "/LiberationSans-BoldItalic.ttf",
    LIBERATON_TTF "/LiberationSans-Italic.ttf",
    LIBERATON_TTF "/LiberationSans-Regular.ttf",
    LIBERATON_TTF "/LiberationSans-Bold.ttf",
    // LiberationSerif
    LIBERATON_TTF "/LiberationSerif-BoldItalic.ttf",
    LIBERATON_TTF "/LiberationSerif-Italic.ttf",
    LIBERATON_TTF "/LiberationSerif-Regular.ttf",
    LIBERATON_TTF "/LiberationSerif-Bold.ttf",
    // LiberationMono
    LIBERATON_TTF "/LiberationMono-BoldItalic.ttf",
    LIBERATON_TTF "/LiberationMono-Italic.ttf",
    LIBERATON_TTF "/LiberationMono-Regular.ttf",
    LIBERATON_TTF "/LiberationMono-Bold.ttf",
#ifdef HAVE_IPA_PGOTHIC
    // IPAPGothic
    HAVE_IPA_PGOTHIC,
#endif
#ifdef HAVE_IPA_PMINCHO
    // IPAPMincho
    HAVE_IPA_PMINCHO,
#endif
#ifdef HAVE_IPA_GOTHIC
    // IPAGothic
    HAVE_IPA_GOTHIC,
#endif
#ifdef HAVE_IPA_MINCHO
    // IPAMincho
    HAVE_IPA_MINCHO,
#endif
#ifdef DEJAVU_TTF
    DEJAVU_TTF "/DejaVuSans-Bold.ttf",
    DEJAVU_TTF "/DejaVuSans-BoldOblique.ttf",
    DEJAVU_TTF "/DejaVuSans-ExtraLight.ttf",
    DEJAVU_TTF "/DejaVuSans-Oblique.ttf",
    DEJAVU_TTF "/DejaVuSans.ttf",
    DEJAVU_TTF "/DejaVuSansMono-Bold.ttf",
    DEJAVU_TTF "/DejaVuSansMono-BoldOblique.ttf",
    DEJAVU_TTF "/DejaVuSansMono-Oblique.ttf",
    DEJAVU_TTF "/DejaVuSansMono.ttf",
    DEJAVU_TTF "/DejaVuSerif-Bold.ttf",
    DEJAVU_TTF "/DejaVuSerif-BoldItalic.ttf",
    DEJAVU_TTF "/DejaVuSerif-Italic.ttf",
    DEJAVU_TTF "/DejaVuSerif.ttf",
#endif
#ifdef HAVE_AEGEAN
    // Aegean
    HAVE_AEGEAN,
#endif
#ifdef HAVE_AHEM
    // Ahem
    HAVE_AHEM,
#endif
};

// Test fonts for CSS 2.1 test suite
const char* testFontList[] =
{
    // Ahem!
    TEST_FONTS "/AhemExtra/AHEM_Ahem!.TTF",
    // MissingNormal
    TEST_FONTS "/AhemExtra/AHEM_MissingNormal.TTF",
    // SmallCaps
    TEST_FONTS "/AhemExtra/AHEM_SmallCaps.TTF",
    // MissingItalicOblique
    TEST_FONTS "/AhemExtra/AHEM_MissingItalicOblique.TTF",
    // White Space
    TEST_FONTS "/AhemExtra/AHEM_WhiteSpace.TTF",
    // cursive
    TEST_FONTS "/AhemExtra/AHEM_cursive.TTF",
    // default
    TEST_FONTS "/AhemExtra/AHEM_default.TTF",
    // fantasy
    TEST_FONTS "/AhemExtra/AHEM_fantasy.TTF",
    // inherit
    TEST_FONTS "/AhemExtra/AHEM_inherit.TTF",
    // initial
    TEST_FONTS "/AhemExtra/AHEM_initial.TTF",
    // monospace
    TEST_FONTS "/AhemExtra/AHEM_monospace.TTF",
    // serif
    TEST_FONTS "/AhemExtra/AHEM_serif.TTF",
    // sans-serif
    TEST_FONTS "/AhemExtra/AHEM_sans-serif.TTF",
    // CSSTest ASCII
    TEST_FONTS "/CSSTest/csstest-ascii.ttf",
    // CSSTest Basic
    TEST_FONTS "/CSSTest/csstest-basic-bold.ttf",
    TEST_FONTS "/CSSTest/csstest-basic-bolditalic.ttf",
    TEST_FONTS "/CSSTest/csstest-basic-italic.ttf",
    TEST_FONTS "/CSSTest/csstest-basic-regular.ttf",
    // CSSTest Fallback
    TEST_FONTS "/CSSTest/csstest-fallback.ttf",
    // small-caps 1in CSSTest FamilyName Funky
    TEST_FONTS "/CSSTest/csstest-familyname-funkyA.ttf",
    // x-large CSSTest FamilyName Funky
    TEST_FONTS "/CSSTest/csstest-familyname-funkyB.ttf",
    // 12px CSSTest FamilyName Funky
    TEST_FONTS "/CSSTest/csstest-familyname-funkyC.ttf",
    // CSSTest FamilyName
    TEST_FONTS "/CSSTest/csstest-familyname.ttf",
    TEST_FONTS "/CSSTest/csstest-familyname-bold.ttf",
    // CSSTest Verify
    TEST_FONTS "/CSSTest/csstest-verify.ttf",
    // CSSTest Weights
    TEST_FONTS "/CSSTest/csstest-weights-100.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-1479-w1.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-1479-w4.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-1479-w7.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-1479-w9.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-15-w1.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-15-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-200.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-24-w2.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-24-w4.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-2569-w2.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-2569-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-2569-w6.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-2569-w9.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-258-w2.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-258-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-258-w8.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-300.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-3589-w3.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-3589-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-3589-w8.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-3589-w9.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-400.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-47-w4.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-47-w7.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-500.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-600.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-700.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-800.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-900.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w1.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w2.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w3.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w4.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w5.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w6.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w7.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w8.ttf",
    TEST_FONTS "/CSSTest/csstest-weights-full-w9.ttf",
    TEST_FONTS "/CSSTest/csstest-weights.ttf",
};

}

void FontDatabase::loadBaseFonts(FontManager* manager)
{
    for (auto i = fontList; i < &fontList[sizeof fontList / sizeof fontList[0]]; ++i) {
        try {
            manager->loadFont(*i);
        } catch (...) {
        }
    }
}

void FontDatabase::loadTestFonts(FontManager* manager)
{
    for (auto i = testFontList; i < &testFontList[sizeof testFontList / sizeof testFontList[0]]; ++i) {
        try {
            manager->loadFont(*i);
        } catch (...) {
        }
    }
}
<|endoftext|>


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

#include "condor_common.h"

#include "debug.h"
#include "condor_daemon_core.h"
#include "MyString.h"

debug_level_t debug_level    = DEBUG_NORMAL;
const char *        debug_progname = NULL;

// sometimes we want to keep a cache for our log message when DAGMan is
// emitting thousands of them quickly and we could be writing the log to
// and NFS server.
static bool cache_enabled = false;		// do I honor start/stop requests?
static bool cache_is_caching = false;	// have I started caching or stopped it?
static MyString cache;

// NOTE: if this isn't changed via a config file or something, then all lines
// are immediately written as they enter the cache.
static int cache_size = 0;

// from condor_util_lib/dprintf.c
extern int DebugUseTimestamps;
extern int DebugFlags;
extern FILE *DebugFP;

static void debug_cache_insert(int flags, const char *fmt, va_list args);

/*--------------------------------------------------------------------------*/
void
debug_printf( debug_level_t level, char *fmt, ... ) {
	if( DEBUG_LEVEL( level ) ) {
		if (cache_enabled == false ||
			(cache_enabled == true && cache_is_caching == false))
		{
			// let it go through right away
			va_list args;
			va_start( args, fmt );
			_condor_dprintf_va( D_ALWAYS, fmt, args );
			va_end( args );
		} else {
			// store it for later flushing
			va_list args;
			va_start( args, fmt );
			debug_cache_insert( D_ALWAYS, fmt, args );
			va_end( args );
		}
	}
}

/*--------------------------------------------------------------------------*/
void
debug_dprintf( int flags, debug_level_t level, char *fmt, ... ) {

	if( DEBUG_LEVEL( level ) ) {

		if (cache_enabled == true && cache_is_caching == true) {
			// This call is uncachable because I haven't implemented how we
			// can cache a debug_dprintf() because it specifies the flags
			// parameter that _condor_dprintf_va needs. So for now, we'll
			// just flush the cache and this isn't so bad because the place
			// where we need the caching doesn't call debug_dprintf() within
			// the critical section of the caching.

			// Note: After thinking about it, maybe I can
			// check the flag against DebugFlags and see if
			// it should pass, if so, we can shove it into
			// the cache. I'll attempt it later.

			dprintf(D_ALWAYS, "Uncachable dprintf forcing log line flush.\n");
			debug_cache_flush();
		}

		va_list args;
		va_start( args, fmt );
		_condor_dprintf_va( flags, fmt, args );
		va_end( args );

	}
}

/*--------------------------------------------------------------------------*/
void
debug_error( int error, debug_level_t level, char *fmt, ... ) {

	// make sure these come out before emitting the error
	debug_cache_flush();
	debug_cache_stop_caching();
	debug_cache_disable();

    if( DEBUG_LEVEL( level ) ) {
        va_list args;
        va_start( args, fmt );
        _condor_dprintf_va( D_ALWAYS, fmt, args );
        va_end( args );
    }
	DC_Exit( error );
}

/*--------------------------------------------------------------------------*/
void
debug_cache_enable(void)
{
	cache_enabled = true;
	dprintf(D_ALWAYS,
		"Enabling log line cache for increased NFS performance.\n");
}

/*--------------------------------------------------------------------------*/
void
debug_cache_disable(void)
{
	debug_cache_flush();
	cache_enabled = false;
	dprintf(D_ALWAYS, "Disabling log line cache.\n");
}

/*--------------------------------------------------------------------------*/
void
debug_cache_start_caching(void)
{
	if (cache_enabled) {
		cache_is_caching = true;
		dprintf(D_ALWAYS, "Starting to cache log lines.\n");
	}
}

/*--------------------------------------------------------------------------*/
void
debug_cache_stop_caching(void)
{
	if (cache_enabled) {
		debug_cache_flush();
		cache_is_caching = false;
		dprintf(D_ALWAYS, "Stopping the caching of log lines.\n");
	}
}

/*--------------------------------------------------------------------------*/
void
debug_cache_insert(int flags, const char *fmt, va_list args)
{
	time_t clock_now;
	struct tm *tm = NULL;

	MyString tstamp, fds, line, pid;
	pid_t my_pid;

	// XXX TODO
	// handle flags...
	// For now, always assume D_ALWAYS since the caller assumes it as well.

	// HACK
	// Note: This nasty bit of code is copied in spirit from dprintf.c
	// It needs abstracting out a little bit from there into its own
	// function, but this is a quick hack for LIGO. I'll come back to it
	// and do it better later when I have time.
	clock_now = time(NULL);
	if (!DebugUseTimestamps) {
		tm = localtime(&clock_now);
	}

	if (((DebugFlags|flags) & D_NOHEADER) == 0) {
		if (DebugUseTimestamps) {
			tstamp.sprintf("(%d) ", (int)clock_now);
		} else {
			tstamp.sprintf("%d/%d %02d:%02d:%02d ",
				tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
				tm->tm_min, tm->tm_sec );
		}

		if ((DebugFlags|flags) & D_FDS) {
			fds.sprintf("(fd:%d) ", fileno(DebugFP) );
		}

		if ((DebugFlags|flags) & D_PID) {
#ifdef WIN32
			my_pid = (int) GetCurrentProcessId();
#else
			my_pid = (int) getpid();
#endif
			pid.sprintf("(pid:%d) ", my_pid );

		}

		// We skip running of the DebugId function, since it needs to
		// emit to a FILE* and we can't store it in the cache. It, as of this
		// time, isn't used in condor_dagman.
	}

	// figure out the line the user needs to emit.
	line.vsprintf(fmt, args);

	// build the cached line and add it to the cache
	cache += (tstamp + fds + line);

	// if the cache has surpassed the highwater mark, then flush it.
	if (cache.Length() > cache_size) {
		debug_cache_flush();
	}
}

/*--------------------------------------------------------------------------*/
void
debug_cache_flush(void)
{
	// This emits a dprintf call which could write out up to the maximum
	// size of the cache. The lines in the cache are newline delimited.
	// We bracket the output a bit so people know what happened.

	if (cache != "") {
		dprintf(D_ALWAYS, "LOG LINE CACHE: Begin Flush\n");
		dprintf(D_ALWAYS | D_NOHEADER, "%s", cache.Value());
		dprintf(D_ALWAYS, "LOG LINE CACHE: End Flush\n");
		cache = "";
	}
}

/*--------------------------------------------------------------------------*/
void debug_cache_set_size(int size)
{
	// Get rid of what is there, if anything.
	if (cache_enabled && cache_is_caching) {
		debug_cache_flush();
	}

	if (size < 0) {
		cache_size = 0;
	}

	cache_size = size;
}

<commit_msg>Made some extern definitions wrapped in extern "C".<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include "condor_common.h"

#include "debug.h"
#include "condor_daemon_core.h"
#include "MyString.h"

debug_level_t debug_level    = DEBUG_NORMAL;
const char *        debug_progname = NULL;

// sometimes we want to keep a cache for our log message when DAGMan is
// emitting thousands of them quickly and we could be writing the log to
// and NFS server.
static bool cache_enabled = false;		// do I honor start/stop requests?
static bool cache_is_caching = false;	// have I started caching or stopped it?
static MyString cache;

// NOTE: if this isn't changed via a config file or something, then all lines
// are immediately written as they enter the cache.
static int cache_size = 0;

// from condor_util_lib/dprintf.c
// from condor_util_lib/dprintf_common.c
extern "C" {
extern int DebugFlags;
extern int DebugUseTimestamps;
extern FILE *DebugFP;
}

static void debug_cache_insert(int flags, const char *fmt, va_list args);

/*--------------------------------------------------------------------------*/
void
debug_printf( debug_level_t level, char *fmt, ... ) {
	if( DEBUG_LEVEL( level ) ) {
		if (cache_enabled == false ||
			(cache_enabled == true && cache_is_caching == false))
		{
			// let it go through right away
			va_list args;
			va_start( args, fmt );
			_condor_dprintf_va( D_ALWAYS, fmt, args );
			va_end( args );
		} else {
			// store it for later flushing
			va_list args;
			va_start( args, fmt );
			debug_cache_insert( D_ALWAYS, fmt, args );
			va_end( args );
		}
	}
}

/*--------------------------------------------------------------------------*/
void
debug_dprintf( int flags, debug_level_t level, char *fmt, ... ) {

	if( DEBUG_LEVEL( level ) ) {

		if (cache_enabled == true && cache_is_caching == true) {
			// This call is uncachable because I haven't implemented how we
			// can cache a debug_dprintf() because it specifies the flags
			// parameter that _condor_dprintf_va needs. So for now, we'll
			// just flush the cache and this isn't so bad because the place
			// where we need the caching doesn't call debug_dprintf() within
			// the critical section of the caching.

			// Note: After thinking about it, maybe I can
			// check the flag against DebugFlags and see if
			// it should pass, if so, we can shove it into
			// the cache. I'll attempt it later.

			dprintf(D_ALWAYS, "Uncachable dprintf forcing log line flush.\n");
			debug_cache_flush();
		}

		va_list args;
		va_start( args, fmt );
		_condor_dprintf_va( flags, fmt, args );
		va_end( args );

	}
}

/*--------------------------------------------------------------------------*/
void
debug_error( int error, debug_level_t level, char *fmt, ... ) {

	// make sure these come out before emitting the error
	debug_cache_flush();
	debug_cache_stop_caching();
	debug_cache_disable();

    if( DEBUG_LEVEL( level ) ) {
        va_list args;
        va_start( args, fmt );
        _condor_dprintf_va( D_ALWAYS, fmt, args );
        va_end( args );
    }
	DC_Exit( error );
}

/*--------------------------------------------------------------------------*/
void
debug_cache_enable(void)
{
	cache_enabled = true;
	dprintf(D_ALWAYS,
		"Enabling log line cache for increased NFS performance.\n");
}

/*--------------------------------------------------------------------------*/
void
debug_cache_disable(void)
{
	debug_cache_flush();
	cache_enabled = false;
	dprintf(D_ALWAYS, "Disabling log line cache.\n");
}

/*--------------------------------------------------------------------------*/
void
debug_cache_start_caching(void)
{
	if (cache_enabled) {
		cache_is_caching = true;
		dprintf(D_ALWAYS, "Starting to cache log lines.\n");
	}
}

/*--------------------------------------------------------------------------*/
void
debug_cache_stop_caching(void)
{
	if (cache_enabled) {
		debug_cache_flush();
		cache_is_caching = false;
		dprintf(D_ALWAYS, "Stopping the caching of log lines.\n");
	}
}

/*--------------------------------------------------------------------------*/
void
debug_cache_insert(int flags, const char *fmt, va_list args)
{
	time_t clock_now;
	struct tm *tm = NULL;

	MyString tstamp, fds, line, pid;
	pid_t my_pid;

	// XXX TODO
	// handle flags...
	// For now, always assume D_ALWAYS since the caller assumes it as well.

	// HACK
	// Note: This nasty bit of code is copied in spirit from dprintf.c
	// It needs abstracting out a little bit from there into its own
	// function, but this is a quick hack for LIGO. I'll come back to it
	// and do it better later when I have time.
	clock_now = time(NULL);
	if (!DebugUseTimestamps) {
		tm = localtime(&clock_now);
	}

	if (((DebugFlags|flags) & D_NOHEADER) == 0) {
		if (DebugUseTimestamps) {
			tstamp.sprintf("(%d) ", (int)clock_now);
		} else {
			tstamp.sprintf("%d/%d %02d:%02d:%02d ",
				tm->tm_mon + 1, tm->tm_mday, tm->tm_hour,
				tm->tm_min, tm->tm_sec );
		}

		if ((DebugFlags|flags) & D_FDS) {
			fds.sprintf("(fd:%d) ", fileno(DebugFP) );
		}

		if ((DebugFlags|flags) & D_PID) {
#ifdef WIN32
			my_pid = (int) GetCurrentProcessId();
#else
			my_pid = (int) getpid();
#endif
			pid.sprintf("(pid:%d) ", my_pid );

		}

		// We skip running of the DebugId function, since it needs to
		// emit to a FILE* and we can't store it in the cache. It, as of this
		// time, isn't used in condor_dagman.
	}

	// figure out the line the user needs to emit.
	line.vsprintf(fmt, args);

	// build the cached line and add it to the cache
	cache += (tstamp + fds + line);

	// if the cache has surpassed the highwater mark, then flush it.
	if (cache.Length() > cache_size) {
		debug_cache_flush();
	}
}

/*--------------------------------------------------------------------------*/
void
debug_cache_flush(void)
{
	// This emits a dprintf call which could write out up to the maximum
	// size of the cache. The lines in the cache are newline delimited.
	// We bracket the output a bit so people know what happened.

	if (cache != "") {
		dprintf(D_ALWAYS, "LOG LINE CACHE: Begin Flush\n");
		dprintf(D_ALWAYS | D_NOHEADER, "%s", cache.Value());
		dprintf(D_ALWAYS, "LOG LINE CACHE: End Flush\n");
		cache = "";
	}
}

/*--------------------------------------------------------------------------*/
void debug_cache_set_size(int size)
{
	// Get rid of what is there, if anything.
	if (cache_enabled && cache_is_caching) {
		debug_cache_flush();
	}

	if (size < 0) {
		cache_size = 0;
	}

	cache_size = size;
}

<|endoftext|>


<commit_before>#ifndef MINIASCAPE_STEPPER
#define MINIASCAPE_STEPPER
#include "core/World.hpp"
#include "core/Rule.hpp"
#include "core/RandomNumberGenerator.hpp"
#include "util/zip_iterator.hpp"

namespace miniascape
{

template<typename T_traits>
class SynchronousStepper
{
  public:
    using traits_type = T_traits;
    using time_type   = typename traits_type::time_type;
    using state_type  = typename traits_type::state_type;
    using cell_type   = typename traits_type::cell_type;
    using rule_type   = RuleBase<traits_type>;
    using world_type  = World<traits_type>;

    SynchronousStepper() = default;
    ~SynchronousStepper() = default;
    time_type step(world_type& world, const rule_type& rule) const;
};

template<typename T_traits>
typename SynchronousStepper<T_traits>::time_type
SynchronousStepper<T_traits>::step(
        world_type& world, const rule_type& rule) const
{
    std::vector<state_type> temp(world.size());

    for(auto cell = make_zip(world.cbegin(), temp.begin());
            cell != make_zip(world.cend(),   temp.end()); ++cell)
        *get<1>(cell) = rule.step(**get<0>(cell));

    for(auto cell = make_zip(world.begin(), temp.begin());
            cell != make_zip(world.end(),   temp.end()); ++cell)
        (*get<0>(cell))->state = *get<1>(cell);

    return rule.delta_t();
}

template<typename T_traits>
class AsynchronousRandomStepper
{
  public:
    using traits_type = T_traits;
    using time_type   = typename traits_type::time_type;
    using state_type  = typename traits_type::state_type;
    using cell_type   = typename traits_type::cell_type;
    using rule_type   = RuleBase<traits_type>;
    using world_type  = World<traits_type>;

    AsynchronousRandomStepper(const std::shared_ptr<RandomNumberGenerator>& rng)
        : rng_(rng)
    {}
    ~AsynchronousRandomStepper() = default;
    time_type step(world_type& world, const rule_type& rule) const;

    const std::shared_ptr<RandomNumberGenerator> rng_;
};

template<typename T_traits>
typename AsynchronousRandomStepper<T_traits>::time_type
AsynchronousRandomStepper<T_traits>::step(
        world_type& world, const rule_type& rule) const
{
    const std::size_t idx = rng_->uniform_int<std::size_t>(0, world.size()-1);
    world.at(idx)->state = rule.step(*(world.at(idx)));

    return rule.delta_t();
}

}

#endif /* MINIASCAPE_STEPPER */
<commit_msg>add asynchronous successive stepper<commit_after>#ifndef MINIASCAPE_STEPPER
#define MINIASCAPE_STEPPER
#include "core/World.hpp"
#include "core/Rule.hpp"
#include "core/RandomNumberGenerator.hpp"
#include "util/zip_iterator.hpp"

namespace miniascape
{

template<typename T_traits>
class SynchronousStepper
{
  public:
    using traits_type = T_traits;
    using time_type   = typename traits_type::time_type;
    using state_type  = typename traits_type::state_type;
    using cell_type   = typename traits_type::cell_type;
    using rule_type   = RuleBase<traits_type>;
    using world_type  = World<traits_type>;

    SynchronousStepper() = default;
    ~SynchronousStepper() = default;
    time_type step(world_type& world, const rule_type& rule) const;
};

template<typename T_traits>
typename SynchronousStepper<T_traits>::time_type
SynchronousStepper<T_traits>::step(
        world_type& world, const rule_type& rule) const
{
    std::vector<state_type> temp(world.size());

    for(auto cell = make_zip(world.cbegin(), temp.begin());
            cell != make_zip(world.cend(),   temp.end()); ++cell)
        *get<1>(cell) = rule.step(**get<0>(cell));

    for(auto cell = make_zip(world.begin(), temp.begin());
            cell != make_zip(world.end(),   temp.end()); ++cell)
        (*get<0>(cell))->state = *get<1>(cell);

    return rule.delta_t();
}

template<typename T_traits>
class AsynchronousRandomStepper
{
  public:
    using traits_type = T_traits;
    using time_type   = typename traits_type::time_type;
    using state_type  = typename traits_type::state_type;
    using cell_type   = typename traits_type::cell_type;
    using rule_type   = RuleBase<traits_type>;
    using world_type  = World<traits_type>;

    AsynchronousRandomStepper(const std::shared_ptr<RandomNumberGenerator>& rng)
        : rng_(rng)
    {}
    ~AsynchronousRandomStepper() = default;
    time_type step(world_type& world, const rule_type& rule) const;

    const std::shared_ptr<RandomNumberGenerator> rng_;
};

template<typename T_traits>
inline typename AsynchronousRandomStepper<T_traits>::time_type
AsynchronousRandomStepper<T_traits>::step(
        world_type& world, const rule_type& rule) const
{
    const std::size_t idx = rng_->uniform_int<std::size_t>(0, world.size()-1);
    world.at(idx)->state = rule.step(*(world.at(idx)));

    return rule.delta_t();
}

template<typename T_traits>
class AsynchronousSuccessiveStepper
{
  public:
    using traits_type = T_traits;
    using time_type   = typename traits_type::time_type;
    using state_type  = typename traits_type::state_type;
    using cell_type   = typename traits_type::cell_type;
    using rule_type   = RuleBase<traits_type>;
    using world_type  = World<traits_type>;

    AsynchronousSuccessiveStepper() = default;
    ~AsynchronousSuccessiveStepper() = default;
    time_type step(world_type& world, const rule_type& rule) const;

    const std::shared_ptr<RandomNumberGenerator> rng_;
};

template<typename T_traits>
inline typename AsynchronousSuccessiveStepper<T_traits>::time_type
AsynchronousSuccessiveStepper<T_traits>::step(
        world_type& world, const rule_type& rule) const
{
    for(auto cell = world.cbegin(); cell != world.cend(); ++cell)
        (*cell)->state = rule.step(**cell);

    return rule.delta_t();
}

}

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


<commit_before>#include "feature_utils.hpp"
#include "classificator.hpp"
#include "../geometry/point2d.hpp"
#include "../base/base.hpp"
#include "../std/vector.hpp"

namespace feature
{

namespace impl
{

class FeatureEstimator
{
public:

  FeatureEstimator()
  {
    m_TypeContinent   = GetType("place", "continent");
    m_TypeCountry     = GetType("place", "country");
    m_TypeCity        = GetType("place", "city");
    m_TypeCityCapital = GetType("place", "city", "capital");
    m_TypeTown        = GetType("place", "town");
    m_TypeVillage     = GetType("place", "village");
  }

  m2::RectD GetViewport(FeatureType const & feature) const
  {
    m2::RectD limitRect = feature.GetLimitRect(-2);
    if (feature.GetFeatureType() != feature::GEOM_POINT)
      return limitRect;

    m2::PointD maxSizeMeters(100, 100);
    FeatureBase::GetTypesFn types;
    feature.ForEachTypeRef(types);
    for (size_t i = 0; i < types.m_size; ++i)
    {
      m2::PointD const sizeMeters = GetSizeForType(types.m_types[i], feature);
      maxSizeMeters.x = max(maxSizeMeters.x, sizeMeters.x);
      maxSizeMeters.y = max(maxSizeMeters.y, sizeMeters.y);
    }

    m2::PointD const centerXY = limitRect.Center();
    return MercatorBounds::RectByCenterXYAndSizeInMeters(centerXY.x, centerXY.y,
                                                         maxSizeMeters.x, maxSizeMeters.y);
  }

  uint8_t GetSearchRank(FeatureType const & feature) const
  {
    uint32_t const population = feature.GetPopulation();
    return static_cast<uint8_t>(log(double(population)) / log(1.1));
  }

private:

  // Returns width and height (lon and lat) for a given type.
  m2::PointD GetSizeForType(uint32_t const type, FeatureType const & feature) const
  {
    static double const km = 1000.0;
    if (type == m_TypeContinent)
      return m2::PointD(5000*km, 5000*km);

    /// @todo Load countries bounding rects.
    if (type == m_TypeCountry)
      return m2::PointD(500*km, 500*km);

    if (type == m_TypeCity || type == m_TypeCityCapital)
    {
      double const radius = sqrt(static_cast<double>(feature.GetPopulation() / 3000));
      return m2::PointD(radius*km, radius*km);
    }

    if (type == m_TypeTown)
      return m2::PointD(8*km, 8*km);
    if (type == m_TypeVillage)
      return m2::PointD(3*km, 3*km);
    return m2::PointD(0, 0);
  }

  static uint32_t GetType(string const & s1,
                          string const & s2 = string(),
                          string const & s3 = string())
  {
    vector<string> path;
    path.push_back(s1);
    if (!s2.empty()) path.push_back(s2);
    if (!s3.empty()) path.push_back(s3);
    return classif().GetTypeByPath(path);
  }

  uint32_t m_TypeContinent;
  uint32_t m_TypeCountry;
  uint32_t m_TypeCity;
  uint32_t m_TypeCityCapital;
  uint32_t m_TypeTown;
  uint32_t m_TypeVillage;
};

FeatureEstimator const & GetFeatureEstimator()
{
  static FeatureEstimator const featureEstimator;
  return featureEstimator;
}

}  // namespace feature::impl

m2::RectD GetFeatureViewport(FeatureType const & feature)
{
  return impl::GetFeatureEstimator().GetViewport(feature);
}

uint8_t GetSearchRank(FeatureType const & feature)
{
  return impl::GetFeatureEstimator().GetSearchRank(feature);
}

} // namespace feature
<commit_msg>[search] Take into account some feature types in GetSearchRank(). TODO: Support more types.<commit_after>#include "feature_utils.hpp"
#include "classificator.hpp"
#include "../geometry/point2d.hpp"
#include "../base/base.hpp"
#include "../std/vector.hpp"

namespace feature
{

namespace impl
{

class FeatureEstimator
{
public:

  FeatureEstimator()
  {
    m_TypeContinent   = GetType("place", "continent");
    m_TypeCountry     = GetType("place", "country");
    m_TypeCity        = GetType("place", "city");
    m_TypeCityCapital = GetType("place", "city", "capital");
    m_TypeTown        = GetType("place", "town");
    m_TypeVillage     = GetType("place", "village");
  }

  m2::RectD GetViewport(FeatureType const & feature) const
  {
    m2::RectD limitRect = feature.GetLimitRect(-2);
    if (feature.GetFeatureType() != feature::GEOM_POINT)
      return limitRect;

    m2::PointD maxSizeMeters(100, 100);
    FeatureBase::GetTypesFn types;
    feature.ForEachTypeRef(types);
    for (size_t i = 0; i < types.m_size; ++i)
    {
      m2::PointD const sizeMeters = GetSizeForType(types.m_types[i], feature);
      maxSizeMeters.x = max(maxSizeMeters.x, sizeMeters.x);
      maxSizeMeters.y = max(maxSizeMeters.y, sizeMeters.y);
    }

    m2::PointD const centerXY = limitRect.Center();
    return MercatorBounds::RectByCenterXYAndSizeInMeters(centerXY.x, centerXY.y,
                                                         maxSizeMeters.x, maxSizeMeters.y);
  }

  uint8_t GetSearchRank(FeatureType const & feature) const
  {
    uint32_t const population = feature.GetPopulation();

    FeatureBase::GetTypesFn types;
    feature.ForEachTypeRef(types);
    for (size_t i = 0; i < types.m_size; ++i)
    {
      if (types.m_types[i] == m_TypeVillage)
        population = max(population, 1000);
      else if (types.m_types[i] == m_TypeTown)
        population = max(population, 10000);
      else if (types.m_types[i] == m_TypeCity)
        population = max(population, 100000);
      else if (types.m_types[i] == m_TypeCityCapital)
        population = max(population, 1000000);
      else if (types.m_types[i] == m_TypeCountry)
        population = max(population, 2000000);
      else if (types.m_types[i] == m_TypeContinent)
        population = max(population, 20000000);
    }

    return static_cast<uint8_t>(log(double(population)) / log(1.1));
  }

private:

  // Returns width and height (lon and lat) for a given type.
  m2::PointD GetSizeForType(uint32_t const type, FeatureType const & feature) const
  {
    static double const km = 1000.0;
    if (type == m_TypeContinent)
      return m2::PointD(5000*km, 5000*km);

    /// @todo Load countries bounding rects.
    if (type == m_TypeCountry)
      return m2::PointD(500*km, 500*km);

    if (type == m_TypeCity || type == m_TypeCityCapital)
    {
      double const radius = sqrt(static_cast<double>(feature.GetPopulation() / 3000));
      return m2::PointD(radius*km, radius*km);
    }

    if (type == m_TypeTown)
      return m2::PointD(8*km, 8*km);
    if (type == m_TypeVillage)
      return m2::PointD(3*km, 3*km);
    return m2::PointD(0, 0);
  }

  static uint32_t GetType(string const & s1,
                          string const & s2 = string(),
                          string const & s3 = string())
  {
    vector<string> path;
    path.push_back(s1);
    if (!s2.empty()) path.push_back(s2);
    if (!s3.empty()) path.push_back(s3);
    return classif().GetTypeByPath(path);
  }

  uint32_t m_TypeContinent;
  uint32_t m_TypeCountry;
  uint32_t m_TypeCity;
  uint32_t m_TypeCityCapital;
  uint32_t m_TypeTown;
  uint32_t m_TypeVillage;
};

FeatureEstimator const & GetFeatureEstimator()
{
  static FeatureEstimator const featureEstimator;
  return featureEstimator;
}

}  // namespace feature::impl

m2::RectD GetFeatureViewport(FeatureType const & feature)
{
  return impl::GetFeatureEstimator().GetViewport(feature);
}

uint8_t GetSearchRank(FeatureType const & feature)
{
  return impl::GetFeatureEstimator().GetSearchRank(feature);
}

} // namespace feature
<|endoftext|>


<commit_before>/**
 *
 * Copyright (c) 2010, Kitty Barnett
 *
 * The source code in this file is provided to you under the terms of the
 * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt
 * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt
 *
 * By copying, modifying or distributing this software, you acknowledge that
 * you have read and understood your obligations described above, and agree to
 * abide by those obligations.
 *
 */

#include "linden_common.h"

#include "lldir.h"
#include "llsdserialize.h"

#include "llhunspell.h"

#if LL_WINDOWS
	#include <hunspell/hunspelldll.h>
	#pragma comment(lib, "libhunspell.lib")
#else
	#include <hunspell/hunspell.hxx>
#endif

// ============================================================================
// Static variables
//

static const std::string c_strDictCustomSuffix = "_custom";
static const std::string c_strDictIgnoreSuffix = "_ignore";

// ============================================================================

LLHunspellWrapper::LLHunspellWrapper()
	: m_pHunspell(NULL)
{
	m_strDictionaryPath = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "dictionaries", "");

	// Load dictionary information (file name, friendly name, ...)
	llifstream fileDictMap(m_strDictionaryPath + "dictionaries.xml", std::ios::binary);
	if (fileDictMap.is_open())
		LLSDSerialize::fromXMLDocument(m_sdDictionaryMap, fileDictMap);

	// Look for installed dictionaries
	std::string strTempPath;
	for (LLSD::array_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
			itDictInfo != endDictInfo; ++itDictInfo)
	{
		LLSD& sdDict = *itDictInfo;
		strTempPath = (sdDict.has("name")) ? m_strDictionaryPath + sdDict["name"].asString() : LLStringUtil::null;
		sdDict["installed"] =
			(!strTempPath.empty()) && (gDirUtilp->fileExists(strTempPath + ".aff")) && (gDirUtilp->fileExists(strTempPath + ".dic"));
		sdDict["has_custom"] = (!strTempPath.empty()) && (gDirUtilp->fileExists(strTempPath + c_strDictCustomSuffix + ".dic"));
		sdDict["has_ignore"] = (!strTempPath.empty()) && (gDirUtilp->fileExists(strTempPath + c_strDictIgnoreSuffix + ".dic"));
	}
}

LLHunspellWrapper::~LLHunspellWrapper()
{
	delete m_pHunspell;
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
bool LLHunspellWrapper::checkSpelling(const std::string& strWord) const
{
	if ( (!m_pHunspell) || (strWord.length() < 3) || (0 != m_pHunspell->spell(strWord.c_str())) )
	{
		return true;
	}
	if (m_IgnoreList.size() > 0)
	{
		std::string strWordLower(strWord);
		LLStringUtil::toLower(strWordLower);
		return (std::find(m_IgnoreList.begin(), m_IgnoreList.end(), strWordLower) != m_IgnoreList.end());
	}
	return false;
}

S32 LLHunspellWrapper::getSuggestions(const std::string& strWord, std::vector<std::string>& strSuggestionList) const
{
	if ( (!m_pHunspell) || (strWord.length() < 3) )
		return 0;

	strSuggestionList.clear();

	char** ppstrSuggestionList; int cntSuggestion = 0;
	if ( (cntSuggestion = m_pHunspell->suggest(&ppstrSuggestionList, strWord.c_str())) != 0 )
	{
		for (int idxSuggestion = 0; idxSuggestion < cntSuggestion; idxSuggestion++)
			strSuggestionList.push_back(ppstrSuggestionList[idxSuggestion]);
		m_pHunspell->free_list(&ppstrSuggestionList, cntSuggestion);
	}
	return strSuggestionList.size();
}

// ============================================================================
// Dictionary related functions
//

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
S32 LLHunspellWrapper::getDictionaries(std::vector<std::string>& strDictionaryList) const
{
	strDictionaryList.clear();
	for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
			itDictInfo != endDictInfo; ++itDictInfo)
	{
		const LLSD& sdDict = *itDictInfo;
		strDictionaryList.push_back(sdDict["language"].asString());
	}
	return strDictionaryList.size();
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
S32 LLHunspellWrapper::getInstalledDictionaries(std::vector<std::string>& strDictionaryList) const
{
	strDictionaryList.clear();
	for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
			itDictInfo != endDictInfo; ++itDictInfo)
	{
		const LLSD& sdDict = *itDictInfo;
		if (sdDict["installed"].asBoolean())
			strDictionaryList.push_back(sdDict["language"].asString());
	}
	return strDictionaryList.size();
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
bool LLHunspellWrapper::setCurrentDictionary(const std::string& strDictionary)
{
	if (strDictionary == m_strDictionaryName)
		return false;
	s_SettingsChangeSignal();

	if (m_pHunspell)
	{
		delete m_pHunspell;
		m_pHunspell = NULL;
		m_strDictionaryName = m_strDictionaryFile = "";
		m_IgnoreList.clear();
	}

	if (strDictionary.empty())
		return false;

	LLSD sdDictInfo;
	for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
			itDictInfo != endDictInfo; ++itDictInfo)
	{
		const LLSD& sdDict = *itDictInfo;
		if ( (sdDict["installed"].asBoolean()) && (strDictionary == sdDict["language"].asString()) )
			sdDictInfo = sdDict;
	}

	if (sdDictInfo.has("name"))
	{
		std::string strPathAff = m_strDictionaryPath + sdDictInfo["name"].asString() + ".aff";
		std::string strPathDic = m_strDictionaryPath + sdDictInfo["name"].asString() + ".dic";
		m_pHunspell = new Hunspell(strPathAff.c_str(), strPathDic.c_str());
		if (!m_pHunspell)
			return false;

		m_strDictionaryName = strDictionary;
		m_strDictionaryFile = sdDictInfo["name"].asString();

		// Add the custom dictionary (if there is one)
		if (sdDictInfo["has_custom"].asBoolean())
		{
			std::string strPathCustomDic = m_strDictionaryPath + m_strDictionaryFile + c_strDictCustomSuffix + ".dic";
			m_pHunspell->add_dic(strPathCustomDic.c_str());
		}

		// Load the ignore list (if there is one)
		if (sdDictInfo["has_ignore"].asBoolean())
		{
			llifstream fileDictIgnore(m_strDictionaryPath + m_strDictionaryFile + c_strDictIgnoreSuffix + ".dic", std::ios::in);
			if (fileDictIgnore.is_open())
			{
				std::string strWord; int idxLine = 0;
				while (getline(fileDictIgnore, strWord))
				{
					// Skip over the first line since that's just a line count
					if (0 != idxLine)
					{
						LLStringUtil::toLower(strWord);
						m_IgnoreList.push_back(strWord);
					}
					idxLine++;
				}
			}
		}
	}

	return (NULL != m_pHunspell);
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToCustomDictionary(const std::string& strWord)
{
	if (m_pHunspell)
		m_pHunspell->add(strWord.c_str());
	addToDictFile(m_strDictionaryPath + m_strDictionaryFile + c_strDictCustomSuffix + ".dic", strWord);
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToIgnoreList(const std::string& strWord)
{
	std::string strWordLower(strWord);
	LLStringUtil::toLower(strWordLower);
	if (std::find(m_IgnoreList.begin(), m_IgnoreList.end(), strWordLower) == m_IgnoreList.end())
	{
		m_IgnoreList.push_back(strWordLower);
		addToDictFile(m_strDictionaryPath + m_strDictionaryFile + c_strDictIgnoreSuffix + ".dic", strWordLower);
	}
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToDictFile(const std::string& strDictPath, const std::string& strWord)
{
	// TODO-Catznip: has to be a better way to add one word to the end and increment the line count?
	std::vector<std::string> wordList;

	// Read any existing words
	if (gDirUtilp->fileExists(strDictPath))
	{
		llifstream fileDictIn(strDictPath, std::ios::in);
		if (fileDictIn.is_open())
		{
			std::string strWord; int idxLine = 0;
			while (getline(fileDictIn, strWord))
			{
				// Skip over the first line since that's just a line count
				if (0 != idxLine)
					wordList.push_back(strWord);
				idxLine++;
			}
		}
		else
		{
			// TODO-Catznip: show error message?
			return;
		}
	}

	// Add the new word to the end
	wordList.push_back(strWord);

	// Recreate the file with the new line count and new word added
	llofstream fileDictOut(strDictPath, std::ios::out | std::ios::trunc);
	if (fileDictOut.is_open())
	{
		fileDictOut << wordList.size() << std::endl;
		for (std::vector<std::string>::const_iterator itWord = wordList.begin(); itWord != wordList.end(); ++itWord)
			fileDictOut << *itWord << std::endl;
		fileDictOut.close();
	}
}

// ============================================================================
// Static member functions
//

LLHunspellWrapper::settings_change_signal_t LLHunspellWrapper::s_SettingsChangeSignal;

boost::signals2::connection LLHunspellWrapper::setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb)
{
	return s_SettingsChangeSignal.connect(cb);
}

bool LLHunspellWrapper::useSpellCheck()
{
	return (LLHunspellWrapper::instanceExists()) || (LLHunspellWrapper::instance().m_pHunspell);
}

void LLHunspellWrapper::setUseSpellCheck(const std::string& strDictionary)
{
	if ( ((strDictionary.empty()) && (useSpellCheck())) || (!strDictionary.empty()) )
		LLHunspellWrapper::instance().setCurrentDictionary(strDictionary);
}

// ============================================================================
<commit_msg>- fixed : adding a word to the ignore list or custom dictionary doesn't recheck all spelling<commit_after>/**
 *
 * Copyright (c) 2010, Kitty Barnett
 *
 * The source code in this file is provided to you under the terms of the
 * GNU Lesser General Public License, version 2.1, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE. Terms of the LGPL can be found in doc/LGPL-licence.txt
 * in this distribution, or online at http://www.gnu.org/licenses/lgpl-2.1.txt
 *
 * By copying, modifying or distributing this software, you acknowledge that
 * you have read and understood your obligations described above, and agree to
 * abide by those obligations.
 *
 */

#include "linden_common.h"

#include "lldir.h"
#include "llsdserialize.h"

#include "llhunspell.h"

#if LL_WINDOWS
	#include <hunspell/hunspelldll.h>
	#pragma comment(lib, "libhunspell.lib")
#else
	#include <hunspell/hunspell.hxx>
#endif

// ============================================================================
// Static variables
//

static const std::string c_strDictCustomSuffix = "_custom";
static const std::string c_strDictIgnoreSuffix = "_ignore";

// ============================================================================

LLHunspellWrapper::LLHunspellWrapper()
	: m_pHunspell(NULL)
{
	m_strDictionaryPath = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "dictionaries", "");

	// Load dictionary information (file name, friendly name, ...)
	llifstream fileDictMap(m_strDictionaryPath + "dictionaries.xml", std::ios::binary);
	if (fileDictMap.is_open())
		LLSDSerialize::fromXMLDocument(m_sdDictionaryMap, fileDictMap);

	// Look for installed dictionaries
	std::string strTempPath;
	for (LLSD::array_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
			itDictInfo != endDictInfo; ++itDictInfo)
	{
		LLSD& sdDict = *itDictInfo;
		strTempPath = (sdDict.has("name")) ? m_strDictionaryPath + sdDict["name"].asString() : LLStringUtil::null;
		sdDict["installed"] =
			(!strTempPath.empty()) && (gDirUtilp->fileExists(strTempPath + ".aff")) && (gDirUtilp->fileExists(strTempPath + ".dic"));
		sdDict["has_custom"] = (!strTempPath.empty()) && (gDirUtilp->fileExists(strTempPath + c_strDictCustomSuffix + ".dic"));
		sdDict["has_ignore"] = (!strTempPath.empty()) && (gDirUtilp->fileExists(strTempPath + c_strDictIgnoreSuffix + ".dic"));
	}
}

LLHunspellWrapper::~LLHunspellWrapper()
{
	delete m_pHunspell;
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
bool LLHunspellWrapper::checkSpelling(const std::string& strWord) const
{
	if ( (!m_pHunspell) || (strWord.length() < 3) || (0 != m_pHunspell->spell(strWord.c_str())) )
	{
		return true;
	}
	if (m_IgnoreList.size() > 0)
	{
		std::string strWordLower(strWord);
		LLStringUtil::toLower(strWordLower);
		return (std::find(m_IgnoreList.begin(), m_IgnoreList.end(), strWordLower) != m_IgnoreList.end());
	}
	return false;
}

S32 LLHunspellWrapper::getSuggestions(const std::string& strWord, std::vector<std::string>& strSuggestionList) const
{
	if ( (!m_pHunspell) || (strWord.length() < 3) )
		return 0;

	strSuggestionList.clear();

	char** ppstrSuggestionList; int cntSuggestion = 0;
	if ( (cntSuggestion = m_pHunspell->suggest(&ppstrSuggestionList, strWord.c_str())) != 0 )
	{
		for (int idxSuggestion = 0; idxSuggestion < cntSuggestion; idxSuggestion++)
			strSuggestionList.push_back(ppstrSuggestionList[idxSuggestion]);
		m_pHunspell->free_list(&ppstrSuggestionList, cntSuggestion);
	}
	return strSuggestionList.size();
}

// ============================================================================
// Dictionary related functions
//

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
S32 LLHunspellWrapper::getDictionaries(std::vector<std::string>& strDictionaryList) const
{
	strDictionaryList.clear();
	for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
			itDictInfo != endDictInfo; ++itDictInfo)
	{
		const LLSD& sdDict = *itDictInfo;
		strDictionaryList.push_back(sdDict["language"].asString());
	}
	return strDictionaryList.size();
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
S32 LLHunspellWrapper::getInstalledDictionaries(std::vector<std::string>& strDictionaryList) const
{
	strDictionaryList.clear();
	for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
			itDictInfo != endDictInfo; ++itDictInfo)
	{
		const LLSD& sdDict = *itDictInfo;
		if (sdDict["installed"].asBoolean())
			strDictionaryList.push_back(sdDict["language"].asString());
	}
	return strDictionaryList.size();
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
bool LLHunspellWrapper::setCurrentDictionary(const std::string& strDictionary)
{
	if (strDictionary == m_strDictionaryName)
		return false;
	s_SettingsChangeSignal();

	if (m_pHunspell)
	{
		delete m_pHunspell;
		m_pHunspell = NULL;
		m_strDictionaryName = m_strDictionaryFile = "";
		m_IgnoreList.clear();
	}

	if (strDictionary.empty())
		return false;

	LLSD sdDictInfo;
	for (LLSD::array_const_iterator itDictInfo = m_sdDictionaryMap.beginArray(), endDictInfo = m_sdDictionaryMap.endArray();
			itDictInfo != endDictInfo; ++itDictInfo)
	{
		const LLSD& sdDict = *itDictInfo;
		if ( (sdDict["installed"].asBoolean()) && (strDictionary == sdDict["language"].asString()) )
			sdDictInfo = sdDict;
	}

	if (sdDictInfo.has("name"))
	{
		std::string strPathAff = m_strDictionaryPath + sdDictInfo["name"].asString() + ".aff";
		std::string strPathDic = m_strDictionaryPath + sdDictInfo["name"].asString() + ".dic";
		m_pHunspell = new Hunspell(strPathAff.c_str(), strPathDic.c_str());
		if (!m_pHunspell)
			return false;

		m_strDictionaryName = strDictionary;
		m_strDictionaryFile = sdDictInfo["name"].asString();

		// Add the custom dictionary (if there is one)
		if (sdDictInfo["has_custom"].asBoolean())
		{
			std::string strPathCustomDic = m_strDictionaryPath + m_strDictionaryFile + c_strDictCustomSuffix + ".dic";
			m_pHunspell->add_dic(strPathCustomDic.c_str());
		}

		// Load the ignore list (if there is one)
		if (sdDictInfo["has_ignore"].asBoolean())
		{
			llifstream fileDictIgnore(m_strDictionaryPath + m_strDictionaryFile + c_strDictIgnoreSuffix + ".dic", std::ios::in);
			if (fileDictIgnore.is_open())
			{
				std::string strWord; int idxLine = 0;
				while (getline(fileDictIgnore, strWord))
				{
					// Skip over the first line since that's just a line count
					if (0 != idxLine)
					{
						LLStringUtil::toLower(strWord);
						m_IgnoreList.push_back(strWord);
					}
					idxLine++;
				}
			}
		}
	}

	return (NULL != m_pHunspell);
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToCustomDictionary(const std::string& strWord)
{
	if (m_pHunspell)
		m_pHunspell->add(strWord.c_str());
	addToDictFile(m_strDictionaryPath + m_strDictionaryFile + c_strDictCustomSuffix + ".dic", strWord);
	s_SettingsChangeSignal();
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToIgnoreList(const std::string& strWord)
{
	std::string strWordLower(strWord);
	LLStringUtil::toLower(strWordLower);
	if (std::find(m_IgnoreList.begin(), m_IgnoreList.end(), strWordLower) == m_IgnoreList.end())
	{
		m_IgnoreList.push_back(strWordLower);
		addToDictFile(m_strDictionaryPath + m_strDictionaryFile + c_strDictIgnoreSuffix + ".dic", strWordLower);
		s_SettingsChangeSignal();
	}
}

// Checked: 2010-12-23 (Catznip-2.5.0a) | Added: Catznip-2.5.0a
void LLHunspellWrapper::addToDictFile(const std::string& strDictPath, const std::string& strWord)
{
	// TODO-Catznip: has to be a better way to add one word to the end and increment the line count?
	std::vector<std::string> wordList;

	// Read any existing words
	if (gDirUtilp->fileExists(strDictPath))
	{
		llifstream fileDictIn(strDictPath, std::ios::in);
		if (fileDictIn.is_open())
		{
			std::string strWord; int idxLine = 0;
			while (getline(fileDictIn, strWord))
			{
				// Skip over the first line since that's just a line count
				if (0 != idxLine)
					wordList.push_back(strWord);
				idxLine++;
			}
		}
		else
		{
			// TODO-Catznip: show error message?
			return;
		}
	}

	// Add the new word to the end
	wordList.push_back(strWord);

	// Recreate the file with the new line count and new word added
	llofstream fileDictOut(strDictPath, std::ios::out | std::ios::trunc);
	if (fileDictOut.is_open())
	{
		fileDictOut << wordList.size() << std::endl;
		for (std::vector<std::string>::const_iterator itWord = wordList.begin(); itWord != wordList.end(); ++itWord)
			fileDictOut << *itWord << std::endl;
		fileDictOut.close();
	}
}

// ============================================================================
// Static member functions
//

LLHunspellWrapper::settings_change_signal_t LLHunspellWrapper::s_SettingsChangeSignal;

boost::signals2::connection LLHunspellWrapper::setSettingsChangeCallback(const settings_change_signal_t::slot_type& cb)
{
	return s_SettingsChangeSignal.connect(cb);
}

bool LLHunspellWrapper::useSpellCheck()
{
	return (LLHunspellWrapper::instanceExists()) || (LLHunspellWrapper::instance().m_pHunspell);
}

void LLHunspellWrapper::setUseSpellCheck(const std::string& strDictionary)
{
	if ( ((strDictionary.empty()) && (useSpellCheck())) || (!strDictionary.empty()) )
		LLHunspellWrapper::instance().setCurrentDictionary(strDictionary);
}

// ============================================================================
<|endoftext|>


<commit_before>#include <atomic>
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

#include <getopt.h>
#include <semaphore.h>

#if defined(__GNUC__)
#define INLINE inline __attribute__((__always_inline__))
#elif defined(_MSC_VER)
#define INLINE __forceinline
#else
#define INLINE inline
#endif

static INLINE void memory_fence(void)
{
#if defined(__GNUC__)
    __sync_synchronize();
#elif defined(__llvm__)
    __sync_synchronize();
#elif defined(_MSC_VER)
    _mm_mfence();
#else
#error This compiler is not supported.
#endif
}

static void print_usage(FILE* out, const char* const name) {
  fprintf(out, "Usage: %s [options]\n", name);
  fprintf(out, "option:\n");
  fprintf(out, "\t-t {number}\tthe number of threads (default is 8)\n");
  fprintf(out, "\t-n {number}\tthe number to add (default is 1,000,000\n");
  fprintf(out, "\t-l [semaphore|filter|cpp11|buggy|atomic]\tthe type of lock (default is semaphore)\n");
  fprintf(out, "\t-h\tprint this message\n");
}

enum class lock_type {
  semaphore, filter, cpp11, buggy, atomic
};

struct config {
  uint32_t number_of_threads;
  uint32_t number_to_add;
  ::lock_type lock_type;

  config()
      : number_of_threads(8),
      number_to_add(1000000),
      lock_type(::lock_type::semaphore) {
  }

  config(uint32_t number_of_threads, uint32_t number_to_add, ::lock_type lock_type)
      : number_of_threads(number_of_threads),
      number_to_add(number_to_add),
      lock_type(lock_type) {
  }
};

static config parse_argument(int argc, char * const argv[]) {
  config config;

  int opt;
  while ((opt = getopt(argc, argv, "ht:n:l:")) != -1) {
  switch (opt) {
  case 't': {
    int opt_number_of_threads = atoi(optarg);
    if (opt_number_of_threads <= 0) {
      print_usage(stderr, argv[0]);
      exit(-1);
    }
    config.number_of_threads = static_cast<uint32_t>(opt_number_of_threads);
    break;
  }
  case 'n': {
    int opt_number_to_add = atoi(optarg);
    if (opt_number_to_add <= 0) {
      print_usage(stderr, argv[0]);
      exit(-1);
    }
    config.number_to_add = static_cast<uint32_t>(opt_number_to_add);
    break;
  }
  case 'l': {
    std::string opt_lock_type(optarg);
    if (opt_lock_type == "semaphore") {
      config.lock_type = ::lock_type::semaphore;
    } else if (opt_lock_type == "filter") {
      config.lock_type = ::lock_type::filter;
    } else if (opt_lock_type == "cpp11") {
      config.lock_type = ::lock_type::cpp11;
    } else if (opt_lock_type == "buggy") {
      config.lock_type = ::lock_type::buggy;
    } else if (opt_lock_type == "atomic") {
      config.lock_type = ::lock_type::atomic;
    } else {
      print_usage(stderr, argv[0]);
      exit(-1);
    }
    break;
  }
  case 'h': {
    print_usage(stdout, argv[0]);
    exit(0);
    break;
  }
  default:
    break;
  }
  }

  return config;
}

static uint32_t g_count =0;

static uint32_t* filter_level;
static uint32_t* filter_victim;
static uint32_t filter_n;

static bool filter_condition(const uint32_t& tid, const uint32_t& L) {
  for (uint32_t k = 0; k < filter_n; k += 1) {
    if (k == tid) {
      continue;
    }
    memory_fence();
    if (filter_level[k] >= L) {
      return true;
    }
  }
  return false;
}

static void filter_lock(uint32_t tid) {
  for (uint32_t L = 1; L < filter_n; L += 1) {
    filter_level[tid] = L;
    filter_victim[L] = tid;

    while (true) {
      if (!filter_condition(tid, L)) {
        break;
      }
      memory_fence();
      if (filter_victim[L] != tid) {
        break;
      }
    }
  }
}

static void filter_unlock(uint32_t tid) {
  filter_level[tid] = 0;
}

void increase_with_filter(uint32_t tid, uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    filter_lock(tid);
    g_count += 1;
    filter_unlock(tid);
  }
}

static std::mutex mutex;
void increase_with_cpp11(uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    std::lock_guard<std::mutex> lock(mutex);
    g_count += 1;
  }
}

static sem_t semaphore;
void increase_with_semaphore(uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    sem_wait(&semaphore);
    g_count += 1;
    sem_post(&semaphore);
  }
}

void increase_with_buggy_implementation(uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    g_count += 1;
  }
}

static std::atomic<uint32_t> g_atomic_count;
void increase_with_atomic(uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    g_atomic_count += 1;
  }
}

int main(int argc, char * const argv[]) {
  const auto&& config = parse_argument(argc, argv);

  std::vector<std::thread> threads;
  switch (config.lock_type) {
  case ::lock_type::filter: {
    filter_n = config.number_of_threads;
    filter_level = new uint32_t[filter_n];
    filter_victim = new uint32_t[filter_n];
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_filter, i, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  case ::lock_type::cpp11: {
    std::mutex mutex;
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_cpp11, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  case ::lock_type::semaphore: {
    sem_init(&semaphore, 0, 1);
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_semaphore, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  case ::lock_type::buggy: {
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_buggy_implementation, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  case ::lock_type::atomic: {
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_atomic, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  default:
    assert(false);
    break;
  }

  for (auto& thread : threads) {
    thread.join();
  }

  if (config.lock_type == ::lock_type::atomic) {
    printf("Count: %u (%s)\n", static_cast<uint32_t>(g_atomic_count), g_atomic_count.is_lock_free() ? "true" : "false");
  } else {
    printf("Count: %u\n", g_count);
  }

  return 0;
}
<commit_msg>[C++] Use lambda function for filter lock.<commit_after>#include <atomic>
#include <cassert>
#include <cstdint>
#include <cstdio>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

#include <getopt.h>
#include <semaphore.h>

#if defined(__GNUC__)
#define INLINE inline __attribute__((__always_inline__))
#elif defined(_MSC_VER)
#define INLINE __forceinline
#else
#define INLINE inline
#endif

static INLINE void memory_fence(void)
{
#if defined(__GNUC__)
    __sync_synchronize();
#elif defined(__llvm__)
    __sync_synchronize();
#elif defined(_MSC_VER)
    _mm_mfence();
#else
#error This compiler is not supported.
#endif
}

static void print_usage(FILE* out, const char* const name) {
  fprintf(out, "Usage: %s [options]\n", name);
  fprintf(out, "option:\n");
  fprintf(out, "\t-t {number}\tthe number of threads (default is 8)\n");
  fprintf(out, "\t-n {number}\tthe number to add (default is 1,000,000\n");
  fprintf(out, "\t-l [semaphore|filter|cpp11|buggy|atomic]\tthe type of lock (default is semaphore)\n");
  fprintf(out, "\t-h\tprint this message\n");
}

enum class lock_type {
  semaphore, filter, cpp11, buggy, atomic
};

struct config {
  uint32_t number_of_threads;
  uint32_t number_to_add;
  ::lock_type lock_type;

  config()
      : number_of_threads(8),
      number_to_add(1000000),
      lock_type(::lock_type::semaphore) {
  }

  config(uint32_t number_of_threads, uint32_t number_to_add, ::lock_type lock_type)
      : number_of_threads(number_of_threads),
      number_to_add(number_to_add),
      lock_type(lock_type) {
  }
};

static config parse_argument(int argc, char * const argv[]) {
  config config;

  int opt;
  while ((opt = getopt(argc, argv, "ht:n:l:")) != -1) {
  switch (opt) {
  case 't': {
    int opt_number_of_threads = atoi(optarg);
    if (opt_number_of_threads <= 0) {
      print_usage(stderr, argv[0]);
      exit(-1);
    }
    config.number_of_threads = static_cast<uint32_t>(opt_number_of_threads);
    break;
  }
  case 'n': {
    int opt_number_to_add = atoi(optarg);
    if (opt_number_to_add <= 0) {
      print_usage(stderr, argv[0]);
      exit(-1);
    }
    config.number_to_add = static_cast<uint32_t>(opt_number_to_add);
    break;
  }
  case 'l': {
    std::string opt_lock_type(optarg);
    if (opt_lock_type == "semaphore") {
      config.lock_type = ::lock_type::semaphore;
    } else if (opt_lock_type == "filter") {
      config.lock_type = ::lock_type::filter;
    } else if (opt_lock_type == "cpp11") {
      config.lock_type = ::lock_type::cpp11;
    } else if (opt_lock_type == "buggy") {
      config.lock_type = ::lock_type::buggy;
    } else if (opt_lock_type == "atomic") {
      config.lock_type = ::lock_type::atomic;
    } else {
      print_usage(stderr, argv[0]);
      exit(-1);
    }
    break;
  }
  case 'h': {
    print_usage(stdout, argv[0]);
    exit(0);
    break;
  }
  default:
    break;
  }
  }

  return config;
}

static uint32_t g_count =0;

static uint32_t* filter_level;
static uint32_t* filter_victim;
static uint32_t filter_n;

static void filter_lock(uint32_t tid) {
  for (uint32_t L = 1; L < filter_n; L += 1) {
    filter_level[tid] = L;
    filter_victim[L] = tid;

    const auto&& filter_condition = [&L, &tid]() -> bool {
      for (uint32_t k = 0; k < filter_n; k += 1) {
        if (k == tid) {
          continue;
        }
        memory_fence();
        if (filter_level[k] >= L) {
          return true;
        }
      }
      return false;
    };
    while (true) {
      if (!filter_condition()) {
        break;
      }
      memory_fence();
      if (filter_victim[L] != tid) {
        break;
      }
    }
  }
}

static void filter_unlock(uint32_t tid) {
  filter_level[tid] = 0;
}

void increase_with_filter(uint32_t tid, uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    filter_lock(tid);
    g_count += 1;
    filter_unlock(tid);
  }
}

static std::mutex mutex;
void increase_with_cpp11(uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    std::lock_guard<std::mutex> lock(mutex);
    g_count += 1;
  }
}

static sem_t semaphore;
void increase_with_semaphore(uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    sem_wait(&semaphore);
    g_count += 1;
    sem_post(&semaphore);
  }
}

void increase_with_buggy_implementation(uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    g_count += 1;
  }
}

static std::atomic<uint32_t> g_atomic_count;
void increase_with_atomic(uint32_t n) {
  for (uint32_t i = 0; i < n; i += 1) {
    g_atomic_count += 1;
  }
}

int main(int argc, char * const argv[]) {
  const auto&& config = parse_argument(argc, argv);

  std::vector<std::thread> threads;
  switch (config.lock_type) {
  case ::lock_type::filter: {
    filter_n = config.number_of_threads;
    filter_level = new uint32_t[filter_n];
    filter_victim = new uint32_t[filter_n];
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_filter, i, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  case ::lock_type::cpp11: {
    std::mutex mutex;
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_cpp11, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  case ::lock_type::semaphore: {
    sem_init(&semaphore, 0, 1);
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_semaphore, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  case ::lock_type::buggy: {
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_buggy_implementation, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  case ::lock_type::atomic: {
    for (size_t i = 0; i < config.number_of_threads; i += 1) {
      threads.emplace_back(increase_with_atomic, config.number_to_add / config.number_of_threads);
    }
    break;
  }
  default:
    assert(false);
    break;
  }

  for (auto& thread : threads) {
    thread.join();
  }

  if (config.lock_type == ::lock_type::atomic) {
    printf("Count: %u (%s)\n", static_cast<uint32_t>(g_atomic_count), g_atomic_count.is_lock_free() ? "true" : "false");
  } else {
    printf("Count: %u\n", g_count);
  }

  return 0;
}
<|endoftext|>