<commit_before>/* Copyright 2017 QReal Research Group
 *
 * 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 "pioneerStateMachineGenerator.h"

#include <generatorBase/semanticTree/semanticNode.h>
#include <generatorBase/semanticTree/simpleNode.h>

using namespace pioneer::lua;
using namespace generatorBase;
using namespace generatorBase::semantics;

PioneerStateMachineGenerator::PioneerStateMachineGenerator(
		const qrRepo::RepoApi &repo
		, qReal::ErrorReporterInterface &errorReporter
		, generatorBase::GeneratorCustomizer &customizer
		, generatorBase::PrimaryControlFlowValidator &validator
		, const qReal::Id &diagramId
		, QObject *parent
		, bool isThisDiagramMain)
	: GotoControlFlowGenerator(repo, errorReporter, customizer, validator, diagramId, parent, isThisDiagramMain)
{
	mAsynchronousNodes << "GeoTakeoff" << "GeoLanding" << "GoToPoint";
}

void PioneerStateMachineGenerator::registerNodeHook(std::function<void(const qReal::Id)> hook)
{
	mNodeHooks.append(hook);
}

void PioneerStateMachineGenerator::performGeneration()
{
	mSemanticTreeManager.reset(new SemanticTreeManager(*mSemanticTree, mErrorReporter, mErrorsOccured));
	GotoControlFlowGenerator::performGeneration();
}

void PioneerStateMachineGenerator::visitRegular(const qReal::Id &id, const QList<LinkInfo> &links)
{
	// Base class method checks for subprogram calls, which is irrelevant for now, but does not hurt and hopefully
	// will be needed later.
	ControlFlowGeneratorBase::visitRegular(id, links);

	SimpleNode * const thisNode = static_cast<SimpleNode *>(mSemanticTree->findNodeFor(id));
	SemanticNode *nextNode = nullptr;
	const qReal::Id target = links[0].target;

	if (mAsynchronousNodes.contains(id.element())) {
		if (mSemanticTree->findNodeFor(target)) {
			// thisNode is asyncronous node that transfers control to already visited node.
			// Generated code for thisNode will initiate asynchronous action and all we need to do is to generate
			// transition to a state which will execute target block when this block finishes its asynchronous
			// operation.
			nextNode = produceGotoNode(target);
			mSemanticTreeManager->addAfter(thisNode, nextNode);

			if (!mLabeledNodes.contains(target)) {
				//
				// Target node, despite being already visited, does not have a label, it means that it is a part of a
				// synchronous fragment. We copy that fragment from this node to the first asyncronous node and
				// label a start of the copied fragment. At the end of the fragment we will generate said asynchronous
				// node, which will initiate asynchronous operation, and then transition to its next state, which will
				// continue execution when operation is done.
				//
				// It will confuse "findNodeFor(id)" (there will be many semantic nodes for a block with given id), but
				// we actually do not care which copy will be used later, since they are the same.
				//
				nextNode = copySynchronousFragment(nextNode, target, true);
			}

			if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
				SemanticNode * const endNode = produceEndOfHandlerNode();
				mSemanticTreeManager->addAfter(nextNode, endNode);
			}
		} else {
			// thisNode is asynchronous node that transfers control to a node that has not been visited yet. Generating
			// transition into a state associated with that node and then a new handler for target node itself.
			nextNode = mSemanticTreeManager->produceLabeledNode(target);
			if (!nextNode) {
				return;
			} else {
				mLabeledNodes << nextNode->id();
			}

			SemanticNode * const gotoNode = produceGotoNode(target);
			mSemanticTreeManager->addAfter(thisNode, gotoNode);

			// Labeled node can not be a part of a zone (i.e. "then" or "else" branch), it shall be generated in top
			// level zone.
			if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
				SemanticNode * const endNode = produceEndOfHandlerNode();
				mSemanticTreeManager->addAfter(gotoNode, endNode);
				mSemanticTreeManager->addAfter(endNode, nextNode);
			} else {
				// Getting parent node (i.e. If statement to the branch of which our node belongs).
				NonZoneNode *aParent = mSemanticTreeManager->parent(thisNode);
				while (!mSemanticTreeManager->isTopLevelNode(aParent)) {
					aParent = mSemanticTreeManager->parent(aParent);
				}

				// Skipping "end" that finishes handler with If.
				SemanticNode * const endOfHandler = mSemanticTreeManager->findSibling(
						aParent
						, [](SemanticNode *node){ return node->id().element() == "EndOfHandler"; });

				if (!endOfHandler) {
					mErrorReporter.addError(tr("Can not find end of an If statement, generation internal error or "
							"too complex algorithmic construction."));
					mErrorsOccured = true;
					return;
				}

				// Adding our labeled node denoting new handler after the end of a handler with If node.
				mSemanticTreeManager->addAfter(endOfHandler, nextNode);
			}
		}
	} else {
		if (!mSemanticTree->findNodeFor(target)) {
			// It is not an asynchronous node, generating as-is.
			nextNode = mSemanticTree->produceNodeFor(target);
			mSemanticTreeManager->addAfter(thisNode, nextNode);
		} else {
			// Synchronous node leading to already visited node. Need some copypasting of synchronous fragments,
			// or else we will stall the program waiting for an event that was never initiated.
			copySynchronousFragment(thisNode, target, false);
		}
	}
}

void PioneerStateMachineGenerator::visitConditional(const qReal::Id &id, const QList<LinkInfo> &links)
{
	Q_UNUSED(links)

	const QPair<LinkInfo, LinkInfo> branches(ifBranchesFor(id));
	const LinkInfo thenLink = branches.first;
	const LinkInfo elseLink = branches.second;

	IfNode * const thisNode = static_cast<IfNode *>(mSemanticTree->findNodeFor(id));

	mSemanticTreeManager->addToZone(thisNode->thenZone(), thenLink.target);
	mSemanticTreeManager->addToZone(thisNode->elseZone(), elseLink.target);

	if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
		SemanticNode * const endNode = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(thisNode, endNode);
	}
}

void PioneerStateMachineGenerator::visitFinal(const qReal::Id &id, const QList<LinkInfo> &links)
{
	generatorBase::GotoControlFlowGenerator::visitFinal(id, links);

	// Here we are going to add finishing end-of-handler node in case it is missing (for example, diagrams like
	// "Initial Node" -> "Final Node" will not generate it automatically).
	// It is a kind of hack because asynchronous handler shall be a first-class entity and a zone node.

	SimpleNode * const thisNode = static_cast<SimpleNode *>(mSemanticTree->findNodeFor(id));

	// Getting root node.
	NonZoneNode *aParent = mSemanticTreeManager->parent(thisNode);
	while (!mSemanticTreeManager->isTopLevelNode(aParent)) {
		aParent = mSemanticTreeManager->parent(aParent);
	}

	// Searching for end-of-handler node.
	SemanticNode * endOfHandler = mSemanticTreeManager->findSibling(
			aParent
			, [](SemanticNode *node){ return node->id().element() == "EndOfHandler"; });

	if (!endOfHandler) {
		// If not found, create and add one.
		endOfHandler = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(thisNode, endOfHandler);
	}
}

void PioneerStateMachineGenerator::visit(const qReal::Id &nodeId, QList<utils::DeepFirstSearcher::LinkInfo> &links)
{
	generatorBase::GotoControlFlowGenerator::visit(nodeId, links);
	for (const auto &hook : mNodeHooks) {
		hook(nodeId);
	}
}

SemanticNode *PioneerStateMachineGenerator::copySynchronousFragment(
		SemanticNode *after
		, const qReal::Id &from
		, bool withLabel)
{
	NonZoneNode *oldTarget = dynamic_cast<NonZoneNode *>(mSemanticTree->findNodeFor(from));
	if (!oldTarget) {
		/// @todo: actually, why not?
		mErrorReporter.addError(tr("Can not close a loop on algorithmic block."));
		mErrorsOccured = true;
		return nullptr;
	}

	NonZoneNode *fragmentStartNode = withLabel
			? mSemanticTreeManager->produceLabeledNode(from)
			: dynamic_cast<NonZoneNode *>(mSemanticTree->produceNodeFor(from));

	if (!fragmentStartNode) {
		return nullptr;
	} else {
		mLabeledNodes << fragmentStartNode->id();
	}

	if (!dynamic_cast<NonZoneNode *>(after)) {
		mErrorReporter.addError(tr("Generation internal error, non-zone node is a start of a fragment."));
		mErrorsOccured = true;
		return nullptr;
	}

	// End-of-handler shall go before every labeled node, since label here is actually a start of a new handler.
	SemanticNode * const endNode = produceEndOfHandlerNode();
	mSemanticTreeManager->addAfter(after, endNode);

	mSemanticTreeManager->addAfter(endNode, fragmentStartNode);

	if (isAsynchronous(fragmentStartNode)) {
		// Synchronous fragment is trivial and its first node is asynchronous. Generating transition from it and we're
		// done here.
		//
		// Using oldTarget because fragmentStartNode was just added and does not have siblings, but it is a copy
		// of oldTarget.
		const auto rightSibling = mSemanticTreeManager->findRightSibling(oldTarget);
		if (rightSibling) {
			auto gotoNode = produceGotoNode(rightSibling->id());
			fragmentStartNode->appendSibling(gotoNode);
			return gotoNode;
		} else {
			mErrorReporter.addError(tr("Generation internal error, asynchronous fragment start node generation " \
					"failed."));
			mErrorsOccured = true;
		}

		return nullptr;
	}

	auto siblings = mSemanticTreeManager->copyRightSiblingsUntil(
			oldTarget
			, [this](SemanticNode * node){ return isAsynchronous(node); });

	if (siblings.isEmpty()) {
		mErrorReporter.addError(tr("Loop can not be closed on a block that is last in its structural construct."));
		mErrorsOccured = true;
		return nullptr;
	}

	if (isAsynchronous(siblings.last())) {
		// Synchronous fragment finished with asynchronous node
		fragmentStartNode->appendSiblings(siblings);

		// Now we shall look for the end of original fragment: find asynchronous node on which a fragment shall be
		// ending and get its target node. Assuming that they are already visited (here we assuming that it is a loop,
		// may not be the case when logical branching blocks will be introduced).
		auto asynchronousNode = mSemanticTreeManager->findSibling(
				oldTarget
				, [this](SemanticNode * node){ return isAsynchronous(node); });

		const auto asynchronousNodeTarget = mSemanticTreeManager->findRightSibling(asynchronousNode);
		if (!asynchronousNodeTarget) {
			mErrorReporter.addError(tr("Generation internal error, asynchronous node does not have target node."));
			mErrorsOccured = true;
			return nullptr;
		}

		auto gotoNode = produceGotoNode(asynchronousNodeTarget->id());
		fragmentStartNode->appendSibling(gotoNode);
		return gotoNode;
	} else {
		mErrorReporter.addError(tr("Purely synchronous loops or If branches are not supported yet."));
		mErrorsOccured = true;
	}

	return nullptr;
}

bool PioneerStateMachineGenerator::isAsynchronous(const SemanticNode * const node) const
{
	return mAsynchronousNodes.contains(node->id().element());
}

SemanticNode *PioneerStateMachineGenerator::produceEndOfHandlerNode()
{
	qReal::Id syntheticId = qReal::Id::createElementId("synthetic", "synthetic", "EndOfHandler");
	SimpleNode * const result = mSemanticTree->produceSimple(syntheticId);
	// No need for special handling, from the point of view of a generator it is just some simple node.
	result->bindToSyntheticConstruction(SimpleNode::noSytheticBinding);
	return result;
}
<commit_msg>If branches converging on final node are generating now<commit_after>/* Copyright 2017 QReal Research Group
 *
 * 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 "pioneerStateMachineGenerator.h"

#include <generatorBase/semanticTree/semanticNode.h>
#include <generatorBase/semanticTree/simpleNode.h>

using namespace pioneer::lua;
using namespace generatorBase;
using namespace generatorBase::semantics;

PioneerStateMachineGenerator::PioneerStateMachineGenerator(
		const qrRepo::RepoApi &repo
		, qReal::ErrorReporterInterface &errorReporter
		, generatorBase::GeneratorCustomizer &customizer
		, generatorBase::PrimaryControlFlowValidator &validator
		, const qReal::Id &diagramId
		, QObject *parent
		, bool isThisDiagramMain)
	: GotoControlFlowGenerator(repo, errorReporter, customizer, validator, diagramId, parent, isThisDiagramMain)
{
	mAsynchronousNodes << "GeoTakeoff" << "GeoLanding" << "GoToPoint";
}

void PioneerStateMachineGenerator::registerNodeHook(std::function<void(const qReal::Id)> hook)
{
	mNodeHooks.append(hook);
}

void PioneerStateMachineGenerator::performGeneration()
{
	mSemanticTreeManager.reset(new SemanticTreeManager(*mSemanticTree, mErrorReporter, mErrorsOccured));
	GotoControlFlowGenerator::performGeneration();
}

void PioneerStateMachineGenerator::visitRegular(const qReal::Id &id, const QList<LinkInfo> &links)
{
	// Base class method checks for subprogram calls, which is irrelevant for now, but does not hurt and hopefully
	// will be needed later.
	ControlFlowGeneratorBase::visitRegular(id, links);

	SimpleNode * const thisNode = static_cast<SimpleNode *>(mSemanticTree->findNodeFor(id));
	SemanticNode *nextNode = nullptr;
	const qReal::Id target = links[0].target;

	if (mAsynchronousNodes.contains(id.element())) {
		if (mSemanticTree->findNodeFor(target)) {
			// thisNode is asyncronous node that transfers control to already visited node.
			// Generated code for thisNode will initiate asynchronous action and all we need to do is to generate
			// transition to a state which will execute target block when this block finishes its asynchronous
			// operation.
			nextNode = produceGotoNode(target);
			mSemanticTreeManager->addAfter(thisNode, nextNode);

			if (!mLabeledNodes.contains(target)) {
				//
				// Target node, despite being already visited, does not have a label, it means that it is a part of a
				// synchronous fragment. We copy that fragment from this node to the first asyncronous node and
				// label a start of the copied fragment. At the end of the fragment we will generate said asynchronous
				// node, which will initiate asynchronous operation, and then transition to its next state, which will
				// continue execution when operation is done.
				//
				// It will confuse "findNodeFor(id)" (there will be many semantic nodes for a block with given id), but
				// we actually do not care which copy will be used later, since they are the same.
				//
				nextNode = copySynchronousFragment(nextNode, target, true);
			}

			if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
				SemanticNode * const endNode = produceEndOfHandlerNode();
				mSemanticTreeManager->addAfter(nextNode, endNode);
			}
		} else {
			// thisNode is asynchronous node that transfers control to a node that has not been visited yet. Generating
			// transition into a state associated with that node and then a new handler for target node itself.
			nextNode = mSemanticTreeManager->produceLabeledNode(target);
			if (!nextNode) {
				return;
			} else {
				mLabeledNodes << nextNode->id();
			}

			SemanticNode * const gotoNode = produceGotoNode(target);
			mSemanticTreeManager->addAfter(thisNode, gotoNode);

			// Labeled node can not be a part of a zone (i.e. "then" or "else" branch), it shall be generated in top
			// level zone.
			if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
				SemanticNode * const endNode = produceEndOfHandlerNode();
				mSemanticTreeManager->addAfter(gotoNode, endNode);
				mSemanticTreeManager->addAfter(endNode, nextNode);
			} else {
				// Getting parent node (i.e. If statement to the branch of which our node belongs).
				NonZoneNode *aParent = mSemanticTreeManager->parent(thisNode);
				while (!mSemanticTreeManager->isTopLevelNode(aParent)) {
					aParent = mSemanticTreeManager->parent(aParent);
				}

				// Skipping "end" that finishes handler with If.
				SemanticNode * const endOfHandler = mSemanticTreeManager->findSibling(
						aParent
						, [](SemanticNode *node){ return node->id().element() == "EndOfHandler"; });

				if (!endOfHandler) {
					mErrorReporter.addError(tr("Can not find end of an If statement, generation internal error or "
							"too complex algorithmic construction."));
					mErrorsOccured = true;
					return;
				}

				// Adding our labeled node denoting new handler after the end of a handler with If node.
				mSemanticTreeManager->addAfter(endOfHandler, nextNode);
			}
		}
	} else {
		if (!mSemanticTree->findNodeFor(target)) {
			// It is not an asynchronous node, generating as-is.
			nextNode = mSemanticTree->produceNodeFor(target);
			mSemanticTreeManager->addAfter(thisNode, nextNode);
		} else {
			// Synchronous node leading to already visited node. Need some copypasting of synchronous fragments,
			// or else we will stall the program waiting for an event that was never initiated.
			copySynchronousFragment(thisNode, target, false);
		}
	}
}

void PioneerStateMachineGenerator::visitConditional(const qReal::Id &id, const QList<LinkInfo> &links)
{
	Q_UNUSED(links)

	const QPair<LinkInfo, LinkInfo> branches(ifBranchesFor(id));
	const LinkInfo thenLink = branches.first;
	const LinkInfo elseLink = branches.second;

	IfNode * const thisNode = static_cast<IfNode *>(mSemanticTree->findNodeFor(id));

	mSemanticTreeManager->addToZone(thisNode->thenZone(), thenLink.target);
	mSemanticTreeManager->addToZone(thisNode->elseZone(), elseLink.target);

	if (mSemanticTreeManager->isTopLevelNode(thisNode)) {
		SemanticNode * const endNode = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(thisNode, endNode);
	}
}

void PioneerStateMachineGenerator::visitFinal(const qReal::Id &id, const QList<LinkInfo> &links)
{
	generatorBase::GotoControlFlowGenerator::visitFinal(id, links);

	// Here we are going to add finishing end-of-handler node in case it is missing (for example, diagrams like
	// "Initial Node" -> "Final Node" will not generate it automatically).
	// It is a kind of hack because asynchronous handler shall be a first-class entity and a zone node.

	SimpleNode * const thisNode = static_cast<SimpleNode *>(mSemanticTree->findNodeFor(id));

	// Getting root node.
	NonZoneNode *aParent = mSemanticTreeManager->parent(thisNode);
	while (!mSemanticTreeManager->isTopLevelNode(aParent)) {
		aParent = mSemanticTreeManager->parent(aParent);
	}

	// Searching for end-of-handler node.
	SemanticNode * endOfHandler = mSemanticTreeManager->findSibling(
			aParent
			, [](SemanticNode *node){ return node->id().element() == "EndOfHandler"; });

	if (!endOfHandler) {
		// If not found, create and add one.
		endOfHandler = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(thisNode, endOfHandler);
	}
}

void PioneerStateMachineGenerator::visit(const qReal::Id &nodeId, QList<utils::DeepFirstSearcher::LinkInfo> &links)
{
	generatorBase::GotoControlFlowGenerator::visit(nodeId, links);
	for (const auto &hook : mNodeHooks) {
		hook(nodeId);
	}
}

SemanticNode *PioneerStateMachineGenerator::copySynchronousFragment(
		SemanticNode *after
		, const qReal::Id &from
		, bool withLabel)
{
	NonZoneNode *oldTarget = dynamic_cast<NonZoneNode *>(mSemanticTree->findNodeFor(from));
	if (!oldTarget) {
		/// @todo: actually, why not?
		mErrorReporter.addError(tr("Can not close a loop on algorithmic block."));
		mErrorsOccured = true;
		return nullptr;
	}

	NonZoneNode *fragmentStartNode = withLabel
			? mSemanticTreeManager->produceLabeledNode(from)
			: dynamic_cast<NonZoneNode *>(mSemanticTree->produceNodeFor(from));

	if (!fragmentStartNode) {
		return nullptr;
	} else {
		mLabeledNodes << fragmentStartNode->id();
	}

	if (!dynamic_cast<NonZoneNode *>(after)) {
		mErrorReporter.addError(tr("Generation internal error, non-zone node is a start of a fragment."));
		mErrorsOccured = true;
		return nullptr;
	}

	if (withLabel) {
		// End-of-handler shall go before every labeled node, since label here is actually a start of a new handler.
		SemanticNode * const endNode = produceEndOfHandlerNode();
		mSemanticTreeManager->addAfter(after, endNode);
		mSemanticTreeManager->addAfter(endNode, fragmentStartNode);
	} else {
		mSemanticTreeManager->addAfter(after, fragmentStartNode);
	}

	if (isAsynchronous(fragmentStartNode)) {
		// Synchronous fragment is trivial and its first node is asynchronous. Generating transition from it and we're
		// done here.
		//
		// Using oldTarget because fragmentStartNode was just added and does not have siblings, but it is a copy
		// of oldTarget.
		const auto rightSibling = mSemanticTreeManager->findRightSibling(oldTarget);
		if (rightSibling) {
			auto gotoNode = produceGotoNode(rightSibling->id());
			fragmentStartNode->appendSibling(gotoNode);
			return gotoNode;
		} else {
			mErrorReporter.addError(tr("Generation internal error, asynchronous fragment start node generation " \
					"failed."));
			mErrorsOccured = true;
		}

		return nullptr;
	}

	auto siblings = mSemanticTreeManager->copyRightSiblingsUntil(
			oldTarget
			, [this](SemanticNode * node){ return isAsynchronous(node); });

	if (siblings.isEmpty()) {
		// Fragment is trivial and non-asynchronous --- so it must be FinalNode. Fine, no need to copy it.
		if (fragmentStartNode->id().element() != "FinalNode") {
			mErrorReporter.addError(tr("Generation internal error, program ends abruptly."));
			mErrorsOccured = true;
			return nullptr;
		}

		return fragmentStartNode;
	}

	if (isAsynchronous(siblings.last())) {
		// Synchronous fragment finished with asynchronous node
		fragmentStartNode->appendSiblings(siblings);

		// Now we shall look for the end of original fragment: find asynchronous node on which a fragment shall be
		// ending and get its target node. Assuming that they are already visited (here we assuming that it is a loop,
		// may not be the case when logical branching blocks will be introduced).
		auto asynchronousNode = mSemanticTreeManager->findSibling(
				oldTarget
				, [this](SemanticNode * node){ return isAsynchronous(node); });

		const auto asynchronousNodeTarget = mSemanticTreeManager->findRightSibling(asynchronousNode);
		if (!asynchronousNodeTarget) {
			mErrorReporter.addError(tr("Generation internal error, asynchronous node does not have target node."));
			mErrorsOccured = true;
			return nullptr;
		}

		auto gotoNode = produceGotoNode(asynchronousNodeTarget->id());
		fragmentStartNode->appendSibling(gotoNode);
		return gotoNode;
	} else {
		mErrorReporter.addError(tr("Purely synchronous loops or If branches are not supported yet."));
		mErrorsOccured = true;
	}

	return nullptr;
}

bool PioneerStateMachineGenerator::isAsynchronous(const SemanticNode * const node) const
{
	return mAsynchronousNodes.contains(node->id().element());
}

SemanticNode *PioneerStateMachineGenerator::produceEndOfHandlerNode()
{
	qReal::Id syntheticId = qReal::Id::createElementId("synthetic", "synthetic", "EndOfHandler");
	SimpleNode * const result = mSemanticTree->produceSimple(syntheticId);
	// No need for special handling, from the point of view of a generator it is just some simple node.
	result->bindToSyntheticConstruction(SimpleNode::noSytheticBinding);
	return result;
}
<|endoftext|>


<commit_before>/**
 * @file hud_fox.cpp
 * @brief Purpose: Contains methods to game class' management.
 *
 * MIT License
 * Copyright (c) 2017 MindScape
 *
 * https://github.com/TecProg2017-2/mindscape/blob/master/LICENSE.md
 */
