<commit_before>/*
 * Copyright (c) 1998-2004, Index Data.
 * See the file LICENSE for details.
 *
 * $Id: yaz-cql2rpn.cpp,v 1.11 2007-01-12 10:09:25 adam Exp $
 */

#include <yaz/log.h>
#include <yaz/pquery.h>
#include <yazpp/cql2rpn.h>

using namespace yazpp_1;

Yaz_cql2rpn::Yaz_cql2rpn()
{
    m_transform = 0;
}

Yaz_cql2rpn::~Yaz_cql2rpn()
{
    if (m_transform)
        cql_transform_close(m_transform);
}

void Yaz_cql2rpn::set_pqf_file(const char *fname)
{
    if (!m_transform)
        m_transform = cql_transform_open_fname(fname);
}

bool Yaz_cql2rpn::parse_spec_file(const char *fname, int *error)
{
    *error = 0;
    cql_transform_close(m_transform);
    m_transform = cql_transform_open_fname(fname);
    return m_transform ? true : false;
}

int Yaz_cql2rpn::query_transform(const char *cql_query,
                                 Z_RPNQuery **rpnquery, ODR o,
                                 char **addinfop)
{
    const char *addinfo = 0;
    if (!m_transform)
        return -3;
    CQL_parser cp = cql_parser_create();

    int r = cql_parser_string(cp, cql_query);
    if (r)
    {
        yaz_log(YLOG_LOG, "CQL Parse Error");
        r = 10;
    }
    else
    {
        char rpn_buf[10240];
        r = cql_transform_buf(m_transform, cql_parser_result(cp),
                              rpn_buf, sizeof(rpn_buf)-1);
        if (!r)
        {
            YAZ_PQF_Parser pp = yaz_pqf_create();

            *rpnquery = yaz_pqf_parse(pp, o, rpn_buf);
            if (!*rpnquery)
            {
                size_t off;
                const char *pqf_msg;
                int code = yaz_pqf_error(pp, &pqf_msg, &off);
                yaz_log(YLOG_WARN, "PQF Parser Error %s (code %d)",
                        pqf_msg, code);
                r = -1;
            }
            yaz_pqf_destroy(pp);
        }
        else
        {
            r = cql_transform_error(m_transform, &addinfo);
            yaz_log(YLOG_LOG, "CQL Transform Error %d %s", r,
                    addinfo ? addinfo : "");
        }
    }
    cql_parser_destroy(cp);
    if (addinfo)
        *addinfop = odr_strdup(o, addinfo);
    else
        *addinfop = 0;
    return r;
}
/*
 * Local variables:
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * End:
 * vim: shiftwidth=4 tabstop=8 expandtab
 */

<commit_msg>Omit YLOG_LOG msg<commit_after>/*
 * Copyright (c) 1998-2004, Index Data.
 * See the file LICENSE for details.
 *
 * $Id: yaz-cql2rpn.cpp,v 1.12 2007-01-12 10:15:06 adam Exp $
 */

#include <yaz/log.h>
#include <yaz/pquery.h>
#include <yazpp/cql2rpn.h>

using namespace yazpp_1;

Yaz_cql2rpn::Yaz_cql2rpn()
{
    m_transform = 0;
}

Yaz_cql2rpn::~Yaz_cql2rpn()
{
    if (m_transform)
        cql_transform_close(m_transform);
}

void Yaz_cql2rpn::set_pqf_file(const char *fname)
{
    if (!m_transform)
        m_transform = cql_transform_open_fname(fname);
}

bool Yaz_cql2rpn::parse_spec_file(const char *fname, int *error)
{
    *error = 0;
    cql_transform_close(m_transform);
    m_transform = cql_transform_open_fname(fname);
    return m_transform ? true : false;
}

int Yaz_cql2rpn::query_transform(const char *cql_query,
                                 Z_RPNQuery **rpnquery, ODR o,
                                 char **addinfop)
{
    const char *addinfo = 0;
    if (!m_transform)
        return -3;
    CQL_parser cp = cql_parser_create();

    int r = cql_parser_string(cp, cql_query);
    if (r)
    {
        r = 10;
    }
    else
    {
        char rpn_buf[10240];
        r = cql_transform_buf(m_transform, cql_parser_result(cp),
                              rpn_buf, sizeof(rpn_buf)-1);
        if (!r)
        {
            YAZ_PQF_Parser pp = yaz_pqf_create();

            *rpnquery = yaz_pqf_parse(pp, o, rpn_buf);
            if (!*rpnquery)
            {
                size_t off;
                const char *pqf_msg;
                int code = yaz_pqf_error(pp, &pqf_msg, &off);
                yaz_log(YLOG_WARN, "PQF Parser Error %s (code %d)",
                        pqf_msg, code);
                r = -1;
            }
            yaz_pqf_destroy(pp);
        }
        else
        {
            r = cql_transform_error(m_transform, &addinfo);
            yaz_log(YLOG_LOG, "CQL Transform Error %d %s", r,
                    addinfo ? addinfo : "");
        }
    }
    cql_parser_destroy(cp);
    if (addinfo)
        *addinfop = odr_strdup(o, addinfo);
    else
        *addinfop = 0;
    return r;
}
/*
 * Local variables:
 * c-basic-offset: 4
 * indent-tabs-mode: nil
 * End:
 * vim: shiftwidth=4 tabstop=8 expandtab
 */

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "contexttyperegistryinfo.h"
#include "logging.h"
#include "loggingfeatures.h"
#include <QMutex>
#include <QMutexLocker>
#include <QCoreApplication>
#include "nanoxml.h"

ContextTypeRegistryInfo* ContextTypeRegistryInfo::registryInstance = NULL;

/* Public */

/// Returns the singleton instance of the ContextTypeRegistryInfo. The object
/// is constructed automaticall on first access.
ContextTypeRegistryInfo* ContextTypeRegistryInfo::instance()
{
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    if (! registryInstance) {
        contextDebug() << F_TYPES << "Creating ContextTypeRegistryInfo instance";
        registryInstance = new ContextTypeRegistryInfo;

        // Move the backend to the main thread
        registryInstance->moveToThread(QCoreApplication::instance()->thread());
    }

    return registryInstance;
}

/// Returns the full path to the registry directory. Takes the
/// \c CONTEXT_TYPES env variable into account.
QString ContextTypeRegistryInfo::registryPath()
{
    const char *regpath = getenv("CONTEXT_TYPES");
    if (! regpath)
        regpath = DEFAULT_CONTEXT_TYPES;

    return QString(regpath);
}

/// Returns the full path to the core property declaration file. Takes
/// the \c CONTEXT_CORE_TYPES env variable into account.
QString ContextTypeRegistryInfo::coreTypesPath()
{
    const char *corepath = getenv("CONTEXT_CORE_TYPES");
    if (! corepath)
        corepath = DEFAULT_CONTEXT_CORE_TYPES;

    return QString(corepath);
}

/// Returns a type definition for the type with the given name. The type
/// is being fetched from the registry.
NanoTree ContextTypeRegistryInfo::typeDefinitionForName(QString name)
{
    // Support for the old types
    if (name == "TRUTH")
        return boolType();
    else if (name == "STRING")
        return stringType();
    else if (name == "INT")
        return int32Type();
    else if (name == "INTEGER")
        return int32Type();
    else if (name == "DOUBLE")
        return doubleType();

    // Try using the cache first
    if (typeCache.contains(name))
        return typeCache.value(name);

    // No type in cache? Find it in the nano tree and put in cache.
    foreach (NanoTree typeTree, coreTree.keyValues("type")) {
        if (typeTree.keyValue("name") == name) {
            typeCache.insert(name, typeTree);
            return typeTree;
        }
    }

    // Not found. Return blank null type.
    return NanoTree();
}

/// Returns in instance of the int64 type definition.
NanoTree ContextTypeRegistryInfo::int64Type()
{
    return typeDefinitionForName("int64");
}

/// Returns in instance of the string type definition.
NanoTree ContextTypeRegistryInfo::stringType()
{
    return typeDefinitionForName("string");
}

/// Returns in instance of the double type definition.
NanoTree ContextTypeRegistryInfo::doubleType()
{
    return typeDefinitionForName("double");
}

/// Returns in instance of the bool type definition.
NanoTree ContextTypeRegistryInfo::boolType()
{
    return typeDefinitionForName("bool");
}

/// Returns in instance of the int32 type definition.
NanoTree ContextTypeRegistryInfo::int32Type()
{
    return typeDefinitionForName("int32");
}

/* Private */

/// Private constructor. Do not use.
ContextTypeRegistryInfo::ContextTypeRegistryInfo()
{
    if (QFile(ContextTypeRegistryInfo::coreTypesPath()).exists()) {
        contextDebug() << F_TYPES << "Reading core types from:" << ContextTypeRegistryInfo::coreTypesPath();

        NanoXml parser(ContextTypeRegistryInfo::coreTypesPath());
        if (parser.didFail())
            contextWarning() << F_TYPES << "Reading core types from" << ContextTypeRegistryInfo::coreTypesPath() << "failed, parsing error";
        else
            coreTree = parser.result();
    } else
        contextDebug() << F_TYPES << "Core types at" << ContextTypeRegistryInfo::coreTypesPath() << "missing.";
}
<commit_msg>Overview documentation for contexttyperegistryinfo.<commit_after>/*
 * Copyright (C) 2008 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "contexttyperegistryinfo.h"
#include "logging.h"
#include "loggingfeatures.h"
#include <QMutex>
#include <QMutexLocker>
#include <QCoreApplication>
#include "nanoxml.h"

/*!
    \class ContextTypeRegistryInfo

    \brief A class to access the type registry.

    This is a singelton class used to obtain information about the core types defined
    in the type registry. Information is provided as type definitions returned as
    NanoTree instances. Each type definition is a QVariant tree wrapped in NanoTree
    for easy helper key accessors.

    \section Usage

    To obtain a type definition for a given type:

    \code
    NanoTree typeDefinition = ContextTypeRegistryInfo::instance()->typeDefinitionForName("string-enum");
    \endcode

    Unless you're building a dedicated type-introspection application, you don't
    want to deal with ContextTypeRegistryInfo directly. Instead, you can use the ContextTypeInfo
    class to fetch concrete types and use the easy accessors provided there.
*/

ContextTypeRegistryInfo* ContextTypeRegistryInfo::registryInstance = NULL;

/* Public */

/// Returns the singleton instance of the ContextTypeRegistryInfo. The object
/// is constructed automaticall on first access.
ContextTypeRegistryInfo* ContextTypeRegistryInfo::instance()
{
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    if (! registryInstance) {
        contextDebug() << F_TYPES << "Creating ContextTypeRegistryInfo instance";
        registryInstance = new ContextTypeRegistryInfo;

        // Move the backend to the main thread
        registryInstance->moveToThread(QCoreApplication::instance()->thread());
    }

    return registryInstance;
}

/// Returns the full path to the registry directory. Takes the
/// \c CONTEXT_TYPES env variable into account.
QString ContextTypeRegistryInfo::registryPath()
{
    const char *regpath = getenv("CONTEXT_TYPES");
    if (! regpath)
        regpath = DEFAULT_CONTEXT_TYPES;

    return QString(regpath);
}

