<commit_before>#include <iostream>

#include "unit.h"
#include "surface.h"
#include "random.h"

using namespace std;

Unit::Unit(SDL_Surface* tileset, SDL_Surface* shadowset) {
  init(tileset, shadowset, 128 + rnd(256), 128 + rnd(256));
}

Unit::Unit(SDL_Surface* tileset, SDL_Surface* shadowset, int x, int y) {
  init(tileset, shadowset, x, y);
}

Unit::~Unit() {
  SDL_FreeSurface(tileset);
  SDL_FreeSurface(shadowset);
}

void Unit::draw(SDL_Surface* screen, int x, int y) {
  int src_x = this->body_facing * this->tile_width;
  int src_y = this->tile_height * this->anim_frame;

  Surface::draw(shadowset, screen, src_x, src_y, this->tile_width, this->tile_height, x, y, this->shadow_alpha);
  Surface::draw(tileset, screen, src_x, src_y, this->tile_width, this->tile_height, x, y);

  if (selected)
      Surface::draw(selected_bitmap, screen, x, y);
}

void Unit::init(SDL_Surface* tileset, SDL_Surface* shadowset, int x, int y) {
  this->selected = false;
  this->selected_bitmap = Surface::load("graphics/selected.bmp", 0, 0, 0);
  this->tileset = tileset;
  this->shadowset = shadowset;
  this->body_facing = rnd(FACINGS);
  this->tile_width = tileset->w / FACINGS;
  // check if our assumption (width==height) is true for this tileset, and if not warn the user.
  if (tileset->h % this->tile_width > 0) {
    cerr << "WARNING: This tileset does not meet the requirement : tile height must equal width." << endl << endl;
    cerr << "Based on 8 sides of a unit, the calculated width of a tile on this tileset is " << this->tile_width << ". The general assumption is that the height of a tile must equal width. " << endl;
    cerr << "However the height of this tileset (" << tileset->h << ") was not one or more multiplications of " << this->tile_width << ". Its dimensions are " << tileset->h << "x" << tileset->w << ".";
    cerr << endl;

    // if it is less height than wide, we
    if (tileset->h < this->tile_width) {
      cerr << "Because the height of the tileset (" << tileset->h << ") is *lower* than the width of a calculated tile(=" << this->tile_width << ") (and therefor has no animations). We assume the height of the tileset of " << tileset->h << "." << endl;
      this->tile_height = tileset->h;
    } else {
      cerr << "Since the height of the tileset (" << tileset->h << ") is *greater* than the width of a calculated tile(=" << this->tile_width <<") (which means there could be animations) we assume the height equals the calculated width of " << this->tile_width << "." << endl;
      this->tile_height = this->tile_width;
    }
    cerr << endl;
  } else {
    this->tile_height = this->tile_width;
  }
  this->shadow_alpha = 128;
  this->x = x;
  this->y = y;
  this->anim_frame = 0;

  // every pixel short/too much of the perfect tile size will be spread evenly
  this->offset_x = (TILE_SIZE - this->tile_width) / 2;
  this->offset_y = (TILE_SIZE - this->tile_height) / 2;

}

//////////////////////////////////////////
// Unit Repository
//
//////////////////////////////////////////

UnitRepository::UnitRepository() {
  for (int i = 0; i < MAX_UNIT_TYPES; i++) {
    unit_animation[i] = NULL;
    unit_shadow[i] = NULL;
  }

  unit_animation[UNIT_QUAD] = Surface::load8bit("graphics/Unit_Quad.bmp");
  unit_shadow[UNIT_QUAD] = Surface::load("graphics/Unit_Quad_s.bmp", 255, 0, 255);

  unit_animation[UNIT_DEVASTATOR] = Surface::load8bit("graphics/Unit_Devastator.bmp");
  unit_shadow[UNIT_DEVASTATOR] = Surface::load("graphics/Unit_Devastator_s.bmp", 255, 0, 255);

  unit_animation[UNIT_FRIGATE] = Surface::load8bit("graphics/Unit_Frigate.bmp");
  unit_shadow[UNIT_FRIGATE] = Surface::load("graphics/Unit_Frigate_s.bmp", 255, 0, 255);

  unit_animation[UNIT_CARRYALL] = Surface::load8bit("graphics/Unit_Carryall.bmp");
  unit_shadow[UNIT_CARRYALL] = Surface::load("graphics/Unit_Carryall_s.bmp", 255, 0, 255);

  unit_animation[UNIT_TRIKE] = Surface::load8bit("graphics/Unit_Trike.bmp");
  unit_shadow[UNIT_TRIKE] = Surface::load("graphics/Unit_Trike_s.bmp", 255, 0, 255);
}

UnitRepository::~UnitRepository() {
  for (int i = 0; i < MAX_UNIT_TYPES; i++) {
    SDL_FreeSurface(unit_animation[i]);
    SDL_FreeSurface(unit_shadow[i]);
  }
}

Unit* UnitRepository::create(int unitType, int house, int x, int y) {
  SDL_Surface* copy = Surface::copy(unit_animation[unitType]);
  int paletteIndexUsedForColoring = 144;
  int paletteIndex = paletteIndexUsedForColoring + (16 * house);
  SDL_SetColors(copy, &copy->format->palette->colors[paletteIndex], paletteIndexUsedForColoring, 8);

  SDL_Surface* shadow_copy = Surface::copy(unit_shadow[unitType]);
  Unit* unit = new Unit(copy, shadow_copy, x, y);
  return unit;
}
<commit_msg>use different RGB colorkey so it renders the 'selected' state of a unit with the correct gfx.<commit_after>#include <iostream>

#include "unit.h"
#include "surface.h"
#include "random.h"

using namespace std;

Unit::Unit(SDL_Surface* tileset, SDL_Surface* shadowset) {
  init(tileset, shadowset, 128 + rnd(256), 128 + rnd(256));
}

Unit::Unit(SDL_Surface* tileset, SDL_Surface* shadowset, int x, int y) {
  init(tileset, shadowset, x, y);
}

Unit::~Unit() {
  SDL_FreeSurface(tileset);
  SDL_FreeSurface(shadowset);
}

void Unit::draw(SDL_Surface* screen, int x, int y) {
  int src_x = this->body_facing * this->tile_width;
  int src_y = this->tile_height * this->anim_frame;

  Surface::draw(shadowset, screen, src_x, src_y, this->tile_width, this->tile_height, x, y, this->shadow_alpha);
  Surface::draw(tileset, screen, src_x, src_y, this->tile_width, this->tile_height, x, y);

  if (selected)
      Surface::draw(selected_bitmap, screen, x, y);
}

void Unit::init(SDL_Surface* tileset, SDL_Surface* shadowset, int x, int y) {
  this->selected = false;
  this->selected_bitmap = Surface::load("graphics/selected.bmp", 255, 0, 255);
  this->tileset = tileset;
  this->shadowset = shadowset;
  this->body_facing = rnd(FACINGS);
  this->tile_width = tileset->w / FACINGS;
  // check if our assumption (width==height) is true for this tileset, and if not warn the user.
  if (tileset->h % this->tile_width > 0) {
    cerr << "WARNING: This tileset does not meet the requirement : tile height must equal width." << endl << endl;
    cerr << "Based on 8 sides of a unit, the calculated width of a tile on this tileset is " << this->tile_width << ". The general assumption is that the height of a tile must equal width. " << endl;
    cerr << "However the height of this tileset (" << tileset->h << ") was not one or more multiplications of " << this->tile_width << ". Its dimensions are " << tileset->h << "x" << tileset->w << ".";
    cerr << endl;

    // if it is less height than wide, we
    if (tileset->h < this->tile_width) {
      cerr << "Because the height of the tileset (" << tileset->h << ") is *lower* than the width of a calculated tile(=" << this->tile_width << ") (and therefor has no animations). We assume the height of the tileset of " << tileset->h << "." << endl;
      this->tile_height = tileset->h;
    } else {
      cerr << "Since the height of the tileset (" << tileset->h << ") is *greater* than the width of a calculated tile(=" << this->tile_width <<") (which means there could be animations) we assume the height equals the calculated width of " << this->tile_width << "." << endl;
      this->tile_height = this->tile_width;
    }
    cerr << endl;
  } else {
    this->tile_height = this->tile_width;
  }
  this->shadow_alpha = 128;
  this->x = x;
  this->y = y;
  this->anim_frame = 0;

  // every pixel short/too much of the perfect tile size will be spread evenly
  this->offset_x = (TILE_SIZE - this->tile_width) / 2;
  this->offset_y = (TILE_SIZE - this->tile_height) / 2;

}

//////////////////////////////////////////
// Unit Repository
//
//////////////////////////////////////////

UnitRepository::UnitRepository() {
  for (int i = 0; i < MAX_UNIT_TYPES; i++) {
    unit_animation[i] = NULL;
    unit_shadow[i] = NULL;
  }

  unit_animation[UNIT_QUAD] = Surface::load8bit("graphics/Unit_Quad.bmp");
  unit_shadow[UNIT_QUAD] = Surface::load("graphics/Unit_Quad_s.bmp", 255, 0, 255);

  unit_animation[UNIT_DEVASTATOR] = Surface::load8bit("graphics/Unit_Devastator.bmp");
  unit_shadow[UNIT_DEVASTATOR] = Surface::load("graphics/Unit_Devastator_s.bmp", 255, 0, 255);

  unit_animation[UNIT_FRIGATE] = Surface::load8bit("graphics/Unit_Frigate.bmp");
  unit_shadow[UNIT_FRIGATE] = Surface::load("graphics/Unit_Frigate_s.bmp", 255, 0, 255);

  unit_animation[UNIT_CARRYALL] = Surface::load8bit("graphics/Unit_Carryall.bmp");
  unit_shadow[UNIT_CARRYALL] = Surface::load("graphics/Unit_Carryall_s.bmp", 255, 0, 255);

  unit_animation[UNIT_TRIKE] = Surface::load8bit("graphics/Unit_Trike.bmp");
  unit_shadow[UNIT_TRIKE] = Surface::load("graphics/Unit_Trike_s.bmp", 255, 0, 255);
}

