<commit_before>#include "runtime_internal.h"
#include "HalideRuntimeQurt.h"
#include "printer.h"
#include "mini_qurt.h"
#include "hexagon_remote/log.h"

using namespace Halide::Runtime::Internal::Qurt;

extern "C" {

enum qurt_hvx_mode_t {
    QURT_HVX_MODE_64B = 0,
    QURT_HVX_MODE_128B = 1,
};

extern int qurt_hvx_lock(qurt_hvx_mode_t);
extern int qurt_hvx_unlock();

WEAK int halide_qurt_hvx_lock(void *user_context, int size) {
    qurt_hvx_mode_t mode;
    switch (size) {
    case 64: mode = QURT_HVX_MODE_64B; break;
    case 128: mode = QURT_HVX_MODE_128B; break;
    default:
        error(user_context) << "HVX lock size must be 64 or 128.\n";
        return -1;
    }

    debug(user_context) << "QuRT: qurt_hvx_lock(" << mode << ") ->\n";
    int result = qurt_hvx_lock(mode);
    debug(user_context) << "        " << result << "\n";
    if (result != QURT_EOK) {
        error(user_context) << "qurt_hvx_lock failed\n";
        return -1;
    }

    return 0;
}

WEAK int halide_qurt_hvx_unlock(void *user_context) {
    debug(user_context) << "QuRT: qurt_hvx_unlock ->\n";
    int result = qurt_hvx_unlock();
    debug(user_context) << "        " << result << "\n";
    if (result != QURT_EOK) {
        error(user_context) << "qurt_hvx_unlock failed\n";
        return -1;
    }

    return 0;
}

WEAK void halide_qurt_hvx_unlock_as_destructor(void *user_context, void * /*obj*/) {
    halide_qurt_hvx_unlock(user_context);
}

/////////////////////////////////////////////////////////////////////////////
//
// halide_hexagon_prefetch_buffer_t
//
// Prefetch a multi-dimensional box (subset) from a larger array
//
//   dim        number of dimensions in array
//   elem_size  size in bytes of one element
//   num_elem   total number of elements in array
//   buf        buffer_t describing box to prefetch
//
// Notes:
//  - Prefetches can be queued up to 3 deep (MAX_PREFETCH)
//  - If 3 are already pending, the oldest request is dropped
//  - USR:PFA status bit is set to indicate that prefetches are in progress
//  - A l2fetch with any subfield set to zero cancels all pending prefetches
//  - The l2fetch starting address must be in mapped memory but the range
//    prefetched can go into unmapped memory without raising an exception
//
// TODO: Opt: Generate more control code for prefetch_buffer_t directly in
// TODO       Prefetch.cpp to avoid passing box info through a buffer_t
// TODO       (which results in ~30 additional stores/loads)
//
#define MAX_PREFETCH    3
#define MASK16          0xFFFF
#define MIN(x,y)        (((x)<(y)) ? (x) : (y))
#define MAX(x,y)        (((x)>(y)) ? (x) : (y))
//
// Prefetch debugging controls
//#define DEBUG_PREFETCH       1  // Uncomment to enable prefetch debug

#ifdef DEBUG_PREFETCH // Prefetch debug enabled

// Only produce debug info in this range
#define DBG_P_START          1  // First debug instance to display
#define DBG_P_STOP           9  // Last debug instance to display
//#define DBG_P_STOP           0xFFFFFFFF  // Last debug instance to display

#define DBG_PREFETCH_V(...)  log_printf(__VA_ARGS__) // Verbose debug enabled
//#define DBG_PREFETCH_V(...)  {} // Verbose debug disabled
#define DBG_PREFETCH(...)    if ((dbg_cnt >= DBG_P_START) && \
                                 (dbg_cnt <= DBG_P_STOP)) { \
                                 log_printf(__VA_ARGS__); \
                             }  // Normal range-based debug enabled

#else // Prefetch debug disabled

#define DBG_PREFETCH_V(...)  {} // Verbose debug disabled
#define DBG_PREFETCH(...)    {} // Normal debug disabled

#endif
//
WEAK int halide_hexagon_prefetch_buffer_t(const uint32_t dim, const int32_t elem_size,
                const uint32_t num_elem, const buffer_t *buf)
{
    // Extract needed fields from buffer_t
    unsigned char *addr   = buf->host;
    const int32_t *extent = buf->extent;
    const int32_t *stride = buf->stride;
    const int32_t *min    = buf->min;
    unsigned int boxdim   = dim;  // dimensions of entire box to prefetch
    unsigned int iterdim  = 2;    // dimension to iterate over
    const unsigned char *addr_beg = addr;
    const unsigned char *addr_end = addr + (num_elem * elem_size);

#ifdef DEBUG_PREFETCH
    static uint32_t dbg_cnt = 0;  // limit how many calls debug is generated
    dbg_cnt++;
    DBG_PREFETCH("halide_hexagon_prefetch_buffer_t(%u, %d)\n", dim, elem_size, num_elem);

    DBG_PREFETCH("  addr range 0x%p => 0x%p\n", addr_beg, addr_end);

    DBG_PREFETCH("  buf host=0x%p elem_size=%d\n", addr, buf->elem_size);
    for (unsigned int i = 0; i < boxdim; i++) {
        DBG_PREFETCH("  buf stride[%d]=0x%-8x min[%d]=%-6d ext[%d]=%d\n",
                          i, stride[i], i, min[i], i, extent[i]);
    }
#endif

    // Compute starting position of box
    int32_t startpos = 0;
    for (unsigned int i = 0; i < boxdim; i++) {
        startpos += min[i] * stride[i];
    }
    addr += startpos * elem_size;
    DBG_PREFETCH("  +startpos=0x%x*%d => addr=0x%p\n", startpos, elem_size, addr);

    // Range check starting address
    if ((addr < addr_beg) || (addr >= addr_end)) {
        DBG_PREFETCH_V("  l2fetch: 0x%p out of range [0x%p, 0x%p]\n",
                        addr, addr_beg, addr_end);
        return 0;
    }

    // Compute 2-D prefetch descriptor
    // l2fetch(Rs,Rtt): 48 bit descriptor
    uint32_t pdir    = 1;   // 0 row major, 1 = column major
    uint32_t pstride = stride[0] * elem_size;
    uint32_t pwidth  = extent[0] * elem_size;
    uint32_t pheight = 1;
    if (boxdim > 1) {
        pheight = extent[1];
    }

#if 0   // TODO: Opt: Box collapse disabled for now - needs more testing, and
        // TODO       collapse candidates tend to exceed the stride mask size
    // For boxes with dimension > 2 try to "collapse" any unit height
    // dimensions by increasing the descriptor stride
    int32_t newpstride = pstride;
    while (boxdim > 2) {
        if (pheight == 1) {     // if height is currently 1
            newpstride = stride[iterdim-1] * elem_size;  // update stride
        } else {
            break;              // otherwise, we're done collapsing
        }
        if (newpstride == (newpstride & MASK16)) {  // and if it fits in mask...
            pstride = newpstride;             // ...accept new stride
            pheight = extent[iterdim];        // ...and height
        } else {
            break;              // otherwise, we're done collapsing
        }
        boxdim--;       // remaining non-collapsed dimensions
        iterdim++;      // update innermost iterate dimension
    }
#endif

    pdir    = pdir & 0x1;           // bit  48
    pstride = MIN(pstride, MASK16); // bits 47:32
    pwidth  = MIN(pwidth,  MASK16); // bits 31:16
    pheight = MIN(pheight, MASK16); // bits 15:0

    uint64_t pdir64    = pdir;
    uint64_t pstride64 = pstride;
    uint64_t pdesc = (pdir64<<48) | (pstride64<<32) | (pwidth<<16) | pheight;
    const uint32_t pbytes = pwidth * pheight;   // Bytes in prefetch descriptor
    uint32_t tbytes = 0;                        // Total bytes prefetched
    uint32_t tcnt = 0;                          // Total count of prefetch ops

    // Hard coded descriptors for testing
    // uint64_t pdesc = 0x1020002000002; // col, 512 width, 2 rows
    // uint64_t pdesc = 0x1020002000001; // col, 512 width, 1 row
    // uint64_t pdesc = 0x1144014400001; // col, 5184 width, 1 row

    DBG_PREFETCH("  prefetch pdir:0x%x pstride:0x%x pwidth:0x%x pheight:0x%x\n",
                           pdir, pstride, pwidth, pheight);
    DBG_PREFETCH("  prefetch addr:0x%p pdesc:0x%llx\n", addr, pdesc);

    DBG_PREFETCH("  iterdim:%d\n", iterdim);
    // If iterdim is not last dim && iterdim extent is 1...
    while ((iterdim < dim-1) && (extent[iterdim] == 1)) {
        iterdim++;          // ...iterate at the next higher dimension
    }

    // TODO: Add support for iterating over multiple higher dimensions?
    // TODO  Currently just iterating over one outer (non-unity) dimension
    // TODO  since only MAX_PREFETCH prefetches can be queued anyway.

    if ((boxdim <= 2) ||          // 2-D box, or
        (iterdim >= dim) ||       // No dimension remaining to iterate over, or
        (extent[iterdim] == 1)) { // >2-D box, but unity outer dimension

        // Perform a single prefetch
        DBG_PREFETCH("  l2fetch(0x%p, 0x%llx): %d bytes\n", addr, pdesc, pbytes);
        __asm__ __volatile__ ("l2fetch(%0,%1)" : : "r"(addr), "r"(pdesc));
        tbytes += pbytes;
        tcnt++;

    } else { // Iterate for higher dimension boxes...

        // Get iteration stride and extents
        int32_t iterstride = stride[iterdim] * elem_size;
        int32_t iterextent = extent[iterdim];
        iterextent = MIN(iterextent, MAX_PREFETCH);   // Limit # of prefetches

        DBG_PREFETCH("  stride[%d]*%d=0x%x ext[%d]=%d\n",
                        iterdim, elem_size, iterstride, iterdim, iterextent);

        for (int32_t i = 0; i < iterextent; i++) {
            DBG_PREFETCH("  %d: l2fetch(0x%p, 0x%llx): %d bytes\n", i, addr, pdesc, pbytes);
            // Range check starting address
            if ((addr >= addr_beg) && (addr < addr_end)) {
                // Perform prefetch
                __asm__ __volatile__ ("l2fetch(%0,%1)" : : "r"(addr), "r"(pdesc));
                tbytes += pbytes;
                tcnt++;
            } else {
                DBG_PREFETCH_V("  %d: l2fetch: +0x%x => 0x%p out of range [0x%p, 0x%p]\n",
                                i, iterstride, addr, addr_beg, addr_end);
            }
            addr += iterstride;
        }
    }

    // TODO: Opt: Return the number of prefetch instructions (tcnt) issued?
    //       to not exceed MAX_PREFETCH in a sequence of prefetch ops
    // TODO: Opt: Return the size in bytes (tbytes) prefetched?
    //       to avoid prefetching too much data in one sequence
    DBG_PREFETCH("  l2fetch: %d ops, %d bytes\n", tcnt, tbytes);
    return 0;
}

}
<commit_msg>Fix stride descriptor for higher dimension arrays<commit_after>#include "runtime_internal.h"
#include "HalideRuntimeQurt.h"
#include "printer.h"
#include "mini_qurt.h"
#include "hexagon_remote/log.h"

