<commit_before>#include <Core/Tasks/TaskQueue.hpp>
#include <Core/Tasks/Task.hpp>

#include <stack>
#include <iostream>

namespace Ra
{
    namespace Core
    {

        TaskQueue::TaskQueue( uint numThreads )
            : m_processingTasks( 0 ), m_shuttingDown( false )
        {
            CORE_ASSERT( numThreads > 0, " You need at least one thread" );
            m_workerThreads.reserve( numThreads );
            for ( uint i = 0 ; i < numThreads; ++i )
            {
                m_workerThreads.emplace_back( std::thread( &TaskQueue::runThread, this, i ) );
            }
        }

        TaskQueue::~TaskQueue()
        {
            flushTaskQueue();
            m_shuttingDown = true;
            m_threadNotifier.notify_all();
            for ( auto& t :  m_workerThreads )
            {
                t.join();
            }
        }

        TaskQueue::TaskId TaskQueue::registerTask( Task* task )
        {
            m_tasks.emplace_back( std::unique_ptr<Task> ( task ) );
            m_dependencies.push_back( std::vector<TaskId>() );
            m_remainingDependencies.push_back( 0 );
            TimerData tdata;
            tdata.taskName = task->getName();
            m_timerData.push_back( tdata );

            CORE_ASSERT( m_tasks.size() == m_dependencies.size(), "Inconsistent task list" );
            CORE_ASSERT( m_tasks.size() == m_remainingDependencies.size(), "Inconsistent task list" );
            CORE_ASSERT( m_tasks.size() == m_timerData.size(), "Inconsistent task list" );
            return TaskId( m_tasks.size() - 1 );
        }

        void TaskQueue::addDependency( TaskQueue::TaskId predecessor, TaskQueue::TaskId successor )
        {
            CORE_ASSERT( ( predecessor != InvalidTaskId ) && ( predecessor < m_tasks.size() ), "Invalid predecessor task" );
            CORE_ASSERT( ( successor != InvalidTaskId ) && ( successor < m_tasks.size() ), "Invalid successor task" );
            CORE_ASSERT( predecessor != successor, "Cannot add self-dependency" );

            m_dependencies[predecessor].push_back( successor );
            ++m_remainingDependencies[successor];
        }

        bool TaskQueue::addDependency(const std::string &predecessors, TaskQueue::TaskId successor)
        {
            bool added = false;
            for (uint i = 0; i < m_tasks.size(); ++i)
            {
                if (m_tasks[i]->getName() == predecessors )
                {
                    added = true;
                    addDependency( i, successor);
                }
            }
            return added;
        }

        bool TaskQueue::addDependency(TaskQueue::TaskId predecessor, const std::string &successors)
        {
            bool added = false;
            for (uint i = 0; i < m_tasks.size(); ++i)
            {
                if (m_tasks[i]->getName() == successors )
                {
                    added = true;
                    addDependency( predecessor, i );
                }
            }
            return added;
        }

        void TaskQueue::addPendingDependency(const std::string &predecessors, TaskQueue::TaskId successor)
        {
            m_pendingDepsSucc.push_back(std::make_pair(predecessors, successor));
        }

        void TaskQueue::addPendingDependency( TaskId predecessor, const std::string& successors)
        {
            m_pendingDepsPre.push_back(std::make_pair(predecessor, successors));
        }

        void TaskQueue::resolveDependencies()
        {
           for ( const auto& pre : m_pendingDepsPre )
           {
               bool result = addDependency( pre.first, pre.second );
               CORE_ASSERT( result, "Pending dependency unresolved");
           }
           for ( const auto& pre : m_pendingDepsSucc )
           {
               bool result = addDependency( pre.first, pre.second );
               CORE_ASSERT( result, "Pending dependency unresolved");
           }
           m_pendingDepsPre.clear();
           m_pendingDepsSucc.clear();

        }

        void TaskQueue::queueTask( TaskQueue::TaskId task )
        {
            CORE_ASSERT( m_remainingDependencies[task] == 0, " Task has unsatisfied dependencies" );
            m_taskQueue.push_front( task );
        }

        void TaskQueue::detectCycles()
        {
#if defined (CORE_DEBUG)
            // Do a depth-first search of the nodes.
            std::vector<bool> visited( m_tasks.size(), false );
            std::stack<TaskId> pending;

            for (TaskId id = 0; id < m_tasks.size(); ++id)
            {
                if ( m_dependencies[id].size() == 0 )
                {
                    pending.push(id);
                }
            }

            // If you hit this assert, there are tasks in the list but
            // all tasks have dependencies so no task can start.
            CORE_ASSERT( m_tasks.empty() || !pending.empty(), "No free tasks.");

            while (! pending.empty())
            {
                TaskId id = pending.top();
                pending.pop();

                // The task has already been visited. It means there is a cycle in the task graph.
                CORE_ASSERT( !(visited[id]), "Cycle detected in tasks !");

                visited[id] = true;
                for ( const auto& dep : m_dependencies[id])
                {
                    pending.push( dep );
                }
            }
#endif
        }

        void TaskQueue::startTasks()
        {
            // Add pending dependencies.
            resolveDependencies();

            // Do a debug check
            detectCycles();

            // Enqueue all tasks with no dependencies.
            for ( uint t = 0; t < m_tasks.size(); ++t )
            {
                if ( m_remainingDependencies[t] == 0 )
                {
                    queueTask( t );
                }
            }

            // Wake up all threads.
            m_threadNotifier.notify_all();
        }

        void TaskQueue::waitForTasks()
        {
            bool isFinished = false;
            while ( !isFinished )
            {
                // TODO : use a notifier for task queue empty.
                m_taskQueueMutex.lock();
                isFinished = ( m_taskQueue.empty() && m_processingTasks == 0 );
                m_taskQueueMutex.unlock();
                if ( !isFinished )
                {
                    std::this_thread::yield();
                }
            }
        }

        const std::vector<TaskQueue::TimerData>& TaskQueue::getTimerData()
        {
            return m_timerData;
        }

        void TaskQueue::flushTaskQueue()
        {
            CORE_ASSERT( m_processingTasks == 0, "You have tasks still in process" );
            CORE_ASSERT( m_taskQueue.empty(), " You have unprocessed tasks " );
            m_tasks.clear();
            m_dependencies.clear();
            m_timerData.clear();
            m_remainingDependencies.clear();
        }

        void TaskQueue::runThread( uint id )
        {
            while ( true )
            {
                TaskId task = InvalidTaskId;

                // Acquire mutex.
                {
                    std::unique_lock<std::mutex> lock( m_taskQueueMutex );

                    // Wait for a new task
                    // TODO : use the second form of wait()
                    while ( !m_shuttingDown && m_taskQueue.empty() )
                    {
                        m_threadNotifier.wait( lock );
                    }
                    // If the task queue is shutting down we quit, releasing
                    // the lock.
                    if ( m_shuttingDown )
                    {
                        return;
                    }

                    // If we are here it means we got a task
                    task = m_taskQueue.back();
                    m_taskQueue.pop_back();
                    ++m_processingTasks;
                    CORE_ASSERT( task != InvalidTaskId && task < m_tasks.size(), "Invalid task" );
                }
                // Release mutex.

                // Run task
                m_timerData[task].start = Timer::Clock::now();
                m_tasks[task]->process();
                m_timerData[task].end = Timer::Clock::now();

                // Critical section : mark task as finished and en-queue dependencies.
                uint newTasks = 0;
                {
                    std::unique_lock<std::mutex> lock( m_taskQueueMutex );
                    for ( auto t : m_dependencies[task] )
                    {
                        uint& nDepends = m_remainingDependencies[t];
                        CORE_ASSERT( nDepends > 0, "Inconsistency in dependencies" );
                        --nDepends;
                        if ( nDepends == 0 )
                        {
                            queueTask( t );
                            ++newTasks;
                        }
                        // TODO :Easy optimization : grab one of the new task and process it immediately.
                    }
                    --m_processingTasks;
                }
                // If we added new tasks, we wake up one thread to execute it.
                if ( newTasks > 0 )
                {
                    m_threadNotifier.notify_one();
                }
            } // End of while(true)
        }
    }
}
<commit_msg>Debug check adding duplicate task dependency<commit_after>#include <Core/Tasks/TaskQueue.hpp>
#include <Core/Tasks/Task.hpp>

#include <stack>
#include <iostream>

namespace Ra
{
    namespace Core
    {

        TaskQueue::TaskQueue( uint numThreads )
            : m_processingTasks( 0 ), m_shuttingDown( false )
        {
            CORE_ASSERT( numThreads > 0, " You need at least one thread" );
            m_workerThreads.reserve( numThreads );
            for ( uint i = 0 ; i < numThreads; ++i )
            {
                m_workerThreads.emplace_back( std::thread( &TaskQueue::runThread, this, i ) );
            }
        }

        TaskQueue::~TaskQueue()
        {
            flushTaskQueue();
            m_shuttingDown = true;
            m_threadNotifier.notify_all();
            for ( auto& t :  m_workerThreads )
            {
                t.join();
            }
        }

        TaskQueue::TaskId TaskQueue::registerTask( Task* task )
        {
            m_tasks.emplace_back( std::unique_ptr<Task> ( task ) );
            m_dependencies.push_back( std::vector<TaskId>() );
            m_remainingDependencies.push_back( 0 );
            TimerData tdata;
            tdata.taskName = task->getName();
            m_timerData.push_back( tdata );

            CORE_ASSERT( m_tasks.size() == m_dependencies.size(), "Inconsistent task list" );
            CORE_ASSERT( m_tasks.size() == m_remainingDependencies.size(), "Inconsistent task list" );
            CORE_ASSERT( m_tasks.size() == m_timerData.size(), "Inconsistent task list" );
            return TaskId( m_tasks.size() - 1 );
        }

        void TaskQueue::addDependency( TaskQueue::TaskId predecessor, TaskQueue::TaskId successor )
        {
            CORE_ASSERT( ( predecessor != InvalidTaskId ) && ( predecessor < m_tasks.size() ), "Invalid predecessor task" );
            CORE_ASSERT( ( successor != InvalidTaskId )   && ( successor < m_tasks.size() ), "Invalid successor task" );
            CORE_ASSERT( predecessor != successor, "Cannot add self-dependency" );
            CORE_ASSERT( m_dependencies[predecessor].find(successor) == m_dependencies[predecessor].end(), "Cannot add a dependency twice" );

            m_dependencies[predecessor].push_back( successor );
            ++m_remainingDependencies[successor];
        }

        bool TaskQueue::addDependency(const std::string &predecessors, TaskQueue::TaskId successor)
        {
            bool added = false;
            for (uint i = 0; i < m_tasks.size(); ++i)
            {
                if (m_tasks[i]->getName() == predecessors )
                {
                    added = true;
                    addDependency( i, successor);
                }
            }
            return added;
        }

        bool TaskQueue::addDependency(TaskQueue::TaskId predecessor, const std::string &successors)
        {
            bool added = false;
            for (uint i = 0; i < m_tasks.size(); ++i)
            {
                if (m_tasks[i]->getName() == successors )
                {
                    added = true;
                    addDependency( predecessor, i );
                }
            }
            return added;
        }

        void TaskQueue::addPendingDependency(const std::string &predecessors, TaskQueue::TaskId successor)
        {
            m_pendingDepsSucc.push_back(std::make_pair(predecessors, successor));
        }

        void TaskQueue::addPendingDependency( TaskId predecessor, const std::string& successors)
        {
            m_pendingDepsPre.push_back(std::make_pair(predecessor, successors));
        }