#include "../include/hud_fox.hpp"
#include "../engine/include/log.hpp"

using namespace mindscape;

/**
 * @brief Class Contructor.
 *
 * Sets Hud Fox's firsts informations (attributes' values).
 * (Hud Fox: Fox's health bar).
 *
 * @param name Hud Fox's name(name of the object).
 * @param position Hud Fox's coordinates in the game's map.
 * @param priority Hud Fox's priority in game's execution.
 * @return void.
 */
HudFox::HudFox(
    std::string name,
    std::pair<int, int> position,
    int priority)
    :engine::GameObject(
        name,
        position,
        priority,
        {
            //No engine Keyboard Event needed.
        }
    ) {
        initialize_animations();
        initialize_audio_effects();
};

/**
 * @brief Initiates Hud Fox's sound effect.
 *
 * Sets sound effect of heart.
 * @return void.
 */
void HudFox::initialize_audio_effects() {

    DEBUG("Started");

    /* Initiates sound effect */
    engine::Audio * take_this_hp = nullptr;
    take_this_hp = new engine::Audio(
        "heart", "../assets/audios/effects_songs/mindscape_heart.wav",
        engine::Audio::CHUNK);

    /* Set duration of the sound effect and add component in game */
    take_this_hp->set_duration(1);
    add_component(take_this_hp);

    DEBUG("Ended");
}

