<commit_before>/**
 * @file simpleConnectionPool.cc
 * @author Rafal Slota
 * @copyright (C) 2013 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in 'LICENSE.txt'
 */

#include "simpleConnectionPool.h"
#include "communicationHandler.h"
#include "logging.h"
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iterator>
#include <algorithm>
#include <boost/thread/thread_time.hpp>
#include <exception>
#include "helpers/storageHelperFactory.h"

using namespace boost;
using namespace std;

namespace veil {

SimpleConnectionPool::SimpleConnectionPool(const string &hostname, int port,
                                           cert_info_fun certInfoFun,
                                           const bool checkCertificate,
                                           int metaPoolSize, int dataPoolSize)
    : m_hostname(hostname)
    , m_port(port)
    , m_getCertInfo(certInfoFun)
    , m_checkCertificate{checkCertificate}
{
    m_connectionPools.emplace(META_POOL, std::unique_ptr<ConnectionPoolInfo>(new ConnectionPoolInfo(certInfoFun, metaPoolSize, checkCertificate)));
    m_connectionPools.emplace(DATA_POOL, std::unique_ptr<ConnectionPoolInfo>(new ConnectionPoolInfo(certInfoFun, dataPoolSize, checkCertificate)));
}

void SimpleConnectionPool::resetAllConnections(PoolType type)
{
    m_connectionPools.at(type)->connections.clear();
    setPoolSize(type, m_connectionPools.at(type)->size); // Force connection reinitialization
}

boost::shared_ptr<CommunicationHandler> SimpleConnectionPool::newConnection(PoolType type)
{
    boost::unique_lock< boost::recursive_mutex > lock(m_access);

    ConnectionPoolInfo &poolInfo = *m_connectionPools.at(type);
    boost::shared_ptr<CommunicationHandler> conn;

    CounterRAII sc(poolInfo.currWorkers);

    lock.unlock();
    list<string> ips = dnsQuery(m_hostname);
    lock.lock();

    list<string>::iterator it = m_hostnamePool.begin();
    while(it != m_hostnamePool.end()) // Delete all hostname from m_hostnamePool which are not present in dnsQuery response
    {
        list<string>::const_iterator itIP = find(ips.begin(), ips.end(), (*it));
        if(itIP == ips.end())
            it = m_hostnamePool.erase(it);
        else ++it;
    }

    for(it = ips.begin(); it != ips.end(); ++it)
    {
        list<string>::const_iterator itIP = find(m_hostnamePool.begin(), m_hostnamePool.end(), (*it));
        if(itIP == m_hostnamePool.end())
            m_hostnamePool.push_back((*it));
    }

    // Connect to first working host
    int hostnameCount = m_hostnamePool.size();
    while(hostnameCount--)
    {
        string connectTo = m_hostnamePool.front();
        m_hostnamePool.pop_front();
        m_hostnamePool.push_back(connectTo);

        lock.unlock();

        conn.reset(new CommunicationHandler(connectTo, m_port, m_getCertInfo, m_checkCertificate, poolInfo.endpoint));
        conn->setFuseID(m_fuseId);  // Set FuseID that shall be used by this connection as session ID
        if(m_pushCallback)                          // Set callback that shall be used for PUSH messages and error messages
            conn->setPushCallback(m_pushCallback);  // Note that this doesnt enable/register PUSH channel !

        if(m_pushCallback && m_fuseId.size() > 0 && type == META_POOL) // Enable PUSH channel (will register itself when possible)
            conn->enablePushChannel();

        if(conn->openConnection() == 0) {
            break;
        }

        m_lastError.store(conn->getLastError());

        lock.lock();
        conn.reset();
        LOG(WARNING) << "Cannot connect to host: " << connectTo << ":" << m_port;

    }

    if(conn)
        poolInfo.connections.push_front(make_pair(conn, time(NULL)));
    else
        LOG(ERROR) << "Opening new connection (type: " << type << ") failed!";

    return conn;
}

boost::shared_ptr<CommunicationHandler> SimpleConnectionPool::selectConnection(PoolType type)
{
    boost::unique_lock< boost::recursive_mutex > lock(m_access);
    boost::shared_ptr<CommunicationHandler> conn;

    ConnectionPoolInfo &poolInfo = *m_connectionPools.at(type);

    // Delete first connection in pool if its error counter is way to big
    if(poolInfo.connections.size() > 0 && poolInfo.connections.front().first->getErrorCount() > MAX_CONNECTION_ERROR_COUNT) {
        LOG(INFO) << "Connection " << poolInfo.connections.front().first << " emited to many errors. Reinitializing object.";
        poolInfo.connections.pop_front();
    }

    // Remove redundant connections
    while(poolInfo.connections.size() > poolInfo.size)
        poolInfo.connections.pop_back();

    // Check if pool size matches config
    long toStart = poolInfo.size - poolInfo.connections.size() - poolInfo.currWorkers;
    if(poolInfo.connections.size() == 0 && toStart <= 0)
        toStart = 1;

    DLOG(INFO) << "Current pool size: " << poolInfo.connections.size() << ", connections in construction: " << poolInfo.currWorkers << ", expected: " << poolInfo.size;

    while(toStart --> 0) // Current pool is too small, we should create some connection(s)
    {
        LOG(INFO) << "Connection pool (" << type << " is to small (" << poolInfo.connections.size() << " connections - expected: " << poolInfo.size << "). Opening new connection...";

        if(poolInfo.connections.size() > 0) {
            boost::thread t = boost::thread(boost::bind(&SimpleConnectionPool::newConnection, shared_from_this(), type));
            t.detach();
        }
        else
            conn = newConnection(type);
    }

    if(poolInfo.connections.size() > 0)
    {
        conn = poolInfo.connections.front().first;

        // Round-robin
        poolInfo.connections.push_back(poolInfo.connections.front());
        poolInfo.connections.pop_front();
    }

    return conn;
}

void SimpleConnectionPool::releaseConnection(boost::shared_ptr<CommunicationHandler> conn)
{
    return;
}

error::Error SimpleConnectionPool::getLastError() const
{
    return m_lastError.load();
}

void SimpleConnectionPool::setPoolSize(PoolType type, unsigned int s)
{
    boost::unique_lock< boost::recursive_mutex > lock(m_access);
    m_connectionPools.at(type)->size = s;

    // Insert new connections to pool if needed (async)
    long toStart = m_connectionPools.at(type)->size - m_connectionPools.at(type)->connections.size();
    while(toStart-- > 0) {
        boost::thread t = boost::thread(boost::bind(&SimpleConnectionPool::newConnection, shared_from_this(), type));
        t.detach();
    }
}

void SimpleConnectionPool::setPushCallback(const string &fuseId, push_callback hdl)
{
    m_fuseId = fuseId;
    m_pushCallback = hdl;
}

list<string> SimpleConnectionPool::dnsQuery(const string &hostname)
{
    boost::unique_lock< boost::recursive_mutex > lock(m_access);

    list<string> lst;
    struct addrinfo *result;
    struct addrinfo *res;

    if(getaddrinfo(hostname.c_str(), NULL, NULL, &result) == 0) {
        for(res = result; res != NULL; res = res->ai_next) {
            char ip[INET_ADDRSTRLEN + 1] = "";
            switch(res->ai_addr->sa_family) {
                case AF_INET:
                    if(inet_ntop(res->ai_addr->sa_family, &((struct sockaddr_in*)res->ai_addr)->sin_addr, ip, INET_ADDRSTRLEN + 1) != NULL)
                        lst.push_back(string(ip));
                case AF_INET6:
                    // Not supported
                    break;
                default:
                    break;
            }
        }
        freeaddrinfo(result);
    }

    if(lst.size() == 0) {
        LOG(ERROR) << "DNS lookup failed for host: " << hostname;
        lst.push_back(hostname); // Make sure that returned list is not empty but adding argument to it
    }

    return lst;
}

SimpleConnectionPool::ConnectionPoolInfo::ConnectionPoolInfo(cert_info_fun getCertInfo, unsigned int s, const bool checkCertificate)
    : size(s)
    , m_getCertInfo{std::move(getCertInfo)}
    , m_checkCertificate(checkCertificate)
{
    LOG(INFO) << "Initializing a WebSocket endpoint";
    websocketpp::lib::error_code ec;
    endpoint->clear_access_channels(websocketpp::log::alevel::all);
    endpoint->clear_error_channels(websocketpp::log::elevel::all);
    endpoint->init_asio(ec);
    endpoint->start_perpetual();

    if(ec)
    {
        LOG(ERROR) << "Cannot initlize WebSocket endpoint; terminating.";
        std::terminate();
    }

    // Start worker thread
    m_ioWorker = boost::thread(&ws_client::run, endpoint);

    endpoint->set_tls_init_handler(bind(&ConnectionPoolInfo::onTLSInit, this, ::_1));
    endpoint->set_socket_init_handler(bind(&ConnectionPoolInfo::onSocketInit, this, ::_1, ::_2));
}

SimpleConnectionPool::ConnectionPoolInfo::~ConnectionPoolInfo()
{
    LOG(INFO) << "Stopping the WebSocket endpoint";
    endpoint->stop_perpetual();

    for(auto &con: connections)
        con.first->closeConnection();

    LOG(INFO) << "Stopping WebSocket endpoint worker thread";
    m_ioWorker.join();
}

context_ptr SimpleConnectionPool::ConnectionPoolInfo::onTLSInit(websocketpp::connection_hdl hdl)
{
    if (!m_getCertInfo) {
        LOG(ERROR) << "Cannot get CertificateInfo due to null getter";
        return context_ptr();
    }

    CertificateInfo certInfo = m_getCertInfo();

    try {
        context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::sslv3));

        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::single_dh_use);

        ctx->set_default_verify_paths();
        ctx->set_verify_mode(m_checkCertificate
                             ? boost::asio::ssl::verify_peer
                             : boost::asio::ssl::verify_none);

        boost::asio::ssl::context_base::file_format file_format; // Certificate format
        if(certInfo.cert_type == CertificateInfo::ASN1) {
            file_format = boost::asio::ssl::context::asn1;
        } else {
            file_format = boost::asio::ssl::context::pem;
        }

        if(certInfo.cert_type == CertificateInfo::P12) {
            LOG(ERROR) << "Unsupported certificate format: P12";
            return context_ptr();
        }

        if(boost::asio::buffer_size(certInfo.chain_data) && boost::asio::buffer_size(certInfo.chain_data)) {
            ctx->use_certificate_chain(certInfo.chain_data);
            ctx->use_private_key(certInfo.key_data, file_format);
        } else {
            ctx->use_certificate_chain_file(certInfo.user_cert_path);
            ctx->use_private_key_file(certInfo.user_key_path, file_format);
        }

        return ctx;

    } catch (boost::system::system_error& e) {
        LOG(ERROR) << "Cannot initialize TLS socket due to: " << e.what()
                   << " with cert file: " << certInfo.user_cert_path << " and key file: "
                   << certInfo.user_key_path;
    }

    return context_ptr();
}