        void TaskQueue::resolveDependencies()
        {
           for ( const auto& pre : m_pendingDepsPre )
           {
               bool result = addDependency( pre.first, pre.second );
               CORE_ASSERT( result, "Pending dependency unresolved");
           }
           for ( const auto& pre : m_pendingDepsSucc )
           {
               bool result = addDependency( pre.first, pre.second );
               CORE_ASSERT( result, "Pending dependency unresolved");
           }
           m_pendingDepsPre.clear();
           m_pendingDepsSucc.clear();

        }

        void TaskQueue::queueTask( TaskQueue::TaskId task )
        {
            CORE_ASSERT( m_remainingDependencies[task] == 0, " Task has unsatisfied dependencies" );
            m_taskQueue.push_front( task );
        }

        void TaskQueue::detectCycles()
        {
#if defined (CORE_DEBUG)
            // Do a depth-first search of the nodes.
            std::vector<bool> visited( m_tasks.size(), false );
            std::stack<TaskId> pending;

            for (TaskId id = 0; id < m_tasks.size(); ++id)
            {
                if ( m_dependencies[id].size() == 0 )
                {
                    pending.push(id);
                }
            }

            // If you hit this assert, there are tasks in the list but
            // all tasks have dependencies so no task can start.
            CORE_ASSERT( m_tasks.empty() || !pending.empty(), "No free tasks.");

            while (! pending.empty())
            {
                TaskId id = pending.top();
                pending.pop();

                // The task has already been visited. It means there is a cycle in the task graph.
                CORE_ASSERT( !(visited[id]), "Cycle detected in tasks !");

                visited[id] = true;
                for ( const auto& dep : m_dependencies[id])
                {
                    pending.push( dep );
                }
            }
#endif
        }

        void TaskQueue::startTasks()
        {
            // Add pending dependencies.
            resolveDependencies();

            // Do a debug check
            detectCycles();

            // Enqueue all tasks with no dependencies.
            for ( uint t = 0; t < m_tasks.size(); ++t )
            {
                if ( m_remainingDependencies[t] == 0 )
                {
                    queueTask( t );
                }
            }

            // Wake up all threads.
            m_threadNotifier.notify_all();
        }

        void TaskQueue::waitForTasks()
        {
            bool isFinished = false;
            while ( !isFinished )
            {
                // TODO : use a notifier for task queue empty.
                m_taskQueueMutex.lock();
                isFinished = ( m_taskQueue.empty() && m_processingTasks == 0 );
                m_taskQueueMutex.unlock();
                if ( !isFinished )
                {
                    std::this_thread::yield();
                }
            }
        }

        const std::vector<TaskQueue::TimerData>& TaskQueue::getTimerData()
        {
            return m_timerData;
        }

        void TaskQueue::flushTaskQueue()
        {
            CORE_ASSERT( m_processingTasks == 0, "You have tasks still in process" );
            CORE_ASSERT( m_taskQueue.empty(), " You have unprocessed tasks " );
            m_tasks.clear();
            m_dependencies.clear();
            m_timerData.clear();
            m_remainingDependencies.clear();
        }

        void TaskQueue::runThread( uint id )
        {
            while ( true )
            {
                TaskId task = InvalidTaskId;

                // Acquire mutex.
                {
                    std::unique_lock<std::mutex> lock( m_taskQueueMutex );

                    // Wait for a new task
                    // TODO : use the second form of wait()
                    while ( !m_shuttingDown && m_taskQueue.empty() )
                    {
                        m_threadNotifier.wait( lock );
                    }
                    // If the task queue is shutting down we quit, releasing
                    // the lock.
                    if ( m_shuttingDown )
                    {
                        return;
                    }

                    // If we are here it means we got a task
                    task = m_taskQueue.back();
                    m_taskQueue.pop_back();
                    ++m_processingTasks;
                    CORE_ASSERT( task != InvalidTaskId && task < m_tasks.size(), "Invalid task" );
                }
                // Release mutex.

                // Run task
                m_timerData[task].start = Timer::Clock::now();
                m_tasks[task]->process();
                m_timerData[task].end = Timer::Clock::now();

                // Critical section : mark task as finished and en-queue dependencies.
                uint newTasks = 0;
                {
                    std::unique_lock<std::mutex> lock( m_taskQueueMutex );
                    for ( auto t : m_dependencies[task] )
                    {
                        uint& nDepends = m_remainingDependencies[t];
                        CORE_ASSERT( nDepends > 0, "Inconsistency in dependencies" );
                        --nDepends;
                        if ( nDepends == 0 )
                        {
                            queueTask( t );
                            ++newTasks;
                        }
                        // TODO :Easy optimization : grab one of the new task and process it immediately.
                    }
                    --m_processingTasks;
                }
                // If we added new tasks, we wake up one thread to execute it.
                if ( newTasks > 0 )
                {
                    m_threadNotifier.notify_one();
                }
            } // End of while(true)
        }
    }
}
<|endoftext|>


<commit_before>#include <ctime>
#include <json/json.h>
#include <fstream>
#include <iostream>
#include <utils.h>
#include "CommandHandler.h"

static bool valid_resp(const std::string &resp, std::string &err);

CustomCommandHandler::CustomCommandHandler(commandMap *defaultCmds,
		TimerManager *tm, const std::string &wheelCmd,
		const std::string &name, const std::string &channel)
	: m_cmp(defaultCmds), m_tmp(tm), m_wheelCmd(wheelCmd),
	m_name(name), m_channel(channel)
{
	if (!(m_active = utils::readJSON("customcmds.json", m_commands))) {
		std::cerr << "Could not read customcmds.json.";
		return;
	}

	if (!m_commands.isMember("commands")
			|| !m_commands["commands"].isArray()) {
		m_active = false;
		std::cerr << "customcmds.json is improperly configured";
		return;
	}

	if (!cmdcheck())
		std::cerr << "\nCustom commands disabled." << std::endl;
}

CustomCommandHandler::~CustomCommandHandler() {}

bool CustomCommandHandler::isActive()
{
	return m_active;
}

/* addCom: add a new custom command cmd with response and cooldown */
bool CustomCommandHandler::addcom(const std::string &cmd,
		const std::string &response, const std::string &nick,
		time_t cooldown)
{
	time_t t;

	if (!validName(cmd)) {
		m_error = "invalid command name: $" + cmd;
		return false;
	}
	if (!valid_resp(response, m_error))
		return false;
	Json::Value command;
	command["active"] = true;
	command["cmd"] = cmd;
	command["response"] = response;
	command["cooldown"] = (Json::Int64)cooldown;
	command["ctime"] = (Json::Int64)(t = time(nullptr));
	command["mtime"] = (Json::Int64)t;
	command["creator"] = nick;
	command["uses"] = 0;

	m_commands["commands"].append(command);
	m_tmp->add(cmd, cooldown);
	write();
	return true;
}

/* delCom: delete command cmd if it exists */
bool CustomCommandHandler::delcom(const std::string &cmd)
{
	Json::ArrayIndex ind = 0;
	Json::Value def, rem;
	while (ind < m_commands["commands"].size()) {
		Json::Value val = m_commands["commands"].get(ind, def);
		if (val["cmd"] == cmd) break;
		++ind;
	}

	if (ind == m_commands["commands"].size())
		return false;

	m_commands["commands"].removeIndex(ind, &rem);
	m_tmp->remove(cmd);
	write();
	return true;
}

/* editCom: modify the command cmd with newResp and newcd */
bool CustomCommandHandler::editcom(const std::string &cmd,
		const std::string &newResp, time_t newcd)
{
	auto *com = getcom(cmd);
	if (com->empty()) {
		m_error = "not a command: $" + cmd;
		return false;
	}
	if (!valid_resp(newResp, m_error))
		return false;
	if (!newResp.empty())
		(*com)["response"] = newResp;
	if (newcd != -1) {
		(*com)["cooldown"] = (Json::Int64)newcd;
		m_tmp->remove(cmd);
		m_tmp->add(cmd, newcd);
	}
	(*com)["mtime"] = (Json::Int64)time(nullptr);
	write();
	return true;
}

/* activate: activate the command cmd */
bool CustomCommandHandler::activate(const std::string &cmd)
{
	Json::Value *com;

	if ((com = getcom(cmd))->empty()) {
		m_error = "not a command: $" + cmd;
		return false;
	}
	if (!valid_resp((*com)["response"].asString(), m_error))
		return false;
	(*com)["active"] = true;
	write();
	return true;
}

/* deactivate: deactivate the command cmd */
bool CustomCommandHandler::deactivate(const std::string &cmd)
{
	Json::Value *com;

	if ((com = getcom(cmd))->empty()) {
		m_error = "not a command: $" + cmd;
		return false;
	}
	(*com)["active"] = false;
	write();
	return true;
}

/* rename: rename custom command cmd to newcmd */
bool CustomCommandHandler::rename(const std::string &cmd,
		const std::string &newcmd)
{
	Json::Value *com;

	if ((com = getcom(cmd))->empty()) {
		m_error = "not a command: $" + cmd;
		return false;
	}
	(*com)["cmd"] = newcmd;
	(*com)["mtime"] = (Json::Int64)time(nullptr);
	m_tmp->remove(cmd);
	m_tmp->add(newcmd, (*com)["cooldown"].asInt64());
	write();
	return true;
}

/* getcom: return command value if it exists, empty value otherwise */
Json::Value *CustomCommandHandler::getcom(const std::string &cmd)
{
	for (auto &val : m_commands["commands"]) {
		if (val["cmd"] == cmd)
			return &val;
	}

	/* returns an empty value if the command is not found */
	return &m_emptyVal;
}

const Json::Value *CustomCommandHandler::commands()
{
	return &m_commands;
}

/* write: write all commands to file */
void CustomCommandHandler::write()
{
	utils::writeJSON("customcmds.json", m_commands);
}

/* validName: check if cmd is a valid command name */
bool CustomCommandHandler::validName(const std::string &cmd, bool loading)
{
	/* if CCH is loading commands from file (in constructor) */
	/* it doesn't need to check against its stored commands */
	return m_cmp->find(cmd) == m_cmp->end() && cmd != m_wheelCmd
		&& cmd.length() < 20 && (loading ? true : getcom(cmd)->empty());
}

std::string CustomCommandHandler::error() const
{
	return m_error;
}

/* format: format a response for cmd */
std::string CustomCommandHandler::format(const Json::Value *cmd,
		const std::string &nick) const
{
	size_t ind;
	std::string out, ins;
	char c;

	ind = 0;
	out = (*cmd)["response"].asString();
	while ((ind = out.find('%', ind)) != std::string::npos) {
		c = out[ind + 1];
		out.erase(ind, 2);
		switch (c) {
		case '%':
			ins = "%";
			break;
		case 'N':
			ins = "@" + nick + ",";
			break;
		case 'b':
			ins = m_name;
			break;
		case 'c':
			ins = m_channel;
			break;
		case 'n':
			ins = nick;
			break;
		case 'u':
			ins = utils::formatInteger((*cmd)["uses"].asString());
			break;
		default:
			break;
		}
		out.insert(ind, ins);
		ind += ins.length();
	}
	return out;
}

