<commit_before>// Copyright (c) 2012-2015 Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdlib>
#include <string>
#include <subhook.h>
#include "jithandler.h"
#include "logprintf.h"
#include "os.h"
#include "plugin.h"
#include "version.h"

typedef int (AMXAPI *AMX_EXEC)(AMX *amx, cell *retval, int index);

extern void *pAMXFunctions;
static subhook::Hook exec_hook;

#ifdef LINUX
  static cell *opcode_table = NULL;
#endif

namespace {

void *GetAmxFunction(int index) {
  return static_cast<void**>(pAMXFunctions)[index];
}

std::string GetFileName(const std::string &path) {
  std::string::size_type pos = path.find_last_of("/\\");
  if (pos != std::string::npos) {
    return path.substr(pos + 1);
  }
  return path;
}

int AMXAPI amx_Exec_JIT(AMX *amx, cell *retval, int index) {
  #ifdef LINUX
    if ((amx->flags & AMX_FLAG_BROWSE) == AMX_FLAG_BROWSE) {
      assert(::opcode_table != 0);
      *retval = reinterpret_cast<cell>(::opcode_table);
      return AMX_ERR_NONE;
    }
  #endif
  int error = JITHandler::GetHandler(amx)->Exec(retval, index);
  if (error == AMX_ERR_INIT_JIT) {
    AMX_EXEC exec = (AMX_EXEC)exec_hook.GetTrampoline();
    return exec(amx, retval, index);
  }
  return error;
}

cell AMX_NATIVE_CALL n_JITSleep(AMX *amx, cell *params) {
  amx->error = AMX_ERR_SLEEP;
  return 0;
}

} // anonymous namespace

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
  return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
  logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];
  pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];

  void *exec_start = GetAmxFunction(PLUGIN_AMX_EXPORT_Exec);
  void *other_guy = subhook::Hook::ReadDst(exec_start);

  if (other_guy != 0) {
    std::string module = GetFileName(os::GetModuleName(other_guy));
    if (!module.empty()) {
      logprintf("  JIT plugin must be loaded before '%s'", module.c_str());
    } else {
      logprintf("  Sorry, your server is messed up");
    }
    return false;
  }

  #ifdef LINUX
    // Get the opcode table before we hook amx_Exec().
    AMX amx = {0};
    amx.flags |= AMX_FLAG_BROWSE;
    amx_Exec(&amx, reinterpret_cast<cell*>(&::opcode_table), 0);
    amx.flags &= ~AMX_FLAG_BROWSE;
  #endif
  exec_hook.Install(exec_start, (void *)amx_Exec_JIT);

  logprintf("  JIT plugin %s", PROJECT_VERSION_STRING);
  return true;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
  if (std::getenv("JIT_TEST") != NULL) {
    const AMX_NATIVE_INFO natives[] = {
      {"JITSleep", n_JITSleep}
    };
    amx_Register(amx, natives, sizeof(natives) / sizeof(natives[0]));
  }
  return AMX_ERR_NONE;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
 JITHandler::DestroyHandler(amx);
 return AMX_ERR_NONE;
}
<commit_msg>Remove JITSleep native<commit_after>// Copyright (c) 2012-2015 Zeex
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

#include <cassert>
#include <cstdlib>
#include <string>
#include <subhook.h>
#include "jithandler.h"
#include "logprintf.h"
#include "os.h"
#include "plugin.h"
#include "version.h"

typedef int (AMXAPI *AMX_EXEC)(AMX *amx, cell *retval, int index);

extern void *pAMXFunctions;
static subhook::Hook exec_hook;

#ifdef LINUX
  static cell *opcode_table = NULL;
#endif

namespace {

void *GetAmxFunction(int index) {
  return static_cast<void**>(pAMXFunctions)[index];
}

std::string GetFileName(const std::string &path) {
  std::string::size_type pos = path.find_last_of("/\\");
  if (pos != std::string::npos) {
    return path.substr(pos + 1);
  }
  return path;
}

int AMXAPI amx_Exec_JIT(AMX *amx, cell *retval, int index) {
  #ifdef LINUX
    if ((amx->flags & AMX_FLAG_BROWSE) == AMX_FLAG_BROWSE) {
      assert(::opcode_table != 0);
      *retval = reinterpret_cast<cell>(::opcode_table);
      return AMX_ERR_NONE;
    }
  #endif
  int error = JITHandler::GetHandler(amx)->Exec(retval, index);
  if (error == AMX_ERR_INIT_JIT) {
    AMX_EXEC exec = (AMX_EXEC)exec_hook.GetTrampoline();
    return exec(amx, retval, index);
  }
  return error;
}

} // anonymous namespace

PLUGIN_EXPORT unsigned int PLUGIN_CALL Supports() {
  return SUPPORTS_VERSION | SUPPORTS_AMX_NATIVES;
}

