<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 "irccommandparser.h"
#include <climits>

/*!
    \file irccommandparser.h
    \brief #include <IrcCommandParser>
 */

/*!
    \class IrcCommandParser irccommandparser.h <IrcCommandParser>
    \ingroup utility
    \brief Parses commands from user input.

    \section syntax Syntax

    Since the list of supported commands and the exact syntax for each
    command is application specific, IrcCommandParser does not provide
    any built-in command syntaxes. It is left up to the applications
    to introduce the supported commands and syntaxes.
    IrcCommandParser supports the following command syntax markup:

    Syntax             | Example              | Description
    -------------------|----------------------|------------
    <param>      | <target>       | A required parameter.
    (<param>)    | (<key>)        | An optional parameter.
    <param...>   | <message...>   | A required parameter, multiple words accepted. (1)
    (<param...>) | (<message...>) | An optional parameter, multiple words accepted. (1)
    (<#param>)   | (<#channel>)   | An optional channel parameter. (2)
    [param]            | [target]             | Inject the current target.

    -# Multi-word parameters are only supported in the last parameter position.
    -# An optional channel parameter is filled up with the current channel when absent.

    \note The parameter names are insignificant, but descriptive
    parameter names are recommended for the sake of readability.

    \section context Context

    Notice that commands are often context sensitive. While some command
    may accept an optional parameter that is filled up with the current
    target (channel/query) name when absent, another command may always
    inject the current target name as a certain parameter. Therefore
    IrcCommandParser must be kept up-to-date with the \ref currentTarget
    "current target" and the \ref channels "list of channels".

    \section example Example

    \code
    IrcCommandParser* parser = new IrcCommandParser(this);
    parser->addCommand(IrcCommand::Join, "JOIN <#channel> (<key>)");
    parser->addCommand(IrcCommand::Part, "PART (<#channel>) (<message...>)");
    parser->addCommand(IrcCommand::Kick, "KICK (<#channel>) <nick> (<reason...>)");
    parser->addCommand(IrcCommand::CtcpAction, "ME [target] <message...>");
    parser->addCommand(IrcCommand::CtcpAction, "ACTION <target> <message...>");

    // currently in a query, and also present on some channels
    parser->setCurrentTarget("jpnurmi");
    parser->setChannels(QStringList() << "#communi" << "#freenode");
    \endcode

    \section custom Custom commands

    The parser also supports such custom client specific commands that
    are not sent to the server. Since IrcCommand does not know how to
    handle custom commands, the parser treats them as a special case
    injecting the command as a first parameter.

    \code
    IrcParser parser;
    parser.addCommand(IrcCommand::Custom, "QUERY <user>");
    IrcCommand* command = parser.parse("/query jpnurmi");
    Q_ASSERT(command->type() == IrcCommand::Custom);
    qDebug() << command->parameters; // ("QUERY", "jpnurmi")
    \endcode
 */

struct IrcCommandInfo
{
    IrcCommand::Type type;
    QString command;
    QString syntax;
};

class IrcCommandParserPrivate
{
public:
    IrcCommandParserPrivate() : prefix(QLatin1Char('/')) { }

    QList<IrcCommandInfo> find(const QString& command) const;
    IrcCommand* parse(const IrcCommandInfo& command, QStringList params) const;

    QString prefix;
    QString current;
    QStringList channels;
    QList<IrcCommandInfo> commands;
};

QList<IrcCommandInfo> IrcCommandParserPrivate::find(const QString& command) const
{
    QList<IrcCommandInfo> result;
    foreach (const IrcCommandInfo& cmd, commands) {
        if (cmd.command == command)
            result += cmd;
    }
    return result;
}

static inline bool isOptional(const QString& token)
{
    return token.startsWith(QLatin1Char('(')) && token.endsWith(QLatin1Char(')'));
}

static inline bool isMultiWord(const QString& token)
{
    return token.contains(QLatin1String("..."));
}

static inline bool isChannel(const QString& token)
{
    return token.contains(QLatin1Char('#'));
}

static inline bool isCurrent(const QString& token)
{
    return token.startsWith(QLatin1Char('[')) && token.endsWith(QLatin1Char(']'));
}

IrcCommand* IrcCommandParserPrivate::parse(const IrcCommandInfo& command, QStringList params) const
{
    const QStringList tokens = command.syntax.split(QLatin1Char(' '), QString::SkipEmptyParts);
    const bool onChannel = channels.contains(current, Qt::CaseInsensitive);

    int min = 0;
    int max = tokens.count();

    for (int i = 0; i < tokens.count(); ++i) {
        const QString token = tokens.at(i);
        if (!isOptional(token))
            ++min;
        const bool last = (i == tokens.count() - 1);
        if (last && isMultiWord(token))
            max = INT_MAX;
        if (isOptional(token) && isChannel(token)) {
            if (onChannel) {
                const QString param = params.value(i);
                if (param.isNull() || !channels.contains(param, Qt::CaseInsensitive))
                    params.insert(i, current);
            } else  if (!channels.contains(params.value(i))) {
                return 0;
            }
            ++min;
        } else if (isCurrent(token)) {
            params.insert(i, current);
        }
    }

    if (params.count() >= min && params.count() <= max) {
        IrcCommand* cmd = new IrcCommand;
        cmd->setType(command.type);
        if (command.type == IrcCommand::Custom)
            params.prepend(command.command);
        cmd->setParameters(params);
        return cmd;
    }

    return 0;
}

/*!
    Clears the list of commands.

    \sa reset()
 */
void IrcCommandParser::clear()
{
    Q_D(IrcCommandParser);
    d->commands.clear();
    emit commandsChanged(QStringList());
}

/*!
    Resets the channels and the current target.

    \sa clear()
 */
void IrcCommandParser::reset()
{
    setChannels(QStringList());
    setCurrentTarget(QString());
}

/*!
    Constructs a command parser with \a parent.
 */
IrcCommandParser::IrcCommandParser(QObject* parent) : QObject(parent), d_ptr(new IrcCommandParserPrivate)
{
}

/*!
    Destructs the command parser.
 */
IrcCommandParser::~IrcCommandParser()
{
}

/*!
    This property holds the known commands.

    \par Access function:
    \li QStringList commands() const

    \par Notifier signal:
    \li void commandsChanged(const QStringList& commands)

    \sa addCommand(), removeCommand()
 */
QStringList IrcCommandParser::commands() const
{
    Q_D(const IrcCommandParser);
    QStringList result;
    foreach (const IrcCommandInfo& cmd, d->commands)
        result += cmd.command;
    return result;
}

/*!
    Returns the syntax for the given \a command.
 */
QString IrcCommandParser::syntax(const QString& command) const
{
    Q_D(const IrcCommandParser);
    IrcCommandInfo info = d->find(command.toUpper()).value(0);
    if (!info.command.isEmpty())
        return info.command + QLatin1Char(' ') + info.syntax;
    return QString();
}

/*!
    Adds a command with \a type and \a syntax.
 */
void IrcCommandParser::addCommand(IrcCommand::Type type, const QString& syntax)
{
    Q_D(IrcCommandParser);
    QStringList words = syntax.split(QLatin1Char(' '), QString::SkipEmptyParts);
    if (!words.isEmpty()) {
        IrcCommandInfo cmd;
        cmd.type = type;
        cmd.command = words.takeFirst().toUpper();
        cmd.syntax = words.join(QLatin1String(" "));
        d->commands += cmd;
        emit commandsChanged(commands());
    }
}

/*!
    Removes the command with \a type and \a syntax.
 */
void IrcCommandParser::removeCommand(IrcCommand::Type type, const QString& syntax)
{
    Q_D(IrcCommandParser);
    int count = d->commands.count();
    QMutableListIterator<IrcCommandInfo> it(d->commands);
    while (it.hasNext()) {
        IrcCommandInfo cmd = it.next();
        if (cmd.type == type && (syntax.isEmpty() || !syntax.compare(cmd.syntax, Qt::CaseInsensitive)))
            it.remove();
    }
    if (d->commands.count() != count)
        emit commandsChanged(commands());
}

/*!
    This property holds the available channels.

    \par Access function:
    \li QStringList channels() const
    \li void setChannels(const QStringList& channels)

    \par Notifier signal:
    \li void channelsChanged(const QStringList& channels)
 */
QStringList IrcCommandParser::channels() const
{
    Q_D(const IrcCommandParser);
    return d->channels;
}

void IrcCommandParser::setChannels(const QStringList& channels)
{
    Q_D(IrcCommandParser);
    if (d->channels != channels) {
        d->channels = channels;
        emit channelsChanged(channels);
    }
}

/*!
    This property holds the current target.

    \par Access function:
    \li QString currentTarget() const
    \li void setCurrentTarget(const QString& target)

    \par Notifier signal:
    \li void currentTargetChanged(const QString& target)
 */
QString IrcCommandParser::currentTarget() const
{
    Q_D(const IrcCommandParser);
    return d->current;
}

void IrcCommandParser::setCurrentTarget(const QString& target)
{
    Q_D(IrcCommandParser);
    if (d->current != target) {
        d->current = target;
        emit currentTargetChanged(target);
    }
}

/*!
    This property holds the command prefix.

    The default value is "/".

    \par Access function:
    \li QString prefix() const
    \li void setPrefix(const QString& prefix)

    \par Notifier signal:
    \li void prefixChanged(const QString& prefix)
 */
QString IrcCommandParser::prefix() const
{
    Q_D(const IrcCommandParser);
    return d->prefix;
}

void IrcCommandParser::setPrefix(const QString& prefix)
{
    Q_D(IrcCommandParser);
    if (d->prefix != prefix) {
        d->prefix = prefix;
        emit prefixChanged(prefix);
    }
}

static bool isMessage(const QString& input, const QString& prefix)
{
    if (prefix.isEmpty())
        return false;

    return !input.startsWith(prefix) || input.startsWith(prefix.repeated(2)) || input.startsWith(prefix + QLatin1Char(' '));
}

/*!
    Parses and returns the command for \a input, or \c 0 if the input is not valid.
 */
IrcCommand* IrcCommandParser::parse(const QString& input) const
{
    Q_D(const IrcCommandParser);
    if (isMessage(input, d->prefix)) {
        QString message = input;
        if (message.startsWith(d->prefix))
            message.remove(0, 1);
        return IrcCommand::createMessage(d->current, message.trimmed());
    } else {
        QStringList params = input.mid(d->prefix.length()).split(QLatin1Char(' '), QString::SkipEmptyParts);
        if (!params.isEmpty()) {
            const QString& command = params.takeFirst().toUpper();
            foreach (const IrcCommandInfo& c, d->find(command)) {
                IrcCommand* cmd = d->parse(c, params);
                if (cmd)
                    return cmd;
            }
        }
    }
    return 0;
}
<commit_msg>IrcCommandParser: disallow empty input<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 "irccommandparser.h"
#include <climits>

/*!
    \file irccommandparser.h
    \brief #include <IrcCommandParser>
 */

