<commit_before>/******************************************************************************
 * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "user.h"

#include "connection.h"
#include "room.h"
#include "avatar.h"
#include "events/event.h"
#include "events/roommemberevent.h"
#include "csapi/room_state.h"
#include "csapi/profile.h"
#include "csapi/content-repo.h"

#include <QtCore/QTimer>
#include <QtCore/QRegularExpression>
#include <QtCore/QPointer>
#include <QtCore/QStringBuilder>
#include <QtCore/QElapsedTimer>

#include <functional>

using namespace QMatrixClient;
using namespace std::placeholders;
using std::move;

class User::Private
{
    public:
        static Avatar makeAvatar(QUrl url)
        {
            return Avatar(move(url));
        }

        Private(QString userId, Connection* connection)
            : userId(move(userId)), connection(connection)
        { }

        QString userId;
        Connection* connection;

        QString bridged;
        QString mostUsedName;
        QMultiHash<QString, const Room*> otherNames;
        Avatar mostUsedAvatar { makeAvatar({}) };
        std::vector<Avatar> otherAvatars;
        auto otherAvatar(QUrl url)
        {
            return std::find_if(otherAvatars.begin(), otherAvatars.end(),
                    [&url] (const auto& av) { return av.url() == url; });
        }
        QMultiHash<QUrl, const Room*> avatarsToRooms;

        mutable int totalRooms = 0;

        QString nameForRoom(const Room* r, const QString& hint = {}) const;
        void setNameForRoom(const Room* r, QString newName, QString oldName);
        QUrl avatarUrlForRoom(const Room* r, const QUrl& hint = {}) const;
        void setAvatarForRoom(const Room* r, const QUrl& newUrl,
                              const QUrl& oldUrl);

        void setAvatarOnServer(QString contentUri, User* q);

};

QString User::Private::nameForRoom(const Room* r, const QString& hint) const
{
    // If the hint is accurate, this function is O(1) instead of O(n)
    if (hint == mostUsedName || otherNames.contains(hint, r))
        return hint;
    return otherNames.key(r, mostUsedName);
}

static constexpr int MIN_JOINED_ROOMS_TO_LOG = 20;

void User::Private::setNameForRoom(const Room* r, QString newName,
                                   QString oldName)
{
    Q_ASSERT(oldName != newName);
    Q_ASSERT(oldName == mostUsedName || otherNames.contains(oldName, r));
    if (totalRooms < 2)
    {
        Q_ASSERT_X(totalRooms > 0 && otherNames.empty(), __FUNCTION__,
                   "Internal structures inconsistency");
        mostUsedName = move(newName);
        return;
    }
    otherNames.remove(oldName, r);
    if (newName != mostUsedName)
    {
        // Check if the newName is about to become most used.
        if (otherNames.count(newName) >= totalRooms - otherNames.size())
        {
            Q_ASSERT(totalRooms > 1);
            QElapsedTimer et;
            if (totalRooms > MIN_JOINED_ROOMS_TO_LOG)
            {
                qCDebug(MAIN) << "Switching the most used name of user" << userId
                              << "from" << mostUsedName << "to" << newName;
                qCDebug(MAIN) << "The user is in" << totalRooms << "rooms";
                et.start();
            }

            for (auto* r1: connection->roomMap())
                if (nameForRoom(r1) == mostUsedName)
                    otherNames.insert(mostUsedName, r1);

            mostUsedName = newName;
            otherNames.remove(newName);
            if (totalRooms > MIN_JOINED_ROOMS_TO_LOG)
                qCDebug(PROFILER) << et << "to switch the most used name";
        }
        else
            otherNames.insert(newName, r);
    }
}

QUrl User::Private::avatarUrlForRoom(const Room* r, const QUrl& hint) const
{
    // If the hint is accurate, this function is O(1) instead of O(n)
    if (hint == mostUsedAvatar.url() || avatarsToRooms.contains(hint, r))
        return hint;
    auto it = std::find(avatarsToRooms.begin(), avatarsToRooms.end(), r);
    return it == avatarsToRooms.end() ? mostUsedAvatar.url() : it.key();
}

void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl,
                                     const QUrl& oldUrl)
{
    Q_ASSERT(oldUrl != newUrl);
    Q_ASSERT(oldUrl == mostUsedAvatar.url() ||
             avatarsToRooms.contains(oldUrl, r));
    if (totalRooms < 2)
    {
        Q_ASSERT_X(totalRooms > 0 && otherAvatars.empty(), __FUNCTION__,
                   "Internal structures inconsistency");
        mostUsedAvatar.updateUrl(newUrl);
        return;
    }
    avatarsToRooms.remove(oldUrl, r);
    if (!avatarsToRooms.contains(oldUrl))
    {
        auto it = otherAvatar(oldUrl);
        if (it != otherAvatars.end())
            otherAvatars.erase(it);
    }
    if (newUrl != mostUsedAvatar.url())
    {
        // Check if the new avatar is about to become most used.
        if (avatarsToRooms.count(newUrl) >= totalRooms - avatarsToRooms.size())
        {
            QElapsedTimer et;
            if (totalRooms > MIN_JOINED_ROOMS_TO_LOG)
            {
                qCDebug(MAIN) << "Switching the most used avatar of user" << userId
                              << "from" << mostUsedAvatar.url().toDisplayString()
                              << "to" << newUrl.toDisplayString();
                et.start();
            }
            avatarsToRooms.remove(newUrl);
            auto nextMostUsedIt = otherAvatar(newUrl);
            Q_ASSERT(nextMostUsedIt != otherAvatars.end());
            std::swap(mostUsedAvatar, *nextMostUsedIt);
            for (const auto* r1: connection->roomMap())
                if (avatarUrlForRoom(r1) == nextMostUsedIt->url())
                    avatarsToRooms.insert(nextMostUsedIt->url(), r1);

            if (totalRooms > MIN_JOINED_ROOMS_TO_LOG)
                qCDebug(PROFILER) << et << "to switch the most used avatar";
        } else {
            if (otherAvatar(newUrl) == otherAvatars.end())
                otherAvatars.emplace_back(makeAvatar(newUrl));
            avatarsToRooms.insert(newUrl, r);
        }
    }
}

User::User(QString userId, Connection* connection)
    : QObject(connection), d(new Private(move(userId), connection))
{
    setObjectName(userId);
}

Connection* User::connection() const
{
    Q_ASSERT(d->connection);
    return d->connection;
}

User::~User() = default;

QString User::id() const
{
    return d->userId;
}

bool User::isGuest() const
{
    Q_ASSERT(!d->userId.isEmpty() && d->userId.startsWith('@'));
    auto it = std::find_if_not(d->userId.begin() + 1, d->userId.end(),
                               [] (QChar c) { return c.isDigit(); });
    Q_ASSERT(it != d->userId.end());
    return *it == ':';
}

QString User::name(const Room* room) const
{
    return d->nameForRoom(room);
}

QString User::rawName(const Room* room) const
{
    return d->bridged.isEmpty() ? name(room) :
                                  name(room) % " (" % d->bridged % ')';
}

void User::updateName(const QString& newName, const Room* room)
{
    updateName(newName, d->nameForRoom(room), room);
}

void User::updateName(const QString& newName, const QString& oldName,
                      const Room* room)
{
    Q_ASSERT(oldName == d->mostUsedName || d->otherNames.contains(oldName, room));
    if (newName != oldName)
    {
        emit nameAboutToChange(newName, oldName, room);
        d->setNameForRoom(room, newName, oldName);
        setObjectName(displayname());
        emit nameChanged(newName, oldName, room);
    }
}

void User::updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl,
                           const Room* room)
{
    Q_ASSERT(oldUrl == d->mostUsedAvatar.url() ||
             d->avatarsToRooms.contains(oldUrl, room));
    if (newUrl != oldUrl)
    {
        d->setAvatarForRoom(room, newUrl, oldUrl);
        setObjectName(displayname());
        emit avatarChanged(this, room);
    }

}

void User::rename(const QString& newName)
{
    auto job = connection()->callApi<SetDisplayNameJob>(id(), newName);
    connect(job, &BaseJob::success, this, [=] { updateName(newName); });
}

void User::rename(const QString& newName, const Room* r)
{
    if (!r)
    {
        qCWarning(MAIN) << "Passing a null room to two-argument User::rename()"
                           "is incorrect; client developer, please fix it";
        rename(newName);
        return;
    }
    Q_ASSERT_X(r->memberJoinState(this) == JoinState::Join, __FUNCTION__,
               "Attempt to rename a user that's not a room member");
    MemberEventContent evtC;
    evtC.displayName = newName;
    auto job = r->setMemberState(id(), RoomMemberEvent(move(evtC)));
    connect(job, &BaseJob::success, this, [=] { updateName(newName, r); });
}

bool User::setAvatar(const QString& fileName)
{
    return avatarObject().upload(connection(), fileName,
                std::bind(&Private::setAvatarOnServer, d.data(), _1, this));
}

bool User::setAvatar(QIODevice* source)
{
    return avatarObject().upload(connection(), source,
                std::bind(&Private::setAvatarOnServer, d.data(), _1, this));
}

void User::requestDirectChat()
{
    connection()->requestDirectChat(this);
}

void User::ignore()
{
    connection()->addToIgnoredUsers(this);
}

void User::unmarkIgnore()
{
    connection()->removeFromIgnoredUsers(this);
}

void User::Private::setAvatarOnServer(QString contentUri, User* q)
{
    auto* j = connection->callApi<SetAvatarUrlJob>(userId, contentUri);
    connect(j, &BaseJob::success, q,
            [=] { q->updateAvatarUrl(contentUri, avatarUrlForRoom(nullptr)); });
}

QString User::displayname(const Room* room) const
{
    auto name = d->nameForRoom(room);
    return name.isEmpty() ? d->userId :
           room ? room->roomMembername(this) : name;
}

QString User::fullName(const Room* room) const
{
    auto name = d->nameForRoom(room);
    return name.isEmpty() ? d->userId : name % " (" % d->userId % ')';
}

QString User::bridged() const
{
    return d->bridged;
}

const Avatar& User::avatarObject(const Room* room) const
{
    auto it = d->otherAvatar(d->avatarUrlForRoom(room));
    return it != d->otherAvatars.end() ? *it : d->mostUsedAvatar;
}

QImage User::avatar(int dimension, const Room* room)
{
    return avatar(dimension, dimension, room);
}

QImage User::avatar(int width, int height, const Room* room)
{
    return avatar(width, height, room, []{});
}

QImage User::avatar(int width, int height, const Room* room,
                    const Avatar::get_callback_t& callback)
{
    return avatarObject(room).get(d->connection, width, height,
                [=] { emit avatarChanged(this, room); callback(); });
}

QString User::avatarMediaId(const Room* room) const
{
    return avatarObject(room).mediaId();
}

QUrl User::avatarUrl(const Room* room) const
{
    return avatarObject(room).url();
}

void User::processEvent(const RoomMemberEvent& event, const Room* room)
{
    Q_ASSERT(room);
    if (event.membership() != MembershipType::Invite &&
            event.membership() != MembershipType::Join)
        return;

    auto aboutToEnter = room->memberJoinState(this) == JoinState::Leave &&
            (event.membership() == MembershipType::Join ||
             event.membership() == MembershipType::Invite);
    if (aboutToEnter)
        ++d->totalRooms;

    auto newName = event.displayName();
    // `bridged` value uses the same notification signal as the name;
    // it is assumed that first setting of the bridge occurs together with
    // the first setting of the name, and further bridge updates are
    // exceptionally rare (the only reasonable case being that the bridge
    // changes the naming convention). For the same reason room-specific
    // bridge tags are not supported at all.
    QRegularExpression reSuffix(" \\((IRC|Gitter|Telegram)\\)$");
    auto match = reSuffix.match(newName);
    if (match.hasMatch())
    {
        if (d->bridged != match.captured(1))
        {
            if (!d->bridged.isEmpty())
                qCWarning(MAIN) << "Bridge for user" << id() << "changed:"
                                << d->bridged << "->" << match.captured(1);
            d->bridged = match.captured(1);
        }
        newName.truncate(match.capturedStart(0));
    }
    if (event.prevContent())
    {
        // FIXME: the hint doesn't work for bridged users
        auto oldNameHint =
                d->nameForRoom(room, event.prevContent()->displayName);
        updateName(newName, oldNameHint, room);
        updateAvatarUrl(event.avatarUrl(),
                        d->avatarUrlForRoom(room, event.prevContent()->avatarUrl),
                        room);
    } else {
        updateName(newName, room);
        updateAvatarUrl(event.avatarUrl(), d->avatarUrlForRoom(room), room);
    }
}
<commit_msg>User::displayname(): avoid calling nameForRoom twice<commit_after>/******************************************************************************
 * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "user.h"

#include "connection.h"
#include "room.h"
#include "avatar.h"
#include "events/event.h"
#include "events/roommemberevent.h"
#include "csapi/room_state.h"
#include "csapi/profile.h"
#include "csapi/content-repo.h"

#include <QtCore/QTimer>
#include <QtCore/QRegularExpression>
#include <QtCore/QPointer>
#include <QtCore/QStringBuilder>
#include <QtCore/QElapsedTimer>

#include <functional>

using namespace QMatrixClient;
using namespace std::placeholders;
using std::move;

class User::Private
{
    public:
        static Avatar makeAvatar(QUrl url)
        {
            return Avatar(move(url));
        }

        Private(QString userId, Connection* connection)
            : userId(move(userId)), connection(connection)
        { }

        QString userId;
        Connection* connection;

        QString bridged;
        QString mostUsedName;
        QMultiHash<QString, const Room*> otherNames;
        Avatar mostUsedAvatar { makeAvatar({}) };
        std::vector<Avatar> otherAvatars;
        auto otherAvatar(QUrl url)
        {
            return std::find_if(otherAvatars.begin(), otherAvatars.end(),
                    [&url] (const auto& av) { return av.url() == url; });
        }
        QMultiHash<QUrl, const Room*> avatarsToRooms;

        mutable int totalRooms = 0;

        QString nameForRoom(const Room* r, const QString& hint = {}) const;
        void setNameForRoom(const Room* r, QString newName, QString oldName);
        QUrl avatarUrlForRoom(const Room* r, const QUrl& hint = {}) const;
        void setAvatarForRoom(const Room* r, const QUrl& newUrl,
                              const QUrl& oldUrl);

        void setAvatarOnServer(QString contentUri, User* q);

};

QString User::Private::nameForRoom(const Room* r, const QString& hint) const
{
    // If the hint is accurate, this function is O(1) instead of O(n)
    if (hint == mostUsedName || otherNames.contains(hint, r))
        return hint;
    return otherNames.key(r, mostUsedName);
}

static constexpr int MIN_JOINED_ROOMS_TO_LOG = 20;

void User::Private::setNameForRoom(const Room* r, QString newName,
                                   QString oldName)
{
    Q_ASSERT(oldName != newName);
    Q_ASSERT(oldName == mostUsedName || otherNames.contains(oldName, r));
    if (totalRooms < 2)
    {
        Q_ASSERT_X(totalRooms > 0 && otherNames.empty(), __FUNCTION__,
                   "Internal structures inconsistency");
        mostUsedName = move(newName);
        return;
    }
    otherNames.remove(oldName, r);
    if (newName != mostUsedName)
    {
        // Check if the newName is about to become most used.
        if (otherNames.count(newName) >= totalRooms - otherNames.size())
        {
            Q_ASSERT(totalRooms > 1);
            QElapsedTimer et;
            if (totalRooms > MIN_JOINED_ROOMS_TO_LOG)
            {
                qCDebug(MAIN) << "Switching the most used name of user" << userId
                              << "from" << mostUsedName << "to" << newName;
                qCDebug(MAIN) << "The user is in" << totalRooms << "rooms";
                et.start();
            }

            for (auto* r1: connection->roomMap())
                if (nameForRoom(r1) == mostUsedName)
                    otherNames.insert(mostUsedName, r1);

            mostUsedName = newName;
            otherNames.remove(newName);
            if (totalRooms > MIN_JOINED_ROOMS_TO_LOG)
                qCDebug(PROFILER) << et << "to switch the most used name";
        }
        else
            otherNames.insert(newName, r);
    }
}

QUrl User::Private::avatarUrlForRoom(const Room* r, const QUrl& hint) const
{
    // If the hint is accurate, this function is O(1) instead of O(n)
    if (hint == mostUsedAvatar.url() || avatarsToRooms.contains(hint, r))
        return hint;
    auto it = std::find(avatarsToRooms.begin(), avatarsToRooms.end(), r);
    return it == avatarsToRooms.end() ? mostUsedAvatar.url() : it.key();
}

void User::Private::setAvatarForRoom(const Room* r, const QUrl& newUrl,
                                     const QUrl& oldUrl)
{
    Q_ASSERT(oldUrl != newUrl);
    Q_ASSERT(oldUrl == mostUsedAvatar.url() ||
             avatarsToRooms.contains(oldUrl, r));
    if (totalRooms < 2)
    {
        Q_ASSERT_X(totalRooms > 0 && otherAvatars.empty(), __FUNCTION__,
                   "Internal structures inconsistency");
        mostUsedAvatar.updateUrl(newUrl);
        return;
    }
    avatarsToRooms.remove(oldUrl, r);
    if (!avatarsToRooms.contains(oldUrl))
    {
        auto it = otherAvatar(oldUrl);
        if (it != otherAvatars.end())
            otherAvatars.erase(it);
    }
    if (newUrl != mostUsedAvatar.url())
    {
        // Check if the new avatar is about to become most used.
        if (avatarsToRooms.count(newUrl) >= totalRooms - avatarsToRooms.size())
        {
            QElapsedTimer et;
            if (totalRooms > MIN_JOINED_ROOMS_TO_LOG)
            {
                qCDebug(MAIN) << "Switching the most used avatar of user" << userId
                              << "from" << mostUsedAvatar.url().toDisplayString()
                              << "to" << newUrl.toDisplayString();
                et.start();
            }
            avatarsToRooms.remove(newUrl);
            auto nextMostUsedIt = otherAvatar(newUrl);
            Q_ASSERT(nextMostUsedIt != otherAvatars.end());
            std::swap(mostUsedAvatar, *nextMostUsedIt);
            for (const auto* r1: connection->roomMap())
                if (avatarUrlForRoom(r1) == nextMostUsedIt->url())
                    avatarsToRooms.insert(nextMostUsedIt->url(), r1);

            if (totalRooms > MIN_JOINED_ROOMS_TO_LOG)
                qCDebug(PROFILER) << et << "to switch the most used avatar";
        } else {
            if (otherAvatar(newUrl) == otherAvatars.end())
                otherAvatars.emplace_back(makeAvatar(newUrl));
            avatarsToRooms.insert(newUrl, r);
        }
    }
}

User::User(QString userId, Connection* connection)
    : QObject(connection), d(new Private(move(userId), connection))
{
    setObjectName(userId);
}

Connection* User::connection() const
{
    Q_ASSERT(d->connection);
    return d->connection;
}

User::~User() = default;

QString User::id() const
{
    return d->userId;
}

bool User::isGuest() const
{
    Q_ASSERT(!d->userId.isEmpty() && d->userId.startsWith('@'));
    auto it = std::find_if_not(d->userId.begin() + 1, d->userId.end(),
                               [] (QChar c) { return c.isDigit(); });
    Q_ASSERT(it != d->userId.end());
    return *it == ':';
}

QString User::name(const Room* room) const
{
    return d->nameForRoom(room);
}

QString User::rawName(const Room* room) const
{
    return d->bridged.isEmpty() ? name(room) :
                                  name(room) % " (" % d->bridged % ')';
}

void User::updateName(const QString& newName, const Room* room)
{
    updateName(newName, d->nameForRoom(room), room);
}

void User::updateName(const QString& newName, const QString& oldName,
                      const Room* room)
{
    Q_ASSERT(oldName == d->mostUsedName || d->otherNames.contains(oldName, room));
    if (newName != oldName)
    {
        emit nameAboutToChange(newName, oldName, room);
        d->setNameForRoom(room, newName, oldName);
        setObjectName(displayname());
        emit nameChanged(newName, oldName, room);
    }
}

void User::updateAvatarUrl(const QUrl& newUrl, const QUrl& oldUrl,
                           const Room* room)
{
    Q_ASSERT(oldUrl == d->mostUsedAvatar.url() ||
             d->avatarsToRooms.contains(oldUrl, room));
    if (newUrl != oldUrl)
    {
        d->setAvatarForRoom(room, newUrl, oldUrl);
        setObjectName(displayname());
        emit avatarChanged(this, room);
    }

}

void User::rename(const QString& newName)
{
    auto job = connection()->callApi<SetDisplayNameJob>(id(), newName);
    connect(job, &BaseJob::success, this, [=] { updateName(newName); });
}

void User::rename(const QString& newName, const Room* r)
{
    if (!r)
    {
        qCWarning(MAIN) << "Passing a null room to two-argument User::rename()"
                           "is incorrect; client developer, please fix it";
        rename(newName);
        return;
    }
    Q_ASSERT_X(r->memberJoinState(this) == JoinState::Join, __FUNCTION__,
               "Attempt to rename a user that's not a room member");
    MemberEventContent evtC;
    evtC.displayName = newName;
    auto job = r->setMemberState(id(), RoomMemberEvent(move(evtC)));
    connect(job, &BaseJob::success, this, [=] { updateName(newName, r); });
}

bool User::setAvatar(const QString& fileName)
{
    return avatarObject().upload(connection(), fileName,
                std::bind(&Private::setAvatarOnServer, d.data(), _1, this));
}

bool User::setAvatar(QIODevice* source)
{
    return avatarObject().upload(connection(), source,
                std::bind(&Private::setAvatarOnServer, d.data(), _1, this));
}

void User::requestDirectChat()
{
    connection()->requestDirectChat(this);
}

void User::ignore()
{
    connection()->addToIgnoredUsers(this);
}

void User::unmarkIgnore()
{
    connection()->removeFromIgnoredUsers(this);
}

void User::Private::setAvatarOnServer(QString contentUri, User* q)
{
    auto* j = connection->callApi<SetAvatarUrlJob>(userId, contentUri);
    connect(j, &BaseJob::success, q,
            [=] { q->updateAvatarUrl(contentUri, avatarUrlForRoom(nullptr)); });
}

QString User::displayname(const Room* room) const
{
    if (room)
        return room->roomMembername(this);

    const auto name = d->nameForRoom(nullptr);
    return name.isEmpty() ? d->userId : name;
}

QString User::fullName(const Room* room) const
{
    const auto name = d->nameForRoom(room);
    return name.isEmpty() ? d->userId : name % " (" % d->userId % ')';
}

QString User::bridged() const
{
    return d->bridged;
}

const Avatar& User::avatarObject(const Room* room) const
{
    auto it = d->otherAvatar(d->avatarUrlForRoom(room));
    return it != d->otherAvatars.end() ? *it : d->mostUsedAvatar;
}

QImage User::avatar(int dimension, const Room* room)
{
    return avatar(dimension, dimension, room);
}

QImage User::avatar(int width, int height, const Room* room)
{
    return avatar(width, height, room, []{});
}

QImage User::avatar(int width, int height, const Room* room,
                    const Avatar::get_callback_t& callback)
{
    return avatarObject(room).get(d->connection, width, height,
                [=] { emit avatarChanged(this, room); callback(); });
}

QString User::avatarMediaId(const Room* room) const
{
    return avatarObject(room).mediaId();
}

QUrl User::avatarUrl(const Room* room) const
{
    return avatarObject(room).url();
}

void User::processEvent(const RoomMemberEvent& event, const Room* room)
{
    Q_ASSERT(room);
    if (event.membership() != MembershipType::Invite &&
            event.membership() != MembershipType::Join)
        return;

    auto aboutToEnter = room->memberJoinState(this) == JoinState::Leave &&
            (event.membership() == MembershipType::Join ||
             event.membership() == MembershipType::Invite);
    if (aboutToEnter)
        ++d->totalRooms;

    auto newName = event.displayName();
    // `bridged` value uses the same notification signal as the name;
    // it is assumed that first setting of the bridge occurs together with
    // the first setting of the name, and further bridge updates are
    // exceptionally rare (the only reasonable case being that the bridge
    // changes the naming convention). For the same reason room-specific
    // bridge tags are not supported at all.
    QRegularExpression reSuffix(" \\((IRC|Gitter|Telegram)\\)$");
    auto match = reSuffix.match(newName);
    if (match.hasMatch())
    {
        if (d->bridged != match.captured(1))
        {
            if (!d->bridged.isEmpty())
                qCWarning(MAIN) << "Bridge for user" << id() << "changed:"
                                << d->bridged << "->" << match.captured(1);
            d->bridged = match.captured(1);
        }
        newName.truncate(match.capturedStart(0));
    }
    if (event.prevContent())
    {
        // FIXME: the hint doesn't work for bridged users
        auto oldNameHint =
                d->nameForRoom(room, event.prevContent()->displayName);
        updateName(newName, oldNameHint, room);
        updateAvatarUrl(event.avatarUrl(),
                        d->avatarUrlForRoom(room, event.prevContent()->avatarUrl),
                        room);
    } else {
        updateName(newName, room);
        updateAvatarUrl(event.avatarUrl(), d->avatarUrlForRoom(room), room);
    }
}
<|endoftext|>


<commit_before>/* -----------------------------------------------------------------------------
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
----------------------------------------------------------------------------- */