using namespace Halide::Runtime::Internal::Qurt;

extern "C" {

enum qurt_hvx_mode_t {
    QURT_HVX_MODE_64B = 0,
    QURT_HVX_MODE_128B = 1,
};

extern int qurt_hvx_lock(qurt_hvx_mode_t);
extern int qurt_hvx_unlock();

WEAK int halide_qurt_hvx_lock(void *user_context, int size) {
    qurt_hvx_mode_t mode;
    switch (size) {
    case 64: mode = QURT_HVX_MODE_64B; break;
    case 128: mode = QURT_HVX_MODE_128B; break;
    default:
        error(user_context) << "HVX lock size must be 64 or 128.\n";
        return -1;
    }

    debug(user_context) << "QuRT: qurt_hvx_lock(" << mode << ") ->\n";
    int result = qurt_hvx_lock(mode);
    debug(user_context) << "        " << result << "\n";
    if (result != QURT_EOK) {
        error(user_context) << "qurt_hvx_lock failed\n";
        return -1;
    }

    return 0;
}

WEAK int halide_qurt_hvx_unlock(void *user_context) {
    debug(user_context) << "QuRT: qurt_hvx_unlock ->\n";
    int result = qurt_hvx_unlock();
    debug(user_context) << "        " << result << "\n";
    if (result != QURT_EOK) {
        error(user_context) << "qurt_hvx_unlock failed\n";
        return -1;
    }

    return 0;
}

WEAK void halide_qurt_hvx_unlock_as_destructor(void *user_context, void * /*obj*/) {
    halide_qurt_hvx_unlock(user_context);
}

/////////////////////////////////////////////////////////////////////////////
//
// halide_hexagon_prefetch_buffer_t
//
// Prefetch a multi-dimensional box (subset) from a larger array
//
//   dim        number of dimensions in array
//   elem_size  size in bytes of one element
//   num_elem   total number of elements in array
//   buf        buffer_t describing box to prefetch
//
// Notes:
//  - Prefetches can be queued up to 3 deep (MAX_PREFETCH)
//  - If 3 are already pending, the oldest request is dropped
//  - USR:PFA status bit is set to indicate that prefetches are in progress
//  - A l2fetch with any subfield set to zero cancels all pending prefetches
//  - The l2fetch starting address must be in mapped memory but the range
//    prefetched can go into unmapped memory without raising an exception
//
// TODO: Opt: Generate more control code for prefetch_buffer_t directly in
// TODO       Prefetch.cpp to avoid passing box info through a buffer_t
// TODO       (which results in ~30 additional stores/loads)
//
#define MAX_PREFETCH    3
#define MASK16          0xFFFF
#define MIN(x,y)        (((x)<(y)) ? (x) : (y))
#define MAX(x,y)        (((x)>(y)) ? (x) : (y))
//
// Prefetch debugging controls
//#define DEBUG_PREFETCH       1  // Uncomment to enable prefetch debug

#ifdef DEBUG_PREFETCH // Prefetch debug enabled

// Only produce debug info in this range
#define DBG_P_START          1  // First debug instance to display
#define DBG_P_STOP           9  // Last debug instance to display
//#define DBG_P_STOP           0xFFFFFFFF  // Last debug instance to display

#define DBG_PREFETCH_V(...)  log_printf(__VA_ARGS__) // Verbose debug enabled
//#define DBG_PREFETCH_V(...)  {} // Verbose debug disabled
#define DBG_PREFETCH(...)    if ((dbg_cnt >= DBG_P_START) && \
                                 (dbg_cnt <= DBG_P_STOP)) { \
                                 log_printf(__VA_ARGS__); \
                             }  // Normal range-based debug enabled

#else // Prefetch debug disabled

#define DBG_PREFETCH_V(...)  {} // Verbose debug disabled
#define DBG_PREFETCH(...)    {} // Normal debug disabled

#endif
//
WEAK int halide_hexagon_prefetch_buffer_t(const uint32_t dim, const int32_t elem_size,
                const uint32_t num_elem, const buffer_t *buf)
{
    // Extract needed fields from buffer_t
    unsigned char *addr   = buf->host;
    const int32_t *extent = buf->extent;
    const int32_t *stride = buf->stride;
    const int32_t *min    = buf->min;
    unsigned int boxdim   = dim;  // dimensions of entire box to prefetch
    unsigned int iterdim  = 2;    // dimension to iterate over
    const unsigned char *addr_beg = addr;
    const unsigned char *addr_end = addr + (num_elem * elem_size);

#ifdef DEBUG_PREFETCH
    static uint32_t dbg_cnt = 0;  // limit how many calls debug is generated
    dbg_cnt++;
    DBG_PREFETCH("halide_hexagon_prefetch_buffer_t(%u, %d)\n", dim, elem_size, num_elem);

    DBG_PREFETCH("  addr range 0x%p => 0x%p\n", addr_beg, addr_end);

    DBG_PREFETCH("  buf host=0x%p elem_size=%d\n", addr, buf->elem_size);
    for (unsigned int i = 0; i < boxdim; i++) {
        DBG_PREFETCH("  buf stride[%d]=0x%-8x min[%d]=%-6d ext[%d]=%d\n",
                          i, stride[i], i, min[i], i, extent[i]);
    }
#endif

    // Compute starting position of box
    int32_t startpos = 0;
    for (unsigned int i = 0; i < boxdim; i++) {
        startpos += min[i] * stride[i];
    }
    addr += startpos * elem_size;
    DBG_PREFETCH("  +startpos=0x%x*%d => addr=0x%p\n", startpos, elem_size, addr);

    // Range check starting address
    if ((addr < addr_beg) || (addr >= addr_end)) {
        DBG_PREFETCH_V("  l2fetch: 0x%p out of range [0x%p, 0x%p]\n",
                        addr, addr_beg, addr_end);
        return 0;
    }

    // Compute 2-D prefetch descriptor
    // l2fetch(Rs,Rtt): 48 bit descriptor
    uint32_t pdir    = 1;   // 0 row major, 1 = column major
    uint32_t pstride = stride[0] * elem_size;
    uint32_t pwidth  = extent[0] * elem_size;
    uint32_t pheight = 1;
    if (boxdim > 1) {
        // Note: Currently assuming this will fit within MASK16
        pstride = stride[1] * elem_size;
        pheight = extent[1];
    }

#if 0   // TODO: Opt: Box collapse disabled for now - needs more testing, and
        // TODO       collapse candidates tend to exceed the stride mask size
    // For boxes with dimension > 2 try to "collapse" any unit height
    // dimensions by increasing the descriptor stride
    int32_t newpstride = pstride;
    while (boxdim > 2) {
        if (pheight == 1) {     // if height is currently 1
            newpstride = stride[iterdim-1] * elem_size;  // update stride
        } else {
            break;              // otherwise, we're done collapsing
        }
        if (newpstride == (newpstride & MASK16)) {  // and if it fits in mask...
            pstride = newpstride;             // ...accept new stride
            pheight = extent[iterdim];        // ...and height
        } else {
            break;              // otherwise, we're done collapsing
        }
        boxdim--;       // remaining non-collapsed dimensions
        iterdim++;      // update innermost iterate dimension
    }
#endif

    pdir    = pdir & 0x1;           // bit  48
    pstride = MIN(pstride, MASK16); // bits 47:32
    pwidth  = MIN(pwidth,  MASK16); // bits 31:16
    pheight = MIN(pheight, MASK16); // bits 15:0

    uint64_t pdir64    = pdir;
    uint64_t pstride64 = pstride;
    uint64_t pdesc = (pdir64<<48) | (pstride64<<32) | (pwidth<<16) | pheight;
    const uint32_t pbytes = pwidth * pheight;   // Bytes in prefetch descriptor
    uint32_t tbytes = 0;                        // Total bytes prefetched
    uint32_t tcnt = 0;                          // Total count of prefetch ops

    // Hard coded descriptors for testing
    // uint64_t pdesc = 0x1020002000002; // col, 512 width, 2 rows
    // uint64_t pdesc = 0x1020002000001; // col, 512 width, 1 row
    // uint64_t pdesc = 0x1144014400001; // col, 5184 width, 1 row

    DBG_PREFETCH("  prefetch pdir:0x%x pstride:0x%x pwidth:0x%x pheight:0x%x\n",
                           pdir, pstride, pwidth, pheight);
    DBG_PREFETCH("  prefetch addr:0x%p pdesc:0x%llx\n", addr, pdesc);

    DBG_PREFETCH("  iterdim:%d\n", iterdim);
    // If iterdim is not last dim && iterdim extent is 1...
    while ((iterdim < dim-1) && (extent[iterdim] == 1)) {
        iterdim++;          // ...iterate at the next higher dimension
    }

    // TODO: Add support for iterating over multiple higher dimensions?
    // TODO  Currently just iterating over one outer (non-unity) dimension
    // TODO  since only MAX_PREFETCH prefetches can be queued anyway.

    if ((boxdim <= 2) ||          // 2-D box, or
        (iterdim >= dim) ||       // No dimension remaining to iterate over, or
        (extent[iterdim] == 1)) { // >2-D box, but unity outer dimension

        // Perform a single prefetch
        DBG_PREFETCH("  l2fetch(0x%p, 0x%llx): %d bytes\n", addr, pdesc, pbytes);
        __asm__ __volatile__ ("l2fetch(%0,%1)" : : "r"(addr), "r"(pdesc));
        tbytes += pbytes;
        tcnt++;

    } else { // Iterate for higher dimension boxes...

        // Get iteration stride and extents
        int32_t iterstride = stride[iterdim] * elem_size;
        int32_t iterextent = extent[iterdim];
        iterextent = MIN(iterextent, MAX_PREFETCH);   // Limit # of prefetches

        DBG_PREFETCH("  stride[%d]*%d=0x%x ext[%d]=%d\n",
                        iterdim, elem_size, iterstride, iterdim, iterextent);

        for (int32_t i = 0; i < iterextent; i++) {
            DBG_PREFETCH("  %d: l2fetch(0x%p, 0x%llx): %d bytes\n", i, addr, pdesc, pbytes);
            // Range check starting address
            if ((addr >= addr_beg) && (addr < addr_end)) {
                // Perform prefetch
                __asm__ __volatile__ ("l2fetch(%0,%1)" : : "r"(addr), "r"(pdesc));
                tbytes += pbytes;
                tcnt++;
            } else {
                DBG_PREFETCH_V("  %d: l2fetch: +0x%x => 0x%p out of range [0x%p, 0x%p]\n",
                                i, iterstride, addr, addr_beg, addr_end);
            }
            addr += iterstride;
        }
    }

    // TODO: Opt: Return the number of prefetch instructions (tcnt) issued?
    //       to not exceed MAX_PREFETCH in a sequence of prefetch ops
    // TODO: Opt: Return the size in bytes (tbytes) prefetched?
    //       to avoid prefetching too much data in one sequence
    DBG_PREFETCH("  l2fetch: %d ops, %d bytes\n", tcnt, tbytes);
    return 0;
}

}
<|endoftext|>