/* cmdcheck: check the validity of a command and add missing fields */
bool CustomCommandHandler::cmdcheck()
{
	time_t t;
	bool added;

	t = time(nullptr);
	added = false;
	for (Json::Value &val : m_commands["commands"]) {
		/* add new values to old commands */
		if (!val.isMember("ctime")) {
			val["ctime"] = (Json::Int64)t;
			added = true;
		}
		if (!val.isMember("mtime")) {
			val["mtime"] = (Json::Int64)t;
			added = true;
		}
		if (!val.isMember("creator")) {
			val["creator"] = "unknown";
			added = true;
		}
		if (!val.isMember("uses")) {
			val["uses"] = 0;
			added = true;
		}
		if (!val.isMember("active")) {
			val["active"] = true;
			added = true;
		}
		if (!(val.isMember("cmd") && val.isMember("response")
					&& val.isMember("cooldown"))) {
			m_active = false;
			std::cerr << "customcmds.json is improperly configured";
			return false;
		}
		if (!validName(val["cmd"].asString(), true)) {
			m_active = false;
			std::cerr << val["cmd"].asString()
				<< " is an invalid command name - change "
				"or remove it";
			return false;
		}
		if (val["cooldown"].asInt() < 0) {
			m_active = false;
			std::cerr << "command \"" << val["cmd"].asString()
				<< "\" has a negative cooldown - change "
				"or remove it";
			return false;
		}
		/* check validity of response */
		if (!valid_resp(val["response"].asString(), m_error)) {
			std::cerr << "Custom command " << val["cmd"].asString()
				<< ": " << m_error << std::endl;
			val["active"] = false;
			added = true;
		}
		if (added)
			write();
		m_tmp->add(val["cmd"].asString(), val["cooldown"].asInt64());
	}
	return true;
}

/* valid_resp: check if a response has valid format characters */
static bool valid_resp(const std::string &resp, std::string &err)
{
	static const std::string fmt_c = "%Nbcnu";
	size_t ind;
	int c;

	ind = -1;
	while ((ind = resp.find('%', ind + 1)) != std::string::npos) {
		if (ind == resp.length() - 1) {
			err = "unexpected end of line after '%' in response";
			return false;
		}
		c = resp[ind + 1];
		if (fmt_c.find(c) == std::string::npos) {
			err = "invalid format sequence '%";
			err += (char)c;
			err += "' in response";
			return false;
		}
		if (c == '%')
			++ind;
	}
	return true;
}
<commit_msg>Check valid name on editcom rename<commit_after>#include <ctime>
#include <json/json.h>
#include <fstream>
#include <iostream>
#include <utils.h>
#include "CommandHandler.h"

static bool valid_resp(const std::string &resp, std::string &err);

CustomCommandHandler::CustomCommandHandler(commandMap *defaultCmds,
		TimerManager *tm, const std::string &wheelCmd,
		const std::string &name, const std::string &channel)
	: m_cmp(defaultCmds), m_tmp(tm), m_wheelCmd(wheelCmd),
	m_name(name), m_channel(channel)
{
	if (!(m_active = utils::readJSON("customcmds.json", m_commands))) {
		std::cerr << "Could not read customcmds.json.";
		return;
	}

	if (!m_commands.isMember("commands")
			|| !m_commands["commands"].isArray()) {
		m_active = false;
		std::cerr << "customcmds.json is improperly configured";
		return;
	}

	if (!cmdcheck())
		std::cerr << "\nCustom commands disabled." << std::endl;
}

CustomCommandHandler::~CustomCommandHandler() {}

bool CustomCommandHandler::isActive()
{
	return m_active;
}

/* addCom: add a new custom command cmd with response and cooldown */
bool CustomCommandHandler::addcom(const std::string &cmd,
		const std::string &response, const std::string &nick,
		time_t cooldown)
{
	time_t t;

	if (!validName(cmd)) {
		m_error = "invalid command name: $" + cmd;
		return false;
	}
	if (!valid_resp(response, m_error))
		return false;
	Json::Value command;
	command["active"] = true;
	command["cmd"] = cmd;
	command["response"] = response;
	command["cooldown"] = (Json::Int64)cooldown;
	command["ctime"] = (Json::Int64)(t = time(nullptr));
	command["mtime"] = (Json::Int64)t;
	command["creator"] = nick;
	command["uses"] = 0;

	m_commands["commands"].append(command);
	m_tmp->add(cmd, cooldown);
	write();
	return true;
}

/* delCom: delete command cmd if it exists */
bool CustomCommandHandler::delcom(const std::string &cmd)
{
	Json::ArrayIndex ind = 0;
	Json::Value def, rem;
	while (ind < m_commands["commands"].size()) {
		Json::Value val = m_commands["commands"].get(ind, def);
		if (val["cmd"] == cmd) break;
		++ind;
	}

	if (ind == m_commands["commands"].size())
		return false;

	m_commands["commands"].removeIndex(ind, &rem);
	m_tmp->remove(cmd);
	write();
	return true;
}

/* editCom: modify the command cmd with newResp and newcd */
bool CustomCommandHandler::editcom(const std::string &cmd,
		const std::string &newResp, time_t newcd)
{
	auto *com = getcom(cmd);
	if (com->empty()) {
		m_error = "not a command: $" + cmd;
		return false;
	}
	if (!valid_resp(newResp, m_error))
		return false;
	if (!newResp.empty())
		(*com)["response"] = newResp;
	if (newcd != -1) {
		(*com)["cooldown"] = (Json::Int64)newcd;
		m_tmp->remove(cmd);
		m_tmp->add(cmd, newcd);
	}
	(*com)["mtime"] = (Json::Int64)time(nullptr);
	write();
	return true;
}

/* activate: activate the command cmd */
bool CustomCommandHandler::activate(const std::string &cmd)
{
	Json::Value *com;

	if ((com = getcom(cmd))->empty()) {
		m_error = "not a command: $" + cmd;
		return false;
	}
	if (!valid_resp((*com)["response"].asString(), m_error))
		return false;
	(*com)["active"] = true;
	write();
	return true;
}

/* deactivate: deactivate the command cmd */
bool CustomCommandHandler::deactivate(const std::string &cmd)
{
	Json::Value *com;

	if ((com = getcom(cmd))->empty()) {
		m_error = "not a command: $" + cmd;
		return false;
	}
	(*com)["active"] = false;
	write();
	return true;
}

/* rename: rename custom command cmd to newcmd */
bool CustomCommandHandler::rename(const std::string &cmd,
		const std::string &newcmd)
{
	Json::Value *com;

	if ((com = getcom(cmd))->empty()) {
		m_error = "not a command: $" + cmd;
		return false;
	}
	if (!validName(newcmd)) {
		m_error = "invalid command name: $" + newcmd;
		return false;
	}
	(*com)["cmd"] = newcmd;
	(*com)["mtime"] = (Json::Int64)time(nullptr);
	m_tmp->remove(cmd);
	m_tmp->add(newcmd, (*com)["cooldown"].asInt64());
	write();
	return true;
}

/* getcom: return command value if it exists, empty value otherwise */
Json::Value *CustomCommandHandler::getcom(const std::string &cmd)
{
	for (auto &val : m_commands["commands"]) {
		if (val["cmd"] == cmd)
			return &val;
	}

	/* returns an empty value if the command is not found */
	return &m_emptyVal;
}

const Json::Value *CustomCommandHandler::commands()
{
	return &m_commands;
}

/* write: write all commands to file */
void CustomCommandHandler::write()
{
	utils::writeJSON("customcmds.json", m_commands);
}

/* validName: check if cmd is a valid command name */
bool CustomCommandHandler::validName(const std::string &cmd, bool loading)
{
	/* if CCH is loading commands from file (in constructor) */
	/* it doesn't need to check against its stored commands */
	return m_cmp->find(cmd) == m_cmp->end() && cmd != m_wheelCmd
		&& cmd.length() < 20 && (loading ? true : getcom(cmd)->empty());
}

std::string CustomCommandHandler::error() const
{
	return m_error;
}

/* format: format a response for cmd */
std::string CustomCommandHandler::format(const Json::Value *cmd,
		const std::string &nick) const
{
	size_t ind;
	std::string out, ins;
	char c;

	ind = 0;
	out = (*cmd)["response"].asString();
	while ((ind = out.find('%', ind)) != std::string::npos) {
		c = out[ind + 1];
		out.erase(ind, 2);
		switch (c) {
		case '%':
			ins = "%";
			break;
		case 'N':
			ins = "@" + nick + ",";
			break;
		case 'b':
			ins = m_name;
			break;
		case 'c':
			ins = m_channel;
			break;
		case 'n':
			ins = nick;
			break;
		case 'u':
			ins = utils::formatInteger((*cmd)["uses"].asString());
			break;
		default:
			break;
		}
		out.insert(ind, ins);
		ind += ins.length();
	}
	return out;
}

/* cmdcheck: check the validity of a command and add missing fields */
bool CustomCommandHandler::cmdcheck()
{
	time_t t;
	bool added;

	t = time(nullptr);
	added = false;
	for (Json::Value &val : m_commands["commands"]) {
		/* add new values to old commands */
		if (!val.isMember("ctime")) {
			val["ctime"] = (Json::Int64)t;
			added = true;
		}
		if (!val.isMember("mtime")) {
			val["mtime"] = (Json::Int64)t;
			added = true;
		}
		if (!val.isMember("creator")) {
			val["creator"] = "unknown";
			added = true;
		}
		if (!val.isMember("uses")) {
			val["uses"] = 0;
			added = true;
		}
		if (!val.isMember("active")) {
			val["active"] = true;
			added = true;
		}
		if (!(val.isMember("cmd") && val.isMember("response")
					&& val.isMember("cooldown"))) {
			m_active = false;
			std::cerr << "customcmds.json is improperly configured";
			return false;
		}
		if (!validName(val["cmd"].asString(), true)) {
			m_active = false;
			std::cerr << val["cmd"].asString()
				<< " is an invalid command name - change "
				"or remove it";
			return false;
		}
		if (val["cooldown"].asInt() < 0) {
			m_active = false;
			std::cerr << "command \"" << val["cmd"].asString()
				<< "\" has a negative cooldown - change "
				"or remove it";
			return false;
		}
		/* check validity of response */
		if (!valid_resp(val["response"].asString(), m_error)) {
			std::cerr << "Custom command " << val["cmd"].asString()
				<< ": " << m_error << std::endl;
			val["active"] = false;
			added = true;
		}
		if (added)
			write();
		m_tmp->add(val["cmd"].asString(), val["cooldown"].asInt64());
	}
	return true;
}

/* valid_resp: check if a response has valid format characters */
static bool valid_resp(const std::string &resp, std::string &err)
{
	static const std::string fmt_c = "%Nbcnu";
	size_t ind;
	int c;

	ind = -1;
	while ((ind = resp.find('%', ind + 1)) != std::string::npos) {
		if (ind == resp.length() - 1) {
			err = "unexpected end of line after '%' in response";
			return false;
		}
		c = resp[ind + 1];
		if (fmt_c.find(c) == std::string::npos) {
			err = "invalid format sequence '%";
			err += (char)c;
			err += "' in response";
			return false;
		}
		if (c == '%')
			++ind;
	}
	return true;
}
<|endoftext|>


<commit_before>#ifndef TATUM_COMMON_ANALYSIS_VISITOR_HPP
#define TATUM_COMMON_ANALYSIS_VISITOR_HPP
#include "tatum_error.hpp"
#include "TimingGraph.hpp"
#include "TimingConstraints.hpp"
#include "TimingTags.hpp"

namespace tatum { namespace detail {

/** \file
 *
 * Common analysis functionality for both setup and hold analysis.
 */

/** \class CommonAnalysisVisitor
 *
 * A class satisfying the GraphVisitor concept, which contains common
 * node and edge processing code used by both setup and hold analysis.
 *
 * \see GraphVisitor
 *
 * \tparam AnalysisOps a class defining the setup/hold specific operations
 * \see SetupAnalysisOps
 * \see HoldAnalysisOps
 */
template<class AnalysisOps>
class CommonAnalysisVisitor {
    public:
        CommonAnalysisVisitor(size_t num_tags)
            : ops_(num_tags) { }