/// Returns the full path to the core property declaration file. Takes
/// the \c CONTEXT_CORE_TYPES env variable into account.
QString ContextTypeRegistryInfo::coreTypesPath()
{
    const char *corepath = getenv("CONTEXT_CORE_TYPES");
    if (! corepath)
        corepath = DEFAULT_CONTEXT_CORE_TYPES;

    return QString(corepath);
}

/// Returns a type definition for the type with the given name. The type
/// is being fetched from the registry.
NanoTree ContextTypeRegistryInfo::typeDefinitionForName(QString name)
{
    // Support for the old types
    if (name == "TRUTH")
        return boolType();
    else if (name == "STRING")
        return stringType();
    else if (name == "INT")
        return int32Type();
    else if (name == "INTEGER")
        return int32Type();
    else if (name == "DOUBLE")
        return doubleType();

    // Try using the cache first
    if (typeCache.contains(name))
        return typeCache.value(name);

    // No type in cache? Find it in the nano tree and put in cache.
    foreach (NanoTree typeTree, coreTree.keyValues("type")) {
        if (typeTree.keyValue("name") == name) {
            typeCache.insert(name, typeTree);
            return typeTree;
        }
    }

    // Not found. Return blank null type.
    return NanoTree();
}

/// Returns in instance of the int64 type definition.
NanoTree ContextTypeRegistryInfo::int64Type()
{
    return typeDefinitionForName("int64");
}

/// Returns in instance of the string type definition.
NanoTree ContextTypeRegistryInfo::stringType()
{
    return typeDefinitionForName("string");
}

/// Returns in instance of the double type definition.
NanoTree ContextTypeRegistryInfo::doubleType()
{
    return typeDefinitionForName("double");
}

/// Returns in instance of the bool type definition.
NanoTree ContextTypeRegistryInfo::boolType()
{
    return typeDefinitionForName("bool");
}

/// Returns in instance of the int32 type definition.
NanoTree ContextTypeRegistryInfo::int32Type()
{
    return typeDefinitionForName("int32");
}

/* Private */

/// Private constructor. Do not use.
ContextTypeRegistryInfo::ContextTypeRegistryInfo()
{
    if (QFile(ContextTypeRegistryInfo::coreTypesPath()).exists()) {
        contextDebug() << F_TYPES << "Reading core types from:" << ContextTypeRegistryInfo::coreTypesPath();

        NanoXml parser(ContextTypeRegistryInfo::coreTypesPath());
        if (parser.didFail())
            contextWarning() << F_TYPES << "Reading core types from" << ContextTypeRegistryInfo::coreTypesPath() << "failed, parsing error";
        else
            coreTree = parser.result();
    } else
        contextDebug() << F_TYPES << "Core types at" << ContextTypeRegistryInfo::coreTypesPath() << "missing.";
}
<|endoftext|>


<commit_before>// This file is part of SWGANH which is released under the MIT license.
// See file LICENSE or go to http://swganh.com/LICENSE

#include "event_dispatcher.h"

#include <algorithm>

#ifdef WIN32
#include <chrono>
using namespace std::chrono;
#else
#include <boost/chrono.hpp>
using namespace boost::chrono;
#endif

#include <boost/asio/io_service.hpp>

using namespace swganh;
using namespace std;

namespace ba = boost::asio;

BaseEvent::BaseEvent(EventType type)
: type_(type)
{}

EventType BaseEvent::Type() const
{
    return type_;
}

EventDispatcher::EventDispatcher(ba::io_service& io_service)
: io_service_(io_service)
{}

EventDispatcher::~EventDispatcher()
{
    Shutdown();
}

CallbackId EventDispatcher::Subscribe(EventType type, EventHandlerCallback callback)
{
    auto handler_id = GenerateCallbackId();

    boost::upgrade_lock<boost::shared_mutex> lg(event_handlers_mutex_);

    auto find_iter = event_handlers_.find(type);

    if (find_iter == end(event_handlers_))
    {
        find_iter = event_handlers_.insert(make_pair(type, EventHandlerList())).first;
    }

    find_iter->second.insert(std::make_pair(handler_id, move(callback)));

    return handler_id;
}

void EventDispatcher::Unsubscribe(EventType type, CallbackId identifier)
{
    boost::upgrade_lock<boost::shared_mutex> lg(event_handlers_mutex_);

    auto event_type_iter = event_handlers_.find(type);

    if (event_type_iter != end(event_handlers_))
    {
        auto type_handlers = event_type_iter->second;
        type_handlers.erase(identifier);
        type_handlers.clear();
    }
}

boost::unique_future<shared_ptr<EventInterface>> EventDispatcher::Dispatch(const shared_ptr<EventInterface>& dispatch_event)
{
    auto task = make_shared<boost::packaged_task<shared_ptr<EventInterface>>>(
        [this, dispatch_event] () -> shared_ptr<EventInterface>
    {
        InvokeCallbacks(dispatch_event);

        return dispatch_event;
    });

    io_service_.post([task] () {
        (*task)();
    });

    return task->get_future();
}

CallbackId EventDispatcher::GenerateCallbackId()
{
    return CallbackId(steady_clock::now().time_since_epoch().count());
}

void EventDispatcher::InvokeCallbacks(const shared_ptr<EventInterface>& dispatch_event)
{
    boost::shared_lock<boost::shared_mutex> lg(event_handlers_mutex_);

    auto event_type_iter = event_handlers_.find(dispatch_event->Type());

    if (event_type_iter != end(event_handlers_))
    {
        for_each(
            begin(event_type_iter->second),
            end(event_type_iter->second),
            [&dispatch_event] (const EventHandlerList::value_type& handler)
        {
            handler.second(dispatch_event);
        });
    }
}

void EventDispatcher::Shutdown()
{
	boost::lock_guard<boost::shared_mutex> lg(event_handlers_mutex_);
    for (auto& item : event_handlers_)
    {
        item.second.clear();
    }
    event_handlers_.clear();
}<commit_msg>Handlers are now wrapped in a try-catch<commit_after>// This file is part of SWGANH which is released under the MIT license.
// See file LICENSE or go to http://swganh.com/LICENSE

#include "event_dispatcher.h"
#include "logger.h"

#include <algorithm>

#ifdef WIN32
#include <chrono>
using namespace std::chrono;
#else
#include <boost/chrono.hpp>
using namespace boost::chrono;
#endif

#include <boost/asio/io_service.hpp>

using namespace swganh;
using namespace std;

namespace ba = boost::asio;

BaseEvent::BaseEvent(EventType type)
: type_(type)
{}

EventType BaseEvent::Type() const
{
    return type_;
}

EventDispatcher::EventDispatcher(ba::io_service& io_service)
: io_service_(io_service)
{}

EventDispatcher::~EventDispatcher()
{
    Shutdown();
}

CallbackId EventDispatcher::Subscribe(EventType type, EventHandlerCallback callback)
{
    auto handler_id = GenerateCallbackId();

    boost::upgrade_lock<boost::shared_mutex> lg(event_handlers_mutex_);

    auto find_iter = event_handlers_.find(type);

    if (find_iter == end(event_handlers_))
    {
        find_iter = event_handlers_.insert(make_pair(type, EventHandlerList())).first;
    }

    find_iter->second.insert(std::make_pair(handler_id, move(callback)));

    return handler_id;
}

void EventDispatcher::Unsubscribe(EventType type, CallbackId identifier)
{
    boost::upgrade_lock<boost::shared_mutex> lg(event_handlers_mutex_);

    auto event_type_iter = event_handlers_.find(type);

    if (event_type_iter != end(event_handlers_))
    {
        auto type_handlers = event_type_iter->second;
        type_handlers.erase(identifier);
        type_handlers.clear();
    }
}

boost::unique_future<shared_ptr<EventInterface>> EventDispatcher::Dispatch(const shared_ptr<EventInterface>& dispatch_event)
{
    auto task = make_shared<boost::packaged_task<shared_ptr<EventInterface>>>(
        [this, dispatch_event] () -> shared_ptr<EventInterface>
    {
        InvokeCallbacks(dispatch_event);

        return dispatch_event;
    });

    io_service_.post([task] () {
        (*task)();
    });

    return task->get_future();
}

CallbackId EventDispatcher::GenerateCallbackId()
{
    return CallbackId(steady_clock::now().time_since_epoch().count());
}

void EventDispatcher::InvokeCallbacks(const shared_ptr<EventInterface>& dispatch_event)
{
    boost::shared_lock<boost::shared_mutex> lg(event_handlers_mutex_);

    auto event_type_iter = event_handlers_.find(dispatch_event->Type());

    if (event_type_iter != end(event_handlers_))
    {
        for_each(
            begin(event_type_iter->second),
            end(event_type_iter->second),
            [&dispatch_event] (const EventHandlerList::value_type& handler)
        {
            try
            {
                handler.second(dispatch_event);
            }
            catch(...)
            {
            	DLOG(warning) << "A handler callback caused an exception.";
            }
        });
    }
}

void EventDispatcher::Shutdown()
{
	boost::lock_guard<boost::shared_mutex> lg(event_handlers_mutex_);
    for (auto& item : event_handlers_)
    {
        item.second.clear();
    }
    event_handlers_.clear();
}
<|endoftext|>


<commit_before>/*
 * UIRenderSystem.cpp
 *
 * Created on: 14 Sep 2014
 * Author: Ashley Davis (SgtCoDFish)
 */

#include <string>
#include <algorithm>
#include <bitset>

#include "SDL2/SDL.h"

#include "Ashley/AshleyCore.hpp"

#include "StinkingRich.hpp"
#include "StinkingRichConstants.hpp"
#include "components/YesNoQuery.hpp"
#include "components/MouseListener.hpp"
#include "systems/UIRenderSystem.hpp"

using namespace stinkingRich;

stinkingRich::UIRenderSystem::UIRenderSystem(int priority) :
		UIRenderSystem(nullptr, priority) {
}

stinkingRich::UIRenderSystem::UIRenderSystem(SDL_Renderer *renderer, int priority) :
		ashley::IteratingSystem(
				ashley::Family::getFor(
						ashley::BitsType(ashley::ComponentType::getBitsFor<YesNoQuery>()),
						ashley::BitsType(), ashley::BitsType()), priority), renderer(renderer) {

	const int boxW = static_cast<int>(constants::boardWidth * 0.75);
	const int boxWDiff = (constants::boardWidth - boxW) / 2;

	const int boxH = static_cast<int>(constants::boardHeight * 0.75);
	const int boxHDiff = (constants::boardHeight - boxH) / 2;

	const SDL_Rect mainBox = { StinkingRich::leftGap + boxWDiff, StinkingRich::topGap + boxHDiff,
			boxW, boxH };

	boxes.push_back(mainBox);

	constexpr int optionW = 100;
	const int optionY = static_cast<int>(mainBox.h * 0.75) + mainBox.y;
	const int gapW = (boxW - (2 * optionW)) / 3;

	const SDL_Rect yesBox = { mainBox.x + gapW, optionY, optionW, 50 };
	const SDL_Rect noBox = { mainBox.x + gapW * 2 + optionW, optionY, optionW, 50 };

	boxes.push_back(yesBox);
	boxes.push_back(noBox);

	const auto textRenderer = stinkingRich::StinkingRich::getTextRenderer();

	textRenderer->setColor({0x00, 0x00, 0x00, 0xFF});

	yesTexture = textRenderer->renderToTexture(renderer, 0, 0, yesBox.w, yesBox.h, "Yes", yesBox.w);
	noTexture = textRenderer->renderToTexture(renderer, 0, 0, noBox.w, noBox.h, "No", noBox.w);

	constexpr int queryOffsetX = 10;
	constexpr int queryOffsetY = 10;
	const int queryBoxW = mainBox.w - (2 * queryOffsetX);
	const int queryBoxH = static_cast<int>(mainBox.h * 0.25);

	const SDL_Rect queryBox = { mainBox.x + queryOffsetX, mainBox.y + queryOffsetY, queryBoxW,
			queryBoxH };

	boxes.push_back(queryBox);
}