<commit_before>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "ft_mmoc.h"

#include <QDebug>
#include <QString>
#include <QStringList>
#include <QDir>
#include <QFile>
#include <QTest>

void Ft_MMoc::initTestCase()
{
    m_skipTests = false;

    // check for qt moc
    m_qtMocFound = QFileInfo( "/usr/bin/moc" ).exists();

    // check for mmoc perl script
    m_perlMmoc = "/usr/bin/mmoc.pl";
    m_perlMmocFound = QFileInfo( m_perlMmoc ).exists();

    // check for mmoc binary
    m_binaryMmoc = "/usr/bin/mmoc";
    m_binaryMmocFound = QFileInfo( m_binaryMmoc ).exists();
}

void Ft_MMoc::cleanupTestCase()
{
}

int Ft_MMoc::runProcess( const QString& process, const QStringList &params )
{
    QProcess p;
    p.setProcessChannelMode( QProcess::ForwardedChannels );

    p.start( process, params );
    if ( !p.waitForStarted() )
    {
        qCritical( "process start failed" );
        return -1;
    }

    if ( !p.waitForFinished() )
    {
        qCritical( "process finish failed" );
        return -2;
    }

    return p.exitCode();
}

void Ft_MMoc::doMMoc_data()
{
    QTest::addColumn<QString>("mocPath");
    QTest::addColumn<QString>("fileName");

    // test all .h files in samples subdir.
    QStringList files( QDir( "/usr/lib/libmeegotouch-tests/ft_mmoc-samples" ).
                       entryList( QStringList("*.h") ) );
    foreach ( QString file, files )
    {
        if ( m_qtMocFound && m_binaryMmocFound )
        {
            QTest::newRow( qPrintable( file + " using " + m_binaryMmoc ) )
              << m_binaryMmoc << file;
        }
        if ( m_qtMocFound && m_perlMmocFound )
        {
            QTest::newRow( qPrintable( file + " using " + m_perlMmoc ) )
              << m_perlMmoc << file;
        }
    }
}

void Ft_MMoc::doMMoc()
{
  QFETCH( QString, mocPath );
  QFETCH( QString, fileName );

  QString path = "/usr/lib/libmeegotouch-tests/ft_mmoc-samples/";

  qWarning( "testing: moc: %s file: %s",
            qPrintable( mocPath ),
            qPrintable( fileName ) );

  QString baseName = fileName;
  baseName.remove( ".h" );

  int result = runProcess( mocPath, QStringList()
                           << path + fileName
                           << "-o" << "/tmp/moc_" + baseName + ".cpp" );

  // check for successful return
  QVERIFY( result == 0 );

  // now compare files
  QVERIFY( compareFiles( "/tmp/moc_" + baseName + ".cpp",
                         path + "moc_" + baseName + ".cpp.correct" ) );

}

bool Ft_MMoc::compareFiles(const QString &filename, const QString &correctFilename) const
{
    bool filesAreTheSame = true;

    QFile newFile(filename);
    if (!newFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "a Could not open" << filename;
        return false;
    }
    QTextStream newIn(&newFile);

    QFile correctFile(correctFilename);
    if (!correctFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "b Could not open" << correctFilename;
        return false;
    }
    QTextStream correctIn(&correctFile);

    do {
        if (newIn.atEnd() || correctIn.atEnd()) {
            bool oneFileFinishedBeforeOther = (!newIn.atEnd() || !correctIn.atEnd());
            if (oneFileFinishedBeforeOther) {
                qDebug( "one file finished before the other" );
                filesAreTheSame = false;
            }
            break;
        }

        QString newLine     = newIn.readLine();
        QString correctLine = correctIn.readLine();

        // skip exceptional lines
        QString headerGuard(QFileInfo(filename).fileName().toUpper().replace(".", "_"));
        bool lineIsExceptional =
            newLine.contains(headerGuard) ||
            newLine.contains("** Created: ") ||
            newLine.contains("**      by: The Qt Meta Object Compiler version ") ||
            newLine.contains("#error \"This file was generated using the moc from") ||
            newLine.contains("#include ") ||
            newLine.contains("** Meta object code from reading C++ file ") ||
            newLine.contains("ft_mservicefwgen")
          ;
        if (lineIsExceptional) {
            continue;
        }

        bool linesAreIdentical = (newLine == correctLine);
        if ( ! linesAreIdentical )
        {
          qDebug() << "got these different lines: new:\n"
                   << filename
                   << "\n"
                   << newLine
                   << "\nexpected:\n"
                   << correctFilename
                   << "\n"
                   << correctLine;
        }
        filesAreTheSame = linesAreIdentical;
    } while (filesAreTheSame);

    correctFile.close();
    newFile.close();

    return filesAreTheSame;
}

QTEST_MAIN(Ft_MMoc)
<commit_msg>Changes: Skips ft_mmoc when no development environment is installed.<commit_after>/***************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (directui@nokia.com)
**
** This file is part of libmeegotouch.
**
** If you have questions regarding the use of this file, please contact
** Nokia at directui@nokia.com.
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation
** and appearing in the file LICENSE.LGPL included in the packaging
** of this file.
**
****************************************************************************/

#include "ft_mmoc.h"

#include <QDebug>
#include <QString>
#include <QStringList>
#include <QDir>
#include <QFile>
#include <QTest>

void Ft_MMoc::initTestCase()
{
    m_skipTests = false;

    // check for qt moc
    m_qtMocFound = QFileInfo( "/usr/bin/moc" ).exists();

    // check for mmoc perl script
    m_perlMmoc = "/usr/bin/mmoc.pl";
    m_perlMmocFound = QFileInfo( m_perlMmoc ).exists();

    // check for mmoc binary
    m_binaryMmoc = "/usr/bin/mmoc";
    m_binaryMmocFound = QFileInfo( m_binaryMmoc ).exists();

    if ( !m_binaryMmocFound && !m_perlMmocFound ) {
        QSKIP( "need development environment", SkipAll );
    }
}

void Ft_MMoc::cleanupTestCase()
{
}