PLUGIN_EXPORT bool PLUGIN_CALL Load(void **ppData) {
  logprintf = (logprintf_t)ppData[PLUGIN_DATA_LOGPRINTF];
  pAMXFunctions = ppData[PLUGIN_DATA_AMX_EXPORTS];

  void *exec_start = GetAmxFunction(PLUGIN_AMX_EXPORT_Exec);
  void *other_guy = subhook::Hook::ReadDst(exec_start);

  if (other_guy != 0) {
    std::string module = GetFileName(os::GetModuleName(other_guy));
    if (!module.empty()) {
      logprintf("  JIT plugin must be loaded before '%s'", module.c_str());
    } else {
      logprintf("  Sorry, your server is messed up");
    }
    return false;
  }

  #ifdef LINUX
    // Get the opcode table before we hook amx_Exec().
    AMX amx = {0};
    amx.flags |= AMX_FLAG_BROWSE;
    amx_Exec(&amx, reinterpret_cast<cell*>(&::opcode_table), 0);
    amx.flags &= ~AMX_FLAG_BROWSE;
  #endif
  exec_hook.Install(exec_start, (void *)amx_Exec_JIT);

  logprintf("  JIT plugin %s", PROJECT_VERSION_STRING);
  return true;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxLoad(AMX *amx) {
  return AMX_ERR_NONE;
}

PLUGIN_EXPORT int PLUGIN_CALL AmxUnload(AMX *amx) {
 JITHandler::DestroyHandler(amx);
 return AMX_ERR_NONE;
}
<|endoftext|>


<commit_before>/**
 * KQOAuth - An OAuth authentication library for Qt.
 *
 * Author: Johan Paul (johan.paul@gmail.com)
 *         http://www.johanpaul.com
 *
 *  KQOAuth 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  KQOAuth 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 KQOAuth.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <QByteArray>
#include <QDateTime>
#include <QCryptographicHash>
#include <QPair>
#include <QStringList>

#include <QtDebug>
#include <QtAlgorithms>

#include "kqoauthrequest.h"
#include "kqoauthrequest_p.h"
#include "kqoauthutils.h"
#include "kqoauthglobals.h"

//////////// Private d_ptr implementation /////////

KQOAuthRequestPrivate::KQOAuthRequestPrivate()
{

}

KQOAuthRequestPrivate::~KQOAuthRequestPrivate()
{

}

// This method will not include the "oauthSignature" paramater, since it is calculated from these parameters.
void KQOAuthRequestPrivate::prepareRequest() {

    // If parameter list is not empty, we don't want to insert these values by
    // accident a second time. So giving up.
    if( !requestParameters.isEmpty() ) {
        return;
    }

    switch ( requestType ) {
    case KQOAuthRequest::TemporaryCredentials:
        requestParameters.append( qMakePair( OAUTH_KEY_CALLBACK, QString(QUrl::toPercentEncoding( oauthCallbackUrl.toString()) )));  // This is so ugly that it is almost beautiful.
        requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
        requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
        requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
        requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
        requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
        insertAdditionalParams(requestParameters);
        break;

    case KQOAuthRequest::AccessToken:
        requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
        requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
        requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
        requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
        requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
        requestParameters.append( qMakePair( OAUTH_KEY_VERIFIER, oauthVerifier ));
        requestParameters.append( qMakePair( OAUTH_KEY_TOKEN, oauthToken ));
        insertAdditionalParams(requestParameters);
        break;

    case KQOAuthRequest::AuthorizedRequest:
        requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
        requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
        requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
        requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
        requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
        requestParameters.append( qMakePair( OAUTH_KEY_TOKEN, oauthToken ));
        insertAdditionalParams(requestParameters);
        break;

    default:
        break;
    }
}

void KQOAuthRequestPrivate::insertAdditionalParams(QList< QPair<QString, QString> > &requestParams) {
    QList<QString> additionalKeys = this->additionalParams.keys();
    QList<QString> additionalValues = this->additionalParams.values();

    for(int i=0; i<additionalKeys.size(); i++) {
        requestParams.append( qMakePair(QString(QUrl::toPercentEncoding(additionalKeys.at(i))),
                                        QString(QUrl::toPercentEncoding(additionalValues.at(i))))
                             );
    }

    if (oauthHttpMethod == KQOAuthRequest::POST) {
        insertPostBody();
    }
}

void KQOAuthRequestPrivate::insertPostBody() {
    QList<QString> postBodyKeys = this->additionalParams.keys();
    QList<QString> postBodyValues = this->additionalParams.values();

    postBodyContent.clear();
    bool first = true;
    for(int i=0; i<postBodyKeys.size(); i++) {
        if(!first) {
            postBodyContent.append("&");
        } else {
            first = false;
        }

        QString key = postBodyKeys.at(i);
        QString value = postBodyValues.at(i);

        postBodyContent.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() +
                               QUrl::toPercentEncoding(value));
    }

}

void KQOAuthRequestPrivate::signRequest() {
    QString signature = this->oauthSignature();
    requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE, signature) );
}

QString KQOAuthRequestPrivate::oauthSignature()  {
    QByteArray baseString = this->requestBaseString();

    QString signature = KQOAuthUtils::hmac_sha1(baseString, oauthConsumerSecretKey + "&" + oauthTokenSecret);
    return QString( QUrl::toPercentEncoding( signature ) );
}

bool normalizedParameterSort(const QPair<QString, QString> &left, const QPair<QString, QString> &right) {
    QString keyLeft = left.first;
    QString valueLeft = left.second;
    QString keyRight = right.first;
    QString valueRight = right.second;

    if(keyLeft == keyRight) {
        return (valueLeft < valueRight);
    } else {
        return (keyLeft < keyRight);
    }
}
QByteArray KQOAuthRequestPrivate::requestBaseString() {
    QByteArray baseString;

    // Every request has these as the commont parameters.
    baseString.append( oauthHttpMethodString.toUtf8() + "&");                                                     // HTTP method
    baseString.append( QUrl::toPercentEncoding( oauthRequestEndpoint.toString(QUrl::RemoveQuery) ) + "&" ); // The path and query components

    QList< QPair<QString, QString> > baseStringParameters;
    baseStringParameters.append(requestParameters);

    // Sort the request parameters. These parameters have been
    // initialized earlier.
    qSort(baseStringParameters.begin(),
          baseStringParameters.end(),
          normalizedParameterSort
          );

    // Last append the request parameters correctly encoded.
    baseString.append( encodedParamaterList(baseStringParameters) );

    return baseString;
}

QByteArray KQOAuthRequestPrivate::encodedParamaterList(const QList< QPair<QString, QString> > &parameters) {
    QByteArray resultList;

    bool first = true;
    QPair<QString, QString> parameter;
    foreach (parameter, parameters) {
        if(!first) {
            resultList.append( "%26" );
        } else {
            first = false;
        }

        // Here we don't need to explicitely encode the strings to UTF-8 since
        // QUrl::toPercentEncoding() takes care of that for us.
        resultList.append( QUrl::toPercentEncoding(parameter.first)     // Parameter key
                           + "%3D"                                      // '=' encoded
                           + QUrl::toPercentEncoding(parameter.second)  // Parameter value
                          );
    }

    return resultList;
}

QString KQOAuthRequestPrivate::oauthTimestamp() const {
    // This is basically for unit tests only. In most cases we don't set the nonce beforehand.
    if (!oauthTimestamp_.isEmpty()) {
        return oauthTimestamp_;
    }

#if QT_VERSION >= 0x040700
    return QString::number(QDateTime::currentDateTimeUtc().toTime_t());
#else
   return QString::number(QDateTime::currentDateTime().toUTC().toTime_t());
#endif

}

QString KQOAuthRequestPrivate::oauthNonce() const {
    // This is basically for unit tests only. In most cases we don't set the nonce beforehand.
    if (!oauthNonce_.isEmpty()) {
        return oauthNonce_;
    }

    QString nonceTimestamp = oauthTimestamp_;

    if (nonceTimestamp.isEmpty()) {
        nonceTimestamp = oauthTimestamp();
    }

    return QCryptographicHash::hash(nonceTimestamp.toAscii(), QCryptographicHash::Md5).toHex();
}

bool KQOAuthRequestPrivate::validateRequest() const {
    switch ( requestType ) {
    case KQOAuthRequest::TemporaryCredentials:
        if (oauthRequestEndpoint.isEmpty()
            || oauthConsumerKey.isEmpty()
            || oauthNonce_.isEmpty()
            || oauthSignatureMethod.isEmpty()
            || oauthTimestamp_.isEmpty()
            || oauthVersion.isEmpty())
        {
            return false;
        }
        return true;

    case KQOAuthRequest::AccessToken:
        if (oauthRequestEndpoint.isEmpty()
            ||  oauthVerifier.isEmpty()
            || oauthConsumerKey.isEmpty()
            || oauthNonce_.isEmpty()
            || oauthSignatureMethod.isEmpty()
            || oauthTimestamp_.isEmpty()
            || oauthToken.isEmpty()
            || oauthTokenSecret.isEmpty()
            || oauthVersion.isEmpty())
        {
            return false;
        }
        return true;

    case KQOAuthRequest::AuthorizedRequest:
        if (oauthRequestEndpoint.isEmpty()
            || oauthConsumerKey.isEmpty()
            || oauthNonce_.isEmpty()
            || oauthSignatureMethod.isEmpty()
            || oauthTimestamp_.isEmpty()
            || oauthToken.isEmpty()
            || oauthTokenSecret.isEmpty()
            || oauthVersion.isEmpty())
        {
            return false;
        }
        return true;

    default:
        return false;
    }

    // We should not come here.
    return false;
}

QByteArray KQOAuthRequestPrivate::requestBody() const {
    return postBodyContent;
}

//////////// Public implementation ////////////////

KQOAuthRequest::KQOAuthRequest(QObject *parent) :
    QObject(parent),
    d_ptr(new KQOAuthRequestPrivate)
{
}

KQOAuthRequest::~KQOAuthRequest()
{
    delete d_ptr;
}

void KQOAuthRequest::initRequest(KQOAuthRequest::RequestType type, const QUrl &requestEndpoint) {
    Q_D(KQOAuthRequest);

    if (!requestEndpoint.isValid()) {
        qWarning() << "Endpoint URL is not valid. Ignoring. This request might not work.";
        return;
    }

    if (type < 0 || type > KQOAuthRequest::AuthorizedRequest) {
        qWarning() << "Invalid request type. Ignoring. This request might not work.";
        return;
    }

    // Set smart defaults.
    d->requestType = type;
    d->oauthRequestEndpoint = requestEndpoint;
    d->oauthTimestamp_ = d->oauthTimestamp();
    d->oauthNonce_ = d->oauthNonce();
    this->setSignatureMethod(KQOAuthRequest::HMAC_SHA1);
    this->setHttpMethod(KQOAuthRequest::POST);
    d->oauthVersion = "1.0"; // Currently supports only version 1.0
}

void KQOAuthRequest::setConsumerKey(const QString &consumerKey) {
    Q_D(KQOAuthRequest);
    d->oauthConsumerKey = consumerKey;
}

void KQOAuthRequest::setConsumerSecretKey(const QString &consumerSecretKey) {
    Q_D(KQOAuthRequest);
    d->oauthConsumerSecretKey = consumerSecretKey;
}

void KQOAuthRequest::setCallbackUrl(const QUrl &callbackUrl) {
    Q_D(KQOAuthRequest);

    d->oauthCallbackUrl = callbackUrl;
}

void KQOAuthRequest::setSignatureMethod(KQOAuthRequest::RequestSignatureMethod requestMethod) {
    Q_D(KQOAuthRequest);
    QString requestMethodString;

    switch (requestMethod) {
    case KQOAuthRequest::PLAINTEXT:
        requestMethodString = "PLAINTEXT";
        break;
    case KQOAuthRequest::HMAC_SHA1:
        requestMethodString = "HMAC-SHA1";
        break;
    case KQOAuthRequest::RSA_SHA1:
        requestMethodString = "RSA-SHA1";
        break;
    default:
        // We should not come here
        qWarning() << "Invalid signature method set.";
        break;
    }

    d->oauthSignatureMethod = requestMethodString;
}

void KQOAuthRequest::setTokenSecret(const QString &tokenSecret) {
    Q_D(KQOAuthRequest);

    d->oauthTokenSecret = tokenSecret;
}

void KQOAuthRequest::setToken(const QString &token) {
    Q_D(KQOAuthRequest);

    d->oauthToken = token;
}

void KQOAuthRequest::setVerifier(const QString &verifier) {
    Q_D(KQOAuthRequest);

    d->oauthVerifier = verifier;
}

void KQOAuthRequest::setHttpMethod(KQOAuthRequest::RequestHttpMethod httpMethod) {
    Q_D(KQOAuthRequest);

    QString requestHttpMethodString;

    switch (httpMethod) {
    case KQOAuthRequest::GET:
        requestHttpMethodString = "GET";
        break;
    case KQOAuthRequest::POST:
        requestHttpMethodString = "POST";
        break;
    default:
        qWarning() << "Invalid HTTP method set.";
        break;
    }

    d->oauthHttpMethod = httpMethod;
    d->oauthHttpMethodString = requestHttpMethodString;
}

KQOAuthRequest::RequestHttpMethod KQOAuthRequest::httpMethod() const {
    Q_D(const KQOAuthRequest);

    return d->oauthHttpMethod;
}

void KQOAuthRequest::setAdditionalParameters(const KQOAuthParameters &additionalParams) {
    Q_D(KQOAuthRequest);

    d->additionalParams = additionalParams;
}

KQOAuthParameters KQOAuthRequest::additionalParameters() const {
    Q_D(const KQOAuthRequest);

    return d->additionalParams;
}

KQOAuthRequest::RequestType KQOAuthRequest::requestType() const {
    Q_D(const KQOAuthRequest);

    return d->requestType;
}

QUrl KQOAuthRequest::requestEndpoint() const {
    Q_D(const KQOAuthRequest);

    return d->oauthRequestEndpoint;
}

QList<QByteArray> KQOAuthRequest::requestParameters() {
    Q_D(KQOAuthRequest);

    QList<QByteArray> requestParamList;

    d->prepareRequest();
    if (!d->validateRequest() ) {
        qWarning() << "Request is not valid! I will still sign it, but it will probably not work.";
    }
    d->signRequest();

    QPair<QString, QString> requestParam;
    QString param;
    QString value;
    foreach (requestParam, d->requestParameters) {
        param = requestParam.first;
        value = requestParam.second;
        requestParamList.append(QString(param + "=\"" + value +"\"").toUtf8());
    }

    return requestParamList;
}

bool KQOAuthRequest::isValid() const {
    Q_D(const KQOAuthRequest);

    return d->validateRequest();
}

void KQOAuthRequest::clearRequest() {
    Q_D(KQOAuthRequest);

    d->oauthRequestEndpoint = "";
    d->oauthHttpMethodString = "";
    d->oauthConsumerKey = "";
    d->oauthConsumerSecretKey = "";
    d->oauthToken = "";
    d->oauthTokenSecret = "";
    d->oauthSignatureMethod = "";
    d->oauthCallbackUrl = "";
    d->oauthVerifier = "";
    d->oauthTimestamp_ = "";
    d->oauthNonce_ = "";
    d->requestParameters.clear();
    d->additionalParams.clear();
}

<commit_msg>When initing the request, we should clear old data too.<commit_after>/**
 * KQOAuth - An OAuth authentication library for Qt.
 *
 * Author: Johan Paul (johan.paul@gmail.com)
 *         http://www.johanpaul.com
 *
 *  KQOAuth 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 3 of the License, or
 *  (at your option) any later version.
 *
 *  KQOAuth 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 KQOAuth.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <QByteArray>
#include <QDateTime>
#include <QCryptographicHash>
#include <QPair>
#include <QStringList>

#include <QtDebug>
#include <QtAlgorithms>

#include "kqoauthrequest.h"
#include "kqoauthrequest_p.h"
#include "kqoauthutils.h"
#include "kqoauthglobals.h"

//////////// Private d_ptr implementation /////////

KQOAuthRequestPrivate::KQOAuthRequestPrivate()
{

}

KQOAuthRequestPrivate::~KQOAuthRequestPrivate()
{

}

// This method will not include the "oauthSignature" paramater, since it is calculated from these parameters.
void KQOAuthRequestPrivate::prepareRequest() {

    // If parameter list is not empty, we don't want to insert these values by
    // accident a second time. So giving up.
    if( !requestParameters.isEmpty() ) {
        return;
    }

    switch ( requestType ) {
    case KQOAuthRequest::TemporaryCredentials:
        requestParameters.append( qMakePair( OAUTH_KEY_CALLBACK, QString(QUrl::toPercentEncoding( oauthCallbackUrl.toString()) )));  // This is so ugly that it is almost beautiful.
        requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
        requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
        requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
        requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
        requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
        insertAdditionalParams(requestParameters);
        break;

    case KQOAuthRequest::AccessToken:
        requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
        requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
        requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
        requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
        requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
        requestParameters.append( qMakePair( OAUTH_KEY_VERIFIER, oauthVerifier ));
        requestParameters.append( qMakePair( OAUTH_KEY_TOKEN, oauthToken ));
        insertAdditionalParams(requestParameters);
        break;

    case KQOAuthRequest::AuthorizedRequest:
        requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE_METHOD, oauthSignatureMethod ));
        requestParameters.append( qMakePair( OAUTH_KEY_CONSUMER_KEY, oauthConsumerKey ));
        requestParameters.append( qMakePair( OAUTH_KEY_VERSION, oauthVersion ));
        requestParameters.append( qMakePair( OAUTH_KEY_TIMESTAMP, this->oauthTimestamp() ));
        requestParameters.append( qMakePair( OAUTH_KEY_NONCE, this->oauthNonce() ));
        requestParameters.append( qMakePair( OAUTH_KEY_TOKEN, oauthToken ));
        insertAdditionalParams(requestParameters);
        break;

    default:
        break;
    }
}

void KQOAuthRequestPrivate::insertAdditionalParams(QList< QPair<QString, QString> > &requestParams) {
    QList<QString> additionalKeys = this->additionalParams.keys();
    QList<QString> additionalValues = this->additionalParams.values();

    for(int i=0; i<additionalKeys.size(); i++) {
        requestParams.append( qMakePair(QString(QUrl::toPercentEncoding(additionalKeys.at(i))),
                                        QString(QUrl::toPercentEncoding(additionalValues.at(i))))
                             );
    }

    if (oauthHttpMethod == KQOAuthRequest::POST) {
        insertPostBody();
    }
}

void KQOAuthRequestPrivate::insertPostBody() {
    QList<QString> postBodyKeys = this->additionalParams.keys();
    QList<QString> postBodyValues = this->additionalParams.values();

    postBodyContent.clear();
    bool first = true;
    for(int i=0; i<postBodyKeys.size(); i++) {
        if(!first) {
            postBodyContent.append("&");
        } else {
            first = false;
        }

        QString key = postBodyKeys.at(i);
        QString value = postBodyValues.at(i);

        postBodyContent.append(QUrl::toPercentEncoding(key) + QString("=").toUtf8() +
                               QUrl::toPercentEncoding(value));
    }

}

void KQOAuthRequestPrivate::signRequest() {
    QString signature = this->oauthSignature();
    requestParameters.append( qMakePair( OAUTH_KEY_SIGNATURE, signature) );
}

QString KQOAuthRequestPrivate::oauthSignature()  {
    QByteArray baseString = this->requestBaseString();

    QString signature = KQOAuthUtils::hmac_sha1(baseString, oauthConsumerSecretKey + "&" + oauthTokenSecret);
    return QString( QUrl::toPercentEncoding( signature ) );
}

bool normalizedParameterSort(const QPair<QString, QString> &left, const QPair<QString, QString> &right) {
    QString keyLeft = left.first;
    QString valueLeft = left.second;
    QString keyRight = right.first;
    QString valueRight = right.second;

    if(keyLeft == keyRight) {
        return (valueLeft < valueRight);
    } else {
        return (keyLeft < keyRight);
    }
}
QByteArray KQOAuthRequestPrivate::requestBaseString() {
    QByteArray baseString;

    // Every request has these as the commont parameters.
    baseString.append( oauthHttpMethodString.toUtf8() + "&");                                                     // HTTP method
    baseString.append( QUrl::toPercentEncoding( oauthRequestEndpoint.toString(QUrl::RemoveQuery) ) + "&" ); // The path and query components

    QList< QPair<QString, QString> > baseStringParameters;
    baseStringParameters.append(requestParameters);

    // Sort the request parameters. These parameters have been
    // initialized earlier.
    qSort(baseStringParameters.begin(),
          baseStringParameters.end(),
          normalizedParameterSort
          );

    // Last append the request parameters correctly encoded.
    baseString.append( encodedParamaterList(baseStringParameters) );

    return baseString;
}

QByteArray KQOAuthRequestPrivate::encodedParamaterList(const QList< QPair<QString, QString> > &parameters) {
    QByteArray resultList;

    bool first = true;
    QPair<QString, QString> parameter;
    foreach (parameter, parameters) {
        if(!first) {
            resultList.append( "%26" );
        } else {
            first = false;
        }

        // Here we don't need to explicitely encode the strings to UTF-8 since
        // QUrl::toPercentEncoding() takes care of that for us.
        resultList.append( QUrl::toPercentEncoding(parameter.first)     // Parameter key
                           + "%3D"                                      // '=' encoded
                           + QUrl::toPercentEncoding(parameter.second)  // Parameter value
                          );
    }

    return resultList;
}

QString KQOAuthRequestPrivate::oauthTimestamp() const {
    // This is basically for unit tests only. In most cases we don't set the nonce beforehand.
    if (!oauthTimestamp_.isEmpty()) {
        return oauthTimestamp_;
    }

#if QT_VERSION >= 0x040700
    return QString::number(QDateTime::currentDateTimeUtc().toTime_t());
#else
   return QString::number(QDateTime::currentDateTime().toUTC().toTime_t());
#endif

}

QString KQOAuthRequestPrivate::oauthNonce() const {
    // This is basically for unit tests only. In most cases we don't set the nonce beforehand.
    if (!oauthNonce_.isEmpty()) {
        return oauthNonce_;
    }

    QString nonceTimestamp = oauthTimestamp_;

    if (nonceTimestamp.isEmpty()) {
        nonceTimestamp = oauthTimestamp();
    }

    return QCryptographicHash::hash(nonceTimestamp.toAscii(), QCryptographicHash::Md5).toHex();
}

bool KQOAuthRequestPrivate::validateRequest() const {
    switch ( requestType ) {
    case KQOAuthRequest::TemporaryCredentials:
        if (oauthRequestEndpoint.isEmpty()
            || oauthConsumerKey.isEmpty()
            || oauthNonce_.isEmpty()
            || oauthSignatureMethod.isEmpty()
            || oauthTimestamp_.isEmpty()
            || oauthVersion.isEmpty())
        {
            return false;
        }
        return true;

    case KQOAuthRequest::AccessToken:
        if (oauthRequestEndpoint.isEmpty()
            ||  oauthVerifier.isEmpty()
            || oauthConsumerKey.isEmpty()
            || oauthNonce_.isEmpty()
            || oauthSignatureMethod.isEmpty()
            || oauthTimestamp_.isEmpty()
            || oauthToken.isEmpty()
            || oauthTokenSecret.isEmpty()
            || oauthVersion.isEmpty())
        {
            return false;
        }
        return true;

    case KQOAuthRequest::AuthorizedRequest:
        if (oauthRequestEndpoint.isEmpty()
            || oauthConsumerKey.isEmpty()
            || oauthNonce_.isEmpty()
            || oauthSignatureMethod.isEmpty()
            || oauthTimestamp_.isEmpty()
            || oauthToken.isEmpty()
            || oauthTokenSecret.isEmpty()
            || oauthVersion.isEmpty())
        {
            return false;
        }
        return true;

    default:
        return false;
    }

    // We should not come here.
    return false;
}

QByteArray KQOAuthRequestPrivate::requestBody() const {
    return postBodyContent;
}

//////////// Public implementation ////////////////

KQOAuthRequest::KQOAuthRequest(QObject *parent) :
    QObject(parent),
    d_ptr(new KQOAuthRequestPrivate)
{
}

KQOAuthRequest::~KQOAuthRequest()
{
    delete d_ptr;
}

void KQOAuthRequest::initRequest(KQOAuthRequest::RequestType type, const QUrl &requestEndpoint) {
    Q_D(KQOAuthRequest);

    if (!requestEndpoint.isValid()) {
        qWarning() << "Endpoint URL is not valid. Ignoring. This request might not work.";
        return;
    }

    if (type < 0 || type > KQOAuthRequest::AuthorizedRequest) {
        qWarning() << "Invalid request type. Ignoring. This request might not work.";
        return;
    }

    // Clear the request
    clearRequest();

    // Set smart defaults.
    d->requestType = type;
    d->oauthRequestEndpoint = requestEndpoint;
    d->oauthTimestamp_ = d->oauthTimestamp();
    d->oauthNonce_ = d->oauthNonce();
    this->setSignatureMethod(KQOAuthRequest::HMAC_SHA1);
    this->setHttpMethod(KQOAuthRequest::POST);
    d->oauthVersion = "1.0"; // Currently supports only version 1.0
}

void KQOAuthRequest::setConsumerKey(const QString &consumerKey) {
    Q_D(KQOAuthRequest);
    d->oauthConsumerKey = consumerKey;
}

void KQOAuthRequest::setConsumerSecretKey(const QString &consumerSecretKey) {
    Q_D(KQOAuthRequest);
    d->oauthConsumerSecretKey = consumerSecretKey;
}

void KQOAuthRequest::setCallbackUrl(const QUrl &callbackUrl) {
    Q_D(KQOAuthRequest);

    d->oauthCallbackUrl = callbackUrl;
}

void KQOAuthRequest::setSignatureMethod(KQOAuthRequest::RequestSignatureMethod requestMethod) {
    Q_D(KQOAuthRequest);
    QString requestMethodString;

    switch (requestMethod) {
    case KQOAuthRequest::PLAINTEXT:
        requestMethodString = "PLAINTEXT";
        break;
    case KQOAuthRequest::HMAC_SHA1:
        requestMethodString = "HMAC-SHA1";
        break;
    case KQOAuthRequest::RSA_SHA1:
        requestMethodString = "RSA-SHA1";
        break;
    default:
        // We should not come here
        qWarning() << "Invalid signature method set.";
        break;
    }

    d->oauthSignatureMethod = requestMethodString;
}

void KQOAuthRequest::setTokenSecret(const QString &tokenSecret) {
    Q_D(KQOAuthRequest);

    d->oauthTokenSecret = tokenSecret;
}

void KQOAuthRequest::setToken(const QString &token) {
    Q_D(KQOAuthRequest);

    d->oauthToken = token;
}

void KQOAuthRequest::setVerifier(const QString &verifier) {
    Q_D(KQOAuthRequest);

    d->oauthVerifier = verifier;
}

void KQOAuthRequest::setHttpMethod(KQOAuthRequest::RequestHttpMethod httpMethod) {
    Q_D(KQOAuthRequest);

    QString requestHttpMethodString;

    switch (httpMethod) {
    case KQOAuthRequest::GET:
        requestHttpMethodString = "GET";
        break;
    case KQOAuthRequest::POST:
        requestHttpMethodString = "POST";
        break;
    default:
        qWarning() << "Invalid HTTP method set.";
        break;
    }

    d->oauthHttpMethod = httpMethod;
    d->oauthHttpMethodString = requestHttpMethodString;
}

KQOAuthRequest::RequestHttpMethod KQOAuthRequest::httpMethod() const {
    Q_D(const KQOAuthRequest);

    return d->oauthHttpMethod;
}

void KQOAuthRequest::setAdditionalParameters(const KQOAuthParameters &additionalParams) {
    Q_D(KQOAuthRequest);

    d->additionalParams = additionalParams;
}

KQOAuthParameters KQOAuthRequest::additionalParameters() const {
    Q_D(const KQOAuthRequest);

    return d->additionalParams;
}

KQOAuthRequest::RequestType KQOAuthRequest::requestType() const {
    Q_D(const KQOAuthRequest);

    return d->requestType;
}

QUrl KQOAuthRequest::requestEndpoint() const {
    Q_D(const KQOAuthRequest);

    return d->oauthRequestEndpoint;
}

QList<QByteArray> KQOAuthRequest::requestParameters() {
    Q_D(KQOAuthRequest);

    QList<QByteArray> requestParamList;

    d->prepareRequest();
    if (!d->validateRequest() ) {
        qWarning() << "Request is not valid! I will still sign it, but it will probably not work.";
    }
    d->signRequest();

    QPair<QString, QString> requestParam;
    QString param;
    QString value;
    foreach (requestParam, d->requestParameters) {
        param = requestParam.first;
        value = requestParam.second;
        requestParamList.append(QString(param + "=\"" + value +"\"").toUtf8());
    }

    return requestParamList;
}

bool KQOAuthRequest::isValid() const {
    Q_D(const KQOAuthRequest);

    return d->validateRequest();
}

void KQOAuthRequest::clearRequest() {
    Q_D(KQOAuthRequest);

    d->oauthRequestEndpoint = "";
    d->oauthHttpMethodString = "";
    d->oauthConsumerKey = "";
    d->oauthConsumerSecretKey = "";
    d->oauthToken = "";
    d->oauthTokenSecret = "";
    d->oauthSignatureMethod = "";
    d->oauthCallbackUrl = "";
    d->oauthVerifier = "";
    d->oauthTimestamp_ = "";
    d->oauthNonce_ = "";
    d->requestParameters.clear();
    d->additionalParams.clear();
}

<|endoftext|>


<commit_before>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#include "XalanNode.hpp"

#define XALAN_NODE_SPECIAL_DEBUG

#if !defined(NDEBUG)
unsigned long	XalanNode::s_instanceCount = 0;

#if defined(XALAN_NODE_SPECIAL_DEBUG)

#include <iostream>
#include <set>

#if defined(XALAN_NO_NAMESPACES)

typedef set<XalanNode*>			InstanceSetType;

#else

typedef std::set<XalanNode*>	InstanceSetType;
using std::cerr;
using std::endl;

#endif

static InstanceSetType	s_instanceSet;

#endif

#endif

XalanNode::XalanNode()
{
#if !defined(NDEBUG)
	s_instanceCount++;

#if defined(XALAN_NODE_SPECIAL_DEBUG)
	s_instanceSet.insert(this);

#endif

#endif

}

XalanNode::~XalanNode()
{
#if !defined(NDEBUG)

	s_instanceCount--;

#if defined(XALAN_NODE_SPECIAL_DEBUG)

	s_instanceSet.erase(this);

#endif

#endif
}

XalanNode::XalanNode(const XalanNode&	/* theSource */)
{
#if !defined(NDEBUG)

	s_instanceCount++;

#if defined(XALAN_NODE_SPECIAL_DEBUG)

	s_instanceSet.insert(this);

#endif

#endif
}