        void do_arrival_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id);
        void do_required_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id);

        template<class DelayCalc>
        void do_arrival_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalc& dc, const NodeId node_id);

        template<class DelayCalc>
        void do_required_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalc& dc, const NodeId node_id);

        void reset() { ops_.reset(); }

    protected:
        AnalysisOps ops_;

    private:
        template<class DelayCalc>
        void do_arrival_traverse_edge(const TimingGraph& tg, const DelayCalc& dc, const NodeId node_id, const EdgeId edge_id);

        template<class DelayCalc>
        void do_required_traverse_edge(const TimingGraph& tg, const DelayCalc& dc, const NodeId node_id, const EdgeId edge_id);

};

/*
 * Pre-traversal
 */

template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_arrival_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id) {
    //Logical Input
    TATUM_ASSERT_MSG(tg.node_in_edges(node_id).size() == 0, "Logical input has input edges: timing graph not levelized.");

    NodeType node_type = tg.node_type(node_id);

    //We don't propagate any tags from constant generators,
    //since they do not effect the dynamic timing behaviour of the
    //system
    if(tc.node_is_constant_generator(node_id)) return;

    if(tc.node_is_clock_source(node_id)) {
        //Generate the appropriate clock tag

        //Note that we assume that edge counting has set the effective period constraint assuming a
        //launch edge at time zero.  This means we don't need to do anything special for clocks
        //with rising edges after time zero.
        TATUM_ASSERT_MSG(ops_.get_clock_tags(node_id).num_tags() == 0, "Clock source already has clock tags");

        //Find it's domain
        DomainId domain_id = tc.node_clock_domain(node_id);
        TATUM_ASSERT(domain_id);

        //Initialize a clock tag with zero arrival, invalid required time
        TimingTag clock_tag = TimingTag(Time(0.), Time(NAN), domain_id, node_id);

        //Add the tag
        ops_.get_clock_tags(node_id).add_tag(clock_tag);

    } else {
        TATUM_ASSERT(node_type == NodeType::SOURCE);

        //A standard primary input, generate the appropriate data tag

        //We assume input delays are on the arc from INPAD_SOURCE to INPAD_OPIN,
        //so we do not need to account for it directly in the arrival time of INPAD_SOURCES

        TATUM_ASSERT_MSG(ops_.get_data_tags(node_id).num_tags() == 0, "Primary input already has data tags");

        DomainId domain_id = tc.node_clock_domain(node_id);
        TATUM_ASSERT(domain_id);

        //Initialize a data tag with zero arrival, invalid required time
        TimingTag input_tag = TimingTag(Time(0.), Time(NAN), domain_id, node_id);

        ops_.get_data_tags(node_id).add_tag(input_tag);
    }
}

template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_required_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id) {

    TimingTags& node_data_tags = ops_.get_data_tags(node_id);
    TimingTags& node_clock_tags = ops_.get_clock_tags(node_id);

    /*
     * Calculate required times
     */
    auto node_type = tg.node_type(node_id);
    TATUM_ASSERT(node_type == NodeType::SINK);

    //Sinks corresponding to FF sinks will have propagated clock tags,
    //while those corresponding to outpads will not.
    if(node_clock_tags.empty()) {
        //Initialize the outpad's clock tags based on the specified constraints.

        auto output_constraints = tc.output_constraints(node_id);

        if(output_constraints.empty()) {
            //throw tatum::Error("Output unconstrained");
            std::cerr << "Warning: Timing graph " << node_id << " " << node_type << " has no incomming clock tags, and no output constraint. No required time will be calculated\n";

#if 1
            //Debug trace-back

            //TODO: remove debug code!
            if(node_type == NodeType::FF_SINK) {
                std::cerr << "\tClock path:\n";
                int i = 0;
                NodeId curr_node = node_id;
                while(tg.node_type(curr_node) != NodeType::INPAD_SOURCE &&
                      tg.node_type(curr_node) != NodeType::CLOCK_SOURCE &&
                      tg.node_type(curr_node) != NodeType::CONSTANT_GEN_SOURCE &&
                      i < 100) {

                    //Look throught the fanin for a clock or other node to follow
                    //
                    //Typically the first hop from node_id will be to either the IPIN or CLOCK pin
                    //the following preferentially prefers the CLOCK pin to show the clock path
                    EdgeId best_edge;
                    for(auto edge_id : tg.node_in_edges(curr_node)) {
                        if(!best_edge) {
                            best_edge = edge_id;
                        }

                        NodeId src_node = tg.edge_src_node(edge_id);
                        auto src_node_type = tg.node_type(src_node);
                        if(src_node_type == NodeType::FF_CLOCK) {
                            best_edge = edge_id;
                        }
                    }

                    //Step back
                    curr_node = tg.edge_src_node(best_edge);
                    auto curr_node_type = tg.node_type(curr_node);

                    std::cerr << "\tNode " << curr_node << " Type: " << curr_node_type << "\n";
                    if(++i >= 100) {
                        std::cerr << "\tStopping backtrace\n";

                    }
                }
            }
#endif

        } else {
            for(auto constraint : output_constraints) {
                //TODO: use real constraint value when output delay no-longer on edges
                TimingTag constraint_tag = TimingTag(Time(0.), Time(NAN), constraint.second.domain, node_id);
                node_clock_tags.add_tag(constraint_tag);
            }
        }
    }

    //At this stage both FF and outpad sinks now have the relevant clock
    //tags and we can process them equivalently

    //Determine the required time at this sink
    //
    //We need to generate a required time for each clock domain for which there is a data
    //arrival time at this node, while considering all possible clocks that could drive
    //this node (i.e. take the most restrictive constraint accross all clock tags at this
    //node)
    for(TimingTag& node_data_tag : node_data_tags) {
        for(const TimingTag& node_clock_tag : node_clock_tags) {

            //Should we be analyzing paths between these two domains?
            if(tc.should_analyze(node_data_tag.clock_domain(), node_clock_tag.clock_domain())) {

                //We only set a required time if the source domain actually reaches this sink
                //domain.  This is indicated by having a valid arrival time.
                if(node_data_tag.arr_time().valid()) {
                    float clock_constraint = ops_.clock_constraint(tc, node_data_tag.clock_domain(),
                                                                  node_clock_tag.clock_domain());

                    //Update the required time. This will keep the most restrictive constraint.
                    ops_.merge_req_tag(node_data_tag, node_clock_tag.arr_time() + Time(clock_constraint), node_data_tag);
                }
            }
        }
    }
}

/*
 * Arrival Time Operations
 */

template<class AnalysisOps>
template<class DelayCalc>
void CommonAnalysisVisitor<AnalysisOps>::do_arrival_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalc& dc, NodeId node_id) {
    //Do not propagate arrival tags through constant generators
    if(tc.node_is_constant_generator(node_id)) return;

    //Pull from upstream sources to current node
    for(EdgeId edge_id : tg.node_in_edges(node_id)) {
        do_arrival_traverse_edge(tg, dc, node_id, edge_id);
    }
}

template<class AnalysisOps>
template<class DelayCalc>
void CommonAnalysisVisitor<AnalysisOps>::do_arrival_traverse_edge(const TimingGraph& tg, const DelayCalc& dc, const NodeId node_id, const EdgeId edge_id) {
    //We must use the tags by reference so we don't accidentally wipe-out any
    //existing tags
    TimingTags& node_data_tags = ops_.get_data_tags(node_id);
    TimingTags& node_clock_tags = ops_.get_clock_tags(node_id);

    //Pulling values from upstream source node
    NodeId src_node_id = tg.edge_src_node(edge_id);

    const Time& edge_delay = ops_.edge_delay(dc, tg, edge_id);

    const TimingTags& src_clk_tags = ops_.get_clock_tags(src_node_id);
    const TimingTags& src_data_tags = ops_.get_data_tags(src_node_id);

    /*
     * Clock tags
     */

    if(src_data_tags.empty()) {
        //Propagate the clock tags through the clock network

        for(const TimingTag& src_clk_tag : src_clk_tags) {
            //Standard propagation through the clock network
            ops_.merge_arr_tags(node_clock_tags, src_clk_tag.arr_time() + edge_delay, src_clk_tag);

            if(tg.node_type(node_id) == NodeType::FF_SOURCE) { //FIXME: Should be any source type
                //We are traversing a clock to data launch edge.
                //
                //We convert the clock arrival time to a data
                //arrival time at this node (since the clock
                //arrival launches the data).

                //Make a copy of the tag
                TimingTag launch_tag = src_clk_tag;

                //Update the launch node, since the data is
                //launching from this node
                launch_tag.set_launch_node(node_id);

                //Mark propagated launch time as a DATA tag
                ops_.merge_arr_tags(node_data_tags, launch_tag.arr_time() + edge_delay, launch_tag);
            }
        }
    }

    /*
     * Data tags
     */

    for(const TimingTag& src_data_tag : src_data_tags) {
        //Standard data-path propagation
        ops_.merge_arr_tags(node_data_tags, src_data_tag.arr_time() + edge_delay, src_data_tag);
    }
}

/*
 * Required Time Operations
 */

template<class AnalysisOps>
template<class DelayCalc>
void CommonAnalysisVisitor<AnalysisOps>::do_required_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalc& dc, const NodeId node_id) {
    //Do not propagate required tags through constant generators
    if(tc.node_is_constant_generator(node_id)) return;

    //Pull from downstream sinks to current node
    for(EdgeId edge_id : tg.node_out_edges(node_id)) {
        do_required_traverse_edge(tg, dc, node_id, edge_id);
    }
}

template<class AnalysisOps>
template<class DelayCalc>
void CommonAnalysisVisitor<AnalysisOps>::do_required_traverse_edge(const TimingGraph& tg, const DelayCalc& dc, const NodeId node_id, const EdgeId edge_id) {
    //We must use the tags by reference so we don't accidentally wipe-out any
    //existing tags
    TimingTags& node_data_tags = ops_.get_data_tags(node_id);

    //Pulling values from downstream sink node
    NodeId sink_node_id = tg.edge_sink_node(edge_id);

    const Time& edge_delay = ops_.edge_delay(dc, tg, edge_id);

    const TimingTags& sink_data_tags = ops_.get_data_tags(sink_node_id);

    for(const TimingTag& sink_tag : sink_data_tags) {
        //We only propogate the required time if we have a valid arrival time
        auto matched_tag_iter = node_data_tags.find_tag_by_clock_domain(sink_tag.clock_domain());
        if(matched_tag_iter != node_data_tags.end() && matched_tag_iter->arr_time().valid()) {
            //Valid arrival, update required
            ops_.merge_req_tag(*matched_tag_iter, sink_tag.req_time() - edge_delay, sink_tag);
        }
    }
}

}} //namepsace

#endif
<commit_msg>Factor out clock to data edge detection into function<commit_after>#ifndef TATUM_COMMON_ANALYSIS_VISITOR_HPP
#define TATUM_COMMON_ANALYSIS_VISITOR_HPP
#include "tatum_error.hpp"
#include "TimingGraph.hpp"
#include "TimingConstraints.hpp"
#include "TimingTags.hpp"