int Ft_MMoc::runProcess( const QString& process, const QStringList &params )
{
    QProcess p;
    p.setProcessChannelMode( QProcess::ForwardedChannels );

    p.start( process, params );
    if ( !p.waitForStarted() )
    {
        qCritical( "process start failed" );
        return -1;
    }

    if ( !p.waitForFinished() )
    {
        qCritical( "process finish failed" );
        return -2;
    }

    return p.exitCode();
}

void Ft_MMoc::doMMoc_data()
{
    QTest::addColumn<QString>("mocPath");
    QTest::addColumn<QString>("fileName");

    // test all .h files in samples subdir.
    QStringList files( QDir( "/usr/lib/libmeegotouch-tests/ft_mmoc-samples" ).
                       entryList( QStringList("*.h") ) );
    foreach ( QString file, files )
    {
        if ( m_qtMocFound && m_binaryMmocFound )
        {
            QTest::newRow( qPrintable( file + " using " + m_binaryMmoc ) )
              << m_binaryMmoc << file;
        }
        if ( m_qtMocFound && m_perlMmocFound )
        {
            QTest::newRow( qPrintable( file + " using " + m_perlMmoc ) )
              << m_perlMmoc << file;
        }
    }
}

void Ft_MMoc::doMMoc()
{
  QFETCH( QString, mocPath );
  QFETCH( QString, fileName );

  QString path = "/usr/lib/libmeegotouch-tests/ft_mmoc-samples/";

  qWarning( "testing: moc: %s file: %s",
            qPrintable( mocPath ),
            qPrintable( fileName ) );

  QString baseName = fileName;
  baseName.remove( ".h" );

  int result = runProcess( mocPath, QStringList()
                           << path + fileName
                           << "-o" << "/tmp/moc_" + baseName + ".cpp" );

  // check for successful return
  QVERIFY( result == 0 );

  // now compare files
  QVERIFY( compareFiles( "/tmp/moc_" + baseName + ".cpp",
                         path + "moc_" + baseName + ".cpp.correct" ) );

}

bool Ft_MMoc::compareFiles(const QString &filename, const QString &correctFilename) const
{
    bool filesAreTheSame = true;

    QFile newFile(filename);
    if (!newFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "a Could not open" << filename;
        return false;
    }
    QTextStream newIn(&newFile);

    QFile correctFile(correctFilename);
    if (!correctFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "b Could not open" << correctFilename;
        return false;
    }
    QTextStream correctIn(&correctFile);

    do {
        if (newIn.atEnd() || correctIn.atEnd()) {
            bool oneFileFinishedBeforeOther = (!newIn.atEnd() || !correctIn.atEnd());
            if (oneFileFinishedBeforeOther) {
                qDebug( "one file finished before the other" );
                filesAreTheSame = false;
            }
            break;
        }

        QString newLine     = newIn.readLine();
        QString correctLine = correctIn.readLine();

        // skip exceptional lines
        QString headerGuard(QFileInfo(filename).fileName().toUpper().replace(".", "_"));
        bool lineIsExceptional =
            newLine.contains(headerGuard) ||
            newLine.contains("** Created: ") ||
            newLine.contains("**      by: The Qt Meta Object Compiler version ") ||
            newLine.contains("#error \"This file was generated using the moc from") ||
            newLine.contains("#include ") ||
            newLine.contains("** Meta object code from reading C++ file ") ||
            newLine.contains("ft_mservicefwgen")
          ;
        if (lineIsExceptional) {
            continue;
        }

        bool linesAreIdentical = (newLine == correctLine);
        if ( ! linesAreIdentical )
        {
          qDebug() << "got these different lines: new:\n"
                   << filename
                   << "\n"
                   << newLine
                   << "\nexpected:\n"
                   << correctFilename
                   << "\n"
                   << correctLine;
        }
        filesAreTheSame = linesAreIdentical;
    } while (filesAreTheSame);

    correctFile.close();
    newFile.close();

    return filesAreTheSame;
}

QTEST_MAIN(Ft_MMoc)
<|endoftext|>


<commit_before>/*********************************************************************/
/* Copyright (c) 2011 - 2012, The University of Texas at Austin.     */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. Redistributions in binary form must reproduce the above      */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer in the documentation and/or other materials       */
/*      provided with the distribution.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``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 UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN 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.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#include "Movie.h"
#include "main.h"
#include "log.h"

Movie::Movie(std::string uri)
{
    initialized_ = false;

    // defaults
    textureId_ = 0;
    textureBound_ = false;
    avFormatContext_ = NULL;
    avCodecContext_ = NULL;
    swsContext_ = NULL;
    avFrame_ = NULL;
    avFrameRGB_ = NULL;
    streamIdx_ = -1;
    paused_ = false;
    loop_ = true;

    start_time_ = 0;
    duration_ = 0;
    num_frames_ = 0;
    frame_index_ = 0;
    skipped_frames_ = false;

    // assign values
    uri_ = uri;

    // initialize ffmpeg
    av_register_all();

    // open movie file
    if(avformat_open_input(&avFormatContext_, uri.c_str(), NULL, NULL) != 0)
    {
        put_flog(LOG_ERROR, "could not open movie file %s", uri.c_str());
        return;
    }

    // get stream information
    if(avformat_find_stream_info(avFormatContext_, NULL) < 0)
    {
        put_flog(LOG_ERROR, "could not find stream information");
        return;
    }

    // dump format information to stderr
    av_dump_format(avFormatContext_, 0, uri.c_str(), 0);

    // find the first video stream
    streamIdx_ = -1;

    for(unsigned int i=0; i<avFormatContext_->nb_streams; i++)
    {
        if(avFormatContext_->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            streamIdx_ = i;
            break;
        }
    }

    if(streamIdx_ == -1)
    {
        put_flog(LOG_ERROR, "could not find video stream");
        return;
    }

    videostream_ = avFormatContext_->streams[streamIdx_];

    // get a pointer to the codec context for the video stream
    avCodecContext_ = videostream_->codec;

    // find the decoder for the video stream
    AVCodec * codec = avcodec_find_decoder(avCodecContext_->codec_id);

    if(codec == NULL)
    {
        put_flog(LOG_ERROR, "unsupported codec");
        return;
    }

    // open codec
    int ret = avcodec_open2(avCodecContext_, codec, NULL);

    if(ret < 0)
    {
        char errbuf[256];
        av_strerror(ret, errbuf, 256);

        put_flog(LOG_ERROR, "could not open codec, error code %i: %s", ret, errbuf);
        return;
    }

    den2_ = videostream_->time_base.den * videostream_->r_frame_rate.den;
    num2_ = videostream_->time_base.num * videostream_->r_frame_rate.num;

    // generate seeking parameters
    start_time_ = videostream_->start_time;
    duration_ = videostream_->duration;
    num_frames_ = av_rescale(duration_, num2_, den2_);
    frameDuration_ = (double)videostream_->r_frame_rate.den / (double)videostream_->r_frame_rate.num;

    put_flog(LOG_DEBUG, "seeking parameters: start_time = %i, duration_ = %i, num frames = %i", start_time_, duration_, num_frames_);

    // create texture for movie
    QImage image(avCodecContext_->width, avCodecContext_->height, QImage::Format_RGB32);
    image.fill(0);

    textureId_ = g_mainWindow->getGLWindow()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption);
    textureBound_ = true;

    // allocate video frame for video decoding
    avFrame_ = avcodec_alloc_frame();

    // allocate video frame for RGB conversion
    avFrameRGB_ = avcodec_alloc_frame();

    if(avFrame_ == NULL || avFrameRGB_ == NULL)
    {
        put_flog(LOG_ERROR, "error allocating frames");
        return;
    }

    // get required buffer size and allocate buffer for pFrameRGB
    // this memory will be overwritten during frame conversion, but needs to be allocated ahead of time
    int numBytes = avpicture_get_size(PIX_FMT_RGBA, avCodecContext_->width, avCodecContext_->height);
    uint8_t * buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

    // assign buffer to pFrameRGB
    avpicture_fill((AVPicture *)avFrameRGB_, buffer, PIX_FMT_RGBA, avCodecContext_->width, avCodecContext_->height);

    // create sws scaler context
    swsContext_ = sws_getContext(avCodecContext_->width, avCodecContext_->height, avCodecContext_->pix_fmt, avCodecContext_->width, avCodecContext_->height, PIX_FMT_RGBA, SWS_FAST_BILINEAR, NULL, NULL, NULL);

    initialized_ = true;
}

Movie::~Movie()
{
    if(textureBound_ == true)
    {
        // delete bound texture
        glDeleteTextures(1, &textureId_); // it appears deleteTexture() below is not actually deleting the texture from the GPU...
        g_mainWindow->getGLWindow()->deleteTexture(textureId_);
    }

    // close the format context
    avformat_close_input(&avFormatContext_);

    // free scaler context
    sws_freeContext(swsContext_);

    // free frames
    av_free(avFrame_);
    av_free(avFrameRGB_);
}

void Movie::getDimensions(int &width, int &height)
{
    width = avCodecContext_->width;
    height = avCodecContext_->height;
}

void Movie::render(float tX, float tY, float tW, float tH)
{
    updateRenderedFrameCount();

    if(initialized_ != true)
    {
        return;
    }

    // draw the texture
    glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureId_);

    // on zoom-out, clamp to edge (instead of showing the texture tiled / repeated)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glBegin(GL_QUADS);

    glTexCoord2f(tX,tY);
    glVertex2f(0.,0.);

    glTexCoord2f(tX+tW,tY);
    glVertex2f(1.,0.);

    glTexCoord2f(tX+tW,tY+tH);
    glVertex2f(1.,1.);

    glTexCoord2f(tX,tY+tH);
    glVertex2f(0.,1.);

    glEnd();

    glPopAttrib();
}

void Movie::nextFrame(bool skip)
{
    if( !initialized_ || (paused_ && !skipped_frames_) )
        return;

    // rate limiting
    double elapsedSeconds = 999999.;
    if( !nextTimestamp_.is_not_a_date_time( ))
        elapsedSeconds = (g_displayGroupManager->getTimestamp() -
                           nextTimestamp_).total_microseconds() / 1000000.;

    if( elapsedSeconds < frameDuration_ )
        return;

    // update timestamp of last frame
    nextTimestamp_ = g_displayGroupManager->getTimestamp();

    // seeking

    // where we should be after we read a frame in this function
    if( !paused_ )
        ++frame_index_;

    // if we're skipping this frame, increment the skipped frame counter and return
    // otherwise, make sure we're in the correct position in the stream and decode
    if( skip )
    {
        skipped_frames_ = true;
        return;
    }

    // keep track of a desired timestamp if we seek in this frame
    int64_t desiredTimestamp = 0;
    if( skipped_frames_ )
    {
        // need to seek

        // frame number we want
        const int64_t index = (frame_index_) % (num_frames_ + 1);

        // timestamp we want
        desiredTimestamp = start_time_ + av_rescale(index, den2_, num2_);

        // seek to the nearest keyframe before desiredTimestamp and flush buffers
        if(avformat_seek_file(avFormatContext_, streamIdx_, 0, desiredTimestamp, desiredTimestamp, 0) != 0)
        {
            put_flog(LOG_ERROR, "seeking error");
            return;
        }

        avcodec_flush_buffers(avCodecContext_);

        skipped_frames_ = false;
    }

    int avReadStatus = 0;

    AVPacket packet;
    int frameFinished;

    while((avReadStatus = av_read_frame(avFormatContext_, &packet)) >= 0)
    {
        // make sure packet is from video stream
        if(packet.stream_index == streamIdx_)
        {
            // decode video frame
            avcodec_decode_video2(avCodecContext_, avFrame_, &frameFinished, &packet);

            // make sure we got a full video frame
            if(frameFinished)
            {
                // note that the last packet decoded will have a DTS corresponding to this frame's PTS
                // hence the use of avFrame_->pkt_dts as the timestamp. also, we'll keep reading frames
                // until we get to the desired timestamp (in the case that we seeked)
                if(desiredTimestamp == 0 || (avFrame_->pkt_dts >= desiredTimestamp))
                {
                    // convert the frame from its native format to RGB
                    sws_scale(swsContext_, avFrame_->data, avFrame_->linesize, 0, avCodecContext_->height, avFrameRGB_->data, avFrameRGB_->linesize);

                    // put the RGB image to the already-created texture
                    // glTexSubImage2D uses the existing texture and is more efficient than other means
                    glBindTexture(GL_TEXTURE_2D, textureId_);
                    glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, avCodecContext_->width, avCodecContext_->height, GL_RGBA, GL_UNSIGNED_BYTE, avFrameRGB_->data[0]);

                    // free the packet that was allocated by av_read_frame
                    av_free_packet(&packet);

                    break;
                }
            }
        }

        // free the packet that was allocated by av_read_frame
        av_free_packet(&packet);
    }

    // see if we need to loop
    if(avReadStatus < 0 && loop_)
    {
        av_seek_frame(avFormatContext_, streamIdx_, 0, AVSEEK_FLAG_BACKWARD);
    }
}

void Movie::setPause(const bool pause)
{
    paused_ = pause;
}

void Movie::setLoop(const bool loop)
{
    loop_ = loop;
}
<commit_msg>Fix offset with resized movies on skipped nodes<commit_after>/*********************************************************************/
/* Copyright (c) 2011 - 2012, The University of Texas at Austin.     */
/* All rights reserved.                                              */
/*                                                                   */
/* Redistribution and use in source and binary forms, with or        */
/* without modification, are permitted provided that the following   */
/* conditions are met:                                               */
/*                                                                   */
/*   1. Redistributions of source code must retain the above         */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer.                                                  */
/*                                                                   */
/*   2. Redistributions in binary form must reproduce the above      */
/*      copyright notice, this list of conditions and the following  */
/*      disclaimer in the documentation and/or other materials       */
/*      provided with the distribution.                              */
/*                                                                   */
/*    THIS  SOFTWARE IS PROVIDED  BY THE  UNIVERSITY OF  TEXAS AT    */
/*    AUSTIN  ``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 UNIVERSITY  OF TEXAS AT    */
/*    AUSTIN 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.                                    */
/*                                                                   */
/* The views and conclusions contained in the software and           */
/* documentation are those of the authors and should not be          */
/* interpreted as representing official policies, either expressed   */
/* or implied, of The University of Texas at Austin.                 */
/*********************************************************************/