XalanNode&
XalanNode::operator=(const XalanNode&	/* theSource */)
{
	return *this;
}

bool
XalanNode::operator==(const XalanNode&	/* theRHS */) const
{
	return false;
}

#if !defined(NDEBUG)

void
XalanNode::getLiveInstances(XalanNode*	theNodes[])
{
#if defined(XALAN_NODE_SPECIAL_DEBUG)

	InstanceSetType::const_iterator		i =
		s_instanceSet.begin();

	InstanceSetType::const_iterator		end =
		s_instanceSet.end();

	for(size_t j = 0; i != end; ++i, ++j)
	{
		theNodes[j] = *i;
	}
#endif
}

#endif

<commit_msg>Removed extraneous debug code.<commit_after>/*
 * The Apache Software License, Version 1.1
 *
 *
 * Copyright (c) 1999 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Xalan" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation and was
 * originally based on software copyright (c) 1999, International
 * Business Machines, Inc., http://www.ibm.com.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 */
#include "XalanNode.hpp"

#if !defined(NDEBUG)
unsigned long	XalanNode::s_instanceCount = 0;

#if defined(XALAN_NODE_SPECIAL_DEBUG)

#include <set>

#if defined(XALAN_NO_NAMESPACES)

typedef set<XalanNode*>			InstanceSetType;