/**
 * @brief Notifies Hud Fox of Fox's state.
 *
 * Verifies Fox's state and sets stars' animation depending of the quantity of
 *  stars collected.
 *
 * @param game_object Object for observe game's situation,
 * in this case, the Fox.
 * @return void.
 */
void HudFox::notify(engine::Observable* game_object) {

    Fox* fox = nullptr;
    fox = dynamic_cast<Fox *>(game_object);

    if(fox) {
    /* If the fox exists */
        bool give_hp = false; /*< Boolean. Boolean that defines if
        hp is being given or not */
        give_hp = fox->get_animation_hud_fading();

        engine::Animation* actual = NULL;
        actual = get_actual_animation();

        if(actual == animations["three_star_fading"]) {
        /* If stars are fading */
            if(actual->is_finished) {
            /* If stars already faded */
                give_hp = false;
                fox->set_star_count(0);
                set_actual_animation(animations["zero_star"]);
            }

            else {
                /* Do nothing */
            }
        }

        else {
        /* If the animation is not stars fading */
            int count = 0; /*< Integer. Number of stars */
            count = fox->get_star_count();

            if(count == 0) {
            /* If there are no stars */
                if(!(get_actual_animation() == animations["zero_star"])) {
                    set_actual_animation(animations["zero_star"]);
                }

                else {
                    /* Do nothing */
                }
            }

            else if(count == 1) {
            /* If there is one star */
                if(!(get_actual_animation() == animations["one_star"])){
                    set_actual_animation(animations["one_star"]);
                }

                else {
                    /* Do nothing */
                }
            }

            else if(count == 2) {
            /* If there are two stars */
                if(!(get_actual_animation() == animations["two_star"])) {
                    set_actual_animation(animations["two_star"]);
                }

                else {
                    /* Do nothing */
                }
            }

            else if(count == 3 && !give_hp) {
            /* If there are three stars and is not giving hp */
                if(!(get_actual_animation() == animations["three_star"])) {
                    set_actual_animation(animations["three_star"]);
                }
                else {
                    /* Do nothing */
                }
            }

            else if(count == 3 && give_hp) {
            /* If there are three stars and is giving hp */
                fox->set_animation_hud_fading(false);
                set_actual_animation(animations["three_star_fading"]);
                play_song("heart");
            }

            else {
                /* Do nothing */
            }
        }
    }

    else {
        /* Do nothing */
        WARN("HudFox: Fox IS NULL");
    }

}

/**
 * @brief Initiates Hud Fox's animation.
 *
 * Initiates all Hud Fox's sprites(images).
 *
 * @return void.
 */
void HudFox::initialize_animations() {

    DEBUG("Started");

    engine::Animation* fox_zero_star = nullptr;
    fox_zero_star = create_animation(
        "../assets/images/sprites/hud/hud_fox_0.png",
        1,1,0.9, "RIGHT"
    );
    add_animation("zero_star", fox_zero_star);

    engine::Animation* fox_one_star = nullptr;
    fox_one_star = create_animation(
        "../assets/images/sprites/hud/hud_fox_1.png",
        1,1,0.9, "RIGHT"
    );
    add_animation("one_star", fox_one_star);

    engine::Animation* fox_two_star = nullptr;
    fox_two_star = create_animation(
        "../assets/images/sprites/hud/hud_fox_2.png",
        1,1,0.9, "RIGHT"
    );
    add_animation("two_star", fox_two_star);

    engine::Animation* fox_three_star = nullptr;
    fox_three_star = create_animation(
        "../assets/images/sprites/hud/hud_fox_3.png",
        1,1,0.9, "RIGHT"
    );
    add_animation("three_star", fox_three_star);

    engine::Animation* fox_three_star_fading = nullptr;
    fox_three_star_fading = create_animation(
        "../assets/images/sprites/hud/hud_fox_3_animation.png",
        1,4,1.0, "RIGHT"
    );
    fox_three_star_fading->in_loop = false;
    add_animation("three_star_fading", fox_three_star_fading);

    fox_zero_star->activate();
    set_actual_animation(fox_zero_star);

    DEBUG("Ended");
}

/**
 * @brief Creates Hud Fox's animation.
 *
 * Creates all Hud Fox's animation based on Hud Fox's sprites.
 *
 * @param image_path Path of the Hud Fox's sprite.
 * @param sprite_lines Line of the Hud Fox's sprite.
 * @warning Limitations of sprite_lines and sprite_columns are
 * 1 to the quantity of lines/columns in the image.
 * @param sprite_columns Column of the Fox's sprite needed.
 * @param duration Duration of the Hud Fox's image to show up.
 * @param direction Direction of the Hud Fox's image.
 * @return engine::Animation* The animation constructed.
 */
engine::Animation* HudFox::create_animation(
    std::string path,
    int sprite_lines,
    int sprite_columns,
    double duration,
    std::string direction) {

    DEBUG("Started");
    engine::Game& game = engine::Game::get_instance();

    engine::Animation* animation = nullptr;
    animation = new engine::Animation(
        game.get_renderer(),
        path,
        // is_active
        false,
        std::make_pair(0, 0),
        // priority
        1,
        sprite_lines,
        sprite_columns,
        duration,
        // in_loop
        true,
        direction
    );

    animation->set_values(
        std::make_pair(170, 78),
        std::make_pair(170, 78),
        std::make_pair(0, 0)
    );

    DEBUG("Ended");

    return animation;
}
<commit_msg>[CONSTANTS] Applies constants in Hud_Fox.cpp<commit_after>/**
 * @file hud_fox.cpp
 * @brief Purpose: Contains methods to game class' management.
 *
 * MIT License
 * Copyright (c) 2017 MindScape
 *
 * https://github.com/TecProg2017-2/mindscape/blob/master/LICENSE.md
 */
#include "../include/hud_fox.hpp"
#include "../engine/include/log.hpp"

using namespace mindscape;

/**
 * @brief Class Contructor.
 *
 * Sets Hud Fox's firsts informations (attributes' values).
 * (Hud Fox: Fox's health bar).
 *
 * @param name Hud Fox's name(name of the object).
 * @param position Hud Fox's coordinates in the game's map.
 * @param priority Hud Fox's priority in game's execution.
 * @return void.
 */
HudFox::HudFox(
    std::string name,
    std::pair<int, int> position,
    int priority)
    :engine::GameObject(
        name,
        position,
        priority,
        {
            //No engine Keyboard Event needed.
        }
    ) {
        initialize_animations();
        initialize_audio_effects();
};

/**
 * @brief Initiates Hud Fox's sound effect.
 *
 * Sets sound effect of heart.
 * @return void.
 */
void HudFox::initialize_audio_effects() {

    DEBUG("Started");

    /* Initiates sound effect */
    engine::Audio * take_this_hp = nullptr;
    take_this_hp = new engine::Audio(
        "heart", "../assets/audios/effects_songs/mindscape_heart.wav",
        engine::Audio::CHUNK);

    /* Set duration of the sound effect and add component in game */
    const int sound_duration = 1; /**< Integer. Duration of the sound effect in seconds*/
    take_this_hp->set_duration(sound_duration);
    add_component(take_this_hp);

    DEBUG("Ended");
}

/**
 * @brief Notifies Hud Fox of Fox's state.
 *
 * Verifies Fox's state and sets stars' animation depending of the quantity of
 *  stars collected.
 *
 * @param game_object Object for observe game's situation,
 * in this case, the Fox.
 * @return void.
 */
void HudFox::notify(engine::Observable* game_object) {

    Fox* fox = nullptr;
    fox = dynamic_cast<Fox *>(game_object);

    if(fox) {
    /* If the fox exists */
        bool give_hp = false; /*< Boolean. Boolean that defines if
        hp is being given or not */
        give_hp = fox->get_animation_hud_fading();

        engine::Animation* actual = NULL;
        actual = get_actual_animation();

        if(actual == animations["three_star_fading"]) {
        /* If stars are fading */
            if(actual->is_finished) {
            /* If stars already faded */
                give_hp = false;
                const int default_star_count = 0; /**< Integer.
                Default star count. Range 0-3*/
                fox->set_star_count(default_star_count);
                set_actual_animation(animations["zero_star"]);
            }

            else {
                /* Do nothing */
            }
        }

        else {
        /* If the animation is not stars fading */
            int count = 0; /*< Integer. Number of stars */
            count = fox->get_star_count();

            if(count == 0) {
            /* If there are no stars */
                if(!(get_actual_animation() == animations["zero_star"])) {
                    set_actual_animation(animations["zero_star"]);
                }

                else {
                    /* Do nothing */
                }
            }

            else if(count == 1) {
            /* If there is one star */
                if(!(get_actual_animation() == animations["one_star"])){
                    set_actual_animation(animations["one_star"]);
                }

                else {
                    /* Do nothing */
                }
            }

            else if(count == 2) {
            /* If there are two stars */
                if(!(get_actual_animation() == animations["two_star"])) {
                    set_actual_animation(animations["two_star"]);
                }

                else {
                    /* Do nothing */
                }
            }

            else if(count == 3 && !give_hp) {
            /* If there are three stars and is not giving hp */
                if(!(get_actual_animation() == animations["three_star"])) {
                    set_actual_animation(animations["three_star"]);
                }
                else {
                    /* Do nothing */
                }
            }

            else if(count == 3 && give_hp) {
            /* If there are three stars and is giving hp */
                fox->set_animation_hud_fading(false);
                set_actual_animation(animations["three_star_fading"]);
                play_song("heart");
            }

            else {
                /* Do nothing */
            }
        }
    }

    else {
        /* Do nothing */
        WARN("HudFox: Fox IS NULL");
    }

}

/**
 * @brief Initiates Hud Fox's animation.
 *
 * Initiates all Hud Fox's sprites(images).
 *
 * @return void.
 */