#include "Movie.h"
#include "main.h"
#include "log.h"

Movie::Movie(std::string uri)
{
    initialized_ = false;

    // defaults
    textureId_ = 0;
    textureBound_ = false;
    avFormatContext_ = NULL;
    avCodecContext_ = NULL;
    swsContext_ = NULL;
    avFrame_ = NULL;
    avFrameRGB_ = NULL;
    streamIdx_ = -1;
    paused_ = false;
    loop_ = true;

    start_time_ = 0;
    duration_ = 0;
    num_frames_ = 0;
    frame_index_ = 0;
    skipped_frames_ = false;

    // assign values
    uri_ = uri;

    // initialize ffmpeg
    av_register_all();

    // open movie file
    if(avformat_open_input(&avFormatContext_, uri.c_str(), NULL, NULL) != 0)
    {
        put_flog(LOG_ERROR, "could not open movie file %s", uri.c_str());
        return;
    }

    // get stream information
    if(avformat_find_stream_info(avFormatContext_, NULL) < 0)
    {
        put_flog(LOG_ERROR, "could not find stream information");
        return;
    }

    // dump format information to stderr
    av_dump_format(avFormatContext_, 0, uri.c_str(), 0);

    // find the first video stream
    streamIdx_ = -1;

    for(unsigned int i=0; i<avFormatContext_->nb_streams; i++)
    {
        if(avFormatContext_->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            streamIdx_ = i;
            break;
        }
    }

    if(streamIdx_ == -1)
    {
        put_flog(LOG_ERROR, "could not find video stream");
        return;
    }

    videostream_ = avFormatContext_->streams[streamIdx_];

    // get a pointer to the codec context for the video stream
    avCodecContext_ = videostream_->codec;

    // find the decoder for the video stream
    AVCodec * codec = avcodec_find_decoder(avCodecContext_->codec_id);

    if(codec == NULL)
    {
        put_flog(LOG_ERROR, "unsupported codec");
        return;
    }

    // open codec
    int ret = avcodec_open2(avCodecContext_, codec, NULL);

    if(ret < 0)
    {
        char errbuf[256];
        av_strerror(ret, errbuf, 256);

        put_flog(LOG_ERROR, "could not open codec, error code %i: %s", ret, errbuf);
        return;
    }

    den2_ = videostream_->time_base.den * videostream_->r_frame_rate.den;
    num2_ = videostream_->time_base.num * videostream_->r_frame_rate.num;

    // generate seeking parameters
    start_time_ = videostream_->start_time;
    duration_ = videostream_->duration;
    num_frames_ = av_rescale(duration_, num2_, den2_);
    frameDuration_ = (double)videostream_->r_frame_rate.den / (double)videostream_->r_frame_rate.num;

    put_flog(LOG_DEBUG, "seeking parameters: start_time = %i, duration_ = %i, num frames = %i", start_time_, duration_, num_frames_);

    // create texture for movie
    QImage image(avCodecContext_->width, avCodecContext_->height, QImage::Format_RGB32);
    image.fill(0);

    textureId_ = g_mainWindow->getGLWindow()->bindTexture(image, GL_TEXTURE_2D, GL_RGBA, QGLContext::LinearFilteringBindOption);
    textureBound_ = true;

    // allocate video frame for video decoding
    avFrame_ = avcodec_alloc_frame();

    // allocate video frame for RGB conversion
    avFrameRGB_ = avcodec_alloc_frame();

    if(avFrame_ == NULL || avFrameRGB_ == NULL)
    {
        put_flog(LOG_ERROR, "error allocating frames");
        return;
    }

    // get required buffer size and allocate buffer for pFrameRGB
    // this memory will be overwritten during frame conversion, but needs to be allocated ahead of time
    int numBytes = avpicture_get_size(PIX_FMT_RGBA, avCodecContext_->width, avCodecContext_->height);
    uint8_t * buffer = (uint8_t *)av_malloc(numBytes*sizeof(uint8_t));

    // assign buffer to pFrameRGB
    avpicture_fill((AVPicture *)avFrameRGB_, buffer, PIX_FMT_RGBA, avCodecContext_->width, avCodecContext_->height);

    // create sws scaler context
    swsContext_ = sws_getContext(avCodecContext_->width, avCodecContext_->height, avCodecContext_->pix_fmt, avCodecContext_->width, avCodecContext_->height, PIX_FMT_RGBA, SWS_FAST_BILINEAR, NULL, NULL, NULL);

    initialized_ = true;
}

Movie::~Movie()
{
    if(textureBound_ == true)
    {
        // delete bound texture
        glDeleteTextures(1, &textureId_); // it appears deleteTexture() below is not actually deleting the texture from the GPU...
        g_mainWindow->getGLWindow()->deleteTexture(textureId_);
    }

    // close the format context
    avformat_close_input(&avFormatContext_);

    // free scaler context
    sws_freeContext(swsContext_);

    // free frames
    av_free(avFrame_);
    av_free(avFrameRGB_);
}