#else

typedef std::set<XalanNode*>	InstanceSetType;

#endif

static InstanceSetType	s_instanceSet;

#endif

#endif

XalanNode::XalanNode()
{
#if !defined(NDEBUG)
	s_instanceCount++;

#if defined(XALAN_NODE_SPECIAL_DEBUG)
	s_instanceSet.insert(this);

#endif

#endif

}

XalanNode::~XalanNode()
{
#if !defined(NDEBUG)

	s_instanceCount--;

#if defined(XALAN_NODE_SPECIAL_DEBUG)

	s_instanceSet.erase(this);

#endif

#endif
}

XalanNode::XalanNode(const XalanNode&	/* theSource */)
{
#if !defined(NDEBUG)

	s_instanceCount++;

#if defined(XALAN_NODE_SPECIAL_DEBUG)

	s_instanceSet.insert(this);

#endif

#endif
}

XalanNode&
XalanNode::operator=(const XalanNode&	/* theSource */)
{
	return *this;
}

bool
XalanNode::operator==(const XalanNode&	/* theRHS */) const
{
	return false;
}

#if !defined(NDEBUG)

void
#if defined(XALAN_NODE_SPECIAL_DEBUG)
XalanNode::getLiveInstances(XalanNode*	theNodes[])
#else
XalanNode::getLiveInstances(XalanNode*	/* theNodes*/ [])
#endif
{
#if defined(XALAN_NODE_SPECIAL_DEBUG)

	InstanceSetType::const_iterator		i =
		s_instanceSet.begin();

	InstanceSetType::const_iterator		end =
		s_instanceSet.end();

	for(size_t j = 0; i != end; ++i, ++j)
	{
		theNodes[j] = *i;
	}
#endif
}

#endif

<|endoftext|>


<commit_before>#include "NL_Scheduler.h"
#include "NL_SysManager.h"
#include "tbb/tbb.h"
#include "NL_System.h"
#include "NL_SysTask.h"
#include "NL_StateManager.h"

namespace NLE
{
	namespace Core
	{
		Scheduler::Scheduler() :
			_syncSystems(),
			_asyncSystems(),
			_numCores()
		{
		}

		Scheduler::~Scheduler()
		{

		}

		bool Scheduler::initialize()
		{
			_numCores = tbb::task_scheduler_init::default_num_threads();
			_taskSchedulerInit = new tbb::task_scheduler_init(_numCores + 1);
			printf("Running on %i threads.\n", _numCores + 1);
			return true;
		}

		void Scheduler::release()
		{
			if (_taskSchedulerInit)
				delete _taskSchedulerInit;
		}

		uint_fast8_t Scheduler::getNumCores()
		{
			return _numCores;
		}

		void Scheduler::scheduleExecution(ExecutionDesc execDesc)
		{
			printf("Scheduling system %i\n", execDesc.getSysId());
			if (execDesc.getExecutionType() == ExecutionType::SYNC)
				_syncSystems.push(execDesc);
			else
				_asyncSystems.push(execDesc);
		}

		void Scheduler::executeSystems(
			std::unique_ptr<SysManager> const& sysManager,
			std::unique_ptr<StateManager> const& stateManager)
		{
			ExecutionDesc execDesc;
			std::function<void()> procedure;

			size_t  size = _syncSystems.size();
			if (size > 0 && size == sysManager->getNumSyncSystems())
			{
				stateManager->distributeData();
				while (_syncSystems.try_pop(execDesc))
				{
					procedure = sysManager->getSystemById(execDesc.getSysId()).get()->getExecutionProcedure();
					tbb::task::enqueue(*new (tbb::task::allocate_root())NLE::Core::SysTask(this, execDesc, procedure));
				}
			}

			if (!_asyncSystems.empty())
			{
				while (_asyncSystems.try_pop(execDesc))
				{
					procedure = sysManager->getSystemById(execDesc.getSysId()).get()->getExecutionProcedure();
					tbb::task::enqueue(*new (tbb::task::allocate_root())NLE::Core::SysTask(this, execDesc, procedure));
				}
			}
		}
	}
}<commit_msg>Data is distributed after synchronization<commit_after>#include "NL_Scheduler.h"
#include "NL_SysManager.h"
#include "tbb/tbb.h"
#include "NL_System.h"
#include "NL_SysTask.h"
#include "NL_StateManager.h"

namespace NLE
{
	namespace Core
	{
		Scheduler::Scheduler() :
			_syncSystems(),
			_asyncSystems(),
			_numCores()
		{
		}

		Scheduler::~Scheduler()
		{

		}

		bool Scheduler::initialize()
		{
			_numCores = tbb::task_scheduler_init::default_num_threads();
			_taskSchedulerInit = new tbb::task_scheduler_init(_numCores + 1);
			printf("Running on %i threads.\n", _numCores + 1);
			return true;
		}

		void Scheduler::release()
		{
			if (_taskSchedulerInit)
				delete _taskSchedulerInit;
		}

		uint_fast8_t Scheduler::getNumCores()
		{
			return _numCores;
		}

		void Scheduler::scheduleExecution(ExecutionDesc execDesc)
		{
			printf("Scheduling system %i\n", execDesc.getSysId());
			if (execDesc.getExecutionType() == ExecutionType::SYNC)
				_syncSystems.push(execDesc);
			else
				_asyncSystems.push(execDesc);
		}

		void Scheduler::executeSystems(
			std::unique_ptr<SysManager> const& sysManager,
			std::unique_ptr<StateManager> const& stateManager)
		{
			ExecutionDesc execDesc;
			std::function<void()> procedure;

			size_t  size = _syncSystems.size();
			if (size > 0 && size == sysManager->getNumSyncSystems())
			{
				while (_syncSystems.try_pop(execDesc))
				{
					procedure = sysManager->getSystemById(execDesc.getSysId()).get()->getExecutionProcedure();
					tbb::task::enqueue(*new (tbb::task::allocate_root())NLE::Core::SysTask(this, execDesc, procedure));
				}
				stateManager->distributeData();
			}

			if (!_asyncSystems.empty())
			{
				while (_asyncSystems.try_pop(execDesc))
				{
					procedure = sysManager->getSystemById(execDesc.getSysId()).get()->getExecutionProcedure();
					tbb::task::enqueue(*new (tbb::task::allocate_root())NLE::Core::SysTask(this, execDesc, procedure));
				}
			}
		}
	}
}<|endoftext|>


<commit_before>#pragma once

#include "../traits.hpp"
#include "fft2d.pb.h"
#include <thrust/device_vector.h>

namespace cujak {
namespace fft2d {

inline int calc_stride(int Ny) { return (Ny % 2 ? Ny / 2 + 1 : Ny / 2); }

template <typename Container> class wrapper_base {
protected:
  const int Nx, Ny, stride, N;
  Container &u;
  wrapper_base(int Nx_, int Ny_, int stride_, int N_, Container &u_)
      : Nx(Nx_), Ny(Ny_), stride(stride_), N(N_), u(u_) {}

public:
  typedef typename Container::value_type value_type;

  pb::Property property;

  value_type *get() const { return u.data().get(); }
  Container &data() const { return u; }

  value_type operator()(int i, int j) const { return u[stride * i + j]; }
  void set(int i, int j, value_type v) { u[stride * i + j] = v; }

  int size_x() const { return Nx; }
  int size_y() const { return Ny; }
  int size() const { return N; }
  int get_stride() const { return stride; }
};

template <typename Float>
class Field_wrapper : public wrapper_base<rdVector<Float> > {
public:
  typedef rdVector<Float> Container;
  Field_wrapper(int Nx, int Ny, Container &u)
      : wrapper_base<Container>(Nx, Ny, Ny, Nx * Ny, u) {}
  virtual ~Field_wrapper() = default;
};

template <typename Float>
class Coefficient_wrapper : public wrapper_base<cdVector<Float> > {

public:
  typedef cdVector<Float> Container;
  Coefficient_wrapper(int Nx, int Ny, Container &u)
      : wrapper_base<Container>(Nx, Ny, calc_stride(Ny), Nx * calc_stride(Ny),
                                u) {}
  virtual ~Coefficient_wrapper() = default;
};

template <typename Float> class Field : public Field_wrapper<Float> {
  typename Field_wrapper<Float>::Container data_;

public:
  Field(int Nx, int Ny) : data_(Nx * Ny), Field_wrapper<Float>(Nx, Ny, data_) {}
};

template <typename Float>
class Coefficient : public Coefficient_wrapper<Float> {
  typedef Coefficient_wrapper<Float> Inhereted;
  typedef typename Inhereted::Container Container;
  std::unique_ptr<Container> p;

public:
  Coefficient(int Nx, int Ny)
      : p(new Container(Nx * calc_stride(Ny))),
        Coefficient_wrapper<Float>(Nx, Ny, *p) {}
};

} // namespace fft2d
} // namespace Kolmogorov2D
<commit_msg>Use Container directly<commit_after>#pragma once

