<commit_before>/**
 *  pretty_print_msg.cpp
 *
 *  @author Nathan Campos
 */

#include <cstdio>
#include <cstdlib>
#include <sstream>

#include "channels.h"
#include "color.h"
#include "irc_reply_codes.h"
#include "message.h"
#include "pretty_print_msg.h"
using namespace std;

Pretty_Print_Message::Pretty_Print_Message(const char *_buffer) {
    buffer = _buffer;
    echo = true;
}

string Pretty_Print_Message::color_string(string nickname, bool include_msg) {
    // Generate a color for the nick based on its letters.
    string chars = " abcdefghijklmnopqrstuvwxyz1234567890_-";
    int color = 0;
    char color_chr[2];

    for (size_t i = 0; i < nickname.size(); i++) {
        // Get the characted position in our dictionary.
        size_t pos = chars.find(nickname.at(i));

        // If we couldn't find it just default it to 10.
        if (pos == string::npos) {
            pos = 10;
        }

        color += pos;
    }

    // Build the string.
    color = (color % 7) + 1;
    sprintf(color_chr, "%d", color);

    if (!include_msg) {
        return "\033[1m\033[3" + string(color_chr) + "m";
    } else {
        return "\033[1m\033[3" + string(color_chr) + "m" + nickname + RESET;
    }
}

string Pretty_Print_Message::generate(Message &message, Channels &channels) {
    // Parse and return a better and more human-readable message.
    string str_buffer(buffer);
    vector<string> arguments = message.get_command_args();

    if (message.get_command() == "PRIVMSG") {
        if (arguments.at(0).at(0) == '#') {
            // Channel message.
            if (arguments.at(0) != "#" + channels.list.at(channels.current)) {
                echo = false;
            }

            // A normal message.
            string nickname = message.get_nickname();
            str_buffer = color_string(nickname, false) + "<" + nickname + "> " + string(RESET) + arguments.at(1) + "\r\n";

            if (arguments.at(1).size() > 6) {
                if (arguments.at(1).substr(0, 7) == "\001ACTION") {
                    // This is a ACTION message.
                    str_buffer = string(BOLDMAGENTA) + "\u2022 " + message.get_nickname() + " " + arguments.at(1).substr(8) + string(RESET) + "\r\n";
                }
            }
        }
    } else if (message.get_command() == "JOIN") {
        // Someone joined the channel.
        // TODO: Check if this isn't adding more than one time when another user joins the channel.
        // If it is move this to the repl loop and create an eval return for it.
        if (!arguments.empty()) {
            channels.add(arguments.at(0).substr(1, arguments.at(0).find(":") - 1));
        }

        str_buffer = string(BOLDGREEN) + "> " + string(RESET) + message.get_nickname() + " joined " + arguments.at(0) + "\r\n";
    } else if (message.get_command() == "PART") {
        // Someone left the channel.
        str_buffer = string(BOLDRED) + "< " + string(RESET) + message.get_nickname() + " left\r\n";
    } else if (message.get_command() == "KICK") {
        // Someone got kicked.
        str_buffer = string(BOLDRED) + "<< " + message.get_nickname() + " got kicked from " + arguments.at(0) + " (" + arguments.at(2) + ")" + string(RESET) + "\r\n";
    } else if (message.get_command() == "MODE") {
        // Changing modes.
        str_buffer = string(BOLDBLUE) + "* " + string(RESET) + message.get_nickname() + " set mode ";

        for (unsigned int i = 0; i < arguments.size(); i++) {
            str_buffer += arguments.at(i) + " ";
        }

        str_buffer += "\r\n";
    } else if (message.get_command() == "QUIT") {
        // Quitting.
        str_buffer = string(BOLDRED) + "<< " + string(RESET) + message.get_nickname() + " quit (" + arguments.at(0) + ")\r\n";
    } else if (message.get_command() == "ERROR") {
        // Oh noes! Error!
        str_buffer = string(BOLDRED) + "Error: " + arguments.at(0) + string(RESET) + "\r\n";
    } else if (message.get_command() == "NICK") {
        // Someone is chaning the nick.
        str_buffer = string(BOLDBLUE) + "* " + color_string(message.get_nickname(), true) + " is now known as " + color_string(arguments.at(0), true) + "\r\n";
    } else {
        // Might be a server message, so let's check for the reply code.
        int reply_code = message.get_reply_code();
        ostringstream stream;

        switch (reply_code) {
            case RPL_TOPIC:
                // Got a topic.
                str_buffer = string(BOLDWHITE) + "Topic: " + "\"" + arguments.at(2) + "\"" + string(RESET) + "\r\n";
                break;
            case RPL_TOPICWHOTIME:
                // Got who set the topic.
                str_buffer = "Topic set by " +  arguments.at(2).substr(0,  arguments.at(2).find('!')) + "\r\n";
                break;
            case RPL_NAMREPLY:
                // Got a list of the people online.
                stream << "Users: ";

                str_buffer = arguments.at(3);
                while (str_buffer.find(" ") != string::npos) {
                    size_t pos = str_buffer.find(" ");

                    stream << color_string(str_buffer.substr(0, pos), true) << " ";
                    str_buffer.erase(0, pos + 1);
                }

                if (!str_buffer.empty()) {
                    stream << color_string(str_buffer, true);
                }

                stream << "\r\n";
                str_buffer = stream.str();
                break;
            case RPL_ENDOFNAMES:
                // (Ignored) Got the end of the online names list
                str_buffer = "";
                break;
            default:
                // Got some other kind of message that we haven't covered.
                str_buffer = "";

                for (size_t i = 1; i < arguments.size(); i++) {
                    str_buffer += arguments.at(i) + " ";
                }

                str_buffer += "\r\n";
                break;
        }
    }

    if (arguments.at(0).at(0) == '#') {
        channels.cache(arguments.at(0).substr(1), str_buffer);
    }

    return str_buffer;
}

bool Pretty_Print_Message::echo_message() {
    return echo;
}
<commit_msg>#20 - Documented the Pretty_Print_Message class<commit_after>/**
 *  pretty_print_msg.cpp
 *
 *  @author Nathan Campos
 */

#include <cstdio>
#include <cstdlib>
#include <sstream>

#include "channels.h"
#include "color.h"
#include "irc_reply_codes.h"
#include "message.h"
#include "pretty_print_msg.h"
using namespace std;

/**
 * Constructor for the class.
 *
 * \param _buffer Message string.
 */
Pretty_Print_Message::Pretty_Print_Message(const char *_buffer) {
    buffer = _buffer;
    echo = true;
}

/**
 * Create a color string based on the string's letters.
 *
 * \param nickname Nickname to be colorized.
 * \param include_msg Include the whole message with it.
 * \return Colorized string.
 */
string Pretty_Print_Message::color_string(string nickname, bool include_msg) {
    // Generate a color for the nick based on its letters.
    string chars = " abcdefghijklmnopqrstuvwxyz1234567890_-";
    int color = 0;
    char color_chr[2];

    for (size_t i = 0; i < nickname.size(); i++) {
        // Get the characted position in our dictionary.
        size_t pos = chars.find(nickname.at(i));

        // If we couldn't find it just default it to 10.
        if (pos == string::npos) {
            pos = 10;
        }

        color += pos;
    }

    // Build the string.
    color = (color % 7) + 1;
    sprintf(color_chr, "%d", color);

    if (!include_msg) {
        return "\033[1m\033[3" + string(color_chr) + "m";
    } else {
        return "\033[1m\033[3" + string(color_chr) + "m" + nickname + RESET;
    }
}

/**
 * Generate the pretty formatted string based on the message.
 *
 * \param message Pointer to a Message class.
 * \param channels Pointer to a Channels class.
 * \return Prettyfied string.
 */
string Pretty_Print_Message::generate(Message &message, Channels &channels) {
    // Parse and return a better and more human-readable message.
    string str_buffer(buffer);
    vector<string> arguments = message.get_command_args();

    if (message.get_command() == "PRIVMSG") {
        if (arguments.at(0).at(0) == '#') {
            // Channel message.
            if (arguments.at(0) != "#" + channels.list.at(channels.current)) {
                echo = false;
            }

            // A normal message.
            string nickname = message.get_nickname();
            str_buffer = color_string(nickname, false) + "<" + nickname + "> " + string(RESET) + arguments.at(1) + "\r\n";

            if (arguments.at(1).size() > 6) {
                if (arguments.at(1).substr(0, 7) == "\001ACTION") {
                    // This is a ACTION message.
                    str_buffer = string(BOLDMAGENTA) + "\u2022 " + message.get_nickname() + " " + arguments.at(1).substr(8) + string(RESET) + "\r\n";
                }
            }
        }
    } else if (message.get_command() == "JOIN") {
        // Someone joined the channel.
        // TODO: Check if this isn't adding more than one time when another user joins the channel.
        // If it is move this to the repl loop and create an eval return for it.
        if (!arguments.empty()) {
            channels.add(arguments.at(0).substr(1, arguments.at(0).find(":") - 1));
        }

        str_buffer = string(BOLDGREEN) + "> " + string(RESET) + message.get_nickname() + " joined " + arguments.at(0) + "\r\n";
    } else if (message.get_command() == "PART") {
        // Someone left the channel.
        str_buffer = string(BOLDRED) + "< " + string(RESET) + message.get_nickname() + " left\r\n";
    } else if (message.get_command() == "KICK") {
        // Someone got kicked.
        str_buffer = string(BOLDRED) + "<< " + message.get_nickname() + " got kicked from " + arguments.at(0) + " (" + arguments.at(2) + ")" + string(RESET) + "\r\n";
    } else if (message.get_command() == "MODE") {
        // Changing modes.
        str_buffer = string(BOLDBLUE) + "* " + string(RESET) + message.get_nickname() + " set mode ";

        for (unsigned int i = 0; i < arguments.size(); i++) {
            str_buffer += arguments.at(i) + " ";
        }

        str_buffer += "\r\n";
    } else if (message.get_command() == "QUIT") {
        // Quitting.
        str_buffer = string(BOLDRED) + "<< " + string(RESET) + message.get_nickname() + " quit (" + arguments.at(0) + ")\r\n";
    } else if (message.get_command() == "ERROR") {
        // Oh noes! Error!
        str_buffer = string(BOLDRED) + "Error: " + arguments.at(0) + string(RESET) + "\r\n";
    } else if (message.get_command() == "NICK") {
        // Someone is chaning the nick.
        str_buffer = string(BOLDBLUE) + "* " + color_string(message.get_nickname(), true) + " is now known as " + color_string(arguments.at(0), true) + "\r\n";
    } else {
        // Might be a server message, so let's check for the reply code.
        int reply_code = message.get_reply_code();
        ostringstream stream;

        switch (reply_code) {
            case RPL_TOPIC:
                // Got a topic.
                str_buffer = string(BOLDWHITE) + "Topic: " + "\"" + arguments.at(2) + "\"" + string(RESET) + "\r\n";
                break;
            case RPL_TOPICWHOTIME:
                // Got who set the topic.
                str_buffer = "Topic set by " +  arguments.at(2).substr(0,  arguments.at(2).find('!')) + "\r\n";
                break;
            case RPL_NAMREPLY:
                // Got a list of the people online.
                stream << "Users: ";

                str_buffer = arguments.at(3);
                while (str_buffer.find(" ") != string::npos) {
                    size_t pos = str_buffer.find(" ");

                    stream << color_string(str_buffer.substr(0, pos), true) << " ";
                    str_buffer.erase(0, pos + 1);
                }

                if (!str_buffer.empty()) {
                    stream << color_string(str_buffer, true);
                }

                stream << "\r\n";
                str_buffer = stream.str();
                break;
            case RPL_ENDOFNAMES:
                // (Ignored) Got the end of the online names list
                str_buffer = "";
                break;
            default:
                // Got some other kind of message that we haven't covered.
                str_buffer = "";

                for (size_t i = 1; i < arguments.size(); i++) {
                    str_buffer += arguments.at(i) + " ";
                }

                str_buffer += "\r\n";
                break;
        }
    }

    if (arguments.at(0).at(0) == '#') {
        channels.cache(arguments.at(0).substr(1), str_buffer);
    }

    return str_buffer;
}