void Movie::getDimensions(int &width, int &height)
{
    width = avCodecContext_->width;
    height = avCodecContext_->height;
}

void Movie::render(float tX, float tY, float tW, float tH)
{
    updateRenderedFrameCount();

    if(initialized_ != true)
    {
        return;
    }

    // draw the texture
    glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT);

    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, textureId_);

    // on zoom-out, clamp to edge (instead of showing the texture tiled / repeated)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glBegin(GL_QUADS);

    glTexCoord2f(tX,tY);
    glVertex2f(0.,0.);

    glTexCoord2f(tX+tW,tY);
    glVertex2f(1.,0.);

    glTexCoord2f(tX+tW,tY+tH);
    glVertex2f(1.,1.);

    glTexCoord2f(tX,tY+tH);
    glVertex2f(0.,1.);

    glEnd();

    glPopAttrib();
}

void Movie::nextFrame(bool skip)
{
    if( !initialized_ || (paused_ && !skipped_frames_) )
        return;

    // rate limiting
    double elapsedSeconds = 999999.;
    if( !nextTimestamp_.is_not_a_date_time( ))
        elapsedSeconds = (g_displayGroupManager->getTimestamp() -
                           nextTimestamp_).total_microseconds() / 1000000.;

    if( elapsedSeconds < frameDuration_ )
        return;

    // update timestamp of last frame
    nextTimestamp_ = g_displayGroupManager->getTimestamp();

    // seeking

    // where we should be after we read a frame in this function
    if( !paused_ )
        ++frame_index_;

    // if we're skipping this frame, increment the skipped frame counter and return
    // otherwise, make sure we're in the correct position in the stream and decode
    if( skip )
    {
        skipped_frames_ = true;
        return;
    }

    // keep track of a desired timestamp if we seek in this frame
    int64_t desiredTimestamp = 0;
    if( skipped_frames_ )
    {
        // need to seek

        // frame number we want
        const int64_t index = (frame_index_-1) % (num_frames_+1);

        // timestamp we want
        desiredTimestamp = start_time_ + av_rescale(index, den2_, num2_);

        // seek to the nearest keyframe before desiredTimestamp and flush buffers
        if(avformat_seek_file(avFormatContext_, streamIdx_, 0, desiredTimestamp, desiredTimestamp, 0) != 0)
        {
            put_flog(LOG_ERROR, "seeking error");
            return;
        }

        avcodec_flush_buffers(avCodecContext_);

        skipped_frames_ = false;
    }

    int avReadStatus = 0;

    AVPacket packet;
    int frameFinished;

    while((avReadStatus = av_read_frame(avFormatContext_, &packet)) >= 0)
    {
        // make sure packet is from video stream
        if(packet.stream_index == streamIdx_)
        {
            // decode video frame
            avcodec_decode_video2(avCodecContext_, avFrame_, &frameFinished, &packet);

            // make sure we got a full video frame
            if(frameFinished)
            {
                // note that the last packet decoded will have a DTS corresponding to this frame's PTS
                // hence the use of avFrame_->pkt_dts as the timestamp. also, we'll keep reading frames
                // until we get to the desired timestamp (in the case that we seeked)
                if(desiredTimestamp == 0 || (avFrame_->pkt_dts >= desiredTimestamp))
                {
                    // convert the frame from its native format to RGB
                    sws_scale(swsContext_, avFrame_->data, avFrame_->linesize, 0, avCodecContext_->height, avFrameRGB_->data, avFrameRGB_->linesize);

                    // put the RGB image to the already-created texture
                    // glTexSubImage2D uses the existing texture and is more efficient than other means
                    glBindTexture(GL_TEXTURE_2D, textureId_);
                    glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, avCodecContext_->width, avCodecContext_->height, GL_RGBA, GL_UNSIGNED_BYTE, avFrameRGB_->data[0]);

                    // free the packet that was allocated by av_read_frame
                    av_free_packet(&packet);

                    break;
                }
            }
        }

        // free the packet that was allocated by av_read_frame
        av_free_packet(&packet);
    }

    // see if we need to loop
    if(avReadStatus < 0 && loop_)
    {
        av_seek_frame(avFormatContext_, streamIdx_, 0, AVSEEK_FLAG_BACKWARD);
        frame_index_ = 0;
    }
}

void Movie::setPause(const bool pause)
{
    paused_ = pause;
}

void Movie::setLoop(const bool loop)
{
    loop_ = loop;
}
<|endoftext|>


<commit_before>#include "MyTools.h"
#include "Nurse.h"

//-----------------------------------------------------------------------------
//
//  S t r u c t u r e   S t a t e
//
//-----------------------------------------------------------------------------

// Updates the state if a new day is worked on shift newShift
void State::updateWithNewDay(int newShift){

	// Consecutives : +1 iff it is the same as the previous one
	consShifts_ = (shift_==newShift) ? (consShifts_ + 1) : 1;

	// Consecutive Days Worked : +1 if the new one is worked (!=0), 0 if it is a rest (==0)
	consDaysWorked_ = shift_ ? (consDaysWorked_ + 1) : 0;

	// Current shift worked : updated with the new one
	shift_ = newShift;
}

//-----------------------------------------------------------------------------
//
//  S t r u c t u r e   P r e f e r e n c e
//
//-----------------------------------------------------------------------------

// Initialization with an vector or size nNurses with no wished Shift-Off
Preferences::Preferences(int nNurses){
	nNurses_ = nNurses;
	vector<map<int,set<int>>> wishesOff
	for(int i=0; i<nNurses; i++){
		map<int,set<int>> m;
		wishesOff.push_back(m);
	}
	wishesOff_ = wishesOff;
}

// Add a wished day-shift off for a nurse
void Preferences::addShiftOff(int nurse, int day, int shift){
	// If the nurse does not already have a wish for that day -> insert a new emptyset on that day
	if(wishesOff_[nurse].find(day) == wishesOff_[nurse].end()){
		set<int> emptyset;
		wishesOff_[nurse].insert(pair<int,set<int>>(day,emptyset));
	}
	// Insert the wished shift in the set
	wishesOff_[nurse][day].insert(shift);
}

// Returns true if the nurses wants that shift off
bool Preferences::wantsTheShiftOff(int nurse, int day, int shift){
	// If the day is not in the wish-list, return false
	map<int,set<int>>::iterator itM = wishesOff_[nurse].find(day);
	if(itM == wishesOff_[nurse].end())
		return false;
	// If the shift is not in the wish-list for that day, return false
	else if(itM->second.find(shift) == itM->second.end())
		return false;
	else
		return true;
}

<commit_msg>Begin Samuel<commit_after>#include "MyTools.h"
#include "Nurse.h"

//-----------------------------------------------------------------------------
//
//  S t r u c t u r e   S t a t e
//
//-----------------------------------------------------------------------------

// Updates the state if a new day is worked on shift newShift
void State::updateWithNewDay(int newShift){

	// Consecutives : +1 iff it is the same as the previous one
	consShifts_ = (shift_==newShift) ? (consShifts_ + 1) : 1;

	// Consecutive Days Worked : +1 if the new one is worked (!=0), 0 if it is a rest (==0)
	consDaysWorked_ = shift_ ? (consDaysWorked_ + 1) : 0;

	// Current shift worked : updated with the new one
	shift_ = newShift;
}

//-----------------------------------------------------------------------------
//
//  S t r u c t u r e   P r e f e r e n c e
//
//-----------------------------------------------------------------------------

// Initialization with an vector or size nNurses with no wished Shift-Off.
Preferences::Preferences(int nNurses){
	nNurses_ = nNurses;
	vector<map<int,set<int>>> wishesOff
	for(int i=0; i<nNurses; i++){
		map<int,set<int>> m;
		wishesOff.push_back(m);
	}
	wishesOff_ = wishesOff;
}

// Add a wished day-shift off for a nurse
void Preferences::addShiftOff(int nurse, int day, int shift){
	// If the nurse does not already have a wish for that day -> insert a new emptyset on that day
	if(wishesOff_[nurse].find(day) == wishesOff_[nurse].end()){
		set<int> emptyset;
		wishesOff_[nurse].insert(pair<int,set<int>>(day,emptyset));
	}
	// Insert the wished shift in the set
	wishesOff_[nurse][day].insert(shift);
}

// Returns true if the nurses wants that shift off
bool Preferences::wantsTheShiftOff(int nurse, int day, int shift){
	// If the day is not in the wish-list, return false
	map<int,set<int>>::iterator itM = wishesOff_[nurse].find(day);
	if(itM == wishesOff_[nurse].end())
		return false;
	// If the shift is not in the wish-list for that day, return false
	else if(itM->second.find(shift) == itM->second.end())
		return false;
	else
		return true;
}

<|endoftext|>


<commit_before>#include <limits.h>
#include <gtest/gtest.h>
#include "ramdis.h"

// Global variable for coordinator locator string.
char* coordinatorLocator = NULL;