#include "../traits.hpp"
#include "fft2d.pb.h"
#include <thrust/device_vector.h>

namespace cujak {
namespace fft2d {

inline int calc_stride(int Ny) { return (Ny % 2 ? Ny / 2 + 1 : Ny / 2); }

template <typename Container> class wrapper_base {
protected:
  const int Nx, Ny, stride, N;
  Container &u;
  wrapper_base(int Nx_, int Ny_, int stride_, int N_, Container &u_)
      : Nx(Nx_), Ny(Ny_), stride(stride_), N(N_), u(u_) {}

public:
  typedef typename Container::value_type value_type;

  pb::Property property;

  value_type *get() const { return u.data().get(); }
  Container &data() const { return u; }

  value_type operator()(int i, int j) const { return u[stride * i + j]; }
  void set(int i, int j, value_type v) { u[stride * i + j] = v; }

  int size_x() const { return Nx; }
  int size_y() const { return Ny; }
  int size() const { return N; }
  int get_stride() const { return stride; }
};

template <typename Float>
class Field_wrapper : public wrapper_base<rdVector<Float> > {
public:
  typedef rdVector<Float> Container;
  Field_wrapper(int Nx, int Ny, Container &u)
      : wrapper_base<Container>(Nx, Ny, Ny, Nx * Ny, u) {}
  virtual ~Field_wrapper() = default;
};

template <typename Float>
class Coefficient_wrapper : public wrapper_base<cdVector<Float> > {

public:
  typedef cdVector<Float> Container;
  Coefficient_wrapper(int Nx, int Ny, Container &u)
      : wrapper_base<Container>(Nx, Ny, calc_stride(Ny), Nx * calc_stride(Ny),
                                u) {}
  virtual ~Coefficient_wrapper() = default;
};

template <typename Float> class Field : public Field_wrapper<Float> {
  typename Field_wrapper<Float>::Container data_;

public:
  Field(int Nx, int Ny) : data_(Nx * Ny), Field_wrapper<Float>(Nx, Ny, data_) {}
};

template <typename Float>
class Coefficient : public Coefficient_wrapper<Float> {
  typename Coefficient<Float>::Container data_;

public:
  Coefficient(int Nx, int Ny)
      : data_(Nx * calc_stride(Ny)), Coefficient_wrapper<Float>(Nx, Ny, data_) {
  }
};

} // namespace fft2d
} // namespace Kolmogorov2D
<|endoftext|>


<commit_before>#ifndef FILE_UTILS_INCLUDED__
#define FILE_UTILS_INCLUDED__

#include <fstream>
#include <string>
#include <vector>

inline std::string file_slurp (const std::string & filename)
{
    std::string retval;

    std::ifstream ifs(filename);
    ifs.seekg(0, std::ifstream::end);
    if (0 < ifs.tellg())
        retval.resize(ifs.tellg());
    ifs.seekg(0);

    const std::streamsize read_size = 64 * 1024; // 64k per read
    char* retval_pos = &retval[0];
    std::streamsize bytes_read = 0;
    do {
        ifs.read(retval_pos, read_size);
        bytes_read = ifs.gcount();
        retval_pos += bytes_read;
    } while (bytes_read == read_size);

    return retval;
}

inline std::vector<std::string> line_break (const std::string & file)
{
    std::vector<std::string> retval;

    std::string::size_type prev_pos = 0;
    while (true) {
        std::string::size_type pos_1 = file.find("\r\n", prev_pos);
        std::string::size_type pos_2 = file.find_first_of("\r\n", prev_pos);
        std::string::size_type pos = pos_2;
        std::string::size_type match_size = 1;
        if (pos_1 != std::string::npos) {
            pos = pos_1;
            match_size = 2;
        } else if (pos_2 == std::string::npos) {
	        break;
        }
        retval.push_back(file.substr(prev_pos, pos - prev_pos));
        prev_pos = pos + match_size;
    }

    retval.push_back(file.substr(prev_pos));

    return retval;
}

#endif
<commit_msg>Simplify file_slurp() to eliminate stray null characters in generated code on Windows.<commit_after>#ifndef FILE_UTILS_INCLUDED__
#define FILE_UTILS_INCLUDED__

#include <fstream>
#include <string>
#include <vector>

inline std::string file_slurp (const std::string & filename)
{
    std::string retval;
    std::ifstream ifs(filename);
    std::getline(ifs, retval, '\0');
    return retval;
}

inline std::vector<std::string> line_break (const std::string & file)
{
    std::vector<std::string> retval;

    std::string::size_type prev_pos = 0;
    while (true) {
        std::string::size_type pos_1 = file.find("\r\n", prev_pos);
        std::string::size_type pos_2 = file.find_first_of("\r\n", prev_pos);
        std::string::size_type pos = pos_2;
        std::string::size_type match_size = 1;
        if (pos_1 != std::string::npos) {
            pos = pos_1;
            match_size = 2;
        } else if (pos_2 == std::string::npos) {
	        break;
        }
        retval.push_back(file.substr(prev_pos, pos - prev_pos));
        prev_pos = pos + match_size;
    }

    retval.push_back(file.substr(prev_pos));

    return retval;
}

#endif
<|endoftext|>


<commit_before>/*
* Copyright (C) 2008-2013 The Communi Project
*
* 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 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.
*/

#include "ircchannelmodel.h"
#include "ircmessagefilter.h"
#include "ircsessioninfo.h"
#include "ircchannel_p.h"
#include "ircmessage.h"
#include "ircsession.h"
#include <qpointer.h>

/*!
    \file ircchannelmodel.h
    \brief #include <IrcChannelModel>
 */

/*!
    \class IrcChannelModel ircchannelmodel.h <IrcChannelModel>
    \ingroup models
    \brief Keeps track of channels.

    IrcChannelModel automatically keeps track of channels
    and manages IrcChannel instances for them.

    \sa models
 */

/*!
    \fn void IrcChannelModel::channelAdded(IrcChannel* channel)

    This signal is emitted when a \a channel is added to the list of channels.
 */

/*!
    \fn void IrcChannelModel::channelRemoved(IrcChannel* channel)

    This signal is emitted when a \a channel is removed from the list of channels.
 */

/*!
    \fn void IrcChannelModel::messageIgnored(IrcMessage* message)

    This signal is emitted when a message was ignored.

    IrcChannelModel handles only channel specific messages and delivers
    them to the appropriate IrcChannel instances. When applications decide
    to handle IrcChannel::messageReceived(), this signal makes it easy to
    implement handling for the rest, non-channel specific messages.

    \sa IrcSession::messageReceived(), IrcChannel::messageReceived()
 */

class IrcChannelModelPrivate : public IrcMessageFilter
{
    Q_DECLARE_PUBLIC(IrcChannelModel)

public:
    IrcChannelModelPrivate(IrcChannelModel* q);

    bool messageFilter(IrcMessage* message);

    void addChannel(const QString& title);
    void removeChannel(const QString& title);
    bool processMessage(const QString& title, IrcMessage* message);

    void _irc_channelDestroyed(IrcChannel* channel);

    IrcChannelModel* q_ptr;
    Irc::ItemDataRole role;
    QPointer<IrcSession> session;
    QList<IrcChannel*> channelList;
    QMap<QString, IrcChannel*> channelMap;
};

IrcChannelModelPrivate::IrcChannelModelPrivate(IrcChannelModel* q) :
    q_ptr(q), role(Irc::TitleRole)
{
}

bool IrcChannelModelPrivate::messageFilter(IrcMessage* msg)
{
    Q_Q(IrcChannelModel);
    if (msg->type() == IrcMessage::Join && msg->flags() & IrcMessage::Own)
        addChannel(static_cast<IrcJoinMessage*>(msg)->channel().toLower());

    switch (msg->type()) {
        case IrcMessage::Nick:
        case IrcMessage::Quit:
            foreach (IrcChannel* channel, channelList)
                channel->d_func()->processMessage(msg);
            break;

        case IrcMessage::Join:
        case IrcMessage::Part:
        case IrcMessage::Kick:
        case IrcMessage::Names:
        case IrcMessage::Topic:
            if (!processMessage(msg->property("channel").toString().toLower(), msg))
                emit q->messageIgnored(msg);
            break;

        case IrcMessage::Mode:
        case IrcMessage::Notice:
        case IrcMessage::Private:
            if (!processMessage(msg->property("target").toString().toLower(), msg))
                emit q->messageIgnored(msg);
            break;

        default:
            emit q->messageIgnored(msg);
            break;
    }

    if (msg->type() == IrcMessage::Part && msg->flags() & IrcMessage::Own) {
        removeChannel(static_cast<IrcPartMessage*>(msg)->channel().toLower());
    } else if (msg->type() == IrcMessage::Quit && msg->flags() & IrcMessage::Own) {
        foreach (const QString& channel, channelMap.keys())
            removeChannel(channel);
    } else if (msg->type() == IrcMessage::Kick) {
        const IrcKickMessage* kickMsg = static_cast<IrcKickMessage*>(msg);
        if (!kickMsg->user().compare(msg->session()->nickName(), Qt::CaseInsensitive))
            removeChannel(kickMsg->channel().toLower());
    }

    return false;
}

static QString channelPrefix(const QString& channel, const QStringList& prefixes)
{
    int i = 0;
    while (i < channel.length() && prefixes.contains(channel.at(i)))
        ++i;
    return channel.left(i);
}

static QString unprefixedChannel(const QString& channel, const QStringList& prefixes)
{
    int i = 0;
    while (i < channel.length() && prefixes.contains(channel.at(i)))
        ++i;
    return channel.mid(i);
}

void IrcChannelModelPrivate::addChannel(const QString& title)
{
    Q_Q(IrcChannelModel);
    if (!channelMap.contains(title)) {
        IrcChannel* channel = q->createChannel(title);
        if (channel) {
            const QStringList prefixes = IrcSessionInfo(session).channelTypes();
            channel->d_func()->init(channelPrefix(title, prefixes), unprefixedChannel(title, prefixes));

            q->beginInsertRows(QModelIndex(), 0, 0);
            channelList.append(channel);
            channelMap.insert(title, channel);
            q->connect(channel, SIGNAL(destroyed(IrcChannel*)), SLOT(_irc_channelDestroyed(IrcChannel*)));
            q->endInsertRows();
            emit q->channelAdded(channel);
            emit q->channelsChanged(channelList);
            emit q->countChanged(channelList.count());
        }
    }
}