/*!
    \class IrcCommandParser irccommandparser.h <IrcCommandParser>
    \ingroup utility
    \brief Parses commands from user input.

    \section syntax Syntax

    Since the list of supported commands and the exact syntax for each
    command is application specific, IrcCommandParser does not provide
    any built-in command syntaxes. It is left up to the applications
    to introduce the supported commands and syntaxes.
    IrcCommandParser supports the following command syntax markup:

    Syntax             | Example              | Description
    -------------------|----------------------|------------
    <param>      | <target>       | A required parameter.
    (<param>)    | (<key>)        | An optional parameter.
    <param...>   | <message...>   | A required parameter, multiple words accepted. (1)
    (<param...>) | (<message...>) | An optional parameter, multiple words accepted. (1)
    (<#param>)   | (<#channel>)   | An optional channel parameter. (2)
    [param]            | [target]             | Inject the current target.

    -# Multi-word parameters are only supported in the last parameter position.
    -# An optional channel parameter is filled up with the current channel when absent.

    \note The parameter names are insignificant, but descriptive
    parameter names are recommended for the sake of readability.

    \section context Context

    Notice that commands are often context sensitive. While some command
    may accept an optional parameter that is filled up with the current
    target (channel/query) name when absent, another command may always
    inject the current target name as a certain parameter. Therefore
    IrcCommandParser must be kept up-to-date with the \ref currentTarget
    "current target" and the \ref channels "list of channels".

    \section example Example

    \code
    IrcCommandParser* parser = new IrcCommandParser(this);
    parser->addCommand(IrcCommand::Join, "JOIN <#channel> (<key>)");
    parser->addCommand(IrcCommand::Part, "PART (<#channel>) (<message...>)");
    parser->addCommand(IrcCommand::Kick, "KICK (<#channel>) <nick> (<reason...>)");
    parser->addCommand(IrcCommand::CtcpAction, "ME [target] <message...>");
    parser->addCommand(IrcCommand::CtcpAction, "ACTION <target> <message...>");

    // currently in a query, and also present on some channels
    parser->setCurrentTarget("jpnurmi");
    parser->setChannels(QStringList() << "#communi" << "#freenode");
    \endcode

    \section custom Custom commands

    The parser also supports such custom client specific commands that
    are not sent to the server. Since IrcCommand does not know how to
    handle custom commands, the parser treats them as a special case
    injecting the command as a first parameter.

    \code
    IrcParser parser;
    parser.addCommand(IrcCommand::Custom, "QUERY <user>");
    IrcCommand* command = parser.parse("/query jpnurmi");
    Q_ASSERT(command->type() == IrcCommand::Custom);
    qDebug() << command->parameters; // ("QUERY", "jpnurmi")
    \endcode
 */

struct IrcCommandInfo
{
    IrcCommand::Type type;
    QString command;
    QString syntax;
};

class IrcCommandParserPrivate
{
public:
    IrcCommandParserPrivate() : prefix(QLatin1Char('/')) { }

    QList<IrcCommandInfo> find(const QString& command) const;
    IrcCommand* parse(const IrcCommandInfo& command, QStringList params) const;

    QString prefix;
    QString current;
    QStringList channels;
    QList<IrcCommandInfo> commands;
};

QList<IrcCommandInfo> IrcCommandParserPrivate::find(const QString& command) const
{
    QList<IrcCommandInfo> result;
    foreach (const IrcCommandInfo& cmd, commands) {
        if (cmd.command == command)
            result += cmd;
    }
    return result;
}

static inline bool isOptional(const QString& token)
{
    return token.startsWith(QLatin1Char('(')) && token.endsWith(QLatin1Char(')'));
}

static inline bool isMultiWord(const QString& token)
{
    return token.contains(QLatin1String("..."));
}

static inline bool isChannel(const QString& token)
{
    return token.contains(QLatin1Char('#'));
}

static inline bool isCurrent(const QString& token)
{
    return token.startsWith(QLatin1Char('[')) && token.endsWith(QLatin1Char(']'));
}

IrcCommand* IrcCommandParserPrivate::parse(const IrcCommandInfo& command, QStringList params) const
{
    const QStringList tokens = command.syntax.split(QLatin1Char(' '), QString::SkipEmptyParts);
    const bool onChannel = channels.contains(current, Qt::CaseInsensitive);

    int min = 0;
    int max = tokens.count();

    for (int i = 0; i < tokens.count(); ++i) {
        const QString token = tokens.at(i);
        if (!isOptional(token))
            ++min;
        const bool last = (i == tokens.count() - 1);
        if (last && isMultiWord(token))
            max = INT_MAX;
        if (isOptional(token) && isChannel(token)) {
            if (onChannel) {
                const QString param = params.value(i);
                if (param.isNull() || !channels.contains(param, Qt::CaseInsensitive))
                    params.insert(i, current);
            } else  if (!channels.contains(params.value(i))) {
                return 0;
            }
            ++min;
        } else if (isCurrent(token)) {
            params.insert(i, current);
        }
    }

    if (params.count() >= min && params.count() <= max) {
        IrcCommand* cmd = new IrcCommand;
        cmd->setType(command.type);
        if (command.type == IrcCommand::Custom)
            params.prepend(command.command);
        cmd->setParameters(params);
        return cmd;
    }

    return 0;
}

/*!
    Clears the list of commands.

    \sa reset()
 */
void IrcCommandParser::clear()
{
    Q_D(IrcCommandParser);
    d->commands.clear();
    emit commandsChanged(QStringList());
}

/*!
    Resets the channels and the current target.

    \sa clear()
 */
void IrcCommandParser::reset()
{
    setChannels(QStringList());
    setCurrentTarget(QString());
}

/*!
    Constructs a command parser with \a parent.
 */
IrcCommandParser::IrcCommandParser(QObject* parent) : QObject(parent), d_ptr(new IrcCommandParserPrivate)
{
}

/*!
    Destructs the command parser.
 */
IrcCommandParser::~IrcCommandParser()
{
}

/*!
    This property holds the known commands.

    \par Access function:
    \li QStringList commands() const

    \par Notifier signal:
    \li void commandsChanged(const QStringList& commands)

    \sa addCommand(), removeCommand()
 */
QStringList IrcCommandParser::commands() const
{
    Q_D(const IrcCommandParser);
    QStringList result;
    foreach (const IrcCommandInfo& cmd, d->commands)
        result += cmd.command;
    return result;
}

/*!
    Returns the syntax for the given \a command.
 */
QString IrcCommandParser::syntax(const QString& command) const
{
    Q_D(const IrcCommandParser);
    IrcCommandInfo info = d->find(command.toUpper()).value(0);
    if (!info.command.isEmpty())
        return info.command + QLatin1Char(' ') + info.syntax;
    return QString();
}

/*!
    Adds a command with \a type and \a syntax.
 */
void IrcCommandParser::addCommand(IrcCommand::Type type, const QString& syntax)
{
    Q_D(IrcCommandParser);
    QStringList words = syntax.split(QLatin1Char(' '), QString::SkipEmptyParts);
    if (!words.isEmpty()) {
        IrcCommandInfo cmd;
        cmd.type = type;
        cmd.command = words.takeFirst().toUpper();
        cmd.syntax = words.join(QLatin1String(" "));
        d->commands += cmd;
        emit commandsChanged(commands());
    }
}

/*!
    Removes the command with \a type and \a syntax.
 */
void IrcCommandParser::removeCommand(IrcCommand::Type type, const QString& syntax)
{
    Q_D(IrcCommandParser);
    int count = d->commands.count();
    QMutableListIterator<IrcCommandInfo> it(d->commands);
    while (it.hasNext()) {
        IrcCommandInfo cmd = it.next();
        if (cmd.type == type && (syntax.isEmpty() || !syntax.compare(cmd.syntax, Qt::CaseInsensitive)))
            it.remove();
    }
    if (d->commands.count() != count)
        emit commandsChanged(commands());
}

/*!
    This property holds the available channels.

    \par Access function:
    \li QStringList channels() const
    \li void setChannels(const QStringList& channels)

    \par Notifier signal:
    \li void channelsChanged(const QStringList& channels)
 */
QStringList IrcCommandParser::channels() const
{
    Q_D(const IrcCommandParser);
    return d->channels;
}

void IrcCommandParser::setChannels(const QStringList& channels)
{
    Q_D(IrcCommandParser);
    if (d->channels != channels) {
        d->channels = channels;
        emit channelsChanged(channels);
    }
}

/*!
    This property holds the current target.

    \par Access function:
    \li QString currentTarget() const
    \li void setCurrentTarget(const QString& target)

    \par Notifier signal:
    \li void currentTargetChanged(const QString& target)
 */
QString IrcCommandParser::currentTarget() const
{
    Q_D(const IrcCommandParser);
    return d->current;
}

void IrcCommandParser::setCurrentTarget(const QString& target)
{
    Q_D(IrcCommandParser);
    if (d->current != target) {
        d->current = target;
        emit currentTargetChanged(target);
    }
}

/*!
    This property holds the command prefix.

    The default value is "/".

    \par Access function:
    \li QString prefix() const
    \li void setPrefix(const QString& prefix)

    \par Notifier signal:
    \li void prefixChanged(const QString& prefix)
 */
QString IrcCommandParser::prefix() const
{
    Q_D(const IrcCommandParser);
    return d->prefix;
}

void IrcCommandParser::setPrefix(const QString& prefix)
{
    Q_D(IrcCommandParser);
    if (d->prefix != prefix) {
        d->prefix = prefix;
        emit prefixChanged(prefix);
    }
}

static bool isMessage(const QString& input, const QString& prefix)
{
    if (prefix.isEmpty() || input.isEmpty())
        return false;

    return !input.startsWith(prefix) || input.startsWith(prefix.repeated(2)) || input.startsWith(prefix + QLatin1Char(' '));
}

/*!
    Parses and returns the command for \a input, or \c 0 if the input is not valid.
 */
IrcCommand* IrcCommandParser::parse(const QString& input) const
{
    Q_D(const IrcCommandParser);
    if (isMessage(input, d->prefix)) {
        QString message = input;
        if (message.startsWith(d->prefix))
            message.remove(0, 1);
        return IrcCommand::createMessage(d->current, message.trimmed());
    } else {
        QStringList params = input.mid(d->prefix.length()).split(QLatin1Char(' '), QString::SkipEmptyParts);
        if (!params.isEmpty()) {
            const QString& command = params.takeFirst().toUpper();
            foreach (const IrcCommandInfo& c, d->find(command)) {
                IrcCommand* cmd = d->parse(c, params);
                if (cmd)
                    return cmd;
            }
        }
    }
    return 0;
}
<|endoftext|>


<commit_before>#include <csc_coder.h>
#include <stdio.h>
#include <stdlib.h>

int Coder::Init(MemIO *io)
{
    rc_low_ = 0;
    rc_range_ = 0xFFFFFFFF;
    rc_cachesize_ = 1;
    rc_cache_ = 0;
    rc_code_=0;

    rc_size_ = bc_size_ = 0;
    bc_curbits_ = bc_curval_ =0;

    outsize_ = 0;

    io_ = io;
    rc_bufsize_ = bc_bufsize_ = io->GetBlockSize();
    prc_ = rc_buf_ = (uint8_t *)malloc(rc_bufsize_);
    pbc_ = bc_buf_ = (uint8_t *)malloc(bc_bufsize_);

    if (rc_buf_ && bc_buf_)
        return 0;
    else {
        free(rc_buf_);
        free(bc_buf_);
        return -1;
    }
}

void Coder::Destroy()
{
    free(rc_buf_);
    free(bc_buf_);
}