void HudFox::initialize_animations() {

    DEBUG("Started");
    const int default_sprite_line = 1; /**< Integer. Default sprite line, RANGE 1 */
    const int default_sprite_column = 1;  /**< Integer. Default sprite column, RANGE 1 */
    const double default_animation_duration = 0.9;  /**< Double. Default animation
    duration in seconds */

    engine::Animation* fox_zero_star = nullptr;
    fox_zero_star = create_animation(
        "../assets/images/sprites/hud/hud_fox_0.png",
        default_sprite_line, default_sprite_column, default_animation_duration,
        "RIGHT"
    );
    add_animation("zero_star", fox_zero_star);

    engine::Animation* fox_one_star = nullptr;
    fox_one_star = create_animation(
        "../assets/images/sprites/hud/hud_fox_1.png",
        default_sprite_line, default_sprite_column, default_animation_duration,
        "RIGHT"
    );
    add_animation("one_star", fox_one_star);

    engine::Animation* fox_two_star = nullptr;
    fox_two_star = create_animation(
        "../assets/images/sprites/hud/hud_fox_2.png",
        default_sprite_line, default_sprite_column, default_animation_duration,
        "RIGHT"
    );
    add_animation("two_star", fox_two_star);

    engine::Animation* fox_three_star = nullptr;
    fox_three_star = create_animation(
        "../assets/images/sprites/hud/hud_fox_3.png",
        default_sprite_line, default_sprite_column, default_animation_duration,
        "RIGHT"
    );
    add_animation("three_star", fox_three_star);

    const int sprite_columns_tree_star = 4; /**< Default sprite column of tree
    star fading */
    const double duration_tree_star = 1.0; /**< Default duration of tree
    star fading in seconds */

    engine::Animation* fox_three_star_fading = nullptr;
    fox_three_star_fading = create_animation(
        "../assets/images/sprites/hud/hud_fox_3_animation.png",
        default_sprite_line, sprite_columns_tree_star, duration_tree_star,
        "RIGHT"
    );
    fox_three_star_fading->in_loop = false;
    add_animation("three_star_fading", fox_three_star_fading);

    fox_zero_star->activate();
    set_actual_animation(fox_zero_star);

    DEBUG("Ended");
}

/**
 * @brief Creates Hud Fox's animation.
 *
 * Creates all Hud Fox's animation based on Hud Fox's sprites.
 *
 * @param image_path Path of the Hud Fox's sprite.
 * @param sprite_lines Line of the Hud Fox's sprite.
 * @warning Limitations of sprite_lines and sprite_columns are
 * 1 to the quantity of lines/columns in the image.
 * @param sprite_columns Column of the Fox's sprite needed.
 * @param duration Duration of the Hud Fox's image to show up.
 * @param direction Direction of the Hud Fox's image.
 * @return engine::Animation* The animation constructed.
 */
engine::Animation* HudFox::create_animation(
    std::string path,
    int sprite_lines,
    int sprite_columns,
    double duration,
    std::string direction) {

    DEBUG("Started");
    engine::Game& game = engine::Game::get_instance();

    /* Constants for default animation creation */
    const bool default_is_active = false;
    const std::pair<int, int> default_displacement = std::make_pair(0, 0);
    const int default_priority = 1;
    const bool default_in_loop = true;

    engine::Animation* animation = nullptr;
    animation = new engine::Animation(
        game.get_renderer(),
        path,
        default_is_active,
        default_displacement,
        default_priority,
        sprite_lines,
        sprite_columns,
        duration,
        default_in_loop,
        direction
    );

    /* Defaults dimensions and coordinates of hud fox in pixels */
    const std::pair<int, int> default_dimensions_hud_fox =
        std::make_pair(170, 78);
    const std::pair<int, int> coordinates_on_texture_hud_fox =
        std::make_pair(0, 0);

    animation->set_values(
        default_dimensions_hud_fox,
        default_dimensions_hud_fox ,
        coordinates_on_texture_hud_fox
    );

    DEBUG("Ended");

    return animation;
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/utils/stopreg/p9_stop_data_struct.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file   p9_stop_data_struct.H
/// @brief  describes data structures internal to STOP API.
///
// *HWP HW Owner    :  Greg Still <stillgs@us.ibm.com>
// *HWP FW Owner    :  Prem Shanker Jha <premjha2@in.ibm.com>
// *HWP Team        :  PM
// *HWP Level       :  2
// *HWP Consumed by :  HB:HYP
#ifndef __STOP_DATA_STRUCT_
#define __STOP_DATA_STRUCT_

#include "p9_hcd_memmap_base.H"

#ifdef __SKIBOOT__
    #include <skiboot.h>
#endif

#ifdef __FAPI_2_
    #include <fapi2.H>
#endif

#ifdef PPC_HYP

    #define STATIC

#else

    #define STATIC static

#endif

#ifdef __cplusplus
extern "C" {
namespace stopImageSection
{
#endif

enum
{
    MAX_SPR_RESTORE_INST = 0x08,
    SIZE_PER_SPR_RESTORE_INST = ((4 * sizeof(uint8_t)) / sizeof(uint32_t)),
};

typedef struct
{
    uint32_t scomEntryHeader;
    uint32_t scomEntryAddress;
    uint64_t scomEntryData;
} ScomEntry_t;

/**
 * @brief models a CPU register restoration area in STOP section of homer image.
 */
typedef struct
{
    uint8_t threadArea[CORE_RESTORE_THREAD_AREA_SIZE];
    uint8_t coreArea[CORE_RESTORE_CORE_AREA_SIZE];
} SprRestoreArea_t;

/**
 * @brief   models homer image of a chip.
 * @note    sections not relevant for CPU register restoration have been
 * abstracted using field 'reserve'.
 */
typedef struct
{
    uint8_t occ_host_sgpe_area[ TWO_MB ];  // CPU restore area starts at an offset of 2MB from chip HOMER
    uint8_t interrruptHandler[SELF_RESTORE_INT_SIZE];
    uint8_t threadLauncher[THREAD_LAUNCHER_SIZE];
    SprRestoreArea_t coreThreadRestore[MAX_CORES_PER_CHIP][MAX_THREADS_PER_CORE];
    uint8_t reserve[(ONE_KB * ONE_KB) - SELF_RESTORE_SIZE_TOTAL];
} HomerSection_t;

/**
 * @brief models cache subsection in STOP section of a given homer image.
 * @note    given the start of cache subsection associated with a given core,
 *          the structure below represents what a cache subsection would look
 *          like. Based on known start address, quick traversing can be done
 *          within the cache subsection.
 */
typedef struct
{
    ScomEntry_t  nonCacheArea[MAX_EQ_SCOM_ENTRIES];
    ScomEntry_t  l2CacheArea[MAX_L2_SCOM_ENTRIES];
    ScomEntry_t  l3CacheArea[MAX_L3_SCOM_ENTRIES];
} StopCacheSection_t;

/**
 * @brief summarizes attributes associated with a SPR register.
 */
typedef struct
{
    uint32_t sprId;
    bool isThreadScope;
} StopSprReg_t;

enum
{
    SIZE_SCOM_ENTRY = sizeof( ScomEntry_t ),
    SCOM_ENTRY_START = 0xDEADDEAD,
};

#ifdef __FAPI_2_
    #define MY_ERR( _fmt_, _args_...)  FAPI_ERR(_fmt_, ##_args_)
    #define MY_INF(_fmt_, _args_...) FAPI_INF(_fmt_, ##_args_)
#else
    #define MY_ERR( _fmt_, _args_...)
    #define MY_INF(_fmt_, _args_...)
#endif

#define CORE_ID_SCOM_START(io_image,\
                           i_chipletId) \
((ScomEntry_t*)(((uint8_t*)(io_image)) + CORE_SCOM_RESTORE_HOMER_OFFSET +\
                ((i_chipletId - CORE_CHIPLET_ID_MIN) * \
                 CORE_SCOM_RESTORE_SIZE_PER_CORE)));

#define CACHE_SECTN_START(io_image,\
                          i_chipletId) \
((StopCacheSection_t *)(((uint8_t *)(io_image)) + QUAD_SCOM_RESTORE_HOMER_OFFSET +\
                        ((i_chipletId - CACHE_CHIPLET_ID_MIN) * \
                         QUAD_SCOM_RESTORE_SIZE_PER_QUAD)));

#define CACHE_SCOM_ADDR(io_image,\
                        i_chipletId,\
                        i_maxScomEntry)\
((ScomEntry_t *)(((uint8_t *)(io_image)) + QUAD_SCOM_RESTORE_HOMER_OFFSET +\
                 ((i_chipletId - CACHE_CHIPLET_ID_MIN) * \
                  ((i_maxScomEntry + 1) * 16 ))));
#ifdef __cplusplus
}  // extern "C"

} //namespace stopImageSection ends
#endif //__cplusplus

#endif
<commit_msg>UV Support : Augmented STOP API and self restore for enabling ultravisor.<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/utils/stopreg/p9_stop_data_struct.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* 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.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file   p9_stop_data_struct.H
/// @brief  describes data structures internal to STOP API.
///
// *HWP HW Owner    :  Greg Still <stillgs@us.ibm.com>
// *HWP FW Owner    :  Prem Shanker Jha <premjha2@in.ibm.com>
// *HWP Team        :  PM
// *HWP Level       :  2
// *HWP Consumed by :  HB:HYP
#ifndef __STOP_DATA_STRUCT_
#define __STOP_DATA_STRUCT_

#include "p9_hcd_memmap_base.H"

#ifdef __SKIBOOT__
    #include <skiboot.h>
#endif

#ifdef __FAPI_2_
    #include <fapi2.H>
#endif

#ifdef PPC_HYP

    #define STATIC

#else

    #define STATIC static

#endif