void SimpleConnectionPool::ConnectionPoolInfo::onSocketInit(websocketpp::connection_hdl hdl, socket_type &socket)
{
    // Disable socket delay
    socket.lowest_layer().set_option(boost::asio::ip::tcp::no_delay(true));
}

} // namespace veil
<commit_msg>fix reorder warning<commit_after>/**
 * @file simpleConnectionPool.cc
 * @author Rafal Slota
 * @copyright (C) 2013 ACK CYFRONET AGH
 * @copyright This software is released under the MIT license cited in 'LICENSE.txt'
 */

#include "simpleConnectionPool.h"
#include "communicationHandler.h"
#include "logging.h"
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <iterator>
#include <algorithm>
#include <boost/thread/thread_time.hpp>
#include <exception>
#include "helpers/storageHelperFactory.h"

using namespace boost;
using namespace std;

namespace veil {

SimpleConnectionPool::SimpleConnectionPool(const string &hostname, int port,
                                           cert_info_fun certInfoFun,
                                           const bool checkCertificate,
                                           int metaPoolSize, int dataPoolSize)
    : m_hostname(hostname)
    , m_port(port)
    , m_getCertInfo(certInfoFun)
    , m_checkCertificate{checkCertificate}
{
    m_connectionPools.emplace(META_POOL, std::unique_ptr<ConnectionPoolInfo>(new ConnectionPoolInfo(certInfoFun, metaPoolSize, checkCertificate)));
    m_connectionPools.emplace(DATA_POOL, std::unique_ptr<ConnectionPoolInfo>(new ConnectionPoolInfo(certInfoFun, dataPoolSize, checkCertificate)));
}

void SimpleConnectionPool::resetAllConnections(PoolType type)
{
    m_connectionPools.at(type)->connections.clear();
    setPoolSize(type, m_connectionPools.at(type)->size); // Force connection reinitialization
}

boost::shared_ptr<CommunicationHandler> SimpleConnectionPool::newConnection(PoolType type)
{
    boost::unique_lock< boost::recursive_mutex > lock(m_access);

    ConnectionPoolInfo &poolInfo = *m_connectionPools.at(type);
    boost::shared_ptr<CommunicationHandler> conn;

    CounterRAII sc(poolInfo.currWorkers);

    lock.unlock();
    list<string> ips = dnsQuery(m_hostname);
    lock.lock();

    list<string>::iterator it = m_hostnamePool.begin();
    while(it != m_hostnamePool.end()) // Delete all hostname from m_hostnamePool which are not present in dnsQuery response
    {
        list<string>::const_iterator itIP = find(ips.begin(), ips.end(), (*it));
        if(itIP == ips.end())
            it = m_hostnamePool.erase(it);
        else ++it;
    }

    for(it = ips.begin(); it != ips.end(); ++it)
    {
        list<string>::const_iterator itIP = find(m_hostnamePool.begin(), m_hostnamePool.end(), (*it));
        if(itIP == m_hostnamePool.end())
            m_hostnamePool.push_back((*it));
    }

    // Connect to first working host
    int hostnameCount = m_hostnamePool.size();
    while(hostnameCount--)
    {
        string connectTo = m_hostnamePool.front();
        m_hostnamePool.pop_front();
        m_hostnamePool.push_back(connectTo);

        lock.unlock();

        conn.reset(new CommunicationHandler(connectTo, m_port, m_getCertInfo, m_checkCertificate, poolInfo.endpoint));
        conn->setFuseID(m_fuseId);  // Set FuseID that shall be used by this connection as session ID
        if(m_pushCallback)                          // Set callback that shall be used for PUSH messages and error messages
            conn->setPushCallback(m_pushCallback);  // Note that this doesnt enable/register PUSH channel !

        if(m_pushCallback && m_fuseId.size() > 0 && type == META_POOL) // Enable PUSH channel (will register itself when possible)
            conn->enablePushChannel();

        if(conn->openConnection() == 0) {
            break;
        }

        m_lastError.store(conn->getLastError());

        lock.lock();
        conn.reset();
        LOG(WARNING) << "Cannot connect to host: " << connectTo << ":" << m_port;

    }

    if(conn)
        poolInfo.connections.push_front(make_pair(conn, time(NULL)));
    else
        LOG(ERROR) << "Opening new connection (type: " << type << ") failed!";

    return conn;
}

boost::shared_ptr<CommunicationHandler> SimpleConnectionPool::selectConnection(PoolType type)
{
    boost::unique_lock< boost::recursive_mutex > lock(m_access);
    boost::shared_ptr<CommunicationHandler> conn;

    ConnectionPoolInfo &poolInfo = *m_connectionPools.at(type);

    // Delete first connection in pool if its error counter is way to big
    if(poolInfo.connections.size() > 0 && poolInfo.connections.front().first->getErrorCount() > MAX_CONNECTION_ERROR_COUNT) {
        LOG(INFO) << "Connection " << poolInfo.connections.front().first << " emited to many errors. Reinitializing object.";
        poolInfo.connections.pop_front();
    }

    // Remove redundant connections
    while(poolInfo.connections.size() > poolInfo.size)
        poolInfo.connections.pop_back();

    // Check if pool size matches config
    long toStart = poolInfo.size - poolInfo.connections.size() - poolInfo.currWorkers;
    if(poolInfo.connections.size() == 0 && toStart <= 0)
        toStart = 1;

    DLOG(INFO) << "Current pool size: " << poolInfo.connections.size() << ", connections in construction: " << poolInfo.currWorkers << ", expected: " << poolInfo.size;

    while(toStart --> 0) // Current pool is too small, we should create some connection(s)
    {
        LOG(INFO) << "Connection pool (" << type << " is to small (" << poolInfo.connections.size() << " connections - expected: " << poolInfo.size << "). Opening new connection...";

        if(poolInfo.connections.size() > 0) {
            boost::thread t = boost::thread(boost::bind(&SimpleConnectionPool::newConnection, shared_from_this(), type));
            t.detach();
        }
        else
            conn = newConnection(type);
    }

    if(poolInfo.connections.size() > 0)
    {
        conn = poolInfo.connections.front().first;

        // Round-robin
        poolInfo.connections.push_back(poolInfo.connections.front());
        poolInfo.connections.pop_front();
    }

    return conn;
}

void SimpleConnectionPool::releaseConnection(boost::shared_ptr<CommunicationHandler> conn)
{
    return;
}

error::Error SimpleConnectionPool::getLastError() const
{
    return m_lastError.load();
}

void SimpleConnectionPool::setPoolSize(PoolType type, unsigned int s)
{
    boost::unique_lock< boost::recursive_mutex > lock(m_access);
    m_connectionPools.at(type)->size = s;

    // Insert new connections to pool if needed (async)
    long toStart = m_connectionPools.at(type)->size - m_connectionPools.at(type)->connections.size();
    while(toStart-- > 0) {
        boost::thread t = boost::thread(boost::bind(&SimpleConnectionPool::newConnection, shared_from_this(), type));
        t.detach();
    }
}

void SimpleConnectionPool::setPushCallback(const string &fuseId, push_callback hdl)
{
    m_fuseId = fuseId;
    m_pushCallback = hdl;
}

list<string> SimpleConnectionPool::dnsQuery(const string &hostname)
{
    boost::unique_lock< boost::recursive_mutex > lock(m_access);

    list<string> lst;
    struct addrinfo *result;
    struct addrinfo *res;

    if(getaddrinfo(hostname.c_str(), NULL, NULL, &result) == 0) {
        for(res = result; res != NULL; res = res->ai_next) {
            char ip[INET_ADDRSTRLEN + 1] = "";
            switch(res->ai_addr->sa_family) {
                case AF_INET:
                    if(inet_ntop(res->ai_addr->sa_family, &((struct sockaddr_in*)res->ai_addr)->sin_addr, ip, INET_ADDRSTRLEN + 1) != NULL)
                        lst.push_back(string(ip));
                case AF_INET6:
                    // Not supported
                    break;
                default:
                    break;
            }
        }
        freeaddrinfo(result);
    }

    if(lst.size() == 0) {
        LOG(ERROR) << "DNS lookup failed for host: " << hostname;
        lst.push_back(hostname); // Make sure that returned list is not empty but adding argument to it
    }

    return lst;
}

SimpleConnectionPool::ConnectionPoolInfo::ConnectionPoolInfo(cert_info_fun getCertInfo, unsigned int s, const bool checkCertificate)
    : size(s)
    , m_checkCertificate(checkCertificate)
    , m_getCertInfo{std::move(getCertInfo)}
{
    LOG(INFO) << "Initializing a WebSocket endpoint";
    websocketpp::lib::error_code ec;
    endpoint->clear_access_channels(websocketpp::log::alevel::all);
    endpoint->clear_error_channels(websocketpp::log::elevel::all);
    endpoint->init_asio(ec);
    endpoint->start_perpetual();

    if(ec)
    {
        LOG(ERROR) << "Cannot initlize WebSocket endpoint; terminating.";
        std::terminate();
    }

    // Start worker thread
    m_ioWorker = boost::thread(&ws_client::run, endpoint);

    endpoint->set_tls_init_handler(bind(&ConnectionPoolInfo::onTLSInit, this, ::_1));
    endpoint->set_socket_init_handler(bind(&ConnectionPoolInfo::onSocketInit, this, ::_1, ::_2));
}

SimpleConnectionPool::ConnectionPoolInfo::~ConnectionPoolInfo()
{
    LOG(INFO) << "Stopping the WebSocket endpoint";
    endpoint->stop_perpetual();

    for(auto &con: connections)
        con.first->closeConnection();

    LOG(INFO) << "Stopping WebSocket endpoint worker thread";
    m_ioWorker.join();
}

context_ptr SimpleConnectionPool::ConnectionPoolInfo::onTLSInit(websocketpp::connection_hdl hdl)
{
    if (!m_getCertInfo) {
        LOG(ERROR) << "Cannot get CertificateInfo due to null getter";
        return context_ptr();
    }

    CertificateInfo certInfo = m_getCertInfo();

    try {
        context_ptr ctx(new boost::asio::ssl::context(boost::asio::ssl::context::sslv3));

        ctx->set_options(boost::asio::ssl::context::default_workarounds |
                         boost::asio::ssl::context::no_sslv2 |
                         boost::asio::ssl::context::single_dh_use);

        ctx->set_default_verify_paths();
        ctx->set_verify_mode(m_checkCertificate
                             ? boost::asio::ssl::verify_peer
                             : boost::asio::ssl::verify_none);

        boost::asio::ssl::context_base::file_format file_format; // Certificate format
        if(certInfo.cert_type == CertificateInfo::ASN1) {
            file_format = boost::asio::ssl::context::asn1;
        } else {
            file_format = boost::asio::ssl::context::pem;
        }

        if(certInfo.cert_type == CertificateInfo::P12) {
            LOG(ERROR) << "Unsupported certificate format: P12";
            return context_ptr();
        }

        if(boost::asio::buffer_size(certInfo.chain_data) && boost::asio::buffer_size(certInfo.chain_data)) {
            ctx->use_certificate_chain(certInfo.chain_data);
            ctx->use_private_key(certInfo.key_data, file_format);
        } else {
            ctx->use_certificate_chain_file(certInfo.user_cert_path);
            ctx->use_private_key_file(certInfo.user_key_path, file_format);
        }

        return ctx;

    } catch (boost::system::system_error& e) {
        LOG(ERROR) << "Cannot initialize TLS socket due to: " << e.what()
                   << " with cert file: " << certInfo.user_cert_path << " and key file: "
                   << certInfo.user_key_path;
    }

    return context_ptr();
}

void SimpleConnectionPool::ConnectionPoolInfo::onSocketInit(websocketpp::connection_hdl hdl, socket_type &socket)
{
    // Disable socket delay
    socket.lowest_layer().set_option(boost::asio::ip::tcp::no_delay(true));
}

} // namespace veil
<|endoftext|>