void IrcChannelModelPrivate::removeChannel(const QString& title)
{
    Q_Q(IrcChannelModel);
    IrcChannel* channel = channelMap.value(title);
    if (channel)
        q->destroyChannel(channel);
}

bool IrcChannelModelPrivate::processMessage(const QString& title, IrcMessage* message)
{
    IrcChannel* channel = channelMap.value(title);
    if (channel)
        return channel->d_func()->processMessage(message);
    return false;
}

void IrcChannelModelPrivate::_irc_channelDestroyed(IrcChannel* channel)
{
    Q_Q(IrcChannelModel);
    int idx = channelList.indexOf(channel);
    if (idx != -1) {
        q->beginRemoveRows(QModelIndex(), idx, idx);
        channelList.removeAt(idx);
        channelMap.remove(channel->title());
        q->endRemoveRows();
        emit q->channelRemoved(channel);
        emit q->channelsChanged(channelList);
        emit q->countChanged(channelList.count());
    }
}

Q_DECLARE_METATYPE(IrcChannel*)
Q_DECLARE_METATYPE(QList<IrcChannel*>)

/*!
    Constructs a new model with \a parent.

    \note If \a parent is an instance of IrcSession, it will be
    automatically assigned to \ref IrcChannelModel::session "session".
 */
IrcChannelModel::IrcChannelModel(QObject* parent)
    : QAbstractListModel(parent), d_ptr(new IrcChannelModelPrivate(this))
{
    setSession(qobject_cast<IrcSession*>(parent));

    qRegisterMetaType<IrcChannel*>();
    qRegisterMetaType<QList<IrcChannel*> >();
}

/*!
    Destructs the model.
 */
IrcChannelModel::~IrcChannelModel()
{
    Q_D(IrcChannelModel);
    foreach (IrcChannel* channel, d->channelList) {
        channel->blockSignals(true);
        delete channel;
    }
    d->channelList.clear();
    d->channelMap.clear();
}

/*!
    This property holds the session.

    \par Access functions:
    \li \ref IrcSession* session() const
    \li void setSession(\ref IrcSession* session)

    \warning Changing the session on the fly is not supported.
 */
IrcSession* IrcChannelModel::session() const
{
    Q_D(const IrcChannelModel);
    return d->session;
}

void IrcChannelModel::setSession(IrcSession* session)
{
    Q_D(IrcChannelModel);
    if (d->session != session) {
        if (d->session)
            qFatal("IrcChannelModel::setSession(): changing the session on the fly is not supported.");
        d->session = session;
        d->session->installMessageFilter(d);
        emit sessionChanged(session);
    }
}

/*!
    This property holds the number of channels.

    \par Access function:
    \li int count() const

    \par Notifier signal:
    \li void countChanged(int count)
 */
int IrcChannelModel::count() const
{
    return rowCount();
}

/*!
    This property holds the list of channels.

    \par Access function:
    \li QList<\ref IrcChannel*> channels() const

    \par Notifier signal:
    \li void channelsChanged(const QList<\ref IrcChannel*>& channels)
 */
QList<IrcChannel*> IrcChannelModel::channels() const
{
    Q_D(const IrcChannelModel);
    return d->channelList;
}

/*!
    Returns the channel object at \a index.
 */
IrcChannel* IrcChannelModel::get(int index) const
{
    Q_D(const IrcChannelModel);
    return d->channelList.value(index);
}

/*!
    Returns the channel object for \a title.
 */
IrcChannel* IrcChannelModel::channel(const QString& title) const
{
    Q_D(const IrcChannelModel);
    return d->channelMap.value(title);
}

/*!
    Returns \c true if the model contains \a title.
 */
bool IrcChannelModel::contains(const QString& title) const
{
    Q_D(const IrcChannelModel);
    return d->channelMap.contains(title);
}

/*!
    This property holds the display role.

    The specified data role is returned for Qt::DisplayRole.

    The default value is \ref Irc::TitleRole.

    \par Access functions:
    \li \ref Irc::ItemDataRole displayRole() const
    \li void setDisplayRole(\ref Irc::ItemDataRole role)
 */
Irc::ItemDataRole IrcChannelModel::displayRole() const
{
    Q_D(const IrcChannelModel);
    return d->role;
}

void IrcChannelModel::setDisplayRole(Irc::ItemDataRole role)
{
    Q_D(IrcChannelModel);
    d->role = role;
}

/*!
    Clears the model.
 */
void IrcChannelModel::clear()
{
    Q_D(IrcChannelModel);
    if (!d->channelList.isEmpty()) {
        beginResetModel();
        qDeleteAll(d->channelList);
        d->channelList.clear();
        d->channelMap.clear();
        endResetModel();
    }
}

/*!
    Creates a channel object for channel \a title.

    IrcChannelModel will automatically call this factory method when a
    need for the channel object occurs ie. the channel is being joined.

    The default implementation creates a new instance of IrcChannel.
    Reimplement this function in order to alter the default behavior,
    for example to provide a custom IrcChannel subclass.
 */
IrcChannel* IrcChannelModel::createChannel(const QString& title)
{
    Q_UNUSED(title);
    return new IrcChannel(this);
}

/*!
    Destroys the channel \a model.

    IrcChannelModel will automatically call this method when the channel
    object is no longer needed ie. the user quit, the channel was parted,
    or the user was kicked from the channel.

    The default implementation deletes the channel object.
    Reimplement this function in order to alter the default behavior,
    for example to keep the channel object alive.
 */
void IrcChannelModel::destroyChannel(IrcChannel* channel)
{
    delete channel;
}

/*!
    The following role names are provided by default:

    Role             | Name      | Type        | Example
    -----------------|-----------|-------------|--------
    Qt::DisplayRole  | "display" | 1)          | -
    Irc::ChannelRole | "channel" | IrcChannel* | <object>
    Irc::NameRole    | "name"    | QString     | "communi"
    Irc::PrefixRole  | "prefix"  | QString     | "#"
    Irc::TitleRole   | "title"   | QString     | "#communi"

    1) The type depends on \ref displayRole.
 */
QHash<int, QByteArray> IrcChannelModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[Qt::DisplayRole] = "display";
    roles[Irc::ChannelRole] = "channel";
    roles[Irc::NameRole] = "name";
    roles[Irc::PrefixRole] = "prefix";
    roles[Irc::TitleRole] = "title";
    return roles;
}

/*!
    Returns the number of channels.
 */
int IrcChannelModel::rowCount(const QModelIndex& parent) const
{
    if (parent.isValid())
        return 0;

    Q_D(const IrcChannelModel);
    return d->channelList.count();
}

/*!
    Returns the data for specified \a role and user referred to by by the \a index.
 */
QVariant IrcChannelModel::data(const QModelIndex& index, int role) const
{
    Q_D(const IrcChannelModel);
    if (!hasIndex(index.row(), index.column()))
        return QVariant();

    switch (role) {
    case Qt::DisplayRole:
        return data(index, d->role);
    case Irc::ChannelRole:
        return QVariant::fromValue(d->channelList.at(index.row()));
    case Irc::NameRole:
        if (IrcChannel* channel =  d->channelList.at(index.row()))
            return channel->name();
        break;
    case Irc::PrefixRole:
        if (IrcChannel* channel =  d->channelList.at(index.row()))
            return channel->prefix();
        break;
    case Irc::TitleRole:
        if (IrcChannel* channel =  d->channelList.at(index.row()))
            return channel->title();
        break;
    }

    return QVariant();
}

#include "moc_ircchannelmodel.cpp"
<commit_msg>Fix IrcChannelModelPrivate::addChannel()<commit_after>/*
* Copyright (C) 2008-2013 The Communi Project
*
* 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 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.
*/

#include "ircchannelmodel.h"
#include "ircmessagefilter.h"
#include "ircsessioninfo.h"
#include "ircchannel_p.h"
#include "ircmessage.h"
#include "ircsession.h"
#include <qpointer.h>

/*!
    \file ircchannelmodel.h
    \brief #include <IrcChannelModel>
 */

/*!
    \class IrcChannelModel ircchannelmodel.h <IrcChannelModel>
    \ingroup models
    \brief Keeps track of channels.

    IrcChannelModel automatically keeps track of channels
    and manages IrcChannel instances for them.

    \sa models
 */

/*!
    \fn void IrcChannelModel::channelAdded(IrcChannel* channel)

    This signal is emitted when a \a channel is added to the list of channels.
 */

/*!
    \fn void IrcChannelModel::channelRemoved(IrcChannel* channel)

    This signal is emitted when a \a channel is removed from the list of channels.
 */

/*!
    \fn void IrcChannelModel::messageIgnored(IrcMessage* message)

    This signal is emitted when a message was ignored.

    IrcChannelModel handles only channel specific messages and delivers
    them to the appropriate IrcChannel instances. When applications decide
    to handle IrcChannel::messageReceived(), this signal makes it easy to
    implement handling for the rest, non-channel specific messages.

    \sa IrcSession::messageReceived(), IrcChannel::messageReceived()
 */

class IrcChannelModelPrivate : public IrcMessageFilter
{
    Q_DECLARE_PUBLIC(IrcChannelModel)

public:
    IrcChannelModelPrivate(IrcChannelModel* q);

    bool messageFilter(IrcMessage* message);

    void addChannel(const QString& title);
    void removeChannel(const QString& title);
    bool processMessage(const QString& title, IrcMessage* message);

    void _irc_channelDestroyed(IrcChannel* channel);

    IrcChannelModel* q_ptr;
    Irc::ItemDataRole role;
    QPointer<IrcSession> session;
    QList<IrcChannel*> channelList;
    QMap<QString, IrcChannel*> channelMap;
};

IrcChannelModelPrivate::IrcChannelModelPrivate(IrcChannelModel* q) :
    q_ptr(q), role(Irc::TitleRole)
{
}

bool IrcChannelModelPrivate::messageFilter(IrcMessage* msg)
{
    Q_Q(IrcChannelModel);
    if (msg->type() == IrcMessage::Join && msg->flags() & IrcMessage::Own)
        addChannel(static_cast<IrcJoinMessage*>(msg)->channel().toLower());

    switch (msg->type()) {
        case IrcMessage::Nick:
        case IrcMessage::Quit:
            foreach (IrcChannel* channel, channelList)
                channel->d_func()->processMessage(msg);
            break;

        case IrcMessage::Join:
        case IrcMessage::Part:
        case IrcMessage::Kick:
        case IrcMessage::Names:
        case IrcMessage::Topic:
            if (!processMessage(msg->property("channel").toString().toLower(), msg))
                emit q->messageIgnored(msg);
            break;

        case IrcMessage::Mode:
        case IrcMessage::Notice:
        case IrcMessage::Private:
            if (!processMessage(msg->property("target").toString().toLower(), msg))
                emit q->messageIgnored(msg);
            break;

        default:
            emit q->messageIgnored(msg);
            break;
    }

    if (msg->type() == IrcMessage::Part && msg->flags() & IrcMessage::Own) {
        removeChannel(static_cast<IrcPartMessage*>(msg)->channel().toLower());
    } else if (msg->type() == IrcMessage::Quit && msg->flags() & IrcMessage::Own) {
        foreach (const QString& channel, channelMap.keys())
            removeChannel(channel);
    } else if (msg->type() == IrcMessage::Kick) {
        const IrcKickMessage* kickMsg = static_cast<IrcKickMessage*>(msg);
        if (!kickMsg->user().compare(msg->session()->nickName(), Qt::CaseInsensitive))
            removeChannel(kickMsg->channel().toLower());
    }

    return false;
}