#ifdef __cplusplus
extern "C" {
namespace stopImageSection
{
#endif

enum
{
    MAX_SPR_RESTORE_INST        =   0x08,
    SIZE_PER_SPR_RESTORE_INST   =   ((4 * sizeof(uint8_t)) / sizeof(uint32_t)),
    MAX_THREAD_LEVEL_SPRS       =   11,
    MAX_CORE_LEVEL_SPRS         =   6,
    MAX_SPR_BIT_POS             =   31,
    SPR_BIT_POS_8               =    8,
    SPR_BIT_POS_19              =   19,
    SPR_BIT_POS_25              =   25,
    SPR_BIT_POS_27              =   27,
};

enum SprEntryUpdateMode
{
    INIT_SPR_REGION       =   0x01,
    UPDATE_SPR_ENTRY      =   0x02,
};

typedef struct
{
    uint32_t scomEntryHeader;
    uint32_t scomEntryAddress;
    uint64_t scomEntryData;
} ScomEntry_t;

/**
 * @brief models a CPU register restoration area in STOP section of homer image.
 */
typedef struct
{
    uint8_t iv_threadRestoreArea[MAX_THREADS_PER_CORE][CORE_RESTORE_THREAD_AREA_SIZE];
    uint8_t iv_threadSaveArea[MAX_THREADS_PER_CORE][SELF_SAVE_THREAD_AREA_SIZE];
    uint8_t iv_coreRestoreArea[CORE_RESTORE_CORE_AREA_SIZE];
    uint8_t iv_coreSaveArea[CORE_SAVE_CORE_AREA_SIZE];
} SprRestoreArea_t;

/**
 * @brief   models homer image of a chip.
 * @note    sections not relevant for CPU register restoration have been
 * abstracted using field 'reserve'.
 */
typedef struct
{
    uint8_t iv_occ_host_sgpe_area[ TWO_MB ];  // CPU restore area starts at an offset of 2MB from chip HOMER
    uint8_t iv_interrruptHandler[SELF_RESTORE_INT_SIZE];
    uint8_t iv_threadLauncher[THREAD_LAUNCHER_SIZE];
    SprRestoreArea_t iv_coreThreadRestore[MAX_CORES_PER_CHIP];
    uint8_t reserve[(ONE_KB * ONE_KB) - SELF_RESTORE_SIZE_TOTAL];
} HomerSection_t;

/**
 * @brief models cache subsection in STOP section of a given homer image.
 * @note    given the start of cache subsection associated with a given core,
 *          the structure below represents what a cache subsection would look
 *          like. Based on known start address, quick traversing can be done
 *          within the cache subsection.
 */
typedef struct
{
    ScomEntry_t  nonCacheArea[MAX_EQ_SCOM_ENTRIES];
    ScomEntry_t  l2CacheArea[MAX_L2_SCOM_ENTRIES];
    ScomEntry_t  l3CacheArea[MAX_L3_SCOM_ENTRIES];
} StopCacheSection_t;

/**
 * @brief summarizes attributes associated with a SPR register.
 */
typedef struct
{
    uint32_t iv_sprId;
    bool     iv_isThreadScope;
    uint32_t iv_saveMaskPos;

} StopSprReg_t;

enum
{
    SIZE_SCOM_ENTRY     =   sizeof( ScomEntry_t ),
    SCOM_ENTRY_START    =   0xDEADDEAD,
    BAD_SAVE_MASK       =   0x007FF000,
    MAX_SPR_INDEX       =   31,
    TEST_BIT_PATTERN    =   0x80000000,
};

#ifdef __FAPI_2_
    #define MY_ERR( _fmt_, _args_...)  FAPI_ERR(_fmt_, ##_args_)
    #define MY_INF(_fmt_, _args_...) FAPI_INF(_fmt_, ##_args_)
#else
    #define MY_ERR( _fmt_, _args_...)
    #define MY_INF(_fmt_, _args_...)
#endif

#define CORE_ID_SCOM_START(io_image,\
                           i_chipletId) \
((ScomEntry_t*)(((uint8_t*)(io_image)) + CORE_SCOM_RESTORE_HOMER_OFFSET +\
                ((i_chipletId - CORE_CHIPLET_ID_MIN) * \
                 CORE_SCOM_RESTORE_SIZE_PER_CORE)));

#define CACHE_SECTN_START(io_image,\
                          i_chipletId) \
((StopCacheSection_t *)(((uint8_t *)(io_image)) + QUAD_SCOM_RESTORE_HOMER_OFFSET +\
                        ((i_chipletId - CACHE_CHIPLET_ID_MIN) * \
                         QUAD_SCOM_RESTORE_SIZE_PER_QUAD)));

#define CACHE_SCOM_ADDR(io_image,\
                        i_chipletId,\
                        i_maxScomEntry)\
((ScomEntry_t *)(((uint8_t *)(io_image)) + QUAD_SCOM_RESTORE_HOMER_OFFSET +\
                 ((i_chipletId - CACHE_CHIPLET_ID_MIN) * \
                  ((i_maxScomEntry + 1) * 16 ))));
#ifdef __cplusplus
}  // extern "C"

} //namespace stopImageSection ends
#endif //__cplusplus

#endif
<|endoftext|>


<commit_before>#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK

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

#include <mutex>
#include <thread>

#include <boost/test/unit_test.hpp>

#include "jml/arch/atomic_ops.h"
#include "jml/arch/exception.h"
#include "jml/arch/futex.h"
#include "jml/arch/timers.h"
#include "jml/utils/exc_assert.h"
#include "jml/utils/string_functions.h"
#include "soa/service/message_loop.h"
#include "soa/service/runner.h"
#include "soa/service/sink.h"
#include "soa/types/date.h"

#include <iostream>

#include "runner_test.h"

#include "signals.h"

using namespace std;
using namespace Datacratic;

// #define BOOST_CHECK_EQUAL(x,y)  { ExcCheckEqual((x), (y), ""); }

struct _Init {
    _Init() {
        signal(SIGPIPE, SIG_IGN);
    }
} myInit;

#if 1
/* ensures that the basic callback system works */
BOOST_AUTO_TEST_CASE( test_runner_callbacks )
{
    BlockedSignals blockedSigs2(SIGCHLD);

    MessageLoop loop;

    RunnerTestHelperCommands commands;
    commands.sendOutput(true, "hello stdout");
    commands.sendOutput(true, "hello stdout2");
    commands.sendOutput(false, "hello stderr");
    commands.sendExit(0);

    string receivedStdOut, expectedStdOut;
    string receivedStdErr, expectedStdErr;

    expectedStdOut = ("helper: ready\nhello stdout\nhello stdout2\n"
                      "helper: exit with code 0\n");
    expectedStdErr = "hello stderr\n";

    int done = false;
    auto onTerminate = [&] (const Runner::RunResult & result) {
        done = true;
        ML::futex_wake(done);
    };

    auto onStdOut = [&] (string && message) {
        // cerr << "received message on stdout: /" + message + "/" << endl;
        receivedStdOut += message;
    };
    auto stdOutSink = make_shared<CallbackInputSink>(onStdOut);

    auto onStdErr = [&] (string && message) {
        // cerr << "received message on stderr: /" + message + "/" << endl;
        receivedStdErr += message;
    };
    auto stdErrSink = make_shared<CallbackInputSink>(onStdErr);

    Runner runner;
    loop.addSource("runner", runner);
    loop.start();

    auto & stdInSink = runner.getStdInSink();
    runner.run({"build/x86_64/bin/runner_test_helper"},
               onTerminate, stdOutSink, stdErrSink);
    for (const string & command: commands) {
        while (!stdInSink.write(string(command))) {
            ML::sleep(0.1);
        }
    }
    stdInSink.requestClose();

    while (!done) {
        ML::futex_wait(done, false);
    }

    BOOST_CHECK_EQUAL(ML::hexify_string(receivedStdOut),
                      ML::hexify_string(expectedStdOut));
    BOOST_CHECK_EQUAL(ML::hexify_string(receivedStdErr),
                      ML::hexify_string(expectedStdErr));

    loop.shutdown();
}
#endif

#if 1
/* ensures that the returned status is properly set after termination */
BOOST_AUTO_TEST_CASE( test_runner_normal_exit )
{
    BlockedSignals blockedSigs(SIGCHLD);

    auto nullSink = make_shared<NullInputSink>();

    /* normal termination, with code */
    {
        MessageLoop loop;

        RunnerTestHelperCommands commands;
        commands.sendExit(123);

        Runner::RunResult result;
        auto onTerminate = [&] (const Runner::RunResult & newResult) {
            result = newResult;
        };
        Runner runner;
        loop.addSource("runner", runner);
        loop.start();

        auto & stdInSink = runner.getStdInSink();
        runner.run({"build/x86_64/bin/runner_test_helper"},
                   onTerminate, nullSink, nullSink);
        for (const string & command: commands) {
            stdInSink.write(string(command));
        }
        stdInSink.requestClose();
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, false);
        BOOST_CHECK_EQUAL(result.returnCode, 123);

        loop.shutdown();
    }

    /* aborted termination, with signum */
    {
        MessageLoop loop;

        RunnerTestHelperCommands commands;
        commands.sendAbort();

        Runner::RunResult result;
        auto onTerminate = [&] (const Runner::RunResult & newResult) {
            result = newResult;
        };
        Runner runner;
        loop.addSource("runner", runner);
        loop.start();

        auto & stdInSink = runner.getStdInSink();
        runner.run({"build/x86_64/bin/runner_test_helper"},
                   onTerminate, nullSink, nullSink);
        for (const string & command: commands) {
            stdInSink.write(string(command));
        }
        stdInSink.requestClose();
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, true);
        BOOST_CHECK_EQUAL(result.returnCode, SIGABRT);

        loop.shutdown();
    }
}
#endif

#if 1
/* test the behaviour of the Runner class when attempting to launch a missing
 * executable, mostly mimicking bash */
BOOST_AUTO_TEST_CASE( test_runner_missing_exe )
{
    Runner::RunResult result;
    auto onTerminate = [&] (const Runner::RunResult & newResult) {
        result = newResult;
    };

    /* running a program that does not exist */
    {
        MessageLoop loop;
        Runner runner;

        loop.start();
        loop.addSource("runner1", runner);

        runner.run({"/this/command/is/missing"}, onTerminate);
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, false);
        BOOST_CHECK_EQUAL(result.returnCode, 127);

        loop.removeSource(&runner);
        loop.shutdown();
    }

    /* running a non-executable but existing file */
    {
        MessageLoop loop;
        Runner runner;

        loop.start();
        loop.addSource("runner2", runner);

        runner.run({"/dev/null"}, onTerminate);
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, false);
        BOOST_CHECK_EQUAL(result.returnCode, 126);

        loop.removeSource(&runner);
        loop.shutdown();
    }

    /* running a non-executable but existing non-file */
    {
        MessageLoop loop;
        Runner runner;

        loop.start();
        loop.addSource("runner3", runner);

        runner.run({"/dev"}, onTerminate);
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, false);
        BOOST_CHECK_EQUAL(result.returnCode, 126);

        loop.removeSource(&runner);
        loop.shutdown();
    }

}
#endif

#if 1
/* test the "execute" function */
BOOST_AUTO_TEST_CASE( test_runner_execute )
{
    string received;
    auto onStdOut = [&] (string && message) {
        received = move(message);
    };
    auto stdOutSink = make_shared<CallbackInputSink>(onStdOut, nullptr);

    auto result = execute({"/bin/cat", "-"},
                          stdOutSink, nullptr, "hello callbacks");
    BOOST_CHECK_EQUAL(received, "hello callbacks");
    BOOST_CHECK_EQUAL(result.signaled, false);
    BOOST_CHECK_EQUAL(result.returnCode, 0);
}
#endif

#if 1
/* perform multiple runs with the same Runner and ensures task-specific
 * components are properly segregated */
BOOST_AUTO_TEST_CASE( test_runner_cleanup )
{
    MessageLoop loop;

    Runner runner;
    loop.addSource("runner", runner);
    loop.start();

    auto nullSink = make_shared<NullInputSink>();

    auto performLoop = [&] (const string & loopData) {
        RunnerTestHelperCommands commands;
        commands.sendOutput(true, loopData);
        commands.sendExit(0);

        string expectedStdOut("helper: ready\n" + loopData
                              + "\nhelper: exit with code 0\n");
        string receivedStdOut;
        auto onStdOut = [&] (string && message) {
            // cerr << "received message on stdout: /" + message + "/" << endl;
            receivedStdOut += message;
        };
        auto stdOutSink = make_shared<CallbackInputSink>(onStdOut);

        auto & stdInSink = runner.getStdInSink();
        runner.run({"build/x86_64/bin/runner_test_helper"},
                   nullptr, stdOutSink, nullSink);
        for (const string & command: commands) {
            stdInSink.write(string(command));
        }
        stdInSink.requestClose();
        runner.waitTermination();

        BOOST_CHECK_EQUAL(ML::hexify_string(receivedStdOut),
                          ML::hexify_string(expectedStdOut));
    };

    for (int i = 0; i < 5; i++) {
        performLoop(to_string(i));
    }

    loop.shutdown();
}
#endif

#if 1
/* Ensures that the output is received as soon as it is emitted, and not by
 * chunks. This is done by expecting different types of strings: a simple one
 * with a few chars, another one with two carriage returns and a third one
 * with 1024 chars. The test works by ensuring that all strings are received
 * one by one, with a relatively precise and constant delay of 1 second
 * between them. */