<commit_before>#include <QPainter>
#include <QApplication>
#include <QPixmap>
#include <QToolTip>
#include <QLayout>
#include <QDebug>

#include "seafile-applet.h"
#include "main-window.h"
#include "events-service.h"
#include "avatar-service.h"
#include "event-details-dialog.h"
#include "utils/paint-utils.h"
#include "utils/utils.h"
#include "api/event.h"

#include "events-list-view.h"

namespace {

/**
            nick date
   icon
            description
 */

const int kMarginLeft = 5;
const int kMarginRight = 5;
const int kMarginTop = 5;
const int kMarginBottom = 5;
const int kPadding = 5;

const int kAvatarHeight = 36;
const int kAvatarWidth = 36;
//const int kNickWidth = 210;
const int kNickHeight = 30;

const int kMarginBetweenAvatarAndNick = 10;

const char *kNickColor = "#D8AC8F";
const char *kNickColorHighlighted = "#D8AC8F";
const char *kRepoNameColor = "#D8AC8F";
const char *kRepoNameColorHighlighted = "#D8AC8F";
const char *kDescriptionColor = "#3F3F3F";
const char *kDescriptionColorHighlighted = "#544D49";

const int kNickFontSize = 16;
const int kDescriptionFontSize = 13;
const int kDescriptionHeight = 30;

const char *kEventItemBackgroundColor = "white";
const char *kEventItemBackgroundColorHighlighted = "#F9E0C7";

const int kTimeWidth = 100;
const int kTimeHeight = 30;
const int kTimeFontSize = 13;

const int kRepoNameWidth = 80;

const char *kTimeColor = "#959595";
const char *kTimeColorHighlighted = "#9D9B9A";

const int kMarginBetweenNickAndTime = 10;

const int kMarginBetweenRepoNameAndDesc = 18;

const char *kItemBottomBorderColor = "#EEE";

} // namespace

EventItem::EventItem(const SeafEvent& event)
    : event_(event)
{
}

EventItemDelegate::EventItemDelegate(QObject *parent)
    : QStyledItemDelegate(parent)
{
}

void EventItemDelegate::paint(QPainter *painter,
                              const QStyleOptionViewItem& option,
                              const QModelIndex& index) const
{
    QBrush backBrush;
    bool selected = false;
    EventItem *item = getItem(index);
    const SeafEvent& event = item->event();
    QString time_text = translateCommitTime(event.timestamp);

    if (option.state & (QStyle::State_HasFocus | QStyle::State_Selected)) {
        backBrush = QColor(kEventItemBackgroundColorHighlighted);
        selected = true;

    } else {
        backBrush = QColor(kEventItemBackgroundColor);
    }

    painter->save();
    painter->fillRect(option.rect, backBrush);
    painter->restore();
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setRenderHint(QPainter::HighQualityAntialiasing);

    // get the device pixel radio from current painter device
    int scale_factor = 1;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    scale_factor = painter->device()->devicePixelRatio();
#endif // QT5

    // paint avatar
    QImage avatar;
    if (!event.anonymous) {
        avatar = AvatarService::instance()->getAvatar(event.author);
    }

    QRect actualRect(0, 0, kAvatarWidth * scale_factor , kAvatarHeight * scale_factor);
    avatar.scaled(actualRect.size());
    QImage masked_image(actualRect.size(), QImage::Format_ARGB32_Premultiplied);
    masked_image.fill(Qt::transparent);
    QPainter mask_painter;
    mask_painter.begin(&masked_image);
    mask_painter.setRenderHint(QPainter::Antialiasing);
    mask_painter.setRenderHint(QPainter::HighQualityAntialiasing);
    mask_painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    mask_painter.setPen(Qt::NoPen);
    mask_painter.setBrush(Qt::white);
    mask_painter.drawEllipse(actualRect);
    mask_painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
    mask_painter.drawImage(actualRect, avatar);
    mask_painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
    mask_painter.fillRect(actualRect, Qt::transparent);
    mask_painter.end();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    masked_image.setDevicePixelRatio(scale_factor);
#endif // QT5

    QPoint avatar_pos(kMarginLeft + kPadding, kMarginTop + kPadding);
    avatar_pos += option.rect.topLeft();
    painter->save();
    painter->drawImage(avatar_pos, masked_image);
    painter->restore();

    const int time_width = qMin(kTimeWidth,
        ::textWidthInFont(time_text,
            changeFontSize(painter->font(), kTimeFontSize)));
    int nick_width = option.rect.width() - kMarginLeft - kAvatarWidth - kMarginBetweenAvatarAndNick
        - time_width - kMarginBetweenNickAndTime - kPadding * 2 - kMarginRight;
    nick_width = qMin(nick_width,
                      ::textWidthInFont(event.nick,
                            changeFontSize(painter->font(), kNickFontSize)));

    // Paint nick name
    QPoint nick_pos = avatar_pos + QPoint(kAvatarWidth + kMarginBetweenAvatarAndNick, 0);
    QRect nick_rect(nick_pos, QSize(nick_width, kNickHeight));
    painter->save();
    painter->setPen(QColor(selected ? kNickColorHighlighted : kNickColor));
    painter->setFont(changeFontSize(painter->font(), kNickFontSize));
    painter->drawText(nick_rect,
                      Qt::AlignLeft | Qt::AlignTop,
                      fitTextToWidth(event.nick, option.font, nick_width),
                      &nick_rect);
    painter->restore();

    // Paint event time
    painter->save();
    QPoint time_pos = option.rect.topRight() + QPoint(-time_width - kPadding - kMarginRight, kMarginTop + kPadding);
    QRect time_rect(time_pos, QSize(time_width, kTimeHeight));
    painter->setPen(QColor(selected ? kTimeColorHighlighted : kTimeColor));
    painter->setFont(changeFontSize(painter->font(), kTimeFontSize));

    painter->drawText(time_rect,
                      Qt::AlignRight | Qt::AlignTop,
                      time_text,
                      &time_rect);
    painter->restore();

    // Paint description
    painter->save();

    const int repo_name_width = qMin(kRepoNameWidth, ::textWidthInFont(event.repo_name, changeFontSize(painter->font(), kTimeFontSize)));
    const int repo_name_height = ::textHeightInFont(event.repo_name, changeFontSize(painter->font(), kTimeFontSize));

    int desc_width = option.rect.width() - kMarginLeft - kAvatarWidth - kMarginBetweenAvatarAndNick - kPadding * 3 - kMarginBetweenRepoNameAndDesc - repo_name_width - kMarginRight;
    desc_width = qMin(desc_width, ::textWidthInFont(event.desc, changeFontSize(painter->font(), kDescriptionFontSize)));
    const int desc_height = ::textHeightInFont(event.desc, changeFontSize(painter->font(), kDescriptionFontSize)) * 2;

    const QPoint event_desc_pos = option.rect.bottomLeft() + QPoint(nick_rect.left(), - desc_height - kPadding - kMarginBottom);

    QRect event_desc_rect(event_desc_pos, QSize(desc_width, desc_height));
    painter->setFont(changeFontSize(painter->font(), kDescriptionFontSize));
    painter->setPen(QColor(selected ? kDescriptionColorHighlighted : kDescriptionColor));

    QString desc = event.desc;
    desc.replace(QChar('\n'), QChar(' '));
    painter->drawText(event_desc_rect,
                      Qt::AlignLeft | Qt::AlignTop | Qt::TextWrapAnywhere,
                      // we have two lines
                      fitTextToWidth(desc, option.font, desc_width * 2),
                      &event_desc_rect);
    painter->restore();

    // Paint repo name
    painter->save();

    const QPoint event_repo_name_pos = option.rect.bottomRight() +
        QPoint(-repo_name_width - kPadding - kMarginRight,
               -repo_name_height - kPadding - kMarginBottom);

    QRect event_repo_name_rect(event_repo_name_pos, QSize(repo_name_width, kNickHeight));
    painter->setFont(changeFontSize(painter->font(), kTimeFontSize));
    painter->setPen(QColor(selected ? kRepoNameColorHighlighted : kRepoNameColor));
    painter->drawText(event_repo_name_rect,
                      Qt::AlignRight | Qt::AlignTop | Qt::TextSingleLine,
                      fitTextToWidth(event.repo_name, option.font, repo_name_width),
                      &event_repo_name_rect);
    painter->restore();

    // Draw the bottom border lines
    painter->save();
    painter->setPen(QPen(QColor(kItemBottomBorderColor), 1, Qt::SolidLine));
    painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
    painter->restore();
}

QSize EventItemDelegate::sizeHint(const QStyleOptionViewItem& option,
                                  const QModelIndex& index) const
{
    int height = kNickHeight + kDescriptionHeight + kPadding * 3;
    return QSize(option.rect.width(), height);
}

EventItem*
EventItemDelegate::getItem(const QModelIndex &index) const
{
    if (!index.isValid()) {
        return NULL;
    }
    const EventsListModel *model = (const EventsListModel*)index.model();
    QStandardItem *qitem = model->itemFromIndex(index);
    if (qitem->type() == EVENT_ITEM_TYPE) {
        return (EventItem *)qitem;
    }
    return NULL;
}

EventsListView::EventsListView(QWidget *parent)
    : QListView(parent)
{
    setItemDelegate(new EventItemDelegate);
    connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
            this, SLOT(onItemDoubleClicked(const QModelIndex&)));

    setEditTriggers(QAbstractItemView::NoEditTriggers);
}

EventItem*
EventsListView::getItem(const QModelIndex &index) const
{
    if (!index.isValid()) {
        return NULL;
    }
    const EventsListModel *model = (const EventsListModel*)index.model();
    QStandardItem *qitem = model->itemFromIndex(index);
    if (qitem->type() == EVENT_ITEM_TYPE) {
        return (EventItem *)qitem;
    }
    return NULL;
}