UnitRepository::~UnitRepository() {
  for (int i = 0; i < MAX_UNIT_TYPES; i++) {
    SDL_FreeSurface(unit_animation[i]);
    SDL_FreeSurface(unit_shadow[i]);
  }
}

Unit* UnitRepository::create(int unitType, int house, int x, int y) {
  SDL_Surface* copy = Surface::copy(unit_animation[unitType]);
  int paletteIndexUsedForColoring = 144;
  int paletteIndex = paletteIndexUsedForColoring + (16 * house);
  SDL_SetColors(copy, &copy->format->palette->colors[paletteIndex], paletteIndexUsedForColoring, 8);

  SDL_Surface* shadow_copy = Surface::copy(unit_shadow[unitType]);
  Unit* unit = new Unit(copy, shadow_copy, x, y);
  return unit;
}
<|endoftext|>


<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 "events/event.h"
#include "events/roommemberevent.h"
#include "jobs/mediathumbnailjob.h"

#include <QtCore/QTimer>
#include <QtCore/QPair>

using namespace QMatrixClient;

class User::Private
{
    public:
        User* q;
        QString userId;
        QString name;
        QUrl avatarUrl;
        Connection* connection;

        QPixmap avatar;
        int requestedWidth;
        int requestedHeight;
        bool avatarValid;
        bool avatarOngoingRequest;
        QHash<QPair<int,int>,QPixmap> scaledMap;

        void requestAvatar();
};

User::User(QString userId, Connection* connection)
    : QObject(connection), d(new Private)
{
    d->q = this;
    d->connection = connection;
    d->userId = userId;
    d->avatarValid = false;
    d->avatarOngoingRequest = false;
}

User::~User()
{
    delete d;
}

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

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

QString User::displayname() const
{
    if( !d->name.isEmpty() )
        return d->name;
    return d->userId;
}

QPixmap User::avatar(int width, int height)
{
    if( !d->avatarValid
        || width > d->requestedWidth
        || height > d->requestedHeight )
    {
        if( !d->avatarOngoingRequest && d->avatarUrl.isValid() )
        {
            qDebug() << "Getting avatar for" << id();
            d->requestedWidth = width;
            d->requestedHeight = height;
            d->avatarOngoingRequest = true;
            QTimer::singleShot(0, this, SLOT(requestAvatar()));
        }
    }

    if( d->avatar.isNull() )
        return d->avatar;
    QPair<int,int> size(width, height);
    if( !d->scaledMap.contains(size) )
    {
        d->scaledMap.insert(size, d->avatar.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    }
    return d->scaledMap.value(size);
}

void User::processEvent(Event* event)
{
    if( event->type() == EventType::RoomMember )
    {
        RoomMemberEvent* e = static_cast<RoomMemberEvent*>(event);
        if( d->name != e->displayName() )
        {
            const auto oldName = d->name;
            d->name = e->displayName();
            emit nameChanged(this, oldName);
        }
        if( d->avatarUrl != e->avatarUrl() )
        {
            d->avatarUrl = e->avatarUrl();
            d->avatarValid = false;
        }
    }
}

void User::requestAvatar()
{
    d->requestAvatar();
}

void User::Private::requestAvatar()
{
    MediaThumbnailJob* job =
            connection->getThumbnail(avatarUrl, requestedWidth, requestedHeight);
    connect( job, &MediaThumbnailJob::success, [=]() {
        avatarOngoingRequest = false;
        avatarValid = true;
        avatar = job->thumbnail().scaled(requestedWidth, requestedHeight,
                        Qt::KeepAspectRatio, Qt::SmoothTransformation);
        scaledMap.clear();
        emit q->avatarChanged(q);
    });
}
<commit_msg>Added a missing #include<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 "events/event.h"
#include "events/roommemberevent.h"
#include "jobs/mediathumbnailjob.h"

#include <QtCore/QTimer>
#include <QtCore/QPair>
#include <QtCore/QDebug>

using namespace QMatrixClient;

class User::Private
{
    public:
        User* q;
        QString userId;
        QString name;
        QUrl avatarUrl;
        Connection* connection;

        QPixmap avatar;
        int requestedWidth;
        int requestedHeight;
        bool avatarValid;
        bool avatarOngoingRequest;
        QHash<QPair<int,int>,QPixmap> scaledMap;

        void requestAvatar();
};

User::User(QString userId, Connection* connection)
    : QObject(connection), d(new Private)
{
    d->q = this;
    d->connection = connection;
    d->userId = userId;
    d->avatarValid = false;
    d->avatarOngoingRequest = false;
}

User::~User()
{
    delete d;
}

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

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

QString User::displayname() const
{
    if( !d->name.isEmpty() )
        return d->name;
    return d->userId;
}

QPixmap User::avatar(int width, int height)
{
    if( !d->avatarValid
        || width > d->requestedWidth
        || height > d->requestedHeight )
    {
        if( !d->avatarOngoingRequest && d->avatarUrl.isValid() )
        {
            qDebug() << "Getting avatar for" << id();
            d->requestedWidth = width;
            d->requestedHeight = height;
            d->avatarOngoingRequest = true;
            QTimer::singleShot(0, this, SLOT(requestAvatar()));
        }
    }

    if( d->avatar.isNull() )
        return d->avatar;
    QPair<int,int> size(width, height);
    if( !d->scaledMap.contains(size) )
    {
        d->scaledMap.insert(size, d->avatar.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation));
    }
    return d->scaledMap.value(size);
}

void User::processEvent(Event* event)
{
    if( event->type() == EventType::RoomMember )
    {
        RoomMemberEvent* e = static_cast<RoomMemberEvent*>(event);
        if( d->name != e->displayName() )
        {
            const auto oldName = d->name;
            d->name = e->displayName();
            emit nameChanged(this, oldName);
        }
        if( d->avatarUrl != e->avatarUrl() )
        {
            d->avatarUrl = e->avatarUrl();
            d->avatarValid = false;
        }
    }
}

void User::requestAvatar()
{
    d->requestAvatar();
}

void User::Private::requestAvatar()
{
    MediaThumbnailJob* job =
            connection->getThumbnail(avatarUrl, requestedWidth, requestedHeight);
    connect( job, &MediaThumbnailJob::success, [=]() {
        avatarOngoingRequest = false;
        avatarValid = true;
        avatar = job->thumbnail().scaled(requestedWidth, requestedHeight,
                        Qt::KeepAspectRatio, Qt::SmoothTransformation);
        scaledMap.clear();
        emit q->avatarChanged(q);
    });
}
<|endoftext|>


<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 "jobs/setroomstatejob.h"
#include "jobs/generated/profile.h"
#include "jobs/generated/content-repo.h"

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

#include <functional>
#include <unordered_set>

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

class User::Private
{
    public:
        static Avatar* makeAvatar(QUrl url);

        Private(QString userId, Connection* connection)
            : userId(move(userId)), connection(connection)
        { }
        ~Private()
        {
            for (auto a: otherAvatars)
                delete a;
        }

        QString userId;
        Connection* connection;

        QString mostUsedName;
        QString bridged;
        const QScopedPointer<Avatar> mostUsedAvatar { makeAvatar({}) };
        QMultiHash<QString, const Room*> otherNames;
        QHash<QUrl, Avatar*> otherAvatars;
        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);

};

Avatar* User::Private::makeAvatar(QUrl url)
{
    static const QIcon icon
        { QIcon::fromTheme(QStringLiteral("user-available")) };
    return new Avatar(url, icon);
}

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))
    {
        delete otherAvatars.value(oldUrl);
        otherAvatars.remove(oldUrl);
    }
    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* nextMostUsed = otherAvatars.take(newUrl);
            std::swap(*mostUsedAvatar, *nextMostUsed);
            otherAvatars.insert(nextMostUsed->url(), nextMostUsed);
            for (const auto* r1: connection->roomMap())
                if (avatarUrlForRoom(r1) == nextMostUsed->url())
                    avatarsToRooms.insert(nextMostUsed->url(), r1);

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

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

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);
}

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 = d->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);
    }
    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 = d->connection->callApi<SetRoomStateJob>(
                r->id(), id(), RoomMemberEvent(move(evtC)));
    connect(job, &BaseJob::success, this, [=] { updateName(newName, r); });
}

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