stinkingRich::UIRenderSystem::~UIRenderSystem() {
	if (addedUIEntities.size() > 0) {
		removeUIEntity();
	}

	if (queryTexture != nullptr) {
		SDL_DestroyTexture(queryTexture);
	}

	if (yesTexture != nullptr) {
		SDL_DestroyTexture(yesTexture);
	}

	if (noTexture != nullptr) {
		SDL_DestroyTexture(noTexture);
	}
}

void stinkingRich::UIRenderSystem::processEntity(std::shared_ptr<ashley::Entity> &entity,
		float deltaTime) {
	auto &text =
			ashley::ComponentMapper<stinkingRich::YesNoQuery>::getMapper().get(entity)->question;

	SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
	SDL_RenderFillRects(renderer, boxes.data(), boxes.size());

	SDL_RenderCopy(renderer, yesTexture, nullptr, &boxes[1]);
	SDL_RenderCopy(renderer, noTexture, nullptr, &boxes[2]);
	SDL_RenderCopy(renderer, queryTexture, nullptr, &boxes[3]);

	SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
	SDL_RenderDrawRects(renderer, boxes.data(), boxes.size());

}

void stinkingRich::UIRenderSystem::addUIEntity(std::shared_ptr<ashley::Entity>& e) {
	if (engine == nullptr) {
		std::cerr << "Call to addQuery with invalid engine.\n";
		return;
	}

	if (addedUIEntities.size() > 0) {
		std::cerr << "Trying to add new ui component when one already exists.\n";
		return;
	}

	engine->addEntity(e);
	auto yes = std::make_shared<ashley::Entity>();
	auto no = std::make_shared<ashley::Entity>();

	yes->add<stinkingRich::MouseListener>(boxes[1], stinkingRich::MouseMessage::YES);
	no->add<stinkingRich::MouseListener>(boxes[2], stinkingRich::MouseMessage::NO);

	engine->addEntity(yes);
	engine->addEntity(no);

	addedUIEntities.emplace_back(std::shared_ptr<ashley::Entity>(e));
	addedUIEntities.emplace_back(std::shared_ptr<ashley::Entity>(yes));
	addedUIEntities.emplace_back(std::shared_ptr<ashley::Entity>(no));

	auto queryText = std::string(ashley::ComponentMapper<YesNoQuery>::getMapper().get(e)->question);

	queryText.erase(std::remove(queryText.begin(), queryText.end(), '\n'), queryText.end());

	const auto textRenderer = stinkingRich::StinkingRich::getTextRenderer();
	textRenderer->setColor({0x00, 0x00, 0x00, 0xFF});
	queryTexture = textRenderer->renderToTexture(renderer, 0, 0, boxes[3].w, boxes[3].h, queryText, boxes[3].w);
}

void stinkingRich::UIRenderSystem::removeUIEntity() {
	if (addedUIEntities.size() == 0) {
		std::cerr << "Trying to remove UI entity when none exists.\n";
		return;
	}

	for (const auto &e : addedUIEntities) {
		engine->removeEntity(e);
	}

	addedUIEntities.clear();

	if(queryTexture != nullptr) {
		SDL_DestroyTexture(queryTexture);
	}
}

void stinkingRich::UIRenderSystem::setRenderer(SDL_Renderer *renderer) {
	this->renderer = renderer;
}

void stinkingRich::UIRenderSystem::addedToEngine(ashley::Engine &engine) {
	ashley::IteratingSystem::addedToEngine(engine);

	this->engine = &engine;
}

void stinkingRich::UIRenderSystem::removedFromEngine(ashley::Engine &engine) {
	ashley::IteratingSystem::addedToEngine(engine);

	this->engine = nullptr;
}
<commit_msg>Update to new version of AshleyCPP<commit_after>/*
 * UIRenderSystem.cpp
 *
 * Created on: 14 Sep 2014
 * Author: Ashley Davis (SgtCoDFish)
 */

#include <string>
#include <algorithm>
#include <bitset>

#include "SDL2/SDL.h"

#include "Ashley/AshleyCore.hpp"

#include "StinkingRich.hpp"
#include "StinkingRichConstants.hpp"
#include "components/YesNoQuery.hpp"
#include "components/MouseListener.hpp"
#include "systems/UIRenderSystem.hpp"

using namespace stinkingRich;

stinkingRich::UIRenderSystem::UIRenderSystem(int priority) :
		UIRenderSystem(nullptr, priority) {
}

stinkingRich::UIRenderSystem::UIRenderSystem(SDL_Renderer *renderer, int priority) :
		ashley::IteratingSystem(
				ashley::Family::getFor(
						ashley::BitsType(ashley::ComponentType::getBitsFor<YesNoQuery>()),
						ashley::BitsType(), ashley::BitsType()), priority), renderer(renderer) {

	const int boxW = static_cast<int>(constants::boardWidth * 0.75);
	const int boxWDiff = (constants::boardWidth - boxW) / 2;

	const int boxH = static_cast<int>(constants::boardHeight * 0.75);
	const int boxHDiff = (constants::boardHeight - boxH) / 2;

	const SDL_Rect mainBox = { StinkingRich::leftGap + boxWDiff, StinkingRich::topGap + boxHDiff,
			boxW, boxH };

	boxes.push_back(mainBox);

	constexpr int optionW = 100;
	const int optionY = static_cast<int>(mainBox.h * 0.75) + mainBox.y;
	const int gapW = (boxW - (2 * optionW)) / 3;

	const SDL_Rect yesBox = { mainBox.x + gapW, optionY, optionW, 50 };
	const SDL_Rect noBox = { mainBox.x + gapW * 2 + optionW, optionY, optionW, 50 };

	boxes.push_back(yesBox);
	boxes.push_back(noBox);

	const auto textRenderer = stinkingRich::StinkingRich::getTextRenderer();

	textRenderer->setColor({0x00, 0x00, 0x00, 0xFF});

	yesTexture = textRenderer->renderToTexture(renderer, 0, 0, yesBox.w, yesBox.h, "Yes", yesBox.w);
	noTexture = textRenderer->renderToTexture(renderer, 0, 0, noBox.w, noBox.h, "No", noBox.w);

	constexpr int queryOffsetX = 10;
	constexpr int queryOffsetY = 10;
	const int queryBoxW = mainBox.w - (2 * queryOffsetX);
	const int queryBoxH = static_cast<int>(mainBox.h * 0.25);

	const SDL_Rect queryBox = { mainBox.x + queryOffsetX, mainBox.y + queryOffsetY, queryBoxW,
			queryBoxH };

	boxes.push_back(queryBox);
}

stinkingRich::UIRenderSystem::~UIRenderSystem() {
	if (addedUIEntities.size() > 0) {
		removeUIEntity();
	}

	if (queryTexture != nullptr) {
		SDL_DestroyTexture(queryTexture);
	}

	if (yesTexture != nullptr) {
		SDL_DestroyTexture(yesTexture);
	}

	if (noTexture != nullptr) {
		SDL_DestroyTexture(noTexture);
	}
}

void stinkingRich::UIRenderSystem::processEntity(std::shared_ptr<ashley::Entity> &entity,
		float deltaTime) {
	auto &text =
			ashley::ComponentMapper<stinkingRich::YesNoQuery>::getMapper().get(entity)->question;

	SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF);
	SDL_RenderFillRects(renderer, boxes.data(), boxes.size());

	SDL_RenderCopy(renderer, yesTexture, nullptr, &boxes[1]);
	SDL_RenderCopy(renderer, noTexture, nullptr, &boxes[2]);
	SDL_RenderCopy(renderer, queryTexture, nullptr, &boxes[3]);

	SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0xFF);
	SDL_RenderDrawRects(renderer, boxes.data(), boxes.size());

}

void stinkingRich::UIRenderSystem::addUIEntity(std::shared_ptr<ashley::Entity>& e) {
	if (engine == nullptr) {
		std::cerr << "Call to addQuery with invalid engine.\n";
		return;
	}

	if (addedUIEntities.size() > 0) {
		std::cerr << "Trying to add new ui component when one already exists.\n";
		return;
	}

	engine->addEntity(e);
	auto yes = std::make_shared<ashley::Entity>();
	auto no = std::make_shared<ashley::Entity>();

	yes->add<stinkingRich::MouseListener>(boxes[1], stinkingRich::MouseMessage::YES);
	no->add<stinkingRich::MouseListener>(boxes[2], stinkingRich::MouseMessage::NO);

	engine->addEntity(yes);
	engine->addEntity(no);

	addedUIEntities.emplace_back(std::shared_ptr<ashley::Entity>(e));
	addedUIEntities.emplace_back(std::shared_ptr<ashley::Entity>(yes));
	addedUIEntities.emplace_back(std::shared_ptr<ashley::Entity>(no));

	auto queryText = std::string(ashley::ComponentMapper<YesNoQuery>::getMapper().get(e)->question);

	queryText.erase(std::remove(queryText.begin(), queryText.end(), '\n'), queryText.end());

	const auto textRenderer = stinkingRich::StinkingRich::getTextRenderer();
	textRenderer->setColor({0x00, 0x00, 0x00, 0xFF});
	queryTexture = textRenderer->renderToTexture(renderer, 0, 0, boxes[3].w, boxes[3].h, queryText, boxes[3].w);
}

void stinkingRich::UIRenderSystem::removeUIEntity() {
	if (addedUIEntities.size() == 0) {
		std::cerr << "Trying to remove UI entity when none exists.\n";
		return;
	}

	for (auto &e : addedUIEntities) {
		engine->removeEntity(e);
	}

	addedUIEntities.clear();

	if(queryTexture != nullptr) {
		SDL_DestroyTexture(queryTexture);
	}
}

void stinkingRich::UIRenderSystem::setRenderer(SDL_Renderer *renderer) {
	this->renderer = renderer;
}

void stinkingRich::UIRenderSystem::addedToEngine(ashley::Engine &engine) {
	ashley::IteratingSystem::addedToEngine(engine);

	this->engine = &engine;
}

void stinkingRich::UIRenderSystem::removedFromEngine(ashley::Engine &engine) {
	ashley::IteratingSystem::addedToEngine(engine);

	this->engine = nullptr;
}
<|endoftext|>


<commit_before>#pragma once

#include <eosio/chain/transaction_metadata.hpp>
#include <eosio/chain/trace.hpp>
#include <eosio/chain/block_state.hpp>
#include <eosio/chain/exceptions.hpp>

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>

namespace fc {
  inline std::size_t hash_value( const fc::sha256& v ) {
     return v._hash[3];
  }
}