static void
test_runner_no_output_delay_helper(bool stdout)
{
    double delays[3];
    int sizes[3];
    int pos(stdout ? -1 : 0);
    shared_ptr<CallbackInputSink> stdOutSink(nullptr);
    shared_ptr<CallbackInputSink> stdErrSink(nullptr);

    Date start = Date::now();
    Date last = start;

    auto onCapture = [&] (string && message) {
        Date now = Date::now();
        if (pos > -1 && pos < 3) {
            /* skip "helper: ready" message */
            delays[pos] = now.secondsSinceEpoch() - last.secondsSinceEpoch();
            sizes[pos] = message.size();
        }
        pos++;
        last = now;
    };
    if (stdout) {
        stdOutSink.reset(new CallbackInputSink(onCapture));
    }
    else {
        stdErrSink.reset(new CallbackInputSink(onCapture));
    }

    RunnerTestHelperCommands commands;
    commands.sendSleep(10);
    commands.sendOutput(stdout, "first");
    commands.sendSleep(10);
    commands.sendOutput(stdout, "second\nsecond");
    commands.sendSleep(10);

    string third;
    for (int i = 0; i < 128; i++) {
        third += "abcdefgh";
    }
    commands.sendOutput(stdout, third);
    commands.sendSleep(10);
    commands.sendExit(0);

    MessageLoop loop;
    Runner runner;
    loop.addSource("runner", runner);
    loop.start();

    auto & stdInSink = runner.getStdInSink();
    runner.run({"build/x86_64/bin/runner_test_helper"},
               nullptr, stdOutSink, stdErrSink);
    for (const string & command: commands) {
        while (!stdInSink.write(string(command))) {
            ML::sleep(0.1);
        }
    }
    stdInSink.requestClose();
    Date end = Date::now();
    runner.waitTermination();

    BOOST_CHECK_EQUAL(sizes[0], 6);
    BOOST_CHECK(delays[0] >= 0.9);
    BOOST_CHECK_EQUAL(sizes[1], 14);
    BOOST_CHECK(delays[1] >= 0.9);
    BOOST_CHECK_EQUAL(sizes[2], 1025);
    BOOST_CHECK(delays[2] >= 0.9);

    for (int i = 0; i < 3; i++) {
        ::fprintf(stderr, "%d: size: %d; delay: %f\n", i, sizes[i], delays[i]);
    }
}

BOOST_AUTO_TEST_CASE( test_runner_no_output_delay_stdout )
{
    test_runner_no_output_delay_helper(true);
}

BOOST_AUTO_TEST_CASE( test_runner_no_output_delay_stderr )
{
    test_runner_no_output_delay_helper(false);
}
#endif

<commit_msg>Revert "runner_test: worked around timing issues causing test to hang when using the same MessageLoop"<commit_after>#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK

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

#include <mutex>
#include <thread>

#include <boost/test/unit_test.hpp>

#include "jml/arch/atomic_ops.h"
#include "jml/arch/exception.h"
#include "jml/arch/futex.h"
#include "jml/arch/timers.h"
#include "jml/utils/exc_assert.h"
#include "jml/utils/string_functions.h"
#include "soa/service/message_loop.h"
#include "soa/service/runner.h"
#include "soa/service/sink.h"
#include "soa/types/date.h"

#include <iostream>

#include "runner_test.h"

#include "signals.h"

using namespace std;
using namespace Datacratic;

// #define BOOST_CHECK_EQUAL(x,y)  { ExcCheckEqual((x), (y), ""); }

struct _Init {
    _Init() {
        signal(SIGPIPE, SIG_IGN);
    }
} myInit;

#if 1
/* ensures that the basic callback system works */
BOOST_AUTO_TEST_CASE( test_runner_callbacks )
{
    BlockedSignals blockedSigs2(SIGCHLD);

    MessageLoop loop;

    RunnerTestHelperCommands commands;
    commands.sendOutput(true, "hello stdout");
    commands.sendOutput(true, "hello stdout2");
    commands.sendOutput(false, "hello stderr");
    commands.sendExit(0);

    string receivedStdOut, expectedStdOut;
    string receivedStdErr, expectedStdErr;

    expectedStdOut = ("helper: ready\nhello stdout\nhello stdout2\n"
                      "helper: exit with code 0\n");
    expectedStdErr = "hello stderr\n";

    int done = false;
    auto onTerminate = [&] (const Runner::RunResult & result) {
        done = true;
        ML::futex_wake(done);
    };

    auto onStdOut = [&] (string && message) {
        // cerr << "received message on stdout: /" + message + "/" << endl;
        receivedStdOut += message;
    };
    auto stdOutSink = make_shared<CallbackInputSink>(onStdOut);

    auto onStdErr = [&] (string && message) {
        // cerr << "received message on stderr: /" + message + "/" << endl;
        receivedStdErr += message;
    };
    auto stdErrSink = make_shared<CallbackInputSink>(onStdErr);

    Runner runner;
    loop.addSource("runner", runner);
    loop.start();

    auto & stdInSink = runner.getStdInSink();
    runner.run({"build/x86_64/bin/runner_test_helper"},
               onTerminate, stdOutSink, stdErrSink);
    for (const string & command: commands) {
        while (!stdInSink.write(string(command))) {
            ML::sleep(0.1);
        }
    }
    stdInSink.requestClose();

    while (!done) {
        ML::futex_wait(done, false);
    }

    BOOST_CHECK_EQUAL(ML::hexify_string(receivedStdOut),
                      ML::hexify_string(expectedStdOut));
    BOOST_CHECK_EQUAL(ML::hexify_string(receivedStdErr),
                      ML::hexify_string(expectedStdErr));

    loop.shutdown();
}
#endif

#if 1
/* ensures that the returned status is properly set after termination */
BOOST_AUTO_TEST_CASE( test_runner_normal_exit )
{
    BlockedSignals blockedSigs(SIGCHLD);

    auto nullSink = make_shared<NullInputSink>();

    /* normal termination, with code */
    {
        MessageLoop loop;

        RunnerTestHelperCommands commands;
        commands.sendExit(123);

        Runner::RunResult result;
        auto onTerminate = [&] (const Runner::RunResult & newResult) {
            result = newResult;
        };
        Runner runner;
        loop.addSource("runner", runner);
        loop.start();

        auto & stdInSink = runner.getStdInSink();
        runner.run({"build/x86_64/bin/runner_test_helper"},
                   onTerminate, nullSink, nullSink);
        for (const string & command: commands) {
            stdInSink.write(string(command));
        }
        stdInSink.requestClose();
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, false);
        BOOST_CHECK_EQUAL(result.returnCode, 123);

        loop.shutdown();
    }

    /* aborted termination, with signum */
    {
        MessageLoop loop;

        RunnerTestHelperCommands commands;
        commands.sendAbort();

        Runner::RunResult result;
        auto onTerminate = [&] (const Runner::RunResult & newResult) {
            result = newResult;
        };
        Runner runner;
        loop.addSource("runner", runner);
        loop.start();

        auto & stdInSink = runner.getStdInSink();
        runner.run({"build/x86_64/bin/runner_test_helper"},
                   onTerminate, nullSink, nullSink);
        for (const string & command: commands) {
            stdInSink.write(string(command));
        }
        stdInSink.requestClose();
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, true);
        BOOST_CHECK_EQUAL(result.returnCode, SIGABRT);

        loop.shutdown();
    }
}
#endif

#if 1
/* test the behaviour of the Runner class when attempting to launch a missing
 * executable, mostly mimicking bash */
BOOST_AUTO_TEST_CASE( test_runner_missing_exe )
{
    MessageLoop loop;

    loop.start();

    Runner::RunResult result;
    auto onTerminate = [&] (const Runner::RunResult & newResult) {
        result = newResult;
    };

    /* running a program that does not exist */
    {
        Runner runner;
        loop.addSource("runner1", runner);

        runner.run({"/this/command/is/missing"}, onTerminate);
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, false);
        BOOST_CHECK_EQUAL(result.returnCode, 127);

        loop.removeSource(&runner);
    }

    /* running a non-executable but existing file */
    {
        Runner runner;
        loop.addSource("runner2", runner);

        runner.run({"/dev/null"}, onTerminate);
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, false);
        BOOST_CHECK_EQUAL(result.returnCode, 126);

        loop.removeSource(&runner);
    }

    /* running a non-executable but existing non-file */
    {
        Runner runner;
        loop.addSource("runner2", runner);

        runner.run({"/dev"}, onTerminate);
        runner.waitTermination();

        BOOST_CHECK_EQUAL(result.signaled, false);
        BOOST_CHECK_EQUAL(result.returnCode, 126);

        loop.removeSource(&runner);
    }

    loop.shutdown();
}
#endif

#if 1
/* test the "execute" function */
BOOST_AUTO_TEST_CASE( test_runner_execute )
{
    string received;
    auto onStdOut = [&] (string && message) {
        received = move(message);
    };
    auto stdOutSink = make_shared<CallbackInputSink>(onStdOut, nullptr);

    auto result = execute({"/bin/cat", "-"},
                          stdOutSink, nullptr, "hello callbacks");
    BOOST_CHECK_EQUAL(received, "hello callbacks");
    BOOST_CHECK_EQUAL(result.signaled, false);
    BOOST_CHECK_EQUAL(result.returnCode, 0);
}
#endif

#if 1
/* perform multiple runs with the same Runner and ensures task-specific
 * components are properly segregated */
BOOST_AUTO_TEST_CASE( test_runner_cleanup )
{
    MessageLoop loop;

    Runner runner;
    loop.addSource("runner", runner);
    loop.start();

    auto nullSink = make_shared<NullInputSink>();

    auto performLoop = [&] (const string & loopData) {
        RunnerTestHelperCommands commands;
        commands.sendOutput(true, loopData);
        commands.sendExit(0);

        string expectedStdOut("helper: ready\n" + loopData
                              + "\nhelper: exit with code 0\n");
        string receivedStdOut;
        auto onStdOut = [&] (string && message) {
            // cerr << "received message on stdout: /" + message + "/" << endl;
            receivedStdOut += message;
        };
        auto stdOutSink = make_shared<CallbackInputSink>(onStdOut);

        auto & stdInSink = runner.getStdInSink();
        runner.run({"build/x86_64/bin/runner_test_helper"},
                   nullptr, stdOutSink, nullSink);
        for (const string & command: commands) {
            stdInSink.write(string(command));
        }
        stdInSink.requestClose();
        runner.waitTermination();

        BOOST_CHECK_EQUAL(ML::hexify_string(receivedStdOut),
                          ML::hexify_string(expectedStdOut));
    };

    for (int i = 0; i < 5; i++) {
        performLoop(to_string(i));
    }

    loop.shutdown();
}
#endif

#if 1
/* Ensures that the output is received as soon as it is emitted, and not by
 * chunks. This is done by expecting different types of strings: a simple one
 * with a few chars, another one with two carriage returns and a third one
 * with 1024 chars. The test works by ensuring that all strings are received
 * one by one, with a relatively precise and constant delay of 1 second
 * between them. */