bool User::setAvatar(QIODevice* source)
{
    return avatarObject().upload(d->connection, source,
                std::bind(&Private::setAvatarOnServer, d.data(), _1, 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(name) : 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
{
    return *d->otherAvatars.value(d->avatarUrlForRoom(room),
                                  d->mostUsedAvatar.data());
}

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,
                    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(RoomMemberEvent* event, const Room* 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(event->displayName(), oldNameHint, room);
        updateAvatarUrl(event->avatarUrl(),
                        d->avatarUrlForRoom(room, event->prevContent()->avatarUrl),
                        room);
    } else {
        updateName(event->displayName(), room);
        updateAvatarUrl(event->avatarUrl(), d->avatarUrlForRoom(room), room);
    }
}
<commit_msg>User: use std::unique_ptr instead of QScopedPointer<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 "jobs/setroomstatejob.h"
#include "jobs/generated/profile.h"
#include "jobs/generated/content-repo.h"

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

#include <functional>
#include <unordered_set>

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

class User::Private
{
    public:
        static Avatar* makeAvatar(QUrl url);

        Private(QString userId, Connection* connection)
            : userId(move(userId)), connection(connection)
        { }
        ~Private()
        {
            for (auto a: otherAvatars)
                delete a;
        }

        QString userId;
        Connection* connection;

        QString mostUsedName;
        QString bridged;
        const std::unique_ptr<Avatar> mostUsedAvatar { makeAvatar({}) };
        QMultiHash<QString, const Room*> otherNames;
        QHash<QUrl, Avatar*> otherAvatars;
        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);

};

Avatar* User::Private::makeAvatar(QUrl url)
{
    static const QIcon icon
        { QIcon::fromTheme(QStringLiteral("user-available")) };
    return new Avatar(move(url), icon);
}

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))
    {
        delete otherAvatars.value(oldUrl);
        otherAvatars.remove(oldUrl);
    }
    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* nextMostUsed = otherAvatars.take(newUrl);
            std::swap(*mostUsedAvatar, *nextMostUsed);
            otherAvatars.insert(nextMostUsed->url(), nextMostUsed);
            for (const auto* r1: connection->roomMap())
                if (avatarUrlForRoom(r1) == nextMostUsed->url())
                    avatarsToRooms.insert(nextMostUsed->url(), r1);

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

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

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);
}

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 = d->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);
    }
    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 = d->connection->callApi<SetRoomStateJob>(
                r->id(), id(), RoomMemberEvent(move(evtC)));
    connect(job, &BaseJob::success, this, [=] { updateName(newName, r); });
}

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