namespace tatum { namespace detail {

/** \file
 *
 * Common analysis functionality for both setup and hold analysis.
 */

/** \class CommonAnalysisVisitor
 *
 * A class satisfying the GraphVisitor concept, which contains common
 * node and edge processing code used by both setup and hold analysis.
 *
 * \see GraphVisitor
 *
 * \tparam AnalysisOps a class defining the setup/hold specific operations
 * \see SetupAnalysisOps
 * \see HoldAnalysisOps
 */
template<class AnalysisOps>
class CommonAnalysisVisitor {
    public:
        CommonAnalysisVisitor(size_t num_tags)
            : ops_(num_tags) { }

        void do_arrival_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id);
        void do_required_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id);

        template<class DelayCalc>
        void do_arrival_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalc& dc, const NodeId node_id);

        template<class DelayCalc>
        void do_required_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalc& dc, const NodeId node_id);

        void reset() { ops_.reset(); }

    protected:
        AnalysisOps ops_;

    private:
        template<class DelayCalc>
        void do_arrival_traverse_edge(const TimingGraph& tg, const DelayCalc& dc, const NodeId node_id, const EdgeId edge_id);

        template<class DelayCalc>
        void do_required_traverse_edge(const TimingGraph& tg, const DelayCalc& dc, const NodeId node_id, const EdgeId edge_id);

        bool is_clock_to_data_edge(const TimingGraph& tg, const NodeId src_node_id, const NodeId node_id) const;

};

/*
 * Pre-traversal
 */

template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_arrival_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id) {
    //Logical Input
    TATUM_ASSERT_MSG(tg.node_in_edges(node_id).size() == 0, "Logical input has input edges: timing graph not levelized.");

    NodeType node_type = tg.node_type(node_id);

    //We don't propagate any tags from constant generators,
    //since they do not effect the dynamic timing behaviour of the
    //system
    if(tc.node_is_constant_generator(node_id)) return;

    if(tc.node_is_clock_source(node_id)) {
        //Generate the appropriate clock tag

        //Note that we assume that edge counting has set the effective period constraint assuming a
        //launch edge at time zero.  This means we don't need to do anything special for clocks
        //with rising edges after time zero.
        TATUM_ASSERT_MSG(ops_.get_clock_tags(node_id).num_tags() == 0, "Clock source already has clock tags");

        //Find it's domain
        DomainId domain_id = tc.node_clock_domain(node_id);
        TATUM_ASSERT(domain_id);

        //Initialize a clock tag with zero arrival, invalid required time
        TimingTag clock_tag = TimingTag(Time(0.), Time(NAN), domain_id, node_id);

        //Add the tag
        ops_.get_clock_tags(node_id).add_tag(clock_tag);

    } else {
        TATUM_ASSERT(node_type == NodeType::SOURCE);

        //A standard primary input, generate the appropriate data tag

        //We assume input delays are on the arc from INPAD_SOURCE to INPAD_OPIN,
        //so we do not need to account for it directly in the arrival time of INPAD_SOURCES

        TATUM_ASSERT_MSG(ops_.get_data_tags(node_id).num_tags() == 0, "Primary input already has data tags");

        DomainId domain_id = tc.node_clock_domain(node_id);
        TATUM_ASSERT(domain_id);

        //Initialize a data tag with zero arrival, invalid required time
        TimingTag input_tag = TimingTag(Time(0.), Time(NAN), domain_id, node_id);

        ops_.get_data_tags(node_id).add_tag(input_tag);
    }
}

template<class AnalysisOps>
void CommonAnalysisVisitor<AnalysisOps>::do_required_pre_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const NodeId node_id) {

    TimingTags& node_data_tags = ops_.get_data_tags(node_id);
    TimingTags& node_clock_tags = ops_.get_clock_tags(node_id);

    /*
     * Calculate required times
     */
    auto node_type = tg.node_type(node_id);
    TATUM_ASSERT(node_type == NodeType::SINK);

    //Sinks corresponding to FF sinks will have propagated clock tags,
    //while those corresponding to outpads will not.
    if(node_clock_tags.empty()) {
        //Initialize the outpad's clock tags based on the specified constraints.

        auto output_constraints = tc.output_constraints(node_id);

        if(output_constraints.empty()) {
            //throw tatum::Error("Output unconstrained");
            std::cerr << "Warning: Timing graph " << node_id << " " << node_type << " has no incomming clock tags, and no output constraint. No required time will be calculated\n";

#if 1
            //Debug trace-back

            //TODO: remove debug code!
            if(node_type == NodeType::FF_SINK) {
                std::cerr << "\tClock path:\n";
                int i = 0;
                NodeId curr_node = node_id;
                while(tg.node_type(curr_node) != NodeType::INPAD_SOURCE &&
                      tg.node_type(curr_node) != NodeType::CLOCK_SOURCE &&
                      tg.node_type(curr_node) != NodeType::CONSTANT_GEN_SOURCE &&
                      i < 100) {

                    //Look throught the fanin for a clock or other node to follow
                    //
                    //Typically the first hop from node_id will be to either the IPIN or CLOCK pin
                    //the following preferentially prefers the CLOCK pin to show the clock path
                    EdgeId best_edge;
                    for(auto edge_id : tg.node_in_edges(curr_node)) {
                        if(!best_edge) {
                            best_edge = edge_id;
                        }

                        NodeId src_node = tg.edge_src_node(edge_id);
                        auto src_node_type = tg.node_type(src_node);
                        if(src_node_type == NodeType::FF_CLOCK) {
                            best_edge = edge_id;
                        }
                    }

                    //Step back
                    curr_node = tg.edge_src_node(best_edge);
                    auto curr_node_type = tg.node_type(curr_node);

                    std::cerr << "\tNode " << curr_node << " Type: " << curr_node_type << "\n";
                    if(++i >= 100) {
                        std::cerr << "\tStopping backtrace\n";

                    }
                }
            }
#endif

        } else {
            for(auto constraint : output_constraints) {
                //TODO: use real constraint value when output delay no-longer on edges
                TimingTag constraint_tag = TimingTag(Time(0.), Time(NAN), constraint.second.domain, node_id);
                node_clock_tags.add_tag(constraint_tag);
            }
        }
    }

    //At this stage both FF and outpad sinks now have the relevant clock
    //tags and we can process them equivalently

    //Determine the required time at this sink
    //
    //We need to generate a required time for each clock domain for which there is a data
    //arrival time at this node, while considering all possible clocks that could drive
    //this node (i.e. take the most restrictive constraint accross all clock tags at this
    //node)
    for(TimingTag& node_data_tag : node_data_tags) {
        for(const TimingTag& node_clock_tag : node_clock_tags) {

            //Should we be analyzing paths between these two domains?
            if(tc.should_analyze(node_data_tag.clock_domain(), node_clock_tag.clock_domain())) {

                //We only set a required time if the source domain actually reaches this sink
                //domain.  This is indicated by having a valid arrival time.
                if(node_data_tag.arr_time().valid()) {
                    float clock_constraint = ops_.clock_constraint(tc, node_data_tag.clock_domain(),
                                                                  node_clock_tag.clock_domain());

                    //Update the required time. This will keep the most restrictive constraint.
                    ops_.merge_req_tag(node_data_tag, node_clock_tag.arr_time() + Time(clock_constraint), node_data_tag);
                }
            }
        }
    }
}

/*
 * Arrival Time Operations
 */

template<class AnalysisOps>
template<class DelayCalc>
void CommonAnalysisVisitor<AnalysisOps>::do_arrival_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalc& dc, NodeId node_id) {
    //Do not propagate arrival tags through constant generators
    if(tc.node_is_constant_generator(node_id)) return;

    //Pull from upstream sources to current node
    for(EdgeId edge_id : tg.node_in_edges(node_id)) {
        do_arrival_traverse_edge(tg, dc, node_id, edge_id);
    }
}

template<class AnalysisOps>
template<class DelayCalc>
void CommonAnalysisVisitor<AnalysisOps>::do_arrival_traverse_edge(const TimingGraph& tg, const DelayCalc& dc, const NodeId node_id, const EdgeId edge_id) {
    //We must use the tags by reference so we don't accidentally wipe-out any
    //existing tags
    TimingTags& node_data_tags = ops_.get_data_tags(node_id);
    TimingTags& node_clock_tags = ops_.get_clock_tags(node_id);

    //Pulling values from upstream source node
    NodeId src_node_id = tg.edge_src_node(edge_id);

    const Time& edge_delay = ops_.edge_delay(dc, tg, edge_id);

    const TimingTags& src_clk_tags = ops_.get_clock_tags(src_node_id);
    const TimingTags& src_data_tags = ops_.get_data_tags(src_node_id);

    /*
     * Clock tags
     */

    if(src_data_tags.empty()) {
        //Propagate the clock tags through the clock network

        for(const TimingTag& src_clk_tag : src_clk_tags) {
            //Standard propagation through the clock network
            ops_.merge_arr_tags(node_clock_tags, src_clk_tag.arr_time() + edge_delay, src_clk_tag);

            if(is_clock_to_data_edge(tg, src_node_id, node_id)) {
                //We convert the clock arrival time to a data
                //arrival time at this node (since the clock
                //arrival launches the data).

                //Make a copy of the tag
                TimingTag launch_tag = src_clk_tag;

                //Update the launch node, since the data is
                //launching from this node
                launch_tag.set_launch_node(node_id);

                //Mark propagated launch time as a DATA tag
                ops_.merge_arr_tags(node_data_tags, launch_tag.arr_time() + edge_delay, launch_tag);
            }
        }
    }

    /*
     * Data tags
     */

    for(const TimingTag& src_data_tag : src_data_tags) {
        //Standard data-path propagation
        ops_.merge_arr_tags(node_data_tags, src_data_tag.arr_time() + edge_delay, src_data_tag);
    }
}

/*
 * Required Time Operations
 */

template<class AnalysisOps>
template<class DelayCalc>
void CommonAnalysisVisitor<AnalysisOps>::do_required_traverse_node(const TimingGraph& tg, const TimingConstraints& tc, const DelayCalc& dc, const NodeId node_id) {
    //Do not propagate required tags through constant generators
    if(tc.node_is_constant_generator(node_id)) return;

    //Pull from downstream sinks to current node
    for(EdgeId edge_id : tg.node_out_edges(node_id)) {
        do_required_traverse_edge(tg, dc, node_id, edge_id);
    }
}

template<class AnalysisOps>
template<class DelayCalc>
void CommonAnalysisVisitor<AnalysisOps>::do_required_traverse_edge(const TimingGraph& tg, const DelayCalc& dc, const NodeId node_id, const EdgeId edge_id) {
    //We must use the tags by reference so we don't accidentally wipe-out any
    //existing tags
    TimingTags& node_data_tags = ops_.get_data_tags(node_id);

    //Pulling values from downstream sink node
    NodeId sink_node_id = tg.edge_sink_node(edge_id);

    const Time& edge_delay = ops_.edge_delay(dc, tg, edge_id);

    const TimingTags& sink_data_tags = ops_.get_data_tags(sink_node_id);

    for(const TimingTag& sink_tag : sink_data_tags) {
        //We only propogate the required time if we have a valid arrival time
        auto matched_tag_iter = node_data_tags.find_tag_by_clock_domain(sink_tag.clock_domain());
        if(matched_tag_iter != node_data_tags.end() && matched_tag_iter->arr_time().valid()) {
            //Valid arrival, update required
            ops_.merge_req_tag(*matched_tag_iter, sink_tag.req_time() - edge_delay, sink_tag);
        }
    }
}

template<class AnalysisOps>
bool CommonAnalysisVisitor<AnalysisOps>::is_clock_to_data_edge(const TimingGraph& tg, const NodeId /*src_node_id*/, const NodeId node_id) const {
    if(tg.node_type(node_id) == NodeType::FF_SOURCE) return true;
    return false;
}

}} //namepsace