static void
test_runner_no_output_delay_helper(bool stdout)
{
    double delays[3];
    int sizes[3];
    int pos(stdout ? -1 : 0);
    shared_ptr<CallbackInputSink> stdOutSink(nullptr);
    shared_ptr<CallbackInputSink> stdErrSink(nullptr);

    Date start = Date::now();
    Date last = start;

    auto onCapture = [&] (string && message) {
        Date now = Date::now();
        if (pos > -1 && pos < 3) {
            /* skip "helper: ready" message */
            delays[pos] = now.secondsSinceEpoch() - last.secondsSinceEpoch();
            sizes[pos] = message.size();
        }
        pos++;
        last = now;
    };
    if (stdout) {
        stdOutSink.reset(new CallbackInputSink(onCapture));
    }
    else {
        stdErrSink.reset(new CallbackInputSink(onCapture));
    }

    RunnerTestHelperCommands commands;
    commands.sendSleep(10);
    commands.sendOutput(stdout, "first");
    commands.sendSleep(10);
    commands.sendOutput(stdout, "second\nsecond");
    commands.sendSleep(10);

    string third;
    for (int i = 0; i < 128; i++) {
        third += "abcdefgh";
    }
    commands.sendOutput(stdout, third);
    commands.sendSleep(10);
    commands.sendExit(0);

    MessageLoop loop;
    Runner runner;
    loop.addSource("runner", runner);
    loop.start();

    auto & stdInSink = runner.getStdInSink();
    runner.run({"build/x86_64/bin/runner_test_helper"},
               nullptr, stdOutSink, stdErrSink);
    for (const string & command: commands) {
        while (!stdInSink.write(string(command))) {
            ML::sleep(0.1);
        }
    }
    stdInSink.requestClose();
    Date end = Date::now();
    runner.waitTermination();

    BOOST_CHECK_EQUAL(sizes[0], 6);
    BOOST_CHECK(delays[0] >= 0.9);
    BOOST_CHECK_EQUAL(sizes[1], 14);
    BOOST_CHECK(delays[1] >= 0.9);
    BOOST_CHECK_EQUAL(sizes[2], 1025);
    BOOST_CHECK(delays[2] >= 0.9);

    for (int i = 0; i < 3; i++) {
        ::fprintf(stderr, "%d: size: %d; delay: %f\n", i, sizes[i], delays[i]);
    }
}

BOOST_AUTO_TEST_CASE( test_runner_no_output_delay_stdout )
{
    test_runner_no_output_delay_helper(true);
}

BOOST_AUTO_TEST_CASE( test_runner_no_output_delay_stderr )
{
    test_runner_no_output_delay_helper(false);
}
#endif

<|endoftext|>


<commit_before>/***********************************************************************
Moses - factored phrase-based language decoder
Copyright (C) 2006 University of Edinburgh

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

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
***********************************************************************/

#include <cassert>
#include <limits>
#include <iostream>
#include <fstream>

#include "LanguageModelRandLM.h"
#include "FactorCollection.h"
#include "Phrase.h"
#include "InputFileStream.h"
#include "StaticData.h"

namespace Moses
{
using namespace std;

bool LanguageModelRandLM::Load(const std::string &filePath, FactorType factorType,
			       size_t nGramOrder) {
  cerr << "Loading LanguageModelRandLM..." << endl;
  FactorCollection &factorCollection = FactorCollection::Instance();
  m_filePath = filePath;
  m_factorType = factorType;
  m_nGramOrder = nGramOrder;
  int cache_MB = 50; // increase cache size
  m_lm = randlm::RandLM::initRandLM(filePath, nGramOrder, cache_MB);
  assert(m_lm != NULL);
  // get special word ids
  m_oov_id = m_lm->getWordID(m_lm->getOOV());
  CreateFactors(factorCollection);
  return true;
}

void LanguageModelRandLM::CreateFactors(FactorCollection &factorCollection) { // add factors which have randlm id
  // code copied & paste from SRI LM class. should do template function
  // first get all bf vocab in map
  std::map<size_t, randlm::WordID> randlm_ids_map; // map from factor id -> randlm id
  size_t maxFactorId = 0; // to create lookup vector later on
  for(std::map<randlm::Word, randlm::WordID>::const_iterator vIter = m_lm->vocabStart();
      vIter != m_lm->vocabEnd(); vIter++){
    // get word from randlm vocab and associate with (new) factor id
    size_t factorId=factorCollection.AddFactor(Output,m_factorType,vIter->first)->GetId();
    randlm_ids_map[factorId] = vIter->second;
    maxFactorId = (factorId > maxFactorId) ? factorId : maxFactorId;
  }
  // add factors for BOS and EOS and store bf word ids
  size_t factorId;
  m_sentenceStart = factorCollection.AddFactor(Output, m_factorType, m_lm->getBOS());
  factorId = m_sentenceStart->GetId();
  maxFactorId = (factorId > maxFactorId) ? factorId : maxFactorId;
  m_sentenceStartArray[m_factorType] = m_sentenceStart;

  m_sentenceEnd	= factorCollection.AddFactor(Output, m_factorType, m_lm->getEOS());
  factorId = m_sentenceEnd->GetId();
  maxFactorId = (factorId > maxFactorId) ? factorId : maxFactorId;
  m_sentenceEndArray[m_factorType] = m_sentenceEnd;

  // add to lookup vector in object
  m_randlm_ids_vec.resize(maxFactorId+1);
  // fill with OOV code
  fill(m_randlm_ids_vec.begin(), m_randlm_ids_vec.end(), m_oov_id);

  for (map<size_t, randlm::WordID>::const_iterator iter = randlm_ids_map.begin();
       iter != randlm_ids_map.end() ; ++iter)
    m_randlm_ids_vec[iter->first] = iter->second;

}

randlm::WordID LanguageModelRandLM::GetLmID( const std::string &str ) const {
  return m_lm->getWordID(str);
}

float LanguageModelRandLM::GetValue(const vector<const Word*> &contextFactor,
				    State* finalState) const {
  FactorType factorType = GetFactorType();
  // set up context
  randlm::WordID ngram[MAX_NGRAM_SIZE];
  int count = contextFactor.size();
  for (int i = 0 ; i < count ; i++) {
    ngram[i] = GetLmID((*contextFactor[i])[factorType]);
    //std::cerr << m_lm->getWord(ngram[i]) << " ";
  }
  int found = 0;
  float logprob = FloorScore(TransformLMScore(m_lm->getProb(&ngram[0], count, &found, finalState)));
  //if (finalState)
  //  std::cerr << " = " << logprob << "(" << *finalState << ", " <<")"<< std::endl;
  //else
  //  std::cerr << " = " << logprob << std::endl;
  return logprob;
}

}

<commit_msg>Changed white space to test effect on svn blame.<commit_after>/***********************************************************************
Moses - factored phrase-based language decoder
Copyright (C) 2006 University of Edinburgh

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

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
***********************************************************************/

#include <cassert>
#include <limits>
#include <iostream>
#include <fstream>

#include "LanguageModelRandLM.h"
#include "FactorCollection.h"
#include "Phrase.h"
#include "InputFileStream.h"
#include "StaticData.h"

namespace Moses
{
using namespace std;

bool LanguageModelRandLM::Load(const std::string &filePath, FactorType factorType,
                               size_t nGramOrder) {
  cerr << "Loading LanguageModelRandLM..." << endl;
  FactorCollection &factorCollection = FactorCollection::Instance();
  m_filePath = filePath;
  m_factorType = factorType;
  m_nGramOrder = nGramOrder;
  int cache_MB = 50; // increase cache size
  m_lm = randlm::RandLM::initRandLM(filePath, nGramOrder, cache_MB);
  assert(m_lm != NULL);
  // get special word ids
  m_oov_id = m_lm->getWordID(m_lm->getOOV());
  CreateFactors(factorCollection);
  return true;
}

void LanguageModelRandLM::CreateFactors(FactorCollection &factorCollection) { // add factors which have randlm id
  // code copied & paste from SRI LM class. should do template function
  // first get all bf vocab in map
  std::map<size_t, randlm::WordID> randlm_ids_map; // map from factor id -> randlm id
  size_t maxFactorId = 0; // to create lookup vector later on
  for(std::map<randlm::Word, randlm::WordID>::const_iterator vIter = m_lm->vocabStart();
      vIter != m_lm->vocabEnd(); vIter++){
    // get word from randlm vocab and associate with (new) factor id
    size_t factorId=factorCollection.AddFactor(Output,m_factorType,vIter->first)->GetId();
    randlm_ids_map[factorId] = vIter->second;
    maxFactorId = (factorId > maxFactorId) ? factorId : maxFactorId;
  }
  // add factors for BOS and EOS and store bf word ids
  size_t factorId;
  m_sentenceStart = factorCollection.AddFactor(Output, m_factorType, m_lm->getBOS());
  factorId = m_sentenceStart->GetId();
  maxFactorId = (factorId > maxFactorId) ? factorId : maxFactorId;
  m_sentenceStartArray[m_factorType] = m_sentenceStart;

  m_sentenceEnd	= factorCollection.AddFactor(Output, m_factorType, m_lm->getEOS());
  factorId = m_sentenceEnd->GetId();
  maxFactorId = (factorId > maxFactorId) ? factorId : maxFactorId;
  m_sentenceEndArray[m_factorType] = m_sentenceEnd;

  // add to lookup vector in object
  m_randlm_ids_vec.resize(maxFactorId+1);
  // fill with OOV code
  fill(m_randlm_ids_vec.begin(), m_randlm_ids_vec.end(), m_oov_id);

  for (map<size_t, randlm::WordID>::const_iterator iter = randlm_ids_map.begin();
       iter != randlm_ids_map.end() ; ++iter)
    m_randlm_ids_vec[iter->first] = iter->second;

}

randlm::WordID LanguageModelRandLM::GetLmID( const std::string &str ) const {
  return m_lm->getWordID(str);
}

float LanguageModelRandLM::GetValue(const vector<const Word*> &contextFactor,
				    State* finalState) const {
  FactorType factorType = GetFactorType();
  // set up context
  randlm::WordID ngram[MAX_NGRAM_SIZE];
  int count = contextFactor.size();
  for (int i = 0 ; i < count ; i++) {
    ngram[i] = GetLmID((*contextFactor[i])[factorType]);
    //std::cerr << m_lm->getWord(ngram[i]) << " ";
  }
  int found = 0;
  float logprob = FloorScore(TransformLMScore(m_lm->getProb(&ngram[0], count, &found, finalState)));
  //if (finalState)
  //  std::cerr << " = " << logprob << "(" << *finalState << ", " <<")"<< std::endl;
  //else
  //  std::cerr << " = " << logprob << std::endl;
  return logprob;
}

}

<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2018 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_map>

#include "incomes.hpp"
#include "accounts.hpp"
#include "budget_exception.hpp"
#include "args.hpp"
#include "data.hpp"
#include "guid.hpp"
#include "config.hpp"
#include "utils.hpp"
#include "console.hpp"
#include "earnings.hpp"
#include "expenses.hpp"
#include "writer.hpp"

using namespace budget;

namespace {

static data_handler<income> incomes { "incomes", "incomes.data" };

} //end of anonymous namespace

std::map<std::string, std::string> budget::income::get_params(){
    std::map<std::string, std::string> params;

    params["input_id"]     = budget::to_string(id);
    params["input_guid"]   = guid;
    params["input_amount"] = budget::to_string(amount);
    params["input_since"]  = budget::to_string(since);
    params["input_until"]  = budget::to_string(until);

    return params;
}

void budget::incomes_module::load(){
    load_incomes();
}

void budget::incomes_module::unload(){
    save_incomes();
}

void budget::incomes_module::handle(const std::vector<std::string>& args){
    console_writer w(std::cout);

    if(args.size() == 1){
        show_incomes(w);
    } else {
        auto& subcommand = args[1];

        if (subcommand == "show") {
            show_incomes(w);
        } else if (subcommand == "set") {
            budget::money amount;
            edit_money(amount, "Amount", not_negative_checker());

            auto & new_income = budget::new_income(amount, true);
            std::cout << "Income " << new_income.id << " has been created" << std::endl;
        } else {
            throw budget_exception("Invalid subcommand \"" + subcommand + "\"");
        }
    }
}