// Author: Kevin P. Barry [ta0kira@gmail.com] [kevinbarry@google.com]

#include "thread-crosser.h"

#include <cassert>

namespace capture_thread {

// static
std::function<void()> ThreadCrosser::WrapCall(std::function<void()> call) {
  const auto current = GetCurrent();
  if (call && current) {
    return current->WrapWithCrosser(WrapCallRec(std::move(call), current));
  } else {
    return call;
  }
}

// static
std::function<void()> ThreadCrosser::WrapCallRec(std::function<void()> call,
                                                 const ThreadCrosser* current) {
  if (current) {
    return WrapCallRec(current->WrapWithContext(std::move(call)),
                       current->Parent());
  } else {
    return call;
  }
}

void ThreadCrosser::SetOverride::Call(std::function<void()> call) const {
  call = WrapCallRec(std::move(call), current_);
  if (call) {
    call();
  }
}

// static
thread_local ThreadCrosser* ThreadCrosser::current_(nullptr);

}  // namespace capture_thread
<commit_msg>Removes unused #include.<commit_after>/* -----------------------------------------------------------------------------
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
----------------------------------------------------------------------------- */

// Author: Kevin P. Barry [ta0kira@gmail.com] [kevinbarry@google.com]

#include "thread-crosser.h"

namespace capture_thread {

// static
std::function<void()> ThreadCrosser::WrapCall(std::function<void()> call) {
  const auto current = GetCurrent();
  if (call && current) {
    return current->WrapWithCrosser(WrapCallRec(std::move(call), current));
  } else {
    return call;
  }
}

// static
std::function<void()> ThreadCrosser::WrapCallRec(std::function<void()> call,
                                                 const ThreadCrosser* current) {
  if (current) {
    return WrapCallRec(current->WrapWithContext(std::move(call)),
                       current->Parent());
  } else {
    return call;
  }
}

void ThreadCrosser::SetOverride::Call(std::function<void()> call) const {
  call = WrapCallRec(std::move(call), current_);
  if (call) {
    call();
  }
}

// static
thread_local ThreadCrosser* ThreadCrosser::current_(nullptr);

}  // namespace capture_thread
<|endoftext|>