/**
 * Check if the message should be echoed.
 *
 * \return true if the message should be echoed.
 */
bool Pretty_Print_Message::echo_message() {
    return echo;
}
<|endoftext|>


<commit_before>/*      _______   __   __   __   ______   __   __   _______   __   __
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
 *
 * Copyright (c) 2004 darkbits                              Js_./
 * Per Larsson a.k.a finalman                          _RqZ{a<^_aa
 * Olof Naessn a.k.a jansem/yakslem                _asww7!uY`>  )\a//
 *                                                 _Qhm`] _f "'c  1!5m
 * Visit: http://guichan.darkbits.org             )Qk<P ` _: :+' .'  "{[
 *                                               .)j(] .d_/ '-(  P .   S
 * License: (BSD)                                <Td/Z <fP"5(\"??"\a.  .L
 * Redistribution and use in source and          _dV>ws?a-?'      ._/L  #'
 * binary forms, with or without                 )4d[#7r, .   '     )d`)[
 * modification, are permitted provided         _Q-5'5W..j/?'   -?!\)cam'
 * that the following conditions are met:       j<<WP+k/);.        _W=j f
 * 1. Redistributions of source code must       .$%w\/]Q  . ."'  .  mj$
 *    retain the above copyright notice,        ]E.pYY(Q]>.   a     J@\
 *    this list of conditions and the           j(]1u<sE"L,. .   ./^ ]{a
 *    following disclaimer.                     4'_uomm\.  )L);-4     (3=
 * 2. Redistributions in binary form must        )_]X{Z('a_"a7'<a"a,  ]"[
 *    reproduce the above copyright notice,       #}<]m7`Za??4,P-"'7. ).m
 *    this list of conditions and the            ]d2e)Q(<Q(  ?94   b-  LQ/
 *    following disclaimer in the                <B!</]C)d_, '(<' .f. =C+m
 *    documentation and/or other materials      .Z!=J ]e []('-4f _ ) -.)m]'
 *    provided with the distribution.          .w[5]' _[ /.)_-"+?   _/ <W"
 * 3. Neither the name of Guichan nor the      :$we` _! + _/ .        j?
 *    names of its contributors may be used     =3)= _f  (_yQmWW$#(    "
 *    to endorse or promote products derived     -   W,  sQQQQmZQ#Wwa]..
 *    from this software without specific        (js, \[QQW$QWW#?!V"".
 *    prior written permission.                    ]y:.<\..          .
 *                                                 -]n w/ '         [.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT       )/ )/           !
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY         <  (; sac    ,    '
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,               ]^ .-  %
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF            c <   r
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR            aga<  <La
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE          5%  )P'-3L
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR        _bQf` y`..)a
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          ,J?4P'.P"_(\?d'.,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES               _Pa,)!f/<[]/  ?"
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT      _2-..:. .r+_,.. .
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     ?a.<%"'  " -'.a_ _,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION)                     ^
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * For comments regarding functions please see the header file.
 */

#include "guichan/exception.hpp"
#include "guichan/widgets/container.hpp"

namespace gcn
{

	Container::Container()
	{
		mWidgetWithMouse = NULL;
		mOpaque = true;

	} // end Container

	Container::~Container()
	{
		clear();

	} // end ~Container

	void Container::logic()
	{
		logicChildren();

	} // end logic

	void Container::draw(Graphics* graphics)
	{
		if (mOpaque)
		{
			graphics->setColor(getBaseColor());
			graphics->fillRectangle(Rectangle(0, 0, getDimension().width, getDimension().height));
		}

		drawChildren(graphics);

	} // draw