namespace eosio { namespace chain {

using namespace boost::multi_index;

enum class trx_enum_type {
   unknown = 0,
   persisted = 1,
   forked = 2,
   aborted = 3,
   incoming_persisted = 4,
   incoming = 5 // incoming_end() needs to be updated if this changes
};

using next_func_t = std::function<void(const fc::static_variant<fc::exception_ptr, transaction_trace_ptr>&)>;

struct unapplied_transaction {
   const transaction_metadata_ptr trx_meta;
   const fc::time_point           expiry;
   trx_enum_type                  trx_type = trx_enum_type::unknown;
   next_func_t                    next;

   const transaction_id_type& id()const { return trx_meta->id(); }

   unapplied_transaction(const unapplied_transaction&) = delete;
   unapplied_transaction() = delete;
   unapplied_transaction& operator=(const unapplied_transaction&) = delete;
   unapplied_transaction(unapplied_transaction&&) = default;
};

/**
 * Track unapplied transactions for persisted, forked blocks, and aborted blocks.
 * Persisted are first so that they can be applied in each block until expired.
 */
class unapplied_transaction_queue {
public:
   enum class process_mode {
      non_speculative,           // HEAD, READ_ONLY, IRREVERSIBLE
      speculative_non_producer,  // will never produce
      speculative_producer       // can produce
   };

private:
   struct by_trx_id;
   struct by_type;
   struct by_expiry;

   typedef multi_index_container< unapplied_transaction,
      indexed_by<
         hashed_unique< tag<by_trx_id>,
               const_mem_fun<unapplied_transaction, const transaction_id_type&, &unapplied_transaction::id>
         >,
         ordered_non_unique< tag<by_type>, member<unapplied_transaction, trx_enum_type, &unapplied_transaction::trx_type> >,
         ordered_non_unique< tag<by_expiry>, member<unapplied_transaction, const fc::time_point, &unapplied_transaction::expiry> >
      >
   > unapplied_trx_queue_type;

   unapplied_trx_queue_type queue;
   process_mode mode = process_mode::speculative_producer;
   uint64_t max_transaction_queue_size = 1024*1024*1024; // enforced for incoming
   uint64_t size_in_bytes = 0;
   size_t incoming_count = 0;

public:

   void set_max_transaction_queue_size( uint64_t v ) { max_transaction_queue_size = v; }

   void set_mode( process_mode new_mode ) {
      if( new_mode != mode ) {
         FC_ASSERT( empty(), "set_mode, queue required to be empty" );
      }
      mode = new_mode;
   }

   bool empty() const {
      return queue.empty();
   }

   size_t size() const {
      return queue.size();
   }

   void clear() {
      queue.clear();
   }

   size_t incoming_size()const {
      return incoming_count;
   }

   transaction_metadata_ptr get_trx( const transaction_id_type& id ) const {
      auto itr = queue.get<by_trx_id>().find( id );
      if( itr == queue.get<by_trx_id>().end() ) return {};
      return itr->trx_meta;
   }

   template <typename Func>
   bool clear_expired( const time_point& pending_block_time, const time_point& deadline, Func&& callback ) {
      auto& persisted_by_expiry = queue.get<by_expiry>();
      while( !persisted_by_expiry.empty() ) {
         const auto& itr = persisted_by_expiry.begin();
         if( itr->expiry > pending_block_time ) {
            break;
         }
         if( deadline <= fc::time_point::now() ) {
            return false;
         }
         callback( itr->id(), itr->trx_type );
         if( itr->next ) {
            itr->next( std::static_pointer_cast<fc::exception>(
                  std::make_shared<expired_tx_exception>(
                        FC_LOG_MESSAGE( error, "expired transaction ${id}, expiration ${e}, block time ${bt}",
                                        ("id", itr->id())("e", itr->trx_meta->packed_trx()->expiration())
                                        ("bt", pending_block_time) ) ) ) );
         }

         removed( itr );
         persisted_by_expiry.erase( itr );
      }
      return true;
   }

   void clear_applied( const block_state_ptr& bs ) {
      if( empty() ) return;
      auto& idx = queue.get<by_trx_id>();
      for( const auto& receipt : bs->block->transactions ) {
         if( receipt.trx.contains<packed_transaction>() ) {
            const auto& pt = receipt.trx.get<packed_transaction>();
            auto itr = queue.get<by_trx_id>().find( pt.id() );
            if( itr != queue.get<by_trx_id>().end() ) {
               if( itr->trx_type != trx_enum_type::persisted &&
                   itr->trx_type != trx_enum_type::incoming_persisted ) {
                  removed( itr );
                  idx.erase( itr );
               }
            }
         }
      }
   }

   void add_forked( const branch_type& forked_branch ) {
      if( mode == process_mode::non_speculative || mode == process_mode::speculative_non_producer ) return;
      // forked_branch is in reverse order
      for( auto ritr = forked_branch.rbegin(), rend = forked_branch.rend(); ritr != rend; ++ritr ) {
         const block_state_ptr& bsptr = *ritr;
         for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) {
            const auto& trx = *itr;
            fc::time_point expiry = trx->packed_trx()->expiration();
            auto insert_itr = queue.insert( { trx, expiry, trx_enum_type::forked } );
            if( insert_itr.second ) added( insert_itr.first );
         }
      }
   }

   void add_aborted( std::vector<transaction_metadata_ptr> aborted_trxs ) {
      if( mode == process_mode::non_speculative || mode == process_mode::speculative_non_producer ) return;
      for( auto& trx : aborted_trxs ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         auto insert_itr = queue.insert( { std::move( trx ), expiry, trx_enum_type::aborted } );
         if( insert_itr.second ) added( insert_itr.first );
      }
   }

   void add_persisted( const transaction_metadata_ptr& trx ) {
      if( mode == process_mode::non_speculative ) return;
      auto itr = queue.get<by_trx_id>().find( trx->id() );
      if( itr == queue.get<by_trx_id>().end() ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         auto insert_itr = queue.insert( { trx, expiry, trx_enum_type::persisted } );
         if( insert_itr.second ) added( insert_itr.first );
      } else if( itr->trx_type != trx_enum_type::persisted ) {
         queue.get<by_trx_id>().modify( itr, [](auto& un){
            un.trx_type = trx_enum_type::persisted;
         } );
      }
   }

   void add_incoming( const transaction_metadata_ptr& trx, bool persist_until_expired, next_func_t next ) {
      auto itr = queue.get<by_trx_id>().find( trx->id() );
      if( itr == queue.get<by_trx_id>().end() ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         auto insert_itr = queue.insert(
               { trx, expiry, persist_until_expired ? trx_enum_type::incoming_persisted : trx_enum_type::incoming, std::move( next ) } );
         if( insert_itr.second ) added( insert_itr.first );
      } else {
         queue.get<by_trx_id>().modify( itr, [persist_until_expired, next{std::move(next)}](auto& un) mutable {
            un.trx_type = persist_until_expired ? trx_enum_type::incoming_persisted : trx_enum_type::incoming;
            un.next = std::move( next );
         } );
      }
   }

   using iterator = unapplied_trx_queue_type::index<by_type>::type::iterator;

   iterator begin() { return queue.get<by_type>().begin(); }
   iterator end() { return queue.get<by_type>().end(); }

   // persisted, forked, aborted
   iterator unapplied_begin() { return queue.get<by_type>().begin(); }
   iterator unapplied_end() { return queue.get<by_type>().upper_bound( trx_enum_type::aborted ); }

   iterator persisted_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::persisted ); }
   iterator persisted_end() { return queue.get<by_type>().upper_bound( trx_enum_type::persisted ); }

   iterator incoming_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::incoming_persisted ); }
   iterator incoming_end() { return queue.get<by_type>().end(); } // if changed to upper_bound, verify usage performance

   iterator erase( iterator itr ) {
      removed( itr );
      return queue.get<by_type>().erase( itr );
   }

private:
   template<typename Itr>
   void added( Itr itr ) {
      auto size = calc_size( itr->trx_meta );
      if( itr->trx_type == trx_enum_type::incoming || itr->trx_type == trx_enum_type::incoming_persisted ) {
         ++incoming_count;
         EOS_ASSERT( size_in_bytes + size < max_transaction_queue_size, tx_resource_exhaustion,
                     "Transaction ${id}, size ${s} bytes would exceed configured "
                     "incoming-transaction-queue-size-mb ${qs}, current queue size ${cs} bytes",
                     ("id", itr->trx_meta->id())("s", size)("qs", max_transaction_queue_size/(1024*1024))
                     ("cs", size_in_bytes) );
      }
      size_in_bytes += size;
   }

   template<typename Itr>
   void removed( Itr itr ) {
      if( itr->trx_type == trx_enum_type::incoming || itr->trx_type == trx_enum_type::incoming_persisted ) {
         --incoming_count;
      }
      size_in_bytes -= calc_size( itr->trx_meta );
   }

   static uint64_t calc_size( const transaction_metadata_ptr& trx ) {
      // packed_trx caches unpacked transaction so double
      return (trx->packed_trx()->get_unprunable_size() + trx->packed_trx()->get_prunable_size()) * 2 + sizeof( *trx );
   }

};

} } //eosio::chain
<commit_msg>Always call next() since net_plugin needs callback for tracking transactions in progress<commit_after>#pragma once

#include <eosio/chain/transaction_metadata.hpp>
#include <eosio/chain/trace.hpp>
#include <eosio/chain/block_state.hpp>
#include <eosio/chain/exceptions.hpp>

#include <boost/multi_index_container.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>

namespace fc {
  inline std::size_t hash_value( const fc::sha256& v ) {
     return v._hash[3];
  }
}

namespace eosio { namespace chain {

using namespace boost::multi_index;

enum class trx_enum_type {
   unknown = 0,
   persisted = 1,
   forked = 2,
   aborted = 3,
   incoming_persisted = 4,
   incoming = 5 // incoming_end() needs to be updated if this changes
};

using next_func_t = std::function<void(const fc::static_variant<fc::exception_ptr, transaction_trace_ptr>&)>;

struct unapplied_transaction {
   const transaction_metadata_ptr trx_meta;
   const fc::time_point           expiry;
   trx_enum_type                  trx_type = trx_enum_type::unknown;
   next_func_t                    next;

   const transaction_id_type& id()const { return trx_meta->id(); }

   unapplied_transaction(const unapplied_transaction&) = delete;
   unapplied_transaction() = delete;
   unapplied_transaction& operator=(const unapplied_transaction&) = delete;
   unapplied_transaction(unapplied_transaction&&) = default;
};

/**
 * Track unapplied transactions for persisted, forked blocks, and aborted blocks.
 * Persisted are first so that they can be applied in each block until expired.
 */
class unapplied_transaction_queue {
public:
   enum class process_mode {
      non_speculative,           // HEAD, READ_ONLY, IRREVERSIBLE
      speculative_non_producer,  // will never produce
      speculative_producer       // can produce
   };

private:
   struct by_trx_id;
   struct by_type;
   struct by_expiry;