bool User::setAvatar(QIODevice* source)
{
    return avatarObject().upload(d->connection, source,
                std::bind(&Private::setAvatarOnServer, d.data(), _1, 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(name) : 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
{
    return *d->otherAvatars.value(d->avatarUrlForRoom(room),
                                  d->mostUsedAvatar.get());
}

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,
                    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(RoomMemberEvent* event, const Room* 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(event->displayName(), oldNameHint, room);
        updateAvatarUrl(event->avatarUrl(),
                        d->avatarUrlForRoom(room, event->prevContent()->avatarUrl),
                        room);
    } else {
        updateName(event->displayName(), room);
        updateAvatarUrl(event->avatarUrl(), d->avatarUrlForRoom(room), room);
    }
}
<|endoftext|>


<commit_before>#pragma once

#include <DO/Sara/Core/EigenExtension.hpp>
#include <DO/Sara/Core/Tensor.hpp>
#include <DO/Sara/Core/Image/Operations.hpp>

namespace DO { namespace Sara {

  // In this world everything, everything is **ROW-MAJOR**.
  template <typename T, int N>
  using TensorView_ = TensorView<T, N, RowMajor>;

  template <typename T, int N>
  using Tensor_ = Tensor<T, N, RowMajor>;

  template <typename T, int N>
  auto image_view(TensorView_<T, N> in) -> ImageView<T, N>
  {
    auto out_sizes = in.sizes();
    std::reverse(out_sizes.data(), out_sizes.data() + N);
    return ImageView<T, N>{in.data(), out_sizes};
  }

  template <typename T, int N>
  auto patch(const TensorView_<T, N>& in, const Matrix<int, N, 1>& beg,
             const Matrix<int, N, 1>& end) -> Image<T, N>
  {
    return safe_crop(image_view(in), beg, end);
  }

  template <typename T, int N>
  auto vec(const TensorView_<T, N>& in)
      -> Map<const Matrix<typename ElementTraits<T>::value_type,
             Dynamic, 1>>
  {
    return {
      reinterpret_cast<typename ElementTraits<T>::pointer>(in.data()),
      static_cast<int64_t>(in.size())
    };
  }

  template <typename T, int N>
  auto vec(TensorView_<T, N>& in)
      -> Map<const Matrix<typename ElementTraits<T>::value_type,
             Dynamic, 1>>
  {
    return {
      reinterpret_cast<typename ElementTraits<T>::pointer>(in.data()),
      static_cast<int64_t>(in.size())
    };
  }

  template <typename T, int N>
  auto reshape_2d(TensorView_<T, N>& in, const Vector2i& shape)
      -> TensorView_<T, 2>
  {
    return {in.data(), shape};
  }

  template <typename T, int N>
  auto im2col(const TensorView_<T, N>& x, const Matrix<int, N, 1>& kernel_sizes)
      -> Tensor_<T, 2>
  {
    const auto num_rows = std::accumulate(
        x.sizes().data(),
        x.sizes().data() + x.sizes().size(), 1,
        std::multiplies<int>());
    const auto num_cols = std::accumulate(
        kernel_sizes.data(),
        kernel_sizes.data() + kernel_sizes.size(), 1,
        std::multiplies<int>());

    auto phi_x = Tensor_<T, 2>{num_rows, num_cols};

    const Matrix<T, N, 1> radius = kernel_sizes / 2;
    for (auto c = x.begin_array(); !c.end(); ++c)
    {
      auto s = c.coords() - radius;
      auto e = c.coords() + radius;

      phi_x.matrix().row(c) = vec(patch(x, s, e)).transpose();
    }

    return phi_x;
  }

  template <typename T, int N>
  void gemm_convolve(TensorView_<T, N>& kx,  //
                     const TensorView_<T, N>& x, const TensorView_<T, N>& k)
  {
    auto phi_x = im2col(x, k.sizes());
    kx.array() = reshape(phi_x.matrix() * k.vector(), x.sizes());
  }

  template <typename T>
  void gemm_batch_convolve(ImageView<T, 4>& fg,  //
                           const ImageView<T, 3>& f, const ImageView<T, 4>& g)
  {
    if (f.sizes().cwiseMax(g.sizes()) == f.sizes())
    {
      fg.matrix() =
          im2col(f, g.sizes()) *
          reshape_2d(g, {g.size(0), g.size(1) * g.size(2) * g.size(2)});
    }
    else
    {
      fg.matrix() =
          im2col(g, f.sizes()) *
          reshape_2d(f, {f.size(0), f.size(1) * f.size(2) * f.size(2)});
    }
  }

} /* namespace Sara */
} /* namespace DO */
<commit_msg>STY: reformat code.<commit_after>#pragma once

#include <DO/Sara/Core/EigenExtension.hpp>
#include <DO/Sara/Core/Image/Operations.hpp>
#include <DO/Sara/Core/Tensor.hpp>

namespace DO { namespace Sara {

  // In this world everything, everything is **ROW-MAJOR**.
  template <typename T, int N>
  using TensorView_ = TensorView<T, N, RowMajor>;

  template <typename T, int N>
  using Tensor_ = Tensor<T, N, RowMajor>;

  template <typename T, int N>
  auto image_view(TensorView_<T, N> in) -> ImageView<T, N>
  {
    auto out_sizes = in.sizes();
    std::reverse(out_sizes.data(), out_sizes.data() + N);
    return ImageView<T, N>{in.data(), out_sizes};
  }

  template <typename T, int N>
  auto patch(const TensorView_<T, N>& in, const Matrix<int, N, 1>& beg,
             const Matrix<int, N, 1>& end) -> Image<T, N>
  {
    return safe_crop(image_view(in), beg, end);
  }

  template <typename T, int N>
  auto vec(const TensorView_<T, N>& in)
      -> Map<const Matrix<typename ElementTraits<T>::value_type, Dynamic, 1>>
  {
    return {reinterpret_cast<typename ElementTraits<T>::pointer>(in.data()),
            static_cast<int64_t>(in.size())};
  }

  template <typename T, int N>
  auto vec(TensorView_<T, N>& in)
      -> Map<const Matrix<typename ElementTraits<T>::value_type, Dynamic, 1>>
  {
    return {reinterpret_cast<typename ElementTraits<T>::pointer>(in.data()),
            static_cast<int64_t>(in.size())};
  }

  template <typename T, int N>
  auto reshape_2d(TensorView_<T, N>& in, const Vector2i& shape)
      -> TensorView_<T, 2>
  {
    return {in.data(), shape};
  }

  template <typename T, int N>
  auto im2col(const TensorView_<T, N>& x, const Matrix<int, N, 1>& kernel_sizes)
      -> Tensor_<T, 2>
  {
    const auto num_rows =
        std::accumulate(x.sizes().data(), x.sizes().data() + x.sizes().size(),
                        1, std::multiplies<int>());
    const auto num_cols = std::accumulate(
        kernel_sizes.data(), kernel_sizes.data() + kernel_sizes.size(), 1,
        std::multiplies<int>());

    auto phi_x = Tensor_<T, 2>{num_rows, num_cols};

    const Matrix<T, N, 1> radius = kernel_sizes / 2;
    for (auto c = x.begin_array(); !c.end(); ++c)
    {
      auto s = c.coords() - radius;
      auto e = c.coords() + radius;

      phi_x.matrix().row(c) = vec(patch(x, s, e)).transpose();
    }

    return phi_x;
  }

  template <typename T, int N>
  void gemm_convolve(TensorView_<T, N>& kx,  //
                     const TensorView_<T, N>& x, const TensorView_<T, N>& k)
  {
    auto phi_x = im2col(x, k.sizes());
    kx.array() = reshape(phi_x.matrix() * k.vector(), x.sizes());
  }

  template <typename T>
  void gemm_batch_convolve(ImageView<T, 4>& fg,  //
                           const ImageView<T, 3>& f, const ImageView<T, 4>& g)
  {
    if (f.sizes().cwiseMax(g.sizes()) == f.sizes())
    {
      fg.matrix() =
          im2col(f, g.sizes()) *
          reshape_2d(g, {g.size(0), g.size(1) * g.size(2) * g.size(2)});
    }
    else
    {
      fg.matrix() =
          im2col(g, f.sizes()) *
          reshape_2d(f, {f.size(0), f.size(1) * f.size(2) * f.size(2)});
    }
  }

} /* namespace Sara */
} /* namespace DO */
<|endoftext|>


<commit_before>#include <iostream>
#include <cstdlib>
#include <cstdio>
#include "util.h"

using namespace std;

bool show_addresses;

void status(string message) {
  cout << "[+] " << message << "\n";
}

void header() {
  cout << "SIC-Disassembler v1.1 by Jay Bosamiya\n"
          "-------------------------------------\n\n";
}

void usage(string progname) {
  cerr << "Usage: " << progname << " input_object_file output_assembly_file [-a]\n";
  cerr << "  Options:\n";
  cerr << "    -a   Show addresses in assembly output\n";
  exit(-2);
}

void error(string err) {
  cerr << "[!] Error: " << err << "\n";
}

void fatal(string err, int code) {
  error(err);
  exit(code);
}

bool is_hex_digit(char c) {
	return
		(c >= 'A' && c <= 'F') ||
		(c >= '0' && c <= '9');
}

bool is_hex_string(std::string s) {
	bool ret = true;
	for ( const char *c = s.c_str(); *c && ret ; c++ ) {
		ret = ret && is_hex_digit(*c);
	}
	return ret;
}

void make_upper_case(std::string &s) {
	for ( string::iterator c = s.begin() ; c != s.end() ; c++ ) {
		if ( *c >= 'a' && *c <= 'z' ) {
			*c = (*c - 'a') + 'A';
		}
	}
}

int hexchar2int(char c) {
	int ret = -1;
	if ( c >= '0' && c <= '9' ) {
		ret = (c-'0');
	} else if ( c >= 'A' && c <= 'F' ) {
		ret = (c-'A')+10;
	}
	return ret;
}

int hex2int(string s) {
	int ret = 0;
	for ( const char * cc = s.c_str() ; *cc ; cc++ ) {
		ret *= 16;
		const char &c = *cc;
		if ( is_hex_digit(c) ) {
			ret += hexchar2int(c);
		} else {
			fatal("Hex values need to be 0-9 or A-F");
		}
	}
	return ret;
}

string byte2hex(int c) {
	string ret = "";
	if ( c < 0 || c >= 256 ) {
		fatal("Illegal value for byte");
	}
	ret += (c/16 < 10)?('0'+c/16):('A'+c/16-10);
	ret += (c%16 < 10)?('0'+c%16):('A'+c%16-10);
	return ret;
}

string int2hex(int c, int bytes) {
	if ( c < 0 ) {
		return int2hex(c&0x7FFFFFFF,bytes);
	}
	if ( bytes <= 0 ) {
		return "";
	}
	return int2hex(c/256,bytes-1)+byte2hex(c%256);
}

std::string int2str(int c) {
	char ret[20];
	sprintf(ret,"%d",c);
	return ret;
}
<commit_msg>Update version number<commit_after>#include <iostream>
#include <cstdlib>
#include <cstdio>
#include "util.h"

using namespace std;

bool show_addresses;

void status(string message) {
  cout << "[+] " << message << "\n";
}

void header() {
  cout << "SIC-Disassembler v1.2 by Jay Bosamiya\n"
          "-------------------------------------\n\n";
}

void usage(string progname) {
  cerr << "Usage: " << progname << " input_object_file output_assembly_file [-a]\n";
  cerr << "  Options:\n";
  cerr << "    -a   Show addresses in assembly output\n";
  exit(-2);
}

void error(string err) {
  cerr << "[!] Error: " << err << "\n";
}

void fatal(string err, int code) {
  error(err);
  exit(code);
}

bool is_hex_digit(char c) {
	return
		(c >= 'A' && c <= 'F') ||
		(c >= '0' && c <= '9');
}

bool is_hex_string(std::string s) {
	bool ret = true;
	for ( const char *c = s.c_str(); *c && ret ; c++ ) {
		ret = ret && is_hex_digit(*c);
	}
	return ret;
}

void make_upper_case(std::string &s) {
	for ( string::iterator c = s.begin() ; c != s.end() ; c++ ) {
		if ( *c >= 'a' && *c <= 'z' ) {
			*c = (*c - 'a') + 'A';
		}
	}
}

int hexchar2int(char c) {
	int ret = -1;
	if ( c >= '0' && c <= '9' ) {
		ret = (c-'0');
	} else if ( c >= 'A' && c <= 'F' ) {
		ret = (c-'A')+10;
	}
	return ret;
}

int hex2int(string s) {
	int ret = 0;
	for ( const char * cc = s.c_str() ; *cc ; cc++ ) {
		ret *= 16;
		const char &c = *cc;
		if ( is_hex_digit(c) ) {
			ret += hexchar2int(c);
		} else {
			fatal("Hex values need to be 0-9 or A-F");
		}
	}
	return ret;
}

string byte2hex(int c) {
	string ret = "";
	if ( c < 0 || c >= 256 ) {
		fatal("Illegal value for byte");
	}
	ret += (c/16 < 10)?('0'+c/16):('A'+c/16-10);
	ret += (c%16 < 10)?('0'+c%16):('A'+c%16-10);
	return ret;
}

string int2hex(int c, int bytes) {
	if ( c < 0 ) {
		return int2hex(c&0x7FFFFFFF,bytes);
	}
	if ( bytes <= 0 ) {
		return "";
	}
	return int2hex(c/256,bytes-1)+byte2hex(c%256);
}

std::string int2str(int c) {
	char ret[20];
	sprintf(ret,"%d",c);
	return ret;
}
<|endoftext|>


<commit_before>/*

  This program will add histograms (see note) and Trees from a list of root files and write them
  to a target root file. The target file is newly created and must not be
  identical to one of the source files.

  Syntax:

       hadd targetfile source1 source2 ...
    or
       hadd -f targetfile source1 source2 ...
         (targetfile is overwritten if it exists)

  When -the -f option is specified, one can also specify the compression
  level of the target file. By default the compression level is 1, but
  if "-f0" is specified, the target file will not be compressed.
  if "-f6" is specified, the compression level 6 will be used.

  For example assume 3 files f1, f2, f3 containing histograms hn and Trees Tn
    f1 with h1 h2 h3 T1
    f2 with h1 h4 T1 T2
    f3 with h5
   the result of
     hadd -f x.root f1.root f2.root f3.root
   will be a file x.root with h1 h2 h3 h4 h5 T1 T2
   where h1 will be the sum of the 2 histograms in f1 and f2
         T1 will be the merge of the Trees in f1 and f2

   The files may contain sub-directories.

  if the source files contains histograms and Trees, one can skip
  the Trees with
       hadd -T targetfile source1 source2 ...

  Wildcarding and indirect files are also supported
    hadd result.root  myfil*.root
   will merge all files in myfil*.root
    hadd result.root file1.root @list.txt file2. root myfil*.root
    will merge file1. root, file2. root, all files in myfil*.root
    and all files in the indirect text file list.txt ("@" as the first
    character of the file indicates an indirect file. An indirect file
    is a text file containing a list of other files, including other
    indirect files, one line per file).

  If the sources and and target compression levels are identical (default),
  the program uses the TChain::Merge function with option "fast", ie
  the merge will be done without  unzipping or unstreaming the baskets
  (i.e. direct copy of the raw byte on disk). The "fast" mode is typically
  5 times faster than the mode unzipping and unstreaming the baskets.

  NOTE: By default histograms are added. However if histograms have their bit kIsAverage
        set, the contents are averaged instead of being summed. See TH1::Add.

  Authors: Rene Brun, Dirk Geppert, Sven A. Schmidt, sven.schmidt@cern.ch
         : rewritten from scratch by Rene Brun (30 November 2005)
            to support files with nested directories.
           Toby Burnett implemented the possibility to use indirect files.
 */

#include "RConfig.h"
#include <string>
#include "TChain.h"
#include "TFile.h"
#include "THashList.h"
#include "TH1.h"
#include "TKey.h"
#include "TObjString.h"
#include "Riostream.h"
#include "TClass.h"
#include "TSystem.h"
#include <stdlib.h>

TList *FileList;
TFile *Target, *Source;
Bool_t noTrees;
Bool_t fastMethod;

int AddFile(TList* sourcelist, std::string entry, int newcomp) ;
void MergeRootfile( TDirectory *target, TList *sourcelist, Int_t isdir );

//___________________________________________________________________________
int main( int argc, char **argv )
{

   if ( argc < 3 || "-h" == string(argv[1]) || "--help" == string(argv[1]) ) {
      cout << "Usage: " << argv[0] << " [-f] [-T] targetfile source1 [source2 source3 ...]" << endl;
      cout << "This program will add histograms from a list of root files and write them" << endl;
      cout << "to a target root file. The target file is newly created and must not " << endl;
      cout << "exist, or if -f (\"force\") is given, must not be one of the source files." << endl;
      cout << "Supply at least two source files for this to make sense... ;-)" << endl;
      cout << "If the first argument is -T, Trees are not merged" <<endl;
      cout << "When -the -f option is specified, one can also specify the compression" <<endl;
      cout << "level of the target file. By default the compression level is 1, but" <<endl;
      cout << "if \"-f0\" is specified, the target file will not be compressed." <<endl;
      cout << "if \"-f6\" is specified, the compression level 6 will be used." <<endl;
      cout << "if Target and source files have different compression levels"<<endl;
      cout << " a slower method is used"<<endl;
      return 1;
   }
   FileList = new TList();

   Bool_t force = (!strcmp(argv[1],"-f") || !strcmp(argv[2],"-f"));
   noTrees = (!strcmp(argv[1],"-T") || !strcmp(argv[2],"-T"));
   Int_t newcomp = 1;
   char ft[4];
   for (int j=0;j<=9;j++) {
      sprintf(ft,"-f%d",j);
      if (!strcmp(argv[1],ft) || !strcmp(argv[2],ft)) {
         force = kTRUE;
         newcomp = j;
         break;
      }
   }

   gSystem->Load("libTreePlayer");

   int ffirst = 2;
   if (force) ffirst++;
   if (noTrees) ffirst++;

   cout << "Target file: " << argv[ffirst-1] << endl;

   Target = TFile::Open( argv[ffirst-1], (force?"RECREATE":"CREATE") );
   if (!Target || Target->IsZombie()) {
      cerr << "Error opening target file (does " << argv[ffirst-1] << " exist?)." << endl;
      cerr << "Pass \"-f\" argument to force re-creation of output file." << endl;
      exit(1);
   }
   Target->SetCompressionLevel(newcomp);

   // by default hadd can merge Trees in a file that can go up to 100 Gbytes
   Long64_t maxsize = 100000000; //100GB
   maxsize *= 100;  //to bypass some compiler limitations with big constants
   TTree::SetMaxTreeSize(maxsize);

   fastMethod = kTRUE;
   for ( int i = ffirst; i < argc; i++ ) {
      if( AddFile(FileList, argv[i], newcomp) !=0 ) return 1;
   }
   if (!fastMethod) {
      cout <<"Sources and Target have different compression levels"<<endl;
      cout <<"Merging will be slower"<<endl;
   }

   MergeRootfile( Target, FileList,0 );

   //must delete Target to avoid a problem with dictionaries in~ TROOT
   delete Target;

   return 0;
}

//___________________________________________________________________________
int AddFile(TList* sourcelist, std::string entry, int newcomp)
{
   // add a new file to the list of files
   static int count(0);
   if( entry.empty() ) return 0;
   size_t j =entry.find_first_not_of(' ');
   if( j==std::string::npos ) return 0;
   entry = entry.substr(j);
   if( entry.substr(0,1)=="@"){
      std::ifstream indirect_file(entry.substr(1).c_str() );
      if( ! indirect_file.is_open() ) {
         std::cerr<< "Could not open indirect file " << entry.substr(1) << std::endl;
         return 1;
      }
      while( indirect_file ){
         std::string line;
         std::getline(indirect_file, line);
         if( AddFile(sourcelist, line, newcomp)!=0 )return 1;;
      }
      return 0;
   }
   cout << "Source file " << (++count) << ": " << entry << endl;

   TFile* source = TFile::Open( entry.c_str());
   if( source==0 ){
      return 1;
   }
   sourcelist->Add(source);
   if (newcomp != source->GetCompressionLevel())  fastMethod = kFALSE;
   return 0;
}

//___________________________________________________________________________
void MergeRootfile( TDirectory *target, TList *sourcelist, Int_t isdir )
{
   // Merge all objects in a directory
   cout << "Target path: " << target->GetPath() << endl;
   TString path( (char*)strstr( target->GetPath(), ":" ) );
   path.Remove( 0, 2 );

   TDirectory *first_source = (TDirectory*)sourcelist->First();
   Int_t nguess = sourcelist->GetSize()+1000;
   THashList allNames(nguess);
   ((THashList*)target->GetList())->Rehash(nguess);
   ((THashList*)target->GetListOfKeys())->Rehash(nguess);
   TList listH;
   while(first_source) {
      TDirectory *current_sourcedir = first_source->GetDirectory(path);
      if (!current_sourcedir) {
         first_source = (TDirectory*)sourcelist->After(first_source);
         continue;
      }

      // loop over all keys in this directory
      TChain *globChain = 0;
      TIter nextkey( current_sourcedir->GetListOfKeys() );
      TKey *key, *oldkey=0;
      //gain time, do not add the objects in the list in memory
      TH1::AddDirectory(kFALSE);

      while ( (key = (TKey*)nextkey())) {
         if (current_sourcedir == target) break;
         //keep only the highest cycle number for each key
         if (oldkey && !strcmp(oldkey->GetName(),key->GetName())) continue;
         if (!strcmp(key->GetClassName(),"TProcessID")) {key->ReadObj(); continue;}
         if (allNames.FindObject(key->GetName())) continue;
         allNames.Add(new TObjString(key->GetName()));
         // read object from first source file
         //current_sourcedir->cd();
         TObject *obj = key->ReadObj();

         if ( obj->IsA()->InheritsFrom( TH1::Class() ) ) {
            // descendant of TH1 -> merge it

            TH1 *h1 = (TH1*)obj;

            // loop over all source files and add the content of the
            // correspondant histogram to the one pointed to by "h1"
            TFile *nextsource = (TFile*)sourcelist->After( first_source );
            while ( nextsource ) {
               // make sure we are at the correct directory level by cd'ing to path
               TDirectory *ndir = nextsource->GetDirectory(path);
               if (ndir) {
                  ndir->cd();
                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
                  if (key2) {
                     TObject *hobj = key2->ReadObj();
                     hobj->ResetBit(kMustCleanup);
                     listH.Add(hobj);
                     h1->Merge(&listH);
                     listH.Delete();
                  }
               }
               nextsource = (TFile*)sourcelist->After( nextsource );
            }
         } else if ( obj->IsA()->InheritsFrom( "TTree" ) ) {

            // loop over all source files create a chain of Trees "globChain"
            if (!noTrees) {
               TString obj_name;
               if (path.Length()) {
                  obj_name = path + "/" + obj->GetName();
               } else {
                  obj_name = obj->GetName();
               }
               globChain = new TChain(obj_name);
               globChain->Add(first_source->GetName());
               TFile *nextsource = (TFile*)sourcelist->After( first_source );
               while ( nextsource ) {
                  //do not add to the list a file that does not contain this Tree
                  TFile *curf = TFile::Open(nextsource->GetName());
                  if (curf) {
                     Bool_t mustAdd = kFALSE;
                     if (curf->FindKey(obj_name)) {
                        mustAdd = kTRUE;
                     } else {
                        //we could be more clever here. No need to import the object
                        //we are missing a function in TDirectory
                        TObject *aobj = curf->Get(obj_name);
                        if (aobj) { mustAdd = kTRUE; delete aobj;}
                     }
                     if (mustAdd) {
                        globChain->Add(nextsource->GetName());
                     }
                  }
                  delete curf;
                  nextsource = (TFile*)sourcelist->After( nextsource );
               }
            }
         } else if ( obj->IsA()->InheritsFrom( "TDirectory" ) ) {
            // it's a subdirectory

            cout << "Found subdirectory " << obj->GetName() << endl;
            // create a new subdir of same name and title in the target file
            target->cd();
            TDirectory *newdir = target->mkdir( obj->GetName(), obj->GetTitle() );

            // newdir is now the starting point of another round of merging
            // newdir still knows its depth within the target file via
            // GetPath(), so we can still figure out where we are in the recursion
            MergeRootfile( newdir, sourcelist,1);

         } else {
            // object is of no type that we can merge
            cout << "Cannot merge object type, name: "
                 << obj->GetName() << " title: " << obj->GetTitle() << endl;

            // loop over all source files and write similar objects directly to the output file
            TFile *nextsource = (TFile*)sourcelist->After( first_source );
            while ( nextsource ) {
               // make sure we are at the correct directory level by cd'ing to path
               TDirectory *ndir = nextsource->GetDirectory(path);
               if (ndir) {
                  ndir->cd();
                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
                  if (key2) {
                     TObject *nobj = key2->ReadObj();
                     nobj->ResetBit(kMustCleanup);
                     target->WriteTObject(nobj, key2->GetName(), "SingleKey" );
                     delete nobj;
                  }
               }
               nextsource = (TFile*)sourcelist->After( nextsource );
            }
         }

         // now write the merged histogram (which is "in" obj) to the target file
         // note that this will just store obj in the current directory level,
         // which is not persistent until the complete directory itself is stored
         // by "target->Write()" below
         if ( obj ) {
            target->cd();

            //!!if the object is a tree, it is stored in globChain...
            if(obj->IsA()->InheritsFrom( "TDirectory" )) {
               //printf("cas d'une directory\n");
            } else if(obj->IsA()->InheritsFrom( "TTree" )) {
               if (!noTrees) {
                  globChain->ls();
                  if (fastMethod) globChain->Merge(target->GetFile(),0,"keep fast");
                  else            globChain->Merge(target->GetFile(),0,"keep");
                  delete globChain;
               }
            } else {
               obj->Write( key->GetName(), TObject::kSingleKey );
            }
         }
         oldkey = key;
      } // while ( ( TKey *key = (TKey*)nextkey() ) )
      first_source = (TDirectory*)sourcelist->After(first_source);
   }
   // save modifications to target file
   target->SaveSelf(kTRUE);
   if (!isdir) sourcelist->Remove(sourcelist->First());
}
<commit_msg>From Axel: checks for "Merge(TCollection*)" for a key's object, and if it exists invokes that. It replaced the TH1/2/3 case and makes hadd also work for THnSparse (and TList and what not).<commit_after>/*

  This program will add histograms (see note) and Trees from a list of root files and write them
  to a target root file. The target file is newly created and must not be
  identical to one of the source files.

  Syntax:

       hadd targetfile source1 source2 ...
    or
       hadd -f targetfile source1 source2 ...
         (targetfile is overwritten if it exists)

  When -the -f option is specified, one can also specify the compression
  level of the target file. By default the compression level is 1, but
  if "-f0" is specified, the target file will not be compressed.
  if "-f6" is specified, the compression level 6 will be used.

  For example assume 3 files f1, f2, f3 containing histograms hn and Trees Tn
    f1 with h1 h2 h3 T1
    f2 with h1 h4 T1 T2
    f3 with h5
   the result of
     hadd -f x.root f1.root f2.root f3.root
   will be a file x.root with h1 h2 h3 h4 h5 T1 T2
   where h1 will be the sum of the 2 histograms in f1 and f2
         T1 will be the merge of the Trees in f1 and f2

   The files may contain sub-directories.

  if the source files contains histograms and Trees, one can skip
  the Trees with
       hadd -T targetfile source1 source2 ...

  Wildcarding and indirect files are also supported
    hadd result.root  myfil*.root
   will merge all files in myfil*.root
    hadd result.root file1.root @list.txt file2. root myfil*.root
    will merge file1. root, file2. root, all files in myfil*.root
    and all files in the indirect text file list.txt ("@" as the first
    character of the file indicates an indirect file. An indirect file
    is a text file containing a list of other files, including other
    indirect files, one line per file).

  If the sources and and target compression levels are identical (default),
  the program uses the TChain::Merge function with option "fast", ie
  the merge will be done without  unzipping or unstreaming the baskets
  (i.e. direct copy of the raw byte on disk). The "fast" mode is typically
  5 times faster than the mode unzipping and unstreaming the baskets.

  NOTE: By default histograms are added. However if histograms have their bit kIsAverage
        set, the contents are averaged instead of being summed. See TH1::Add.

  Authors: Rene Brun, Dirk Geppert, Sven A. Schmidt, sven.schmidt@cern.ch
         : rewritten from scratch by Rene Brun (30 November 2005)
            to support files with nested directories.
           Toby Burnett implemented the possibility to use indirect files.
 */

#include "RConfig.h"
#include <string>
#include "TChain.h"
#include "TFile.h"
#include "THashList.h"
#include "TH1.h"
#include "TKey.h"
#include "TObjString.h"
#include "Riostream.h"
#include "TClass.h"
#include "TSystem.h"
#include <stdlib.h>

TList *FileList;
TFile *Target, *Source;
Bool_t noTrees;
Bool_t fastMethod;

int AddFile(TList* sourcelist, std::string entry, int newcomp) ;
void MergeRootfile( TDirectory *target, TList *sourcelist, Int_t isdir );

//___________________________________________________________________________
int main( int argc, char **argv )
{

   if ( argc < 3 || "-h" == string(argv[1]) || "--help" == string(argv[1]) ) {
      cout << "Usage: " << argv[0] << " [-f] [-T] targetfile source1 [source2 source3 ...]" << endl;
      cout << "This program will add histograms from a list of root files and write them" << endl;
      cout << "to a target root file. The target file is newly created and must not " << endl;
      cout << "exist, or if -f (\"force\") is given, must not be one of the source files." << endl;
      cout << "Supply at least two source files for this to make sense... ;-)" << endl;
      cout << "If the first argument is -T, Trees are not merged" <<endl;
      cout << "When -the -f option is specified, one can also specify the compression" <<endl;
      cout << "level of the target file. By default the compression level is 1, but" <<endl;
      cout << "if \"-f0\" is specified, the target file will not be compressed." <<endl;
      cout << "if \"-f6\" is specified, the compression level 6 will be used." <<endl;
      cout << "if Target and source files have different compression levels"<<endl;
      cout << " a slower method is used"<<endl;
      return 1;
   }
   FileList = new TList();

   Bool_t force = (!strcmp(argv[1],"-f") || !strcmp(argv[2],"-f"));
   noTrees = (!strcmp(argv[1],"-T") || !strcmp(argv[2],"-T"));
   Int_t newcomp = 1;
   char ft[4];
   for (int j=0;j<=9;j++) {
      sprintf(ft,"-f%d",j);
      if (!strcmp(argv[1],ft) || !strcmp(argv[2],ft)) {
         force = kTRUE;
         newcomp = j;
         break;
      }
   }

   gSystem->Load("libTreePlayer");

   int ffirst = 2;
   if (force) ffirst++;
   if (noTrees) ffirst++;

   cout << "Target file: " << argv[ffirst-1] << endl;

   Target = TFile::Open( argv[ffirst-1], (force?"RECREATE":"CREATE") );
   if (!Target || Target->IsZombie()) {
      cerr << "Error opening target file (does " << argv[ffirst-1] << " exist?)." << endl;
      cerr << "Pass \"-f\" argument to force re-creation of output file." << endl;
      exit(1);
   }
   Target->SetCompressionLevel(newcomp);

   // by default hadd can merge Trees in a file that can go up to 100 Gbytes
   Long64_t maxsize = 100000000; //100GB
   maxsize *= 100;  //to bypass some compiler limitations with big constants
   TTree::SetMaxTreeSize(maxsize);

   fastMethod = kTRUE;
   for ( int i = ffirst; i < argc; i++ ) {
      if( AddFile(FileList, argv[i], newcomp) !=0 ) return 1;
   }
   if (!fastMethod) {
      cout <<"Sources and Target have different compression levels"<<endl;
      cout <<"Merging will be slower"<<endl;
   }

   MergeRootfile( Target, FileList,0 );

   //must delete Target to avoid a problem with dictionaries in~ TROOT
   delete Target;

   return 0;
}

//___________________________________________________________________________
int AddFile(TList* sourcelist, std::string entry, int newcomp)
{
   // add a new file to the list of files
   static int count(0);
   if( entry.empty() ) return 0;
   size_t j =entry.find_first_not_of(' ');
   if( j==std::string::npos ) return 0;
   entry = entry.substr(j);
   if( entry.substr(0,1)=="@"){
      std::ifstream indirect_file(entry.substr(1).c_str() );
      if( ! indirect_file.is_open() ) {
         std::cerr<< "Could not open indirect file " << entry.substr(1) << std::endl;
         return 1;
      }
      while( indirect_file ){
         std::string line;
         std::getline(indirect_file, line);
         if( AddFile(sourcelist, line, newcomp)!=0 )return 1;;
      }
      return 0;
   }
   cout << "Source file " << (++count) << ": " << entry << endl;

   TFile* source = TFile::Open( entry.c_str());
   if( source==0 ){
      return 1;
   }
   sourcelist->Add(source);
   if (newcomp != source->GetCompressionLevel())  fastMethod = kFALSE;
   return 0;
}

//___________________________________________________________________________
void MergeRootfile( TDirectory *target, TList *sourcelist, Int_t isdir )
{
   // Merge all objects in a directory
   cout << "Target path: " << target->GetPath() << endl;
   TString path( (char*)strstr( target->GetPath(), ":" ) );
   path.Remove( 0, 2 );

   TDirectory *first_source = (TDirectory*)sourcelist->First();
   Int_t nguess = sourcelist->GetSize()+1000;
   THashList allNames(nguess);
   ((THashList*)target->GetList())->Rehash(nguess);
   ((THashList*)target->GetListOfKeys())->Rehash(nguess);
   TList listH;
   TString listHargs;
   listHargs.Form("((TCollection*)%p)",&listH);
   while(first_source) {
      TDirectory *current_sourcedir = first_source->GetDirectory(path);
      if (!current_sourcedir) {
         first_source = (TDirectory*)sourcelist->After(first_source);
         continue;
      }

      // loop over all keys in this directory
      TChain *globChain = 0;
      TIter nextkey( current_sourcedir->GetListOfKeys() );
      TKey *key, *oldkey=0;
      //gain time, do not add the objects in the list in memory
      TH1::AddDirectory(kFALSE);

      while ( (key = (TKey*)nextkey())) {
         if (current_sourcedir == target) break;
         //keep only the highest cycle number for each key
         if (oldkey && !strcmp(oldkey->GetName(),key->GetName())) continue;
         if (!strcmp(key->GetClassName(),"TProcessID")) {key->ReadObj(); continue;}
         if (allNames.FindObject(key->GetName())) continue;
         allNames.Add(new TObjString(key->GetName()));
         // read object from first source file
         //current_sourcedir->cd();
         TObject *obj = key->ReadObj();

         if ( obj->IsA()->InheritsFrom( "TTree" ) ) {

            // loop over all source files create a chain of Trees "globChain"
            if (!noTrees) {
               TString obj_name;
               if (path.Length()) {
                  obj_name = path + "/" + obj->GetName();
               } else {
                  obj_name = obj->GetName();
               }
               globChain = new TChain(obj_name);
               globChain->Add(first_source->GetName());
               TFile *nextsource = (TFile*)sourcelist->After( first_source );
               while ( nextsource ) {
                  //do not add to the list a file that does not contain this Tree
                  TFile *curf = TFile::Open(nextsource->GetName());
                  if (curf) {
                     Bool_t mustAdd = kFALSE;
                     if (curf->FindKey(obj_name)) {
                        mustAdd = kTRUE;
                     } else {
                        //we could be more clever here. No need to import the object
                        //we are missing a function in TDirectory
                        TObject *aobj = curf->Get(obj_name);
                        if (aobj) { mustAdd = kTRUE; delete aobj;}
                     }
                     if (mustAdd) {
                        globChain->Add(nextsource->GetName());
                     }
                  }
                  delete curf;
                  nextsource = (TFile*)sourcelist->After( nextsource );
               }
            }
         } else if ( obj->IsA()->InheritsFrom( "TDirectory" ) ) {
            // it's a subdirectory

            cout << "Found subdirectory " << obj->GetName() << endl;
            // create a new subdir of same name and title in the target file
            target->cd();
            TDirectory *newdir = target->mkdir( obj->GetName(), obj->GetTitle() );

            // newdir is now the starting point of another round of merging
            // newdir still knows its depth within the target file via
            // GetPath(), so we can still figure out where we are in the recursion
            MergeRootfile( newdir, sourcelist,1);

         } else if ( obj->InheritsFrom(TObject::Class())
              && obj->IsA()->GetMethodWithPrototype("Merge", "TCollection*") ) {
            // object implements Merge(TCollection*)

            // loop over all source files and merge same-name object
            TFile *nextsource = (TFile*)sourcelist->After( first_source );
            while ( nextsource ) {
               // make sure we are at the correct directory level by cd'ing to path
               TDirectory *ndir = nextsource->GetDirectory(path);
               if (ndir) {
                  ndir->cd();
                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
                  if (key2) {
                     TObject *hobj = key2->ReadObj();
                     hobj->ResetBit(kMustCleanup);
                     listH.Add(hobj);
                     Int_t error = 0;
                     obj->Execute("Merge", listHargs.Data(), &error);
                     if (error) {
                        cerr << "Error calling Merge() on " << obj->GetName()
                             << " with the corresponding object in " << nextsource->GetName() << endl;
                     }
                     listH.Delete();
                  }
               }
               nextsource = (TFile*)sourcelist->After( nextsource );
            }
         } else {
            // object is of no type that we can merge
            cout << "Cannot merge object type, name: "
                 << obj->GetName() << " title: " << obj->GetTitle() << endl;

            // loop over all source files and write similar objects directly to the output file
            TFile *nextsource = (TFile*)sourcelist->After( first_source );
            while ( nextsource ) {
               // make sure we are at the correct directory level by cd'ing to path
               TDirectory *ndir = nextsource->GetDirectory(path);
               if (ndir) {
                  ndir->cd();
                  TKey *key2 = (TKey*)gDirectory->GetListOfKeys()->FindObject(key->GetName());
                  if (key2) {
                     TObject *nobj = key2->ReadObj();
                     nobj->ResetBit(kMustCleanup);
                     target->WriteTObject(nobj, key2->GetName(), "SingleKey" );
                     delete nobj;
                  }
               }
               nextsource = (TFile*)sourcelist->After( nextsource );
            }
         }

         // now write the merged histogram (which is "in" obj) to the target file
         // note that this will just store obj in the current directory level,
         // which is not persistent until the complete directory itself is stored
         // by "target->Write()" below
         if ( obj ) {
            target->cd();

            //!!if the object is a tree, it is stored in globChain...
            if(obj->IsA()->InheritsFrom( "TDirectory" )) {
               //printf("cas d'une directory\n");
            } else if(obj->IsA()->InheritsFrom( "TTree" )) {
               if (!noTrees) {
                  globChain->ls();
                  if (fastMethod) globChain->Merge(target->GetFile(),0,"keep fast");
                  else            globChain->Merge(target->GetFile(),0,"keep");
                  delete globChain;
               }
            } else {
               obj->Write( key->GetName(), TObject::kSingleKey );
            }
         }
         oldkey = key;
      } // while ( ( TKey *key = (TKey*)nextkey() ) )
      first_source = (TDirectory*)sourcelist->After(first_source);
   }
   // save modifications to target file
   target->SaveSelf(kTRUE);
   if (!isdir) sourcelist->Remove(sourcelist->First());
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <string>

#include <osquery/sql.h>
#include <osquery/tables.h>

#include "osquery/core/conversions.h"

namespace osquery {
namespace tables {

#define OSX_VERSION_PATH "/System/Library/CoreServices/SystemVersion.plist"

QueryData genOSVersion(QueryContext& context) {
  // The version path plist is parsed by the OS X tool: sw_vers.
  auto sw_vers =
      SQL::selectAllFrom("preferences", "path", EQUALS, OSX_VERSION_PATH);
  if (sw_vers.size() == 0) {
    return {};
  }

  std::string version_string;
  Row r;
  for (const auto& row : sw_vers) {
    // Iterate over each plist key searching for the version string.
    if (row.at("key") == "ProductBuildVersion") {
      r["build"] = row.at("value");
    } else if (row.at("key") == "ProductVersion") {
      version_string = row.at("value");
    } else if (row.at("key") == "ProductName") {
      r["name"] = row.at("value");
    }
  }

  r["patch"] = "0";
  auto version = osquery::split(version_string, ".");
  switch (version.size()) {
  case 3:
    r["patch"] = INTEGER(version[2]);
  case 2:
    r["minor"] = INTEGER(version[1]);
    r["major"] = INTEGER(version[0]);
    break;
  }
  return {r};
}
}
}
<commit_msg>Update os_version table on OS X (#2709)<commit_after>/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <string>

#include <osquery/sql.h>
#include <osquery/tables.h>

#include "osquery/core/conversions.h"

namespace osquery {
namespace tables {

const std::string kVersionPath{
    "/System/Library/CoreServices/SystemVersion.plist"};

QueryData genOSVersion(QueryContext& context) {
  Row r;
  r["platform"] = "darwin";
  r["platform_like"] = "darwin";

  // The version path plist is parsed by the OS X tool: sw_vers.
  auto sw_vers =
      SQL::selectAllFrom("preferences", "path", EQUALS, kVersionPath);
  if (sw_vers.empty()) {
    return {r};
  }

  for (const auto& row : sw_vers) {
    // Iterate over each plist key searching for the version string.
    if (row.at("key") == "ProductBuildVersion") {
      r["build"] = row.at("value");
    } else if (row.at("key") == "ProductVersion") {
      r["version"] = row.at("value");
    } else if (row.at("key") == "ProductName") {
      r["name"] = row.at("value");
    }
  }

  r["patch"] = "0";
  auto version = osquery::split(r["version"], ".");
  switch (version.size()) {
  case 3:
    r["patch"] = INTEGER(version[2]);
  case 2:
    r["minor"] = INTEGER(version[1]);
  case 1:
    r["major"] = INTEGER(version[0]);
    break;
  }
  return {r};
}
}
}
<|endoftext|>


<commit_before>/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <map>
#include <sstream>
#include <string>

#define _WIN32_DCOM
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <psapi.h>
#include <stdlib.h>

#include <boost/algorithm/string/trim.hpp>

#include <osquery/core.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/tables.h>

#include "osquery/core/conversions.h"
#include "osquery/core/utils.h"
#include "osquery/core/windows/wmi.h"

namespace osquery {
namespace tables {

std::set<long> getProcList(const QueryContext& context) {
  std::set<long> pidlist;
  if (context.constraints.count("pid") > 0 &&
      context.constraints.at("pid").exists(EQUALS)) {
    for (const auto& pid : context.constraints.at("pid").getAll<int>(EQUALS)) {
      if (pid > 0) {
        pidlist.insert(pid);
      }
    }
    return pidlist;
  } else {
    WmiRequest request("SELECT ProcessId FROM Win32_Process");
    if (request.getStatus().ok()) {
      long placeHolder;
      Status s;
      for (auto const& result : request.results()) {
        s = result.GetLong("ProcessId", placeHolder);
        if (!s.ok()) {
          continue;
        }
        pidlist.insert(placeHolder);
      }
    }
    return pidlist;
  }
}

void genProcess(long pid, QueryData& results_data) {
  // We store the current processes PID, as there are API calls which are more
  // efficient for populating process info for the current process.
  auto currentPid = GetCurrentProcessId();

  std::stringstream ss;
  ss << "SELECT * FROM Win32_Process WHERE ProcessId=" << pid;

  WmiRequest request(ss.str());
  if (request.getStatus().ok()) {
    std::vector<WmiResultItem>& results = request.results();
    if (results.size() == 1) {
      Row r;
      Status s;
      long pid;
      long lPlaceHolder;
      std::string sPlaceHolder;
      HANDLE hProcess;

      s = results[0].GetLong("ProcessId", pid);
      r["pid"] = s.ok() ? BIGINT(pid) : BIGINT(-1);
      if (pid == currentPid) {
        hProcess = GetCurrentProcess();
      } else {
        hProcess = OpenProcess(
            PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
      }

      results[0].GetString("Name", r["name"]);
      results[0].GetString("ExecutablePath", r["path"]);
      results[0].GetString("CommandLine", r["cmdline"]);
      results[0].GetString("ExecutionState", r["state"]);
      results[0].GetLong("ParentProcessId", lPlaceHolder);
      r["parent"] = BIGINT(lPlaceHolder);
      results[0].GetLong("Priority", lPlaceHolder);
      r["nice"] = INTEGER(lPlaceHolder);
      r["on_disk"] = osquery::pathExists(r["path"]).toString();

      std::vector<char> fileName(MAX_PATH);
      fileName.assign(MAX_PATH + 1, '\0');
      if (pid == currentPid) {
        GetModuleFileName(nullptr, fileName.data(), MAX_PATH);
      } else {
        GetModuleFileNameEx(hProcess, nullptr, fileName.data(), MAX_PATH);
      }
      r["cwd"] = SQL_TEXT(fileName.data());
      r["root"] = r["cwd"];

      r["pgroup"] = "-1";
      r["uid"] = "-1";
      r["euid"] = "-1";
      r["suid"] = "-1";
      r["gid"] = "-1";
      r["egid"] = "-1";
      r["sgid"] = "-1";
      r["start_time"] = "0";

      // We pre-populate the info, in the event the API calls fail.
      results[0].GetString("UserModeTime", sPlaceHolder);
      long long llHolder;
      osquery::safeStrtoll(sPlaceHolder, 10, llHolder);
      r["user_time"] = BIGINT(llHolder / 10000000);
      results[0].GetString("KernelModeTime", sPlaceHolder);
      osquery::safeStrtoll(sPlaceHolder, 10, llHolder);
      r["system_time"] = BIGINT(llHolder / 10000000);

      results[0].GetString("PrivatePageCount", sPlaceHolder);
      r["wired_size"] = BIGINT(sPlaceHolder);
      results[0].GetString("WorkingSetSize", sPlaceHolder);
      r["resident_size"] = sPlaceHolder;
      results[0].GetString("VirtualSize", sPlaceHolder);
      r["total_size"] = BIGINT(sPlaceHolder);
      results_data.push_back(r);
    }
  }
}

QueryData genProcesses(QueryContext& context) {
  QueryData results;

  auto pidlist = getProcList(context);
  for (const auto& pid : pidlist) {
    genProcess(pid, results);
  }

  return results;
}
}
}
<commit_msg>Efficiency improvements to processes table on Windows (#2587)<commit_after>/*
 *  Copyright (c) 2014-present, Facebook, Inc.
 *  All rights reserved.
 *
 *  This source code is licensed under the BSD-style license found in the
 *  LICENSE file in the root directory of this source tree. An additional grant
 *  of patent rights can be found in the PATENTS file in the same directory.
 *
 */

#include <map>
#include <string>

#define _WIN32_DCOM
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <psapi.h>
#include <stdlib.h>

#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/lexical_cast.hpp>

#include <osquery/core.h>
#include <osquery/filesystem.h>
#include <osquery/logger.h>
#include <osquery/tables.h>

#include "osquery/core/conversions.h"
#include "osquery/core/utils.h"
#include "osquery/core/windows/wmi.h"

namespace osquery {
namespace tables {

std::set<long> getSelectedPids(const QueryContext& context) {
  std::set<long> pidlist;
  if (context.constraints.count("pid") > 0 &&
      context.constraints.at("pid").exists(EQUALS)) {
    for (const auto& pid : context.constraints.at("pid").getAll<int>(EQUALS)) {
      if (pid > 0) {
        pidlist.insert(pid);
      }
    }
  }

  /// If there are no constraints, pidlist will be an empty set
  return pidlist;
}

void genProcess(const WmiResultItem& result, QueryData& results_data) {
  Row r;
  Status s;
  long pid;
  long lPlaceHolder;
  std::string sPlaceHolder;
  HANDLE hProcess = nullptr;

  // We store the current processes PID, as there are API calls which are more
  // efficient for populating process info for the current process.
  auto currentPid = GetCurrentProcessId();

  s = result.GetLong("ProcessId", pid);
  r["pid"] = s.ok() ? BIGINT(pid) : BIGINT(-1);
  if (pid == currentPid) {
    hProcess = GetCurrentProcess();
  } else {
    hProcess =
        OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
  }

  result.GetString("Name", r["name"]);
  result.GetString("ExecutablePath", r["path"]);
  result.GetString("CommandLine", r["cmdline"]);
  result.GetString("ExecutionState", r["state"]);
  result.GetLong("ParentProcessId", lPlaceHolder);
  r["parent"] = BIGINT(lPlaceHolder);
  result.GetLong("Priority", lPlaceHolder);
  r["nice"] = INTEGER(lPlaceHolder);
  r["on_disk"] = osquery::pathExists(r["path"]).toString();

  std::vector<char> fileName(MAX_PATH);
  fileName.assign(MAX_PATH + 1, '\0');
  if (pid == currentPid) {
    GetModuleFileName(nullptr, fileName.data(), MAX_PATH);
  } else {
    GetModuleFileNameEx(hProcess, nullptr, fileName.data(), MAX_PATH);
  }
  r["cwd"] = SQL_TEXT(fileName.data());
  r["root"] = r["cwd"];

  r["pgroup"] = "-1";
  r["uid"] = "-1";
  r["euid"] = "-1";
  r["suid"] = "-1";
  r["gid"] = "-1";
  r["egid"] = "-1";
  r["sgid"] = "-1";
  r["start_time"] = "0";

  // We pre-populate the info, in the event the API calls fail.
  result.GetString("UserModeTime", sPlaceHolder);
  long long llHolder;
  osquery::safeStrtoll(sPlaceHolder, 10, llHolder);
  r["user_time"] = BIGINT(llHolder / 10000000);
  result.GetString("KernelModeTime", sPlaceHolder);
  osquery::safeStrtoll(sPlaceHolder, 10, llHolder);
  r["system_time"] = BIGINT(llHolder / 10000000);

  result.GetString("PrivatePageCount", sPlaceHolder);
  r["wired_size"] = BIGINT(sPlaceHolder);
  result.GetString("WorkingSetSize", sPlaceHolder);
  r["resident_size"] = sPlaceHolder;
  result.GetString("VirtualSize", sPlaceHolder);
  r["total_size"] = BIGINT(sPlaceHolder);
  results_data.push_back(r);

  // OpenProcess returns nullptr on error, GetCurrentProcess returns a
  // psuedo-handle of -1, which is the same as INVALID_HANDLE_VALUE
  if (hProcess != nullptr && hProcess != INVALID_HANDLE_VALUE) {
    CloseHandle(hProcess);
  }
}

QueryData genProcesses(QueryContext& context) {
  QueryData results;

  std::string query = "SELECT * FROM Win32_Process";

  auto pidlist = getSelectedPids(context);
  if (pidlist.size() > 0) {
    std::vector<std::string> constraints;
    for (const auto& pid : pidlist) {
      constraints.push_back("ProcessId=" + std::to_string(pid));
    }
    if (constraints.size() > 0) {
      query += " WHERE " + boost::algorithm::join(constraints, " OR ");
    }
  }

  WmiRequest request(query);
  if (request.getStatus().ok()) {
    for (const auto& item : request.results()) {
      long pid = 0;
      if (item.GetLong("ProcessId", pid).ok()) {
        genProcess(item, results);
      }
    }
  }

  return results;
}
}
}
<|endoftext|>


<commit_before>// Copyright (C) 2016 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <GLES2/gl2.h>

#include "RendererOGLEm.h"
#include "core/Engine.h"
#include "utils/Utils.h"

namespace ouzel
{
    namespace graphics
    {
        RendererOGLEm::~RendererOGLEm()
        {
            emscripten_webgl_destroy_context(webGLContext);
        }

        void RendererOGLEm::free()
        {
            if (webGLContext)
            {
                emscripten_webgl_destroy_context(webGLContext);
                webGLContext = 0;
            }
        }

        bool RendererOGLEm::init(const WindowPtr& window,
                                 uint32_t newSampleCount,
                                 TextureFilter newTextureFilter,
                                 PixelFormat newBackBufferFormat,
                                 bool newVerticalSync)
        {
            free();

            apiMajorVersion = 2;
            apiMinorVersion = 0;

            EmscriptenWebGLContextAttributes attrs;
            emscripten_webgl_init_context_attributes(&attrs);

            webGLContext = emscripten_webgl_create_context(0, &attrs);

            if (!webGLContext)
            {
                log(LOG_LEVEL_ERROR, "Failed to create WebGL context");
                return false;
            }

            return RendererOGL::init(window, newSampleCount, newTextureFilter, newBackBufferFormat, newVerticalSync);
        }

        bool RendererOGLEm::present()
        {
            EMSCRIPTEN_RESULT res = emscripten_webgl_make_context_current(webGLContext);

            if (res != EMSCRIPTEN_RESULT_SUCCESS)
            {
                return false;
            }

            if (!RendererOGL::present())
            {
                return false;
            }

            return true;
        }
    } // namespace graphics
} // namespace ouzel
<commit_msg>Make WebGL context current after initialization<commit_after>// Copyright (C) 2016 Elviss Strazdins
// This file is part of the Ouzel engine.

#include <GLES2/gl2.h>

#include "RendererOGLEm.h"
#include "core/Engine.h"
#include "utils/Utils.h"

namespace ouzel
{
    namespace graphics
    {
        RendererOGLEm::~RendererOGLEm()
        {
            emscripten_webgl_destroy_context(webGLContext);
        }

        void RendererOGLEm::free()
        {
            if (webGLContext)
            {
                emscripten_webgl_destroy_context(webGLContext);
                webGLContext = 0;
            }
        }

        bool RendererOGLEm::init(const WindowPtr& window,
                                 uint32_t newSampleCount,
                                 TextureFilter newTextureFilter,
                                 PixelFormat newBackBufferFormat,
                                 bool newVerticalSync)
        {
            free();

            apiMajorVersion = 2;
            apiMinorVersion = 0;

            EmscriptenWebGLContextAttributes attrs;
            emscripten_webgl_init_context_attributes(&attrs);

            webGLContext = emscripten_webgl_create_context(0, &attrs);

            if (!webGLContext)
            {
                log(LOG_LEVEL_ERROR, "Failed to create WebGL context");
                return false;
            }

            EMSCRIPTEN_RESULT res = emscripten_webgl_make_context_current(webGLContext);

            if (res != EMSCRIPTEN_RESULT_SUCCESS)
            {
                return false;
            }

            return RendererOGL::init(window, newSampleCount, newTextureFilter, newBackBufferFormat, newVerticalSync);
        }

        bool RendererOGLEm::present()
        {
            EMSCRIPTEN_RESULT res = emscripten_webgl_make_context_current(webGLContext);

            if (res != EMSCRIPTEN_RESULT_SUCCESS)
            {
                return false;
            }

            if (!RendererOGL::present())
            {
                return false;
            }

            return true;
        }
    } // namespace graphics
} // namespace ouzel
<|endoftext|>