#endif
<|endoftext|>


<commit_before>/***************************************************************************//**
 * FILE : cmiss_set.hpp
 *
 * Template class derived from STL set which handles reference counting and
 * maintains connections between sets of related objects to safely and
 * efficiently manage changing the identifier i.e. sort key of the objects.
 */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is cmgui.
 *
 * The Initial Developer of the Original Code is
 * Auckland Uniservices Ltd, Auckland, New Zealand.
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
#if !defined (CMISS_SET_HPP)
#define CMISS_SET_HPP

#include <set>

/*
Local types
-----------
*/

template<class Key, class Compare > class Cmiss_set :
	private std::set<Key,Compare>
{
private:
	typedef std::set<Key,Compare> Base_class;

	mutable Cmiss_set *next, *prev; // linked list of related sets
	Key temp_removed_object; // removed while changing identifier
	int access_count;

	Cmiss_set() :
		next(this),
		prev(this),
		temp_removed_object(0),
		access_count(1)
	{
	}

	/** copy constructor */
	Cmiss_set(const Cmiss_set& source) :
		Base_class(source),
		next(source.next),
		prev(&source),
		temp_removed_object(0),
		access_count(1)
	{
		for (iterator iter = begin(); iter != end(); ++iter)
		{
			(*iter)->access();
		}
		source.next = this;
		next->prev = this;
	}

	/** creates a set with the same manager, not a copy constructor */
	Cmiss_set(const Cmiss_set *source) :
		Base_class(),
		next(source->next),
		prev(const_cast<Cmiss_set *>(source)),
		temp_removed_object(0),
		access_count(1)
	{
		source->next = this;
		next->prev = this;
	}

public:

	~Cmiss_set()
	{
		clear();
		prev->next = next;
		next->prev = prev;
	}

	typedef typename Base_class::iterator iterator;
	typedef typename Base_class::const_iterator const_iterator;
	typedef typename Base_class::size_type size_type;

	static Cmiss_set *create_independent()
	{
		return new Cmiss_set();
	}

	Cmiss_set *create_related() const
	{
		return new Cmiss_set(this);
	}

	Cmiss_set *create_copy()
	{
		return new Cmiss_set(*this);
	}

	Cmiss_set& operator=(const Cmiss_set& source)
	{
		if (&source == this)
			return *this;
		const Cmiss_set *related_set = this->next;
		while (related_set != this)
		{
			if (related_set == &source)
			{
				break;
			}
			related_set = related_set->next;
		}
		Base_class::operator=(source);
		if (related_set == this)
		{
			// copy from unrelated set: switch linked-lists
			this->next->prev = this->prev;
			this->prev->next = this->next;
			this->prev = const_cast<Cmiss_set *>(&source);
			this->next = source.next;
			source.next->prev = this;
			source.next = this;
		}
		return *this;
	}

	inline Cmiss_set *access()
	{
		++access_count;
		return this;
	}

	static inline int deaccess(Cmiss_set **set_address)
	{
		if (set_address && *set_address)
		{
			if (0 >= (--(*set_address)->access_count))
			{
				delete *set_address;
			}
			*set_address = 0;
			return 1;
		}
		return 0;
	}

	size_type erase(Key object)
	{
		size_type count = Base_class::erase(object);
		if (count)
		{
			object->deaccess(&object);
		}
		return count;
	}

	void erase(iterator iter)
	{
		Key object = *iter;
		Base_class::erase(iter);
		object->deaccess(&object);
	}

	std::pair<iterator,bool> insert(Key object)
	{
		std::pair<iterator,bool> result = Base_class::insert(object);
		if (result.second)
			object->access();
		return result;
	}

	void clear()
	{
		for (iterator iter = begin(); iter != end(); ++iter)
		{
			Key tmp = *iter;
			tmp->deaccess(&tmp);
		}
		Base_class::clear();
	}

	size_type size() const
	{
		return Base_class::size();
	}

	const_iterator find(const Key &object) const
	{
		return Base_class::find(object);
	}

	iterator find(const Key &object)
	{
		return Base_class::find(object);
	}

	iterator begin()
	{
		return Base_class::begin();
	}

	const_iterator begin() const
	{
		return Base_class::begin();
	}

	iterator end()
	{
		return Base_class::end();
	}

	const_iterator end() const
	{
		return Base_class::end();
	}

	bool begin_identifier_change(Key object)
	{
		Cmiss_set *related_set = this;
		do
		{
			iterator iter = related_set->find(object);
			if (iter != related_set->end())
			{
				related_set->temp_removed_object = (*iter)->access();
				related_set->erase(iter);
			}
			else
			{
				related_set->temp_removed_object = 0;
			}
			related_set = related_set->next;
		}
		while (related_set != this);
		return true;
	}

	void end_identifier_change()
	{
		Cmiss_set *related_set = this;
		do
		{
			if (related_set->temp_removed_object)
			{
				related_set->insert(related_set->temp_removed_object); // check success?
				related_set->temp_removed_object->deaccess(&related_set->temp_removed_object);
			}
			related_set = related_set->next;
		}
		while (related_set != this);
	}

	/**
	 * A specialised iterator class which wraps a reference to a container and an
	 * iterator in it, suitable for use from external API because the container
	 * cannot be destroyed before the iterator.
	 */
	struct ext_iterator
	{
		Cmiss_set *container;
		iterator iter;

		ext_iterator(Cmiss_set *container) :
			container(container->access()),
			iter(container->begin())
		{
		}

		~ext_iterator()
		{
			// the container may be destroyed immediately before the iterator;
			// hopefully not a problem
			container->deaccess(&container);
		}

		Key next()
		{
			if (iter != container->end())
			{
				Key object = *iter;
				++iter;
				return object->access();
			}
			return 0;
		}

		Key next_non_access()
		{
			if (iter != container->end())
			{
				Key object = *iter;
				++iter;
				return object;
			}
			return 0;
		}

	};
};

#endif /* !defined (CMISS_SET_HPP) */
<commit_msg>Fixed Cmiss_set assignment operator to handle access_count.  https://tracker.physiomeproject.org/show_bug.cgi?id=1451<commit_after>/***************************************************************************//**
 * FILE : cmiss_set.hpp
 *
 * Template class derived from STL set which handles reference counting and
 * maintains connections between sets of related objects to safely and
 * efficiently manage changing the identifier i.e. sort key of the objects.
 */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is cmgui.
 *
 * The Initial Developer of the Original Code is
 * Auckland Uniservices Ltd, Auckland, New Zealand.
 * Portions created by the Initial Developer are Copyright (C) 2011
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */
#if !defined (CMISS_SET_HPP)
#define CMISS_SET_HPP

#include <set>

/*
Local types
-----------
*/

template<class Key, class Compare > class Cmiss_set :
	private std::set<Key,Compare>
{
private:
	typedef std::set<Key,Compare> Base_class;

	mutable Cmiss_set *next, *prev; // linked list of related sets
	Key temp_removed_object; // removed while changing identifier
	int access_count;

	Cmiss_set() :
		next(this),
		prev(this),
		temp_removed_object(0),
		access_count(1)
	{
	}

	/** copy constructor */
	Cmiss_set(const Cmiss_set& source) :
		Base_class(source),
		next(source.next),
		prev(&source),
		temp_removed_object(0),
		access_count(1)
	{
		for (iterator iter = begin(); iter != end(); ++iter)
		{
			(*iter)->access();
		}
		source.next = this;
		next->prev = this;
	}

	/** creates a set with the same manager, not a copy constructor */
	Cmiss_set(const Cmiss_set *source) :
		Base_class(),
		next(source->next),
		prev(const_cast<Cmiss_set *>(source)),
		temp_removed_object(0),
		access_count(1)
	{
		source->next = this;
		next->prev = this;
	}

public:

	~Cmiss_set()
	{
		clear();
		prev->next = next;
		next->prev = prev;
	}

	typedef typename Base_class::iterator iterator;
	typedef typename Base_class::const_iterator const_iterator;
	typedef typename Base_class::size_type size_type;

	static Cmiss_set *create_independent()
	{
		return new Cmiss_set();
	}

	Cmiss_set *create_related() const
	{
		return new Cmiss_set(this);
	}

	Cmiss_set *create_copy()
	{
		return new Cmiss_set(*this);
	}

	Cmiss_set& operator=(const Cmiss_set& source)
	{
		if (&source == this)
			return *this;
		const Cmiss_set *related_set = this->next;
		while (related_set != this)
		{
			if (related_set == &source)
			{
				break;
			}
			related_set = related_set->next;
		}
		for (iterator iter = begin(); iter != end(); ++iter)
		{
			Key object = *iter;
			object->deaccess(&object);
		}
		Base_class::operator=(source);
		for (iterator iter = begin(); iter != end(); ++iter)
		{
			(*iter)->access();
		}
		if (related_set == this)
		{
			// copy from unrelated set: switch linked-lists
			this->next->prev = this->prev;
			this->prev->next = this->next;
			this->prev = const_cast<Cmiss_set *>(&source);
			this->next = source.next;
			source.next->prev = this;
			source.next = this;
		}
		return *this;
	}

	inline Cmiss_set *access()
	{
		++access_count;
		return this;
	}

	static inline int deaccess(Cmiss_set **set_address)
	{
		if (set_address && *set_address)
		{
			if (0 >= (--(*set_address)->access_count))
			{
				delete *set_address;
			}
			*set_address = 0;
			return 1;
		}
		return 0;
	}

	size_type erase(Key object)
	{
		size_type count = Base_class::erase(object);
		if (count)
		{
			object->deaccess(&object);
		}
		return count;
	}

	void erase(iterator iter)
	{
		Key object = *iter;
		Base_class::erase(iter);
		object->deaccess(&object);
	}

	std::pair<iterator,bool> insert(Key object)
	{
		std::pair<iterator,bool> result = Base_class::insert(object);
		if (result.second)
			object->access();
		return result;
	}

	void clear()
	{
		for (iterator iter = begin(); iter != end(); ++iter)
		{
			Key tmp = *iter;
			tmp->deaccess(&tmp);
		}
		Base_class::clear();
	}

	size_type size() const
	{
		return Base_class::size();
	}

	const_iterator find(const Key &object) const
	{
		return Base_class::find(object);
	}

	iterator find(const Key &object)
	{
		return Base_class::find(object);
	}

	iterator begin()
	{
		return Base_class::begin();
	}

	const_iterator begin() const
	{
		return Base_class::begin();
	}

	iterator end()
	{
		return Base_class::end();
	}

	const_iterator end() const
	{
		return Base_class::end();
	}

	bool begin_identifier_change(Key object)
	{
		Cmiss_set *related_set = this;
		do
		{
			iterator iter = related_set->find(object);
			if (iter != related_set->end())
			{
				related_set->temp_removed_object = (*iter)->access();
				related_set->erase(iter);
			}
			else
			{
				related_set->temp_removed_object = 0;
			}
			related_set = related_set->next;
		}
		while (related_set != this);
		return true;
	}

	void end_identifier_change()
	{
		Cmiss_set *related_set = this;
		do
		{
			if (related_set->temp_removed_object)
			{
				related_set->insert(related_set->temp_removed_object); // check success?
				related_set->temp_removed_object->deaccess(&related_set->temp_removed_object);
			}
			related_set = related_set->next;
		}
		while (related_set != this);
	}

	/**
	 * A specialised iterator class which wraps a reference to a container and an
	 * iterator in it, suitable for use from external API because the container
	 * cannot be destroyed before the iterator.
	 */
	struct ext_iterator
	{
		Cmiss_set *container;
		iterator iter;

		ext_iterator(Cmiss_set *container) :
			container(container->access()),
			iter(container->begin())
		{
		}

		~ext_iterator()
		{
			// the container may be destroyed immediately before the iterator;
			// hopefully not a problem
			container->deaccess(&container);
		}

		Key next()
		{
			if (iter != container->end())
			{
				Key object = *iter;
				++iter;
				return object->access();
			}
			return 0;
		}

		Key next_non_access()
		{
			if (iter != container->end())
			{
				Key object = *iter;
				++iter;
				return object;
			}
			return 0;
		}

	};
};