void budget::show_incomes(budget::writer& w) {
    if (!incomes.size()) {
        w << title_begin << "No income " << add_button("incomes") << title_end;
        return;
    }

    w << title_begin << "Incomes " << add_button("incomes") << title_end;

    std::vector<std::string> columns = {"ID", "Amount", "Since", "Until", "Edit"};
    std::vector<std::vector<std::string>> contents;

    for(auto& income : incomes.data){
        contents.push_back({to_string(income.id), to_string(income.amount), to_string(income.since), to_string(income.until), "::edit::incomes::" + to_string(income.id)});
    }

    w.display_table(columns, contents);
}

void budget::load_incomes(){
    incomes.load();
}

void budget::save_incomes(){
    incomes.save();
}

std::ostream& budget::operator<<(std::ostream& stream, const income& income){
    return stream << income.id  << ':' << income.guid << ':' << income.amount << ':' << to_string(income.since) << ':' << to_string(income.until);
}

void budget::operator>>(const std::vector<std::string>& parts, income& income){
    bool random = config_contains("random");

    income.id = to_number<size_t>(parts[0]);
    income.guid = parts[1];
    income.since = from_string(parts[3]);
    income.until = from_string(parts[4]);

    if(random){
        income.amount = budget::random_money(1000, 10000);
    } else {
        income.amount = parse_money(parts[2]);
    }
}

std::vector<income>& budget::all_incomes(){
    return incomes.data;
}

void budget::set_incomes_changed(){
    incomes.set_changed();
}

void budget::set_incomes_next_id(size_t next_id){
    incomes.next_id = next_id;
}

bool budget::income_exists(size_t id){
    return incomes.exists(id);
}

void budget::income_delete(size_t id) {
    if (!incomes.exists(id)) {
        throw budget_exception("There are no income with id ");
    }

    incomes.remove(id);
}

income& budget::income_get(size_t id) {
    if (!incomes.exists(id)) {
        throw budget_exception("There are no income with id ");
    }

    return incomes[id];
}

void budget::add_income(budget::income&& income){
    incomes.add(std::forward<budget::income>(income));
}

budget::money budget::get_base_income(){
    auto today = budget::local_day();
    return get_base_income(today);
}

budget::money budget::get_base_income(budget::date d){
    // First, we try to get the base income from the incomes module

    for (auto & income : incomes) {
        if (income.since <= d && income.until >= d) {
            return income.amount;
        }
    }

    // Otherwise, we use the accounts

    budget::money income;

    for (auto& account : all_accounts(d.year(), d.month())) {
        income += account.amount;
    }

    return income;
}

budget::income & budget::new_income(budget::money amount, bool print){
    budget::date d = budget::local_day();

    budget::date since(d.year(), d.month(), 1);
    budget::date until = budget::date(2099,12,31);

    budget::income new_income;
    new_income.guid  = generate_guid();
    new_income.since = since;
    new_income.until = until;
    new_income.amount = amount;

    if (incomes.size()) {
        // Try to edit the income from the same month
        for (auto & income : incomes) {
            if (income.since == since && income.until == until) {
                income.amount = new_income.amount;

                if (incomes.edit(income)) {
                    if (print) {
                        std::cout << "Income " << income.id << " has been modified" << std::endl;
                    }
                }

                return income;
            }
        }

        // Edit the previous income

        budget::date date = incomes.data.front().since;
        size_t id         = incomes.data.front().id;

        for (auto & income : incomes) {
            if (income.since > date) {
                date = income.since;
                id   = income.id;
            }
        }

        auto & previous_income = incomes[id];

        previous_income.until = since - budget::days(1);

        if (incomes.edit(previous_income)) {
            if (print) {
                std::cout << "Income " << id << " has been modified" << std::endl;
            }
        }

        return previous_income;
    }

    auto id = incomes.add(std::move(new_income));
    return incomes[id];
}
<commit_msg>Display current income<commit_after>//=======================================================================
// Copyright (c) 2013-2018 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_map>

#include "incomes.hpp"
#include "accounts.hpp"
#include "budget_exception.hpp"
#include "args.hpp"
#include "data.hpp"
#include "guid.hpp"
#include "config.hpp"
#include "utils.hpp"
#include "console.hpp"
#include "earnings.hpp"
#include "expenses.hpp"
#include "writer.hpp"

using namespace budget;

namespace {

static data_handler<income> incomes { "incomes", "incomes.data" };

} //end of anonymous namespace

std::map<std::string, std::string> budget::income::get_params(){
    std::map<std::string, std::string> params;

    params["input_id"]     = budget::to_string(id);
    params["input_guid"]   = guid;
    params["input_amount"] = budget::to_string(amount);
    params["input_since"]  = budget::to_string(since);
    params["input_until"]  = budget::to_string(until);

    return params;
}

void budget::incomes_module::load(){
    load_incomes();
}

void budget::incomes_module::unload(){
    save_incomes();
}

void budget::incomes_module::handle(const std::vector<std::string>& args){
    console_writer w(std::cout);

    if(args.size() == 1){
        show_incomes(w);
    } else {
        auto& subcommand = args[1];

        if (subcommand == "show") {
            show_incomes(w);
        } else if (subcommand == "set") {
            budget::money amount;
            edit_money(amount, "Amount", not_negative_checker());

            auto & new_income = budget::new_income(amount, true);
            std::cout << "Income " << new_income.id << " has been created" << std::endl;
        } else {
            throw budget_exception("Invalid subcommand \"" + subcommand + "\"");
        }
    }
}

void budget::show_incomes(budget::writer& w) {
    if (!incomes.size()) {
        w << title_begin << "No income " << add_button("incomes") << title_end;
        return;
    }

    w << title_begin << "Incomes " << add_button("incomes") << title_end;

    w << p_begin << "Current income: " << get_base_income() << " " << get_default_currency() << p_end;

    std::vector<std::string> columns = {"ID", "Amount", "Since", "Until", "Edit"};
    std::vector<std::vector<std::string>> contents;

    for(auto& income : incomes.data){
        contents.push_back({to_string(income.id), to_string(income.amount), to_string(income.since), to_string(income.until), "::edit::incomes::" + to_string(income.id)});
    }

    w.display_table(columns, contents);
}

void budget::load_incomes(){
    incomes.load();
}

void budget::save_incomes(){
    incomes.save();
}

std::ostream& budget::operator<<(std::ostream& stream, const income& income){
    return stream << income.id  << ':' << income.guid << ':' << income.amount << ':' << to_string(income.since) << ':' << to_string(income.until);
}

void budget::operator>>(const std::vector<std::string>& parts, income& income){
    bool random = config_contains("random");

    income.id = to_number<size_t>(parts[0]);
    income.guid = parts[1];
    income.since = from_string(parts[3]);
    income.until = from_string(parts[4]);

    if(random){
        income.amount = budget::random_money(1000, 10000);
    } else {
        income.amount = parse_money(parts[2]);
    }
}

std::vector<income>& budget::all_incomes(){
    return incomes.data;
}

void budget::set_incomes_changed(){
    incomes.set_changed();
}

void budget::set_incomes_next_id(size_t next_id){
    incomes.next_id = next_id;
}

bool budget::income_exists(size_t id){
    return incomes.exists(id);
}

void budget::income_delete(size_t id) {
    if (!incomes.exists(id)) {
        throw budget_exception("There are no income with id ");
    }

    incomes.remove(id);
}

income& budget::income_get(size_t id) {
    if (!incomes.exists(id)) {
        throw budget_exception("There are no income with id ");
    }

    return incomes[id];
}

void budget::add_income(budget::income&& income){
    incomes.add(std::forward<budget::income>(income));
}

budget::money budget::get_base_income(){
    auto today = budget::local_day();
    return get_base_income(today);
}

budget::money budget::get_base_income(budget::date d){
    // First, we try to get the base income from the incomes module

    for (auto & income : incomes) {
        if (income.since <= d && income.until >= d) {
            return income.amount;
        }
    }

    // Otherwise, we use the accounts

    budget::money income;

    for (auto& account : all_accounts(d.year(), d.month())) {
        income += account.amount;
    }

    return income;
}

budget::income & budget::new_income(budget::money amount, bool print){
    budget::date d = budget::local_day();

    budget::date since(d.year(), d.month(), 1);
    budget::date until = budget::date(2099,12,31);

    budget::income new_income;
    new_income.guid  = generate_guid();
    new_income.since = since;
    new_income.until = until;
    new_income.amount = amount;

    if (incomes.size()) {
        // Try to edit the income from the same month
        for (auto & income : incomes) {
            if (income.since == since && income.until == until) {
                income.amount = new_income.amount;

                if (incomes.edit(income)) {
                    if (print) {
                        std::cout << "Income " << income.id << " has been modified" << std::endl;
                    }
                }

                return income;
            }
        }

        // Edit the previous income

        budget::date date = incomes.data.front().since;
        size_t id         = incomes.data.front().id;

        for (auto & income : incomes) {
            if (income.since > date) {
                date = income.since;
                id   = income.id;
            }
        }

        auto & previous_income = incomes[id];

        previous_income.until = since - budget::days(1);

        if (incomes.edit(previous_income)) {
            if (print) {
                std::cout << "Income " << id << " has been modified" << std::endl;
            }
        }

        return previous_income;
    }

    auto id = incomes.add(std::move(new_income));
    return incomes[id];
}
<|endoftext|>


<commit_before>// =============================================================================
//  This file is part of:
//  Dynamic Adaptive System for Hierarchical Multipole Methods (DASHMM)
//
//  Copyright (c) 2015-2016, Trustees of Indiana University,
//  All rights reserved.
//
//  DASHMM is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  DASHMM 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 General Public License
//  along with DASHMM. If not, see <http://www.gnu.org/licenses/>.
//
//  This software was created at the Indiana University Center for Research in
//  Extreme Scale Technologies (CREST).
// =============================================================================

/// \file
/// \brief Implemention of DASHMM initialization and finalization

#include <hpx/hpx.h>
#include <libhpx/libhpx.h>

#include "dashmm/types.h"

namespace dashmm {

ReturnCode init(int *argc, char ***argv) {
  if (HPX_SUCCESS != hpx_init(argc, argv)) {
    return kRuntimeError;
  }

  if (libhpx_inst_tracer_active()) {
    libhpx_inst_phase_end();
  }

  return kSuccess;
}

ReturnCode finalize() {
  hpx_finalize();

  return kSuccess;
}

} // namespace dashmm
<commit_msg>Protect against poor coding in HPX-5 instrumentation. init() now checks for DASHMM_INSTRUMENTATION before interacting with the instrumentation stuff at all.<commit_after>// =============================================================================
//  This file is part of:
//  Dynamic Adaptive System for Hierarchical Multipole Methods (DASHMM)
//
//  Copyright (c) 2015-2016, Trustees of Indiana University,
//  All rights reserved.
//
//  DASHMM is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  DASHMM 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 General Public License
//  along with DASHMM. If not, see <http://www.gnu.org/licenses/>.
//
//  This software was created at the Indiana University Center for Research in
//  Extreme Scale Technologies (CREST).
// =============================================================================

/// \file
/// \brief Implemention of DASHMM initialization and finalization

#include <hpx/hpx.h>
#include <libhpx/libhpx.h>

#include "dashmm/types.h"

namespace dashmm {

ReturnCode init(int *argc, char ***argv) {
  if (HPX_SUCCESS != hpx_init(argc, argv)) {
    return kRuntimeError;
  }

#ifdef DASHMM_INSTRUMENTATION
  if (libhpx_inst_tracer_active()) {
    libhpx_inst_phase_end();
  }
#endif

  return kSuccess;
}

ReturnCode finalize() {
  hpx_finalize();

  return kSuccess;
}

} // namespace dashmm
<|endoftext|>