void EventsListView::onItemDoubleClicked(const QModelIndex& index)
{
    EventItem *item = getItem(index);
    if (!item) {
        return;
    }

    const SeafEvent& event = item->event();

    if (!event.isDetailsDisplayable()) {
        return;
    }

    EventDetailsDialog dialog(event, seafApplet->mainWindow());

    dialog.exec();
}

bool EventsListView::viewportEvent(QEvent *event)
{
    if (event->type() != QEvent::ToolTip && event->type() != QEvent::WhatsThis) {
        return QListView::viewportEvent(event);
    }

    QPoint global_pos = QCursor::pos();
    QPoint viewport_pos = viewport()->mapFromGlobal(global_pos);
    QModelIndex index = indexAt(viewport_pos);
    if (!index.isValid()) {
        return true;
    }

    EventItem *item = getItem(index);
    if (!item) {
        return true;
    }

    QRect item_rect = visualRect(index);

    QString text = "
";
    text += item->event().desc;
    text += "
";

    QToolTip::showText(QCursor::pos(), text, viewport(), item_rect);

    return true;
}

EventsListModel::EventsListModel(QObject *parent)
    : QStandardItemModel(parent)
{
}

const QModelIndex
EventsListModel::updateEvents(const std::vector<SeafEvent>& events, bool is_loading_more)
{
    if (!is_loading_more) {
        clear();
    }
    int i = 0, n = events.size();

    EventItem *first_item = 0;

    for (i = 0; i < n; i++) {
        SeafEvent event = events[i];
        EventItem *item = new EventItem(event);

        appendRow(item);

        if (!first_item) {
            first_item = item;
        }
    }

    if (is_loading_more && first_item) {
        return indexFromItem(first_item);
    }

    return QModelIndex();
}

void EventsListModel::onAvatarUpdated(const QString& email, const QImage& img)
{
    int i, n = rowCount();

    for (i = 0; i < n; i++) {
        QStandardItem *qitem = item(i);
        if (qitem->type() != EVENT_ITEM_TYPE) {
            return;
        }
        EventItem *item = (EventItem *)qitem;

        if (item->event().author == email) {
            QModelIndex index = indexFromItem(item);
            emit dataChanged(index, index);
        }
    }
}
<commit_msg>activity tab: tweak paddings<commit_after>#include <QPainter>
#include <QApplication>
#include <QPixmap>
#include <QToolTip>
#include <QLayout>
#include <QDebug>

#include "seafile-applet.h"
#include "main-window.h"
#include "events-service.h"
#include "avatar-service.h"
#include "event-details-dialog.h"
#include "utils/paint-utils.h"
#include "utils/utils.h"
#include "api/event.h"

#include "events-list-view.h"

namespace {

/**
            nick date
   icon
            description
 */

const int kMarginLeft = 5;
const int kMarginRight = 5;
const int kMarginTop = 5;
const int kMarginBottom = 5;
const int kPadding = 5;

const int kAvatarHeight = 36;
const int kAvatarWidth = 36;
//const int kNickWidth = 210;
const int kNickHeight = 30;

const int kMarginBetweenAvatarAndNick = 10;

const char *kNickColor = "#D8AC8F";
const char *kNickColorHighlighted = "#D8AC8F";
const char *kRepoNameColor = "#D8AC8F";
const char *kRepoNameColorHighlighted = "#D8AC8F";
const char *kDescriptionColor = "#3F3F3F";
const char *kDescriptionColorHighlighted = "#544D49";

const int kNickFontSize = 16;
const int kDescriptionFontSize = 13;
const int kDescriptionHeight = 30;

const char *kEventItemBackgroundColor = "white";
const char *kEventItemBackgroundColorHighlighted = "#F9E0C7";

const int kTimeWidth = 100;
const int kTimeHeight = 30;
const int kTimeFontSize = 13;

const int kRepoNameWidth = 80;

const char *kTimeColor = "#959595";
const char *kTimeColorHighlighted = "#9D9B9A";

const int kMarginBetweenNickAndTime = 10;

const int kMarginBetweenRepoNameAndDesc = 18;

const char *kItemBottomBorderColor = "#EEE";

} // namespace

EventItem::EventItem(const SeafEvent& event)
    : event_(event)
{
}

EventItemDelegate::EventItemDelegate(QObject *parent)
    : QStyledItemDelegate(parent)
{
}

void EventItemDelegate::paint(QPainter *painter,
                              const QStyleOptionViewItem& option,
                              const QModelIndex& index) const
{
    QBrush backBrush;
    bool selected = false;
    EventItem *item = getItem(index);
    const SeafEvent& event = item->event();
    QString time_text = translateCommitTime(event.timestamp);

    if (option.state & (QStyle::State_HasFocus | QStyle::State_Selected)) {
        backBrush = QColor(kEventItemBackgroundColorHighlighted);
        selected = true;

    } else {
        backBrush = QColor(kEventItemBackgroundColor);
    }

    painter->save();
    painter->fillRect(option.rect, backBrush);
    painter->restore();
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setRenderHint(QPainter::HighQualityAntialiasing);

    // get the device pixel radio from current painter device
    int scale_factor = 1;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    scale_factor = painter->device()->devicePixelRatio();
#endif // QT5

    // paint avatar
    QImage avatar;
    if (!event.anonymous) {
        avatar = AvatarService::instance()->getAvatar(event.author);
    }

    QRect actualRect(0, 0, kAvatarWidth * scale_factor , kAvatarHeight * scale_factor);
    avatar.scaled(actualRect.size());
    QImage masked_image(actualRect.size(), QImage::Format_ARGB32_Premultiplied);
    masked_image.fill(Qt::transparent);
    QPainter mask_painter;
    mask_painter.begin(&masked_image);
    mask_painter.setRenderHint(QPainter::Antialiasing);
    mask_painter.setRenderHint(QPainter::HighQualityAntialiasing);
    mask_painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
    mask_painter.setPen(Qt::NoPen);
    mask_painter.setBrush(Qt::white);
    mask_painter.drawEllipse(actualRect);
    mask_painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
    mask_painter.drawImage(actualRect, avatar);
    mask_painter.setCompositionMode(QPainter::CompositionMode_DestinationOver);
    mask_painter.fillRect(actualRect, Qt::transparent);
    mask_painter.end();
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
    masked_image.setDevicePixelRatio(scale_factor);
#endif // QT5

    QPoint avatar_pos(kMarginLeft + kPadding, kMarginTop + kPadding);
    avatar_pos += option.rect.topLeft();
    painter->save();
    painter->drawImage(avatar_pos, masked_image);
    painter->restore();

    const int time_width = qMin(kTimeWidth,
        ::textWidthInFont(time_text,
            changeFontSize(painter->font(), kTimeFontSize)));
    int nick_width = option.rect.width() - kMarginLeft - kAvatarWidth - kMarginBetweenAvatarAndNick
        - time_width - kMarginBetweenNickAndTime - kPadding * 2 - kMarginRight;
    nick_width = qMin(nick_width,
                      ::textWidthInFont(event.nick,
                            changeFontSize(painter->font(), kNickFontSize)));

    // Paint nick name
    QPoint nick_pos = avatar_pos + QPoint(kAvatarWidth + kMarginBetweenAvatarAndNick, 0);
    QRect nick_rect(nick_pos, QSize(nick_width, kNickHeight));
    painter->save();
    painter->setPen(QColor(selected ? kNickColorHighlighted : kNickColor));
    painter->setFont(changeFontSize(painter->font(), kNickFontSize));
    painter->drawText(nick_rect,
                      Qt::AlignLeft | Qt::AlignTop,
                      fitTextToWidth(event.nick, option.font, nick_width),
                      &nick_rect);
    painter->restore();

    // Paint event time
    painter->save();
    QPoint time_pos = option.rect.topRight() + QPoint(-time_width - kPadding - kMarginRight, kMarginTop + kPadding);
    QRect time_rect(time_pos, QSize(time_width, kTimeHeight));
    painter->setPen(QColor(selected ? kTimeColorHighlighted : kTimeColor));
    painter->setFont(changeFontSize(painter->font(), kTimeFontSize));

    painter->drawText(time_rect,
                      Qt::AlignRight | Qt::AlignTop,
                      time_text,
                      &time_rect);
    painter->restore();

    // Paint description
    painter->save();

    const int repo_name_width = qMin(kRepoNameWidth, ::textWidthInFont(event.repo_name, changeFontSize(painter->font(), kTimeFontSize)));
    const int repo_name_height = ::textHeightInFont(event.repo_name, changeFontSize(painter->font(), kTimeFontSize));

    int desc_width = option.rect.width() - kMarginLeft - kAvatarWidth - kMarginBetweenAvatarAndNick - kPadding * 3 - kMarginBetweenRepoNameAndDesc - repo_name_width - kMarginRight;
    desc_width = qMin(desc_width, ::textWidthInFont(event.desc, changeFontSize(painter->font(), kDescriptionFontSize)));
    const int desc_height = ::textHeightInFont(event.desc, changeFontSize(painter->font(), kDescriptionFontSize)) * 2;

    const QPoint event_desc_pos = option.rect.bottomLeft() + QPoint(nick_rect.left(), - desc_height - kMarginBottom);

    QRect event_desc_rect(event_desc_pos, QSize(desc_width, desc_height));
    painter->setFont(changeFontSize(painter->font(), kDescriptionFontSize));
    painter->setPen(QColor(selected ? kDescriptionColorHighlighted : kDescriptionColor));

    QString desc = event.desc;
    desc.replace(QChar('\n'), QChar(' '));
    painter->drawText(event_desc_rect,
                      Qt::AlignLeft | Qt::AlignTop | Qt::TextWrapAnywhere,
                      // we have two lines
                      fitTextToWidth(desc, option.font, desc_width * 2),
                      &event_desc_rect);
    painter->restore();

    // Paint repo name
    painter->save();

    const QPoint event_repo_name_pos = option.rect.bottomRight() +
        QPoint(-repo_name_width - kPadding - kMarginRight,
               -repo_name_height - kMarginBottom);

    QRect event_repo_name_rect(event_repo_name_pos, QSize(repo_name_width, kNickHeight));
    painter->setFont(changeFontSize(painter->font(), kTimeFontSize));
    painter->setPen(QColor(selected ? kRepoNameColorHighlighted : kRepoNameColor));
    painter->drawText(event_repo_name_rect,
                      Qt::AlignRight | Qt::AlignTop | Qt::TextSingleLine,
                      fitTextToWidth(event.repo_name, option.font, repo_name_width),
                      &event_repo_name_rect);
    painter->restore();

    // Draw the bottom border lines
    painter->save();
    painter->setPen(QPen(QColor(kItemBottomBorderColor), 1, Qt::SolidLine));
    painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());
    painter->restore();
}

QSize EventItemDelegate::sizeHint(const QStyleOptionViewItem& option,
                                  const QModelIndex& index) const
{
    int height = kNickHeight + kDescriptionHeight + kPadding * 3 + kPadding / 2;
    return QSize(option.rect.width(), height);
}