#endif /* !defined (CMISS_SET_HPP) */
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: mythes.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: hr $ $Date: 2007-08-03 12:31:01 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    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., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_lingucomponent.hxx"
#include "license.readme"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include "mythes.hxx"

MyThes::MyThes(const char* idxpath, const char * datpath)
{
    nw = 0;
    encoding = NULL;
    list = NULL;
    offst = NULL;

    if (thInitialize(idxpath, datpath) != 1) {
        fprintf(stderr,"Error - can't open %s or %s\n",idxpath, datpath);
        fflush(stderr);
        thCleanup();
        // did not initialize properly - throw exception?
    }
}

MyThes::~MyThes()
{
    thCleanup();
}

int MyThes::thInitialize(const char* idxpath, const char* datpath)
{

    // open the index file
    FILE * pifile = fopen(idxpath,"r");
    if (!pifile) {
        return 0;
    }

    // parse in encoding and index size */
    char * wrd;
    wrd = (char *)calloc(1, MAX_WD_LEN);
    if (!wrd) {
       fprintf(stderr,"Error - bad memory allocation\n");
       fflush(stderr);
       fclose(pifile);
       return 0;
    }
    int len = readLine(pifile,wrd,MAX_WD_LEN);
    encoding = mystrdup(wrd);
    len = readLine(pifile,wrd,MAX_WD_LEN);
    int idxsz = atoi(wrd);

    // now allocate list, offst for the given size
    list = (char**)   calloc(idxsz,sizeof(char*));
    offst = (unsigned int*) calloc(idxsz,sizeof(unsigned int));

    if ( (!(list)) || (!(offst)) ) {
       fprintf(stderr,"Error - bad memory allocation\n");
       fflush(stderr);
       fclose(pifile);
       return 0;
    }

    // now parse the remaining lines of the index
    len = readLine(pifile,wrd,MAX_WD_LEN);
    while (len > 0)
    {
        int np = mystr_indexOfChar(wrd,'|');
        if (nw < idxsz) {
            if (np >= 0) {
                *(wrd+np) = '\0';
                list[nw] = (char *)calloc(1,(np+1));
                if (!list[nw]) {
                    fprintf(stderr,"Error - bad memory allocation\n");
                    fflush(stderr);
                    fclose(pifile);
                    return 0;
                }
                memcpy((list[nw]),wrd,np);
                offst[nw] = atoi(wrd+np+1);
                nw++;
            }
        }
        len = readLine(pifile,wrd,MAX_WD_LEN);
    }

    free((void *)wrd);
    fclose(pifile);

    /* next open the data file */
    pdfile = fopen(datpath,"r");
    if (!pdfile) {
        return 0;
    }

    return 1;
}

void MyThes::thCleanup()
{
    /* first close the data file */
    if (pdfile) {
        fclose(pdfile);
        pdfile=NULL;
    }

    if (list)
    {
        /* now free up all the allocated strings on the list */
        for (int i=0; i < nw; i++)
        {
            if (list[i]) {
                free(list[i]);
                list[i] = 0;
            }
        }
        free((void*)list);
    }

    if (encoding) free((void*)encoding);
    if (offst) free((void*)offst);

    encoding = NULL;
    list = NULL;
    offst = NULL;
    nw = 0;
}

// lookup text in index and count of meanings and a list of meaning entries
// with each entry having a synonym count and pointer to an
// array of char * (i.e the synonyms)
//
// note: calling routine should call CleanUpAfterLookup with the original
// meaning point and count to properly deallocate memory

int MyThes::Lookup(const char * pText, int len, mentry** pme)
{

    *pme = NULL;

    // handle the case of missing file or file related errors
    if (! pdfile) return 0;

    long offset = 0;

    /* copy search word and make sure null terminated */
    char * wrd = (char *) calloc(1,(len+1));
    memcpy(wrd,pText,len);

    /* find it in the list */
    int idx = nw > 0 ? binsearch(wrd,list,nw) : -1;
    free(wrd);
    if (idx < 0) return 0;

    // now seek to the offset
    offset = (long) offst[idx];
    int rc = fseek(pdfile,offset,SEEK_SET);
    if (rc) {
       return 0;
    }

    // grab the count of the number of meanings
    // and allocate a list of meaning entries
    char * buf = NULL;
    buf  = (char *) malloc( MAX_LN_LEN );
    if (!buf) return 0;
    readLine(pdfile, buf, (MAX_LN_LEN-1));
    int np = mystr_indexOfChar(buf,'|');
    if (np < 0) {
         free(buf);
         return 0;
    }
    int nmeanings = atoi(buf+np+1);
    *pme = (mentry*) malloc( nmeanings * sizeof(mentry) );
    if (!(*pme)) {
        free(buf);
        return 0;
    }

    // now read in each meaning and parse it to get defn, count and synonym lists
    mentry* pm = *(pme);
    char dfn[MAX_WD_LEN];

    for (int j = 0; j < nmeanings; j++) {
        readLine(pdfile, buf, (MAX_LN_LEN-1));

        pm->count = 0;
        pm->psyns = NULL;
        pm->defn = NULL;

        // store away the part of speech for later use
        char * p = buf;
        char * pos = NULL;
        np = mystr_indexOfChar(p,'|');
        if (np >= 0) {
           *(buf+np) = '\0';
       pos = mystrdup(p);
           p = p + np + 1;
    } else {
          pos = mystrdup("");
        }

        // count the number of fields in the remaining line
        int nf = 1;
        char * d = p;
        np = mystr_indexOfChar(d,'|');
        while ( np >= 0 ) {
      nf++;
          d = d + np + 1;
          np = mystr_indexOfChar(d,'|');
    }
    pm->count = nf;
        pm->psyns = (char **) malloc(nf*sizeof(char*));

        // fill in the synonym list
        d = p;
        for (int jj = 0; jj < nf; jj++)
        {
            np = mystr_indexOfChar(d,'|');
            if (np > 0)
            {
                *(d+np) = '\0';
                pm->psyns[jj] = mystrdup(d);
                d = d + np + 1;
            }
            else
            {
              pm->psyns[jj] = mystrdup(d);
            }
        }

        // add pos to first synonym to create the definition
        int k = strlen(pos);
        int m = strlen(pm->psyns[0]);
        if ((k+m) < (MAX_WD_LEN - 1)) {
             strncpy(dfn,pos,k);
             *(dfn+k) = ' ';
             strncpy((dfn+k+1),(pm->psyns[0]),m+1);
             pm->defn = mystrdup(dfn);
    } else {
         pm->defn = mystrdup(pm->psyns[0]);
    }
        free(pos);
        pm++;

    }
    free(buf);

    return nmeanings;
}

void MyThes::CleanUpAfterLookup(mentry ** pme, int nmeanings)
{

    if (nmeanings == 0) return;
    if ((*pme) == NULL) return;

    mentry * pm = *pme;

    for (int i = 0; i < nmeanings; i++) {
       int count = pm->count;
       for (int j = 0; j < count; j++) {
      if (pm->psyns[j]) free(pm->psyns[j]);
          pm->psyns[j] = NULL;
       }
       if (pm->psyns) free(pm->psyns);
       pm->psyns = NULL;
       if (pm->defn) free(pm->defn);
       pm->defn = NULL;
       pm->count = 0;
       pm++;
    }
    pm = *pme;
    free(pm);
    *pme = NULL;
    return;
}

// read a line of text from a text file stripping
// off the line terminator and replacing it with
// a null string terminator.
// returns:  -1 on error or the number of characters in
//             in the returning string

// A maximum of nc characters will be returned

int MyThes::readLine(FILE * pf, char * buf, int nc)
{

  if (fgets(buf,nc,pf)) {
    mychomp(buf);
    return strlen(buf);
  }
  return -1;
}

//  performs a binary search on null terminated character
//  strings
//
//  returns: -1 on not found
//           index of wrd in the list[]

int MyThes::binsearch(char * sw, char* _list[], int nlst)
{
    int lp, up, mp, j, indx;
    lp = 0;
    up = nlst-1;
    indx = -1;
    if (strcmp(sw,_list[lp]) < 0) return -1;
    if (strcmp(sw,_list[up]) > 0) return -1;
    while (indx < 0 ) {
        mp = (int)((lp+up) >> 1);
        j = strcmp(sw,_list[mp]);
        if ( j > 0) {
            lp = mp + 1;
        } else if (j < 0 ) {
            up = mp - 1;
        } else {
            indx = mp;
        }
        if (lp > up) return -1;
    }
    return indx;
}

char * MyThes::get_th_encoding()
{
  if (encoding) return encoding;
  return NULL;
}

// string duplication routine
char * MyThes::mystrdup(const char * p)
{
  int sl = strlen(p) + 1;
  char * d = (char *)malloc(sl);
  if (d) {
    memcpy(d,p,sl);
    return d;
  }
  return NULL;
}

// remove cross-platform text line end characters
void MyThes::mychomp(char * s)
{
  int k = strlen(s);
  if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0';
  if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0';
}

// return index of char in string
int MyThes::mystr_indexOfChar(const char * d, int c)
{
  char * p = strchr((char *)d,c);
  if (p) return (int)(p-d);
  return -1;
}

<commit_msg>INTEGRATION: CWS changefileheader (1.8.56); FILE MERGED 2008/03/31 16:25:06 rt 1.8.56.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: mythes.cxx,v $
 * $Revision: 1.9 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_lingucomponent.hxx"
#include "license.readme"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include "mythes.hxx"

MyThes::MyThes(const char* idxpath, const char * datpath)
{
    nw = 0;
    encoding = NULL;
    list = NULL;
    offst = NULL;

    if (thInitialize(idxpath, datpath) != 1) {
        fprintf(stderr,"Error - can't open %s or %s\n",idxpath, datpath);
        fflush(stderr);
        thCleanup();
        // did not initialize properly - throw exception?
    }
}

MyThes::~MyThes()
{
    thCleanup();
}

int MyThes::thInitialize(const char* idxpath, const char* datpath)
{

    // open the index file
    FILE * pifile = fopen(idxpath,"r");
    if (!pifile) {
        return 0;
    }

    // parse in encoding and index size */
    char * wrd;
    wrd = (char *)calloc(1, MAX_WD_LEN);
    if (!wrd) {
       fprintf(stderr,"Error - bad memory allocation\n");
       fflush(stderr);
       fclose(pifile);
       return 0;
    }
    int len = readLine(pifile,wrd,MAX_WD_LEN);
    encoding = mystrdup(wrd);
    len = readLine(pifile,wrd,MAX_WD_LEN);
    int idxsz = atoi(wrd);

    // now allocate list, offst for the given size
    list = (char**)   calloc(idxsz,sizeof(char*));
    offst = (unsigned int*) calloc(idxsz,sizeof(unsigned int));

    if ( (!(list)) || (!(offst)) ) {
       fprintf(stderr,"Error - bad memory allocation\n");
       fflush(stderr);
       fclose(pifile);
       return 0;
    }

    // now parse the remaining lines of the index
    len = readLine(pifile,wrd,MAX_WD_LEN);
    while (len > 0)
    {
        int np = mystr_indexOfChar(wrd,'|');
        if (nw < idxsz) {
            if (np >= 0) {
                *(wrd+np) = '\0';
                list[nw] = (char *)calloc(1,(np+1));
                if (!list[nw]) {
                    fprintf(stderr,"Error - bad memory allocation\n");
                    fflush(stderr);
                    fclose(pifile);
                    return 0;
                }
                memcpy((list[nw]),wrd,np);
                offst[nw] = atoi(wrd+np+1);
                nw++;
            }
        }
        len = readLine(pifile,wrd,MAX_WD_LEN);
    }

    free((void *)wrd);
    fclose(pifile);

    /* next open the data file */
    pdfile = fopen(datpath,"r");
    if (!pdfile) {
        return 0;
    }

    return 1;
}