// Tests GET and SET commands.
TEST(GetSetTest, readWrite) {
  Context* context = ramdis_connect(coordinatorLocator);

  Object key;
  key.data = (void*)"Robert Tyre Jones Jr.";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  value.data = (void*)"Birthday: 1902/03/17, Height: 5'8\", Weight: 165lb";
  value.len = strlen((char*)value.data) + 1;

  set(context, &key, &value);

  Object* obj = get(context, &key);

  EXPECT_EQ(value.len, obj->len);
  EXPECT_STREQ((char*)value.data, (char*)obj->data);

  freeObject(obj);

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

TEST(LpushTest, pushManyValues) {
  Context* context = ramdis_connect(coordinatorLocator);

  /* Number of elements to put in the list. */
  uint32_t totalElements = (1<<13);
  /* Size of each element in bytes. */
  size_t elementSize = 8;

  Object key;
  key.data = (void*)"mylist";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  char valBuf[elementSize];
  value.data = (void*)valBuf;
  value.len = elementSize;

  uint32_t elemCount;
  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    elemCount = lpush(context, &key, &value);

    EXPECT_EQ(0, context->err);
    EXPECT_EQ(i + 1, elemCount);
  }

  ObjectArray* objArray;
  objArray = lrange(context, &key, 0, -1);

  EXPECT_EQ(totalElements, objArray->len);

  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", totalElements - i - 1);
    EXPECT_STREQ(valBuf, (char*)objArray->array[i].data);
  }

  freeObjectArray(objArray);

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

TEST(RpushTest, pushManyValues) {
  Context* context = ramdis_connect(coordinatorLocator);

  /* Number of elements to put in the list. */
  uint32_t totalElements = (1<<13);
  /* Size of each element in bytes. */
  size_t elementSize = 8;

  Object key;
  key.data = (void*)"mylist";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  char valBuf[elementSize];
  value.data = (void*)valBuf;
  value.len = elementSize;

  uint32_t elemCount;
  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    elemCount = rpush(context, &key, &value);

    EXPECT_EQ(0, context->err);
    EXPECT_EQ(i + 1, elemCount);
  }

  ObjectArray* objArray;
  objArray = lrange(context, &key, 0, -1);

  EXPECT_EQ(totalElements, objArray->len);

  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    EXPECT_STREQ(valBuf, (char*)objArray->array[i].data);
  }

  freeObjectArray(objArray);

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

TEST(LpopTest, popManyValues) {
  Context* context = ramdis_connect(coordinatorLocator);

  /* Number of elements to put in the list. */
  uint32_t totalElements = (1<<13);
  /* Size of each element in bytes. */
  size_t elementSize = 8;

  Object key;
  key.data = (void*)"mylist";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  char valBuf[elementSize];
  value.data = (void*)valBuf;
  value.len = elementSize;

  uint32_t elemCount;
  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    elemCount = lpush(context, &key, &value);

    EXPECT_EQ(0, context->err);
    EXPECT_EQ(i + 1, elemCount);
  }

  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", totalElements - i - 1);
    Object* obj = lpop(context, &key);
    EXPECT_STREQ(valBuf, (char*)obj->data);
    freeObject(obj);
  }

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

// Tests DEL command.
TEST(DelTest, deleteSingleObject) {
  Context* context = ramdis_connect(coordinatorLocator);

  Object key;
  key.data = (void*)"Robert Tyre Jones Jr.";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  value.data = (void*)"Birthday: 1902/03/17, Height: 5'8\", Weight: 165lb";
  value.len = strlen((char*)value.data) + 1;

  set(context, &key, &value);

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  uint64_t n = del(context, &keysArray);

  EXPECT_EQ(n, 1);

  Object *obj = get(context, &key);

  EXPECT_TRUE(obj == NULL);

  ramdis_disconnect(context);
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);

  for (int i = 0; i < argc; i++) {
    if (strcmp(argv[i], "-C") == 0) {
      coordinatorLocator = argv[i+1];
      break;
    }
  }

  if (coordinatorLocator == NULL) {
    printf("ERROR: Required -C argument missing for coordinator locator "
        "string.\n");
    return -1;
  }

  return RUN_ALL_TESTS();
}
<commit_msg>Added RPOP test.<commit_after>#include <limits.h>
#include <gtest/gtest.h>
#include "ramdis.h"

// Global variable for coordinator locator string.
char* coordinatorLocator = NULL;

// Tests GET and SET commands.
TEST(GetSetTest, readWrite) {
  Context* context = ramdis_connect(coordinatorLocator);

  Object key;
  key.data = (void*)"Robert Tyre Jones Jr.";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  value.data = (void*)"Birthday: 1902/03/17, Height: 5'8\", Weight: 165lb";
  value.len = strlen((char*)value.data) + 1;

  set(context, &key, &value);

  Object* obj = get(context, &key);

  EXPECT_EQ(value.len, obj->len);
  EXPECT_STREQ((char*)value.data, (char*)obj->data);

  freeObject(obj);

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

TEST(LpushTest, pushManyValues) {
  Context* context = ramdis_connect(coordinatorLocator);

  /* Number of elements to put in the list. */
  uint32_t totalElements = (1<<13);
  /* Size of each element in bytes. */
  size_t elementSize = 8;

  Object key;
  key.data = (void*)"mylist";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  char valBuf[elementSize];
  value.data = (void*)valBuf;
  value.len = elementSize;

  uint32_t elemCount;
  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    elemCount = lpush(context, &key, &value);

    EXPECT_EQ(0, context->err);
    EXPECT_EQ(i + 1, elemCount);
  }

  ObjectArray* objArray;
  objArray = lrange(context, &key, 0, -1);

  EXPECT_EQ(totalElements, objArray->len);

  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", totalElements - i - 1);
    EXPECT_STREQ(valBuf, (char*)objArray->array[i].data);
  }

  freeObjectArray(objArray);

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

TEST(RpushTest, pushManyValues) {
  Context* context = ramdis_connect(coordinatorLocator);

  /* Number of elements to put in the list. */
  uint32_t totalElements = (1<<13);
  /* Size of each element in bytes. */
  size_t elementSize = 8;

  Object key;
  key.data = (void*)"mylist";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  char valBuf[elementSize];
  value.data = (void*)valBuf;
  value.len = elementSize;

  uint32_t elemCount;
  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    elemCount = rpush(context, &key, &value);

    EXPECT_EQ(0, context->err);
    EXPECT_EQ(i + 1, elemCount);
  }

  ObjectArray* objArray;
  objArray = lrange(context, &key, 0, -1);

  EXPECT_EQ(totalElements, objArray->len);

  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    EXPECT_STREQ(valBuf, (char*)objArray->array[i].data);
  }

  freeObjectArray(objArray);

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

TEST(LpopTest, popManyValues) {
  Context* context = ramdis_connect(coordinatorLocator);

  /* Number of elements to put in the list. */
  uint32_t totalElements = (1<<13);
  /* Size of each element in bytes. */
  size_t elementSize = 8;

  Object key;
  key.data = (void*)"mylist";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  char valBuf[elementSize];
  value.data = (void*)valBuf;
  value.len = elementSize;

  uint32_t elemCount;
  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    elemCount = lpush(context, &key, &value);

    EXPECT_EQ(0, context->err);
    EXPECT_EQ(i + 1, elemCount);
  }

  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", totalElements - i - 1);
    Object* obj = lpop(context, &key);
    EXPECT_STREQ(valBuf, (char*)obj->data);
    freeObject(obj);
  }

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

TEST(RpopTest, popManyValues) {
  Context* context = ramdis_connect(coordinatorLocator);

  /* Number of elements to put in the list. */
  uint32_t totalElements = (1<<13);
  /* Size of each element in bytes. */
  size_t elementSize = 8;

  Object key;
  key.data = (void*)"mylist";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  char valBuf[elementSize];
  value.data = (void*)valBuf;
  value.len = elementSize;

  uint32_t elemCount;
  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    elemCount = lpush(context, &key, &value);

    EXPECT_EQ(0, context->err);
    EXPECT_EQ(i + 1, elemCount);
  }

  for (uint32_t i = 0; i < totalElements; i++) {
    sprintf(valBuf, "%07d", i);
    Object* obj = rpop(context, &key);
    EXPECT_STREQ(valBuf, (char*)obj->data);
    freeObject(obj);
  }

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  del(context, &keysArray);

  ramdis_disconnect(context);
}

// Tests DEL command.
TEST(DelTest, deleteSingleObject) {
  Context* context = ramdis_connect(coordinatorLocator);

  Object key;
  key.data = (void*)"Robert Tyre Jones Jr.";
  key.len = strlen((char*)key.data) + 1;

  Object value;
  value.data = (void*)"Birthday: 1902/03/17, Height: 5'8\", Weight: 165lb";
  value.len = strlen((char*)value.data) + 1;

  set(context, &key, &value);

  ObjectArray keysArray;
  keysArray.array = &key;
  keysArray.len = 1;

  uint64_t n = del(context, &keysArray);

  EXPECT_EQ(n, 1);

  Object *obj = get(context, &key);

  EXPECT_TRUE(obj == NULL);

  ramdis_disconnect(context);
}

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);

  for (int i = 0; i < argc; i++) {
    if (strcmp(argv[i], "-C") == 0) {
      coordinatorLocator = argv[i+1];
      break;
    }
  }

  if (coordinatorLocator == NULL) {
    printf("ERROR: Required -C argument missing for coordinator locator "
        "string.\n");
    return -1;
  }

  return RUN_ALL_TESTS();
}
<|endoftext|>


<commit_before>#include <algorithm>
#include <bts/net/chain_downloader.hpp>
#include <bts/net/chain_server_commands.hpp>

#include <fc/network/tcp_socket.hpp>
#include <fc/io/raw_variant.hpp>
#include <fc/thread/thread.hpp>

namespace bts { namespace net {

    namespace detail {
      class chain_downloader_impl {
        public:
          chain_downloader* self;