EventItem*
EventItemDelegate::getItem(const QModelIndex &index) const
{
    if (!index.isValid()) {
        return NULL;
    }
    const EventsListModel *model = (const EventsListModel*)index.model();
    QStandardItem *qitem = model->itemFromIndex(index);
    if (qitem->type() == EVENT_ITEM_TYPE) {
        return (EventItem *)qitem;
    }
    return NULL;
}

EventsListView::EventsListView(QWidget *parent)
    : QListView(parent)
{
    setItemDelegate(new EventItemDelegate);
    connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
            this, SLOT(onItemDoubleClicked(const QModelIndex&)));

    setEditTriggers(QAbstractItemView::NoEditTriggers);
}

EventItem*
EventsListView::getItem(const QModelIndex &index) const
{
    if (!index.isValid()) {
        return NULL;
    }
    const EventsListModel *model = (const EventsListModel*)index.model();
    QStandardItem *qitem = model->itemFromIndex(index);
    if (qitem->type() == EVENT_ITEM_TYPE) {
        return (EventItem *)qitem;
    }
    return NULL;
}

void EventsListView::onItemDoubleClicked(const QModelIndex& index)
{
    EventItem *item = getItem(index);
    if (!item) {
        return;
    }

    const SeafEvent& event = item->event();

    if (!event.isDetailsDisplayable()) {
        return;
    }

    EventDetailsDialog dialog(event, seafApplet->mainWindow());

    dialog.exec();
}

bool EventsListView::viewportEvent(QEvent *event)
{
    if (event->type() != QEvent::ToolTip && event->type() != QEvent::WhatsThis) {
        return QListView::viewportEvent(event);
    }

    QPoint global_pos = QCursor::pos();
    QPoint viewport_pos = viewport()->mapFromGlobal(global_pos);
    QModelIndex index = indexAt(viewport_pos);
    if (!index.isValid()) {
        return true;
    }

    EventItem *item = getItem(index);
    if (!item) {
        return true;
    }

    QRect item_rect = visualRect(index);

    QString text = "
";
    text += item->event().desc;
    text += "
";

    QToolTip::showText(QCursor::pos(), text, viewport(), item_rect);

    return true;
}

EventsListModel::EventsListModel(QObject *parent)
    : QStandardItemModel(parent)
{
}

const QModelIndex
EventsListModel::updateEvents(const std::vector<SeafEvent>& events, bool is_loading_more)
{
    if (!is_loading_more) {
        clear();
    }
    int i = 0, n = events.size();

    EventItem *first_item = 0;

    for (i = 0; i < n; i++) {
        SeafEvent event = events[i];
        EventItem *item = new EventItem(event);

        appendRow(item);

        if (!first_item) {
            first_item = item;
        }
    }

    if (is_loading_more && first_item) {
        return indexFromItem(first_item);
    }

    return QModelIndex();
}

void EventsListModel::onAvatarUpdated(const QString& email, const QImage& img)
{
    int i, n = rowCount();

    for (i = 0; i < n; i++) {
        QStandardItem *qitem = item(i);
        if (qitem->type() != EVENT_ITEM_TYPE) {
            return;
        }
        EventItem *item = (EventItem *)qitem;

        if (item->event().author == email) {
            QModelIndex index = indexFromItem(item);
            emit dataChanged(index, index);
        }
    }
}
<|endoftext|>


<commit_before>#define BOOST_TEST_MODULE cachelot_test
#include "unit_test.h"

//int main() {
//    return 0;
//}
<commit_msg>cleanup<commit_after>#define BOOST_TEST_MODULE cachelot_test
#include "unit_test.h"

namespace cachelot {

} // namespace cachelot
<|endoftext|>


<commit_before>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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

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

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

#include "paddle/framework/op_registry.h"
#include "paddle/operators/math/math_function.h"

namespace paddle {
namespace operators {

using Tensor = framework::Tensor;
using LoDTensor = framework::LoDTensor;

constexpr char kEPS = 1e-6;

class BipartiteMatchOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("DistMat"),
                   "Input(DistMat) of BipartiteMatch should not be null.");

    auto dims = ctx->GetInputDim("DistMat");
    PADDLE_ENFORCE_EQ(dims.size(), 2, "The rank of Input(DistMat) must be 2.");

    ctx->SetOutputDim("ColToRowMatchIndices", dims);
    ctx->SetOutputDim("ColToRowMatchDis", dims);
  }
};

template <typename T>
class BipartiteMatchKernel : public framework::OpKernel<T> {
 public:
  // The match_indices must be initialized to -1 at first.
  // The match_dist must be initialized to 0 at first.
  void BipartiteMatch(const Tensor& dist, int* match_indices,
                      T* match_dist) const {
    PADDLE_ENFORCE_EQ(dist.dims().size(), 2, "The rank of dist must be 2.");
    int64_t row = dist.dims()[0];
    int64_t col = dist.dims()[1];
    auto* dist_data = dist.data<T>();
    std::vector<int> row_pool;
    for (int i = 0; i < row; ++i) {
      row_pool.push_back(i);
    }
    while (row_pool.size() > 0) {
      int max_idx = -1;
      int max_row_idx = -1;
      T max_dist = -1;
      for (int64_t j = 0; j < col; ++j) {
        if (match_indices[j] != -1) {
          continue;
        }
        for (size_t k = 0; k < row_pool.size(); ++k) {
          int m = row_pool[k];
          // distance is 0 between m-th row and j-th column
          if (dist_data[m * col + j] < kEPS) {
            continue;
          }
          if (dist_data[m * col + j] > max_dist) {
            max_idx = j;
            max_row_idx = m;
            max_dist = dist_data[m * col + j];
          }
        }
      }
      if (max_idx == -1) {
        // Cannot find good match.
        break;
      } else {
        PADDLE_ENFORCE_EQ(match_indices[max_idx], -1);
        match_indices[max_idx] = max_row_idx;
        match_dist[max_idx] = max_dist;
        // Erase the row index.
        row_pool.erase(
            std::find(row_pool.begin(), row_pool.end(), max_row_idx));
      }
    }
  }

  void Compute(const framework::ExecutionContext& context) const override {
    auto* dist_mat = context.Input<LoDTensor>("DistMat");
    auto* match_indices = context.Output<Tensor>("ColToRowMatchIndices");
    auto* match_dist = context.Output<Tensor>("ColToRowMatchDis");

    auto& dev_ctx = context.device_context<platform::CPUDeviceContext>();

    auto col = dist_mat->dims()[1];

    int64_t n = dist_mat->lod().size() == 0UL
                    ? 1
                    : static_cast<int64_t>(dist_mat->lod().back().size() - 1);
    if (dist_mat->lod().size()) {
      PADDLE_ENFORCE_EQ(dist_mat->lod().size(), 1UL,
                        "Only support 1 level of LoD.");
    }
    match_indices->mutable_data<int>({n, col}, context.GetPlace());
    match_dist->mutable_data<T>({n, col}, context.GetPlace());

    math::SetConstant<platform::CPUDeviceContext, int> iset;
    iset(dev_ctx, match_indices, static_cast<int>(-1));
    math::SetConstant<platform::CPUDeviceContext, T> tset;
    tset(dev_ctx, match_dist, static_cast<T>(0));

    int* indices = match_indices->data<int>();
    T* dist = match_dist->data<T>();
    if (n == 1) {
      BipartiteMatch(*dist_mat, indices, dist);
    } else {
      auto lod = dist_mat->lod().back();
      for (size_t i = 0; i < lod.size() - 1; ++i) {
        Tensor one_ins = dist_mat->Slice(lod[i], lod[i + 1]);
        BipartiteMatch(one_ins, indices + i * col, dist + i * col);
      }
    }
  }
};

class BipartiteMatchOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  BipartiteMatchOpMaker(OpProto* proto, OpAttrChecker* op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput(
        "DistMat",
        "(LoDTensor or Tensor) this input is a 2-D LoDTensor with shape "
        "[K, M]. It is pair-wise distance matrix between the entities "
        "represented by each row and each column. For example, assumed one "
        "entity is A with shape [K], another entity is B with shape [M]. The "
        "DistMat[i][j] is the distance between A[i] and B[j]. The bigger "
        "the distance is, the better macthing the pairs are. Please note, "
        "This tensor can contain LoD information to represent a batch of "
        "inputs. One instance of this batch can contain different numbers of "
        "entities.");
    AddOutput("ColToRowMatchIndices",
              "(Tensor) A 2-D Tensor with shape [N, M] in int type. "
              "N is the batch size. If ColToRowMatchIndices[i][j] is -1, it "
              "means B[j] does not match any entity in i-th instance. "
              "Otherwise, it means B[j] is matched to row "
              "ColToRowMatchIndices[i][j] in i-th instance. The row number of "
              "i-th instance is saved in ColToRowMatchIndices[i][j].");
    AddOutput("ColToRowMatchDis",
              "(Tensor) A 2-D Tensor with shape [N, M] in float type. "
              "N is batch size. If ColToRowMatchIndices[i][j] is -1, "
              "ColToRowMatchDis[i][j] is also -1.0. Otherwise, assumed "
              "ColToRowMatchIndices[i][j] = d, and the row offsets of each "
              "instance are called LoD. Then "
              "ColToRowMatchDis[i][j] = DistMat[d+LoD[i]][j]");
    AddComment(R"DOC(
This operator is a greedy bipartite matching algorithm, which is used to
obtain the matching with the maximum distance based on the input
distance matrix. For input 2D matrix, the bipartite matching algorithm can
find the matched column for each row, also can find the matched row for
each column. And this operator only calculate matched indices from column
to row. For each instance, the number of matched indices is the number of
of columns of the input ditance matrix.

There are two outputs to save matched indices and distance.
A simple description, this algothrim matched the best (maximum distance)
row entity to the column entity and the matched indices are not duplicated
in each row of ColToRowMatchIndices. If the column entity is not matched
any row entity, set -1 in ColToRowMatchIndices.

Please note that the input DistMat can be LoDTensor (with LoD) or Tensor.
If LoDTensor with LoD, the height of ColToRowMatchIndices is batch size.
If Tensor, the height of ColToRowMatchIndices is 1.

)DOC");
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OPERATOR(bipartite_match, ops::BipartiteMatchOp,
                  ops::BipartiteMatchOpMaker,
                  paddle::framework::EmptyGradOpMaker);
REGISTER_OP_CPU_KERNEL(bipartite_match, ops::BipartiteMatchKernel<float>,
                       ops::BipartiteMatchKernel<double>);
<commit_msg>Fix compile error in bipartite_match_op<commit_after>/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve.

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

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

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

#include "paddle/framework/op_registry.h"
#include "paddle/operators/math/math_function.h"

namespace paddle {
namespace operators {

using Tensor = framework::Tensor;
using LoDTensor = framework::LoDTensor;

constexpr double kEPS = 1e-6;

class BipartiteMatchOp : public framework::OperatorWithKernel {
 public:
  using framework::OperatorWithKernel::OperatorWithKernel;

  void InferShape(framework::InferShapeContext* ctx) const override {
    PADDLE_ENFORCE(ctx->HasInput("DistMat"),
                   "Input(DistMat) of BipartiteMatch should not be null.");

    auto dims = ctx->GetInputDim("DistMat");
    PADDLE_ENFORCE_EQ(dims.size(), 2, "The rank of Input(DistMat) must be 2.");

    ctx->SetOutputDim("ColToRowMatchIndices", dims);
    ctx->SetOutputDim("ColToRowMatchDis", dims);
  }
};

template <typename T>
class BipartiteMatchKernel : public framework::OpKernel<T> {
 public:
  // The match_indices must be initialized to -1 at first.
  // The match_dist must be initialized to 0 at first.
  void BipartiteMatch(const Tensor& dist, int* match_indices,
                      T* match_dist) const {
    PADDLE_ENFORCE_EQ(dist.dims().size(), 2, "The rank of dist must be 2.");
    int64_t row = dist.dims()[0];
    int64_t col = dist.dims()[1];
    auto* dist_data = dist.data<T>();
    std::vector<int> row_pool;
    for (int i = 0; i < row; ++i) {
      row_pool.push_back(i);
    }
    while (row_pool.size() > 0) {
      int max_idx = -1;
      int max_row_idx = -1;
      T max_dist = -1;
      for (int64_t j = 0; j < col; ++j) {
        if (match_indices[j] != -1) {
          continue;
        }
        for (size_t k = 0; k < row_pool.size(); ++k) {
          int m = row_pool[k];
          // distance is 0 between m-th row and j-th column
          if (dist_data[m * col + j] < kEPS) {
            continue;
          }
          if (dist_data[m * col + j] > max_dist) {
            max_idx = j;
            max_row_idx = m;
            max_dist = dist_data[m * col + j];
          }
        }
      }
      if (max_idx == -1) {
        // Cannot find good match.
        break;
      } else {
        PADDLE_ENFORCE_EQ(match_indices[max_idx], -1);
        match_indices[max_idx] = max_row_idx;
        match_dist[max_idx] = max_dist;
        // Erase the row index.
        row_pool.erase(
            std::find(row_pool.begin(), row_pool.end(), max_row_idx));
      }
    }
  }

  void Compute(const framework::ExecutionContext& context) const override {
    auto* dist_mat = context.Input<LoDTensor>("DistMat");
    auto* match_indices = context.Output<Tensor>("ColToRowMatchIndices");
    auto* match_dist = context.Output<Tensor>("ColToRowMatchDis");

    auto& dev_ctx = context.device_context<platform::CPUDeviceContext>();

    auto col = dist_mat->dims()[1];

    int64_t n = dist_mat->lod().size() == 0UL
                    ? 1
                    : static_cast<int64_t>(dist_mat->lod().back().size() - 1);
    if (dist_mat->lod().size()) {
      PADDLE_ENFORCE_EQ(dist_mat->lod().size(), 1UL,
                        "Only support 1 level of LoD.");
    }
    match_indices->mutable_data<int>({n, col}, context.GetPlace());
    match_dist->mutable_data<T>({n, col}, context.GetPlace());

    math::SetConstant<platform::CPUDeviceContext, int> iset;
    iset(dev_ctx, match_indices, static_cast<int>(-1));
    math::SetConstant<platform::CPUDeviceContext, T> tset;
    tset(dev_ctx, match_dist, static_cast<T>(0));

    int* indices = match_indices->data<int>();
    T* dist = match_dist->data<T>();
    if (n == 1) {
      BipartiteMatch(*dist_mat, indices, dist);
    } else {
      auto lod = dist_mat->lod().back();
      for (size_t i = 0; i < lod.size() - 1; ++i) {
        Tensor one_ins = dist_mat->Slice(lod[i], lod[i + 1]);
        BipartiteMatch(one_ins, indices + i * col, dist + i * col);
      }
    }
  }
};

class BipartiteMatchOpMaker : public framework::OpProtoAndCheckerMaker {
 public:
  BipartiteMatchOpMaker(OpProto* proto, OpAttrChecker* op_checker)
      : OpProtoAndCheckerMaker(proto, op_checker) {
    AddInput(
        "DistMat",
        "(LoDTensor or Tensor) this input is a 2-D LoDTensor with shape "
        "[K, M]. It is pair-wise distance matrix between the entities "
        "represented by each row and each column. For example, assumed one "
        "entity is A with shape [K], another entity is B with shape [M]. The "
        "DistMat[i][j] is the distance between A[i] and B[j]. The bigger "
        "the distance is, the better macthing the pairs are. Please note, "
        "This tensor can contain LoD information to represent a batch of "
        "inputs. One instance of this batch can contain different numbers of "
        "entities.");
    AddOutput("ColToRowMatchIndices",
              "(Tensor) A 2-D Tensor with shape [N, M] in int type. "
              "N is the batch size. If ColToRowMatchIndices[i][j] is -1, it "
              "means B[j] does not match any entity in i-th instance. "
              "Otherwise, it means B[j] is matched to row "
              "ColToRowMatchIndices[i][j] in i-th instance. The row number of "
              "i-th instance is saved in ColToRowMatchIndices[i][j].");
    AddOutput("ColToRowMatchDis",
              "(Tensor) A 2-D Tensor with shape [N, M] in float type. "
              "N is batch size. If ColToRowMatchIndices[i][j] is -1, "
              "ColToRowMatchDis[i][j] is also -1.0. Otherwise, assumed "
              "ColToRowMatchIndices[i][j] = d, and the row offsets of each "
              "instance are called LoD. Then "
              "ColToRowMatchDis[i][j] = DistMat[d+LoD[i]][j]");
    AddComment(R"DOC(
This operator is a greedy bipartite matching algorithm, which is used to
obtain the matching with the maximum distance based on the input
distance matrix. For input 2D matrix, the bipartite matching algorithm can
find the matched column for each row, also can find the matched row for
each column. And this operator only calculate matched indices from column
to row. For each instance, the number of matched indices is the number of
of columns of the input ditance matrix.

There are two outputs to save matched indices and distance.
A simple description, this algothrim matched the best (maximum distance)
row entity to the column entity and the matched indices are not duplicated
in each row of ColToRowMatchIndices. If the column entity is not matched
any row entity, set -1 in ColToRowMatchIndices.

Please note that the input DistMat can be LoDTensor (with LoD) or Tensor.
If LoDTensor with LoD, the height of ColToRowMatchIndices is batch size.
If Tensor, the height of ColToRowMatchIndices is 1.

)DOC");
  }
};

}  // namespace operators
}  // namespace paddle

namespace ops = paddle::operators;
REGISTER_OPERATOR(bipartite_match, ops::BipartiteMatchOp,
                  ops::BipartiteMatchOpMaker,
                  paddle::framework::EmptyGradOpMaker);
REGISTER_OP_CPU_KERNEL(bipartite_match, ops::BipartiteMatchKernel<float>,
                       ops::BipartiteMatchKernel<double>);
<|endoftext|>


<commit_before>// Filename: ffmpegVirtualFile.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#ifdef HAVE_FFMPEG

#include "pandabase.h"
#include "config_movies.h"
#include "ffmpegVirtualFile.h"
#include "virtualFileSystem.h"
extern "C" {
  #include "libavformat/avio.h"
}

#ifndef AVSEEK_SIZE
  #define AVSEEK_SIZE 0x10000
#endif

////////////////////////////////////////////////////////////////////
// These functions need to use C calling conventions.
////////////////////////////////////////////////////////////////////
extern "C" {
  static int       pandavfs_open(URLContext *h, const char *filename, int flags);
  static int       pandavfs_read(URLContext *h, unsigned char *buf, int size);
// This change happened a few days before 52.68.0
#if LIBAVFORMAT_VERSION_INT < 3425280
  static int       pandavfs_write(URLContext *h, const unsigned char *buf, int size);
#else
  static int       pandavfs_write(URLContext *h, unsigned char *buf, int size);
#endif
  static PN_int64  pandavfs_seek(URLContext *h, PN_int64 pos, int whence);
  static int       pandavfs_close(URLContext *h);
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_open
//       Access: Static Function
//  Description: A hook to open a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_open(URLContext *h, const char *filename, int flags) {
  if (flags != 0) {
    movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
    return -1;
  }
  filename += 9; // Skip over "pandavfs:"
  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
  istream *s = vfs->open_read_file(filename, true);
  if (s == 0) {
    return -1;
  }
  // Test whether seek works.
  s->seekg(1, ios::beg);
  int tel1 = s->tellg();
  s->seekg(0, ios::beg);
  int tel2 = s->tellg();
  if (s->fail() || (tel1!=1) || (tel2!=0)) {
    movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
    delete s;
    return -1;
  }
  h->priv_data = s;
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_read
//       Access: Static Function
//  Description: A hook to read a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
  istream *s = (istream*)(h->priv_data);
  s->read((char*)buf, size);
  int gc = s->gcount();
  s->clear();
  return gc;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_write
//       Access: Static Function
//  Description: A hook to write a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
#if LIBAVFORMAT_VERSION_INT < 3425280
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
#else
pandavfs_write(URLContext *h, const unsigned char *buf, int size) {
#endif
  movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
  return -1;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_seek
//       Access: Static Function
//  Description: A hook to seek a panda VFS file.
////////////////////////////////////////////////////////////////////
static PN_int64
pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
  istream *s = (istream*)(h->priv_data);
  switch(whence) {
  case SEEK_SET: s->seekg(pos, ios::beg); break;
  case SEEK_CUR: s->seekg(pos, ios::cur); break;
  case SEEK_END: s->seekg(pos, ios::end); break;
  case AVSEEK_SIZE: {
    s->seekg(0, ios::cur);
    int p = s->tellg();
    s->seekg(-1, ios::end);
    int size = s->tellg();
    if (size < 0) {
      movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
      s->clear();
      return -1;
    }
    size++;
    s->seekg(p, ios::beg);
    s->clear();
    return size; }
  default:
    movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
    s->clear();
    return -1;
  }
  s->clear();
  int tl = s->tellg();
  return tl;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_close
//       Access: Static Function
//  Description: A hook to close a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_close(URLContext *h) {
  istream *s = (istream*)(h->priv_data);
  delete s;
  h->priv_data = 0;
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: FfmpegVirtualFile::register_protocol
//       Access: Public, Static
//  Description: Enables ffmpeg to access panda's VFS.
//
//               After calling this method, ffmpeg will be
//               able to open "URLs" that look like this:
//
//               pandavfs:/c/mygame/foo.avi
//
////////////////////////////////////////////////////////////////////
void FfmpegVirtualFile::
register_protocol() {
  static bool initialized = false;
  if (initialized) {
    return;
  }
  static URLProtocol protocol;
  protocol.name = "pandavfs";
  protocol.url_open  = pandavfs_open;
  protocol.url_read  = pandavfs_read;
  protocol.url_write = pandavfs_write;
  protocol.url_seek  = pandavfs_seek;
  protocol.url_close = pandavfs_close;
#if LIBAVFORMAT_VERSION_INT < 3415296
  ::register_protocol(&protocol);
#else
  av_register_protocol(&protocol);
#endif
}

#endif // HAVE_FFMPEG
<commit_msg>needs more cow bell<commit_after>// Filename: ffmpegVirtualFile.cxx
// Created by: jyelon (02Jul07)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University.  All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license.  You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////

#ifdef HAVE_FFMPEG

#include "pandabase.h"
#include "config_movies.h"
#include "ffmpegVirtualFile.h"
#include "virtualFileSystem.h"
extern "C" {
  #include "libavformat/avio.h"
}

#ifndef AVSEEK_SIZE
  #define AVSEEK_SIZE 0x10000
#endif

////////////////////////////////////////////////////////////////////
// These functions need to use C calling conventions.
////////////////////////////////////////////////////////////////////
extern "C" {
  static int       pandavfs_open(URLContext *h, const char *filename, int flags);
  static int       pandavfs_read(URLContext *h, unsigned char *buf, int size);
// This change happened a few days before 52.68.0
#if LIBAVFORMAT_VERSION_INT < 3425280
  static int       pandavfs_write(URLContext *h, unsigned char *buf, int size);
#else
  static int       pandavfs_write(URLContext *h, const unsigned char *buf, int size);
#endif
  static PN_int64  pandavfs_seek(URLContext *h, PN_int64 pos, int whence);
  static int       pandavfs_close(URLContext *h);
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_open
//       Access: Static Function
//  Description: A hook to open a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_open(URLContext *h, const char *filename, int flags) {
  if (flags != 0) {
    movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
    return -1;
  }
  filename += 9; // Skip over "pandavfs:"
  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
  istream *s = vfs->open_read_file(filename, true);
  if (s == 0) {
    return -1;
  }
  // Test whether seek works.
  s->seekg(1, ios::beg);
  int tel1 = s->tellg();
  s->seekg(0, ios::beg);
  int tel2 = s->tellg();
  if (s->fail() || (tel1!=1) || (tel2!=0)) {
    movies_cat.error() << "cannot play movie (not seekable): " << h->filename << "\n";
    delete s;
    return -1;
  }
  h->priv_data = s;
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_read
//       Access: Static Function
//  Description: A hook to read a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_read(URLContext *h, unsigned char *buf, int size) {
  istream *s = (istream*)(h->priv_data);
  s->read((char*)buf, size);
  int gc = s->gcount();
  s->clear();
  return gc;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_write
//       Access: Static Function
//  Description: A hook to write a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
#if LIBAVFORMAT_VERSION_INT < 3425280
pandavfs_write(URLContext *h, unsigned char *buf, int size) {
#else
pandavfs_write(URLContext *h, const unsigned char *buf, int size) {
#endif
  movies_cat.error() << "ffmpeg is trying to write to the VFS.\n";
  return -1;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_seek
//       Access: Static Function
//  Description: A hook to seek a panda VFS file.
////////////////////////////////////////////////////////////////////
static PN_int64
pandavfs_seek(URLContext *h, PN_int64 pos, int whence) {
  istream *s = (istream*)(h->priv_data);
  switch(whence) {
  case SEEK_SET: s->seekg(pos, ios::beg); break;
  case SEEK_CUR: s->seekg(pos, ios::cur); break;
  case SEEK_END: s->seekg(pos, ios::end); break;
  case AVSEEK_SIZE: {
    s->seekg(0, ios::cur);
    int p = s->tellg();
    s->seekg(-1, ios::end);
    int size = s->tellg();
    if (size < 0) {
      movies_cat.error() << "Failed to determine filesize in ffmpegVirtualFile\n";
      s->clear();
      return -1;
    }
    size++;
    s->seekg(p, ios::beg);
    s->clear();
    return size; }
  default:
    movies_cat.error() << "Illegal parameter to seek in ffmpegVirtualFile\n";
    s->clear();
    return -1;
  }
  s->clear();
  int tl = s->tellg();
  return tl;
}

////////////////////////////////////////////////////////////////////
//     Function: pandavfs_close
//       Access: Static Function
//  Description: A hook to close a panda VFS file.
////////////////////////////////////////////////////////////////////
static int
pandavfs_close(URLContext *h) {
  istream *s = (istream*)(h->priv_data);
  delete s;
  h->priv_data = 0;
  return 0;
}

////////////////////////////////////////////////////////////////////
//     Function: FfmpegVirtualFile::register_protocol
//       Access: Public, Static
//  Description: Enables ffmpeg to access panda's VFS.
//
//               After calling this method, ffmpeg will be
//               able to open "URLs" that look like this:
//
//               pandavfs:/c/mygame/foo.avi
//
////////////////////////////////////////////////////////////////////
void FfmpegVirtualFile::
register_protocol() {
  static bool initialized = false;
  if (initialized) {
    return;
  }
  static URLProtocol protocol;
  protocol.name = "pandavfs";
  protocol.url_open  = pandavfs_open;
  protocol.url_read  = pandavfs_read;
  protocol.url_write = pandavfs_write;
  protocol.url_seek  = pandavfs_seek;
  protocol.url_close = pandavfs_close;
#if LIBAVFORMAT_VERSION_INT < 3415296
  ::register_protocol(&protocol);
#else
  av_register_protocol(&protocol);
#endif
}

#endif // HAVE_FFMPEG
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014-2015 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <iomanip>
#include <iostream>
#include <limits>

#include "ConsoleBotView.hpp"

static int numberOfDigits(int number) {
    int digits = 0;
    while (number) {
        number /= 10;
        digits++;
    }
    return digits;
}

static void printSpaces(int spaces) {
    for (int i = 0; i < spaces; i++) {
        std::cout << " ";
    }
}

void ConsoleBotView::view() {
    while (true) {
        start();
        char mode;
        std::cin >> mode;
        switch (mode) {
            case 't':
                gameWithTime();
                break;
            case 'w':
                gameForWin();
                break;
            case 's':
                gameForScore();
                break;
            case 'q':
                return;
            default:
                sendHelpMessage_impl();
        }
    }
}

void ConsoleBotView::timeNumberMessage() const {
    std::cout << "How many minutes you want to play?" << std::endl;
    std::cout << "The default is " << Rules::DEFAULT_TIME
              << std::endl;
    prompt();
}

void ConsoleBotView::winNumberMessage() const {
    int square = getBoardsSquare();
    std::cout << "What score you want to finish the "
                 "game?" << std::endl;
    std::cout << "The default is " << square * 4
              << std::endl;
    prompt();
}

void ConsoleBotView::deskSizeMessage() const {
    std::cout << "Please enter size of the game board."
              << std::endl;
    std::cout << "Your board will have x * x square."
              << std::endl;
    std::cout << "(Where x is input size)" << std::endl;
    std::cout << "Minimum size is " << Rules::MIN_WIDTH
              << " and maximum is " << Rules::MAX_WIDTH
              << std::endl;
    std::cout << "Please note that you can change font's "
                 "size in your Terminal Preferences if"
              << std::endl;
    std::cout << "you have size mismatch of screen and "
                 "game board."
              << std::endl;
    prompt();
}

void ConsoleBotView::outputGeneral() const {
    int digits = numberOfDigits(maxDeskNumber());
    int width = Console::NUMBER_OF_SPACES + digits;
    int row_number = game_->desk->getRowNumber();
    for (int i = row_number - 1; i >= 0; i--) {
        std::cout << std::right
                  << std::setw(Console::MAX_INDEX_LENGTH)
                  << i << " ||";
        for (int x = 0; x < row_number; x++) {
            Point point;
            point.col = i;
            point.row = x;
            std::cout << std::right << std::setw(width)
                      << game_->desk->getDeskNumber(point);
        }
        std::cout << std::endl;
    }
    rowIndices(width);
    std::cout << std::endl;
}

void ConsoleBotView::prompt() const {
    std::cout << ">>> ";
}

void ConsoleBotView::typeError() const {
    std::cout << "Error: you must enter the NUMBER" << std::endl;
    std::cout << "Try again please." << std::endl;
    prompt();
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<int>::max(), '\n');
}

void ConsoleBotView::rangeError(TypeOfChecking type) const {
    int max_int = std::numeric_limits<int>::max();
    std::cout << "This number is out of allowable "
                 "range." << std::endl;
    if (type == TIME) {
        std::cout << "Please enter a POSITIVE INTEGER"
                  << std::endl;
        std::cout << "Maximum is " << max_int << std::endl;
    } else if (type == SCORE) {
        int square = getBoardsSquare();
        std::cout << "For this size of the game board "
                  << "minimum is " << square * 2 + 1
                  << std::endl;
        std::cout << "Maximum is " << max_int << std::endl;

    }
    std::cout << "Try again: " << std::endl;
    std::cin.clear();
    std::cin.ignore(max_int, '\n');
}

bool ConsoleBotView::checkRange(int verifiable,
                                TypeOfChecking type) const {
    switch (type) {
        case BOARDS_SIZE:
            return ((verifiable >= Rules::MIN_WIDTH) &&
                    (verifiable <= Rules::MAX_WIDTH));
        case TIME:
            // By checking that verifiable is greater than
            // zero we make sure that it's less or equal
            // to the allowable maximum for int also.
            // See cyclic mechanism of ints in C++.
            return (verifiable > 0);
        case SCORE:
            int square = getBoardsSquare();
            return (verifiable > square * 2);
    }
}

int ConsoleBotView::getBoardsSquare() const {
    int boards_size = game_->desk->getRowNumber();
    return boards_size * boards_size;
}

const GameDesk* ConsoleBotView::getDesk() const {
    return game_->desk.data();
}

void ConsoleBotView::finish_impl(bool fail, int score,
                                 int steps_number) const {
    if (fail) {
        std::cout << "You are loser... Your score is " << score << std::endl;
    } else {
        std::cout << "You are winner! Your score is " << score << std::endl;
    }
    std::cout << "You have completed the game in " << steps_number << " steps." << std::endl;
}

void ConsoleBotView::sendHelpMessage_impl() const {
    std::cout << "You must enter t, w, s or q" << std::endl;
    std::cout << "Try again please." << std::endl;
}

void ConsoleBotView::startGame_impl(int row_number) {
    try {
        game_ = Game::make(row_number);
        game_->controller->initialStateOfBoard();
    } catch (std::exception& e) {
        errorHandling_impl(e);
    }
}

void ConsoleBotView::errorHandling_impl(std::exception& e) const {
    std::cout << e.what() << std::endl;
}

int ConsoleBotView::maxDeskNumber() const {
    const GameDesk* desk = game_->desk.data();
    int boards_size = desk->getRowNumber();
    int max = 0;
    for (int i = boards_size - 1; i >= 0; i--) {
        for (int x = 0; x < boards_size; x++) {
            Point pt;
            pt.col = i;
            pt.row = x;
            int current_number = desk->getDeskNumber(pt);
            if (current_number > max) {
                max = current_number;
            }
        }
    }
    return max;
}

void ConsoleBotView::rowIndices(int width) const {
    int boards_size = game_->desk->getRowNumber();
    printSpaces(Console::INDEX_FIELD);
    for (int i = 0; i < width * boards_size; i++) {
        std::cout << "=";
    }
    std::cout << std::endl;
    printSpaces(Console::INDEX_FIELD);
    for (int i = 0; i < boards_size; i++) {
        std::cout << std::right << std::setw(width) << i;
    }
}

void ConsoleBotView::start() const {
    std::cout << "*** BIN_GAME ***" << std::endl;
    std::cout << "----------------" << std::endl;
    std::cout << "t: time mode | w: play for score | s: "
                 "play while not lose" << std::endl;
    std::cout << "q: quit" << std::endl;
    prompt();
}

void ConsoleBotView::gameForWin() {
    int desk_size = getDeskSize_impl();
    startGame_impl(desk_size);
    int win_number = getWinNumber_impl();
    const GameDesk* desk = game_->desk.data();
    output_impl();
    int steps_number = 0;
    while (!checkFail(*desk) && !checkWin(*desk,
                                          win_number)) {
        steps_number += 1;
        play();
    }
    finish_impl(checkFail(*desk), score(*desk),
                steps_number);
}

void ConsoleBotView::gameForScore() {
    int desk_size = getDeskSize_impl();
    startGame_impl(desk_size);
    const GameDesk* desk = game_->desk.data();
    output_impl();
    int steps_number = 0;
    while (!checkFail(*desk)) {
        steps_number += 1;
        play();
    }
    finish_impl(checkFail(*desk), score(*desk),
                steps_number);
}

void ConsoleBotView::gameWithTime() {
    int desk_size = getDeskSize_impl();
    int time_number = getTimeNumber_impl();
    startGame_impl(desk_size);
    int t1 = time(NULL);
    int t2 = 0;
    const GameDesk* desk = game_->desk.data();
    output_impl();
    int steps_number = 0;
    while (!checkFail(*desk) &&
           ((t2 - t1) < time_number * 60)) {
        steps_number += 1;
        play();
        t2 = time(NULL);
    }
    finish_impl(checkFail(*desk), score(*desk),
                steps_number);
}

void ConsoleBotView::play() {
    Points points = getIndex_impl();
    try {
        game_->controller->replace(points);
    } catch (std::exception& e) {
        errorHandling_impl(e);
    }
    output_impl();
}
<commit_msg>Console: print prompt in rangeError()<commit_after>/*
 * Copyright (C) 2014-2015 Pavel Dolgov
 *
 * See the LICENSE file for terms of use.
 */

#include <iomanip>
#include <iostream>
#include <limits>

#include "ConsoleBotView.hpp"

static int numberOfDigits(int number) {
    int digits = 0;
    while (number) {
        number /= 10;
        digits++;
    }
    return digits;
}

static void printSpaces(int spaces) {
    for (int i = 0; i < spaces; i++) {
        std::cout << " ";
    }
}

void ConsoleBotView::view() {
    while (true) {
        start();
        char mode;
        std::cin >> mode;
        switch (mode) {
            case 't':
                gameWithTime();
                break;
            case 'w':
                gameForWin();
                break;
            case 's':
                gameForScore();
                break;
            case 'q':
                return;
            default:
                sendHelpMessage_impl();
        }
    }
}

void ConsoleBotView::timeNumberMessage() const {
    std::cout << "How many minutes you want to play?" << std::endl;
    std::cout << "The default is " << Rules::DEFAULT_TIME
              << std::endl;
    prompt();
}

void ConsoleBotView::winNumberMessage() const {
    int square = getBoardsSquare();
    std::cout << "What score you want to finish the "
                 "game?" << std::endl;
    std::cout << "The default is " << square * 4
              << std::endl;
    prompt();
}

void ConsoleBotView::deskSizeMessage() const {
    std::cout << "Please enter size of the game board."
              << std::endl;
    std::cout << "Your board will have x * x square."
              << std::endl;
    std::cout << "(Where x is input size)" << std::endl;
    std::cout << "Minimum size is " << Rules::MIN_WIDTH
              << " and maximum is " << Rules::MAX_WIDTH
              << std::endl;
    std::cout << "Please note that you can change font's "
                 "size in your Terminal Preferences if"
              << std::endl;
    std::cout << "you have size mismatch of screen and "
                 "game board."
              << std::endl;
    prompt();
}

void ConsoleBotView::outputGeneral() const {
    int digits = numberOfDigits(maxDeskNumber());
    int width = Console::NUMBER_OF_SPACES + digits;
    int row_number = game_->desk->getRowNumber();
    for (int i = row_number - 1; i >= 0; i--) {
        std::cout << std::right
                  << std::setw(Console::MAX_INDEX_LENGTH)
                  << i << " ||";
        for (int x = 0; x < row_number; x++) {
            Point point;
            point.col = i;
            point.row = x;
            std::cout << std::right << std::setw(width)
                      << game_->desk->getDeskNumber(point);
        }
        std::cout << std::endl;
    }
    rowIndices(width);
    std::cout << std::endl;
}

void ConsoleBotView::prompt() const {
    std::cout << ">>> ";
}

void ConsoleBotView::typeError() const {
    std::cout << "Error: you must enter the NUMBER" << std::endl;
    std::cout << "Try again please." << std::endl;
    prompt();
    std::cin.clear();
    std::cin.ignore(std::numeric_limits<int>::max(), '\n');
}

void ConsoleBotView::rangeError(TypeOfChecking type) const {
    int max_int = std::numeric_limits<int>::max();
    std::cout << "This number is out of allowable "
                 "range." << std::endl;
    if (type == TIME) {
        std::cout << "Please enter a POSITIVE INTEGER"
                  << std::endl;
        std::cout << "Maximum is " << max_int << std::endl;
    } else if (type == SCORE) {
        int square = getBoardsSquare();
        std::cout << "For this size of the game board "
                  << "minimum is " << square * 2 + 1
                  << std::endl;
        std::cout << "Maximum is " << max_int << std::endl;

    }
    std::cout << "Try again please." << std::endl;
    prompt();
    std::cin.clear();
    std::cin.ignore(max_int, '\n');
}

bool ConsoleBotView::checkRange(int verifiable,
                                TypeOfChecking type) const {
    switch (type) {
        case BOARDS_SIZE:
            return ((verifiable >= Rules::MIN_WIDTH) &&
                    (verifiable <= Rules::MAX_WIDTH));
        case TIME:
            // By checking that verifiable is greater than
            // zero we make sure that it's less or equal
            // to the allowable maximum for int also.
            // See cyclic mechanism of ints in C++.
            return (verifiable > 0);
        case SCORE:
            int square = getBoardsSquare();
            return (verifiable > square * 2);
    }
}

int ConsoleBotView::getBoardsSquare() const {
    int boards_size = game_->desk->getRowNumber();
    return boards_size * boards_size;
}

const GameDesk* ConsoleBotView::getDesk() const {
    return game_->desk.data();
}

void ConsoleBotView::finish_impl(bool fail, int score,
                                 int steps_number) const {
    if (fail) {
        std::cout << "You are loser... Your score is " << score << std::endl;
    } else {
        std::cout << "You are winner! Your score is " << score << std::endl;
    }
    std::cout << "You have completed the game in " << steps_number << " steps." << std::endl;
}

void ConsoleBotView::sendHelpMessage_impl() const {
    std::cout << "You must enter t, w, s or q" << std::endl;
    std::cout << "Try again please." << std::endl;
}

void ConsoleBotView::startGame_impl(int row_number) {
    try {
        game_ = Game::make(row_number);
        game_->controller->initialStateOfBoard();
    } catch (std::exception& e) {
        errorHandling_impl(e);
    }
}

void ConsoleBotView::errorHandling_impl(std::exception& e) const {
    std::cout << e.what() << std::endl;
}

int ConsoleBotView::maxDeskNumber() const {
    const GameDesk* desk = game_->desk.data();
    int boards_size = desk->getRowNumber();
    int max = 0;
    for (int i = boards_size - 1; i >= 0; i--) {
        for (int x = 0; x < boards_size; x++) {
            Point pt;
            pt.col = i;
            pt.row = x;
            int current_number = desk->getDeskNumber(pt);
            if (current_number > max) {
                max = current_number;
            }
        }
    }
    return max;
}

void ConsoleBotView::rowIndices(int width) const {
    int boards_size = game_->desk->getRowNumber();
    printSpaces(Console::INDEX_FIELD);
    for (int i = 0; i < width * boards_size; i++) {
        std::cout << "=";
    }
    std::cout << std::endl;
    printSpaces(Console::INDEX_FIELD);
    for (int i = 0; i < boards_size; i++) {
        std::cout << std::right << std::setw(width) << i;
    }
}

void ConsoleBotView::start() const {
    std::cout << "*** BIN_GAME ***" << std::endl;
    std::cout << "----------------" << std::endl;
    std::cout << "t: time mode | w: play for score | s: "
                 "play while not lose" << std::endl;
    std::cout << "q: quit" << std::endl;
    prompt();
}

void ConsoleBotView::gameForWin() {
    int desk_size = getDeskSize_impl();
    startGame_impl(desk_size);
    int win_number = getWinNumber_impl();
    const GameDesk* desk = game_->desk.data();
    output_impl();
    int steps_number = 0;
    while (!checkFail(*desk) && !checkWin(*desk,
                                          win_number)) {
        steps_number += 1;
        play();
    }
    finish_impl(checkFail(*desk), score(*desk),
                steps_number);
}

void ConsoleBotView::gameForScore() {
    int desk_size = getDeskSize_impl();
    startGame_impl(desk_size);
    const GameDesk* desk = game_->desk.data();
    output_impl();
    int steps_number = 0;
    while (!checkFail(*desk)) {
        steps_number += 1;
        play();
    }
    finish_impl(checkFail(*desk), score(*desk),
                steps_number);
}

void ConsoleBotView::gameWithTime() {
    int desk_size = getDeskSize_impl();
    int time_number = getTimeNumber_impl();
    startGame_impl(desk_size);
    int t1 = time(NULL);
    int t2 = 0;
    const GameDesk* desk = game_->desk.data();
    output_impl();
    int steps_number = 0;
    while (!checkFail(*desk) &&
           ((t2 - t1) < time_number * 60)) {
        steps_number += 1;
        play();
        t2 = time(NULL);
    }
    finish_impl(checkFail(*desk), score(*desk),
                steps_number);
}

void ConsoleBotView::play() {
    Points points = getIndex_impl();
    try {
        game_->controller->replace(points);
    } catch (std::exception& e) {
        errorHandling_impl(e);
    }
    output_impl();
}
<|endoftext|>