void MyThes::thCleanup()
{
    /* first close the data file */
    if (pdfile) {
        fclose(pdfile);
        pdfile=NULL;
    }

    if (list)
    {
        /* now free up all the allocated strings on the list */
        for (int i=0; i < nw; i++)
        {
            if (list[i]) {
                free(list[i]);
                list[i] = 0;
            }
        }
        free((void*)list);
    }

    if (encoding) free((void*)encoding);
    if (offst) free((void*)offst);

    encoding = NULL;
    list = NULL;
    offst = NULL;
    nw = 0;
}

// lookup text in index and count of meanings and a list of meaning entries
// with each entry having a synonym count and pointer to an
// array of char * (i.e the synonyms)
//
// note: calling routine should call CleanUpAfterLookup with the original
// meaning point and count to properly deallocate memory

int MyThes::Lookup(const char * pText, int len, mentry** pme)
{

    *pme = NULL;

    // handle the case of missing file or file related errors
    if (! pdfile) return 0;

    long offset = 0;

    /* copy search word and make sure null terminated */
    char * wrd = (char *) calloc(1,(len+1));
    memcpy(wrd,pText,len);

    /* find it in the list */
    int idx = nw > 0 ? binsearch(wrd,list,nw) : -1;
    free(wrd);
    if (idx < 0) return 0;

    // now seek to the offset
    offset = (long) offst[idx];
    int rc = fseek(pdfile,offset,SEEK_SET);
    if (rc) {
       return 0;
    }

    // grab the count of the number of meanings
    // and allocate a list of meaning entries
    char * buf = NULL;
    buf  = (char *) malloc( MAX_LN_LEN );
    if (!buf) return 0;
    readLine(pdfile, buf, (MAX_LN_LEN-1));
    int np = mystr_indexOfChar(buf,'|');
    if (np < 0) {
         free(buf);
         return 0;
    }
    int nmeanings = atoi(buf+np+1);
    *pme = (mentry*) malloc( nmeanings * sizeof(mentry) );
    if (!(*pme)) {
        free(buf);
        return 0;
    }

    // now read in each meaning and parse it to get defn, count and synonym lists
    mentry* pm = *(pme);
    char dfn[MAX_WD_LEN];

    for (int j = 0; j < nmeanings; j++) {
        readLine(pdfile, buf, (MAX_LN_LEN-1));

        pm->count = 0;
        pm->psyns = NULL;
        pm->defn = NULL;

        // store away the part of speech for later use
        char * p = buf;
        char * pos = NULL;
        np = mystr_indexOfChar(p,'|');
        if (np >= 0) {
           *(buf+np) = '\0';
       pos = mystrdup(p);
           p = p + np + 1;
    } else {
          pos = mystrdup("");
        }

        // count the number of fields in the remaining line
        int nf = 1;
        char * d = p;
        np = mystr_indexOfChar(d,'|');
        while ( np >= 0 ) {
      nf++;
          d = d + np + 1;
          np = mystr_indexOfChar(d,'|');
    }
    pm->count = nf;
        pm->psyns = (char **) malloc(nf*sizeof(char*));

        // fill in the synonym list
        d = p;
        for (int jj = 0; jj < nf; jj++)
        {
            np = mystr_indexOfChar(d,'|');
            if (np > 0)
            {
                *(d+np) = '\0';
                pm->psyns[jj] = mystrdup(d);
                d = d + np + 1;
            }
            else
            {
              pm->psyns[jj] = mystrdup(d);
            }
        }

        // add pos to first synonym to create the definition
        int k = strlen(pos);
        int m = strlen(pm->psyns[0]);
        if ((k+m) < (MAX_WD_LEN - 1)) {
             strncpy(dfn,pos,k);
             *(dfn+k) = ' ';
             strncpy((dfn+k+1),(pm->psyns[0]),m+1);
             pm->defn = mystrdup(dfn);
    } else {
         pm->defn = mystrdup(pm->psyns[0]);
    }
        free(pos);
        pm++;

    }
    free(buf);

    return nmeanings;
}

void MyThes::CleanUpAfterLookup(mentry ** pme, int nmeanings)
{

    if (nmeanings == 0) return;
    if ((*pme) == NULL) return;

    mentry * pm = *pme;

    for (int i = 0; i < nmeanings; i++) {
       int count = pm->count;
       for (int j = 0; j < count; j++) {
      if (pm->psyns[j]) free(pm->psyns[j]);
          pm->psyns[j] = NULL;
       }
       if (pm->psyns) free(pm->psyns);
       pm->psyns = NULL;
       if (pm->defn) free(pm->defn);
       pm->defn = NULL;
       pm->count = 0;
       pm++;
    }
    pm = *pme;
    free(pm);
    *pme = NULL;
    return;
}

// read a line of text from a text file stripping
// off the line terminator and replacing it with
// a null string terminator.
// returns:  -1 on error or the number of characters in
//             in the returning string

// A maximum of nc characters will be returned

int MyThes::readLine(FILE * pf, char * buf, int nc)
{

  if (fgets(buf,nc,pf)) {
    mychomp(buf);
    return strlen(buf);
  }
  return -1;
}

//  performs a binary search on null terminated character
//  strings
//
//  returns: -1 on not found
//           index of wrd in the list[]

int MyThes::binsearch(char * sw, char* _list[], int nlst)
{
    int lp, up, mp, j, indx;
    lp = 0;
    up = nlst-1;
    indx = -1;
    if (strcmp(sw,_list[lp]) < 0) return -1;
    if (strcmp(sw,_list[up]) > 0) return -1;
    while (indx < 0 ) {
        mp = (int)((lp+up) >> 1);
        j = strcmp(sw,_list[mp]);
        if ( j > 0) {
            lp = mp + 1;
        } else if (j < 0 ) {
            up = mp - 1;
        } else {
            indx = mp;
        }
        if (lp > up) return -1;
    }
    return indx;
}

char * MyThes::get_th_encoding()
{
  if (encoding) return encoding;
  return NULL;
}

// string duplication routine
char * MyThes::mystrdup(const char * p)
{
  int sl = strlen(p) + 1;
  char * d = (char *)malloc(sl);
  if (d) {
    memcpy(d,p,sl);
    return d;
  }
  return NULL;
}

// remove cross-platform text line end characters
void MyThes::mychomp(char * s)
{
  int k = strlen(s);
  if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0';
  if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0';
}

// return index of char in string
int MyThes::mystr_indexOfChar(const char * d, int c)
{
  char * p = strchr((char *)d,c);
  if (p) return (int)(p-d);
  return -1;
}

<|endoftext|>


<commit_before>#include <agency/future.hpp>
#include <agency/new_executor_traits.hpp>
#include <iostream>
#include <cassert>

#include "test_executors.hpp"

template<class Executor>
void test()
{
  using executor_type = Executor;

  size_t n = 100;

  auto int_ready   = agency::detail::make_ready_future(0);
  auto void_ready  = agency::detail::make_ready_future();
  auto vector_ready = agency::detail::make_ready_future(std::vector<int>(n));

  auto futures = std::make_tuple(std::move(int_ready), std::move(void_ready), std::move(vector_ready));

  std::mutex mut;
  executor_type exec;
  std::future<agency::detail::tuple<std::vector<int>,int>> fut = agency::new_executor_traits<executor_type>::template when_all_execute_and_select<2,0>(exec, [&mut](size_t idx, int& x, std::vector<int>& vec)
  {
    mut.lock();
    x += 1;
    mut.unlock();

    vec[idx] = 13;
  },
  n,
  std::move(futures));

  auto got = fut.get();

  assert(std::get<0>(got) == std::vector<int>(n, 13));
  assert(std::get<1>(got) == n);
  assert(exec.valid());
}

int main()
{
  using namespace test_executors;

  test<empty_executor>();
  test<single_agent_when_all_execute_and_select_executor>();
  test<multi_agent_when_all_execute_and_select_executor>();
  test<multi_agent_when_all_execute_and_select_with_shared_inits_executor>();

  test<when_all_executor>();

  test<multi_agent_execute_returning_user_defined_container_executor>();
  test<multi_agent_execute_returning_default_container_executor>();
  test<multi_agent_execute_returning_void_executor>();

  test<multi_agent_async_execute_returning_user_defined_container_executor>();
  test<multi_agent_async_execute_returning_default_container_executor>();
  test<multi_agent_async_execute_returning_void_executor>();

  test<multi_agent_execute_with_shared_inits_returning_user_defined_container_executor>();

  std::cout << "OK" << std::endl;

  return 0;
}

<commit_msg>Test multi_agent_execute_with_shared_inits_returning_default_container_executor with test_multi_agent_when_all_execute_and_select.cpp.<commit_after>#include <agency/future.hpp>
#include <agency/new_executor_traits.hpp>
#include <iostream>
#include <cassert>

#include "test_executors.hpp"

template<class Executor>
void test()
{
  using executor_type = Executor;

  size_t n = 100;

  auto int_ready   = agency::detail::make_ready_future(0);
  auto void_ready  = agency::detail::make_ready_future();
  auto vector_ready = agency::detail::make_ready_future(std::vector<int>(n));

  auto futures = std::make_tuple(std::move(int_ready), std::move(void_ready), std::move(vector_ready));

  std::mutex mut;
  executor_type exec;
  std::future<agency::detail::tuple<std::vector<int>,int>> fut = agency::new_executor_traits<executor_type>::template when_all_execute_and_select<2,0>(exec, [&mut](size_t idx, int& x, std::vector<int>& vec)
  {
    mut.lock();
    x += 1;
    mut.unlock();

    vec[idx] = 13;
  },
  n,
  std::move(futures));

  auto got = fut.get();

  assert(std::get<0>(got) == std::vector<int>(n, 13));
  assert(std::get<1>(got) == n);
  assert(exec.valid());
}

int main()
{
  using namespace test_executors;

  test<empty_executor>();
  test<single_agent_when_all_execute_and_select_executor>();
  test<multi_agent_when_all_execute_and_select_executor>();
  test<multi_agent_when_all_execute_and_select_with_shared_inits_executor>();

  test<when_all_executor>();

  test<multi_agent_execute_returning_user_defined_container_executor>();
  test<multi_agent_execute_returning_default_container_executor>();
  test<multi_agent_execute_returning_void_executor>();

  test<multi_agent_async_execute_returning_user_defined_container_executor>();
  test<multi_agent_async_execute_returning_default_container_executor>();
  test<multi_agent_async_execute_returning_void_executor>();

  test<multi_agent_execute_with_shared_inits_returning_user_defined_container_executor>();
  test<multi_agent_execute_with_shared_inits_returning_default_container_executor>();

  std::cout << "OK" << std::endl;

  return 0;
}

<|endoftext|>