static QString channelPrefix(const QString& channel, const QStringList& prefixes)
{
    int i = 0;
    while (i < channel.length() && prefixes.contains(channel.at(i)))
        ++i;
    return channel.left(i);
}

static QString unprefixedChannel(const QString& channel, const QStringList& prefixes)
{
    int i = 0;
    while (i < channel.length() && prefixes.contains(channel.at(i)))
        ++i;
    return channel.mid(i);
}

void IrcChannelModelPrivate::addChannel(const QString& title)
{
    Q_Q(IrcChannelModel);
    if (!channelMap.contains(title)) {
        IrcChannel* channel = q->createChannel(title);
        if (channel) {
            const QStringList prefixes = IrcSessionInfo(session).channelTypes();
            channel->d_func()->init(channelPrefix(title, prefixes), unprefixedChannel(title, prefixes));

            q->beginInsertRows(QModelIndex(), channelList.count(), channelList.count());
            channelList.append(channel);
            channelMap.insert(title, channel);
            q->connect(channel, SIGNAL(destroyed(IrcChannel*)), SLOT(_irc_channelDestroyed(IrcChannel*)));
            q->endInsertRows();
            emit q->channelAdded(channel);
            emit q->channelsChanged(channelList);
            emit q->countChanged(channelList.count());
        }
    }
}

void IrcChannelModelPrivate::removeChannel(const QString& title)
{
    Q_Q(IrcChannelModel);
    IrcChannel* channel = channelMap.value(title);
    if (channel)
        q->destroyChannel(channel);
}

bool IrcChannelModelPrivate::processMessage(const QString& title, IrcMessage* message)
{
    IrcChannel* channel = channelMap.value(title);
    if (channel)
        return channel->d_func()->processMessage(message);
    return false;
}

void IrcChannelModelPrivate::_irc_channelDestroyed(IrcChannel* channel)
{
    Q_Q(IrcChannelModel);
    int idx = channelList.indexOf(channel);
    if (idx != -1) {
        q->beginRemoveRows(QModelIndex(), idx, idx);
        channelList.removeAt(idx);
        channelMap.remove(channel->title());
        q->endRemoveRows();
        emit q->channelRemoved(channel);
        emit q->channelsChanged(channelList);
        emit q->countChanged(channelList.count());
    }
}

Q_DECLARE_METATYPE(IrcChannel*)
Q_DECLARE_METATYPE(QList<IrcChannel*>)

/*!
    Constructs a new model with \a parent.

    \note If \a parent is an instance of IrcSession, it will be
    automatically assigned to \ref IrcChannelModel::session "session".
 */
IrcChannelModel::IrcChannelModel(QObject* parent)
    : QAbstractListModel(parent), d_ptr(new IrcChannelModelPrivate(this))
{
    setSession(qobject_cast<IrcSession*>(parent));

    qRegisterMetaType<IrcChannel*>();
    qRegisterMetaType<QList<IrcChannel*> >();
}

/*!
    Destructs the model.
 */
IrcChannelModel::~IrcChannelModel()
{
    Q_D(IrcChannelModel);
    foreach (IrcChannel* channel, d->channelList) {
        channel->blockSignals(true);
        delete channel;
    }
    d->channelList.clear();
    d->channelMap.clear();
}

/*!
    This property holds the session.

    \par Access functions:
    \li \ref IrcSession* session() const
    \li void setSession(\ref IrcSession* session)

    \warning Changing the session on the fly is not supported.
 */
IrcSession* IrcChannelModel::session() const
{
    Q_D(const IrcChannelModel);
    return d->session;
}

void IrcChannelModel::setSession(IrcSession* session)
{
    Q_D(IrcChannelModel);
    if (d->session != session) {
        if (d->session)
            qFatal("IrcChannelModel::setSession(): changing the session on the fly is not supported.");
        d->session = session;
        d->session->installMessageFilter(d);
        emit sessionChanged(session);
    }
}

/*!
    This property holds the number of channels.

    \par Access function:
    \li int count() const

    \par Notifier signal:
    \li void countChanged(int count)
 */
int IrcChannelModel::count() const
{
    return rowCount();
}

/*!
    This property holds the list of channels.

    \par Access function:
    \li QList<\ref IrcChannel*> channels() const

    \par Notifier signal:
    \li void channelsChanged(const QList<\ref IrcChannel*>& channels)
 */
QList<IrcChannel*> IrcChannelModel::channels() const
{
    Q_D(const IrcChannelModel);
    return d->channelList;
}

/*!
    Returns the channel object at \a index.
 */
IrcChannel* IrcChannelModel::get(int index) const
{
    Q_D(const IrcChannelModel);
    return d->channelList.value(index);
}

/*!
    Returns the channel object for \a title.
 */
IrcChannel* IrcChannelModel::channel(const QString& title) const
{
    Q_D(const IrcChannelModel);
    return d->channelMap.value(title);
}

/*!
    Returns \c true if the model contains \a title.
 */
bool IrcChannelModel::contains(const QString& title) const
{
    Q_D(const IrcChannelModel);
    return d->channelMap.contains(title);
}

/*!
    This property holds the display role.

    The specified data role is returned for Qt::DisplayRole.

    The default value is \ref Irc::TitleRole.

    \par Access functions:
    \li \ref Irc::ItemDataRole displayRole() const
    \li void setDisplayRole(\ref Irc::ItemDataRole role)
 */
Irc::ItemDataRole IrcChannelModel::displayRole() const
{
    Q_D(const IrcChannelModel);
    return d->role;
}

void IrcChannelModel::setDisplayRole(Irc::ItemDataRole role)
{
    Q_D(IrcChannelModel);
    d->role = role;
}

/*!
    Clears the model.
 */
void IrcChannelModel::clear()
{
    Q_D(IrcChannelModel);
    if (!d->channelList.isEmpty()) {
        beginResetModel();
        qDeleteAll(d->channelList);
        d->channelList.clear();
        d->channelMap.clear();
        endResetModel();
    }
}

/*!
    Creates a channel object for channel \a title.

    IrcChannelModel will automatically call this factory method when a
    need for the channel object occurs ie. the channel is being joined.

    The default implementation creates a new instance of IrcChannel.
    Reimplement this function in order to alter the default behavior,
    for example to provide a custom IrcChannel subclass.
 */
IrcChannel* IrcChannelModel::createChannel(const QString& title)
{
    Q_UNUSED(title);
    return new IrcChannel(this);
}

/*!
    Destroys the channel \a model.

    IrcChannelModel will automatically call this method when the channel
    object is no longer needed ie. the user quit, the channel was parted,
    or the user was kicked from the channel.

    The default implementation deletes the channel object.
    Reimplement this function in order to alter the default behavior,
    for example to keep the channel object alive.
 */
void IrcChannelModel::destroyChannel(IrcChannel* channel)
{
    delete channel;
}

/*!
    The following role names are provided by default:

    Role             | Name      | Type        | Example
    -----------------|-----------|-------------|--------
    Qt::DisplayRole  | "display" | 1)          | -
    Irc::ChannelRole | "channel" | IrcChannel* | <object>
    Irc::NameRole    | "name"    | QString     | "communi"
    Irc::PrefixRole  | "prefix"  | QString     | "#"
    Irc::TitleRole   | "title"   | QString     | "#communi"

    1) The type depends on \ref displayRole.
 */
QHash<int, QByteArray> IrcChannelModel::roleNames() const
{
    QHash<int, QByteArray> roles;
    roles[Qt::DisplayRole] = "display";
    roles[Irc::ChannelRole] = "channel";
    roles[Irc::NameRole] = "name";
    roles[Irc::PrefixRole] = "prefix";
    roles[Irc::TitleRole] = "title";
    return roles;
}

/*!
    Returns the number of channels.
 */
int IrcChannelModel::rowCount(const QModelIndex& parent) const
{
    if (parent.isValid())
        return 0;

    Q_D(const IrcChannelModel);
    return d->channelList.count();
}

/*!
    Returns the data for specified \a role and user referred to by by the \a index.
 */
QVariant IrcChannelModel::data(const QModelIndex& index, int role) const
{
    Q_D(const IrcChannelModel);
    if (!hasIndex(index.row(), index.column()))
        return QVariant();

    switch (role) {
    case Qt::DisplayRole:
        return data(index, d->role);
    case Irc::ChannelRole:
        return QVariant::fromValue(d->channelList.at(index.row()));
    case Irc::NameRole:
        if (IrcChannel* channel =  d->channelList.at(index.row()))
            return channel->name();
        break;
    case Irc::PrefixRole:
        if (IrcChannel* channel =  d->channelList.at(index.row()))
            return channel->prefix();
        break;
    case Irc::TitleRole:
        if (IrcChannel* channel =  d->channelList.at(index.row()))
            return channel->title();
        break;
    }

    return QVariant();
}

#include "moc_ircchannelmodel.cpp"
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * 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 "ResourceAllocator.h"

#include "private/backend/DriverApi.h"

#include "details/Texture.h"

#include <utils/Log.h>

using namespace utils;