          std::unique_ptr<fc::tcp_socket> _client_socket;
          std::vector<fc::ip::endpoint> _chain_servers;

          void connect_to_chain_server()
          { try {
              auto next_server = _chain_servers.begin();
              _client_socket = std::unique_ptr<fc::tcp_socket>(new fc::tcp_socket);
              while(!_client_socket->is_open() && next_server != _chain_servers.end()) {
                  _client_socket = std::unique_ptr<fc::tcp_socket>(new fc::tcp_socket);
                  try {
                      _client_socket->connect_to(*(next_server++));
                  } catch (const fc::exception& e) {
                      wlog("Failed to connect to chain_server: ${e}", ("e", e.to_detail_string()));
                      _client_socket->close();
                      continue;
                  }

                  uint32_t protocol_version = -1;
                  fc::raw::unpack(*_client_socket, protocol_version);
                  if (protocol_version != PROTOCOL_VERSION) {
                      wlog("Can't talk to chain server; he's using protocol ${srv} and I'm using ${cli}!",
                           ("srv", protocol_version)("cli", PROTOCOL_VERSION));
                      fc::raw::pack(*_client_socket, finish);
                      _client_socket->close();
                  }
              }
          } FC_RETHROW_EXCEPTIONS(error, "") }

          void get_all_blocks(std::function<void (const blockchain::full_block&)> new_block_callback,
                              uint32_t first_block_number)
          { try {
              if (!new_block_callback)
                  return;

              connect_to_chain_server();
              FC_ASSERT(_client_socket->is_open(), "unable to connect to any chain server");
              ilog("Connected to ${remote}; requesting blocks after ${num}",
                   ("remote", _client_socket->remote_endpoint())("num", first_block_number));

              ulog("Starting fast-sync of blocks from ${num}", ("num", first_block_number));
              auto start_time = fc::time_point::now();

              fc::raw::pack(*_client_socket, get_blocks_from_number);
              fc::raw::pack(*_client_socket, first_block_number);

              uint32_t blocks_to_retrieve = 0;
              uint32_t blocks_in = 0;
              fc::raw::unpack(*_client_socket, blocks_to_retrieve);
              ilog("Server at ${remote} is sending us ${num} blocks.",
                   ("remote", _client_socket->remote_endpoint())("num", blocks_to_retrieve));

              while(blocks_to_retrieve > 0)
              {
                  blockchain::full_block block;
                  fc::raw::unpack(*_client_socket, block);

                  new_block_callback(block);
                  --blocks_to_retrieve;
                  ++blocks_in;

                  if(blocks_to_retrieve == 0) {
                      fc::raw::unpack(*_client_socket, blocks_to_retrieve);
                      if(blocks_to_retrieve > 0)
                          ilog("Server at ${remote} is sending us ${num} blocks.",
                               ("remote", _client_socket->remote_endpoint())("num", blocks_to_retrieve));
                  }
              }

              ulog("Finished fast-syncing ${num} blocks at ${rate} blocks/sec.",
                   ("num", blocks_in)("rate", blocks_in/((fc::time_point::now() - start_time).count() / 1000000.0)));
              ilog("Finished getting ${num} blocks from ${remote}",
                   ("num", blocks_in)("remote", _client_socket->remote_endpoint()));
              fc::raw::pack(*_client_socket, finish);
            } FC_RETHROW_EXCEPTIONS(error, "", ("first_block_number", first_block_number))
          }
      };
    } //namespace detail

    chain_downloader::chain_downloader(std::vector<fc::ip::endpoint> chain_servers)
      : my(new detail::chain_downloader_impl)
    {
        my->self = this;
        add_chain_servers(chain_servers);
    }

    chain_downloader::~chain_downloader()
    {}

    void chain_downloader::add_chain_server(const fc::ip::endpoint& server)
    {
        if(std::find(my->_chain_servers.begin(), my->_chain_servers.end(), server) == my->_chain_servers.end())
            my->_chain_servers.push_back(server);
    }

    void chain_downloader::add_chain_servers(const std::vector<fc::ip::endpoint>& servers)
    {
        my->_chain_servers.reserve(my->_chain_servers.size() + servers.size());

        for (auto server : servers)
            add_chain_server(server);

        my->_chain_servers.shrink_to_fit();
    }

    fc::future<void> chain_downloader::get_all_blocks(std::function<void (const blockchain::full_block&)> new_block_callback,
                                                      uint32_t first_block_number)
    {
        return fc::async([=]{my->get_all_blocks(new_block_callback, first_block_number);});
    }

  } } //namespace bts::blockchain
<commit_msg>Log chain downloader completion and rate<commit_after>#include <algorithm>
#include <bts/net/chain_downloader.hpp>
#include <bts/net/chain_server_commands.hpp>

#include <fc/network/tcp_socket.hpp>
#include <fc/io/raw_variant.hpp>
#include <fc/thread/thread.hpp>

namespace bts { namespace net {

    namespace detail {
      class chain_downloader_impl {
        public:
          chain_downloader* self;

          std::unique_ptr<fc::tcp_socket> _client_socket;
          std::vector<fc::ip::endpoint> _chain_servers;

          void connect_to_chain_server()
          { try {
              auto next_server = _chain_servers.begin();
              _client_socket = std::unique_ptr<fc::tcp_socket>(new fc::tcp_socket);
              while(!_client_socket->is_open() && next_server != _chain_servers.end()) {
                  _client_socket = std::unique_ptr<fc::tcp_socket>(new fc::tcp_socket);
                  try {
                      _client_socket->connect_to(*(next_server++));
                  } catch (const fc::exception& e) {
                      wlog("Failed to connect to chain_server: ${e}", ("e", e.to_detail_string()));
                      _client_socket->close();
                      continue;
                  }

                  uint32_t protocol_version = -1;
                  fc::raw::unpack(*_client_socket, protocol_version);
                  if (protocol_version != PROTOCOL_VERSION) {
                      wlog("Can't talk to chain server; he's using protocol ${srv} and I'm using ${cli}!",
                           ("srv", protocol_version)("cli", PROTOCOL_VERSION));
                      fc::raw::pack(*_client_socket, finish);
                      _client_socket->close();
                  }
              }
          } FC_RETHROW_EXCEPTIONS(error, "") }

          void get_all_blocks(std::function<void (const blockchain::full_block&)> new_block_callback,
                              uint32_t first_block_number)
          { try {
              if (!new_block_callback)
                  return;

              connect_to_chain_server();
              FC_ASSERT(_client_socket->is_open(), "unable to connect to any chain server");
              ilog("Connected to ${remote}; requesting blocks after ${num}",
                   ("remote", _client_socket->remote_endpoint())("num", first_block_number));

              ulog("Starting fast-sync of blocks from ${num}", ("num", first_block_number));
              auto start_time = fc::time_point::now();

              fc::raw::pack(*_client_socket, get_blocks_from_number);
              fc::raw::pack(*_client_socket, first_block_number);

              uint32_t blocks_to_retrieve = 0;
              uint32_t blocks_in = 0;
              fc::raw::unpack(*_client_socket, blocks_to_retrieve);
              ilog("Server at ${remote} is sending us ${num} blocks.",
                   ("remote", _client_socket->remote_endpoint())("num", blocks_to_retrieve));

              while(blocks_to_retrieve > 0)
              {
                  blockchain::full_block block;
                  fc::raw::unpack(*_client_socket, block);

                  new_block_callback(block);
                  --blocks_to_retrieve;
                  ++blocks_in;

                  if(blocks_to_retrieve == 0) {
                      fc::raw::unpack(*_client_socket, blocks_to_retrieve);
                      if(blocks_to_retrieve > 0)
                          ilog("Server at ${remote} is sending us ${num} blocks.",
                               ("remote", _client_socket->remote_endpoint())("num", blocks_to_retrieve));
                  }
              }

              ulog("Finished fast-syncing ${num} blocks at ${rate} blocks/sec.",
                   ("num", blocks_in)("rate", blocks_in/((fc::time_point::now() - start_time).count() / 1000000.0)));
              wlog("Finished getting ${num} blocks from ${remote} at ${rate} blocks/sec.",
                   ("num", blocks_in)("remote", _client_socket->remote_endpoint())
                   ("rate", blocks_in/((fc::time_point::now() - start_time).count() / 1000000.0)));
              fc::raw::pack(*_client_socket, finish);
            } FC_RETHROW_EXCEPTIONS(error, "", ("first_block_number", first_block_number))
          }
      };
    } //namespace detail

    chain_downloader::chain_downloader(std::vector<fc::ip::endpoint> chain_servers)
      : my(new detail::chain_downloader_impl)
    {
        my->self = this;
        add_chain_servers(chain_servers);
    }

    chain_downloader::~chain_downloader()
    {}

    void chain_downloader::add_chain_server(const fc::ip::endpoint& server)
    {
        if(std::find(my->_chain_servers.begin(), my->_chain_servers.end(), server) == my->_chain_servers.end())
            my->_chain_servers.push_back(server);
    }

    void chain_downloader::add_chain_servers(const std::vector<fc::ip::endpoint>& servers)
    {
        my->_chain_servers.reserve(my->_chain_servers.size() + servers.size());

        for (auto server : servers)
            add_chain_server(server);

        my->_chain_servers.shrink_to_fit();
    }

    fc::future<void> chain_downloader::get_all_blocks(std::function<void (const blockchain::full_block&)> new_block_callback,
                                                      uint32_t first_block_number)
    {
        return fc::async([=]{my->get_all_blocks(new_block_callback, first_block_number);});
    }

  } } //namespace bts::blockchain
<|endoftext|>