<commit_before>// ----------------------------------------
// OpenGL Cellular Automaton Engine
// Krzysztof Jankowski <kj@p1x.in>
//
// (c)2015 P1X
// http://p1x.in
//
// gcc ogl.cpp -o ogl.app -lglut -lGL -lGLU
// gcc -Os ogl.cpp -o ogl.app -lglut -lGL -lGLU
//
// ----------------------------------------

// LIBS
// ----------------------------------------

#include <GL/freeglut.h>
#include <GL/gl.h>
#include <stdio.h>
#include <time.h>

// SYSTEM VARS
// ----------------------------------------

int FPS       = 60;
bool fullScreenMode  = true;
char title[]         = "OpenGL Cellular Automaton Engine";
static float VERSION = 1.1;
int windowWidth      = 320;
int windowHeight     = 240;
int windowPosX       = 50;
int windowPosY       = 50;
int refreshMills     = 1000/FPS;
float camera_scale   = 14.0f;

// AUTOMATION VARS
// ----------------------------------------
static int CELLS_ARRAY_SIZE   = 32;
float cells_main_array[32][32];
float cells_buffer_array[32][32];

static float CELL_START_COLOR = 0.4f;
static float CELL_STEP_COLOUR = 0.01f;
static float CELL_MIN_COLOUR  = 0.05f;
static float CELL_MAX_COLOUR  = 0.9f;
bool automation_mode          = false;
int stat_iteration            = 0;
int stat_alive                = 0;
int stat_change               = 0;

// INIT
// ----------------------------------------

void initGL() {

   glClearColor(0.1f, 0.1f, 1.0f, 1.0f);
}

// HELPERS
// ----------------------------------------

float random_f(){
   float r = (float)rand() / (float)RAND_MAX;
   return r;
}

float random_fcolor(){
   float r = random_f();
   if (r < 0.2f){
      r = 0.0f;
   }
   if  (r > 0.8f){
      r = 0.8f;
   }
   return r;
}

void reshape(GLsizei width, GLsizei height) {
   if (height == 0) height = 1;
   GLfloat aspect = (GLfloat)width / (GLfloat)height;

   glViewport(0, 0, width, height);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   if (width >= height) {
      gluOrtho2D(-camera_scale * aspect, camera_scale * aspect, -camera_scale, camera_scale);
   } else {
      gluOrtho2D(-camera_scale, camera_scale, -camera_scale / aspect, camera_scale / aspect);
   }
}

void change_fps(int change_fps){
   int new_fps = FPS + change_fps;
   if (new_fps < 1){
      new_fps = 1;
   }
   if (new_fps > 60){
      new_fps = 60;
   }
   FPS = new_fps;
   refreshMills = 1000/FPS;
}

// CELLULAR AUTOMATION
// ----------------------------------------

void init_arrays(){

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         cells_main_array[x][y] = 0.0f;
         cells_buffer_array[x][y] = 0.0f;
      }
   }
}

void fill_array(){
   float new_cell;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++ ){
         if (random_f() >= 0.8){
            cells_main_array[x][y] = CELL_START_COLOR;
         }else{
            cells_main_array[x][y] = 0.0f;
         }
      }
   }

   stat_iteration = 0;
}

int count_cells(int cx, int cy, float treshold){
   int count = 0;

   for (int y = cy-1; y <= cy+1; y++){
      for (int x = cx-1; x <= cx+1; x++){
         if (x >= 0 and y >= 0 and x < CELLS_ARRAY_SIZE and y < CELLS_ARRAY_SIZE and !( x == cx and y == cy)){
            if (cells_main_array[x][y] > treshold){
               count++;
            }
         }
      }
   }

   return count;
}

float cell_gain_colour(float colour){
   float new_colour = colour + CELL_STEP_COLOUR;
   if (new_colour > CELL_MAX_COLOUR){
      new_colour = CELL_MAX_COLOUR;
   }
   return new_colour;
}

float cell_lose_colour(float colour){
   float new_colour = colour - CELL_STEP_COLOUR;
   if (new_colour < CELL_MIN_COLOUR){
      new_colour = CELL_MIN_COLOUR;
   }
   return new_colour;
}

void automation(){
   int count;
   float cell;
   float new_cell;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         count = count_cells(x, y, 0.0f);
         cell = cells_main_array[x][y];
         if (cell > 0.0f){
            if (count < 2 or count > 3){
               new_cell = 0.0f;
            }else{
               new_cell = cell_gain_colour(cell);
            }
         }else{
            if (count == 3){
               new_cell = CELL_START_COLOR;
            }else{
               new_cell = 0.0f;
            }
         }
         cells_buffer_array[x][y] = new_cell;
      }
   }
}

void automation2(){
   int count;
   float cell;
   float new_cell;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         count = count_cells(x, y, 0.3f);
         cell = cells_main_array[x][y];
         if (cell > 0.2f){
            if (count < 2 or count > 3){
               new_cell = cell_lose_colour(cell);
            }else{
               new_cell = cell_gain_colour(cell);
            }
         }else{
            if (count == 3){
               new_cell = cell_gain_colour(cell);
            }else{
               new_cell = 0.0f;
            }
         }
         cells_buffer_array[x][y] = new_cell;
      }
   }
}

void swap_arrays(){
   float old_cell;
   float new_cell;
   stat_alive = 0;
   stat_change = 0;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         old_cell = cells_main_array[x][y];
         new_cell = cells_buffer_array[x][y];
         cells_main_array[x][y] = new_cell;
         if (new_cell > 0.0f) stat_alive++;
         if (old_cell != new_cell) stat_change++;
      }
   }
}
void clear_buffer_array(){}

 void init_automation(){
   init_arrays();
   fill_array();
}

void run_automation(){
   if (automation_mode){
      automation();
   }else{
      automation2();
   }
   if (stat_alive > 0) {
      stat_iteration++;
   }
   swap_arrays();
}

// INPUT
// ----------------------------------------

void keyboard(unsigned char key, int x, int y) {
   switch (key) {
      case 27:     // ESC key
         exit(0);
         break;
      case 13: // enter
         fill_array();
         break;
      case 32: // space
         automation_mode = !automation_mode;
         break;
   }
}

void specialKeys(int key, int x, int y) {
   switch (key) {
      case GLUT_KEY_F1:
         fullScreenMode = !fullScreenMode;
         if (fullScreenMode) {
            glutFullScreen();
         } else {
            glutReshapeWindow(windowWidth, windowHeight);
            glutPositionWindow(windowPosX, windowPosX);
         }
         break;
      case GLUT_KEY_RIGHT:
         break;
      case GLUT_KEY_LEFT:
         break;
      case GLUT_KEY_UP:
         change_fps(1);
         break;
      case GLUT_KEY_DOWN:
         change_fps(-1);
         break;
   }
}

void mouse(int button, int state, int x, int y) {
   if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
      // mouse_x = x;
      // mouse_y = y;
   }
}

// RENDERING
// ----------------------------------------