   typedef multi_index_container< unapplied_transaction,
      indexed_by<
         hashed_unique< tag<by_trx_id>,
               const_mem_fun<unapplied_transaction, const transaction_id_type&, &unapplied_transaction::id>
         >,
         ordered_non_unique< tag<by_type>, member<unapplied_transaction, trx_enum_type, &unapplied_transaction::trx_type> >,
         ordered_non_unique< tag<by_expiry>, member<unapplied_transaction, const fc::time_point, &unapplied_transaction::expiry> >
      >
   > unapplied_trx_queue_type;

   unapplied_trx_queue_type queue;
   process_mode mode = process_mode::speculative_producer;
   uint64_t max_transaction_queue_size = 1024*1024*1024; // enforced for incoming
   uint64_t size_in_bytes = 0;
   size_t incoming_count = 0;

public:

   void set_max_transaction_queue_size( uint64_t v ) { max_transaction_queue_size = v; }

   void set_mode( process_mode new_mode ) {
      if( new_mode != mode ) {
         FC_ASSERT( empty(), "set_mode, queue required to be empty" );
      }
      mode = new_mode;
   }

   bool empty() const {
      return queue.empty();
   }

   size_t size() const {
      return queue.size();
   }

   void clear() {
      queue.clear();
   }

   size_t incoming_size()const {
      return incoming_count;
   }

   transaction_metadata_ptr get_trx( const transaction_id_type& id ) const {
      auto itr = queue.get<by_trx_id>().find( id );
      if( itr == queue.get<by_trx_id>().end() ) return {};
      return itr->trx_meta;
   }

   template <typename Func>
   bool clear_expired( const time_point& pending_block_time, const time_point& deadline, Func&& callback ) {
      auto& persisted_by_expiry = queue.get<by_expiry>();
      while( !persisted_by_expiry.empty() ) {
         const auto& itr = persisted_by_expiry.begin();
         if( itr->expiry > pending_block_time ) {
            break;
         }
         if( deadline <= fc::time_point::now() ) {
            return false;
         }
         callback( itr->id(), itr->trx_type );
         if( itr->next ) {
            itr->next( std::static_pointer_cast<fc::exception>(
                  std::make_shared<expired_tx_exception>(
                        FC_LOG_MESSAGE( error, "expired transaction ${id}, expiration ${e}, block time ${bt}",
                                        ("id", itr->id())("e", itr->trx_meta->packed_trx()->expiration())
                                        ("bt", pending_block_time) ) ) ) );
         }

         removed( itr, false );
         persisted_by_expiry.erase( itr );
      }
      return true;
   }

   void clear_applied( const block_state_ptr& bs ) {
      if( empty() ) return;
      auto& idx = queue.get<by_trx_id>();
      for( const auto& receipt : bs->block->transactions ) {
         if( receipt.trx.contains<packed_transaction>() ) {
            const auto& pt = receipt.trx.get<packed_transaction>();
            auto itr = queue.get<by_trx_id>().find( pt.id() );
            if( itr != queue.get<by_trx_id>().end() ) {
               if( itr->trx_type != trx_enum_type::persisted &&
                   itr->trx_type != trx_enum_type::incoming_persisted ) {
                  removed( itr );
                  idx.erase( itr );
               }
            }
         }
      }
   }

   void add_forked( const branch_type& forked_branch ) {
      if( mode == process_mode::non_speculative || mode == process_mode::speculative_non_producer ) return;
      // forked_branch is in reverse order
      for( auto ritr = forked_branch.rbegin(), rend = forked_branch.rend(); ritr != rend; ++ritr ) {
         const block_state_ptr& bsptr = *ritr;
         for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) {
            const auto& trx = *itr;
            fc::time_point expiry = trx->packed_trx()->expiration();
            auto insert_itr = queue.insert( { trx, expiry, trx_enum_type::forked } );
            if( insert_itr.second ) added( insert_itr.first );
         }
      }
   }

   void add_aborted( std::vector<transaction_metadata_ptr> aborted_trxs ) {
      if( mode == process_mode::non_speculative || mode == process_mode::speculative_non_producer ) return;
      for( auto& trx : aborted_trxs ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         auto insert_itr = queue.insert( { std::move( trx ), expiry, trx_enum_type::aborted } );
         if( insert_itr.second ) added( insert_itr.first );
      }
   }

   void add_persisted( const transaction_metadata_ptr& trx ) {
      if( mode == process_mode::non_speculative ) return;
      auto itr = queue.get<by_trx_id>().find( trx->id() );
      if( itr == queue.get<by_trx_id>().end() ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         auto insert_itr = queue.insert( { trx, expiry, trx_enum_type::persisted } );
         if( insert_itr.second ) added( insert_itr.first );
      } else if( itr->trx_type != trx_enum_type::persisted ) {
         queue.get<by_trx_id>().modify( itr, [](auto& un){
            un.trx_type = trx_enum_type::persisted;
         } );
      }
   }

   void add_incoming( const transaction_metadata_ptr& trx, bool persist_until_expired, next_func_t next ) {
      auto itr = queue.get<by_trx_id>().find( trx->id() );
      if( itr == queue.get<by_trx_id>().end() ) {
         fc::time_point expiry = trx->packed_trx()->expiration();
         auto insert_itr = queue.insert(
               { trx, expiry, persist_until_expired ? trx_enum_type::incoming_persisted : trx_enum_type::incoming, std::move( next ) } );
         if( insert_itr.second ) added( insert_itr.first );
      } else {
         queue.get<by_trx_id>().modify( itr, [persist_until_expired, next{std::move(next)}](auto& un) mutable {
            un.trx_type = persist_until_expired ? trx_enum_type::incoming_persisted : trx_enum_type::incoming;
            un.next = std::move( next );
         } );
      }
   }

   using iterator = unapplied_trx_queue_type::index<by_type>::type::iterator;

   iterator begin() { return queue.get<by_type>().begin(); }
   iterator end() { return queue.get<by_type>().end(); }

   // persisted, forked, aborted
   iterator unapplied_begin() { return queue.get<by_type>().begin(); }
   iterator unapplied_end() { return queue.get<by_type>().upper_bound( trx_enum_type::aborted ); }

   iterator persisted_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::persisted ); }
   iterator persisted_end() { return queue.get<by_type>().upper_bound( trx_enum_type::persisted ); }

   iterator incoming_begin() { return queue.get<by_type>().lower_bound( trx_enum_type::incoming_persisted ); }
   iterator incoming_end() { return queue.get<by_type>().end(); } // if changed to upper_bound, verify usage performance

   /// callers responsibilty to call next() if applicable
   iterator erase( iterator itr ) {
      removed( itr, false );
      return queue.get<by_type>().erase( itr );
   }

private:
   template<typename Itr>
   void added( Itr itr ) {
      auto size = calc_size( itr->trx_meta );
      if( itr->trx_type == trx_enum_type::incoming || itr->trx_type == trx_enum_type::incoming_persisted ) {
         ++incoming_count;
         EOS_ASSERT( size_in_bytes + size < max_transaction_queue_size, tx_resource_exhaustion,
                     "Transaction ${id}, size ${s} bytes would exceed configured "
                     "incoming-transaction-queue-size-mb ${qs}, current queue size ${cs} bytes",
                     ("id", itr->trx_meta->id())("s", size)("qs", max_transaction_queue_size/(1024*1024))
                     ("cs", size_in_bytes) );
      }
      size_in_bytes += size;
   }

   template<typename Itr>
   void removed( Itr itr, bool call_next = true ) {
      if( itr->trx_type == trx_enum_type::incoming || itr->trx_type == trx_enum_type::incoming_persisted ) {
         --incoming_count;
      }
      if( call_next && itr->next ) {
         transaction_trace_ptr trace = std::make_shared<transaction_trace>();
         trace->id = itr->trx_meta->id();
         itr->next( trace );
      }
      size_in_bytes -= calc_size( itr->trx_meta );
   }

   static uint64_t calc_size( const transaction_metadata_ptr& trx ) {
      // packed_trx caches unpacked transaction so double
      return (trx->packed_trx()->get_unprunable_size() + trx->packed_trx()->get_prunable_size()) * 2 + sizeof( *trx );
   }

};

} } //eosio::chain
<|endoftext|>


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

#include "JedecFile.h"

#include <algorithm>
#include <string>

using namespace MachXO2;

namespace
{

enum class RowType
{
    COMMENT,
    FUSE_CHECKSUM,
    FUSE_DATA,
    END_DATA,
    FUSE_LIST,
    SECURITY_FUSE,
    FUSE_DEFAULT,
    FUSE_SIZE,
    USER_CODE,
    FEATURE_ROW,
    FEATURE_BITS,
    DONE
};

template<typename TCharIterator>
std::string getline (TCharIterator& begin, TCharIterator end, std::string delim)
{
    std::string line;

    for ( ; begin != end && !std::equal(delim.begin(), delim.end(), begin); ++begin)
    {
        line += *begin;
    }

    if (begin != end)
    {
        begin += delim.length();
    }

    return line;
}

bool string_startswith (const std::string& s, const std::string& start)
{
    return s.length() >= start.length() && s.substr(0, start.length()) == start;
}

bool string_contains (const std::string& str, const std::string& sub)
{
    return str.find(sub) != std::string::npos;
}

void ParseFuseLine (const std::string& line, std::vector<uint8_t>& dest)
{
    int n = 0;

    for (int i = 0; i < 16; i++)
    {
        uint8_t val = 0;
        for (int j = 0; j < 8; j++)
        {
            val = (uint8_t)((val << 1) | (line[n] - '0'));
            ++n;
        }

        dest.push_back(val);
    }
}

std::vector<uint8_t> ParseFeatureRow (const std::string& line, int len)
{
    std::vector<uint8_t> result(len);
    int n = 8 * len - 1;

    for (int i = 0; i < len; i++)
    {
        uint8_t val = 0;
        for (int j = 0; j < 8; j++)
        {
            val = (uint8_t)((val << 1) | (line[n] - '0'));
            --n;
        }

        result[i] = val;
    }

    return result;
}

uint32_t ParseUserCode (const std::string& data)
{
    if (data[0] == 'H')
    {
        uint32_t uc;
        sscanf( data.c_str(), "H%08X", &uc );
        return uc;
    }
    else
    {
        uint32_t val = 0;
        for (size_t i = 0; i < 32 && i < data.length(); ++i)
        {
            val = (val << 1) | (uint32_t)(data[i] - '0');
        }

        return val;
    }
}

std::string RemoveTrailingStar (const std::string& s)
{
    if (s[s.length() - 1] == '*')
        return s.substr(0, s.length() - 1);
    else
        return s;
}

} /* namespace */