	void Container::logicChildren()
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->logic();
		}

	} // end logicChildren

	void Container::drawChildren(Graphics* graphics)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if ((*iter)->isVisible())
			{
				graphics->pushClipArea((*iter)->getDimension());
				(*iter)->draw(graphics);
				graphics->popClipArea();
			}
		}

	} // end drawChildren

	void Container::setOpaque(bool opaque)
	{
		mOpaque = opaque;

	} // end setOpaque

	bool Container::isOpaque() const
	{
		return mOpaque;

	} // end isOpaque

	void Container::moveToTop(Widget* widget)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				mWidgets.push_back(widget);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::moveToTop. There is no such widget in this container");

	} // end moveToTop

	void Container::moveToBottom(Widget* widget)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				mWidgets.push_front(widget);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::moveToBottom. There is no such widget in this container");

	} // end moveToBottom

	void Container::_announceDeath(Widget *widget)
	{
		if (mWidgetWithMouse == widget)
		{
			mWidgetWithMouse = NULL;
		}

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::_announceDeath. There is no such widget in this container");

	} // end _announceDeath

	void Container::getDrawSize(int& width, int& height, Widget* widget)
	{
		WidgetIterator iter;
		bool contains = false;

		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (widget == *iter)
			{
				contains = true;
				break;
			}
		}

		if (contains)
		{
			Rectangle widgetDim = widget->getDimension();
			Rectangle dim = getDimension();

			width = widgetDim.width;
			height = widgetDim.height;

			if (widgetDim.x < 0)
			{
				width += widgetDim.x;
			}

			if (widgetDim.y < 0)
			{
				height += widgetDim.y;
			}

			if (widgetDim.x + widgetDim.width > dim.width)
			{
				width -= (widgetDim.x + widgetDim.width) - dim.width;
			}

			if (widgetDim.y + widgetDim.height > dim.height)
			{
				height -= (widgetDim.y + widgetDim.height) - dim.height;
			}

			if (width < 0)
			{
				width = 0;
			}

			if (height < 0)
			{
				height = 0;
			}
		}
		else
		{
			throw GCN_EXCEPTION("Container::getDrawSize. Widget not in container");
		}

	} // end getDrawSize

	void Container::add(Widget* widget)
	{
		mWidgets.push_back(widget);
		widget->_setFocusHandler(_getFocusHandler());
		widget->_setParent(this);
		// TODO: Give it the mousetypehandler too!

	} // add

	void Container::add(Widget* widget, int x, int y)
	{
		widget->setPosition(x, y);
		add(widget);

	} // end add

	void Container::remove(Widget* widget)
	{
		if (mWidgetWithMouse == widget)
		{
			mWidgetWithMouse = NULL;
		}

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				widget->_setFocusHandler(NULL);
				widget->_setParent(NULL);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::remove. There is no such widget in this container");

	} // end remove

	void Container::clear()
	{
		mWidgetWithMouse = NULL;

		WidgetIterator iter;

		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->_setFocusHandler(NULL);
			(*iter)->_setParent(NULL);
		}

		mWidgets.clear();

	} // end clear

	void Container::_setFocusHandler(FocusHandler* focusHandler)
	{
		Widget::_setFocusHandler(focusHandler);

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->_setFocusHandler(focusHandler);
		}

	} // end _setFocusHandler

	void Container::_mouseInputMessage(const MouseInput &mouseInput)
	{
		Widget* tempWidgetWithMouse = NULL;

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if ((*iter)->getDimension().isPointInRect(mouseInput.x, mouseInput.y))
			{
				tempWidgetWithMouse = (*iter);
			}
		}

		if (tempWidgetWithMouse != mWidgetWithMouse)
		{
			if (mWidgetWithMouse)
			{
				mWidgetWithMouse->_mouseOutMessage();
			}

			if (tempWidgetWithMouse)
			{
				tempWidgetWithMouse->_mouseInMessage();
			}

			mWidgetWithMouse = tempWidgetWithMouse;
		}

		if (mWidgetWithMouse && !mWidgetWithMouse->hasFocus())
		{
			MouseInput mi = mouseInput;
			mi.x -= mWidgetWithMouse->getX();
			mi.y -= mWidgetWithMouse->getY();
			mWidgetWithMouse->_mouseInputMessage(mi);
		}

		if (mWidgetWithMouse == NULL)
		{
			Widget::_mouseInputMessage(mouseInput);
		}

	} //end _mouseInputMessage

	void Container::_mouseOutMessage()
	{
		if (mWidgetWithMouse)
		{
			mWidgetWithMouse->_mouseOutMessage();
			mWidgetWithMouse = NULL;
		}

		Widget::_mouseOutMessage();

	} // end _mouseOutMessage

} // end gcn
<commit_msg>Fixed a bug: If you had a two containers, both with buttons, and the first one you added were visible but not the last, then the last one would shadow the first when it comes to mouseinput. I corrected this with a visible check in _mouseInput for widgets. Widgets not visible should'nt reviece any mouse input.<commit_after>/*      _______   __   __   __   ______   __   __   _______   __   __
 *     / _____/\ / /\ / /\ / /\ / ____/\ / /\ / /\ / ___  /\ /  |\/ /\
 *    / /\____\// / // / // / // /\___\// /_// / // /\_/ / // , |/ / /
 *   / / /__   / / // / // / // / /    / ___  / // ___  / // /| ' / /
 *  / /_// /\ / /_// / // / // /_/_   / / // / // /\_/ / // / |  / /
 * /______/ //______/ //_/ //_____/\ /_/ //_/ //_/ //_/ //_/ /|_/ /
 * \______\/ \______\/ \_\/ \_____\/ \_\/ \_\/ \_\/ \_\/ \_\/ \_\/
 *
 * Copyright (c) 2004 darkbits                              Js_./
 * Per Larsson a.k.a finalman                          _RqZ{a<^_aa
 * Olof Naessn a.k.a jansem/yakslem                _asww7!uY`>  )\a//
 *                                                 _Qhm`] _f "'c  1!5m
 * Visit: http://guichan.darkbits.org             )Qk<P ` _: :+' .'  "{[
 *                                               .)j(] .d_/ '-(  P .   S
 * License: (BSD)                                <Td/Z <fP"5(\"??"\a.  .L
 * Redistribution and use in source and          _dV>ws?a-?'      ._/L  #'
 * binary forms, with or without                 )4d[#7r, .   '     )d`)[
 * modification, are permitted provided         _Q-5'5W..j/?'   -?!\)cam'
 * that the following conditions are met:       j<<WP+k/);.        _W=j f
 * 1. Redistributions of source code must       .$%w\/]Q  . ."'  .  mj$
 *    retain the above copyright notice,        ]E.pYY(Q]>.   a     J@\
 *    this list of conditions and the           j(]1u<sE"L,. .   ./^ ]{a
 *    following disclaimer.                     4'_uomm\.  )L);-4     (3=
 * 2. Redistributions in binary form must        )_]X{Z('a_"a7'<a"a,  ]"[
 *    reproduce the above copyright notice,       #}<]m7`Za??4,P-"'7. ).m
 *    this list of conditions and the            ]d2e)Q(<Q(  ?94   b-  LQ/
 *    following disclaimer in the                <B!</]C)d_, '(<' .f. =C+m
 *    documentation and/or other materials      .Z!=J ]e []('-4f _ ) -.)m]'
 *    provided with the distribution.          .w[5]' _[ /.)_-"+?   _/ <W"
 * 3. Neither the name of Guichan nor the      :$we` _! + _/ .        j?
 *    names of its contributors may be used     =3)= _f  (_yQmWW$#(    "
 *    to endorse or promote products derived     -   W,  sQQQQmZQ#Wwa]..
 *    from this software without specific        (js, \[QQW$QWW#?!V"".
 *    prior written permission.                    ]y:.<\..          .
 *                                                 -]n w/ '         [.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT       )/ )/           !
 * HOLDERS AND CONTRIBUTORS "AS IS" AND ANY         <  (; sac    ,    '
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING,               ]^ .-  %
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF            c <   r
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR            aga<  <La
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE          5%  )P'-3L
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR        _bQf` y`..)a
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,          ,J?4P'.P"_(\?d'.,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES               _Pa,)!f/<[]/  ?"
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT      _2-..:. .r+_,.. .
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,     ?a.<%"'  " -'.a_ _,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION)                     ^
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * For comments regarding functions please see the header file.
 */

#include "guichan/exception.hpp"
#include "guichan/widgets/container.hpp"

namespace gcn
{

	Container::Container()
	{
		mWidgetWithMouse = NULL;
		mOpaque = true;

	} // end Container

	Container::~Container()
	{
		clear();

	} // end ~Container

	void Container::logic()
	{
		logicChildren();

	} // end logic

	void Container::draw(Graphics* graphics)
	{
		if (mOpaque)
		{
			graphics->setColor(getBaseColor());
			graphics->fillRectangle(Rectangle(0, 0, getDimension().width, getDimension().height));
		}

		drawChildren(graphics);

	} // draw

	void Container::logicChildren()
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->logic();
		}

	} // end logicChildren

	void Container::drawChildren(Graphics* graphics)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if ((*iter)->isVisible())
			{
				graphics->pushClipArea((*iter)->getDimension());
				(*iter)->draw(graphics);
				graphics->popClipArea();
			}
		}

	} // end drawChildren

	void Container::setOpaque(bool opaque)
	{
		mOpaque = opaque;

	} // end setOpaque

	bool Container::isOpaque() const
	{
		return mOpaque;

	} // end isOpaque

	void Container::moveToTop(Widget* widget)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				mWidgets.push_back(widget);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::moveToTop. There is no such widget in this container");

	} // end moveToTop

	void Container::moveToBottom(Widget* widget)
	{
		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				mWidgets.push_front(widget);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::moveToBottom. There is no such widget in this container");

	} // end moveToBottom

	void Container::_announceDeath(Widget *widget)
	{
		if (mWidgetWithMouse == widget)
		{
			mWidgetWithMouse = NULL;
		}

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::_announceDeath. There is no such widget in this container");

	} // end _announceDeath

	void Container::getDrawSize(int& width, int& height, Widget* widget)
	{
		WidgetIterator iter;
		bool contains = false;

		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (widget == *iter)
			{
				contains = true;
				break;
			}
		}

		if (contains)
		{
			Rectangle widgetDim = widget->getDimension();
			Rectangle dim = getDimension();

			width = widgetDim.width;
			height = widgetDim.height;

			if (widgetDim.x < 0)
			{
				width += widgetDim.x;
			}

			if (widgetDim.y < 0)
			{
				height += widgetDim.y;
			}

			if (widgetDim.x + widgetDim.width > dim.width)
			{
				width -= (widgetDim.x + widgetDim.width) - dim.width;
			}

			if (widgetDim.y + widgetDim.height > dim.height)
			{
				height -= (widgetDim.y + widgetDim.height) - dim.height;
			}

			if (width < 0)
			{
				width = 0;
			}

			if (height < 0)
			{
				height = 0;
			}
		}
		else
		{
			throw GCN_EXCEPTION("Container::getDrawSize. Widget not in container");
		}

	} // end getDrawSize

	void Container::add(Widget* widget)
	{
		mWidgets.push_back(widget);
		widget->_setFocusHandler(_getFocusHandler());
		widget->_setParent(this);
		// TODO: Give it the mousetypehandler too!

	} // add

	void Container::add(Widget* widget, int x, int y)
	{
		widget->setPosition(x, y);
		add(widget);

	} // end add

	void Container::remove(Widget* widget)
	{
		if (mWidgetWithMouse == widget)
		{
			mWidgetWithMouse = NULL;
		}

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if (*iter == widget)
			{
				mWidgets.erase(iter);
				widget->_setFocusHandler(NULL);
				widget->_setParent(NULL);
				return;
			}
		}

		throw GCN_EXCEPTION("Container::remove. There is no such widget in this container");

	} // end remove

	void Container::clear()
	{
		mWidgetWithMouse = NULL;

		WidgetIterator iter;

		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->_setFocusHandler(NULL);
			(*iter)->_setParent(NULL);
		}

		mWidgets.clear();

	} // end clear

	void Container::_setFocusHandler(FocusHandler* focusHandler)
	{
		Widget::_setFocusHandler(focusHandler);

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			(*iter)->_setFocusHandler(focusHandler);
		}

	} // end _setFocusHandler

	void Container::_mouseInputMessage(const MouseInput &mouseInput)
	{
		Widget* tempWidgetWithMouse = NULL;

		WidgetIterator iter;
		for (iter = mWidgets.begin(); iter != mWidgets.end(); iter++)
		{
			if ((*iter)->getDimension().isPointInRect(mouseInput.x, mouseInput.y)
					&& (*iter)->isVisible())
			{
				tempWidgetWithMouse = (*iter);
			}
		}

		if (tempWidgetWithMouse != mWidgetWithMouse)
		{
			if (mWidgetWithMouse)
			{
				mWidgetWithMouse->_mouseOutMessage();
			}

			if (tempWidgetWithMouse)
			{
				tempWidgetWithMouse->_mouseInMessage();
			}

			mWidgetWithMouse = tempWidgetWithMouse;
		}

		if (mWidgetWithMouse && !mWidgetWithMouse->hasFocus())
		{
			MouseInput mi = mouseInput;
			mi.x -= mWidgetWithMouse->getX();
			mi.y -= mWidgetWithMouse->getY();
			mWidgetWithMouse->_mouseInputMessage(mi);
		}

		if (mWidgetWithMouse == NULL)
		{
			Widget::_mouseInputMessage(mouseInput);
		}

	} //end _mouseInputMessage

	void Container::_mouseOutMessage()
	{
		if (mWidgetWithMouse)
		{
			mWidgetWithMouse->_mouseOutMessage();
			mWidgetWithMouse = NULL;
		}

		Widget::_mouseOutMessage();

	} // end _mouseOutMessage

} // end gcn
<|endoftext|>


<commit_before>/*

This file is part of VROOM.

Copyright (c) 2015-2018, Julien Coupey.
All rights reserved (see LICENSE).

*/

#include "tsp.h"
#include "../../structures/vroom/input/input.h"

tsp::tsp(const input& input,
         std::vector<index_t> problem_indices,
         index_t vehicle_rank)
  : vrp(input),
    _vehicle_rank(vehicle_rank),
    _tsp_index_to_global(std::move(problem_indices)),
    _matrix(_input._matrix.get_sub_matrix(_tsp_index_to_global)),
    _symmetrized_matrix(_matrix.size()),
    _is_symmetric(true),
    _has_start(_input._vehicles[_vehicle_rank].has_start()),
    _has_end(_input._vehicles[_vehicle_rank].has_end()) {

  // Distances on the diagonal are never used except in the minimum
  // weight perfect matching (munkres call during the heuristic). This
  // makes sure no node will be matched with itself at that time.
  for (index_t i = 0; i < _matrix.size(); ++i) {
    _matrix[i][i] = INFINITE_COST;
  }

  if (_has_start) {
    // Use index in _matrix for start.
    auto search =
      std::find(_tsp_index_to_global.begin(),
                _tsp_index_to_global.end(),
                _input._vehicles[_vehicle_rank].start.get().index());
    assert(search != _tsp_index_to_global.end());
    _start = std::distance(_tsp_index_to_global.begin(), search);
    assert(_start < _matrix.size());
  }
  if (_has_end) {
    // Use index in _matrix for start.
    auto search = std::find(_tsp_index_to_global.begin(),
                            _tsp_index_to_global.end(),
                            _input._vehicles[_vehicle_rank].end.get().index());
    assert(search != _tsp_index_to_global.end());
    _end = std::distance(_tsp_index_to_global.begin(), search);
    assert(_end < _matrix.size());
  }

  _round_trip = _has_start and _has_end and (_start == _end);

  if (!_round_trip) {
    // Dealing with open tour cases. Exactly one of the following
    // happens.
    if (_has_start and !_has_end) {
      // Forcing first location as start, end location decided during
      // optimization.
      for (index_t i = 0; i < _matrix.size(); ++i) {
        if (i != _start) {
          _matrix[i][_start] = 0;
        }
      }
    }
    if (!_has_start and _has_end) {
      // Forcing last location as end, start location decided during
      // optimization.
      for (index_t j = 0; j < _matrix.size(); ++j) {
        if (j != _end) {
          _matrix[_end][j] = 0;
        }
      }
    }
    if (_has_start and _has_end) {
      // Forcing first location as start, last location as end to
      // produce an open tour.
      assert(_start != _end);
      _matrix[_end][_start] = 0;
      for (index_t j = 0; j < _matrix.size(); ++j) {
        if ((j != _start) and (j != _end)) {
          _matrix[_end][j] = INFINITE_COST;
        }
      }
    }
  }

  // Compute symmetrized matrix and update _is_symmetric flag.
  const cost_t& (*sym_f)(const cost_t&, const cost_t&) = std::min<cost_t>;
  if ((_has_start and !_has_end) or (!_has_start and _has_end)) {
    // Using symmetrization with max as when only start or only end is
    // forced, the matrix has a line or a column filled with zeros.
    sym_f = std::max<cost_t>;
  }
  for (index_t i = 0; i < _matrix.size(); ++i) {
    _symmetrized_matrix[i][i] = _matrix[i][i];
    for (index_t j = i + 1; j < _matrix.size(); ++j) {
      _is_symmetric &= (_matrix[i][j] == _matrix[j][i]);
      cost_t val = sym_f(_matrix[i][j], _matrix[j][i]);
      _symmetrized_matrix[i][j] = val;
      _symmetrized_matrix[j][i] = val;
    }
  }
}

cost_t tsp::cost(const std::list<index_t>& tour) const {
  cost_t cost = 0;
  index_t init_step = 0; // Initialization actually never used.

  auto step = tour.cbegin();
  if (tour.size() > 0) {
    init_step = *step;
  }

  index_t previous_step = init_step;
  ++step;
  for (; step != tour.cend(); ++step) {
    cost += _matrix[previous_step][*step];
    previous_step = *step;
  }
  if (tour.size() > 0) {
    cost += _matrix[previous_step][init_step];
  }
  return cost;
}

cost_t tsp::symmetrized_cost(const std::list<index_t>& tour) const {
  cost_t cost = 0;
  index_t init_step = 0; // Initialization actually never used.

  auto step = tour.cbegin();
  if (tour.size() > 0) {
    init_step = *step;
  }

  index_t previous_step = init_step;
  ++step;
  for (; step != tour.cend(); ++step) {
    cost += _symmetrized_matrix[previous_step][*step];
    previous_step = *step;
  }
  if (tour.size() > 0) {
    cost += _symmetrized_matrix[previous_step][init_step];
  }
  return cost;
}

solution tsp::solve(unsigned nb_threads) const {
  // Applying heuristic.
  auto start_heuristic = std::chrono::high_resolution_clock::now();
  BOOST_LOG_TRIVIAL(info) << "[Heuristic] Start heuristic on symmetrized "
                             "problem.";

  std::list<index_t> christo_sol = christofides(_symmetrized_matrix);
  cost_t christo_cost = this->symmetrized_cost(christo_sol);

  auto end_heuristic = std::chrono::high_resolution_clock::now();

  auto heuristic_computing_time =
    std::chrono::duration_cast<std::chrono::milliseconds>(end_heuristic -
                                                          start_heuristic)
      .count();

  BOOST_LOG_TRIVIAL(info) << "[Heuristic] Done, took "
                          << heuristic_computing_time << " ms.";

  BOOST_LOG_TRIVIAL(info) << "[Heuristic] Symmetric solution cost is "
                          << christo_cost << ".";

  // Local search on symmetric problem.
  // Applying deterministic, fast local search to improve the current
  // solution in a small amount of time. All possible moves for the
  // different neighbourhoods are performed, stopping when reaching a
  // local minima.
  auto start_sym_local_search = std::chrono::high_resolution_clock::now();
  BOOST_LOG_TRIVIAL(info) << "[Local search] Start local search on symmetrized "
                             "problem.";
  BOOST_LOG_TRIVIAL(info) << "[Local search] Using " << nb_threads
                          << " thread(s).";

  local_search sym_ls(_symmetrized_matrix,
                      true, // Symmetrized problem.
                      christo_sol,
                      nb_threads);

  cost_t sym_two_opt_gain = 0;
  cost_t sym_relocate_gain = 0;
  cost_t sym_or_opt_gain = 0;

  do {
    // All possible 2-opt moves.
    sym_two_opt_gain = sym_ls.perform_all_two_opt_steps();

    // All relocate moves.
    sym_relocate_gain = sym_ls.perform_all_relocate_steps();

    // All or-opt moves.
    sym_or_opt_gain = sym_ls.perform_all_or_opt_steps();
  } while ((sym_two_opt_gain > 0) or (sym_relocate_gain > 0) or
           (sym_or_opt_gain > 0));

  index_t first_loc_index;
  if (_has_start) {
    // Use start value set in constructor from vehicle input.
    first_loc_index = _start;
  } else {
    assert(_has_end);
    // Requiring the tour to be described from the "forced" end
    // location.
    first_loc_index = _end;
  }

  std::list<index_t> current_sol = sym_ls.get_tour(first_loc_index);
  auto current_cost = this->symmetrized_cost(current_sol);

  auto end_sym_local_search = std::chrono::high_resolution_clock::now();

  auto sym_local_search_duration =
    std::chrono::duration_cast<std::chrono::milliseconds>(
      end_sym_local_search - start_sym_local_search)
      .count();
  BOOST_LOG_TRIVIAL(info) << "[Local search] Done, took "
                          << sym_local_search_duration << " ms.";

  BOOST_LOG_TRIVIAL(info) << "[Local search] Symmetric solution cost is now "
                          << current_cost << " (" << std::fixed
                          << std::setprecision(2)
                          << 100 * (((double)current_cost) / christo_cost - 1)
                          << "%).";

  auto asym_local_search_duration = 0;

  if (!_is_symmetric) {
    auto start_asym_local_search = std::chrono::high_resolution_clock::now();

    // Back to the asymmetric problem, picking the best way.
    std::list<index_t> reverse_current_sol(current_sol);
    reverse_current_sol.reverse();
    cost_t direct_cost = this->cost(current_sol);
    cost_t reverse_cost = this->cost(reverse_current_sol);

    // Cost reference after symmetric local search.
    cost_t sym_ls_cost = std::min(direct_cost, reverse_cost);

    // Local search on asymmetric problem.
    local_search asym_ls(_matrix,
                         false, // Not the symmetrized problem.
                         (direct_cost <= reverse_cost) ? current_sol
                                                       : reverse_current_sol,
                         nb_threads);

    BOOST_LOG_TRIVIAL(info) << "[Asym. local search] Back to asymmetric "
                               "problem, initial solution cost is "
                            << sym_ls_cost << ".";

    BOOST_LOG_TRIVIAL(info)
      << "[Asym. local search] Start local search on asymmetric problem.";

    BOOST_LOG_TRIVIAL(info) << "[Asym. local search] Using " << nb_threads
                            << " thread(s).";

    cost_t asym_two_opt_gain = 0;
    cost_t asym_relocate_gain = 0;
    cost_t asym_or_opt_gain = 0;
    cost_t asym_avoid_loops_gain = 0;

    do {
      // All avoid-loops moves.
      asym_avoid_loops_gain = asym_ls.perform_all_avoid_loop_steps();

      // All possible 2-opt moves.
      asym_two_opt_gain = asym_ls.perform_all_asym_two_opt_steps();

      // All relocate moves.
      asym_relocate_gain = asym_ls.perform_all_relocate_steps();

      // All or-opt moves.
      asym_or_opt_gain = asym_ls.perform_all_or_opt_steps();
    } while ((asym_two_opt_gain > 0) or (asym_relocate_gain > 0) or
             (asym_or_opt_gain > 0) or (asym_avoid_loops_gain > 0));

    current_sol = asym_ls.get_tour(first_loc_index);
    current_cost = this->cost(current_sol);

    auto end_asym_local_search = std::chrono::high_resolution_clock::now();

    asym_local_search_duration =
      std::chrono::duration_cast<std::chrono::milliseconds>(
        end_asym_local_search - start_asym_local_search)
        .count();
    BOOST_LOG_TRIVIAL(info) << "[Asym. local search] Done, took "
                            << asym_local_search_duration << " ms.";

    BOOST_LOG_TRIVIAL(info)
      << "[Asym. local search] Asymmetric solution cost is now " << current_cost
      << " (" << std::fixed << std::setprecision(2)
      << 100 * (((double)current_cost) / sym_ls_cost - 1) << "%).";
  }

  // Deal with open tour cases requiring adaptation.
  if (!_has_start and _has_end) {
    // The tour has been listed starting with the "forced" end. This
    // index has to be popped and put back, the next element being the
    // chosen start resulting from the optimization.
    current_sol.push_back(current_sol.front());
    current_sol.pop_front();
  }

  // Steps for the one route.
  std::vector<step> steps;

  // Handle start.
  auto job_start = current_sol.cbegin();
  if (_has_start) {
    // Add start step.
    assert(current_sol.front() == _start);
    steps.emplace_back(TYPE::START,
                       _input._vehicles[_vehicle_rank].start.get());
    // Remember that jobs start further away in the list.
    ++job_start;
  }
  // Determine end index and where to stop for last job.
  auto job_end = current_sol.cend();
  index_t end_index = current_sol.back();

  if (_round_trip) {
    end_index = _start;
  } else {
    if (_has_end) {
      assert(current_sol.back() == end_index);
      --job_end;
    }
  }

  // Handle jobs.
  for (auto job = job_start; job != job_end; ++job) {
    assert(_input._type_with_ids[*job].type == TYPE::JOB);
    steps.emplace_back(TYPE::JOB,
                       _input._locations[*job],
                       _input._type_with_ids[*job].id);
  }
  // Handle end.
  if (_has_end) {
    // Add end step.
    steps.emplace_back(TYPE::END, _input._vehicles[_vehicle_rank].end.get());
  }

  // Route.
  std::vector<route_t> routes;
  routes.emplace_back(_input._vehicles[_vehicle_rank].id, steps, current_cost);

  solution sol(0, std::move(routes), current_cost);

  return sol;
}
<commit_msg>Adjust determination of tsp::_start and tsp::_end.<commit_after>/*

This file is part of VROOM.

Copyright (c) 2015-2018, Julien Coupey.
All rights reserved (see LICENSE).

*/

#include "tsp.h"
#include "../../structures/vroom/input/input.h"

tsp::tsp(const input& input,
         std::vector<index_t> problem_indices,
         index_t vehicle_rank)
  : vrp(input),
    _vehicle_rank(vehicle_rank),
    _tsp_index_to_global(std::move(problem_indices)),
    _matrix(_input._matrix.get_sub_matrix(_tsp_index_to_global)),
    _symmetrized_matrix(_matrix.size()),
    _is_symmetric(true),
    _has_start(_input._vehicles[_vehicle_rank].has_start()),
    _has_end(_input._vehicles[_vehicle_rank].has_end()) {

  // Distances on the diagonal are never used except in the minimum
  // weight perfect matching (munkres call during the heuristic). This
  // makes sure no node will be matched with itself at that time.
  for (index_t i = 0; i < _matrix.size(); ++i) {
    _matrix[i][i] = INFINITE_COST;
  }

  if (_has_start) {
    // Use index in _matrix for start.
    bool start_found;
    for (index_t i = 0; i < _tsp_index_to_global.size(); ++i) {
      start_found = (_input._type_with_ids[i].type == TYPE::START) and
        (_tsp_index_to_global[i] == _input._vehicles[_vehicle_rank].start.get().index());
      if (start_found) {
        _start = i;
        break;
      }
    }
    assert(start_found);
  }
  if (_has_end) {
    // Use index in _matrix for end.
    bool end_found;
    for (index_t i = 0; i < _tsp_index_to_global.size(); ++i) {
      // Not checking for TYPE::END here because when start_index and
      // end_index are both provided and equal, then the end location
      // is not duplicated in input::_locations.
      end_found = (_input._type_with_ids[i].type != TYPE::JOB) and
        (_tsp_index_to_global[i] == _input._vehicles[_vehicle_rank].end.get().index());
      if (end_found) {
        _end = i;
        break;
      }
    }
    assert(end_found);
  }

  _round_trip = _has_start and _has_end and (_start == _end);

  if (!_round_trip) {
    // Dealing with open tour cases. Exactly one of the following
    // happens.
    if (_has_start and !_has_end) {
      // Forcing first location as start, end location decided during
      // optimization.
      for (index_t i = 0; i < _matrix.size(); ++i) {
        if (i != _start) {
          _matrix[i][_start] = 0;
        }
      }
    }
    if (!_has_start and _has_end) {
      // Forcing last location as end, start location decided during
      // optimization.
      for (index_t j = 0; j < _matrix.size(); ++j) {
        if (j != _end) {
          _matrix[_end][j] = 0;
        }
      }
    }
    if (_has_start and _has_end) {
      // Forcing first location as start, last location as end to
      // produce an open tour.
      assert(_start != _end);
      _matrix[_end][_start] = 0;
      for (index_t j = 0; j < _matrix.size(); ++j) {
        if ((j != _start) and (j != _end)) {
          _matrix[_end][j] = INFINITE_COST;
        }
      }
    }
  }

  // Compute symmetrized matrix and update _is_symmetric flag.
  const cost_t& (*sym_f)(const cost_t&, const cost_t&) = std::min<cost_t>;
  if ((_has_start and !_has_end) or (!_has_start and _has_end)) {
    // Using symmetrization with max as when only start or only end is
    // forced, the matrix has a line or a column filled with zeros.
    sym_f = std::max<cost_t>;
  }
  for (index_t i = 0; i < _matrix.size(); ++i) {
    _symmetrized_matrix[i][i] = _matrix[i][i];
    for (index_t j = i + 1; j < _matrix.size(); ++j) {
      _is_symmetric &= (_matrix[i][j] == _matrix[j][i]);
      cost_t val = sym_f(_matrix[i][j], _matrix[j][i]);
      _symmetrized_matrix[i][j] = val;
      _symmetrized_matrix[j][i] = val;
    }
  }
}

cost_t tsp::cost(const std::list<index_t>& tour) const {
  cost_t cost = 0;
  index_t init_step = 0; // Initialization actually never used.

  auto step = tour.cbegin();
  if (tour.size() > 0) {
    init_step = *step;
  }

  index_t previous_step = init_step;
  ++step;
  for (; step != tour.cend(); ++step) {
    cost += _matrix[previous_step][*step];
    previous_step = *step;
  }
  if (tour.size() > 0) {
    cost += _matrix[previous_step][init_step];
  }
  return cost;
}

cost_t tsp::symmetrized_cost(const std::list<index_t>& tour) const {
  cost_t cost = 0;
  index_t init_step = 0; // Initialization actually never used.

  auto step = tour.cbegin();
  if (tour.size() > 0) {
    init_step = *step;
  }

  index_t previous_step = init_step;
  ++step;
  for (; step != tour.cend(); ++step) {
    cost += _symmetrized_matrix[previous_step][*step];
    previous_step = *step;
  }
  if (tour.size() > 0) {
    cost += _symmetrized_matrix[previous_step][init_step];
  }
  return cost;
}

solution tsp::solve(unsigned nb_threads) const {
  // Applying heuristic.
  auto start_heuristic = std::chrono::high_resolution_clock::now();
  BOOST_LOG_TRIVIAL(info) << "[Heuristic] Start heuristic on symmetrized "
                             "problem.";

  std::list<index_t> christo_sol = christofides(_symmetrized_matrix);
  cost_t christo_cost = this->symmetrized_cost(christo_sol);

  auto end_heuristic = std::chrono::high_resolution_clock::now();

  auto heuristic_computing_time =
    std::chrono::duration_cast<std::chrono::milliseconds>(end_heuristic -
                                                          start_heuristic)
      .count();

  BOOST_LOG_TRIVIAL(info) << "[Heuristic] Done, took "
                          << heuristic_computing_time << " ms.";

  BOOST_LOG_TRIVIAL(info) << "[Heuristic] Symmetric solution cost is "
                          << christo_cost << ".";

  // Local search on symmetric problem.
  // Applying deterministic, fast local search to improve the current
  // solution in a small amount of time. All possible moves for the
  // different neighbourhoods are performed, stopping when reaching a
  // local minima.
  auto start_sym_local_search = std::chrono::high_resolution_clock::now();
  BOOST_LOG_TRIVIAL(info) << "[Local search] Start local search on symmetrized "
                             "problem.";
  BOOST_LOG_TRIVIAL(info) << "[Local search] Using " << nb_threads
                          << " thread(s).";

  local_search sym_ls(_symmetrized_matrix,
                      true, // Symmetrized problem.
                      christo_sol,
                      nb_threads);

  cost_t sym_two_opt_gain = 0;
  cost_t sym_relocate_gain = 0;
  cost_t sym_or_opt_gain = 0;

  do {
    // All possible 2-opt moves.
    sym_two_opt_gain = sym_ls.perform_all_two_opt_steps();

    // All relocate moves.
    sym_relocate_gain = sym_ls.perform_all_relocate_steps();

    // All or-opt moves.
    sym_or_opt_gain = sym_ls.perform_all_or_opt_steps();
  } while ((sym_two_opt_gain > 0) or (sym_relocate_gain > 0) or
           (sym_or_opt_gain > 0));

  index_t first_loc_index;
  if (_has_start) {
    // Use start value set in constructor from vehicle input.
    first_loc_index = _start;
  } else {
    assert(_has_end);
    // Requiring the tour to be described from the "forced" end
    // location.
    first_loc_index = _end;
  }

  std::list<index_t> current_sol = sym_ls.get_tour(first_loc_index);
  auto current_cost = this->symmetrized_cost(current_sol);

  auto end_sym_local_search = std::chrono::high_resolution_clock::now();

  auto sym_local_search_duration =
    std::chrono::duration_cast<std::chrono::milliseconds>(
      end_sym_local_search - start_sym_local_search)
      .count();
  BOOST_LOG_TRIVIAL(info) << "[Local search] Done, took "
                          << sym_local_search_duration << " ms.";

  BOOST_LOG_TRIVIAL(info) << "[Local search] Symmetric solution cost is now "
                          << current_cost << " (" << std::fixed
                          << std::setprecision(2)
                          << 100 * (((double)current_cost) / christo_cost - 1)
                          << "%).";

  auto asym_local_search_duration = 0;

  if (!_is_symmetric) {
    auto start_asym_local_search = std::chrono::high_resolution_clock::now();

    // Back to the asymmetric problem, picking the best way.
    std::list<index_t> reverse_current_sol(current_sol);
    reverse_current_sol.reverse();
    cost_t direct_cost = this->cost(current_sol);
    cost_t reverse_cost = this->cost(reverse_current_sol);

    // Cost reference after symmetric local search.
    cost_t sym_ls_cost = std::min(direct_cost, reverse_cost);

    // Local search on asymmetric problem.
    local_search asym_ls(_matrix,
                         false, // Not the symmetrized problem.
                         (direct_cost <= reverse_cost) ? current_sol
                                                       : reverse_current_sol,
                         nb_threads);

    BOOST_LOG_TRIVIAL(info) << "[Asym. local search] Back to asymmetric "
                               "problem, initial solution cost is "
                            << sym_ls_cost << ".";

    BOOST_LOG_TRIVIAL(info)
      << "[Asym. local search] Start local search on asymmetric problem.";

    BOOST_LOG_TRIVIAL(info) << "[Asym. local search] Using " << nb_threads
                            << " thread(s).";

    cost_t asym_two_opt_gain = 0;
    cost_t asym_relocate_gain = 0;
    cost_t asym_or_opt_gain = 0;
    cost_t asym_avoid_loops_gain = 0;

    do {
      // All avoid-loops moves.
      asym_avoid_loops_gain = asym_ls.perform_all_avoid_loop_steps();

      // All possible 2-opt moves.
      asym_two_opt_gain = asym_ls.perform_all_asym_two_opt_steps();

      // All relocate moves.
      asym_relocate_gain = asym_ls.perform_all_relocate_steps();

      // All or-opt moves.
      asym_or_opt_gain = asym_ls.perform_all_or_opt_steps();
    } while ((asym_two_opt_gain > 0) or (asym_relocate_gain > 0) or
             (asym_or_opt_gain > 0) or (asym_avoid_loops_gain > 0));

    current_sol = asym_ls.get_tour(first_loc_index);
    current_cost = this->cost(current_sol);

    auto end_asym_local_search = std::chrono::high_resolution_clock::now();

    asym_local_search_duration =
      std::chrono::duration_cast<std::chrono::milliseconds>(
        end_asym_local_search - start_asym_local_search)
        .count();
    BOOST_LOG_TRIVIAL(info) << "[Asym. local search] Done, took "
                            << asym_local_search_duration << " ms.";

    BOOST_LOG_TRIVIAL(info)
      << "[Asym. local search] Asymmetric solution cost is now " << current_cost
      << " (" << std::fixed << std::setprecision(2)
      << 100 * (((double)current_cost) / sym_ls_cost - 1) << "%).";
  }

  // Deal with open tour cases requiring adaptation.
  if (!_has_start and _has_end) {
    // The tour has been listed starting with the "forced" end. This
    // index has to be popped and put back, the next element being the
    // chosen start resulting from the optimization.
    current_sol.push_back(current_sol.front());
    current_sol.pop_front();
  }

  // Steps for the one route.
  std::vector<step> steps;

  // Handle start.
  auto job_start = current_sol.cbegin();
  if (_has_start) {
    // Add start step.
    assert(current_sol.front() == _start);
    steps.emplace_back(TYPE::START,
                       _input._vehicles[_vehicle_rank].start.get());
    // Remember that jobs start further away in the list.
    ++job_start;
  }
  // Determine end index and where to stop for last job.
  auto job_end = current_sol.cend();
  index_t end_index = current_sol.back();

  if (_round_trip) {
    end_index = _start;
  } else {
    if (_has_end) {
      assert(current_sol.back() == end_index);
      --job_end;
    }
  }

  // Handle jobs.
  for (auto job = job_start; job != job_end; ++job) {
    assert(_input._type_with_ids[*job].type == TYPE::JOB);
    steps.emplace_back(TYPE::JOB,
                       _input._locations[*job],
                       _input._type_with_ids[*job].id);
  }
  // Handle end.
  if (_has_end) {
    // Add end step.
    steps.emplace_back(TYPE::END, _input._vehicles[_vehicle_rank].end.get());
  }

  // Route.
  std::vector<route_t> routes;
  routes.emplace_back(_input._vehicles[_vehicle_rank].id, steps, current_cost);

  solution sol(0, std::move(routes), current_cost);

  return sol;
}
<|endoftext|>


<commit_before>#include <common/endian.h>

#include <event/event_callback.h>

#include <http/http_protocol.h>

#include <io/pipe/pipe.h>
#include <io/pipe/pipe_producer.h>

#include <ssh/ssh_algorithm_negotiation.h>
#include <ssh/ssh_protocol.h>
#include <ssh/ssh_session.h>
#include <ssh/ssh_transport_pipe.h>

namespace {
	static uint8_t zero_padding[255];
}

SSH::TransportPipe::TransportPipe(Session *session)
: PipeProducer("/ssh/transport/pipe"),
  session_(session),
  state_(GetIdentificationString),
  block_size_(8),
  mac_length_(0),
  receive_callback_(NULL),
  receive_action_(NULL)
{
	Buffer identification_string("SSH-2.0-WANProxy " + (std::string)log_ + "\r\n");
	session_->local_version(identification_string);
	produce(&identification_string);
}

SSH::TransportPipe::~TransportPipe()
{
	ASSERT(log_, receive_callback_ == NULL);
	ASSERT(log_, receive_action_ == NULL);
}

Action *
SSH::TransportPipe::receive(EventCallback *cb)
{
	ASSERT(log_, receive_callback_ == NULL);
	ASSERT(log_, receive_action_ == NULL);

	/*
	 * XXX
	 * This pattern is implemented about three different
	 * ways in the WANProxy codebase, and they are all kind
	 * of awful.  Need a better way to handle code that
	 * needs executed either on the request of the caller
	 * or when data comes in that satisfies a deferred
	 * callback to a caller.
	 *
	 * Would an EventDropbox suffice?  Or something like?
	 */
	receive_callback_ = cb;
	receive_do();

	if (receive_callback_ != NULL)
		return (cancellation(this, &SSH::TransportPipe::receive_cancel));

	ASSERT(log_, receive_action_ != NULL);
	Action *a = receive_action_;
	receive_action_ = NULL;
	return (a);
}

/*
 * Because this is primarily for WAN optimization we always use minimal
 * padding and zero padding.  Quick and dirty.  Perhaps revisit later,
 * although it makes send() asynchronous unless we add a blocking
 * RNG interface.
 */
void
SSH::TransportPipe::send(Buffer *payload)
{
	Buffer packet;
	uint8_t padding_len;
	uint32_t packet_len;

	ASSERT(log_, state_ == GetPacket);

	packet_len = sizeof padding_len + payload->length();
	padding_len = 4 + (block_size_ - ((sizeof packet_len + packet_len + 4) % block_size_));
	packet_len += padding_len;

	packet_len = BigEndian::encode(packet_len);
	packet.append(&packet_len);
	packet.append(padding_len);
	payload->moveout(&packet);
	packet.append(zero_padding, padding_len);

	if (mac_length_ != 0)
		NOTREACHED(log_);

	produce(&packet);
}

void
SSH::TransportPipe::consume(Buffer *in)
{
	/* XXX XXX XXX */
	if (in->empty()) {
		if (!input_buffer_.empty())
			DEBUG(log_) << "Received EOS with data outstanding.";
		produce_eos();
		return;
	}

	in->moveout(&input_buffer_);

	if (state_ == GetIdentificationString) {
		HTTPProtocol::ParseStatus status;

		while (!input_buffer_.empty()) {
			Buffer line, line_ending;
			status = HTTPProtocol::ExtractLine(&line, &input_buffer_, &line_ending);
			switch (status) {
			case HTTPProtocol::ParseSuccess:
				break;
			case HTTPProtocol::ParseFailure:
				ERROR(log_) << "Invalid line while waiting for identification string.";
				produce_error();
				return;
			case HTTPProtocol::ParseIncomplete:
				/* Wait for more.  */
				return;
			}

			if (!line.prefix("SSH-"))
				continue; /* Next line.  */

			if (!line.prefix("SSH-2.0")) {
				ERROR(log_) << "Unsupported version.";
				produce_error();
				return;
			}

			line.append(line_ending);
			session_->remote_version(line);

			state_ = GetPacket;
			/*
			 * XXX
			 * Should have a callback here?
			 */
			if (session_->algorithm_negotiation_ != NULL) {
				Buffer packet;
				if (session_->algorithm_negotiation_->output(&packet))
					send(&packet);
			}
			break;
		}
	}

	if (state_ == GetPacket && receive_callback_ != NULL)
		receive_do();
}

void
SSH::TransportPipe::receive_cancel(void)
{
	if (receive_action_ != NULL) {
		receive_action_->cancel();
		receive_action_ = NULL;
	}

	if (receive_callback_ != NULL) {
		delete receive_callback_;
		receive_callback_ = NULL;
	}
}

void
SSH::TransportPipe::receive_do(void)
{
	ASSERT(log_, receive_action_ == NULL);
	ASSERT(log_, receive_callback_ != NULL);

	if (state_ != GetPacket)
		return;

	while (!input_buffer_.empty()) {
		Buffer packet;
		Buffer mac;
		uint32_t packet_len;
		uint8_t padding_len;
		uint8_t msg;

		if (input_buffer_.length() <= sizeof packet_len) {
			DEBUG(log_) << "Waiting for packet length.";
			return;
		}

		input_buffer_.extract(&packet_len);
		packet_len = BigEndian::decode(packet_len);

		if (packet_len == 0) {
			ERROR(log_) << "Need to handle 0-length packet.";
			produce_error();
			return;
		}

		if (input_buffer_.length() < sizeof packet_len + packet_len + mac_length_) {
			DEBUG(log_) << "Need " << sizeof packet_len + packet_len + mac_length_ << "bytes; have " << input_buffer_.length() << ".";
			return;
		}

		input_buffer_.moveout(&packet, sizeof packet_len, packet_len);
		if (mac_length_ != 0)
			input_buffer_.moveout(&mac, 0, mac_length_);

		padding_len = packet.pop();
		if (padding_len != 0) {
			if (packet.length() < padding_len) {
				ERROR(log_) << "Padding too large for packet.";
				produce_error();
				return;
			}
			packet.trim(padding_len);
		}

		if (packet.empty()) {
			ERROR(log_) << "Need to handle empty packet.";
			produce_error();
			return;
		}

		/*
		 * Pass by range to registered handlers for each range.
		 * Unhandled messages go to the receive_callback_, and
		 * the caller can register key exchange mechanisms,
		 * and handle (or discard) whatever they don't handle.
		 *
		 * NB: The caller could do all this, but it's assumed
		 *     that they usually have better things to do.  If
		 *     they register no handlers, they can certainly do
		 *     so by hand.
		 *
		 * XXX It seems like having a separate class which handles
		 *     all these details and algorithm negotiation would be
		 *     nice, and to have this one be a bit more oriented
		 *     towards managing just the transport layer.
		 *
		 *     At the very least, it needs to take responsibility
		 *     for its failures and allow the handler functions
		 *     here to mangle the packet buffer rather than trying
		 *     to send it on to the receiver if decoding fails.
		 *     A decoding failure should result in a disconnect,
		 *     an error.
		 */
		msg = packet.peek();
		if (msg >= SSH::Message::TransportRangeBegin &&
		    msg <= SSH::Message::TransportRangeEnd) {
			DEBUG(log_) << "Using default handler for transport message.";
		} else if (msg >= SSH::Message::AlgorithmNegotiationRangeBegin &&
			   msg <= SSH::Message::AlgorithmNegotiationRangeEnd) {
			if (session_->algorithm_negotiation_ != NULL) {
				if (session_->algorithm_negotiation_->input(&packet))
					continue;
			}
			DEBUG(log_) << "Using default handler for algorithm negotiation message.";
		} else if (msg >= SSH::Message::KeyExchangeMethodRangeBegin &&
			   msg <= SSH::Message::KeyExchangeMethodRangeEnd) {
			SSH::KeyExchange *kex = session_->algorithm_negotiation_->chosen_key_exchange();
			if (kex != NULL) {
				if (kex->input(this, &packet))
					continue;
			}
			DEBUG(log_) << "Using default handler for key exchange method message.";
		} else if (msg >= SSH::Message::UserAuthenticationGenericRangeBegin &&
			   msg <= SSH::Message::UserAuthenticationGenericRangeEnd) {
			DEBUG(log_) << "Using default handler for generic user authentication message.";
		} else if (msg >= SSH::Message::UserAuthenticationMethodRangeBegin &&
			   msg <= SSH::Message::UserAuthenticationMethodRangeEnd) {
			DEBUG(log_) << "Using default handler for user authentication method message.";
		} else if (msg >= SSH::Message::ConnectionProtocolGlobalRangeBegin &&
			   msg <= SSH::Message::ConnectionProtocolGlobalRangeEnd) {
			DEBUG(log_) << "Using default handler for generic connection protocol message.";
		} else if (msg >= SSH::Message::ConnectionChannelRangeBegin &&
			   msg <= SSH::Message::ConnectionChannelRangeEnd) {
			DEBUG(log_) << "Using default handler for connection channel message.";
		} else if (msg >= SSH::Message::ClientProtocolReservedRangeBegin &&
			   msg <= SSH::Message::ClientProtocolReservedRangeEnd) {
			DEBUG(log_) << "Using default handler for client protocol message.";
		} else if (msg >= SSH::Message::LocalExtensionRangeBegin) {
			/* Because msg is a uint8_t, it will always be <= SSH::Message::LocalExtensionRangeEnd.  */
			DEBUG(log_) << "Using default handler for local extension message.";
		} else {
			ASSERT(log_, msg == 0);
			ERROR(log_) << "Message outside of protocol range received.  Passing to default handler, but not expecting much.";
		}

		receive_callback_->param(Event(Event::Done, packet));
		receive_action_ = receive_callback_->schedule();
		receive_callback_ = NULL;

		return;
	}
}
<commit_msg>Don't include line endings in session versions.  Fix a whitespace typo in a string.<commit_after>#include <common/endian.h>

#include <event/event_callback.h>

#include <http/http_protocol.h>

#include <io/pipe/pipe.h>
#include <io/pipe/pipe_producer.h>

#include <ssh/ssh_algorithm_negotiation.h>
#include <ssh/ssh_protocol.h>
#include <ssh/ssh_session.h>
#include <ssh/ssh_transport_pipe.h>

namespace {
	static uint8_t zero_padding[255];
}

SSH::TransportPipe::TransportPipe(Session *session)
: PipeProducer("/ssh/transport/pipe"),
  session_(session),
  state_(GetIdentificationString),
  block_size_(8),
  mac_length_(0),
  receive_callback_(NULL),
  receive_action_(NULL)
{
	Buffer identification_string("SSH-2.0-WANProxy " + (std::string)log_);
	session_->local_version(identification_string);
	identification_string.append("\r\n");
	produce(&identification_string);
}

SSH::TransportPipe::~TransportPipe()
{
	ASSERT(log_, receive_callback_ == NULL);
	ASSERT(log_, receive_action_ == NULL);
}

Action *
SSH::TransportPipe::receive(EventCallback *cb)
{
	ASSERT(log_, receive_callback_ == NULL);
	ASSERT(log_, receive_action_ == NULL);

	/*
	 * XXX
	 * This pattern is implemented about three different
	 * ways in the WANProxy codebase, and they are all kind
	 * of awful.  Need a better way to handle code that
	 * needs executed either on the request of the caller
	 * or when data comes in that satisfies a deferred
	 * callback to a caller.
	 *
	 * Would an EventDropbox suffice?  Or something like?
	 */
	receive_callback_ = cb;
	receive_do();

	if (receive_callback_ != NULL)
		return (cancellation(this, &SSH::TransportPipe::receive_cancel));

	ASSERT(log_, receive_action_ != NULL);
	Action *a = receive_action_;
	receive_action_ = NULL;
	return (a);
}

/*
 * Because this is primarily for WAN optimization we always use minimal
 * padding and zero padding.  Quick and dirty.  Perhaps revisit later,
 * although it makes send() asynchronous unless we add a blocking
 * RNG interface.
 */
void
SSH::TransportPipe::send(Buffer *payload)
{
	Buffer packet;
	uint8_t padding_len;
	uint32_t packet_len;

	ASSERT(log_, state_ == GetPacket);

	packet_len = sizeof padding_len + payload->length();
	padding_len = 4 + (block_size_ - ((sizeof packet_len + packet_len + 4) % block_size_));
	packet_len += padding_len;

	packet_len = BigEndian::encode(packet_len);
	packet.append(&packet_len);
	packet.append(padding_len);
	payload->moveout(&packet);
	packet.append(zero_padding, padding_len);

	if (mac_length_ != 0)
		NOTREACHED(log_);

	produce(&packet);
}

void
SSH::TransportPipe::consume(Buffer *in)
{
	/* XXX XXX XXX */
	if (in->empty()) {
		if (!input_buffer_.empty())
			DEBUG(log_) << "Received EOS with data outstanding.";
		produce_eos();
		return;
	}

	in->moveout(&input_buffer_);

	if (state_ == GetIdentificationString) {
		HTTPProtocol::ParseStatus status;

		while (!input_buffer_.empty()) {
			Buffer line;
			status = HTTPProtocol::ExtractLine(&line, &input_buffer_);
			switch (status) {
			case HTTPProtocol::ParseSuccess:
				break;
			case HTTPProtocol::ParseFailure:
				ERROR(log_) << "Invalid line while waiting for identification string.";
				produce_error();
				return;
			case HTTPProtocol::ParseIncomplete:
				/* Wait for more.  */
				return;
			}

			if (!line.prefix("SSH-"))
				continue; /* Next line.  */

			if (!line.prefix("SSH-2.0")) {
				ERROR(log_) << "Unsupported version.";
				produce_error();
				return;
			}

			session_->remote_version(line);

			state_ = GetPacket;
			/*
			 * XXX
			 * Should have a callback here?
			 */
			if (session_->algorithm_negotiation_ != NULL) {
				Buffer packet;
				if (session_->algorithm_negotiation_->output(&packet))
					send(&packet);
			}
			break;
		}
	}

	if (state_ == GetPacket && receive_callback_ != NULL)
		receive_do();
}

void
SSH::TransportPipe::receive_cancel(void)
{
	if (receive_action_ != NULL) {
		receive_action_->cancel();
		receive_action_ = NULL;
	}

	if (receive_callback_ != NULL) {
		delete receive_callback_;
		receive_callback_ = NULL;
	}
}

void
SSH::TransportPipe::receive_do(void)
{
	ASSERT(log_, receive_action_ == NULL);
	ASSERT(log_, receive_callback_ != NULL);

	if (state_ != GetPacket)
		return;

	while (!input_buffer_.empty()) {
		Buffer packet;
		Buffer mac;
		uint32_t packet_len;
		uint8_t padding_len;
		uint8_t msg;

		if (input_buffer_.length() <= sizeof packet_len) {
			DEBUG(log_) << "Waiting for packet length.";
			return;
		}

		input_buffer_.extract(&packet_len);
		packet_len = BigEndian::decode(packet_len);

		if (packet_len == 0) {
			ERROR(log_) << "Need to handle 0-length packet.";
			produce_error();
			return;
		}

		if (input_buffer_.length() < sizeof packet_len + packet_len + mac_length_) {
			DEBUG(log_) << "Need " << sizeof packet_len + packet_len + mac_length_ << " bytes; have " << input_buffer_.length() << ".";
			return;
		}

		input_buffer_.moveout(&packet, sizeof packet_len, packet_len);
		if (mac_length_ != 0)
			input_buffer_.moveout(&mac, 0, mac_length_);

		padding_len = packet.pop();
		if (padding_len != 0) {
			if (packet.length() < padding_len) {
				ERROR(log_) << "Padding too large for packet.";
				produce_error();
				return;
			}
			packet.trim(padding_len);
		}

		if (packet.empty()) {
			ERROR(log_) << "Need to handle empty packet.";
			produce_error();
			return;
		}

		/*
		 * Pass by range to registered handlers for each range.
		 * Unhandled messages go to the receive_callback_, and
		 * the caller can register key exchange mechanisms,
		 * and handle (or discard) whatever they don't handle.
		 *
		 * NB: The caller could do all this, but it's assumed
		 *     that they usually have better things to do.  If
		 *     they register no handlers, they can certainly do
		 *     so by hand.
		 *
		 * XXX It seems like having a separate class which handles
		 *     all these details and algorithm negotiation would be
		 *     nice, and to have this one be a bit more oriented
		 *     towards managing just the transport layer.
		 *
		 *     At the very least, it needs to take responsibility
		 *     for its failures and allow the handler functions
		 *     here to mangle the packet buffer rather than trying
		 *     to send it on to the receiver if decoding fails.
		 *     A decoding failure should result in a disconnect,
		 *     an error.
		 */
		msg = packet.peek();
		if (msg >= SSH::Message::TransportRangeBegin &&
		    msg <= SSH::Message::TransportRangeEnd) {
			DEBUG(log_) << "Using default handler for transport message.";
		} else if (msg >= SSH::Message::AlgorithmNegotiationRangeBegin &&
			   msg <= SSH::Message::AlgorithmNegotiationRangeEnd) {
			if (session_->algorithm_negotiation_ != NULL) {
				if (session_->algorithm_negotiation_->input(&packet))
					continue;
			}
			DEBUG(log_) << "Using default handler for algorithm negotiation message.";
		} else if (msg >= SSH::Message::KeyExchangeMethodRangeBegin &&
			   msg <= SSH::Message::KeyExchangeMethodRangeEnd) {
			SSH::KeyExchange *kex = session_->algorithm_negotiation_->chosen_key_exchange();
			if (kex != NULL) {
				if (kex->input(this, &packet))
					continue;
			}
			DEBUG(log_) << "Using default handler for key exchange method message.";
		} else if (msg >= SSH::Message::UserAuthenticationGenericRangeBegin &&
			   msg <= SSH::Message::UserAuthenticationGenericRangeEnd) {
			DEBUG(log_) << "Using default handler for generic user authentication message.";
		} else if (msg >= SSH::Message::UserAuthenticationMethodRangeBegin &&
			   msg <= SSH::Message::UserAuthenticationMethodRangeEnd) {
			DEBUG(log_) << "Using default handler for user authentication method message.";
		} else if (msg >= SSH::Message::ConnectionProtocolGlobalRangeBegin &&
			   msg <= SSH::Message::ConnectionProtocolGlobalRangeEnd) {
			DEBUG(log_) << "Using default handler for generic connection protocol message.";
		} else if (msg >= SSH::Message::ConnectionChannelRangeBegin &&
			   msg <= SSH::Message::ConnectionChannelRangeEnd) {
			DEBUG(log_) << "Using default handler for connection channel message.";
		} else if (msg >= SSH::Message::ClientProtocolReservedRangeBegin &&
			   msg <= SSH::Message::ClientProtocolReservedRangeEnd) {
			DEBUG(log_) << "Using default handler for client protocol message.";
		} else if (msg >= SSH::Message::LocalExtensionRangeBegin) {
			/* Because msg is a uint8_t, it will always be <= SSH::Message::LocalExtensionRangeEnd.  */
			DEBUG(log_) << "Using default handler for local extension message.";
		} else {
			ASSERT(log_, msg == 0);
			ERROR(log_) << "Message outside of protocol range received.  Passing to default handler, but not expecting much.";
		}

		receive_callback_->param(Event(Event::Done, packet));
		receive_action_ = receive_callback_->schedule();
		receive_callback_ = NULL;

		return;
	}
}
<|endoftext|>


<commit_before>// Author: Jingyue

#define DEBUG_TYPE "dyn-aa"

#include <cstdio>

#include "llvm/Pass.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"

#include "rcs/IDAssigner.h"

#include "dyn-aa/DynamicAliasAnalysis.h"

using namespace std;
using namespace llvm;
using namespace rcs;
using namespace dyn_aa;

static RegisterPass<DynamicAliasAnalysis> X("dyn-aa",
                                            "Accurate alias analysis "
                                            "according to the point-to log",
                                            false, // Is CFG Only?
                                            true); // Is Analysis?
// Don't register to AliasAnalysis group. It would confuse CallGraphChecker
// to use DynamicAliasAnalysis as the underlying AA to generate the call
// graph.
// static RegisterAnalysisGroup<AliasAnalysis> Y(X);

static cl::opt<string> OutputDynamicAliases(
    "output-dyn-aliases",
    cl::desc("Dump all dynamic aliases"));

STATISTIC(NumRemoveOps, "Number of remove operations");
STATISTIC(NumInsertOps, "Number of insert operations");
STATISTIC(MaxNumPointersToSameLocation,
          "Maximum number of pointers to the same location. "
          "Used for analyzing time complexity");

char DynamicAliasAnalysis::ID = 0;

const unsigned DynamicAliasAnalysis::UnknownVersion = (unsigned)-1;

bool DynamicAliasAnalysis::runOnModule(Module &M) {
  IDAssigner &IDA = getAnalysis<IDAssigner>();

  InitializeAliasAnalysis(this);

  processLog();

  errs() << "# of aliases = " << Aliases.size() << "\n";
  if (OutputDynamicAliases != "") {
    string ErrorInfo;
    raw_fd_ostream OutputFile(OutputDynamicAliases.c_str(), ErrorInfo);
    for (DenseSet<ValuePair>::iterator I = Aliases.begin();
         I != Aliases.end(); ++I) {
      Value *V1 = I->first, *V2 = I->second;
      OutputFile << IDA.getValueID(V1) << " " << IDA.getValueID(V2) << "\n";
    }
  }

#if 0
  errs() << PointersVersionUnknown.size()
      << " pointers whose version is unknown:\n";
  for (ValueSet::iterator I = PointersVersionUnknown.begin();
       I != PointersVersionUnknown.end(); ++I) {
    IDA.printValue(errs(), *I);
    errs() << "\n";
  }
#endif

  return false;
}

void DynamicAliasAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
  AliasAnalysis::getAnalysisUsage(AU);
  AU.setPreservesAll();
  AU.addRequired<IDAssigner>();
}

void DynamicAliasAnalysis::updateVersion(void *Start,
                                         unsigned long Bound,
                                         unsigned Version) {
  Interval I((unsigned long)Start, (unsigned long)Start + Bound);
  pair<IntervalTree<unsigned>::iterator, IntervalTree<unsigned>::iterator> ER =
      AddressVersion.equal_range(I);
  AddressVersion.erase(ER.first, ER.second);
  AddressVersion.insert(make_pair(I, Version));
  assert(lookupAddress(Start) == Version);
}

void DynamicAliasAnalysis::processAddrTakenDecl(
    const AddrTakenDeclLogRecord &Record) {
  updateVersion(Record.Address, Record.Bound, CurrentVersion);
  ++CurrentVersion;
  // Check for numeric overflow.
  assert(CurrentVersion != UnknownVersion);
}

void DynamicAliasAnalysis::processTopLevelPointTo(
    const TopLevelPointToLogRecord &Record) {
  unsigned PointerVID = Record.PointerValueID;
  void *PointeeAddress = Record.PointeeAddress;

  // No matter the new address is NULL or not, remove the original mapping.
  removePointingTo(PointerVID);

  // We don't consider NULLs as aliases.
  if (PointeeAddress != NULL) {
    unsigned Version = lookupAddress(PointeeAddress);

    // Report the pointers pointing to unversioned address.
    if (Version == UnknownVersion) {
      if (!AddressesVersionUnknown.count(PointeeAddress)) {
        AddressesVersionUnknown.insert(PointeeAddress);
#if 0
        errs() << "Unknown version: " << PointerVID << " => "
            << PointeeAddress << "\n";
#endif
        IDAssigner &IDA = getAnalysis<IDAssigner>();
        PointersVersionUnknown.insert(IDA.getValue(PointerVID));
      }
    }

    addPointingTo(PointerVID, PointeeAddress, Version);

    // Report aliases.
    PointedByMapTy::iterator I = BeingPointedBy.find(
        make_pair(PointeeAddress, Version));
    assert(I != BeingPointedBy.end()); // We just added a point-to in.
    addAliasPairs(PointerVID, I->second);
  }
}

void DynamicAliasAnalysis::removePointingTo(unsigned ValueID) {
  ++NumRemoveOps;
  if (ValueID < PointingTo.size() && PointingTo[ValueID].first != NULL) {
    // Remove from BeingPointedBy.
    PointedByMapTy::iterator J = BeingPointedBy.find(PointingTo[ValueID]);
    assert(J != BeingPointedBy.end());
    vector<unsigned>::iterator K = find(J->second.begin(),
                                        J->second.end(),
                                        ValueID);
    assert(K != J->second.end());
    J->second.erase(K);

    // Remove from PointingTo.
    PointingTo[ValueID].first = NULL;
    PointingTo[ValueID].second = UnknownVersion;
  }
}

void DynamicAliasAnalysis::addPointingTo(unsigned ValueID,
                                         void *Address,
                                         unsigned Version) {
  ++NumInsertOps;
  if (ValueID >= PointingTo.size())
    PointingTo.resize(ValueID + 1);
  PointingTo[ValueID].first = Address;
  PointingTo[ValueID].second = Version;
  BeingPointedBy[make_pair(Address, Version)].push_back(ValueID);
}

unsigned DynamicAliasAnalysis::lookupAddress(void *Addr) const {
  Interval I((unsigned long)Addr, (unsigned long)Addr + 1);
  IntervalTree<unsigned>::const_iterator Pos = AddressVersion.find(I);
  if (Pos == AddressVersion.end())
    return UnknownVersion;
  return Pos->second;
}

void *DynamicAliasAnalysis::getAdjustedAnalysisPointer(AnalysisID PI) {
  if (PI == &AliasAnalysis::ID)
    return (AliasAnalysis *)this;
  return this;
}

AliasAnalysis::AliasResult DynamicAliasAnalysis::alias(const Location &L1,
                                                       const Location &L2) {
  Value *V1 = const_cast<Value *>(L1.Ptr);
  Value *V2 = const_cast<Value *>(L2.Ptr);
  if (V1 > V2)
    swap(V1, V2);
  if (Aliases.count(make_pair(V1, V2)))
    return MayAlias;
  return NoAlias;
}

void DynamicAliasAnalysis::addAliasPair(Value *V1, Value *V2) {
  assert(V1 && V2);
  if (V1 > V2)
    swap(V1, V2);
  Aliases.insert(make_pair(V1, V2));
}

void DynamicAliasAnalysis::addAliasPair(unsigned VID1, unsigned VID2) {
  assert(VID1 != IDAssigner::InvalidID && VID2 != IDAssigner::InvalidID);
  IDAssigner &IDA = getAnalysis<IDAssigner>();
  addAliasPair(IDA.getValue(VID1), IDA.getValue(VID2));
}

void DynamicAliasAnalysis::addAliasPairs(unsigned VID1,
                                         const vector<unsigned> &VID2s) {
  if (VID2s.size() > MaxNumPointersToSameLocation)
    MaxNumPointersToSameLocation = VID2s.size();
  for (size_t j = 0; j < VID2s.size(); ++j)
    addAliasPair(VID1, VID2s[j]);
}
<commit_msg>ignore unknown versions when updating MaxNumXXX<commit_after>// Author: Jingyue

#define DEBUG_TYPE "dyn-aa"

#include <cstdio>

#include "llvm/Pass.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/raw_ostream.h"

#include "rcs/IDAssigner.h"

#include "dyn-aa/DynamicAliasAnalysis.h"

using namespace std;
using namespace llvm;
using namespace rcs;
using namespace dyn_aa;

static RegisterPass<DynamicAliasAnalysis> X("dyn-aa",
                                            "Accurate alias analysis "
                                            "according to the point-to log",
                                            false, // Is CFG Only?
                                            true); // Is Analysis?
// Don't register to AliasAnalysis group. It would confuse CallGraphChecker
// to use DynamicAliasAnalysis as the underlying AA to generate the call
// graph.
// static RegisterAnalysisGroup<AliasAnalysis> Y(X);

static cl::opt<string> OutputDynamicAliases(
    "output-dyn-aliases",
    cl::desc("Dump all dynamic aliases"));

STATISTIC(NumRemoveOps, "Number of remove operations");
STATISTIC(NumInsertOps, "Number of insert operations");
STATISTIC(MaxNumPointersToSameLocation,
          "Maximum number of pointers to the same location. "
          "Used for analyzing time complexity");

char DynamicAliasAnalysis::ID = 0;

const unsigned DynamicAliasAnalysis::UnknownVersion = (unsigned)-1;

bool DynamicAliasAnalysis::runOnModule(Module &M) {
  IDAssigner &IDA = getAnalysis<IDAssigner>();

  InitializeAliasAnalysis(this);

  processLog();

  errs() << "# of aliases = " << Aliases.size() << "\n";
  if (OutputDynamicAliases != "") {
    string ErrorInfo;
    raw_fd_ostream OutputFile(OutputDynamicAliases.c_str(), ErrorInfo);
    for (DenseSet<ValuePair>::iterator I = Aliases.begin();
         I != Aliases.end(); ++I) {
      Value *V1 = I->first, *V2 = I->second;
      OutputFile << IDA.getValueID(V1) << " " << IDA.getValueID(V2) << "\n";
    }
  }

#if 0
  errs() << PointersVersionUnknown.size()
      << " pointers whose version is unknown:\n";
  for (ValueSet::iterator I = PointersVersionUnknown.begin();
       I != PointersVersionUnknown.end(); ++I) {
    IDA.printValue(errs(), *I);
    errs() << "\n";
  }
#endif

  return false;
}

void DynamicAliasAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
  AliasAnalysis::getAnalysisUsage(AU);
  AU.setPreservesAll();
  AU.addRequired<IDAssigner>();
}

void DynamicAliasAnalysis::updateVersion(void *Start,
                                         unsigned long Bound,
                                         unsigned Version) {
  Interval I((unsigned long)Start, (unsigned long)Start + Bound);
  pair<IntervalTree<unsigned>::iterator, IntervalTree<unsigned>::iterator> ER =
      AddressVersion.equal_range(I);
  AddressVersion.erase(ER.first, ER.second);
  AddressVersion.insert(make_pair(I, Version));
  assert(lookupAddress(Start) == Version);
}

void DynamicAliasAnalysis::processAddrTakenDecl(
    const AddrTakenDeclLogRecord &Record) {
  updateVersion(Record.Address, Record.Bound, CurrentVersion);
  ++CurrentVersion;
  // Check for numeric overflow.
  assert(CurrentVersion != UnknownVersion);
}

void DynamicAliasAnalysis::processTopLevelPointTo(
    const TopLevelPointToLogRecord &Record) {
  unsigned PointerVID = Record.PointerValueID;
  void *PointeeAddress = Record.PointeeAddress;

  // No matter the new address is NULL or not, remove the original mapping.
  removePointingTo(PointerVID);

  // We don't consider NULLs as aliases.
  if (PointeeAddress != NULL) {
    unsigned Version = lookupAddress(PointeeAddress);

    // Report the pointers pointing to unversioned address.
    if (Version == UnknownVersion) {
      if (!AddressesVersionUnknown.count(PointeeAddress)) {
        AddressesVersionUnknown.insert(PointeeAddress);
#if 0
        errs() << "Unknown version: " << PointerVID << " => "
            << PointeeAddress << "\n";
#endif
        IDAssigner &IDA = getAnalysis<IDAssigner>();
        PointersVersionUnknown.insert(IDA.getValue(PointerVID));
      }
    }

    addPointingTo(PointerVID, PointeeAddress, Version);

    // Report aliases.
    PointedByMapTy::iterator I = BeingPointedBy.find(
        make_pair(PointeeAddress, Version));
    assert(I != BeingPointedBy.end()); // We just added a point-to in.
    if (Version != UnknownVersion &&
        I->second.size() > MaxNumPointersToSameLocation) {
      MaxNumPointersToSameLocation = I->second.size();
    }
    addAliasPairs(PointerVID, I->second);
  }
}

void DynamicAliasAnalysis::removePointingTo(unsigned ValueID) {
  ++NumRemoveOps;
  if (ValueID < PointingTo.size() && PointingTo[ValueID].first != NULL) {
    // Remove from BeingPointedBy.
    PointedByMapTy::iterator J = BeingPointedBy.find(PointingTo[ValueID]);
    assert(J != BeingPointedBy.end());
    vector<unsigned>::iterator K = find(J->second.begin(),
                                        J->second.end(),
                                        ValueID);
    assert(K != J->second.end());
    J->second.erase(K);

    // Remove from PointingTo.
    PointingTo[ValueID].first = NULL;
    PointingTo[ValueID].second = UnknownVersion;
  }
}

void DynamicAliasAnalysis::addPointingTo(unsigned ValueID,
                                         void *Address,
                                         unsigned Version) {
  ++NumInsertOps;
  if (ValueID >= PointingTo.size())
    PointingTo.resize(ValueID + 1);
  PointingTo[ValueID].first = Address;
  PointingTo[ValueID].second = Version;
  BeingPointedBy[make_pair(Address, Version)].push_back(ValueID);
}

unsigned DynamicAliasAnalysis::lookupAddress(void *Addr) const {
  Interval I((unsigned long)Addr, (unsigned long)Addr + 1);
  IntervalTree<unsigned>::const_iterator Pos = AddressVersion.find(I);
  if (Pos == AddressVersion.end())
    return UnknownVersion;
  return Pos->second;
}

void *DynamicAliasAnalysis::getAdjustedAnalysisPointer(AnalysisID PI) {
  if (PI == &AliasAnalysis::ID)
    return (AliasAnalysis *)this;
  return this;
}

AliasAnalysis::AliasResult DynamicAliasAnalysis::alias(const Location &L1,
                                                       const Location &L2) {
  Value *V1 = const_cast<Value *>(L1.Ptr);
  Value *V2 = const_cast<Value *>(L2.Ptr);
  if (V1 > V2)
    swap(V1, V2);
  if (Aliases.count(make_pair(V1, V2)))
    return MayAlias;
  return NoAlias;
}

void DynamicAliasAnalysis::addAliasPair(Value *V1, Value *V2) {
  assert(V1 && V2);
  if (V1 > V2)
    swap(V1, V2);
  Aliases.insert(make_pair(V1, V2));
}

void DynamicAliasAnalysis::addAliasPair(unsigned VID1, unsigned VID2) {
  assert(VID1 != IDAssigner::InvalidID && VID2 != IDAssigner::InvalidID);
  IDAssigner &IDA = getAnalysis<IDAssigner>();
  addAliasPair(IDA.getValue(VID1), IDA.getValue(VID2));
}

void DynamicAliasAnalysis::addAliasPairs(unsigned VID1,
                                         const vector<unsigned> &VID2s) {
  for (size_t j = 0; j < VID2s.size(); ++j)
    addAliasPair(VID1, VID2s[j]);
}
<|endoftext|>


<commit_before>/*
  Copyright (c) 2012 Stuart Walsh

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
*/

#include <Python.h>
#include "stdinc.h"
#include "python/pythonwrap.h"
#include "python/parserwrap.h"
#include "python/clientwrap.h"

template<class T> PyTypeObject PythonWrap<T>::type_object;
template<class T> PyMethodDef *PythonWrap<T>::methods;
template<class T> PyMemberDef *PythonWrap<T>::members;
template<class T> PyGetSetDef *PythonWrap<T>::getsetters;
template<class T> reprfunc PythonWrap<T>::str;

template void PythonWrap<ParserWrap>::init(const char *);
template void PythonWrap<ClientWrap>::init(const char *);

template ClientWrap *PythonWrap<ClientWrap>::wrap(void *);
template bool PythonWrap<ClientWrap>::handle_event(PyObject *, PyObject *);
//template PythonWrap<ParserWrap>;

template<class T>
void
PythonWrap<T>::init(const char *name)
{
  type_object.ob_refcnt = 1;
  type_object.tp_alloc = alloc;
  type_object.tp_free = free;
  type_object.tp_new = create;
  type_object.tp_dealloc = dealloc;
  type_object.tp_basicsize = sizeof(T);
  type_object.tp_name = name;
  type_object.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
  type_object.tp_methods = PythonWrap<T>::methods;
  type_object.tp_members = PythonWrap<T>::members;
  type_object.tp_getset = PythonWrap<T>::getsetters;
  type_object.tp_str = PythonWrap<T>::str;

  if(PyType_Ready(&type_object) < 0)
  {
    PyErr_Print();
    throw runtime_error("Unable to create type");
  }
}

template<class T>
PyObject *
PythonWrap<T>::alloc(PyTypeObject *type, Py_ssize_t items)
{
  char *ret;
  PyObject *obj;

  ret = new char[_PyObject_VAR_SIZE(type, items)]();

  obj = reinterpret_cast<PyObject *>(ret);
  static_cast<void>(PyObject_INIT(obj, type));

  return obj;
}

template<class T>
void
PythonWrap<T>::free(void *ptr)
{
  char *obj = static_cast<char *>(ptr);

  delete[] obj;
}

template<class T>
PyObject *
PythonWrap<T>::create(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
  PyObject *ptr = type->tp_alloc(type, 0);
  T *obj = new(ptr) T(args, kwds);

  return reinterpret_cast<PyObject*>(obj);
}

template<class T>
void
PythonWrap<T>::dealloc(PyObject *obj)
{
  T *ptr = reinterpret_cast<T *>(obj);
  ptr->~T();
  obj->ob_type->tp_free(obj);
}

template<class T>
T *PythonWrap<T>::wrap(void *arg)
{
  PyObject *obj, *args;
  T *wrapped;

  obj = PyCObject_FromVoidPtr(arg, NULL);

  args = Py_BuildValue("(O)", obj);

  wrapped = reinterpret_cast<T *>(PyObject_CallObject(T::get_type(), args));
  if(wrapped == NULL)
    return NULL;

  return wrapped;
}

template<class T>
bool
PythonWrap<T>::handle_event(PyObject *event, PyObject *args)
{
  PyObject *handler, *ret;

  handler = PyObject_GetAttrString(event, "handler");

  if(handler == NULL)
  {
    // No handler, return true to say that everything is ok, we just dont have
    // anything attached
    return true;
  }

  ret = PyObject_CallObject(handler, args);
  if(ret == NULL)
  {
    PyErr_Print();
    return false;
  }
  if(ret == Py_True)
  {
    Py_DECREF(Py_True);
    return true;
  }
  else
  {
    Py_DECREF(Py_False);
    return false;
  }
}
<commit_msg>missing else<commit_after>/*
  Copyright (c) 2012 Stuart Walsh

  Permission is hereby granted, free of charge, to any person
  obtaining a copy of this software and associated documentation
  files (the "Software"), to deal in the Software without
  restriction, including without limitation the rights to use,
  copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the
  Software is furnished to do so, subject to the following
  conditions:

  The above copyright notice and this permission notice shall be
  included in all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  OTHER DEALINGS IN THE SOFTWARE.
*/

#include <Python.h>
#include "stdinc.h"
#include "python/pythonwrap.h"
#include "python/parserwrap.h"
#include "python/clientwrap.h"

template<class T> PyTypeObject PythonWrap<T>::type_object;
template<class T> PyMethodDef *PythonWrap<T>::methods;
template<class T> PyMemberDef *PythonWrap<T>::members;
template<class T> PyGetSetDef *PythonWrap<T>::getsetters;
template<class T> reprfunc PythonWrap<T>::str;

template void PythonWrap<ParserWrap>::init(const char *);
template void PythonWrap<ClientWrap>::init(const char *);

template ClientWrap *PythonWrap<ClientWrap>::wrap(void *);
template bool PythonWrap<ClientWrap>::handle_event(PyObject *, PyObject *);
//template PythonWrap<ParserWrap>;

template<class T>
void
PythonWrap<T>::init(const char *name)
{
  type_object.ob_refcnt = 1;
  type_object.tp_alloc = alloc;
  type_object.tp_free = free;
  type_object.tp_new = create;
  type_object.tp_dealloc = dealloc;
  type_object.tp_basicsize = sizeof(T);
  type_object.tp_name = name;
  type_object.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE;
  type_object.tp_methods = PythonWrap<T>::methods;
  type_object.tp_members = PythonWrap<T>::members;
  type_object.tp_getset = PythonWrap<T>::getsetters;
  type_object.tp_str = PythonWrap<T>::str;

  if(PyType_Ready(&type_object) < 0)
  {
    PyErr_Print();
    throw runtime_error("Unable to create type");
  }
}

template<class T>
PyObject *
PythonWrap<T>::alloc(PyTypeObject *type, Py_ssize_t items)
{
  char *ret;
  PyObject *obj;

  ret = new char[_PyObject_VAR_SIZE(type, items)]();

  obj = reinterpret_cast<PyObject *>(ret);
  static_cast<void>(PyObject_INIT(obj, type));

  return obj;
}

template<class T>
void
PythonWrap<T>::free(void *ptr)
{
  char *obj = static_cast<char *>(ptr);

  delete[] obj;
}

template<class T>
PyObject *
PythonWrap<T>::create(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
  PyObject *ptr = type->tp_alloc(type, 0);
  T *obj = new(ptr) T(args, kwds);

  return reinterpret_cast<PyObject*>(obj);
}

template<class T>
void
PythonWrap<T>::dealloc(PyObject *obj)
{
  T *ptr = reinterpret_cast<T *>(obj);
  ptr->~T();
  obj->ob_type->tp_free(obj);
}

template<class T>
T *PythonWrap<T>::wrap(void *arg)
{
  PyObject *obj, *args;
  T *wrapped;

  obj = PyCObject_FromVoidPtr(arg, NULL);

  args = Py_BuildValue("(O)", obj);

  wrapped = reinterpret_cast<T *>(PyObject_CallObject(T::get_type(), args));
  if(wrapped == NULL)
    return NULL;

  return wrapped;
}

template<class T>
bool
PythonWrap<T>::handle_event(PyObject *event, PyObject *args)
{
  PyObject *handler, *ret;

  handler = PyObject_GetAttrString(event, "handler");

  if(handler == NULL)
  {
    // No handler, return true to say that everything is ok, we just dont have
    // anything attached
    return true;
  }

  ret = PyObject_CallObject(handler, args);
  if(ret == NULL)
  {
    PyErr_Print();
    return false;
  }
  else if(ret == Py_True)
  {
    Py_DECREF(Py_True);
    return true;
  }
  else
  {
    Py_DECREF(Py_False);
    return false;
  }
}
<|endoftext|>