void Timer(int value) {
   run_automation();
   glutPostRedisplay();
   glutTimerFunc(refreshMills, Timer, 0);
}

void draw_stats(){
   char buf[256];

   glPushMatrix();
   glTranslatef (-camera_scale, camera_scale-2, 0);
      // TITLE
   glColor3f(1.0f, 1.0f, 1.0f);
   glRasterPos3f(0.0f, 0.0f, 0.0f);
   snprintf(buf, sizeof(buf) - 1, "%s - version %f", title, VERSION);
   glutBitmapString( GLUT_BITMAP_9_BY_15, (unsigned char*) buf);
   glPopMatrix();

   glPushMatrix();
   glTranslatef (-camera_scale, -camera_scale+2, 0);
   // STATS
   glColor3f(1.0f, 1.0f, 1.0f);
   glRasterPos3f(0.0f, 0.0f, 0.0f);
   snprintf(buf, sizeof(buf) - 1, "ITERATION: [%i] ALIVE: [%i] CHANGE: [%i]", stat_iteration, stat_alive, stat_change);
   glutBitmapString( GLUT_BITMAP_9_BY_15, (unsigned char*) buf);
   glPopMatrix();
}

void draw_one_cell(float x, float y, float size){
   glPushMatrix();
   glTranslatef (x, y, 0.0f);
   glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
   glEnable ( GL_COLOR_MATERIAL ) ;
   glColor4f(size > 0.5 ? 0.5 : size, size > 0.5 ? 0.5 : size, 1.0f, 1.0f);
   GLUquadricObj *quadratic;
   quadratic = gluNewQuadric();
   gluQuadricNormals(quadratic, GLU_SMOOTH);
   gluSphere(quadratic, size > 0.7 ? 0.7 : size, 16, 16);
   glPopMatrix();
}

void draw_cells(){
   float cell;
   float half_size = CELLS_ARRAY_SIZE * 0.5f;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         cell = cells_main_array[x][y];
         if (cell > 0.0f){
            glColor3f(cell, cell, 1.0f);
            draw_one_cell(x-half_size,y-half_size,cell);
         }
      }
   }

   draw_stats();
}

void display() {
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glEnable(GL_DEPTH_TEST);
   draw_cells();
   glutSwapBuffers();
}

void ambient_lighting(){
  glEnable(GL_LIGHTING);

  double amb = .1;
  GLfloat global_ambient[] = {amb,amb,amb, 0.1};
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);
}

void add_point_light(const float x, const float y, const float z, const float amb, const float diff, const float spec)
{
  glEnable(GL_LIGHTING);

  // GLfloat light_ambient[] = { 0.3, 0.0, 0.0, 1.0 };
  // GLfloat light_diffuse[] = { 0.0, 1.0, 0.0, 1.0 };
  // GLfloat light_specular[] = { 0.9, 0.9, 1.0, 1.0 };

  GLfloat light_ambient[] = { amb,amb,amb, 1.0 };
  GLfloat light_diffuse[] = {diff, diff, diff, 1.0 };
  GLfloat light_specular[] = {spec, spec, spec, 1.0 };

  GLfloat light_position[] = {x,y,z, 0.0 };

  glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);

  glEnable(GL_LIGHT0); //enable the light after setting the properties

}

void camera_setup(){
// glMatrixMode(GL_PROJECTION);
// glLoadIdentity();
// gluPerspective(50.0, 1.0, 3.0, 7.0);
// glMatrixMode(GL_MODELVIEW);
// // glLoadIdentity();
// // gluLookAt(0.0, 0.0, 0.0,
// //           0.0, 0.0, 0.0,
// //           0.0, 1.0, 0.0);

}

// MAIN
// ----------------------------------------

int main(int argc, char** argv) {
   glutInit(&argc, argv);
   glutInitDisplayMode(GLUT_DOUBLE);

   glutCreateWindow(title);
   if(fullScreenMode){
      glutFullScreen();
   }else{
      glutInitWindowSize(windowWidth, windowHeight);
      glutInitWindowPosition(windowPosX, windowPosY);
   }

   glutDisplayFunc(display);
   glutReshapeFunc(reshape);
   glutTimerFunc(0, Timer, 0);

   glutKeyboardFunc(keyboard);
   glutSpecialFunc(specialKeys);
   //glutMouseFunc(mouse);

   initGL();
   ambient_lighting();
   add_point_light(0.2f,0.6f,-1.5f, 0.5, 0.8, 1);
   camera_setup();
   init_automation();
   glutMainLoop();
   return 0;
}
<commit_msg>boxes<commit_after>// ----------------------------------------
// OpenGL Cellular Automaton Engine
// Krzysztof Jankowski <kj@p1x.in>
//
// (c)2015 P1X
// http://p1x.in
//
// gcc -Os ogl.cpp -o ogl.app -lglut -lGL -lGLU -lm
//
// ----------------------------------------

// LIBS
// ----------------------------------------

#include <GL/freeglut.h>
#include <GL/gl.h>
#include <stdio.h>
#include <time.h>
#include <math.h>

// SYSTEM VARS
// ----------------------------------------

int FPS       = 60;
bool fullScreenMode  = true;
char title[]         = "OpenGL Cellular Automaton Engine";
static float VERSION = 1.4f;
int windowWidth      = 320;
int windowHeight     = 240;
int windowPosX       = 50;
int windowPosY       = 50;
int refreshMills     = 1000/FPS;
float camera_scale   = 20.0f;

// AUTOMATION VARS
// ----------------------------------------
static int CELLS_ARRAY_SIZE   = 48;
static int MAX_CELLS = CELLS_ARRAY_SIZE*CELLS_ARRAY_SIZE;
float cells_main_array[48][48];
float cells_buffer_array[48][48];

static float CELL_START_COLOR = 0.5f;
static float CELL_STEP_COLOUR = 0.005f;
static float CELL_MIN_COLOUR  = 0.05f;
static float CELL_MAX_COLOUR  = 1.0f;
bool automation_mode          = false;
int stat_iteration            = 0;
int stat_alive                = 0;
int stat_change               = 0;

// INIT
// ----------------------------------------

void initGL() {

   glClearColor(0.1f, 0.1f, 1.0f, 1.0f);
}

// HELPERS
// ----------------------------------------

float random_f(){
   float r = (float)rand() / (float)RAND_MAX;
   return r;
}

float random_fcolor(){
   float r = random_f();
   if (r < 0.2f){
      r = 0.0f;
   }
   if  (r > 0.8f){
      r = 0.8f;
   }
   return r;
}

void reshape(GLsizei width, GLsizei height) {
   if (height == 0) height = 1;
   GLfloat aspect = (GLfloat)width / (GLfloat)height;

   glViewport(0, 0, width, height);

   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   if (width >= height) {
      gluOrtho2D(-camera_scale * aspect, camera_scale * aspect, -camera_scale, camera_scale);
   } else {
      gluOrtho2D(-camera_scale, camera_scale, -camera_scale / aspect, camera_scale / aspect);
   }
}

void change_fps(int change_fps){
   int new_fps = FPS + change_fps;
   if (new_fps < 1){
      new_fps = 1;
   }
   if (new_fps > 60){
      new_fps = 60;
   }
   FPS = new_fps;
   refreshMills = 1000/FPS;
}

// CELLULAR AUTOMATION
// ----------------------------------------

void init_arrays(){

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         cells_main_array[x][y] = 0.0f;
         cells_buffer_array[x][y] = 0.0f;
      }
   }
}

void fill_array(){
   float new_cell;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++ ){
         if (random_f() >= 0.8){
            cells_main_array[x][y] = CELL_START_COLOR;
         }else{
            cells_main_array[x][y] = 0.0f;
         }
      }
   }

   stat_iteration = 0;
}

int count_cells(int cx, int cy, float treshold){
   int count = 0;

   for (int y = cy-1; y <= cy+1; y++){
      for (int x = cx-1; x <= cx+1; x++){
         if (x >= 0 and y >= 0 and x < CELLS_ARRAY_SIZE and y < CELLS_ARRAY_SIZE and !( x == cx and y == cy)){
            if (cells_main_array[x][y] > treshold){
               count++;
            }
         }
      }
   }

   return count;
}

float cell_gain_colour(float colour){
   float new_colour = colour + CELL_STEP_COLOUR;
   if (new_colour > CELL_MAX_COLOUR){
      new_colour = CELL_MAX_COLOUR;
   }
   return new_colour;
}

float cell_lose_colour(float colour){
   float new_colour = colour - CELL_STEP_COLOUR;
   if (new_colour < CELL_MIN_COLOUR){
      new_colour = CELL_MIN_COLOUR;
   }
   return new_colour;
}

void automation(){
   int count;
   float cell;
   float new_cell;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         count = count_cells(x, y, 0.0f);
         cell = cells_main_array[x][y];
         if (cell > 0.0f){
            if (count < 2 or count > 3){
               new_cell = 0.0f;
            }else{
               new_cell = cell_gain_colour(cell);
            }
         }else{
            if (count == 3){
               new_cell = CELL_START_COLOR;
            }else{
               new_cell = 0.0f;
            }
         }
         cells_buffer_array[x][y] = new_cell;
      }
   }
}

void automation2(){
   int count;
   float cell;
   float new_cell;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         count = count_cells(x, y, 0.3f);
         cell = cells_main_array[x][y];
         if (cell > 0.2f){
            if (count < 2 or count > 3){
               new_cell = cell_lose_colour(cell);
            }else{
               new_cell = cell_gain_colour(cell);
            }
         }else{
            if (count == 3){
               new_cell = cell_gain_colour(cell);
            }else{
               new_cell = 0.0f;
            }
         }
         cells_buffer_array[x][y] = new_cell;
      }
   }
}

void swap_arrays(){
   float old_cell;
   float new_cell;
   stat_alive = 0;
   stat_change = 0;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         old_cell = cells_main_array[x][y];
         new_cell = cells_buffer_array[x][y];
         cells_main_array[x][y] = new_cell;
         if (new_cell > 0.0f) stat_alive++;
         if (old_cell != new_cell) stat_change++;
      }
   }
}
void clear_buffer_array(){}

 void init_automation(){
   init_arrays();
   fill_array();
}

void run_automation(){
   if (automation_mode){
      automation();
   }else{
      automation2();
   }
   if (stat_alive > 0) {
      stat_iteration++;
   }
   swap_arrays();
}

// INPUT
// ----------------------------------------

void keyboard(unsigned char key, int x, int y) {
   switch (key) {
      case 27:     // ESC key
         exit(0);
         break;
      case 13: // enter
         fill_array();
         break;
      case 32: // space
         automation_mode = !automation_mode;
         break;
   }
}

void specialKeys(int key, int x, int y) {
   switch (key) {
      case GLUT_KEY_F1:
         fullScreenMode = !fullScreenMode;
         if (fullScreenMode) {
            glutFullScreen();
         } else {
            glutReshapeWindow(windowWidth, windowHeight);
            glutPositionWindow(windowPosX, windowPosX);
         }
         break;
      case GLUT_KEY_RIGHT:
         break;
      case GLUT_KEY_LEFT:
         break;
      case GLUT_KEY_UP:
         change_fps(1);
         break;
      case GLUT_KEY_DOWN:
         change_fps(-1);
         break;
   }
}

void mouse(int button, int state, int x, int y) {
   if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
      // mouse_x = x;
      // mouse_y = y;
   }
}

// RENDERING
// ----------------------------------------

void Timer(int value) {
   run_automation();
   glutPostRedisplay();
   glutTimerFunc(refreshMills, Timer, 0);
}