JedecFile JedecFile::Parse (const std::vector<uint8_t>& data)
{
    RowType state = RowType::COMMENT;

    DeviceInfo devInfo;
    int pageCount = 0;
    int cfgPageCount = 0;
    int ufmPageCount = 0;
    std::vector<uint8_t> configurationData;
    std::vector<uint8_t> ufmData;
    std::vector<uint8_t> featureRow;
    std::vector<uint8_t> featureBits;
    uint32_t userCode = 0;

    auto pos = data.begin();
    while (pos != data.end())
    {
        auto line = getline(pos, data.end(), "\r\n");

        if (state == RowType::FEATURE_ROW)
            state = RowType::FEATURE_BITS;
        else if (line[0] == '0' || line[0] == '1')
            state = RowType::FUSE_DATA;
        else if (string_startswith(line, "NOTE"))
            state = RowType::COMMENT;
        else if (string_startswith( line, "G"))
            state = RowType::SECURITY_FUSE;
        else if (string_startswith(line, "L"))
            state = RowType::FUSE_LIST;
        else if (string_startswith( line, "C"))
            state = RowType::FUSE_CHECKSUM;
        else if(string_startswith( line, "*"))
            state = RowType::END_DATA;
        else if(string_startswith( line, "D"))
            state = RowType::FUSE_DEFAULT;
        else if(string_startswith( line, "U"))
            state = RowType::USER_CODE;
        else if(string_startswith(line, "E"))
            state = RowType::FEATURE_ROW;
        else if(string_startswith( line, "QF"))
            state = RowType::FUSE_SIZE;
        else if(string_startswith(line, "\x03"))
            state = RowType::DONE;

        switch (state)
        {
            case RowType::FUSE_DATA:
                ++pageCount;
                if (pageCount <= devInfo.numCfgPages())
                {
                    ++cfgPageCount;
                    ParseFuseLine(line, configurationData);
                }
                else
                {
                    ++ufmPageCount;
                    ParseFuseLine(line, ufmData);
                }

                break;

            case RowType::FEATURE_ROW:
                featureRow = ParseFeatureRow(line.substr(1), 8);
                break;
            case RowType::FEATURE_BITS:
                featureBits = ParseFeatureRow(line, 2);
                break;
            case RowType::USER_CODE:
                userCode = ParseUserCode(RemoveTrailingStar(line.substr(1)));
                break;
        }

        if (state == RowType::COMMENT && string_contains(line, "DEVICE NAME:"))
        {
            devInfo = DeviceInfo::Find(line);
        }

        if (state == RowType::DONE)
        {
            break;
        }
    }

    return { devInfo.type(), userCode, configurationData, featureRow, featureBits };
}
<commit_msg>Add missing default case<commit_after>/*
 * Copyright 2016 The Imaging Source Europe GmbH
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "JedecFile.h"

#include <algorithm>
#include <string>

using namespace MachXO2;

namespace
{

enum class RowType
{
    COMMENT,
    FUSE_CHECKSUM,
    FUSE_DATA,
    END_DATA,
    FUSE_LIST,
    SECURITY_FUSE,
    FUSE_DEFAULT,
    FUSE_SIZE,
    USER_CODE,
    FEATURE_ROW,
    FEATURE_BITS,
    DONE
};

template<typename TCharIterator>
std::string getline (TCharIterator& begin, TCharIterator end, std::string delim)
{
    std::string line;

    for ( ; begin != end && !std::equal(delim.begin(), delim.end(), begin); ++begin)
    {
        line += *begin;
    }

    if (begin != end)
    {
        begin += delim.length();
    }

    return line;
}

bool string_startswith (const std::string& s, const std::string& start)
{
    return s.length() >= start.length() && s.substr(0, start.length()) == start;
}

bool string_contains (const std::string& str, const std::string& sub)
{
    return str.find(sub) != std::string::npos;
}

void ParseFuseLine (const std::string& line, std::vector<uint8_t>& dest)
{
    int n = 0;

    for (int i = 0; i < 16; i++)
    {
        uint8_t val = 0;
        for (int j = 0; j < 8; j++)
        {
            val = (uint8_t)((val << 1) | (line[n] - '0'));
            ++n;
        }

        dest.push_back(val);
    }
}

std::vector<uint8_t> ParseFeatureRow (const std::string& line, int len)
{
    std::vector<uint8_t> result(len);
    int n = 8 * len - 1;

    for (int i = 0; i < len; i++)
    {
        uint8_t val = 0;
        for (int j = 0; j < 8; j++)
        {
            val = (uint8_t)((val << 1) | (line[n] - '0'));
            --n;
        }

        result[i] = val;
    }

    return result;
}

uint32_t ParseUserCode (const std::string& data)
{
    if (data[0] == 'H')
    {
        uint32_t uc;
        sscanf( data.c_str(), "H%08X", &uc );
        return uc;
    }
    else
    {
        uint32_t val = 0;
        for (size_t i = 0; i < 32 && i < data.length(); ++i)
        {
            val = (val << 1) | (uint32_t)(data[i] - '0');
        }

        return val;
    }
}

std::string RemoveTrailingStar (const std::string& s)
{
    if (s[s.length() - 1] == '*')
        return s.substr(0, s.length() - 1);
    else
        return s;
}

} /* namespace */

JedecFile JedecFile::Parse (const std::vector<uint8_t>& data)
{
    RowType state = RowType::COMMENT;

    DeviceInfo devInfo;
    int pageCount = 0;
    int cfgPageCount = 0;
    int ufmPageCount = 0;
    std::vector<uint8_t> configurationData;
    std::vector<uint8_t> ufmData;
    std::vector<uint8_t> featureRow;
    std::vector<uint8_t> featureBits;
    uint32_t userCode = 0;

    auto pos = data.begin();
    while (pos != data.end())
    {
        auto line = getline(pos, data.end(), "\r\n");

        if (state == RowType::FEATURE_ROW)
            state = RowType::FEATURE_BITS;
        else if (line[0] == '0' || line[0] == '1')
            state = RowType::FUSE_DATA;
        else if (string_startswith(line, "NOTE"))
            state = RowType::COMMENT;
        else if (string_startswith( line, "G"))
            state = RowType::SECURITY_FUSE;
        else if (string_startswith(line, "L"))
            state = RowType::FUSE_LIST;
        else if (string_startswith( line, "C"))
            state = RowType::FUSE_CHECKSUM;
        else if(string_startswith( line, "*"))
            state = RowType::END_DATA;
        else if(string_startswith( line, "D"))
            state = RowType::FUSE_DEFAULT;
        else if(string_startswith( line, "U"))
            state = RowType::USER_CODE;
        else if(string_startswith(line, "E"))
            state = RowType::FEATURE_ROW;
        else if(string_startswith( line, "QF"))
            state = RowType::FUSE_SIZE;
        else if(string_startswith(line, "\x03"))
            state = RowType::DONE;

        switch (state)
        {
            case RowType::FUSE_DATA:
                ++pageCount;
                if (pageCount <= devInfo.numCfgPages())
                {
                    ++cfgPageCount;
                    ParseFuseLine(line, configurationData);
                }
                else
                {
                    ++ufmPageCount;
                    ParseFuseLine(line, ufmData);
                }

                break;

            case RowType::FEATURE_ROW:
                featureRow = ParseFeatureRow(line.substr(1), 8);
                break;
            case RowType::FEATURE_BITS:
                featureBits = ParseFeatureRow(line, 2);
                break;
            case RowType::USER_CODE:
                userCode = ParseUserCode(RemoveTrailingStar(line.substr(1)));
                break;

            default:
                break;
        }

        if (state == RowType::COMMENT && string_contains(line, "DEVICE NAME:"))
        {
            devInfo = DeviceInfo::Find(line);
        }

        if (state == RowType::DONE)
        {
            break;
        }
    }

    return { devInfo.type(), userCode, configurationData, featureRow, featureBits };
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013-2015 John Connor (BM-NC49AxAjcqVcF5jNPu85Rb8MJ2d9JqZt)
 *
 * This file is part of vanillacoin.
 *
 * vanillacoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <cassert>

#include <coin/address_manager.hpp>
#include <coin/configuration.hpp>
#include <coin/globals.hpp>
#include <coin/logger.hpp>
#include <coin/message.hpp>
#include <coin/network.hpp>
#include <coin/stack_impl.hpp>
#include <coin/status_manager.hpp>
#include <coin/tcp_connection.hpp>
#include <coin/tcp_connection_manager.hpp>
#include <coin/tcp_transport.hpp>
#include <coin/time.hpp>
#include <coin/utility.hpp>

using namespace coin;

tcp_connection_manager::tcp_connection_manager(
    boost::asio::io_service & ios, stack_impl & owner
    )
    : io_service_(ios)
    , resolver_(ios)
    , strand_(ios)
    , stack_impl_(owner)
    , timer_(ios)
{
    // ...
}

void tcp_connection_manager::start()
{
    std::vector<boost::asio::ip::tcp::resolver::query> queries;

    /**
     * Get the bootstrap nodes and ready them for DNS lookup.
     */
    auto bootstrap_nodes = stack_impl_.get_configuration().bootstrap_nodes();

    for (auto & i : bootstrap_nodes)
    {
        boost::asio::ip::tcp::resolver::query q(
            i.first, std::to_string(i.second)
        );

        queries.push_back(q);
    }

    /**
     * Randomize the host names.
     */
    std::random_shuffle(queries.begin(), queries.end());

    /**
     * Start host name resolution.
     */
    do_resolve(queries);

    /**
     * Start the timer.
     */
    auto self(shared_from_this());

    timer_.expires_from_now(std::chrono::seconds(1));
    timer_.async_wait(globals::instance().strand().wrap(
        std::bind(&tcp_connection_manager::tick, self,
        std::placeholders::_1))
    );
}

void tcp_connection_manager::stop()
{
    resolver_.cancel();
    timer_.cancel();

    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    for (auto & i : m_tcp_connections)
    {
        if (auto connection = i.second.lock())
        {
            connection->stop();
        }
    }

    m_tcp_connections.clear();
}

void tcp_connection_manager::handle_accept(
    std::shared_ptr<tcp_transport> transport
    )
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    /**
     * Only peers accept incoming connections.
     */
    if (globals::instance().operation_mode() == protocol::operation_mode_peer)
    {
        /**
         * We allow this many incoming connections per same IP address.
         */
        enum { maximum_per_same_ip = 8 };

        auto connections = 0;

        for (auto & i : m_tcp_connections)
        {
            try
            {
                if (auto t = i.second.lock())
                {
                    if (
                        t->is_transport_valid() && i.first.address() ==
                        transport->socket().remote_endpoint().address()
                        )
                    {
                        if (++connections == maximum_per_same_ip)
                        {
                            break;
                        }
                    }
                }
            }
            catch (...)
            {
                // ...
            }
        }

        if (connections > maximum_per_same_ip)
        {
            log_error(
                "TCP connection manager is dropping duplicate IP connection "
                "from " << transport->socket().remote_endpoint() << "."
            );

            /**
             * Stop the transport.
             */
            transport->stop();
        }
        else if (
            network::instance().is_address_banned(
            transport->socket().remote_endpoint().address().to_string())
            )
        {
            log_info(
                "TCP connection manager is dropping banned connection from " <<
                transport->socket().remote_endpoint() << ", limit reached."
            );

            /**
             * Stop the transport.
             */
            transport->stop();
        }
        else if (
            is_ip_banned(
            transport->socket().remote_endpoint().address().to_string())
            )
        {
            log_debug(
                "TCP connection manager is dropping bad connection from " <<
                transport->socket().remote_endpoint() << ", limit reached."
            );

            /**
             * Stop the transport.
             */
            transport->stop();
        }
        else if (
            m_tcp_connections.size() >=
            stack_impl_.get_configuration().network_tcp_inbound_maximum()
            )
        {
            log_error(
                "TCP connection manager is dropping connection from " <<
                transport->socket().remote_endpoint() << ", limit reached."
            );

            /**
             * Stop the transport.
             */
            transport->stop();
        }
        else
        {
            log_debug(
                "TCP connection manager accepted new tcp connection from " <<
                transport->socket().remote_endpoint() << ", " <<
                m_tcp_connections.size() << " connected peers."
            );

            /**
             * Allocate the tcp_connection.
             */
            auto connection = std::make_shared<tcp_connection> (
                io_service_, stack_impl_, tcp_connection::direction_incoming,
                transport
            );

            /**
             * Retain the connection.
             */
            m_tcp_connections[transport->socket().remote_endpoint()] =
                connection
            ;

            /**
             * Start the tcp_connection.
             */
            connection->start();
        }
    }
}