void Coder::Flush()
{
    for (int i=0;i<5;i++) // One more byte for EOF
    {
        RC_ShiftLow();
    }
    prc_++;
    rc_size_++;

    //one more byte for bitcoder is to prevent overflow while reading
    for(int i = 0; i < 2; i++) {
        if (i == 1)
            *pbc_++=0;
        else
            *pbc_++ = (bc_curval_ << (8 - bc_curbits_)) & 0xFF;
        bc_size_++;
        BCWCheckBound();
    }

    outsize_ += rc_size_ + bc_size_;
    io_->WriteRCData(rc_buf_,rc_size_);
    io_->WriteBCData(bc_buf_,bc_size_);

    rc_low_ = 0;
    rc_range_ = 0xFFFFFFFF;
    rc_cachesize_ = 1;
    rc_cache_ = 0;
    rc_code_=0;
    rc_size_ = bc_size_ = 0;
    bc_curbits_ = bc_curval_ = 0;
    prc_ = rc_buf_;
    pbc_ = bc_buf_;
}

void Coder::EncDirect16(uint32_t val,uint32_t len)
{
    bc_curval_ = (bc_curval_ << len) | val;
    bc_curbits_ += len;
    while(bc_curbits_ >= 8)
    {
        *pbc_++ = (bc_curval_ >> (bc_curbits_ - 8)) & 0xFF;
        bc_size_++;
        BCWCheckBound();
        bc_curbits_ -= 8;
    }
}

void Coder::RC_ShiftLow(void)
{
    static uint8_t temp;
    if ((uint32_t)rc_low_ < (uint32_t)0xFF000000 || (int32_t)(rc_low_ >> 32) != 0)
    {
        temp = rc_cache_;
        do
        {
            *prc_++ = (uint8_t)(temp + (uint8_t)(rc_low_ >> 32));
            rc_size_++;
            if (rc_size_ == rc_bufsize_)
            {
                outsize_+=rc_size_;
                io_->WriteRCData(rc_buf_,rc_bufsize_);
                rc_size_=0;
                prc_=rc_buf_;
            }
            temp = 0xFF;
        }
        while (--rc_cachesize_ != 0);
        rc_cache_ = (uint8_t)((uint32_t)rc_low_ >> 24);
    }
    rc_cachesize_++;
    rc_low_ = (uint32_t)rc_low_ << 8;
}

<commit_msg>Fix non-thread-safe variable<commit_after>#include <csc_coder.h>
#include <stdio.h>
#include <stdlib.h>

int Coder::Init(MemIO *io)
{
    rc_low_ = 0;
    rc_range_ = 0xFFFFFFFF;
    rc_cachesize_ = 1;
    rc_cache_ = 0;
    rc_code_=0;

    rc_size_ = bc_size_ = 0;
    bc_curbits_ = bc_curval_ =0;

    outsize_ = 0;

    io_ = io;
    rc_bufsize_ = bc_bufsize_ = io->GetBlockSize();
    prc_ = rc_buf_ = (uint8_t *)malloc(rc_bufsize_);
    pbc_ = bc_buf_ = (uint8_t *)malloc(bc_bufsize_);

    if (rc_buf_ && bc_buf_)
        return 0;
    else {
        free(rc_buf_);
        free(bc_buf_);
        return -1;
    }
}

void Coder::Destroy()
{
    free(rc_buf_);
    free(bc_buf_);
}

void Coder::Flush()
{
    for (int i=0;i<5;i++) // One more byte for EOF
    {
        RC_ShiftLow();
    }
    prc_++;
    rc_size_++;

    //one more byte for bitcoder is to prevent overflow while reading
    for(int i = 0; i < 2; i++) {
        if (i == 1)
            *pbc_++=0;
        else
            *pbc_++ = (bc_curval_ << (8 - bc_curbits_)) & 0xFF;
        bc_size_++;
        BCWCheckBound();
    }

    outsize_ += rc_size_ + bc_size_;
    io_->WriteRCData(rc_buf_,rc_size_);
    io_->WriteBCData(bc_buf_,bc_size_);

    rc_low_ = 0;
    rc_range_ = 0xFFFFFFFF;
    rc_cachesize_ = 1;
    rc_cache_ = 0;
    rc_code_=0;
    rc_size_ = bc_size_ = 0;
    bc_curbits_ = bc_curval_ = 0;
    prc_ = rc_buf_;
    pbc_ = bc_buf_;
}

void Coder::EncDirect16(uint32_t val,uint32_t len)
{
    bc_curval_ = (bc_curval_ << len) | val;
    bc_curbits_ += len;
    while(bc_curbits_ >= 8)
    {
        *pbc_++ = (bc_curval_ >> (bc_curbits_ - 8)) & 0xFF;
        bc_size_++;
        BCWCheckBound();
        bc_curbits_ -= 8;
    }
}

void Coder::RC_ShiftLow(void)
{
    uint8_t temp;
    if ((uint32_t)rc_low_ < (uint32_t)0xFF000000 || (int32_t)(rc_low_ >> 32) != 0)
    {
        temp = rc_cache_;
        do
        {
            *prc_++ = (uint8_t)(temp + (uint8_t)(rc_low_ >> 32));
            rc_size_++;
            if (rc_size_ == rc_bufsize_)
            {
                outsize_+=rc_size_;
                io_->WriteRCData(rc_buf_,rc_bufsize_);
                rc_size_=0;
                prc_=rc_buf_;
            }
            temp = 0xFF;
        }
        while (--rc_cachesize_ != 0);
        rc_cache_ = (uint8_t)((uint32_t)rc_low_ >> 24);
    }
    rc_cachesize_++;
    rc_low_ = (uint32_t)rc_low_ << 8;
}

<|endoftext|>


<commit_before>/****************************************************************************

This file is part of the QtMediaHub project on http://www.gitorious.org.

Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
All rights reserved.

Contact:  Nokia Corporation (qt-info@nokia.com)**

You may use this file under the terms of the BSD license as follows:

"Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Nokia Corporation and its Subsidiary(-ies) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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 "mediamodel.h"
#include "mediascanner.h"
#include "dbreader.h"
#include "backend.h"
#include <sqlite3.h>

#define DEBUG if (0) qDebug() << this << __PRETTY_FUNCTION__

MediaModel::MediaModel(QObject *parent)
    : QAbstractItemModel(parent), m_loading(false), m_loaded(false), m_reader(0), m_readerThread(0)
{
    m_refreshTimer.setInterval(10000);
    connect(&m_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));

    MediaScanner *scanner = Backend::instance()->mediaScanner();
    connect(scanner, SIGNAL(scanStarted(QString)), this, SLOT(handleScanStarted(QString)));
    connect(scanner, SIGNAL(scanFinished(QString)), this, SLOT(handleScanFinished(QString)));
    connect(scanner, SIGNAL(searchPathRemoved(QString, QString)), this, SLOT(handleScanFinished(QString)));

    m_readerThread = new QThread(this);
    m_readerThread->start();
}

MediaModel::~MediaModel()
{
    if (m_reader) {
        m_reader->stop();
        m_reader->deleteLater();
        m_readerThread->quit();
        m_readerThread->wait();
    }
}

QString MediaModel::part() const
{
    return m_structure.split("|").value(m_cursor.length());
}

QString MediaModel::mediaType() const
{
    return m_mediaType;
}

void MediaModel::setMediaType(const QString &type)
{
    if (type == m_mediaType)
        return;

    DEBUG << type;

    m_mediaType = type;
    emit mediaTypeChanged();

    // Add the fields of the table as role names
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    QSqlRecord record = driver->record(m_mediaType);
    if (record.isEmpty())
        qWarning() << "Table " << type << " is not valid it seems";

    QHash<int, QByteArray> hash = roleNames();
    hash.insert(DotDotRole, "dotdot");
    hash.insert(IsLeafRole, "isLeaf");
    hash.insert(ModelIndexRole,"modelIndex");
    hash.insert(PreviewUrlRole, "previewUrl");

    for (int i = 0; i < record.count(); i++) {
        hash.insert(FieldRolesBegin + i, record.fieldName(i).toUtf8());
        m_fieldToRole.insert(record.fieldName(i), FieldRolesBegin + i);
    }

    setRoleNames(hash);

    reload();
}

QString MediaModel::structure() const
{
    return m_structure;
}

void MediaModel::setStructure(const QString &str)
{
    if (str == m_structure)
        return;
    DEBUG << str;
    m_structure = str;
    m_layoutInfo.clear();
    foreach(const QString &part, m_structure.split("|"))
        m_layoutInfo.append(part.split(","));

    reload();

    emit structureChanged();
}

void MediaModel::enter(int index)
{
    if (m_cursor.count() + 1 == m_layoutInfo.count() && !m_data.at(index)[DotDotRole].toBool() /* up on leaf node is OK */) {
        DEBUG << "Refusing to enter leaf node";
        return;
    }

    if (m_data.at(index)[DotDotRole].toBool() && !m_cursor.isEmpty()) {
        back();
        return;
    }

    DEBUG << "Entering " << index;
    m_cursor.append(m_data[index]);
    reload();
    emit partChanged();
}

void MediaModel::back(int count)
{
    if (count == 0 || m_cursor.count() < 1)
        return;

    if (m_cursor.count() > count) {
        for (int i = 0; i < count; ++i)
            m_cursor.removeLast();
    } else {
        m_cursor.clear();
    }
    reload();
    emit partChanged();
}

QVariant MediaModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role == ModelIndexRole)
        return qVariantFromValue(index);

    return m_data.value(index.row()).value(role);
}

QModelIndex MediaModel::index(int row, int col, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    return createIndex(row, col);
}

QModelIndex MediaModel::parent(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return QModelIndex();
}

int MediaModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_data.count();
}

int MediaModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 1;
}

bool MediaModel::hasChildren(const QModelIndex &parent) const
{
    return !parent.isValid();
}

bool MediaModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid() || m_mediaType.isEmpty() || m_layoutInfo.isEmpty()) {
        DEBUG << "false " << parent.isValid() << m_mediaType.isEmpty() << m_layoutInfo.isEmpty();
        return false;
    }
    DEBUG << (!m_loading && !m_loaded);
    return !m_loading && !m_loaded;
}