namespace filament {

using namespace backend;
using namespace details;

namespace fg {

// ------------------------------------------------------------------------------------------------

template<typename K, typename V, typename H>
UTILS_NOINLINE
typename ResourceAllocator::AssociativeContainer<K, V, H>::iterator
ResourceAllocator::AssociativeContainer<K, V, H>::erase(iterator it) {
    return mContainer.erase(it);
}

template<typename K, typename V, typename H>
typename ResourceAllocator::AssociativeContainer<K, V, H>::const_iterator
ResourceAllocator::AssociativeContainer<K, V, H>::find(key_type const& key) const {
    return const_cast<AssociativeContainer*>(this)->find(key);
}

template<typename K, typename V, typename H>
UTILS_NOINLINE
typename ResourceAllocator::AssociativeContainer<K, V, H>::iterator
ResourceAllocator::AssociativeContainer<K, V, H>::find(key_type const& key) {
    return std::find_if(mContainer.begin(), mContainer.end(), [&key](auto const& v) {
        return v.first == key;
    });
}

template<typename K, typename V, typename H>
template<typename... ARGS>
UTILS_NOINLINE
void ResourceAllocator::AssociativeContainer<K, V, H>::emplace(ARGS&& ... args) {
    mContainer.emplace_back(std::forward<ARGS>(args)...);
}

// ------------------------------------------------------------------------------------------------

size_t ResourceAllocator::TextureKey::getSize() const noexcept {
    size_t pixelCount = width * height * depth;
    size_t size = pixelCount * FTexture::getFormatSize(format);
    if (levels > 1) {
        // if we have mip-maps we assume the full pyramid
        size += size / 3;
    }
    size_t s = std::max(uint8_t(1), samples);
    if (s > 1) {
        // if we have MSAA, we assume 8 bit extra per pixel
        size += pixelCount;
    }
    return size;
}

ResourceAllocator::ResourceAllocator(DriverApi& driverApi) noexcept
        : mBackend(driverApi) {
}

ResourceAllocator::~ResourceAllocator() noexcept {
    assert(!mTextureCache.size());
    assert(!mInUseTextures.size());
}

void ResourceAllocator::terminate() noexcept {
    assert(!mInUseTextures.size());
    auto& textureCache = mTextureCache;
    for (auto it = textureCache.begin(); it != textureCache.end();) {
        mBackend.destroyTexture(it->second.handle);
        it = textureCache.erase(it);
    }
}

RenderTargetHandle ResourceAllocator::createRenderTarget(const char* name,
        TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
        uint8_t samples, TargetBufferInfo color, TargetBufferInfo depth,
        TargetBufferInfo stencil) noexcept {
    return mBackend.createRenderTarget(targetBufferFlags,
            width, height, samples ? samples : 1u, color, depth, stencil);
}

void ResourceAllocator::destroyRenderTarget(RenderTargetHandle h) noexcept {
    return mBackend.destroyRenderTarget(h);
}

backend::TextureHandle ResourceAllocator::createTexture(const char* name,
        backend::SamplerType target, uint8_t levels,
        backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
        uint32_t depth, backend::TextureUsage usage) noexcept {

    // Some WebGL implementations complain about an incomplete framebuffer when the attachment sizes
    // are heterogeneous. This merits further investigation.
#if defined(__EMSCRIPTEN__)
    if (!(usage & TextureUsage::SAMPLEABLE)) {
        // If this texture is not going to be sampled, we can round its size up
        // this helps prevent many reallocations for small size changes.
        // We round to 16 pixels, which works for 720p btw.
        width  = (width  + 15u) & ~15u;
        height = (height + 15u) & ~15u;
    }
#endif

    // do we have a suitable texture in the cache?
    TextureHandle handle;
    if (mEnabled) {
        auto& textureCache = mTextureCache;
        const TextureKey key{ name, target, levels, format, samples, width, height, depth, usage };
        auto it = textureCache.find(key);
        if (UTILS_LIKELY(it != textureCache.end())) {
            // we do, move the entry to the in-use list, and remove from the cache
            handle = it->second.handle;
            mCacheSize -= it->second.size;
            textureCache.erase(it);
        } else {
            // we don't, allocate a new texture and populate the in-use list
            handle = mBackend.createTexture(
                    target, levels, format, samples, width, height, depth, usage);
        }
        mInUseTextures.emplace(handle, key);
    } else {
        handle = mBackend.createTexture(
                target, levels, format, samples, width, height, depth, usage);
    }
    return handle;
}

void ResourceAllocator::destroyTexture(TextureHandle h) noexcept {
    if (mEnabled) {
        // find the texture in the in-use list (it must be there!)
        auto it = mInUseTextures.find(h);
        assert(it != mInUseTextures.end());

        // move it to the cache
        const TextureKey key = it->second;
        uint32_t size = key.getSize();

        mTextureCache.emplace(key, TextureCachePayload{ h, mAge, size });
        mCacheSize += size;

        // remove it from the in-use list
        mInUseTextures.erase(it);
    } else {
        mBackend.destroyTexture(h);
    }
}

void ResourceAllocator::gc() noexcept {
    // this is called regularly -- usually once per frame of each Renderer

    // increase our age
    const size_t age = mAge++;

    // Purging strategy:
    // + remove entries that are older than a certain age
    // - remove only one entry per gc(), unless we're at capacity

    auto& textureCache = mTextureCache;
    for (auto it = textureCache.begin(); it != textureCache.end();) {
        const size_t ageDiff = age - it->second.age;
        if (ageDiff >= CACHE_MAX_AGE) {
            mBackend.destroyTexture(it->second.handle);
            mCacheSize -= it->second.size;
            //slog.d << "purging " << it->second.handle.getId() << io::endl;
            it = textureCache.erase(it);
            if (mCacheSize < CACHE_CAPACITY) {
                // if we're not at capacity, only purge a single entry per gc, trying to
                // avoid a burst of work.
                break;
            }
        } else {
            ++it;
        }
    }

    //if (mAge % 60 == 0) dump();
    // TODO: maybe purge LRU entries if we have more than a certain number
    // TODO: maybe purge LRU entries if the size of the cache is too large
}

UTILS_NOINLINE
void ResourceAllocator::dump() const noexcept {
    slog.d << "# entries=" << mTextureCache.size() << ", sz=" << mCacheSize / float(1u << 20u)
           << " MiB" << io::endl;
    for (auto const & it : mTextureCache) {
        auto w = it.first.width;
        auto h = it.first.height;
        auto f = FTexture::getFormatSize(it.first.format);
        slog.d << it.first.name << ": w=" << w << ", h=" << h << ", f=" << f << ", sz="
               << it.second.size / float(1u << 20u) << io::endl;
    }
}

} // namespace fg
} // namespace filament
<commit_msg>Repair web samples by fixing typo in previous fix.<commit_after>/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * 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 "ResourceAllocator.h"

#include "private/backend/DriverApi.h"

#include "details/Texture.h"

#include <utils/Log.h>

using namespace utils;

namespace filament {

using namespace backend;
using namespace details;

namespace fg {

// ------------------------------------------------------------------------------------------------

template<typename K, typename V, typename H>
UTILS_NOINLINE
typename ResourceAllocator::AssociativeContainer<K, V, H>::iterator
ResourceAllocator::AssociativeContainer<K, V, H>::erase(iterator it) {
    return mContainer.erase(it);
}

template<typename K, typename V, typename H>
typename ResourceAllocator::AssociativeContainer<K, V, H>::const_iterator
ResourceAllocator::AssociativeContainer<K, V, H>::find(key_type const& key) const {
    return const_cast<AssociativeContainer*>(this)->find(key);
}

template<typename K, typename V, typename H>
UTILS_NOINLINE
typename ResourceAllocator::AssociativeContainer<K, V, H>::iterator
ResourceAllocator::AssociativeContainer<K, V, H>::find(key_type const& key) {
    return std::find_if(mContainer.begin(), mContainer.end(), [&key](auto const& v) {
        return v.first == key;
    });
}

template<typename K, typename V, typename H>
template<typename... ARGS>
UTILS_NOINLINE
void ResourceAllocator::AssociativeContainer<K, V, H>::emplace(ARGS&& ... args) {
    mContainer.emplace_back(std::forward<ARGS>(args)...);
}

// ------------------------------------------------------------------------------------------------

size_t ResourceAllocator::TextureKey::getSize() const noexcept {
    size_t pixelCount = width * height * depth;
    size_t size = pixelCount * FTexture::getFormatSize(format);
    if (levels > 1) {
        // if we have mip-maps we assume the full pyramid
        size += size / 3;
    }
    size_t s = std::max(uint8_t(1), samples);
    if (s > 1) {
        // if we have MSAA, we assume 8 bit extra per pixel
        size += pixelCount;
    }
    return size;
}

ResourceAllocator::ResourceAllocator(DriverApi& driverApi) noexcept
        : mBackend(driverApi) {
}

ResourceAllocator::~ResourceAllocator() noexcept {
    assert(!mTextureCache.size());
    assert(!mInUseTextures.size());
}

void ResourceAllocator::terminate() noexcept {
    assert(!mInUseTextures.size());
    auto& textureCache = mTextureCache;
    for (auto it = textureCache.begin(); it != textureCache.end();) {
        mBackend.destroyTexture(it->second.handle);
        it = textureCache.erase(it);
    }
}

RenderTargetHandle ResourceAllocator::createRenderTarget(const char* name,
        TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
        uint8_t samples, TargetBufferInfo color, TargetBufferInfo depth,
        TargetBufferInfo stencil) noexcept {
    return mBackend.createRenderTarget(targetBufferFlags,
            width, height, samples ? samples : 1u, color, depth, stencil);
}

void ResourceAllocator::destroyRenderTarget(RenderTargetHandle h) noexcept {
    return mBackend.destroyRenderTarget(h);
}

backend::TextureHandle ResourceAllocator::createTexture(const char* name,
        backend::SamplerType target, uint8_t levels,
        backend::TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
        uint32_t depth, backend::TextureUsage usage) noexcept {

    // Some WebGL implementations complain about an incomplete framebuffer when the attachment sizes
    // are heterogeneous. This merits further investigation.
#if !defined(__EMSCRIPTEN__)
    if (!(usage & TextureUsage::SAMPLEABLE)) {
        // If this texture is not going to be sampled, we can round its size up
        // this helps prevent many reallocations for small size changes.
        // We round to 16 pixels, which works for 720p btw.
        width  = (width  + 15u) & ~15u;
        height = (height + 15u) & ~15u;
    }
#endif

    // do we have a suitable texture in the cache?
    TextureHandle handle;
    if (mEnabled) {
        auto& textureCache = mTextureCache;
        const TextureKey key{ name, target, levels, format, samples, width, height, depth, usage };
        auto it = textureCache.find(key);
        if (UTILS_LIKELY(it != textureCache.end())) {
            // we do, move the entry to the in-use list, and remove from the cache
            handle = it->second.handle;
            mCacheSize -= it->second.size;
            textureCache.erase(it);
        } else {
            // we don't, allocate a new texture and populate the in-use list
            handle = mBackend.createTexture(
                    target, levels, format, samples, width, height, depth, usage);
        }
        mInUseTextures.emplace(handle, key);
    } else {
        handle = mBackend.createTexture(
                target, levels, format, samples, width, height, depth, usage);
    }
    return handle;
}

void ResourceAllocator::destroyTexture(TextureHandle h) noexcept {
    if (mEnabled) {
        // find the texture in the in-use list (it must be there!)
        auto it = mInUseTextures.find(h);
        assert(it != mInUseTextures.end());

        // move it to the cache
        const TextureKey key = it->second;
        uint32_t size = key.getSize();

        mTextureCache.emplace(key, TextureCachePayload{ h, mAge, size });
        mCacheSize += size;

        // remove it from the in-use list
        mInUseTextures.erase(it);
    } else {
        mBackend.destroyTexture(h);
    }
}

void ResourceAllocator::gc() noexcept {
    // this is called regularly -- usually once per frame of each Renderer

    // increase our age
    const size_t age = mAge++;

    // Purging strategy:
    // + remove entries that are older than a certain age
    // - remove only one entry per gc(), unless we're at capacity

    auto& textureCache = mTextureCache;
    for (auto it = textureCache.begin(); it != textureCache.end();) {
        const size_t ageDiff = age - it->second.age;
        if (ageDiff >= CACHE_MAX_AGE) {
            mBackend.destroyTexture(it->second.handle);
            mCacheSize -= it->second.size;
            //slog.d << "purging " << it->second.handle.getId() << io::endl;
            it = textureCache.erase(it);
            if (mCacheSize < CACHE_CAPACITY) {
                // if we're not at capacity, only purge a single entry per gc, trying to
                // avoid a burst of work.
                break;
            }
        } else {
            ++it;
        }
    }

    //if (mAge % 60 == 0) dump();
    // TODO: maybe purge LRU entries if we have more than a certain number
    // TODO: maybe purge LRU entries if the size of the cache is too large
}

UTILS_NOINLINE
void ResourceAllocator::dump() const noexcept {
    slog.d << "# entries=" << mTextureCache.size() << ", sz=" << mCacheSize / float(1u << 20u)
           << " MiB" << io::endl;
    for (auto const & it : mTextureCache) {
        auto w = it.first.width;
        auto h = it.first.height;
        auto f = FTexture::getFormatSize(it.first.format);
        slog.d << it.first.name << ": w=" << w << ", h=" << h << ", f=" << f << ", sz="
               << it.second.size / float(1u << 20u) << io::endl;
    }
}

} // namespace fg
} // namespace filament
<|endoftext|>