void tcp_connection_manager::broadcast(
    const char * buf, const std::size_t & len
    )
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    for (auto & i : m_tcp_connections)
    {
        if (auto j = i.second.lock())
        {
            j->send(buf, len);
        }
    }
}

std::map< boost::asio::ip::tcp::endpoint, std::weak_ptr<tcp_connection> > &
    tcp_connection_manager::tcp_connections()
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    return m_tcp_connections;
}

bool tcp_connection_manager::is_connected()
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    auto tcp_connections = 0;

    for (auto & i : m_tcp_connections)
    {
        if (auto connection = i.second.lock())
        {
            if (auto t = connection->get_tcp_transport().lock())
            {
                if (t->state() == tcp_transport::state_connected)
                {
                    ++tcp_connections;
                }
            }
        }
    }

    return tcp_connections > 0;
}

bool tcp_connection_manager::connect(const boost::asio::ip::tcp::endpoint & ep)
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    if (network::instance().is_address_banned(ep.address().to_string()))
    {
        log_info(
            "TCP connection manager tried to connect to a banned address " <<
            ep << "."
        );

        return false;
    }
    else if (is_ip_banned(ep.address().to_string()))
    {
        log_debug(
            "TCP connection manager tried to connect to a bad address " <<
            ep << "."
        );

        return false;
    }
    else if (m_tcp_connections.find(ep) == m_tcp_connections.end())
    {
        log_none("TCP connection manager is connecting to " << ep << ".");

        /**
         * Inform the address_manager.
         */
        stack_impl_.get_address_manager()->on_connection_attempt(
            protocol::network_address_t::from_endpoint(ep)
        );

        /**
         * Allocate tcp_transport.
         */
        auto transport = std::make_shared<tcp_transport>(io_service_, strand_);

        /**
         * Allocate the tcp_connection.
         */
        auto connection = std::make_shared<tcp_connection> (
            io_service_, stack_impl_, tcp_connection::direction_outgoing,
            transport
        );

        /**
         * Retain the connection.
         */
        m_tcp_connections[ep] = connection;

        /**
         * Start the tcp_connection.
         */
        connection->start(ep);

        return true;
    }
    else
    {
        log_none(
            "TCP connection manager attempted connection to existing "
            "endpoint = " << ep << "."
        );
    }

    return false;
}

void tcp_connection_manager::tick(const boost::system::error_code & ec)
{
    if (ec)
    {
        // ...
    }
    else
    {
        std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

        auto tcp_connections = 0;

        auto it = m_tcp_connections.begin();

        while (it != m_tcp_connections.end())
        {
            if (auto connection = it->second.lock())
            {
                if (connection->is_transport_valid())
                {
                    if (auto t = connection->get_tcp_transport().lock())
                    {
                        if (t->state() == tcp_transport::state_connected)
                        {
                            ++tcp_connections;
                        }
                    }

                    ++it;
                }
                else
                {
                    connection->stop();

                    it = m_tcp_connections.erase(it);
                }
            }
            else
            {
                it = m_tcp_connections.erase(it);
            }
        }

        /**
         * Maintain at least minimum_tcp_connections tcp connections.
         */
        if (tcp_connections < minimum_tcp_connections)
        {
            for (
                auto i = 0; i < minimum_tcp_connections - tcp_connections; i++
                )
            {
                /**
                 * Get a network address from the address_manager.
                 */
                auto addr = stack_impl_.get_address_manager()->select(
                    10 + std::min(m_tcp_connections.size(),
                    static_cast<std::size_t> (8)) * 10
                );

                /**
                 * Only connect to one peer per group.
                 */
                bool is_in_same_group = false;

                for (auto & i : m_tcp_connections)
                {
                    if (auto j = i.second.lock())
                    {
                        if (auto k = j->get_tcp_transport().lock())
                        {
                            try
                            {
                                auto addr_tmp =
                                    protocol::network_address_t::from_endpoint(
                                    k->socket().remote_endpoint()
                                );

                                if (addr.group() == addr_tmp.group())
                                {
                                    is_in_same_group = true;
                                }
                            }
                            catch (std::exception & e)
                            {
                                // ...
                            }
                        }
                    }
                }

                if (
                    addr.is_valid() == false || addr.is_local() ||
                    is_in_same_group
                    )
                {
                    // ...
                }
                else
                {
                    /**
                     * Do not retry connections to the same network address more
                     * often than every 3.33 minutes.
                     */
                    if (time::instance().get_adjusted() - addr.last_try < 200)
                    {
                        log_info(
                            "TCP connection manager attempted to "
                            "connect to " << addr.ipv4_mapped_address() <<
                            ":" << addr.port << " too soon, last try = " <<
                            (time::instance().get_adjusted() - addr.last_try) <<
                            " seconds."
                        );
                    }
                    else
                    {
                        /**
                         * Connect to the endpoint.
                         */
                        if (connect(
                            boost::asio::ip::tcp::endpoint(
                            addr.ipv4_mapped_address(), addr.port))
                            )
                        {
                            log_info(
                                "TCP connection manager is connecting to " <<
                                addr.ipv4_mapped_address() << ":" <<
                                addr.port << ", last seen = " <<
                                (time::instance().get_adjusted() -
                                addr.timestamp) / 60 << " mins, " <<
                                tcp_connections << " connected peers."
                            );
                        }
                    }
                }
            }

            auto self(shared_from_this());

            timer_.expires_from_now(std::chrono::seconds(2));
            timer_.async_wait(globals::instance().strand().wrap(
                std::bind(&tcp_connection_manager::tick, self,
                std::placeholders::_1))
            );
        }
        else
        {
            auto self(shared_from_this());

            timer_.expires_from_now(std::chrono::seconds(8));
            timer_.async_wait(globals::instance().strand().wrap(
                std::bind(&tcp_connection_manager::tick, self,
                std::placeholders::_1))
            );
        }

        /**
         * Allocate the status.
         */
        std::map<std::string, std::string> status;

        /**
         * Set the status message.
         */
        status["type"] = "network";

        /**
         * Set the value.
         */
        status["value"] = tcp_connections > 0 ? "Connected" : "Connecting";

        /**
         * Set the network.tcp.connections.
         */
        status["network.tcp.connections"] = std::to_string(
            tcp_connections
        );

        /**
         * Callback status.
         */
        stack_impl_.get_status_manager()->insert(status);
    }
}

void tcp_connection_manager::do_resolve(
    const std::vector<boost::asio::ip::tcp::resolver::query> & queries
    )
{
    /**
     * Sanity check.
     */
    assert(queries.size() <= 100);

    /**
     * Resolve the first entry.
     */
    resolver_.async_resolve(queries.front(),
        strand_.wrap([this, queries](
            const boost::system::error_code & ec,
            const boost::asio::ip::tcp::resolver::iterator & it
            )
            {
                if (ec)
                {
                    // ...
                }
                else
                {
                    log_debug(
                        "TCP connection manager resolved " << it->endpoint() <<
                        "."
                    );

                    /**
                     * Create the network address.
                     */
                    protocol::network_address_t addr =
                        protocol::network_address_t::from_endpoint(
                        it->endpoint()
                    );

                    /**
                     * Add to the address manager.
                     */
                    stack_impl_.get_address_manager()->add(
                        addr, protocol::network_address_t::from_endpoint(
                        boost::asio::ip::tcp::endpoint(
                        boost::asio::ip::address::from_string("127.0.0.1"), 0))
                    );
                }

                if (queries.size() > 0)
                {
                    auto tmp = queries;

                    /**
                     * Remove the first entry.
                     */
                    tmp.erase(tmp.begin());

                    if (tmp.size() > 0)
                    {
                        /**
                         * Keep resolving as long as we have entries.
                         */
                        do_resolve(tmp);
                    }
                }
            }
        )
    );
}

bool tcp_connection_manager::is_ip_banned(const std::string & val)
{
    if (
        (val[0] == '5' && val[1] == '4') ||
        (val[0] == '5' && val[1] == '0') ||
        (val[0] == '2' && val[1] == '1' && val[2] == '1') ||
        (val[0] == '2' && val[1] == '1' && val[2] == '9') ||
        (val.find("222.190.") != std::string::npos) ||
        (val.find("41.224.") != std::string::npos) ||
        (val.find("5.246.") != std::string::npos) ||
        (val.find("184.73.") != std::string::npos) ||
        (val.find("186.73.252.") != std::string::npos) ||
        (val.find("61.164.110.") != std::string::npos)
        )
    {
        return true;
    }

    return false;
}

<commit_msg>allow re-connection attempts after 60 seconds<commit_after>/*
 * Copyright (c) 2013-2015 John Connor (BM-NC49AxAjcqVcF5jNPu85Rb8MJ2d9JqZt)
 *
 * This file is part of vanillacoin.
 *
 * vanillacoin is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <cassert>

#include <coin/address_manager.hpp>
#include <coin/configuration.hpp>
#include <coin/globals.hpp>
#include <coin/logger.hpp>
#include <coin/message.hpp>
#include <coin/network.hpp>
#include <coin/stack_impl.hpp>
#include <coin/status_manager.hpp>
#include <coin/tcp_connection.hpp>
#include <coin/tcp_connection_manager.hpp>
#include <coin/tcp_transport.hpp>
#include <coin/time.hpp>
#include <coin/utility.hpp>

using namespace coin;

tcp_connection_manager::tcp_connection_manager(
    boost::asio::io_service & ios, stack_impl & owner
    )
    : io_service_(ios)
    , resolver_(ios)
    , strand_(ios)
    , stack_impl_(owner)
    , timer_(ios)
{
    // ...
}

void tcp_connection_manager::start()
{
    std::vector<boost::asio::ip::tcp::resolver::query> queries;

    /**
     * Get the bootstrap nodes and ready them for DNS lookup.
     */
    auto bootstrap_nodes = stack_impl_.get_configuration().bootstrap_nodes();

    for (auto & i : bootstrap_nodes)
    {
        boost::asio::ip::tcp::resolver::query q(
            i.first, std::to_string(i.second)
        );

        queries.push_back(q);
    }

    /**
     * Randomize the host names.
     */
    std::random_shuffle(queries.begin(), queries.end());

    /**
     * Start host name resolution.
     */
    do_resolve(queries);

    /**
     * Start the timer.
     */
    auto self(shared_from_this());

    timer_.expires_from_now(std::chrono::seconds(1));
    timer_.async_wait(globals::instance().strand().wrap(
        std::bind(&tcp_connection_manager::tick, self,
        std::placeholders::_1))
    );
}

void tcp_connection_manager::stop()
{
    resolver_.cancel();
    timer_.cancel();

    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    for (auto & i : m_tcp_connections)
    {
        if (auto connection = i.second.lock())
        {
            connection->stop();
        }
    }

    m_tcp_connections.clear();
}