void draw_floor(){

   glPushMatrix();
   glColorMaterial ( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ) ;
   glEnable ( GL_COLOR_MATERIAL ) ;
   glColor4f(0.5f, 0.5f, 0.8f, 1.0f);
   float size= CELLS_ARRAY_SIZE*0.8f;
   glBegin(GL_POLYGON);
       glVertex2d(-size, size);
       glVertex2d(-size, -size);
       glVertex2d(size, -size);
       glVertex2d(size, size);
   glEnd();
   glPopMatrix();
}

void draw_stats(){
   char buf[256];

   glPushMatrix();
   glTranslatef (-camera_scale, camera_scale-2, 0);
      // TITLE
   glColor3f(1.0f, 1.0f, 1.0f);
   glRasterPos3f(0.0f, 0.0f, 0.0f);
   snprintf(buf, sizeof(buf) - 1, "%s - version %f", title, VERSION);
   glutBitmapString( GLUT_BITMAP_9_BY_15, (unsigned char*) buf);
   glPopMatrix();

   glPushMatrix();
   glTranslatef (-camera_scale, -camera_scale+2, 0);
   // STATS
   glColor3f(1.0f, 1.0f, 1.0f);
   glRasterPos3f(0.0f, 0.0f, 0.0f);
   snprintf(buf, sizeof(buf) - 1, "ITERATION: [%i] ALIVE: [%i/%i] CHANGE: [%i]", stat_iteration, stat_alive, MAX_CELLS, stat_change);
   glutBitmapString( GLUT_BITMAP_9_BY_15, (unsigned char*) buf);
   glPopMatrix();
}

void draw_one_cell(float x, float y, float size){
   glPushMatrix();
   glTranslatef (x, y, 0.0f);
   glColorMaterial ( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ) ;
   glEnable ( GL_COLOR_MATERIAL ) ;
   float color = size > 0.45 ? 0.45 : size;
   glColor4f(color, color, 1.0, 1.0f);
   //glScalef(size,size,0.0f);
   float rf = size*45.0f;
   glRotatef(rf, 1.0f, 1.0f, 1.0f);
   // GLUquadricObj *quadratic;
   // quadratic = gluNewQuadric();
   // gluQuadricNormals(quadratic, GLU_SMOOTH);
   // float quad_size = stat_alive < (MAX_CELLS)*0.2 ? 16 : 12;
   // gluSphere(quadratic, size > 0.6 ? 0.6 : size, quad_size, quad_size);

   glutSolidCube(size);
   glPopMatrix();
}

void draw_cells(){
   float cell;
   float half_size = CELLS_ARRAY_SIZE * 0.5f;

   for (int y = 0; y < CELLS_ARRAY_SIZE; y++){
      for (int x = 0; x < CELLS_ARRAY_SIZE; x++){
         cell = cells_main_array[x][y];
         if (cell > 0.0f){
            //glColor3f(cell, cell, 1.0f);
            draw_one_cell(x-half_size,y-half_size,cell);
         }
      }
   }

   draw_stats();
}

 void camera_setup(){
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   gluPerspective(120.0, 1.0f, 500.0f, -500.0f);
   glMatrixMode(GL_MODELVIEW);
 }

void camera_movement(){
   //float z = cos(stat_iteration*0.01f)*0.4f;
   //glTranslatef (0.0f, 0.0f, z);
  // glRotatef(z, 1.0f, 1.0f, 0.0f);
}

void display() {
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   //glEnable(GL_DEPTH_TEST);

   //glMatrixMode(GL_MODELVIEW);
   //glLoadIdentity();
   //gluLookAt (1.0f, 1.0f, 1.0f, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

   //draw_floor();
   draw_cells();
   //camera_movement();
   glutSwapBuffers();
}

void ambient_lighting(){
  glEnable(GL_LIGHTING);

  double amb = .2;
  GLfloat global_ambient[] = {amb,amb,amb, 0.1};
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient);
}

void add_point_light(const float x, const float y, const float z, const float amb, const float diff, const float spec)
{
  glEnable(GL_LIGHTING);

  // GLfloat light_ambient[] = { 0.3, 0.0, 0.0, 1.0 };
  // GLfloat light_diffuse[] = { 0.0, 1.0, 0.0, 1.0 };
  // GLfloat light_specular[] = { 0.9, 0.9, 1.0, 1.0 };

  GLfloat light_ambient[] = { amb,amb,amb, 1.0 };
  GLfloat light_diffuse[] = {diff, diff, diff, 1.0 };
  GLfloat light_specular[] = {spec, spec, spec, 1.0 };

  GLfloat light_position[] = {x,y,z, 0.0 };

  glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
  glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular);
  glLightfv(GL_LIGHT0, GL_POSITION, light_position);

 light_position[0] = -x;
 light_position[1] = y;
 light_position[2] = -z;

   glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient);
  glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);
  glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular);
  glLightfv(GL_LIGHT1, GL_POSITION, light_position);

  glEnable(GL_LIGHT0); //enable the light after setting the properties
  glEnable(GL_LIGHT1); //enable the light after setting the properties

}

// MAIN
// ----------------------------------------

int main(int argc, char** argv) {
   glutInit(&argc, argv);

   glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_MULTISAMPLE);
   glEnable(GL_MULTISAMPLE);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

   glEnable(GL_LINE_SMOOTH);
   glHint(GL_LINE_SMOOTH, GL_NICEST);

   glEnable(GL_POINT_SMOOTH);
   glHint(GL_POINT_SMOOTH, GL_NICEST);

   glutCreateWindow(title);
   if(fullScreenMode){
      glutFullScreen();
   }else{
      glutInitWindowSize(windowWidth, windowHeight);
      glutInitWindowPosition(windowPosX, windowPosY);
   }

   glutDisplayFunc(display);
   glutReshapeFunc(reshape);
   glutTimerFunc(0, Timer, 0);

   glutKeyboardFunc(keyboard);
   glutSpecialFunc(specialKeys);
   //glutMouseFunc(mouse);

   initGL();
   //ambient_lighting();
   add_point_light(0.5f, 0.6f, 3.0f, 0.6, 0.3, 1);
   camera_setup();
   init_automation();
   glutMainLoop();
   return 0;
}
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2013, Paul Beckingham, Federico Hernandez.
//
// 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.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#ifdef HAVE_LIBGNUTLS

#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/types.h>
#include <netdb.h>
#include <TLSClient.h>

#define MAX_BUF 16384

static bool trust_override = false;

////////////////////////////////////////////////////////////////////////////////
static void gnutls_log_function (int level, const char* message)
{
  std::cout << "c: " << level << " " << message;
}

////////////////////////////////////////////////////////////////////////////////
static int verify_certificate_callback (gnutls_session_t session)
{
  if (trust_override)
    return 0;

  // Read hostname.
  const char* hostname = (const char*) gnutls_session_get_ptr (session);

  // This verification function uses the trusted CAs in the credentials
  // structure. So you must have installed one or more CA certificates.
  unsigned int status;
  int ret = gnutls_certificate_verify_peers3 (session, hostname, &status);
  if (ret < 0)
    return GNUTLS_E_CERTIFICATE_ERROR;

  gnutls_certificate_type_t type = gnutls_certificate_type_get (session);
  gnutls_datum_t out;
  ret = gnutls_certificate_verification_status_print (status, type, &out, 0);
  if (ret < 0)
    return GNUTLS_E_CERTIFICATE_ERROR;

  std::cout << "c: INFO " << out.data << "\n";

  gnutls_free (out.data);

  if (status != 0)
    return GNUTLS_E_CERTIFICATE_ERROR;

  // Continue handshake.
  return 0;
}

////////////////////////////////////////////////////////////////////////////////
TLSClient::TLSClient ()
: _ca ("")
, _socket (0)
, _limit (0)
, _debug (false)
{
}