void MediaModel::fetchMore(const QModelIndex &parent)
{
    if (!canFetchMore(parent))
        return;

    DEBUG << "";

    m_loading = true;

    QSqlQuery q = buildQuery();
    DEBUG << q.lastQuery();
    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

void MediaModel::createNewDbReader()
{
    DEBUG << "";

    if (m_reader) {
        disconnect(m_reader, 0, this, 0);
        m_reader->stop();
        m_reader->deleteLater();
    }
    m_reader = new DbReader;
    m_reader->moveToThread(m_readerThread);

    QMetaObject::invokeMethod(m_reader, "initialize", Q_ARG(QSqlDatabase, Backend::instance()->mediaDatabase()));
    connect(m_reader, SIGNAL(dataReady(DbReader *, QList<QSqlRecord>, void *)),
            this, SLOT(handleDataReady(DbReader *, QList<QSqlRecord>, void *)));
}

void MediaModel::reload()
{
    createNewDbReader();

    beginResetModel();
    m_loading = m_loaded = false;
    m_data.clear();
    endResetModel();
}

QHash<int, QVariant> MediaModel::dataFromRecord(const QSqlRecord &record) const
{
    QHash<int, QVariant> data;
    for (int j = 0; j < record.count(); j++) {
        int role = m_fieldToRole.value(record.fieldName(j));
        if (record.fieldName(j) == "uri")
            data.insert(role, QUrl::fromEncoded(record.value(j).toByteArray()));
        else
            data.insert(role, record.value(j));
    }

    // Provide 'display' role as , separated values
    QStringList cols = m_layoutInfo.value(m_cursor.count());
    QStringList displayString;
    for (int j = 0; j < cols.count(); j++) {
        displayString << record.value(cols[j]).toString();
    }
    data.insert(Qt::DisplayRole, displayString.join(", "));
    data.insert(DotDotRole, false);
    data.insert(IsLeafRole, m_cursor.count() + 1 == m_layoutInfo.count());

    data.insert(PreviewUrlRole, QUrl::fromEncoded(record.value("thumbnail").toByteArray()));
    return data;
}

void MediaModel::handleDataReady(DbReader *reader, const QList<QSqlRecord> &records, void *node)
{
    Q_ASSERT(reader == m_reader);
    Q_UNUSED(reader);
    Q_UNUSED(node);

    DEBUG << "Received response from db of size " << records.size();

    if (records.isEmpty())
        return;

    if (m_data.isEmpty()) {
        insertAll(records);
    } else {
        insertNew(records);
    }

    m_loading = false;
    m_loaded = true;
}

void MediaModel::insertAll(const QList<QSqlRecord> &records)
{
    if (!m_cursor.isEmpty()) {
        beginInsertRows(QModelIndex(), 0, records.count());
    } else {
        beginInsertRows(QModelIndex(), 0, records.count() - 1);
    }

    for (int i = 0; i < records.count(); i++) {
        QHash<int, QVariant> data = dataFromRecord(records[i]);
        m_data.append(data);
    }

    if (!m_cursor.isEmpty()) {
        QHash<int, QVariant> data;
        data.insert(Qt::DisplayRole, tr(".."));
        data.insert(DotDotRole, true);
        data.insert(IsLeafRole, false);
        m_data.append(data);
    }

    endInsertRows();
}

int MediaModel::compareData(int idx, const QSqlRecord &record) const
{
    const QHash<int, QVariant> &curData = m_data[idx];
    QStringList cols = m_layoutInfo.value(m_cursor.count());
    foreach(const QString &col, cols) {
        const int role = m_fieldToRole.value(col);
        int cmp = QString::compare(curData.value(role).toString(), record.value(col).toString(), Qt::CaseInsensitive); // ## must use sqlite's compare
        if (cmp != 0)
            return cmp;
    }
    return 0;
}

void MediaModel::insertNew(const QList<QSqlRecord> &records)
{
    int curIdx = 0;

    for (int i = 0; i < records.count(); i++) {
        const QHash<int, QVariant> &curData = m_data[curIdx];
        const QSqlRecord &record = records[i];
        int cmp = compareData(curIdx, record);

        QHash<int, QVariant> data = dataFromRecord(records[i]);
        // ## assumes that only inserts happenned in the database
        if (cmp != 0) {
            beginInsertRows(QModelIndex(), curIdx, curIdx);
            m_data.insert(curIdx, data);
            endInsertRows();
        } else if (curData != data) { // update?
            m_data[curIdx] = data;
            emit dataChanged(createIndex(curIdx, 0), createIndex(curIdx, 0));
        }
        ++curIdx;
    }
}

QSqlQuery MediaModel::buildQuery() const
{
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();

    QStringList escapedCurParts;
    QStringList curParts = m_layoutInfo[m_cursor.count()];
    for (int i = 0; i < curParts.count(); i++)
        escapedCurParts.append(driver->escapeIdentifier(curParts[i], QSqlDriver::FieldName));
    QString escapedCurPart = escapedCurParts.join(",");

    QSqlQuery query(Backend::instance()->mediaDatabase());
    query.setForwardOnly(true);

    QStringList placeHolders;
    const bool lastPart = m_cursor.count() == m_layoutInfo.count()-1;
    QString queryString;
    // SQLite allows us to select columns that are not present in the GROUP BY. We use this feature
    // to select thumbnails for non-leaf nodes
    queryString.append("SELECT *");
    queryString.append(" FROM " + driver->escapeIdentifier(m_mediaType, QSqlDriver::TableName));

    if (!m_cursor.isEmpty()) {
        QStringList where;
        for (int i = 0; i < m_cursor.count(); i++) {
            QStringList subParts = m_layoutInfo[i];
            for (int j = 0; j < subParts.count(); j++) {
                where.append(subParts[j] + " = ?");
                const int role = m_fieldToRole.value(subParts[j]);
                placeHolders << m_cursor[i].value(role).toString();
            }
        }

        queryString.append(" WHERE " + where.join(" AND "));
    }

    if (!lastPart)
        queryString.append(" GROUP BY " + escapedCurPart);

    queryString.append(" ORDER BY " + escapedCurPart + " COLLATE NOCASE");

    query.prepare(queryString);

    foreach(const QString &placeHolder, placeHolders)
        query.addBindValue(placeHolder);

    return query;
}

void MediaModel::handleScanStarted(const QString &type)
{
    if (type != m_mediaType)
        return;

    m_refreshTimer.start();
}

void MediaModel::handleScanFinished(const QString &type)
{
    if (type != m_mediaType)
        return;
    m_refreshTimer.stop();
    refresh();
}

void MediaModel::refresh()
{
    createNewDbReader();

    m_loading = true;
    m_loaded = false;

    QSqlQuery q = buildQuery();
    DEBUG << m_mediaType << q.lastQuery();
    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

<commit_msg>Fix incremental update code to handle removal of media items in db<commit_after>/****************************************************************************

This file is part of the QtMediaHub project on http://www.gitorious.org.

Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).*
All rights reserved.

Contact:  Nokia Corporation (qt-info@nokia.com)**

You may use this file under the terms of the BSD license as follows:

"Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* 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.
* Neither the name of Nokia Corporation and its Subsidiary(-ies) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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 "mediamodel.h"
#include "mediascanner.h"
#include "dbreader.h"
#include "backend.h"
#include <sqlite3.h>

#define DEBUG if (1) qDebug() << this << __PRETTY_FUNCTION__

MediaModel::MediaModel(QObject *parent)
    : QAbstractItemModel(parent), m_loading(false), m_loaded(false), m_reader(0), m_readerThread(0)
{
    m_refreshTimer.setInterval(10000);
    connect(&m_refreshTimer, SIGNAL(timeout()), this, SLOT(refresh()));

    MediaScanner *scanner = Backend::instance()->mediaScanner();
    connect(scanner, SIGNAL(scanStarted(QString)), this, SLOT(handleScanStarted(QString)));
    connect(scanner, SIGNAL(scanFinished(QString)), this, SLOT(handleScanFinished(QString)));
    connect(scanner, SIGNAL(searchPathRemoved(QString, QString)), this, SLOT(handleScanFinished(QString)));

    m_readerThread = new QThread(this);
    m_readerThread->start();
}

MediaModel::~MediaModel()
{
    if (m_reader) {
        m_reader->stop();
        m_reader->deleteLater();
        m_readerThread->quit();
        m_readerThread->wait();
    }
}

QString MediaModel::part() const
{
    return m_structure.split("|").value(m_cursor.length());
}

QString MediaModel::mediaType() const
{
    return m_mediaType;
}

void MediaModel::setMediaType(const QString &type)
{
    if (type == m_mediaType)
        return;

    DEBUG << type;

    m_mediaType = type;
    emit mediaTypeChanged();

    // Add the fields of the table as role names
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();
    QSqlRecord record = driver->record(m_mediaType);
    if (record.isEmpty())
        qWarning() << "Table " << type << " is not valid it seems";

    QHash<int, QByteArray> hash = roleNames();
    hash.insert(DotDotRole, "dotdot");
    hash.insert(IsLeafRole, "isLeaf");
    hash.insert(ModelIndexRole,"modelIndex");
    hash.insert(PreviewUrlRole, "previewUrl");

    for (int i = 0; i < record.count(); i++) {
        hash.insert(FieldRolesBegin + i, record.fieldName(i).toUtf8());
        m_fieldToRole.insert(record.fieldName(i), FieldRolesBegin + i);
    }

    setRoleNames(hash);

    reload();
}

QString MediaModel::structure() const
{
    return m_structure;
}

void MediaModel::setStructure(const QString &str)
{
    if (str == m_structure)
        return;
    DEBUG << str;
    m_structure = str;
    m_layoutInfo.clear();
    foreach(const QString &part, m_structure.split("|"))
        m_layoutInfo.append(part.split(","));

    reload();

    emit structureChanged();
}

void MediaModel::enter(int index)
{
    if (m_cursor.count() + 1 == m_layoutInfo.count() && !m_data.at(index)[DotDotRole].toBool() /* up on leaf node is OK */) {
        DEBUG << "Refusing to enter leaf node";
        return;
    }

    if (m_data.at(index)[DotDotRole].toBool() && !m_cursor.isEmpty()) {
        back();
        return;
    }

    DEBUG << "Entering " << index;
    m_cursor.append(m_data[index]);
    reload();
    emit partChanged();
}

void MediaModel::back(int count)
{
    if (count == 0 || m_cursor.count() < 1)
        return;

    if (m_cursor.count() > count) {
        for (int i = 0; i < count; ++i)
            m_cursor.removeLast();
    } else {
        m_cursor.clear();
    }
    reload();
    emit partChanged();
}

QVariant MediaModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role == ModelIndexRole)
        return qVariantFromValue(index);

    return m_data.value(index.row()).value(role);
}

QModelIndex MediaModel::index(int row, int col, const QModelIndex &parent) const
{
    if (parent.isValid())
        return QModelIndex();

    return createIndex(row, col);
}

QModelIndex MediaModel::parent(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return QModelIndex();
}

int MediaModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return m_data.count();
}

int MediaModel::columnCount(const QModelIndex &idx) const
{
    Q_UNUSED(idx);
    return 1;
}

bool MediaModel::hasChildren(const QModelIndex &parent) const
{
    return !parent.isValid();
}

bool MediaModel::canFetchMore(const QModelIndex &parent) const
{
    if (parent.isValid() || m_mediaType.isEmpty() || m_layoutInfo.isEmpty()) {
        DEBUG << "false " << parent.isValid() << m_mediaType.isEmpty() << m_layoutInfo.isEmpty();
        return false;
    }
    DEBUG << (!m_loading && !m_loaded);
    return !m_loading && !m_loaded;
}