void tcp_connection_manager::handle_accept(
    std::shared_ptr<tcp_transport> transport
    )
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    /**
     * Only peers accept incoming connections.
     */
    if (globals::instance().operation_mode() == protocol::operation_mode_peer)
    {
        /**
         * We allow this many incoming connections per same IP address.
         */
        enum { maximum_per_same_ip = 8 };

        auto connections = 0;

        for (auto & i : m_tcp_connections)
        {
            try
            {
                if (auto t = i.second.lock())
                {
                    if (
                        t->is_transport_valid() && i.first.address() ==
                        transport->socket().remote_endpoint().address()
                        )
                    {
                        if (++connections == maximum_per_same_ip)
                        {
                            break;
                        }
                    }
                }
            }
            catch (...)
            {
                // ...
            }
        }

        if (connections > maximum_per_same_ip)
        {
            log_error(
                "TCP connection manager is dropping duplicate IP connection "
                "from " << transport->socket().remote_endpoint() << "."
            );

            /**
             * Stop the transport.
             */
            transport->stop();
        }
        else if (
            network::instance().is_address_banned(
            transport->socket().remote_endpoint().address().to_string())
            )
        {
            log_info(
                "TCP connection manager is dropping banned connection from " <<
                transport->socket().remote_endpoint() << ", limit reached."
            );

            /**
             * Stop the transport.
             */
            transport->stop();
        }
        else if (
            is_ip_banned(
            transport->socket().remote_endpoint().address().to_string())
            )
        {
            log_debug(
                "TCP connection manager is dropping bad connection from " <<
                transport->socket().remote_endpoint() << ", limit reached."
            );

            /**
             * Stop the transport.
             */
            transport->stop();
        }
        else if (
            m_tcp_connections.size() >=
            stack_impl_.get_configuration().network_tcp_inbound_maximum()
            )
        {
            log_error(
                "TCP connection manager is dropping connection from " <<
                transport->socket().remote_endpoint() << ", limit reached."
            );

            /**
             * Stop the transport.
             */
            transport->stop();
        }
        else
        {
            log_debug(
                "TCP connection manager accepted new tcp connection from " <<
                transport->socket().remote_endpoint() << ", " <<
                m_tcp_connections.size() << " connected peers."
            );

            /**
             * Allocate the tcp_connection.
             */
            auto connection = std::make_shared<tcp_connection> (
                io_service_, stack_impl_, tcp_connection::direction_incoming,
                transport
            );

            /**
             * Retain the connection.
             */
            m_tcp_connections[transport->socket().remote_endpoint()] =
                connection
            ;

            /**
             * Start the tcp_connection.
             */
            connection->start();
        }
    }
}

void tcp_connection_manager::broadcast(
    const char * buf, const std::size_t & len
    )
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    for (auto & i : m_tcp_connections)
    {
        if (auto j = i.second.lock())
        {
            j->send(buf, len);
        }
    }
}

std::map< boost::asio::ip::tcp::endpoint, std::weak_ptr<tcp_connection> > &
    tcp_connection_manager::tcp_connections()
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    return m_tcp_connections;
}

bool tcp_connection_manager::is_connected()
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    auto tcp_connections = 0;

    for (auto & i : m_tcp_connections)
    {
        if (auto connection = i.second.lock())
        {
            if (auto t = connection->get_tcp_transport().lock())
            {
                if (t->state() == tcp_transport::state_connected)
                {
                    ++tcp_connections;
                }
            }
        }
    }

    return tcp_connections > 0;
}

bool tcp_connection_manager::connect(const boost::asio::ip::tcp::endpoint & ep)
{
    std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

    if (network::instance().is_address_banned(ep.address().to_string()))
    {
        log_info(
            "TCP connection manager tried to connect to a banned address " <<
            ep << "."
        );

        return false;
    }
    else if (is_ip_banned(ep.address().to_string()))
    {
        log_debug(
            "TCP connection manager tried to connect to a bad address " <<
            ep << "."
        );

        return false;
    }
    else if (m_tcp_connections.find(ep) == m_tcp_connections.end())
    {
        log_none("TCP connection manager is connecting to " << ep << ".");

        /**
         * Inform the address_manager.
         */
        stack_impl_.get_address_manager()->on_connection_attempt(
            protocol::network_address_t::from_endpoint(ep)
        );

        /**
         * Allocate tcp_transport.
         */
        auto transport = std::make_shared<tcp_transport>(io_service_, strand_);

        /**
         * Allocate the tcp_connection.
         */
        auto connection = std::make_shared<tcp_connection> (
            io_service_, stack_impl_, tcp_connection::direction_outgoing,
            transport
        );

        /**
         * Retain the connection.
         */
        m_tcp_connections[ep] = connection;

        /**
         * Start the tcp_connection.
         */
        connection->start(ep);

        return true;
    }
    else
    {
        log_none(
            "TCP connection manager attempted connection to existing "
            "endpoint = " << ep << "."
        );
    }

    return false;
}

void tcp_connection_manager::tick(const boost::system::error_code & ec)
{
    if (ec)
    {
        // ...
    }
    else
    {
        std::lock_guard<std::recursive_mutex> l1(mutex_tcp_connections_);

        auto tcp_connections = 0;

        auto it = m_tcp_connections.begin();

        while (it != m_tcp_connections.end())
        {
            if (auto connection = it->second.lock())
            {
                if (connection->is_transport_valid())
                {
                    if (auto t = connection->get_tcp_transport().lock())
                    {
                        if (t->state() == tcp_transport::state_connected)
                        {
                            ++tcp_connections;
                        }
                    }

                    ++it;
                }
                else
                {
                    connection->stop();

                    it = m_tcp_connections.erase(it);
                }
            }
            else
            {
                it = m_tcp_connections.erase(it);
            }
        }

        /**
         * Maintain at least minimum_tcp_connections tcp connections.
         */
        if (tcp_connections < minimum_tcp_connections)
        {
            for (
                auto i = 0; i < minimum_tcp_connections - tcp_connections; i++
                )
            {
                /**
                 * Get a network address from the address_manager.
                 */
                auto addr = stack_impl_.get_address_manager()->select(
                    10 + std::min(m_tcp_connections.size(),
                    static_cast<std::size_t> (8)) * 10
                );

                /**
                 * Only connect to one peer per group.
                 */
                bool is_in_same_group = false;

                for (auto & i : m_tcp_connections)
                {
                    if (auto j = i.second.lock())
                    {
                        if (auto k = j->get_tcp_transport().lock())
                        {
                            try
                            {
                                auto addr_tmp =
                                    protocol::network_address_t::from_endpoint(
                                    k->socket().remote_endpoint()
                                );

                                if (addr.group() == addr_tmp.group())
                                {
                                    is_in_same_group = true;
                                }
                            }
                            catch (std::exception & e)
                            {
                                // ...
                            }
                        }
                    }
                }

                if (
                    addr.is_valid() == false || addr.is_local() ||
                    is_in_same_group
                    )
                {
                    // ...
                }
                else
                {
                    /**
                     * Do not retry connections to the same network address more
                     * often than every 60 seconds.
                     */
                    if (time::instance().get_adjusted() - addr.last_try < 60)
                    {
                        log_info(
                            "TCP connection manager attempted to "
                            "connect to " << addr.ipv4_mapped_address() <<
                            ":" << addr.port << " too soon, last try = " <<
                            (time::instance().get_adjusted() - addr.last_try) <<
                            " seconds."
                        );
                    }
                    else
                    {
                        /**
                         * Connect to the endpoint.
                         */
                        if (connect(
                            boost::asio::ip::tcp::endpoint(
                            addr.ipv4_mapped_address(), addr.port))
                            )
                        {
                            log_info(
                                "TCP connection manager is connecting to " <<
                                addr.ipv4_mapped_address() << ":" <<
                                addr.port << ", last seen = " <<
                                (time::instance().get_adjusted() -
                                addr.timestamp) / 60 << " mins, " <<
                                tcp_connections << " connected peers."
                            );
                        }
                    }
                }
            }

            auto self(shared_from_this());

            timer_.expires_from_now(std::chrono::seconds(2));
            timer_.async_wait(globals::instance().strand().wrap(
                std::bind(&tcp_connection_manager::tick, self,
                std::placeholders::_1))
            );
        }
        else
        {
            auto self(shared_from_this());

            timer_.expires_from_now(std::chrono::seconds(8));
            timer_.async_wait(globals::instance().strand().wrap(
                std::bind(&tcp_connection_manager::tick, self,
                std::placeholders::_1))
            );
        }

        /**
         * Allocate the status.
         */
        std::map<std::string, std::string> status;

        /**
         * Set the status message.
         */
        status["type"] = "network";

        /**
         * Set the value.
         */
        status["value"] = tcp_connections > 0 ? "Connected" : "Connecting";

        /**
         * Set the network.tcp.connections.
         */
        status["network.tcp.connections"] = std::to_string(
            tcp_connections
        );

        /**
         * Callback status.
         */
        stack_impl_.get_status_manager()->insert(status);
    }
}

void tcp_connection_manager::do_resolve(
    const std::vector<boost::asio::ip::tcp::resolver::query> & queries
    )
{
    /**
     * Sanity check.
     */
    assert(queries.size() <= 100);

    /**
     * Resolve the first entry.
     */
    resolver_.async_resolve(queries.front(),
        strand_.wrap([this, queries](
            const boost::system::error_code & ec,
            const boost::asio::ip::tcp::resolver::iterator & it
            )
            {
                if (ec)
                {
                    // ...
                }
                else
                {
                    log_debug(
                        "TCP connection manager resolved " << it->endpoint() <<
                        "."
                    );

                    /**
                     * Create the network address.
                     */
                    protocol::network_address_t addr =
                        protocol::network_address_t::from_endpoint(
                        it->endpoint()
                    );

                    /**
                     * Add to the address manager.
                     */
                    stack_impl_.get_address_manager()->add(
                        addr, protocol::network_address_t::from_endpoint(
                        boost::asio::ip::tcp::endpoint(
                        boost::asio::ip::address::from_string("127.0.0.1"), 0))
                    );
                }

                if (queries.size() > 0)
                {
                    auto tmp = queries;

                    /**
                     * Remove the first entry.
                     */
                    tmp.erase(tmp.begin());

                    if (tmp.size() > 0)
                    {
                        /**
                         * Keep resolving as long as we have entries.
                         */
                        do_resolve(tmp);
                    }
                }
            }
        )
    );
}

bool tcp_connection_manager::is_ip_banned(const std::string & val)
{
    if (
        (val[0] == '5' && val[1] == '4') ||
        (val[0] == '5' && val[1] == '0') ||
        (val[0] == '2' && val[1] == '1' && val[2] == '1') ||
        (val[0] == '2' && val[1] == '1' && val[2] == '9') ||
        (val.find("222.190.") != std::string::npos) ||
        (val.find("41.224.") != std::string::npos) ||
        (val.find("5.246.") != std::string::npos) ||
        (val.find("184.73.") != std::string::npos) ||
        (val.find("186.73.252.") != std::string::npos) ||
        (val.find("61.164.110.") != std::string::npos)
        )
    {
        return true;
    }

    return false;
}

<|endoftext|>