////////////////////////////////////////////////////////////////////////////////
TLSClient::~TLSClient ()
{
  gnutls_deinit (_session);
  gnutls_certificate_free_credentials (_credentials);
  gnutls_global_deinit ();

  if (_socket)
  {
    shutdown (_socket, SHUT_RDWR);
    close (_socket);
  }
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::limit (int max)
{
  _limit = max;
}

////////////////////////////////////////////////////////////////////////////////
// Calling this method results in all subsequent socket traffic being sent to
// std::cout, labelled with 'c: ...'.
void TLSClient::debug (int level)
{
  if (level)
    _debug = true;

  gnutls_global_set_log_function (gnutls_log_function);
  gnutls_global_set_log_level (level);
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::trust (bool value)
{
  trust_override = value;
  if (_debug)
  {
    if (trust_override)
      std::cout << "c: INFO Server certificate trusted automatically.\n";
    else
      std::cout << "c: INFO Server certificate trust verified.\n";
  }
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::init (
  const std::string& cert,
  const std::string& key)
{
  _cert = cert;
  _key  = key;

  gnutls_global_init ();
  gnutls_certificate_allocate_credentials (&_credentials);

  if (_cert != "" &&
      gnutls_certificate_set_x509_trust_file (_credentials, _cert.c_str (), GNUTLS_X509_FMT_PEM) < 0)
    throw std::string ("Missing CA file.");

  if (_cert != "" &&
      _key != "" &&
      gnutls_certificate_set_x509_key_file (_credentials, _cert.c_str (), _key.c_str (), GNUTLS_X509_FMT_PEM) < 0)
    throw std::string ("Missing CERT file.");

  gnutls_certificate_set_verify_function (_credentials, verify_certificate_callback);
  gnutls_init (&_session, GNUTLS_CLIENT);

  // Use default priorities.
  const char *err;
  int ret = gnutls_priority_set_direct (_session, "NORMAL", &err);
  if (ret < 0)
  {
    if (_debug && ret == GNUTLS_E_INVALID_REQUEST)
      std::cout << "c: ERROR Priority error at: " << err << "\n";

    throw std::string ("Error initializing TLS.");
  }

  // Apply the x509 credentials to the current session.
  gnutls_credentials_set (_session, GNUTLS_CRD_CERTIFICATE, _credentials);
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::connect (const std::string& host, const std::string& port)
{
  // use IPv4 or IPv6, does not matter.
  struct addrinfo hints = {0};
  hints.ai_family   = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags    = AI_PASSIVE; // use my IP

  struct addrinfo* res;
  if (::getaddrinfo (host.c_str (), port.c_str (), &hints, &res) != 0)
    throw std::string (::gai_strerror (errno));

  // Try them all, stop on success.
  struct addrinfo* p;
  for (p = res; p != NULL; p = p->ai_next)
  {
    if ((_socket = ::socket (p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
      continue;

    // When a socket is closed, it remains unavailable for a while (netstat -an).
    // Setting SO_REUSEADDR allows this program to assume control of a closed,
    // but unavailable socket.
    int on = 1;
    if (::setsockopt (_socket,
                      SOL_SOCKET,
                      SO_REUSEADDR,
                      (const void*) &on,
                      sizeof (on)) == -1)
      throw std::string (::strerror (errno));

    if (::connect (_socket, p->ai_addr, p->ai_addrlen) == -1)
      continue;

    break;
  }

  free (res);

  if (p == NULL)
    throw std::string ("Could not connect to ") + host + " " + port;

#if GNUTLS_VERSION_NUMBER >= 0x030109
  gnutls_transport_set_int (_session, _socket);
#else
  gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) (long) _socket);
#endif

  // Perform the TLS handshake
  int ret;
  do
  {
    ret = gnutls_handshake (_session);
  }
  while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
  if (ret < 0)
    throw std::string ("Handshake failed.  ") + gnutls_strerror (ret);

    if (_debug)
      std::cout << "c: INFO Handshake was completed\n";
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::bye ()
{
  gnutls_bye (_session, GNUTLS_SHUT_RDWR);
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::send (const std::string& data)
{
  std::string packet = "XXXX" + data;

  // Encode the length.
  unsigned long l = packet.length ();
  packet[0] = l >>24;
  packet[1] = l >>16;
  packet[2] = l >>8;
  packet[3] = l;

  unsigned int total = 0;
  unsigned int remaining = packet.length ();

  while (total < packet.length ())
  {
    int status;
    do
    {
      status = gnutls_record_send (_session, packet.c_str () + total, remaining);
    }
    while (errno == GNUTLS_E_INTERRUPTED ||
           errno == GNUTLS_E_AGAIN);

    if (status == -1)
      break;

    total     += (unsigned int) status;
    remaining -= (unsigned int) status;
  }

  if (_debug)
    std::cout << "c: INFO Sending 'XXXX"
              << data.c_str ()
              << "' (" << total << " bytes)"
              << std::endl;
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::recv (std::string& data)
{
  data = "";          // No appending of data.
  int received = 0;

  // Get the encoded length.
  unsigned char header[4] = {0};
  do
  {
    received = gnutls_record_recv (_session, header, 4);
  }
  while (received > 0 &&
         (errno == GNUTLS_E_INTERRUPTED ||
          errno == GNUTLS_E_AGAIN));

  int total = received;

  // Decode the length.
  unsigned long expected = (header[0]<<24) |
                           (header[1]<<16) |
                           (header[2]<<8) |
                            header[3];
  if (_debug)
    std::cout << "c: INFO expecting " << expected << " bytes.\n";

  // TODO This would be a good place to assert 'expected < _limit'.

  // Arbitrary buffer size.
  char buffer[MAX_BUF];

  // Keep reading until no more data.  Concatenate chunks of data if a) the
  // read was interrupted by a signal, and b) if there is more data than
  // fits in the buffer.
  do
  {
    do
    {
      received = gnutls_record_recv (_session, buffer, MAX_BUF - 1);
    }
    while (received > 0 &&
           (errno == GNUTLS_E_INTERRUPTED ||
            errno == GNUTLS_E_AGAIN));

    // Other end closed the connection.
    if (received == 0)
    {
      if (_debug)
        std::cout << "c: INFO Peer has closed the TLS connection\n";
      break;
    }

    // Something happened.
    if (received < 0)
      throw std::string (gnutls_strerror (received));

    buffer [received] = '\0';
    data += buffer;
    total += received;

    // Stop at defined limit.
    if (_limit && total > _limit)
      break;
  }
  while (received > 0 && total < (int) expected);

  if (_debug)
    std::cout << "c: INFO Receiving 'XXXX"
              << data.c_str ()
              << "' (" << total << " bytes)"
              << std::endl;
}

////////////////////////////////////////////////////////////////////////////////
#endif
<commit_msg>Recv<commit_after>////////////////////////////////////////////////////////////////////////////////
// taskwarrior - a command line task list manager.
//
// Copyright 2006 - 2013, Paul Beckingham, Federico Hernandez.
//
// 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.
//
// http://www.opensource.org/licenses/mit-license.php
//
////////////////////////////////////////////////////////////////////////////////

#ifdef HAVE_LIBGNUTLS

#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/types.h>
#include <netdb.h>
#include <TLSClient.h>

#define MAX_BUF 16384

static bool trust_override = false;

////////////////////////////////////////////////////////////////////////////////
static void gnutls_log_function (int level, const char* message)
{
  std::cout << "c: " << level << " " << message;
}

////////////////////////////////////////////////////////////////////////////////
static int verify_certificate_callback (gnutls_session_t session)
{
  if (trust_override)
    return 0;

  // Read hostname.
  const char* hostname = (const char*) gnutls_session_get_ptr (session);

  // This verification function uses the trusted CAs in the credentials
  // structure. So you must have installed one or more CA certificates.
  unsigned int status;
  int ret = gnutls_certificate_verify_peers3 (session, hostname, &status);
  if (ret < 0)
    return GNUTLS_E_CERTIFICATE_ERROR;

  gnutls_certificate_type_t type = gnutls_certificate_type_get (session);
  gnutls_datum_t out;
  ret = gnutls_certificate_verification_status_print (status, type, &out, 0);
  if (ret < 0)
    return GNUTLS_E_CERTIFICATE_ERROR;

  std::cout << "c: INFO " << out.data << "\n";

  gnutls_free (out.data);

  if (status != 0)
    return GNUTLS_E_CERTIFICATE_ERROR;

  // Continue handshake.
  return 0;
}

////////////////////////////////////////////////////////////////////////////////
TLSClient::TLSClient ()
: _ca ("")
, _socket (0)
, _limit (0)
, _debug (false)
{
}

////////////////////////////////////////////////////////////////////////////////
TLSClient::~TLSClient ()
{
  gnutls_deinit (_session);
  gnutls_certificate_free_credentials (_credentials);
  gnutls_global_deinit ();

  if (_socket)
  {
    shutdown (_socket, SHUT_RDWR);
    close (_socket);
  }
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::limit (int max)
{
  _limit = max;
}

////////////////////////////////////////////////////////////////////////////////
// Calling this method results in all subsequent socket traffic being sent to
// std::cout, labelled with 'c: ...'.
void TLSClient::debug (int level)
{
  if (level)
    _debug = true;

  gnutls_global_set_log_function (gnutls_log_function);
  gnutls_global_set_log_level (level);
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::trust (bool value)
{
  trust_override = value;
  if (_debug)
  {
    if (trust_override)
      std::cout << "c: INFO Server certificate trusted automatically.\n";
    else
      std::cout << "c: INFO Server certificate trust verified.\n";
  }
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::init (
  const std::string& cert,
  const std::string& key)
{
  _cert = cert;
  _key  = key;

  gnutls_global_init ();
  gnutls_certificate_allocate_credentials (&_credentials);

  if (_cert != "" &&
      gnutls_certificate_set_x509_trust_file (_credentials, _cert.c_str (), GNUTLS_X509_FMT_PEM) < 0)
    throw std::string ("Missing CA file.");

  if (_cert != "" &&
      _key != "" &&
      gnutls_certificate_set_x509_key_file (_credentials, _cert.c_str (), _key.c_str (), GNUTLS_X509_FMT_PEM) < 0)
    throw std::string ("Missing CERT file.");

  gnutls_certificate_set_verify_function (_credentials, verify_certificate_callback);
  gnutls_init (&_session, GNUTLS_CLIENT);

  // Use default priorities.
  const char *err;
  int ret = gnutls_priority_set_direct (_session, "NORMAL", &err);
  if (ret < 0)
  {
    if (_debug && ret == GNUTLS_E_INVALID_REQUEST)
      std::cout << "c: ERROR Priority error at: " << err << "\n";

    throw std::string ("Error initializing TLS.");
  }

  // Apply the x509 credentials to the current session.
  gnutls_credentials_set (_session, GNUTLS_CRD_CERTIFICATE, _credentials);
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::connect (const std::string& host, const std::string& port)
{
  // use IPv4 or IPv6, does not matter.
  struct addrinfo hints = {0};
  hints.ai_family   = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_flags    = AI_PASSIVE; // use my IP

  struct addrinfo* res;
  if (::getaddrinfo (host.c_str (), port.c_str (), &hints, &res) != 0)
    throw std::string (::gai_strerror (errno));

  // Try them all, stop on success.
  struct addrinfo* p;
  for (p = res; p != NULL; p = p->ai_next)
  {
    if ((_socket = ::socket (p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
      continue;

    // When a socket is closed, it remains unavailable for a while (netstat -an).
    // Setting SO_REUSEADDR allows this program to assume control of a closed,
    // but unavailable socket.
    int on = 1;
    if (::setsockopt (_socket,
                      SOL_SOCKET,
                      SO_REUSEADDR,
                      (const void*) &on,
                      sizeof (on)) == -1)
      throw std::string (::strerror (errno));

    if (::connect (_socket, p->ai_addr, p->ai_addrlen) == -1)
      continue;

    break;
  }

  free (res);

  if (p == NULL)
    throw std::string ("Could not connect to ") + host + " " + port;

#if GNUTLS_VERSION_NUMBER >= 0x030109
  gnutls_transport_set_int (_session, _socket);
#else
  gnutls_transport_set_ptr (_session, (gnutls_transport_ptr_t) (long) _socket);
#endif

  // Perform the TLS handshake
  int ret;
  do
  {
    ret = gnutls_handshake (_session);
  }
  while (ret < 0 && gnutls_error_is_fatal (ret) == 0);
  if (ret < 0)
    throw std::string ("Handshake failed.  ") + gnutls_strerror (ret);

    if (_debug)
      std::cout << "c: INFO Handshake was completed\n";
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::bye ()
{
  gnutls_bye (_session, GNUTLS_SHUT_RDWR);
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::send (const std::string& data)
{
  std::string packet = "XXXX" + data;

  // Encode the length.
  unsigned long l = packet.length ();
  packet[0] = l >>24;
  packet[1] = l >>16;
  packet[2] = l >>8;
  packet[3] = l;

  unsigned int total = 0;
  unsigned int remaining = packet.length ();

  while (total < packet.length ())
  {
    int status;
    do
    {
      status = gnutls_record_send (_session, packet.c_str () + total, remaining);
    }
    while (errno == GNUTLS_E_INTERRUPTED ||
           errno == GNUTLS_E_AGAIN);

    if (status == -1)
      break;

    total     += (unsigned int) status;
    remaining -= (unsigned int) status;
  }

  if (_debug)
    std::cout << "c: INFO Sending 'XXXX"
              << data.c_str ()
              << "' (" << total << " bytes)"
              << std::endl;
}

////////////////////////////////////////////////////////////////////////////////
void TLSClient::recv (std::string& data)
{
  data = "";          // No appending of data.
  int received = 0;

  // Get the encoded length.
  unsigned char header[4] = {0};
  do
  {
    received = gnutls_record_recv (_session, header, 4);
  }
  while (received > 0 &&
         (errno == GNUTLS_E_INTERRUPTED ||
          errno == GNUTLS_E_AGAIN));

  int total = received;

  // Decode the length.
  unsigned long expected = (header[0]<<24) |
                           (header[1]<<16) |
                           (header[2]<<8) |
                            header[3];
  if (_debug)
    std::cout << "c: INFO expecting " << expected << " bytes.\n";

  // TODO This would be a good place to assert 'expected < _limit'.

  // Arbitrary buffer size.
  char buffer[MAX_BUF];

  // Keep reading until no more data.  Concatenate chunks of data if a) the
  // read was interrupted by a signal, and b) if there is more data than
  // fits in the buffer.
  do
  {
    do
    {
      received = gnutls_record_recv (_session, buffer, MAX_BUF - 1);
    }
    while (received > 0 &&
           (errno == GNUTLS_E_INTERRUPTED ||
            errno == GNUTLS_E_AGAIN));

    // Other end closed the connection.
    if (received == 0)
    {
      if (_debug)
        std::cout << "c: INFO Peer has closed the TLS connection\n";
      break;
    }

    // Something happened.
    if (received < 0 && gnutls_error_is_fatal (received) == 0)
      std::cout << "c: WARNING " << gnutls_strerror (received) << "\n";

    else if (received < 0)
      throw std::string (gnutls_strerror (received));

    buffer [received] = '\0';
    data += buffer;
    total += received;

    // Stop at defined limit.
    if (_limit && total > _limit)
      break;
  }
  while (received > 0 && total < (int) expected);

  if (_debug)
    std::cout << "c: INFO Receiving 'XXXX"
              << data.c_str ()
              << "' (" << total << " bytes)"
              << std::endl;
}

////////////////////////////////////////////////////////////////////////////////
#endif
<|endoftext|>


<commit_before>#line 2 "togo/hash_map.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file hash_map.hpp
@brief HashMap interface.
@ingroup collections
@ingroup hash_map
*/

#pragma once

#include <togo/config.hpp>
#include <togo/types.hpp>
#include <togo/collection_types.hpp>
#include <togo/utility.hpp>
#include <togo/assert.hpp>
#include <togo/memory.hpp>
#include <togo/array.hpp>

#include <utility>

namespace togo {

/// Construct with allocator for storage.
template<class K, class T>
inline HashMap<K, T>::HashMap(Allocator& allocator)
	: _head(allocator)
	, _data(allocator)
{}

namespace hash_map {

/**
	@addtogroup hash_map
	@{
*/

/** @cond INTERNAL */
#define TOGO_HASH_MAP_MAX_LOAD 0.70f
/** @endcond */ // INTERNAL

/// Number of partitions.
template<class K, class T>
inline u32_fast num_partitions(HashMap<K, T> const& hm) { return array::size(hm._head); }

/// Number of values.
template<class K, class T>
inline u32_fast size(HashMap<K, T> const& hm) { return array::size(hm._data); }

/// Number of values reserved.
template<class K, class T>
inline u32_fast capacity(HashMap<K, T> const& hm) {
	return static_cast<u32_fast>(num_partitions(hm) * TOGO_HASH_MAP_MAX_LOAD);
}

/// Number of values that can be added before a resize occurs.
template<class K, class T>
inline u32_fast space(HashMap<K, T> const& hm) {
	return capacity(hm) - size(hm);
}

/// Returns true if there are any values.
template<class K, class T>
inline bool any(HashMap<K, T> const& hm) { return size(hm) != 0; }

/// Returns true if there are no values.
template<class K, class T>
inline bool empty(HashMap<K, T> const& hm) { return size(hm) == 0; }

/// Beginning iterator: [begin, end).
template<class K, class T>
inline HashMapNode<K, T>* begin(HashMap<K, T>& hm) {
	return array::begin(hm._data);
}
/// Beginning iterator: [begin, end).
template<class K, class T>
inline HashMapNode<K, T> const* begin(HashMap<K, T> const& hm) {
	return array::begin(hm._data);
}

/// Ending iterator: [begin, end).
template<class K, class T>
inline HashMapNode<K, T>* end(HashMap<K, T>& hm) {
	return array::end(hm._data);
}
/// Ending iterator: [begin, end).
template<class K, class T>
inline HashMapNode<K, T> const* end(HashMap<K, T> const& hm) {
	return array::end(hm._data);
}

/** @cond INTERNAL */
enum : unsigned {
	END = ~0u,
};

struct FindData {
	// Index into _head
	u32 head;
	// Index into _data
	u32 data;
	// Previous node with same key
	u32 data_prev;
};

namespace internal {

template<class K, class T>
FindData find(HashMap<K, T> const& hm, K const key) {
	FindData fd{END, END, END};
	if (array::empty(hm._head)) {
		return fd;
	}

	fd.head = key % num_partitions(hm);
	fd.data = hm._head[fd.head];
	while (fd.data != END) {
		auto const& node = hm._data[fd.data];
		if (node.key == key) {
			break;
		}
		fd.data_prev = fd.data;
		fd.data = node.next;
	}
	return fd;
}

template<class K, class T>
FindData find(
	HashMap<K, T> const& hm,
	HashMapNode<K, T> const* node
) {
	FindData fd{END, END, END};
	if (array::empty(hm._head)) {
		return fd;
	}

	fd.head = node->key % num_partitions(hm);
	fd.data = hm._head[fd.head];
	while (fd.data != END) {
		auto const& it_node = hm._data[fd.data];
		if (&it_node == node) {
			break;
		}
		fd.data_prev = fd.data;
		fd.data = it_node.next;
	}
	return fd;
}

template<class K, class T>
u32 make(HashMap<K, T>& hm, K const key, bool const keep) {
	TOGO_DEBUG_ASSERTE(array::any(hm._head));
	FindData const fd = find(hm, key);
	if (keep && fd.data != END) {
		return fd.data;
	}
	u32 const index = array::size(hm._data);
	HashMapNode<K, T> node;
	node.key = key;
	node.next = fd.data;
	array::push_back(hm._data, node);
	if (fd.data_prev == END) {
		hm._head[fd.head] = index;
	} else {
		hm._data[fd.data_prev].next = index;
	}
	return index;
}

template<class K, class T>
void remove(HashMap<K, T>& hm, FindData const& fd) {
	if (fd.data_prev == END) {
		// find() was immediate hit; fd is the first item
		// in the head. Move the head to the next item (if any).
		hm._head[fd.head] = hm._data[fd.data].next;
	} else {
		hm._data[fd.data_prev].next = hm._data[fd.data].next;
	}

	// Move last node to the removed position
	if (fd.data != array::size(hm._data) - 1) {
		auto const& last_node = array::back(hm._data);
		FindData const last = find(hm, &last_node);
		if (last.data_prev == END) {
			hm._head[last.head] = fd.data;
		} else {
			hm._data[last.data_prev].next = fd.data;
		}
		hm._data[fd.data] = last_node;
	}
	array::pop_back(hm._data);
}

template<class K, class T>
void resize(HashMap<K, T>& hm, u32_fast const new_size) {
	HashMap<K, T> new_hm{*hm._head._allocator};
	array::resize(new_hm._head, new_size);
	array::reserve(new_hm._data, min(new_size, array::capacity(hm._data)));
	for (unsigned i = 0; i < new_size; ++i) {
		new_hm._head[i] = END;
	}
	u32 index;
	for (auto const& node : hm._data) {
		index = make(hm, node.key, false);
		hm._data[index].value = node.value;
	}
	hm = std::move(new_hm);
}

template<class K, class T>
inline void grow(HashMap<K, T>& hm) {
	resize(hm, num_partitions(hm) * 2 + 16);
}

} // namespace internal
/** @endcond */ // INTERNAL

/// Reserve at least new_capacity.
template<class K, class T>
inline void reserve(HashMap<K, T>& hm, u32_fast const new_capacity) {
	if (new_capacity > capacity(hm)) {
		internal::resize(hm, (new_capacity / TOGO_HASH_MAP_MAX_LOAD) + 1);
	}
}

/// Remove all items.
template<class K, class T>
inline void clear(HashMap<K, T>& hm) {
	auto const size = num_partitions(hm);
	for (unsigned i = 0; i < size; ++i) {
		hm._head[i] = END;
	}
	array::clear(hm._data);
}

/// Set item.
///
/// If key does not exist, it will be inserted.
template<class K, class T>
inline void set(HashMap<K, T>& hm, K const key, T const& value) {
	if (!space(hm)) {
		internal::grow(hm);
	}
	auto const index = internal::make(hm, key, true);
	hm._data[index].value = value;
}

/// Push item.
///
/// If there are existing values with key, they are retained.
template<class K, class T>
inline void push(HashMap<K, T>& hm, K const key, T const& value) {
	if (!space(hm)) {
		internal::grow(hm);
	}
	auto const index = internal::make(hm, key, false);
	hm._data[index].value = value;
}

/// Get item.
///
/// If key does not exist, nullptr will be returned.
/// If multiple values are assigned to key, this will get the most
/// recently pushed one.
template<class K, class T>
inline T* get(HashMap<K, T>& hm, K const key) {
	auto const index = internal::find(hm, key).data;
	return (index != END) ? &hm._data[index].value : nullptr;
}

/// Get item.
///
/// If key does not exist, nullptr will be returned.
/// If multiple values are assigned to key, this will get the most
/// recently pushed one.
template<class K, class T>
inline T const* get(HashMap<K, T> const& hm, K const key) {
	auto const index = internal::find(hm, key).data;
	return (index != END) ? &hm._data[index].value : nullptr;
}

/// Get first node with key.
///
/// If there are no items with key, nullptr will be returned.
template<class K, class T>
inline HashMapNode<K, T>* get_node(
	HashMap<K, T>& hm,
	K const key
) {
	auto const index = internal::find(hm, key).data;
	return (index != END) ? &hm._data[index] : nullptr;
}

/// Get first node with key.
///
/// If there are no items with key, nullptr will be returned.
template<class K, class T>
inline HashMapNode<K, T> const* get_node(
	HashMap<K, T> const& hm,
	K const key
) {
	auto const index = internal::find(hm, key).data;
	return (index != END) ? &hm._data[index] : nullptr;
}

/// Get next node in keyset.
///
/// An assertion will fail if node is nullptr. Returns nullptr when
/// there are no more nodes for the key.
template<class K, class T>
inline HashMapNode<K, T>* get_next(
	HashMap<K, T>& hm,
	HashMapNode<K, T>* node
) {
	TOGO_ASSERTE(node != nullptr);
	K const key = node->key;
	while (node->next != END) {
		node = &hm._data[node->next];
		if (node->key == key) {
			return node;
		}
	}
	return nullptr;
}

/// Get next node in keyset.
///
/// An assertion will fail if node is nullptr. Returns nullptr when
/// there are no more nodes for the key.
template<class K, class T>
inline HashMapNode<K, T> const* get_next(
	HashMap<K, T> const& hm,
	HashMapNode<K, T> const* node
) {
	TOGO_ASSERTE(node != nullptr);
	K const key = node->key;
	while (node->next != END) {
		node = &hm._data[node->next];
		if (node->key == key) {
			return node;
		}
	}
	return nullptr;
}

/// Check if there is a value with key.
template<class K, class T>
inline bool has(HashMap<K, T> const& hm, K const key) {
	return internal::find(hm, key).data != END;
}

/// Count the number of values with key.
template<class K, class T>
inline unsigned count(HashMap<K, T> const& hm, K const key) {
	auto const* node = get_node(hm, key);
	unsigned count = 0;
	while (node != nullptr) {
		node = get_next(hm, node);
		++count;
	}
	return count;
}

/// Remove value.
///
/// If there are multiple values with key, the most recently added
/// one is removed.
template<class K, class T>
inline void remove(HashMap<K, T>& hm, K const key) {
	FindData const fd = internal::find(hm, key);
	if (fd.data != END) {
		internal::remove(hm, fd);
	}
}

/// Remove node.
///
/// An assertion will fail if node is nullptr.
template<class K, class T>
inline void remove(HashMap<K, T>& hm, HashMapNode<K, T> const* node) {
	TOGO_ASSERTE(node != nullptr);
	FindData const fd = internal::find(hm, node);
	if (fd.data != END) {
		internal::remove(hm, fd);
	}
}

/** @cond INTERNAL */
#undef TOGO_HASH_MAP_MAX_LOAD
/** @endcond */ // INTERNAL

/** @} */ // end of doc-group hash_map

} // namespace hash_map

/** @cond INTERNAL */

// ADL support

template<class K, class T>
inline HashMapNode<K, T>* begin(HashMap<K, T>& hm) {
	return hash_map::begin(hm);
}
template<class K, class T>
inline HashMapNode<K, T> const* begin(HashMap<K, T> const& hm) {
	return hash_map::begin(hm);
}

template<class K, class T>
inline HashMapNode<K, T>* end(HashMap<K, T>& hm) {
	return hash_map::end(hm);
}
template<class K, class T>
inline HashMapNode<K, T> const* end(HashMap<K, T> const& hm) {
	return hash_map::end(hm);
}

/** @endcond */ // INTERNAL

} // namespace togo
<commit_msg>hash_map: added value return to set() and push().<commit_after>#line 2 "togo/hash_map.hpp"
/**
@copyright MIT license; see @ref index or the accompanying LICENSE file.

@file hash_map.hpp
@brief HashMap interface.
@ingroup collections
@ingroup hash_map
*/

#pragma once

#include <togo/config.hpp>
#include <togo/types.hpp>
#include <togo/collection_types.hpp>
#include <togo/utility.hpp>
#include <togo/assert.hpp>
#include <togo/memory.hpp>
#include <togo/array.hpp>

#include <utility>

namespace togo {

/// Construct with allocator for storage.
template<class K, class T>
inline HashMap<K, T>::HashMap(Allocator& allocator)
	: _head(allocator)
	, _data(allocator)
{}

namespace hash_map {

/**
	@addtogroup hash_map
	@{
*/

/** @cond INTERNAL */
#define TOGO_HASH_MAP_MAX_LOAD 0.70f
/** @endcond */ // INTERNAL

/// Number of partitions.
template<class K, class T>
inline u32_fast num_partitions(HashMap<K, T> const& hm) { return array::size(hm._head); }

/// Number of values.
template<class K, class T>
inline u32_fast size(HashMap<K, T> const& hm) { return array::size(hm._data); }

/// Number of values reserved.
template<class K, class T>
inline u32_fast capacity(HashMap<K, T> const& hm) {
	return static_cast<u32_fast>(num_partitions(hm) * TOGO_HASH_MAP_MAX_LOAD);
}

/// Number of values that can be added before a resize occurs.
template<class K, class T>
inline u32_fast space(HashMap<K, T> const& hm) {
	return capacity(hm) - size(hm);
}

/// Returns true if there are any values.
template<class K, class T>
inline bool any(HashMap<K, T> const& hm) { return size(hm) != 0; }

/// Returns true if there are no values.
template<class K, class T>
inline bool empty(HashMap<K, T> const& hm) { return size(hm) == 0; }

/// Beginning iterator: [begin, end).
template<class K, class T>
inline HashMapNode<K, T>* begin(HashMap<K, T>& hm) {
	return array::begin(hm._data);
}
/// Beginning iterator: [begin, end).
template<class K, class T>
inline HashMapNode<K, T> const* begin(HashMap<K, T> const& hm) {
	return array::begin(hm._data);
}

/// Ending iterator: [begin, end).
template<class K, class T>
inline HashMapNode<K, T>* end(HashMap<K, T>& hm) {
	return array::end(hm._data);
}
/// Ending iterator: [begin, end).
template<class K, class T>
inline HashMapNode<K, T> const* end(HashMap<K, T> const& hm) {
	return array::end(hm._data);
}

/** @cond INTERNAL */
enum : unsigned {
	END = ~0u,
};

struct FindData {
	// Index into _head
	u32 head;
	// Index into _data
	u32 data;
	// Previous node with same key
	u32 data_prev;
};

namespace internal {

template<class K, class T>
FindData find(HashMap<K, T> const& hm, K const key) {
	FindData fd{END, END, END};
	if (array::empty(hm._head)) {
		return fd;
	}

	fd.head = key % num_partitions(hm);
	fd.data = hm._head[fd.head];
	while (fd.data != END) {
		auto const& node = hm._data[fd.data];
		if (node.key == key) {
			break;
		}
		fd.data_prev = fd.data;
		fd.data = node.next;
	}
	return fd;
}

template<class K, class T>
FindData find(
	HashMap<K, T> const& hm,
	HashMapNode<K, T> const* node
) {
	FindData fd{END, END, END};
	if (array::empty(hm._head)) {
		return fd;
	}

	fd.head = node->key % num_partitions(hm);
	fd.data = hm._head[fd.head];
	while (fd.data != END) {
		auto const& it_node = hm._data[fd.data];
		if (&it_node == node) {
			break;
		}
		fd.data_prev = fd.data;
		fd.data = it_node.next;
	}
	return fd;
}

template<class K, class T>
u32 make(HashMap<K, T>& hm, K const key, bool const keep) {
	TOGO_DEBUG_ASSERTE(array::any(hm._head));
	FindData const fd = find(hm, key);
	if (keep && fd.data != END) {
		return fd.data;
	}
	u32 const index = array::size(hm._data);
	HashMapNode<K, T> node;
	node.key = key;
	node.next = fd.data;
	array::push_back(hm._data, node);
	if (fd.data_prev == END) {
		hm._head[fd.head] = index;
	} else {
		hm._data[fd.data_prev].next = index;
	}
	return index;
}

template<class K, class T>
void remove(HashMap<K, T>& hm, FindData const& fd) {
	if (fd.data_prev == END) {
		// find() was immediate hit; fd is the first item
		// in the head. Move the head to the next item (if any).
		hm._head[fd.head] = hm._data[fd.data].next;
	} else {
		hm._data[fd.data_prev].next = hm._data[fd.data].next;
	}

	// Move last node to the removed position
	if (fd.data != array::size(hm._data) - 1) {
		auto const& last_node = array::back(hm._data);
		FindData const last = find(hm, &last_node);
		if (last.data_prev == END) {
			hm._head[last.head] = fd.data;
		} else {
			hm._data[last.data_prev].next = fd.data;
		}
		hm._data[fd.data] = last_node;
	}
	array::pop_back(hm._data);
}

template<class K, class T>
void resize(HashMap<K, T>& hm, u32_fast const new_size) {
	HashMap<K, T> new_hm{*hm._head._allocator};
	array::resize(new_hm._head, new_size);
	array::reserve(new_hm._data, min(new_size, array::capacity(hm._data)));
	for (unsigned i = 0; i < new_size; ++i) {
		new_hm._head[i] = END;
	}
	u32 index;
	for (auto const& node : hm._data) {
		index = make(hm, node.key, false);
		hm._data[index].value = node.value;
	}
	hm = std::move(new_hm);
}

template<class K, class T>
inline void grow(HashMap<K, T>& hm) {
	resize(hm, num_partitions(hm) * 2 + 16);
}

} // namespace internal
/** @endcond */ // INTERNAL

/// Reserve at least new_capacity.
template<class K, class T>
inline void reserve(HashMap<K, T>& hm, u32_fast const new_capacity) {
	if (new_capacity > capacity(hm)) {
		internal::resize(hm, (new_capacity / TOGO_HASH_MAP_MAX_LOAD) + 1);
	}
}

/// Remove all items.
template<class K, class T>
inline void clear(HashMap<K, T>& hm) {
	auto const size = num_partitions(hm);
	for (unsigned i = 0; i < size; ++i) {
		hm._head[i] = END;
	}
	array::clear(hm._data);
}

/// Set item.
///
/// If key does not exist, it will be inserted.
template<class K, class T>
inline T& set(HashMap<K, T>& hm, K const key, T const& value) {
	if (!space(hm)) {
		internal::grow(hm);
	}
	auto const index = internal::make(hm, key, true);
	return hm._data[index].value = value;
}

/// Push item.
///
/// If there are existing values with key, they are retained.
template<class K, class T>
inline T& push(HashMap<K, T>& hm, K const key, T const& value) {
	if (!space(hm)) {
		internal::grow(hm);
	}
	auto const index = internal::make(hm, key, false);
	return hm._data[index].value = value;
}

/// Get item.
///
/// If key does not exist, nullptr will be returned.
/// If multiple values are assigned to key, this will get the most
/// recently pushed one.
template<class K, class T>
inline T* get(HashMap<K, T>& hm, K const key) {
	auto const index = internal::find(hm, key).data;
	return (index != END) ? &hm._data[index].value : nullptr;
}

/// Get item.
///
/// If key does not exist, nullptr will be returned.
/// If multiple values are assigned to key, this will get the most
/// recently pushed one.
template<class K, class T>
inline T const* get(HashMap<K, T> const& hm, K const key) {
	auto const index = internal::find(hm, key).data;
	return (index != END) ? &hm._data[index].value : nullptr;
}

/// Get first node with key.
///
/// If there are no items with key, nullptr will be returned.
template<class K, class T>
inline HashMapNode<K, T>* get_node(
	HashMap<K, T>& hm,
	K const key
) {
	auto const index = internal::find(hm, key).data;
	return (index != END) ? &hm._data[index] : nullptr;
}

/// Get first node with key.
///
/// If there are no items with key, nullptr will be returned.
template<class K, class T>
inline HashMapNode<K, T> const* get_node(
	HashMap<K, T> const& hm,
	K const key
) {
	auto const index = internal::find(hm, key).data;
	return (index != END) ? &hm._data[index] : nullptr;
}

/// Get next node in keyset.
///
/// An assertion will fail if node is nullptr. Returns nullptr when
/// there are no more nodes for the key.
template<class K, class T>
inline HashMapNode<K, T>* get_next(
	HashMap<K, T>& hm,
	HashMapNode<K, T>* node
) {
	TOGO_ASSERTE(node != nullptr);
	K const key = node->key;
	while (node->next != END) {
		node = &hm._data[node->next];
		if (node->key == key) {
			return node;
		}
	}
	return nullptr;
}

/// Get next node in keyset.
///
/// An assertion will fail if node is nullptr. Returns nullptr when
/// there are no more nodes for the key.
template<class K, class T>
inline HashMapNode<K, T> const* get_next(
	HashMap<K, T> const& hm,
	HashMapNode<K, T> const* node
) {
	TOGO_ASSERTE(node != nullptr);
	K const key = node->key;
	while (node->next != END) {
		node = &hm._data[node->next];
		if (node->key == key) {
			return node;
		}
	}
	return nullptr;
}

/// Check if there is a value with key.
template<class K, class T>
inline bool has(HashMap<K, T> const& hm, K const key) {
	return internal::find(hm, key).data != END;
}

/// Count the number of values with key.
template<class K, class T>
inline unsigned count(HashMap<K, T> const& hm, K const key) {
	auto const* node = get_node(hm, key);
	unsigned count = 0;
	while (node != nullptr) {
		node = get_next(hm, node);
		++count;
	}
	return count;
}

/// Remove value.
///
/// If there are multiple values with key, the most recently added
/// one is removed.
template<class K, class T>
inline void remove(HashMap<K, T>& hm, K const key) {
	FindData const fd = internal::find(hm, key);
	if (fd.data != END) {
		internal::remove(hm, fd);
	}
}

/// Remove node.
///
/// An assertion will fail if node is nullptr.
template<class K, class T>
inline void remove(HashMap<K, T>& hm, HashMapNode<K, T> const* node) {
	TOGO_ASSERTE(node != nullptr);
	FindData const fd = internal::find(hm, node);
	if (fd.data != END) {
		internal::remove(hm, fd);
	}
}

/** @cond INTERNAL */
#undef TOGO_HASH_MAP_MAX_LOAD
/** @endcond */ // INTERNAL

/** @} */ // end of doc-group hash_map

} // namespace hash_map

/** @cond INTERNAL */

// ADL support

template<class K, class T>
inline HashMapNode<K, T>* begin(HashMap<K, T>& hm) {
	return hash_map::begin(hm);
}
template<class K, class T>
inline HashMapNode<K, T> const* begin(HashMap<K, T> const& hm) {
	return hash_map::begin(hm);
}

template<class K, class T>
inline HashMapNode<K, T>* end(HashMap<K, T>& hm) {
	return hash_map::end(hm);
}
template<class K, class T>
inline HashMapNode<K, T> const* end(HashMap<K, T> const& hm) {
	return hash_map::end(hm);
}

/** @endcond */ // INTERNAL

} // namespace togo
<|endoftext|>