void MediaModel::fetchMore(const QModelIndex &parent)
{
    if (!canFetchMore(parent))
        return;

    DEBUG << "";

    m_loading = true;

    QSqlQuery q = buildQuery();
    DEBUG << q.lastQuery();
    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

void MediaModel::createNewDbReader()
{
    DEBUG << "";

    if (m_reader) {
        disconnect(m_reader, 0, this, 0);
        m_reader->stop();
        m_reader->deleteLater();
    }
    m_reader = new DbReader;
    m_reader->moveToThread(m_readerThread);

    QMetaObject::invokeMethod(m_reader, "initialize", Q_ARG(QSqlDatabase, Backend::instance()->mediaDatabase()));
    connect(m_reader, SIGNAL(dataReady(DbReader *, QList<QSqlRecord>, void *)),
            this, SLOT(handleDataReady(DbReader *, QList<QSqlRecord>, void *)));
}

void MediaModel::reload()
{
    createNewDbReader();

    beginResetModel();
    m_loading = m_loaded = false;
    m_data.clear();
    endResetModel();
}

QHash<int, QVariant> MediaModel::dataFromRecord(const QSqlRecord &record) const
{
    QHash<int, QVariant> data;
    for (int j = 0; j < record.count(); j++) {
        int role = m_fieldToRole.value(record.fieldName(j));
        if (record.fieldName(j) == "uri")
            data.insert(role, QUrl::fromEncoded(record.value(j).toByteArray()));
        else
            data.insert(role, record.value(j));
    }

    // Provide 'display' role as , separated values
    QStringList cols = m_layoutInfo.value(m_cursor.count());
    QStringList displayString;
    for (int j = 0; j < cols.count(); j++) {
        displayString << record.value(cols[j]).toString();
    }
    data.insert(Qt::DisplayRole, displayString.join(", "));
    data.insert(DotDotRole, false);
    data.insert(IsLeafRole, m_cursor.count() + 1 == m_layoutInfo.count());

    data.insert(PreviewUrlRole, QUrl::fromEncoded(record.value("thumbnail").toByteArray()));
    return data;
}

void MediaModel::handleDataReady(DbReader *reader, const QList<QSqlRecord> &records, void *node)
{
    Q_ASSERT(reader == m_reader);
    Q_UNUSED(reader);
    Q_UNUSED(node);

    DEBUG << "Received response from db of size " << records.size();

    if (records.isEmpty())
        return;

    if (m_data.isEmpty()) {
        insertAll(records);
    } else {
        insertNew(records);
    }

    m_loading = false;
    m_loaded = true;
}

void MediaModel::insertAll(const QList<QSqlRecord> &records)
{
    if (!m_cursor.isEmpty()) {
        beginInsertRows(QModelIndex(), 0, records.count());
    } else {
        beginInsertRows(QModelIndex(), 0, records.count() - 1);
    }

    for (int i = 0; i < records.count(); i++) {
        QHash<int, QVariant> data = dataFromRecord(records[i]);
        m_data.append(data);
    }

    if (!m_cursor.isEmpty()) {
        QHash<int, QVariant> data;
        data.insert(Qt::DisplayRole, tr(".."));
        data.insert(DotDotRole, true);
        data.insert(IsLeafRole, false);
        m_data.append(data);
    }

    endInsertRows();
}

int MediaModel::compareData(int idx, const QSqlRecord &record) const
{
    const QHash<int, QVariant> &curData = m_data[idx];
    QStringList cols = m_layoutInfo.value(m_cursor.count());
    foreach(const QString &col, cols) {
        const int role = m_fieldToRole.value(col);
        int cmp = QString::compare(curData.value(role).toString(), record.value(col).toString(), Qt::CaseInsensitive); // ## must use sqlite's compare
        if (cmp != 0)
            return cmp;
    }
    return 0;
}

void MediaModel::insertNew(const QList<QSqlRecord> &records)
{
    int old = 0, shiny = 0;

    while (shiny < records.length()) {
        const QSqlRecord &record = records[shiny];
        int cmp = old == m_data.count() ? 1 : compareData(old, record);

        if (cmp == 0) {
            ++old;
            ++shiny;
        } else if (cmp < 0) {
            beginRemoveRows(QModelIndex(), old, old);
            m_data.removeAt(old);
            endRemoveRows();
        } else {
            beginInsertRows(QModelIndex(), old, old);
            m_data.insert(old, dataFromRecord(record));
            endInsertRows();
            ++old;
            ++shiny;
        }
    }
}

QSqlQuery MediaModel::buildQuery() const
{
    QSqlDriver *driver = Backend::instance()->mediaDatabase().driver();

    QStringList escapedCurParts;
    QStringList curParts = m_layoutInfo[m_cursor.count()];
    for (int i = 0; i < curParts.count(); i++)
        escapedCurParts.append(driver->escapeIdentifier(curParts[i], QSqlDriver::FieldName));
    QString escapedCurPart = escapedCurParts.join(",");

    QSqlQuery query(Backend::instance()->mediaDatabase());
    query.setForwardOnly(true);

    QStringList placeHolders;
    const bool lastPart = m_cursor.count() == m_layoutInfo.count()-1;
    QString queryString;
    // SQLite allows us to select columns that are not present in the GROUP BY. We use this feature
    // to select thumbnails for non-leaf nodes
    queryString.append("SELECT *");
    queryString.append(" FROM " + driver->escapeIdentifier(m_mediaType, QSqlDriver::TableName));

    if (!m_cursor.isEmpty()) {
        QStringList where;
        for (int i = 0; i < m_cursor.count(); i++) {
            QStringList subParts = m_layoutInfo[i];
            for (int j = 0; j < subParts.count(); j++) {
                where.append(subParts[j] + " = ?");
                const int role = m_fieldToRole.value(subParts[j]);
                placeHolders << m_cursor[i].value(role).toString();
            }
        }

        queryString.append(" WHERE " + where.join(" AND "));
    }

    if (!lastPart)
        queryString.append(" GROUP BY " + escapedCurPart);

    queryString.append(" ORDER BY " + escapedCurPart + " COLLATE NOCASE");

    query.prepare(queryString);

    foreach(const QString &placeHolder, placeHolders)
        query.addBindValue(placeHolder);

    return query;
}

void MediaModel::handleScanStarted(const QString &type)
{
    if (type != m_mediaType)
        return;

    m_refreshTimer.start();
}

void MediaModel::handleScanFinished(const QString &type)
{
    if (type != m_mediaType)
        return;
    m_refreshTimer.stop();
    refresh();
}

void MediaModel::refresh()
{
    createNewDbReader();

    m_loading = true;
    m_loaded = false;

    QSqlQuery q = buildQuery();
    DEBUG << m_mediaType << q.lastQuery();
    QMetaObject::invokeMethod(m_reader, "execute", Qt::QueuedConnection, Q_ARG(QSqlQuery, q));
}

<|endoftext|>


<commit_before>// Copyright  (C)  2007  Ruben Smits <ruben dot smits at mech dot kuleuven dot be>
// Copyright  (C)  2013  Silvio Traversaro < silvio dot traversaro at iit dot it >

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#include "kdl_codyco/momentumjacobian.hpp"

namespace KDL
{
namespace CoDyCo
{

    using namespace Eigen;

    MomentumJacobian::MomentumJacobian()
    {
    }

    MomentumJacobian::MomentumJacobian(unsigned int nr_of_columns):
        data(6,nr_of_columns)
    {
    }

    MomentumJacobian::MomentumJacobian(const MomentumJacobian& arg):
        data(arg.data)
    {
    }

    MomentumJacobian& MomentumJacobian::operator = (const MomentumJacobian& arg)
    {
        this->data=arg.data;
        return *this;
    }

    MomentumJacobian::~MomentumJacobian()
    {

    }

    void MomentumJacobian::resize(unsigned int new_nr_of_columns)
    {
        data.resize(6,new_nr_of_columns);
    }

    double MomentumJacobian::operator()(unsigned int i,unsigned int j)const
    {
        return data(i,j);
    }

    double& MomentumJacobian::operator()(unsigned int i,unsigned int j)
    {
        return data(i,j);
    }

    unsigned int MomentumJacobian::rows()const
    {
        return data.rows();
    }

    unsigned int MomentumJacobian::columns()const
    {
        return data.cols();
    }

    void SetToZero(MomentumJacobian& jac)
    {
        jac.data.setZero();
    }

    void MomentumJacobian::changeRefPoint(const Vector& base_AB){
        for(unsigned int i=0;i<data.cols();i++)
            this->setColumn(i,this->getColumn(i).RefPoint(base_AB));
    }

    bool changeRefPoint(const MomentumJacobian& src1, const Vector& base_AB, MomentumJacobian& dest)
    {
        if(src1.columns()!=dest.columns())
            return false;
        for(unsigned int i=0;i<src1.columns();i++)
            dest.setColumn(i,src1.getColumn(i).RefPoint(base_AB));
        return true;
    }

    void MomentumJacobian::changeBase(const Rotation& rot){
        for(unsigned int i=0;i<data.cols();i++)
            this->setColumn(i,rot*this->getColumn(i));;
    }

    bool changeBase(const MomentumJacobian& src1, const Rotation& rot, MomentumJacobian& dest)
    {
        if(src1.columns()!=dest.columns())
            return false;
        for(unsigned int i=0;i<src1.columns();i++)
            dest.setColumn(i,rot*src1.getColumn(i));;
        return true;
    }

    void MomentumJacobian::changeRefFrame(const Frame& frame){
        for(unsigned int i=0;i<data.cols();i++)
            this->setColumn(i,frame*this->getColumn(i));
    }

    bool changeRefFrame(const MomentumJacobian& src1,const Frame& frame, MomentumJacobian& dest)
    {
        if(src1.columns()!=dest.columns())
            return false;
        for(unsigned int i=0;i<src1.columns();i++)
            dest.setColumn(i,frame*src1.getColumn(i));
        return true;
    }

    bool MomentumJacobian::operator ==(const MomentumJacobian& arg)const
    {
        return Equal((*this),arg);
    }

    bool MomentumJacobian::operator!=(const MomentumJacobian& arg)const
    {
        return !Equal((*this),arg);
    }

    bool Equal(const MomentumJacobian& a,const MomentumJacobian& b,double eps)
    {
        if(a.rows()==b.rows()&&a.columns()==b.columns()){
            return a.data.isApprox(b.data,eps);
        }else
            return false;
    }

    Wrench MomentumJacobian::getColumn(unsigned int i) const{
        return Wrench(Vector(data(0,i),data(1,i),data(2,i)),Vector(data(3,i),data(4,i),data(5,i)));
    }

    void MomentumJacobian::setColumn(unsigned int i,const Wrench& t){
        data.col(i).head<3>()=Eigen::Map<const Vector3d>(t.force.data);
        data.col(i).tail<3>()=Eigen::Map<const Vector3d>(t.torque.data);
    }
}
}
<commit_msg>Fixed warning about unsigned/int mismatch in momentumjacobian<commit_after>// Copyright  (C)  2007  Ruben Smits <ruben dot smits at mech dot kuleuven dot be>
// Copyright  (C)  2013  Silvio Traversaro < silvio dot traversaro at iit dot it >

// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.

// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.

// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

#include "kdl_codyco/momentumjacobian.hpp"

namespace KDL
{
namespace CoDyCo
{

    using namespace Eigen;

    MomentumJacobian::MomentumJacobian()
    {
    }

    MomentumJacobian::MomentumJacobian(unsigned int nr_of_columns):
        data(6,nr_of_columns)
    {
    }

    MomentumJacobian::MomentumJacobian(const MomentumJacobian& arg):
        data(arg.data)
    {
    }

    MomentumJacobian& MomentumJacobian::operator = (const MomentumJacobian& arg)
    {
        this->data=arg.data;
        return *this;
    }

    MomentumJacobian::~MomentumJacobian()
    {

    }

    void MomentumJacobian::resize(unsigned int new_nr_of_columns)
    {
        data.resize(6,new_nr_of_columns);
    }

    double MomentumJacobian::operator()(unsigned int i,unsigned int j)const
    {
        return data(i,j);
    }

    double& MomentumJacobian::operator()(unsigned int i,unsigned int j)
    {
        return data(i,j);
    }

    unsigned int MomentumJacobian::rows()const
    {
        return data.rows();
    }

    unsigned int MomentumJacobian::columns()const
    {
        return data.cols();
    }

    void SetToZero(MomentumJacobian& jac)
    {
        jac.data.setZero();
    }

    void MomentumJacobian::changeRefPoint(const Vector& base_AB){
        for(unsigned int i=0;i<(unsigned int)data.cols();i++)
            this->setColumn(i,this->getColumn(i).RefPoint(base_AB));
    }

    bool changeRefPoint(const MomentumJacobian& src1, const Vector& base_AB, MomentumJacobian& dest)
    {
        if(src1.columns()!=dest.columns())
            return false;
        for(unsigned int i=0;i<src1.columns();i++)
            dest.setColumn(i,src1.getColumn(i).RefPoint(base_AB));
        return true;
    }

    void MomentumJacobian::changeBase(const Rotation& rot){
        for(unsigned int i=0;i<(unsigned int)data.cols();i++)
            this->setColumn(i,rot*this->getColumn(i));;
    }

    bool changeBase(const MomentumJacobian& src1, const Rotation& rot, MomentumJacobian& dest)
    {
        if(src1.columns()!=dest.columns())
            return false;
        for(unsigned int i=0;i<src1.columns();i++)
            dest.setColumn(i,rot*src1.getColumn(i));;
        return true;
    }

    void MomentumJacobian::changeRefFrame(const Frame& frame){
        for(unsigned int i=0;i<(unsigned int)data.cols();i++)
            this->setColumn(i,frame*this->getColumn(i));
    }

    bool changeRefFrame(const MomentumJacobian& src1,const Frame& frame, MomentumJacobian& dest)
    {
        if(src1.columns()!=dest.columns())
            return false;
        for(unsigned int i=0;i<src1.columns();i++)
            dest.setColumn(i,frame*src1.getColumn(i));
        return true;
    }

    bool MomentumJacobian::operator ==(const MomentumJacobian& arg)const
    {
        return Equal((*this),arg);
    }

    bool MomentumJacobian::operator!=(const MomentumJacobian& arg)const
    {
        return !Equal((*this),arg);
    }

    bool Equal(const MomentumJacobian& a,const MomentumJacobian& b,double eps)
    {
        if(a.rows()==b.rows()&&a.columns()==b.columns()){
            return a.data.isApprox(b.data,eps);
        }else
            return false;
    }

    Wrench MomentumJacobian::getColumn(unsigned int i) const{
        return Wrench(Vector(data(0,i),data(1,i),data(2,i)),Vector(data(3,i),data(4,i),data(5,i)));
    }

    void MomentumJacobian::setColumn(unsigned int i,const Wrench& t){
        data.col(i).head<3>()=Eigen::Map<const Vector3d>(t.force.data);
        data.col(i).tail<3>()=Eigen::Map<const Vector3d>(t.torque.data);
    }
}
}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "assert.hpp"

#include "mtac/Function.hpp"

using namespace eddic;

mtac::basic_block::basic_block(int i) : index(i), label("") {}

mtac::Quadruple& mtac::basic_block::find(std::size_t uid){
    for(auto& quadruple : statements){
        if(quadruple.uid() == uid){
            return quadruple;
        }
    }

    eddic_unreachable("The uid should exists");
}

std::ostream& mtac::operator<<(std::ostream& stream, const std::shared_ptr<basic_block>& basic_block){
    if(basic_block){
        return stream << *basic_block;
    } else {
        return stream << "null_bb";
    }
}

std::ostream& mtac::operator<<(std::ostream& stream, const basic_block& block){
    if(block.index == -1){
        return stream << "ENTRY";
    } else if(block.index == -2){
        return stream << "EXIT";
    } else {
        return stream << "B" << block.index;
    }
}

mtac::basic_block::iterator mtac::begin(mtac::basic_block_p block){
    return block->begin();
}

mtac::basic_block::iterator mtac::end(mtac::basic_block_p block){
    return block->end();
}

void pretty_print(std::vector<mtac::basic_block_p> blocks, std::ostream& stream){
    if(blocks.empty()){
        stream << "{}";
    } else {
        stream << "{" << blocks[0];

        for(std::size_t i = 1; i < blocks.size(); ++i){
            stream << ", " << blocks[i];
        }

        stream << "}";
    }
}

void mtac::pretty_print(std::shared_ptr<const mtac::basic_block> block, std::ostream& stream){
    std::string sep(25, '-');

    stream << sep << std::endl;
    stream << block;

    stream << " prev: " << block->prev << ", next: " << block->next << std::endl;
    stream << "successors "; ::pretty_print(block->successors, stream); stream << std::endl;;
    stream << "predecessors "; ::pretty_print(block->predecessors, stream); stream << std::endl;;

    stream << sep << std::endl;
}
<commit_msg>Fix printing of basic block<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2011-2012.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#include "assert.hpp"

#include "mtac/Function.hpp"

using namespace eddic;

mtac::basic_block::basic_block(int i) : index(i), label("") {}

mtac::Quadruple& mtac::basic_block::find(std::size_t uid){
    for(auto& quadruple : statements){
        if(quadruple.uid() == uid){
            return quadruple;
        }
    }

    eddic_unreachable("The uid should exists");
}

std::ostream& mtac::operator<<(std::ostream& stream, const std::shared_ptr<basic_block>& basic_block){
    if(basic_block){
        return stream << *basic_block;
    } else {
        return stream << "null_bb";
    }
}

std::ostream& mtac::operator<<(std::ostream& stream, const basic_block& block){
    if(block.index == -1){
        return stream << "ENTRY";
    } else if(block.index == -2){
        return stream << "EXIT";
    } else {
        return stream << "B" << block.index;
    }
}

mtac::basic_block::iterator mtac::begin(mtac::basic_block_p block){
    return block->begin();
}

mtac::basic_block::iterator mtac::end(mtac::basic_block_p block){
    return block->end();
}

void pretty_print(std::vector<mtac::basic_block_p> blocks, std::ostream& stream){
    if(blocks.empty()){
        stream << "{}";
    } else {
        stream << "{" << blocks[0];

        for(std::size_t i = 1; i < blocks.size(); ++i){
            stream << ", " << blocks[i];
        }

        stream << "}";
    }
}

void mtac::pretty_print(std::shared_ptr<const mtac::basic_block> block, std::ostream& stream){
    std::string sep(25, '-');

    stream << sep << std::endl;
    stream << *block;

    stream << " prev: " << block->prev << ", next: " << block->next << std::endl;
    stream << "successors "; ::pretty_print(block->successors, stream); stream << std::endl;;
    stream << "predecessors "; ::pretty_print(block->predecessors, stream); stream << std::endl;;

    stream << sep << std::endl;
}
<|endoftext|>


<commit_before>#include "navigationwidget.h"
#include <QFont>
#include <QObjectCleanupHandler>
#include <QStack>
#include <QApplication>
#include <QPushButton>
#include <QVBoxLayout>
#include "slidingstackedwidget.h"

namespace QNavigation
{

const int NavigationBarHeight = 40;

class NavigationWidgetPrivate
{
    Q_DECLARE_PUBLIC(NavigationWidget)
    NavigationWidget * const q_ptr;

public:
    NavigationWidgetPrivate(NavigationWidget *q);
    ~NavigationWidgetPrivate();

    void cleanup();

    QObjectCleanupHandler *cleaner;
    SlidingStackedWidget *stackedWidget;
    QLabel *titleLabel;
    QPushButton *backButton;
    QStack<int> popStack;
};

NavigationWidgetPrivate::NavigationWidgetPrivate(NavigationWidget *q) :
    q_ptr(q), cleaner(new QObjectCleanupHandler()),
    stackedWidget(new SlidingStackedWidget(q)), titleLabel(new QLabel()),
    backButton(new QPushButton(titleLabel))
{
}

NavigationWidgetPrivate::~NavigationWidgetPrivate()
{
    cleaner->deleteLater();
}

void NavigationWidgetPrivate::cleanup()
{
    cleaner->clear();
    backButton->setVisible(!popStack.isEmpty());
    titleLabel->setText(stackedWidget->currentWidget()->windowTitle());
}

NavigationWidget::NavigationWidget(QWidget *parent) :
    QWidget(parent), d_ptr(new NavigationWidgetPrivate(this))
{
    Q_D(NavigationWidget);
    connect(d->stackedWidget, SIGNAL(animationEnded()), this, SLOT(cleanup()));

    d->titleLabel->setFont(QFont(d->titleLabel->font().family(),
                                 16, QFont::Bold));
    d->titleLabel->setFixedHeight(NavigationBarHeight);
    d->titleLabel->setAlignment(Qt::AlignCenter);

    d->backButton->setText(tr("< Back"));
    d->backButton->setFixedHeight(NavigationBarHeight);
    d->backButton->setStyleSheet("QPushButton { border: none; outline: none; }"
                                 "QPushButton:hover { color: gray; }");
    d->backButton->hide();

    connect(d->backButton, SIGNAL(clicked()), this, SLOT(pop()));

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->setMargin(0);
    mainLayout->addWidget(d->titleLabel, 0);
    mainLayout->addWidget(d->stackedWidget, 1);
    setLayout(mainLayout);
}

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

bool NavigationWidget::level() const
{
    return d_ptr->popStack.count();
}

QLabel *NavigationWidget::titleLabel() const
{
    return d_ptr->titleLabel;
}

QPushButton *NavigationWidget::backButton() const
{
    return d_ptr->backButton;
}

SlidingStackedWidget *NavigationWidget::stackedWidget() const
{
    return d_ptr->stackedWidget;
}

bool NavigationWidget::navigationBarHidden() const
{
    return !(d_ptr->titleLabel->isVisible());
}

void NavigationWidget::setNavigationBarHidden(bool hidden)
{
    d_ptr->titleLabel->setVisible(!hidden);
}

void NavigationWidget::push(QWidget *item)
{
    Q_D(NavigationWidget);

    if (d->stackedWidget->currentIndex() >= 0)
        d->popStack.push(d->stackedWidget->currentIndex());

    int next = d->stackedWidget->addWidget(item);
    d->stackedWidget->navigate(next, SlidingStackedWidget::RightToLeft);

    // Set title explicitly because initial push won't trigger cleanup().
    d->titleLabel->setText(item->windowTitle());
}

void NavigationWidget::pop()
{
    Q_D(NavigationWidget);
    if (d->popStack.isEmpty())
        return;

    // Hide the back button as soon as possible to prevent over-clicking.
    d->backButton->setVisible(d->popStack.size() > 1);

    d->cleaner->add(d->stackedWidget->currentWidget());
    d->stackedWidget->navigate(d->popStack.pop(),
                               SlidingStackedWidget::LeftToRight);
}

void NavigationWidget::popToTop()
{
    Q_D(NavigationWidget);
    if (d->popStack.isEmpty())
        return;

    // Hide the back button as soon as possible to prevent over-clicking.
    d->backButton->hide();

    int target = d->stackedWidget->currentIndex();
    do {
        d->cleaner->add(d->stackedWidget->widget(target));
        target = d->popStack.pop();
    } while (!d->popStack.isEmpty());
    d->stackedWidget->navigate(target, SlidingStackedWidget::LeftToRight);
}

}   // namespace QNavigation

// Hack to include symbols so that Q_PRIVATE_SLOT works.
// http://qt-project.org/wiki/Binary_Compatibility_Workarounds
#include "moc_navigationwidget.cpp"
<commit_msg>Use Left angle bracket for back button<commit_after>#include "navigationwidget.h"
#include <QFont>
#include <QObjectCleanupHandler>
#include <QStack>
#include <QPushButton>
#include <QVBoxLayout>
#include "slidingstackedwidget.h"

namespace QNavigation
{

const int NavigationBarHeight = 40;

class NavigationWidgetPrivate
{
    Q_DECLARE_PUBLIC(NavigationWidget)
    NavigationWidget * const q_ptr;

public:
    NavigationWidgetPrivate(NavigationWidget *q);
    ~NavigationWidgetPrivate();

    void cleanup();

    QObjectCleanupHandler *cleaner;
    SlidingStackedWidget *stackedWidget;
    QLabel *titleLabel;
    QPushButton *backButton;
    QStack<int> popStack;
};

NavigationWidgetPrivate::NavigationWidgetPrivate(NavigationWidget *q) :
    q_ptr(q), cleaner(new QObjectCleanupHandler()),
    stackedWidget(new SlidingStackedWidget(q)), titleLabel(new QLabel()),
    backButton(new QPushButton(titleLabel))
{
}

NavigationWidgetPrivate::~NavigationWidgetPrivate()
{
    cleaner->deleteLater();
}

void NavigationWidgetPrivate::cleanup()
{
    cleaner->clear();
    backButton->setVisible(!popStack.isEmpty());
    titleLabel->setText(stackedWidget->currentWidget()->windowTitle());
}

NavigationWidget::NavigationWidget(QWidget *parent) :
    QWidget(parent), d_ptr(new NavigationWidgetPrivate(this))
{
    Q_D(NavigationWidget);
    connect(d->stackedWidget, SIGNAL(animationEnded()), this, SLOT(cleanup()));

    d->titleLabel->setFont(QFont(d->titleLabel->font().family(),
                                 16, QFont::Bold));
    d->titleLabel->setFixedHeight(NavigationBarHeight);
    d->titleLabel->setAlignment(Qt::AlignCenter);

    d->backButton->setText(
                QString("%1 %2").arg(QString::fromUtf8("\xe3\x80\x88", 3),
                                     tr("Back")));
    d->backButton->setFixedHeight(NavigationBarHeight);
    d->backButton->setStyleSheet("QPushButton { border: none; outline: none; }"
                                 "QPushButton:hover { color: gray; }");
    d->backButton->hide();

    connect(d->backButton, SIGNAL(clicked()), this, SLOT(pop()));

    QVBoxLayout *mainLayout = new QVBoxLayout();
    mainLayout->setMargin(0);
    mainLayout->addWidget(d->titleLabel, 0);
    mainLayout->addWidget(d->stackedWidget, 1);
    setLayout(mainLayout);
}

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

bool NavigationWidget::level() const
{
    return d_ptr->popStack.count();
}

QLabel *NavigationWidget::titleLabel() const
{
    return d_ptr->titleLabel;
}

QPushButton *NavigationWidget::backButton() const
{
    return d_ptr->backButton;
}

SlidingStackedWidget *NavigationWidget::stackedWidget() const
{
    return d_ptr->stackedWidget;
}

bool NavigationWidget::navigationBarHidden() const
{
    return !(d_ptr->titleLabel->isVisible());
}

void NavigationWidget::setNavigationBarHidden(bool hidden)
{
    d_ptr->titleLabel->setVisible(!hidden);
}

void NavigationWidget::push(QWidget *item)
{
    Q_D(NavigationWidget);

    if (d->stackedWidget->currentIndex() >= 0)
        d->popStack.push(d->stackedWidget->currentIndex());

    int next = d->stackedWidget->addWidget(item);
    d->stackedWidget->navigate(next, SlidingStackedWidget::RightToLeft);

    // Set title explicitly because initial push won't trigger cleanup().
    d->titleLabel->setText(item->windowTitle());
}

void NavigationWidget::pop()
{
    Q_D(NavigationWidget);
    if (d->popStack.isEmpty())
        return;

    // Hide the back button as soon as possible to prevent over-clicking.
    d->backButton->setVisible(d->popStack.size() > 1);

    d->cleaner->add(d->stackedWidget->currentWidget());
    d->stackedWidget->navigate(d->popStack.pop(),
                               SlidingStackedWidget::LeftToRight);
}

void NavigationWidget::popToTop()
{
    Q_D(NavigationWidget);
    if (d->popStack.isEmpty())
        return;

    // Hide the back button as soon as possible to prevent over-clicking.
    d->backButton->hide();

    int target = d->stackedWidget->currentIndex();
    do {
        d->cleaner->add(d->stackedWidget->widget(target));
        target = d->popStack.pop();
    } while (!d->popStack.isEmpty());
    d->stackedWidget->navigate(target, SlidingStackedWidget::LeftToRight);
}

}   // namespace QNavigation

// Hack to include symbols so that Q_PRIVATE_SLOT works.
// http://qt-project.org/wiki/Binary_Compatibility_Workarounds
#include "moc_navigationwidget.cpp"
<|endoftext|>


<commit_before>// Copyright (c)2008-2011, Preferred Infrastructure Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * 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.
//
//     * Neither the name of Preferred Infrastructure nor the names of other
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// 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 <time.h>
#include <gtest/gtest.h>

#include "rpc.h"

#include <vector>
#include <map>

#include "../lang/bind.h"
#include "../lang/ref.h"
#include "../concurrent/thread.h"

RPC_PROC(test_str, std::string(std::string));
static std::string test_str(const std::string& v){ return v; }
RPC_PROC(test_pair, std::pair<int,std::string>(std::pair<int,std::string>));
static std::pair<int,std::string> test_pair(const std::pair<int,std::string>& v){ return v; }
RPC_PROC(test_vec, std::vector<int>(std::vector<int>));
static std::vector<int> test_vec(const std::vector<int>& v){ return v; }
RPC_PROC(test_map, std::map<int,int>(std::map<int,int>));
static std::map<int, int> test_map(const std::map<int, int>& v){ return v; }
RPC_PROC(test_set, std::set<int>(std::set<int> v));
static std::set<int> test_set(const std::set<int>& v){ return v; }

RPC_GEN(1, testrpc, test_str, test_pair, test_vec, test_map, test_set);

static void server_thread(testrpc_server& server)
{
  server.set_test_str(&test_str);
  server.set_test_pair(&test_pair);
  server.set_test_vec(&test_vec);
  server.set_test_map(&test_map);
  server.set_test_set(&test_set);
  server.serv(31231, 10);
}

TEST(rpc, rpc_test)
{
  testrpc_server server;
  EXPECT_TRUE(server.is_stopped());
  pfi::concurrent::thread t(pfi::lang::bind(&server_thread, pfi::lang::ref(server)));
  t.start();

  sleep(1);
  EXPECT_TRUE(server.is_running());

  EXPECT_NO_THROW({ testrpc_client client1("localhost", 31231); });
  {
    testrpc_client client("localhost", 31231);
    int times = 100;
    for (int t=0;t<times;t++) {
      {
        std::string v;
        for (int i=0;i<10;i++)
          v+='0'+(rand()%10);
        std::string r;
        EXPECT_NO_THROW({ r = client.call_test_str(v); });
        EXPECT_EQ(r.size(), 10U);
        for (int i=0;i<10;i++)
          EXPECT_EQ(r[i], v[i]);
      }
      {
        std::string vs;
        for (int i=0;i<10;i++)
          vs+='0'+(rand()%10);
        std::pair<int, std::string> v = make_pair(rand(), vs);
        std::pair<int, std::string> r;
        EXPECT_NO_THROW({ r = client.call_test_pair(v); });
        EXPECT_EQ(r.first, v.first);
        EXPECT_EQ(r.second, v.second);
      }
      {
        std::vector<int> v;
        for (int i=0;i<10;i++)
          v.push_back(rand());
        std::vector<int> r;
        EXPECT_NO_THROW({ r = client.call_test_vec(v); });
        EXPECT_EQ(r.size(), 10U);
        for (int i=0;i<10;i++)
          EXPECT_EQ(r[i], v[i]);
      }
      {
        std::map<int, int> v;
        for (int i=0;i<10;i++)
          v[i]=rand();
        std::map<int, int> r;
        EXPECT_NO_THROW({ r = client.call_test_map(v); });
        EXPECT_EQ(r.size(), 10U);
        for (int i=0;i<10;i++){
          EXPECT_EQ(r[i], v[i]);
        }
      }
      {
        std::set<int> v;
        for (int i=0;i<10;i++)
          v.insert(i*100);
        std::set<int> r;
        EXPECT_NO_THROW({ r = client.call_test_set(v); });
        EXPECT_EQ(r.size(), 10U);
        int cnt = 0;
        for (std::set<int>::iterator it=v.begin();it!=v.end();++it){
          EXPECT_EQ(*it, cnt++ * 100);
        }
      }
    }
  }

  server.stop();
  t.join();
  EXPECT_TRUE(server.is_stopped());
}

// test for struct and empty vector
struct tstruct {
  int i;
  std::vector<unsigned char> v;

private:
  friend class pfi::data::serialization::access;
  template <class Ar>
  void serialize(Ar& ar) {
    ar & i & v;
  }
};

RPC_PROC(test_struct, tstruct(tstruct));
RPC_GEN(1, test_struct_rpc, test_struct);
static tstruct f_test_struct(tstruct t) { return t; }
static void struct_server_thread(test_struct_rpc_server& server, uint16_t port, int num_threads)
{
  server.set_test_struct(&f_test_struct);
  server.serv(port, num_threads);
}
TEST(rpc, test_struct)
{
  uint16_t port = 31232;
  int num_threads = 10;

  test_struct_rpc_server server;
  EXPECT_TRUE(server.is_stopped());
  pfi::concurrent::thread t(pfi::lang::bind(&struct_server_thread, pfi::lang::ref(server), port, num_threads));
  t.start();

  sleep(1);
  EXPECT_TRUE(server.is_running());

  EXPECT_NO_THROW({ test_struct_rpc_client client1("localhost", port); });
  {
    test_struct_rpc_client client("localhost", port);
    tstruct t1;
    t1.i = 9;
    tstruct t2;
    EXPECT_NO_THROW({ t2 = client.call_test_struct(t1); });
    EXPECT_EQ(t1.i, t2.i);
  }

  server.stop();
  t.join();
  EXPECT_TRUE(server.is_stopped());
}

// test rpc::server when port number is 0
struct sserver_args_struct
{
  testrpc_server server;
  uint16_t arg_port;
};

static void server_thread_for_port_0( sserver_args_struct *sas )
{
  sas->server.serv(sas->arg_port, 1);
}

TEST(rpc, test_server_port_0)
{
  sserver_args_struct sas;
  sas.server.set_test_str(&test_str);
  sas.arg_port = 0;
  EXPECT_TRUE(sas.server.is_stopped());

  pfi::lang::function<void(void)> f = pfi::lang::bind(server_thread_for_port_0, &sas);
  pfi::concurrent::thread t(f);
  t.start();
  sleep(1);
  EXPECT_TRUE(sas.server.is_running());

  uint16_t p0 = sas.server.port();
  EXPECT_NE(0, p0);

  {
    testrpc_client client("localhost", p0);
    std::string v;
    for (int i=0;i<10;i++)
      v+='0'+(rand()%10);
    std::string r;
    EXPECT_NO_THROW({ r = client.call_test_str(v); });
    EXPECT_EQ(r.size(), 10U);
    for (int i=0;i<10;i++)
      EXPECT_EQ(r[i], v[i]);
  }

  sas.server.stop();
  t.join();
  EXPECT_TRUE(sas.server.is_stopped());
}

// test rpc::server timeout
static void server_thread_for_timeout(test_struct_rpc_server& server)
{
  server.set_test_struct(&f_test_struct);
  server.serv(31233, 1);
}

static void dummy_client_for_timeout()
{
  pfi::network::stream_socket s0;
  s0.connect("localhost", 31233);
  sleep(10);
}

uint64_t get_monotonic_time_micro_sec() {
  timespec tt;
  clock_gettime(CLOCK_MONOTONIC, &tt);
  return ( tt.tv_sec*1000000 + tt.tv_nsec/1000 );
}

TEST(rpc, test_rpc_server_timeout)
{
  double test_timeout_sec = 1.0;
  test_struct_rpc_server server(test_timeout_sec);
  EXPECT_TRUE(server.is_stopped());
  pfi::concurrent::thread t(pfi::lang::bind(&server_thread_for_timeout, pfi::lang::ref(server)));
  t.start();

  sleep(1);
  EXPECT_TRUE(server.is_running());

  uint64_t time0 = get_monotonic_time_micro_sec();
  pfi::concurrent::thread dummy_client(&dummy_client_for_timeout);
  dummy_client.start();
  dummy_client.detach();

  sleep(1);

  EXPECT_NO_THROW({ test_struct_rpc_client client1("localhost", 31233); });
  {
    test_struct_rpc_client client("localhost", 31233);
    tstruct t1;
    t1.i = 9;
    tstruct t2;
    EXPECT_NO_THROW({ t2 = client.call_test_struct(t1); });
    EXPECT_EQ(t1.i, t2.i);
  }

//  If it is less than 7 seconds, the timeout is considered to be working effectively.
//  If the timeout setting does not work, it will wait for more than 10 seconds.
  uint64_t time1 = get_monotonic_time_micro_sec();
  EXPECT_LE((time1-time0), 7*1000*1000);

  server.stop();
  t.join();
  EXPECT_TRUE(server.is_stopped());
}

static void long_running_client_thread(uint16_t port)
{
  test_struct_rpc_client client("localhost", port);
  tstruct t1;
  t1.i = 0;
  EXPECT_NO_THROW({ client.call_test_struct(t1); });
  sleep(3);
}

TEST(rpc, test_server_termination_after_long_running_client_ends)
{
  uint16_t port = 31234;
  int num_threads = 10;

  test_struct_rpc_server server;
  EXPECT_TRUE(server.is_stopped());
  pfi::concurrent::thread server_thread(pfi::lang::bind(&struct_server_thread, pfi::lang::ref(server), port, num_threads));
  server_thread.start();
  sleep(1);
  EXPECT_TRUE(server.is_running());

  pfi::concurrent::thread client_thread(pfi::lang::bind(&long_running_client_thread, port));
  client_thread.start();
  client_thread.detach();
  sleep(1);

  server.stop();
  server_thread.join();
  EXPECT_TRUE(server.is_stopped());
}
<commit_msg>Delete sserver_args_struct in rpc_test.cpp<commit_after>// Copyright (c)2008-2011, Preferred Infrastructure Inc.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//
//     * 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.
//
//     * Neither the name of Preferred Infrastructure nor the names of other
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// 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 <time.h>
#include <gtest/gtest.h>

#include "rpc.h"

#include <vector>
#include <map>

#include "../lang/bind.h"
#include "../lang/ref.h"
#include "../concurrent/thread.h"

RPC_PROC(test_str, std::string(std::string));
static std::string test_str(const std::string& v){ return v; }
RPC_PROC(test_pair, std::pair<int,std::string>(std::pair<int,std::string>));
static std::pair<int,std::string> test_pair(const std::pair<int,std::string>& v){ return v; }
RPC_PROC(test_vec, std::vector<int>(std::vector<int>));
static std::vector<int> test_vec(const std::vector<int>& v){ return v; }
RPC_PROC(test_map, std::map<int,int>(std::map<int,int>));
static std::map<int, int> test_map(const std::map<int, int>& v){ return v; }
RPC_PROC(test_set, std::set<int>(std::set<int> v));
static std::set<int> test_set(const std::set<int>& v){ return v; }

RPC_GEN(1, testrpc, test_str, test_pair, test_vec, test_map, test_set);

static void server_thread(testrpc_server& server, uint16_t port, int num_threads)
{
  server.set_test_str(&test_str);
  server.set_test_pair(&test_pair);
  server.set_test_vec(&test_vec);
  server.set_test_map(&test_map);
  server.set_test_set(&test_set);
  server.serv(port, num_threads);
}

TEST(rpc, rpc_test)
{
  uint16_t port = 31231;
  int num_threads = 10;

  testrpc_server server;
  EXPECT_TRUE(server.is_stopped());
  pfi::concurrent::thread t(pfi::lang::bind(&server_thread, pfi::lang::ref(server), port, num_threads));
  t.start();

  sleep(1);
  EXPECT_TRUE(server.is_running());

  EXPECT_NO_THROW({ testrpc_client client1("localhost", port); });
  {
    testrpc_client client("localhost", port);
    int times = 100;
    for (int t=0;t<times;t++) {
      {
        std::string v;
        for (int i=0;i<10;i++)
          v+='0'+(rand()%10);
        std::string r;
        EXPECT_NO_THROW({ r = client.call_test_str(v); });
        EXPECT_EQ(r.size(), 10U);
        for (int i=0;i<10;i++)
          EXPECT_EQ(r[i], v[i]);
      }
      {
        std::string vs;
        for (int i=0;i<10;i++)
          vs+='0'+(rand()%10);
        std::pair<int, std::string> v = make_pair(rand(), vs);
        std::pair<int, std::string> r;
        EXPECT_NO_THROW({ r = client.call_test_pair(v); });
        EXPECT_EQ(r.first, v.first);
        EXPECT_EQ(r.second, v.second);
      }
      {
        std::vector<int> v;
        for (int i=0;i<10;i++)
          v.push_back(rand());
        std::vector<int> r;
        EXPECT_NO_THROW({ r = client.call_test_vec(v); });
        EXPECT_EQ(r.size(), 10U);
        for (int i=0;i<10;i++)
          EXPECT_EQ(r[i], v[i]);
      }
      {
        std::map<int, int> v;
        for (int i=0;i<10;i++)
          v[i]=rand();
        std::map<int, int> r;
        EXPECT_NO_THROW({ r = client.call_test_map(v); });
        EXPECT_EQ(r.size(), 10U);
        for (int i=0;i<10;i++){
          EXPECT_EQ(r[i], v[i]);
        }
      }
      {
        std::set<int> v;
        for (int i=0;i<10;i++)
          v.insert(i*100);
        std::set<int> r;
        EXPECT_NO_THROW({ r = client.call_test_set(v); });
        EXPECT_EQ(r.size(), 10U);
        int cnt = 0;
        for (std::set<int>::iterator it=v.begin();it!=v.end();++it){
          EXPECT_EQ(*it, cnt++ * 100);
        }
      }
    }
  }

  server.stop();
  t.join();
  EXPECT_TRUE(server.is_stopped());
}

// test for struct and empty vector
struct tstruct {
  int i;
  std::vector<unsigned char> v;

private:
  friend class pfi::data::serialization::access;
  template <class Ar>
  void serialize(Ar& ar) {
    ar & i & v;
  }
};

RPC_PROC(test_struct, tstruct(tstruct));
RPC_GEN(1, test_struct_rpc, test_struct);
static tstruct f_test_struct(tstruct t) { return t; }
static void struct_server_thread(test_struct_rpc_server& server, uint16_t port, int num_threads)
{
  server.set_test_struct(&f_test_struct);
  server.serv(port, num_threads);
}
TEST(rpc, test_struct)
{
  uint16_t port = 31232;
  int num_threads = 10;

  test_struct_rpc_server server;
  EXPECT_TRUE(server.is_stopped());
  pfi::concurrent::thread t(pfi::lang::bind(&struct_server_thread, pfi::lang::ref(server), port, num_threads));
  t.start();

  sleep(1);
  EXPECT_TRUE(server.is_running());

  EXPECT_NO_THROW({ test_struct_rpc_client client1("localhost", port); });
  {
    test_struct_rpc_client client("localhost", port);
    tstruct t1;
    t1.i = 9;
    tstruct t2;
    EXPECT_NO_THROW({ t2 = client.call_test_struct(t1); });
    EXPECT_EQ(t1.i, t2.i);
  }

  server.stop();
  t.join();
  EXPECT_TRUE(server.is_stopped());
}

// test rpc::server when port number is 0
TEST(rpc, test_server_port_0)
{
  uint16_t port = 0;
  int num_threads = 1;

  testrpc_server server;
  EXPECT_TRUE(server.is_stopped());

  pfi::lang::function<void(void)> f = pfi::lang::bind(server_thread, pfi::lang::ref(server), port, num_threads);
  pfi::concurrent::thread t(f);
  t.start();
  sleep(1);
  EXPECT_TRUE(server.is_running());

  uint16_t p0 = server.port();
  EXPECT_NE(port, p0);

  {
    testrpc_client client("localhost", p0);
    std::string v;
    for (int i=0;i<10;i++)
      v+='0'+(rand()%10);
    std::string r;
    EXPECT_NO_THROW({ r = client.call_test_str(v); });
    EXPECT_EQ(r.size(), 10U);
    for (int i=0;i<10;i++)
      EXPECT_EQ(r[i], v[i]);
  }

  server.stop();
  t.join();
  EXPECT_TRUE(server.is_stopped());
}

// test rpc::server timeout
static void server_thread_for_timeout(test_struct_rpc_server& server)
{
  server.set_test_struct(&f_test_struct);
  server.serv(31233, 1);
}

static void dummy_client_for_timeout()
{
  pfi::network::stream_socket s0;
  s0.connect("localhost", 31233);
  sleep(10);
}

uint64_t get_monotonic_time_micro_sec() {
  timespec tt;
  clock_gettime(CLOCK_MONOTONIC, &tt);
  return ( tt.tv_sec*1000000 + tt.tv_nsec/1000 );
}

TEST(rpc, test_rpc_server_timeout)
{
  double test_timeout_sec = 1.0;
  test_struct_rpc_server server(test_timeout_sec);
  EXPECT_TRUE(server.is_stopped());
  pfi::concurrent::thread t(pfi::lang::bind(&server_thread_for_timeout, pfi::lang::ref(server)));
  t.start();

  sleep(1);
  EXPECT_TRUE(server.is_running());

  uint64_t time0 = get_monotonic_time_micro_sec();
  pfi::concurrent::thread dummy_client(&dummy_client_for_timeout);
  dummy_client.start();
  dummy_client.detach();

  sleep(1);

  EXPECT_NO_THROW({ test_struct_rpc_client client1("localhost", 31233); });
  {
    test_struct_rpc_client client("localhost", 31233);
    tstruct t1;
    t1.i = 9;
    tstruct t2;
    EXPECT_NO_THROW({ t2 = client.call_test_struct(t1); });
    EXPECT_EQ(t1.i, t2.i);
  }

//  If it is less than 7 seconds, the timeout is considered to be working effectively.
//  If the timeout setting does not work, it will wait for more than 10 seconds.
  uint64_t time1 = get_monotonic_time_micro_sec();
  EXPECT_LE((time1-time0), 7*1000*1000);

  server.stop();
  t.join();
  EXPECT_TRUE(server.is_stopped());
}

static void long_running_client_thread(uint16_t port)
{
  test_struct_rpc_client client("localhost", port);
  tstruct t1;
  t1.i = 0;
  EXPECT_NO_THROW({ client.call_test_struct(t1); });
  sleep(3);
}

TEST(rpc, test_server_termination_after_long_running_client_ends)
{
  uint16_t port = 31234;
  int num_threads = 10;

  test_struct_rpc_server server;
  EXPECT_TRUE(server.is_stopped());
  pfi::concurrent::thread server_thread(pfi::lang::bind(&struct_server_thread, pfi::lang::ref(server), port, num_threads));
  server_thread.start();
  sleep(1);
  EXPECT_TRUE(server.is_running());

  pfi::concurrent::thread client_thread(pfi::lang::bind(&long_running_client_thread, port));
  client_thread.start();
  client_thread.detach();
  sleep(1);

  server.stop();
  server_thread.join();
  EXPECT_TRUE(server.is_stopped());
}
<|endoftext|>