<commit_before>#include <QPainter>
#include <QAbstractEventDispatcher>
#include <QApplication>
#include <QPaintEvent>
#include <QFontMetrics>
#include <QTextStream>
#include <qterm.h>
#include <stdio.h>
#include <QKeyEvent>
#include <QTimer>
#include <sys/select.h>
#include <errno.h>
#ifdef __QNX__
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <bps/bps.h>
#ifdef BPS_VERSION
#include <bps/virtualkeyboard.h>
#else
#include <bbsupport/Keyboard>
#include <bbsupport/Notification>
#endif
#endif

#define WIDTH    80
#define HEIGHT    17
#define BLINK_SPEED 1000

QTerm::QTerm(QWidget *parent) : QWidget(parent)
{
    term_create( &terminal );
    term_begin( terminal, WIDTH, HEIGHT, 0 );
    init();
}

QTerm::QTerm(QWidget *parent, term_t terminal) : QWidget(parent)
{
    this->terminal = terminal;
    init();
}

void QTerm::init()
{
    char_width = 0;
    char_height = 0;
    cursor_x = -1;
    cursor_y = -1;
    cursor_on = 1;

    resize(1024, 600);
    term_set_user_data( terminal, this );
    term_register_update( terminal, term_update );
    term_register_cursor( terminal, term_update_cursor );
    term_register_bell( terminal, term_bell );
    notifier = new QSocketNotifier( term_get_file_descriptor(terminal), QSocketNotifier::Read );
    exit_notifier = new QSocketNotifier( term_get_file_descriptor(terminal), QSocketNotifier::Exception );
    cursor_timer = new QTimer( this );
    QObject::connect(notifier, SIGNAL(activated(int)), this, SLOT(terminal_data()));
    QObject::connect(exit_notifier, SIGNAL(activated(int)), this, SLOT(terminate()));
    QObject::connect(cursor_timer, SIGNAL(timeout()), this, SLOT(blink_cursor()));

    // Setup the initial font
    font = new QFont();
    font->setStyleHint(QFont::Courier);//QFont::TypeWriter);
    font->setPointSize(12);
    font->setStyleStrategy(QFont::NoAntialias);
    font->setFamily("Monospace");
    font->setFixedPitch(true);
    font->setKerning(false);

    // Workaround for a bug in OSX - Dave reports that maxWidth returns 0,
    // when width of different characters returns the correct value
    QFontMetrics metrics(*font);
    char_width = metrics.maxWidth();
    if(char_width==0) {
        fontWorkAround = true;
        char_width = metrics.width(QChar('X'));
    } else {
        fontWorkAround = false;
    }

    char_height = metrics.lineSpacing();
    char_descent = metrics.descent();

#ifdef __QNX__
#ifdef BPS_VERSION
    virtualkeyboard_show();
#else
    BlackBerry::Keyboard::instance().show();
#endif
#endif
    cursor_timer->start(BLINK_SPEED);
}

QTerm::~QTerm()
{
    delete notifier;
    delete exit_notifier;
    term_free( terminal );
}

void QTerm::term_bell(term_t handle)
{
#ifdef __QNX__
    char command[] = "msg::play_sound\ndat:json:{\"sound\":\"notification_general\"}";
    int f = open("/pps/services/multimedia/sound/control", O_RDWR);
    write(f, command, sizeof(command));
    ::close(f);
#else
    QApplication::beep();
#endif
}

void QTerm::term_update(term_t handle, int x, int y, int width, int height)
{
    QTerm *term = (QTerm *)term_get_user_data( handle );

    term->update_grid(x, y, width, height);
}

void QTerm::term_update_cursor(term_t handle, int x, int y)
{
    int old_x, old_y;
    QTerm *term = (QTerm *)term_get_user_data( handle );

    old_x = term->cursor_x;
    old_y = term->cursor_y;

    // Reset cursor blink
    term->cursor_on = 1;
    term->cursor_timer->stop();
    term->cursor_timer->start(BLINK_SPEED);

    // Update position
    term->cursor_x = x;
    term->cursor_y = y;

    // Update old and new cursor location
    term->update_grid( old_x, old_y, 1, 1);
    term->update_grid( term->cursor_x, term->cursor_y, 1, 1);
}

void QTerm::terminal_data()
{
    if( !term_process_child( terminal ) ) {
        exit(0);
    }
}

void QTerm::terminate()
{
    exit(0);
}

void QTerm::blink_cursor()
{
    //int coord_x;
    cursor_on ^= 1;
    update_grid( cursor_x, cursor_y, 1, 1);
}

// Called to update the grid.
// Region is based on grid coordinates.
void QTerm::update_grid(int grid_x_min,
                        int grid_y_min,
                        int grid_width,
                        int grid_height)
{
    int coords_x_min, coords_y_min;
    int coords_x_max, coords_y_max;

//    fprintf(stderr,"Updating Grid: (%d,%d) %d x %d\n", grid_x_min, grid_y_min, grid_width, grid_height);

    if (grid_x_min < 0 || grid_y_min < 0) {
        return;
    }
    if ( fontWorkAround ) {
        // If fontWorkAround is set, we cannot trust the characters to
        // be monospaced.  So we need to calculate the coordinates based
        // on the string lengths of the strings in the grid.
        QFontMetrics metrics(*font);

        const char *str;
        int i;

        coords_x_min = 9999;    // Replace with some window property.
        coords_x_max = 0;
        for (i= grid_y_min; i < (grid_y_min + grid_height); i++) {
            int tmp;
            str = term_get_line( terminal, i );

            // X limits
            tmp = metrics.width(QString(str),grid_x_min);
            if (tmp < coords_x_min) {
                coords_x_min = tmp;
            }
            tmp = metrics.width(QString(str),grid_x_min + grid_width);
            if (tmp > coords_x_max) {
                coords_x_max = tmp;
            }
        }

    } else {
        coords_x_min = grid_x_min * char_width;
        coords_x_max = grid_width * char_width;
    }
    coords_y_min = grid_y_min * char_height;
    coords_y_max = grid_height * char_height;
    QWidget::update(coords_x_min, coords_y_min,
                    coords_x_max, coords_y_max);
}
void QTerm::paintEvent(QPaintEvent *event)
{
    int i;
#if 0
    int j;
    int color;
    const wchar_t **grid;
    const uint32_t **attribs;
    const uint32_t **colors;
#else
    const char *str;
    int cursor_x_coord;
#endif
    QPainter painter(this);
    QRect windowRect = painter.window();
    QColor fgColor(255,255,255);
    QColor bgColor(0,0,0);
    int gridWidth, gridHeight;

    painter.setBackgroundMode(Qt::TransparentMode);
    painter.setBrush(QColor(8, 0, 0));
    painter.setFont( *font );

    // Get grid dimensions
    term_get_grid_size(terminal, &gridWidth, &gridHeight);

    // First erase the grid with its current dimensions
    painter.drawRect(event->rect());

//    fprintf(stderr,"Rect: (%d, %d) %d x %d\n", event->rect().x(), event->rect().y(), event->rect().width(), event->rect().height());

    if (cursor_x < 0 || cursor_y < 0) {
        return;
    }
    painter.setPen(fgColor);
    painter.setBrush(fgColor);

#if 1
    str = term_get_line( terminal, cursor_y );
    // Workaround to get the cursor in the right spot.  For some
    // reason, on OSX (again), the monospace font does is not really
    // monospace for skinny characters!
    if (fontWorkAround) {
        cursor_x_coord = painter.fontMetrics().width(QString(str),cursor_x);
    } else {
        cursor_x_coord = cursor_x * char_width;
    }

    if ( cursor_on ) {
       painter.setPen(fgColor);
       painter.setBrush(fgColor);
       painter.drawRect( cursor_x_coord +1,
                         cursor_y * char_height + 1,
                         char_width-2, char_height-2);
    } else {
       painter.setPen(bgColor);
       painter.setBrush(bgColor);
       painter.drawRect( cursor_x_coord +1,
                         cursor_y * char_height + 1,
                         char_width-2, char_height-2);
    }
    painter.setPen(fgColor);
    painter.setBrush(fgColor);

    for (i=0; i< gridHeight;i++) {
        str = term_get_line( terminal, i );
        painter.drawText(0, (i) * char_height,
                        windowRect.width(),char_height,
                        Qt::TextExpandTabs,
                        str,
                        &windowRect
                        );
    }

#else
    grid = term_get_grid( terminal );
    attribs = term_get_attribs( terminal );
    colors = term_get_colours( terminal );

    painter.setBrush(fgColor);
    for( i = 0; i < term_get_height( terminal ); i ++ ) {
        for( j = 0; j < term_get_width( terminal ); j ++ ) {
            if( cursor_on && j == cursor_x && i == cursor_y ) {
                painter.drawRect(j * char_width + 1, i * char_height + 1, char_width - 2, char_height - 2);
                painter.setPen(QColor(0, 0, 0));
                painter.drawText(j * char_width, (i + 1) * char_height - char_descent, QString( QChar( grid[ i ][ j ] ) ) );
                painter.setPen(QColor(255, 255, 255));
            } else {

                color = term_get_fg_color( attribs[ i ][ j ], colors[ i ][ j ] );
                painter.setPen(QColor((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF));
                painter.drawText(j * char_width, (i + 1) * char_height - char_descent, QString( QChar( grid[ i ][ j ] ) ) );
            }
        }
    }
#endif
}

void QTerm::keyPressEvent(QKeyEvent *event)
{
    switch(event->key()) {
        // FIXME These first two are a workaround for a bug in QT. Remove once it is fixed
        case Qt::Key_CapsLock:
        case Qt::Key_Shift:
            break;
        case Qt::Key_Up:
            term_send_special( terminal, TERM_KEY_UP );
            break;
        case Qt::Key_Down:
            term_send_special( terminal, TERM_KEY_DOWN );
            break;
        case Qt::Key_Right:
            term_send_special( terminal, TERM_KEY_RIGHT );
            break;
        case Qt::Key_Left:
            term_send_special( terminal, TERM_KEY_LEFT );
            break;
        default:
            term_send_data( terminal, event->text().toUtf8().constData(), event->text().count() );
            break;
    }
}

void QTerm::resizeEvent(QResizeEvent *event)
{
    if( char_width != 0 && char_height != 0 ) {
#ifdef __QNX__
        {
            int kbd_height;
#ifdef BPS_VERSION
            virtualkeyboard_get_height( &kbd_height );
#else
            kbd_height = BlackBerry::Keyboard::instance().keyboardHeight();
#endif
            term_resize( terminal, event->size().width() / char_width, (event->size().height() - kbd_height) / char_height, 0 );
        }
#else
        term_resize( terminal, event->size().width() / char_width, event->size().height() / char_height, 0 );
#endif
    }
}

int main(int argc, char *argv[])
{
    term_t terminal;

#ifdef __QNX__
#ifdef BPS_VERSION
    if( bps_initialize() != BPS_SUCCESS ) {
        fprintf(stderr, "Failed to initialize bps (%s)\n", strerror( errno ) );
        exit(1);
    }
#endif
#endif

    if( !term_create( &terminal ) ) {
        fprintf(stderr, "Failed to create terminal (%s)\n", strerror( errno ) );
        exit(1);
    }
    if( !term_begin( terminal, WIDTH, HEIGHT, 0 ) ) {
        fprintf(stderr, "Failed to begin terminal (%s)\n", strerror( errno ) );
        exit(1);
    }
    {
        QCoreApplication::addLibraryPath("app/native/lib");
        QApplication app(argc, argv);

        QTerm term(NULL, terminal);
        term.show();

        return app.exec();
    }
}
<commit_msg>Only resize to 1024x600 on qnx, since that's the playbook resolution. This needs to be fixed for colt<commit_after>#include <QPainter>
#include <QAbstractEventDispatcher>
#include <QApplication>
#include <QPaintEvent>
#include <QFontMetrics>
#include <QTextStream>
#include <qterm.h>
#include <stdio.h>
#include <QKeyEvent>
#include <QTimer>
#include <sys/select.h>
#include <errno.h>
#ifdef __QNX__
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <bps/bps.h>
#ifdef BPS_VERSION
#include <bps/virtualkeyboard.h>
#else
#include <bbsupport/Keyboard>
#include <bbsupport/Notification>
#endif
#endif

#define WIDTH    80
#define HEIGHT    17
#define BLINK_SPEED 1000

QTerm::QTerm(QWidget *parent) : QWidget(parent)
{
    term_create( &terminal );
    term_begin( terminal, WIDTH, HEIGHT, 0 );
    init();
}

QTerm::QTerm(QWidget *parent, term_t terminal) : QWidget(parent)
{
    this->terminal = terminal;
    init();
}

void QTerm::init()
{
    char_width = 0;
    char_height = 0;
    cursor_x = -1;
    cursor_y = -1;
    cursor_on = 1;

#ifdef __QNX__
    resize(1024, 600);
#endif
    term_set_user_data( terminal, this );
    term_register_update( terminal, term_update );
    term_register_cursor( terminal, term_update_cursor );
    term_register_bell( terminal, term_bell );
    notifier = new QSocketNotifier( term_get_file_descriptor(terminal), QSocketNotifier::Read );
    exit_notifier = new QSocketNotifier( term_get_file_descriptor(terminal), QSocketNotifier::Exception );
    cursor_timer = new QTimer( this );
    QObject::connect(notifier, SIGNAL(activated(int)), this, SLOT(terminal_data()));
    QObject::connect(exit_notifier, SIGNAL(activated(int)), this, SLOT(terminate()));
    QObject::connect(cursor_timer, SIGNAL(timeout()), this, SLOT(blink_cursor()));

    // Setup the initial font
    font = new QFont();
    font->setStyleHint(QFont::Courier);//QFont::TypeWriter);
    font->setPointSize(12);
    font->setStyleStrategy(QFont::NoAntialias);
    font->setFamily("Monospace");
    font->setFixedPitch(true);
    font->setKerning(false);

    // Workaround for a bug in OSX - Dave reports that maxWidth returns 0,
    // when width of different characters returns the correct value
    QFontMetrics metrics(*font);
    char_width = metrics.maxWidth();
    if(char_width==0) {
        fontWorkAround = true;
        char_width = metrics.width(QChar('X'));
    } else {
        fontWorkAround = false;
    }

    char_height = metrics.lineSpacing();
    char_descent = metrics.descent();

#ifdef __QNX__
#ifdef BPS_VERSION
    virtualkeyboard_show();
#else
    BlackBerry::Keyboard::instance().show();
#endif
#endif
    cursor_timer->start(BLINK_SPEED);
}

QTerm::~QTerm()
{
    delete notifier;
    delete exit_notifier;
    term_free( terminal );
}

void QTerm::term_bell(term_t handle)
{
#ifdef __QNX__
    char command[] = "msg::play_sound\ndat:json:{\"sound\":\"notification_general\"}";
    int f = open("/pps/services/multimedia/sound/control", O_RDWR);
    write(f, command, sizeof(command));
    ::close(f);
#else
    QApplication::beep();
#endif
}

void QTerm::term_update(term_t handle, int x, int y, int width, int height)
{
    QTerm *term = (QTerm *)term_get_user_data( handle );

    term->update_grid(x, y, width, height);
}

void QTerm::term_update_cursor(term_t handle, int x, int y)
{
    int old_x, old_y;
    QTerm *term = (QTerm *)term_get_user_data( handle );

    old_x = term->cursor_x;
    old_y = term->cursor_y;

    // Reset cursor blink
    term->cursor_on = 1;
    term->cursor_timer->stop();
    term->cursor_timer->start(BLINK_SPEED);

    // Update position
    term->cursor_x = x;
    term->cursor_y = y;

    // Update old and new cursor location
    term->update_grid( old_x, old_y, 1, 1);
    term->update_grid( term->cursor_x, term->cursor_y, 1, 1);
}

void QTerm::terminal_data()
{
    if( !term_process_child( terminal ) ) {
        exit(0);
    }
}

void QTerm::terminate()
{
    exit(0);
}

void QTerm::blink_cursor()
{
    //int coord_x;
    cursor_on ^= 1;
    update_grid( cursor_x, cursor_y, 1, 1);
}

// Called to update the grid.
// Region is based on grid coordinates.
void QTerm::update_grid(int grid_x_min,
                        int grid_y_min,
                        int grid_width,
                        int grid_height)
{
    int coords_x_min, coords_y_min;
    int coords_x_max, coords_y_max;

//    fprintf(stderr,"Updating Grid: (%d,%d) %d x %d\n", grid_x_min, grid_y_min, grid_width, grid_height);

    if (grid_x_min < 0 || grid_y_min < 0) {
        return;
    }
    if ( fontWorkAround ) {
        // If fontWorkAround is set, we cannot trust the characters to
        // be monospaced.  So we need to calculate the coordinates based
        // on the string lengths of the strings in the grid.
        QFontMetrics metrics(*font);

        const char *str;
        int i;

        coords_x_min = 9999;    // Replace with some window property.
        coords_x_max = 0;
        for (i= grid_y_min; i < (grid_y_min + grid_height); i++) {
            int tmp;
            str = term_get_line( terminal, i );

            // X limits
            tmp = metrics.width(QString(str),grid_x_min);
            if (tmp < coords_x_min) {
                coords_x_min = tmp;
            }
            tmp = metrics.width(QString(str),grid_x_min + grid_width);
            if (tmp > coords_x_max) {
                coords_x_max = tmp;
            }
        }

    } else {
        coords_x_min = grid_x_min * char_width;
        coords_x_max = grid_width * char_width;
    }
    coords_y_min = grid_y_min * char_height;
    coords_y_max = grid_height * char_height;
    QWidget::update(coords_x_min, coords_y_min,
                    coords_x_max, coords_y_max);
}
void QTerm::paintEvent(QPaintEvent *event)
{
    int i;
#if 0
    int j;
    int color;
    const wchar_t **grid;
    const uint32_t **attribs;
    const uint32_t **colors;
#else
    const char *str;
    int cursor_x_coord;
#endif
    QPainter painter(this);
    QRect windowRect = painter.window();
    QColor fgColor(255,255,255);
    QColor bgColor(0,0,0);
    int gridWidth, gridHeight;

    painter.setBackgroundMode(Qt::TransparentMode);
    painter.setBrush(QColor(8, 0, 0));
    painter.setFont( *font );

    // Get grid dimensions
    term_get_grid_size(terminal, &gridWidth, &gridHeight);

    // First erase the grid with its current dimensions
    painter.drawRect(event->rect());

//    fprintf(stderr,"Rect: (%d, %d) %d x %d\n", event->rect().x(), event->rect().y(), event->rect().width(), event->rect().height());

    if (cursor_x < 0 || cursor_y < 0) {
        return;
    }
    painter.setPen(fgColor);
    painter.setBrush(fgColor);

#if 1
    str = term_get_line( terminal, cursor_y );
    // Workaround to get the cursor in the right spot.  For some
    // reason, on OSX (again), the monospace font does is not really
    // monospace for skinny characters!
    if (fontWorkAround) {
        cursor_x_coord = painter.fontMetrics().width(QString(str),cursor_x);
    } else {
        cursor_x_coord = cursor_x * char_width;
    }

    if ( cursor_on ) {
       painter.setPen(fgColor);
       painter.setBrush(fgColor);
       painter.drawRect( cursor_x_coord +1,
                         cursor_y * char_height + 1,
                         char_width-2, char_height-2);
    } else {
       painter.setPen(bgColor);
       painter.setBrush(bgColor);
       painter.drawRect( cursor_x_coord +1,
                         cursor_y * char_height + 1,
                         char_width-2, char_height-2);
    }
    painter.setPen(fgColor);
    painter.setBrush(fgColor);

    for (i=0; i< gridHeight;i++) {
        str = term_get_line( terminal, i );
        painter.drawText(0, (i) * char_height,
                        windowRect.width(),char_height,
                        Qt::TextExpandTabs,
                        str,
                        &windowRect
                        );
    }

#else
    grid = term_get_grid( terminal );
    attribs = term_get_attribs( terminal );
    colors = term_get_colours( terminal );

    painter.setBrush(fgColor);
    for( i = 0; i < term_get_height( terminal ); i ++ ) {
        for( j = 0; j < term_get_width( terminal ); j ++ ) {
            if( cursor_on && j == cursor_x && i == cursor_y ) {
                painter.drawRect(j * char_width + 1, i * char_height + 1, char_width - 2, char_height - 2);
                painter.setPen(QColor(0, 0, 0));
                painter.drawText(j * char_width, (i + 1) * char_height - char_descent, QString( QChar( grid[ i ][ j ] ) ) );
                painter.setPen(QColor(255, 255, 255));
            } else {

                color = term_get_fg_color( attribs[ i ][ j ], colors[ i ][ j ] );
                painter.setPen(QColor((color >> 16) & 0xFF, (color >> 8) & 0xFF, color & 0xFF));
                painter.drawText(j * char_width, (i + 1) * char_height - char_descent, QString( QChar( grid[ i ][ j ] ) ) );
            }
        }
    }
#endif
}

void QTerm::keyPressEvent(QKeyEvent *event)
{
    switch(event->key()) {
        // FIXME These first two are a workaround for a bug in QT. Remove once it is fixed
        case Qt::Key_CapsLock:
        case Qt::Key_Shift:
            break;
        case Qt::Key_Up:
            term_send_special( terminal, TERM_KEY_UP );
            break;
        case Qt::Key_Down:
            term_send_special( terminal, TERM_KEY_DOWN );
            break;
        case Qt::Key_Right:
            term_send_special( terminal, TERM_KEY_RIGHT );
            break;
        case Qt::Key_Left:
            term_send_special( terminal, TERM_KEY_LEFT );
            break;
        default:
            term_send_data( terminal, event->text().toUtf8().constData(), event->text().count() );
            break;
    }
}

void QTerm::resizeEvent(QResizeEvent *event)
{
    if( char_width != 0 && char_height != 0 ) {
#ifdef __QNX__
        {
            int kbd_height;
#ifdef BPS_VERSION
            virtualkeyboard_get_height( &kbd_height );
#else
            kbd_height = BlackBerry::Keyboard::instance().keyboardHeight();
#endif
            term_resize( terminal, event->size().width() / char_width, (event->size().height() - kbd_height) / char_height, 0 );
        }
#else
        term_resize( terminal, event->size().width() / char_width, event->size().height() / char_height, 0 );
#endif
    }
}

int main(int argc, char *argv[])
{
    term_t terminal;

#ifdef __QNX__
#ifdef BPS_VERSION
    if( bps_initialize() != BPS_SUCCESS ) {
        fprintf(stderr, "Failed to initialize bps (%s)\n", strerror( errno ) );
        exit(1);
    }
#endif
#endif

    if( !term_create( &terminal ) ) {
        fprintf(stderr, "Failed to create terminal (%s)\n", strerror( errno ) );
        exit(1);
    }
    if( !term_begin( terminal, WIDTH, HEIGHT, 0 ) ) {
        fprintf(stderr, "Failed to begin terminal (%s)\n", strerror( errno ) );
        exit(1);
    }
    {
        QCoreApplication::addLibraryPath("app/native/lib");
        QApplication app(argc, argv);

        QTerm term(NULL, terminal);
        term.show();

        return app.exec();
    }
}
<|endoftext|>


<commit_before>/*! @file mean.cc
 *  @brief Arithmetic and geometric means calculation.
 *  @author Markovtsev Vadim <v.markovtsev@samsung.com>
 *  @version 1.0
 *
 *  @section Notes
 *  This code partially conforms to Google C++ Style Guide.
 *
 *  @section Copyright
 *  Copyright 2013 Samsung R&D Institute Russia
 */

#include "src/transforms/mean.h"
#ifdef __AVX__
#include <simd/avx_mathfun.h>
#elif defined(__ARM_NEON__)
#include <simd/neon_mathfun.h>
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <boost/regex.hpp>
#pragma GCC diagnostic pop
#include <limits>
#include <simd/arithmetic-inl.h>

namespace SoundFeatureExtraction {
namespace Transforms {

using Formats::FixedArray;

const std::unordered_map<std::string, MeanTypes> Mean::kMeanTypesMap {
  { "arithmetic", MEAN_TYPE_ARITHMETIC },
  { "geometric", MEAN_TYPE_GEOMETRIC },
};
const std::set<MeanTypes> Mean::kDefaultMeanTypes { MEAN_TYPE_ARITHMETIC };
const std::string Mean::kDefaultMeanTypesStr = "arithmetic";

Mean::Mean() : types_(kDefaultMeanTypes) {
  RegisterSetter("types", [&](const std::string& value) {
    static const boost::regex allRegex("^\\s*(\\w+\\s*(\\s|$))+");
    boost::smatch match;
    if (!boost::regex_match(value, match, allRegex)) {
      return false;
    }
    static const boost::regex typesRegex("\\s*(\\w+)\\s*");
    static const boost::sregex_token_iterator empty;
    boost::sregex_token_iterator typesIterator(
          value.begin(), value.end(), typesRegex, 1);
    assert(typesIterator != empty);
    while (typesIterator != empty) {
      auto mtypeit = kMeanTypesMap.find(*typesIterator++);
      if (mtypeit == kMeanTypesMap.end()) {
        return false;
      }
      types_.insert(mtypeit->second);
    }
    return true;
  });
}

void Mean::Do(const float* in,
            FixedArray<MEAN_TYPE_COUNT>* out) const noexcept {
  for (int j = 0; j < MEAN_TYPE_COUNT; j++) {
    auto mt = static_cast<MeanTypes>(j);
    if (types_.find(mt) != types_.end()) {
      (*out)[j] = Do(UseSimd(), in, inputFormat_->Size(), mt);
    } else {
      (*out)[j] = 0;
    }
  }
}

float Mean::Do(bool simd, const float* input, size_t length,
               MeanTypes type) noexcept {
  int ilength = static_cast<int>(length);
  switch (type) {
    case MEAN_TYPE_ARITHMETIC: {
      float res;
      if (simd) {
#if defined(__AVX__) || defined(__ARM_NEON__)
        res = sum_elements(input, length);
      } else {
#else
      } {
#endif
        res = sum_elements_na(input, length);
      }
      res /= length;
      return res;
    }
    case MEAN_TYPE_GEOMETRIC: {
      const float power = 1.f / ilength;
      if (simd) {
#ifdef __AVX__
        __m256 res = _mm256_set1_ps(1.f), tmp = _mm256_set1_ps(1.f);
        const __m256 powvec = _mm256_set1_ps(power);
        const __m256 infvec = _mm256_set1_ps(
            std::numeric_limits<float>::infinity());
        for (int j = 0; j < ilength - 7; j += 8) {
          __m256 vec = _mm256_load_ps(input + j);
          __m256 mulvec = _mm256_mul_ps(tmp, vec);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
          __m256 cmpvec = _mm256_cmp_ps(mulvec, infvec, _CMP_EQ_UQ);
          // I do not know how to check fast if at least one element is inf;
          // Using 2 hadd and 2 comparisons
          cmpvec = _mm256_hadd_ps(cmpvec, cmpvec);
          cmpvec = _mm256_hadd_ps(cmpvec, cmpvec);
          if (ElementAt(cmpvec, 0) != 0 || ElementAt(cmpvec, 4) != 0) {
            // Taking a power from 0 can lead to unexpected results...
            // Apply the mask to workaround zeros in tmp
            cmpvec = _mm256_cmp_ps(tmp, _mm256_set1_ps(0.f), _CMP_EQ_UQ);
            tmp = pow256_ps(tmp, powvec);
            tmp = _mm256_blendv_ps(tmp, _mm256_set1_ps(0.f), cmpvec);
            res = _mm256_mul_ps(res, tmp);
            tmp = vec;
          } else {
            tmp = mulvec;
          }
#pragma GCC diagnostic pop
        }
        for (int i = 0; i < 8; i++) {
          if (ElementAt(tmp, i) == 0) {
            return 0;
          }
        }
        tmp = pow256_ps(tmp, powvec);
        res = _mm256_mul_ps(res, tmp);
        float sctmp = 1.f;
        for (int j = (ilength & ~0x7); j < ilength; j++) {
          sctmp *= input[j];
        }
        float scres = powf(sctmp, power);
        for (int j = 0; j < 8; j++) {
          scres *= ElementAt(res, j);
        }
        return scres;
      } else {
#elif defined(__ARM_NEON__)
        float32x4_t res = vdupq_n_f32(1.0f), tmp = vdupq_n_f32(1.0f),
            powvec = vdupq_n_f32(power),
            infvec = vdupq_n_f32(std::numeric_limits<float>::infinity());

        for (int j = 0; j < ilength - 7; j += 8) {
          float32x4_t vec = vld1q_f32(input + j);
          float32x4_t mulvec = vmulq_f32(tmp, vec);
          uint32x4_t cmpvec = vceqq_f32(mulvec, infvec);
          uint64x2_t cmpvec2 = vpaddlq_u32(cmpvec);
          if (vgetq_lane_u64(cmpvec2, 0) != 0 || vgetq_lane_u64(cmpvec2, 1) != 0) {
            tmp = pow_ps(tmp, powvec);
            res = vmulq_f32(res, tmp);
            tmp = vec;
          } else {
            tmp = mulvec;
          }
        }
        tmp = pow_ps(tmp, powvec);
        res = vmulq_f32(res, tmp);
        float sctmp = 1.f;
        for (int j = (ilength & ~0x3); j < ilength; j += 2) {
          sctmp *= input[j];
        }
        float scres = powf(sctmp, power);
        scres *= vgetq_lane_f32(res, 0) * vgetq_lane_f32(res, 1) *
            vgetq_lane_f32(res, 2) * vgetq_lane_f32(res, 3);
        return scres;
      } else {
#else
      } {
#endif
        float res = 1.f, tmp = 1.f;
        for (int j = 0; j < ilength; j++) {
          float val = input[j];
          float multmp = tmp * val;
          if (multmp == std::numeric_limits<float>::infinity()) {
            res *= powf(tmp, power);
            tmp = val;
          } else {
            tmp = multmp;
          }
        }
        if (tmp != 1.f) {
          res *= powf(tmp, power);
        }
        return res;
      }
    }
    default:
      break;
  }
  return 0.f;
}

REGISTER_TRANSFORM(Mean);

}  // namespace Transforms
}  // namespace SoundFeatureExtraction
<commit_msg>Improved Mean transform on AVX<commit_after>/*! @file mean.cc
 *  @brief Arithmetic and geometric means calculation.
 *  @author Markovtsev Vadim <v.markovtsev@samsung.com>
 *  @version 1.0
 *
 *  @section Notes
 *  This code partially conforms to Google C++ Style Guide.
 *
 *  @section Copyright
 *  Copyright 2013 Samsung R&D Institute Russia
 */

#include "src/transforms/mean.h"
#ifdef __AVX__
#include <simd/avx_mathfun.h>
#elif defined(__ARM_NEON__)
#include <simd/neon_mathfun.h>
#endif
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#include <boost/regex.hpp>
#pragma GCC diagnostic pop
#include <limits>
#include <simd/arithmetic-inl.h>

namespace SoundFeatureExtraction {
namespace Transforms {

using Formats::FixedArray;

const std::unordered_map<std::string, MeanTypes> Mean::kMeanTypesMap {
  { "arithmetic", MEAN_TYPE_ARITHMETIC },
  { "geometric", MEAN_TYPE_GEOMETRIC },
};
const std::set<MeanTypes> Mean::kDefaultMeanTypes { MEAN_TYPE_ARITHMETIC };
const std::string Mean::kDefaultMeanTypesStr = "arithmetic";

Mean::Mean() : types_(kDefaultMeanTypes) {
  RegisterSetter("types", [&](const std::string& value) {
    static const boost::regex allRegex("^\\s*(\\w+\\s*(\\s|$))+");
    boost::smatch match;
    if (!boost::regex_match(value, match, allRegex)) {
      return false;
    }
    static const boost::regex typesRegex("\\s*(\\w+)\\s*");
    static const boost::sregex_token_iterator empty;
    boost::sregex_token_iterator typesIterator(
          value.begin(), value.end(), typesRegex, 1);
    assert(typesIterator != empty);
    while (typesIterator != empty) {
      auto mtypeit = kMeanTypesMap.find(*typesIterator++);
      if (mtypeit == kMeanTypesMap.end()) {
        return false;
      }
      types_.insert(mtypeit->second);
    }
    return true;
  });
}

void Mean::Do(const float* in,
            FixedArray<MEAN_TYPE_COUNT>* out) const noexcept {
  for (int j = 0; j < MEAN_TYPE_COUNT; j++) {
    auto mt = static_cast<MeanTypes>(j);
    if (types_.find(mt) != types_.end()) {
      (*out)[j] = Do(UseSimd(), in, inputFormat_->Size(), mt);
    } else {
      (*out)[j] = 0;
    }
  }
}

float Mean::Do(bool simd, const float* input, size_t length,
               MeanTypes type) noexcept {
  int ilength = static_cast<int>(length);
  switch (type) {
    case MEAN_TYPE_ARITHMETIC: {
      float res;
      if (simd) {
#if defined(__AVX__) || defined(__ARM_NEON__)
        res = sum_elements(input, length);
      } else {
#else
      } {
#endif
        res = sum_elements_na(input, length);
      }
      res /= length;
      return res;
    }
    case MEAN_TYPE_GEOMETRIC: {
      const float power = 1.f / ilength;
      if (simd) {
#ifdef __AVX__
        __m256 res = _mm256_set1_ps(1.f), tmp = _mm256_set1_ps(1.f);
        const __m256 powvec = _mm256_set1_ps(power);
        const __m256 infvec = _mm256_set1_ps(
            std::numeric_limits<float>::infinity());
        for (int j = 0; j < ilength - 7; j += 8) {
          __m256 vec = _mm256_load_ps(input + j);
          __m256 mulvec = _mm256_mul_ps(tmp, vec);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
          __m256 cmpvec = _mm256_cmp_ps(mulvec, infvec, _CMP_EQ_UQ);
          int check = _mm256_movemask_ps(cmpvec);
          if (check != 0) {
            // Taking a power from 0 can lead to unexpected results...
            // Apply the mask to workaround zeros in tmp
            cmpvec = _mm256_cmp_ps(tmp, _mm256_set1_ps(0.f), _CMP_EQ_UQ);
            tmp = pow256_ps(tmp, powvec);
            tmp = _mm256_blendv_ps(tmp, _mm256_set1_ps(0.f), cmpvec);
            res = _mm256_mul_ps(res, tmp);
            tmp = vec;
          } else {
            tmp = mulvec;
          }
#pragma GCC diagnostic pop
        }
        for (int i = 0; i < 8; i++) {
          if (ElementAt(tmp, i) == 0) {
            return 0;
          }
        }
        tmp = pow256_ps(tmp, powvec);
        res = _mm256_mul_ps(res, tmp);
        float sctmp = 1.f;
        for (int j = (ilength & ~0x7); j < ilength; j++) {
          sctmp *= input[j];
        }
        float scres = powf(sctmp, power);
        for (int j = 0; j < 8; j++) {
          scres *= ElementAt(res, j);
        }
        return scres;
      } else {
#elif defined(__ARM_NEON__)
        float32x4_t res = vdupq_n_f32(1.0f), tmp = vdupq_n_f32(1.0f),
            powvec = vdupq_n_f32(power),
            infvec = vdupq_n_f32(std::numeric_limits<float>::infinity());

        for (int j = 0; j < ilength - 7; j += 8) {
          float32x4_t vec = vld1q_f32(input + j);
          float32x4_t mulvec = vmulq_f32(tmp, vec);
          uint32x4_t cmpvec = vceqq_f32(mulvec, infvec);
          uint64x2_t cmpvec2 = vpaddlq_u32(cmpvec);
          if (vgetq_lane_u64(cmpvec2, 0) != 0 || vgetq_lane_u64(cmpvec2, 1) != 0) {
            tmp = pow_ps(tmp, powvec);
            res = vmulq_f32(res, tmp);
            tmp = vec;
          } else {
            tmp = mulvec;
          }
        }
        tmp = pow_ps(tmp, powvec);
        res = vmulq_f32(res, tmp);
        float sctmp = 1.f;
        for (int j = (ilength & ~0x3); j < ilength; j += 2) {
          sctmp *= input[j];
        }
        float scres = powf(sctmp, power);
        scres *= vgetq_lane_f32(res, 0) * vgetq_lane_f32(res, 1) *
            vgetq_lane_f32(res, 2) * vgetq_lane_f32(res, 3);
        return scres;
      } else {
#else
      } {
#endif
        float res = 1.f, tmp = 1.f;
        for (int j = 0; j < ilength; j++) {
          float val = input[j];
          float multmp = tmp * val;
          if (multmp == std::numeric_limits<float>::infinity()) {
            res *= powf(tmp, power);
            tmp = val;
          } else {
            tmp = multmp;
          }
        }
        if (tmp != 1.f) {
          res *= powf(tmp, power);
        }
        return res;
      }
    }
    default:
      break;
  }
  return 0.f;
}

REGISTER_TRANSFORM(Mean);

}  // namespace Transforms
}  // namespace SoundFeatureExtraction
<|endoftext|>


<commit_before>/*
	This file is part of the CVD Library.

	Copyright (C) 2005 The Authors

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

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

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include "cvd/internal/io/tiff.h"
#include "cvd/image_io.h"
#include "cvd/config.h"
#include <tiffio.h>
#include <algorithm>
#include <vector>
#include <iostream>

using namespace CVD;
using namespace CVD::TIFF;
using namespace CVD::Exceptions::Image_IO;
using namespace std;

////////////////////////////////////////////////////////////////////////////////
//
// Private implementation of TIFF reading
//

class CVD::TIFF::TIFFPimpl
{
	public:
		TIFFPimpl(istream&);
		~TIFFPimpl();
		ImageRef size();
		string datatype();
		template<class C> void get_raw_pixel_line(C* data);
		void get_raw_pixel_line(bool* data);

	private:
		istream& i;
		unsigned long row;
		ImageRef my_size;
		string   type;
		streamoff length;
		::TIFF* tif;
		bool use_cooked_rgba_interface;
		bool inverted_grey;

		vector<uint32> raster_data;
		vector<uint8>  bool_rowbuf;

		static tsize_t write(thandle_t vis, tdata_t data, tsize_t count);
		static tsize_t read(thandle_t vis, tdata_t data, tsize_t count);
		static toff_t seek(thandle_t vis, toff_t off, int dir);
		static toff_t size(thandle_t vis);
		static int close(thandle_t vis);
		static int map(thandle_t, tdata_t*, toff_t*);
		static void unmap(thandle_t, tdata_t, toff_t);
};

tsize_t TIFFPimpl::read(thandle_t vis, tdata_t data, tsize_t count)
{
	TIFFPimpl* i = (TIFFPimpl*)vis;
	i->i.read((char*)data, count);
	return i->i.gcount();
}

//tsize_t tiff_in::write(thandle_t vis, tdata_t data, tsize_t count)
tsize_t TIFFPimpl::write(thandle_t, tdata_t, tsize_t)
{
	return 0;
}

toff_t TIFFPimpl::seek(thandle_t vis, toff_t off, int dir)
{
	TIFFPimpl* i = (TIFFPimpl*)vis;

	if(dir == SEEK_SET)
		i->i.seekg(off, ios_base::beg);
	else if(dir == SEEK_CUR)
		i->i.seekg(off, ios_base::cur);
	else if(dir == SEEK_END)
		i->i.seekg(off, ios_base::end);

	return i->i.tellg();
}

toff_t TIFFPimpl::size(thandle_t vis)
{
	TIFFPimpl* ii = (TIFFPimpl*)vis;
	return ii->length;
}

int TIFFPimpl::close(thandle_t)
{
	return 0;
}

int TIFFPimpl::map(thandle_t, tdata_t*, toff_t*)
{
	return 0;
}

void TIFFPimpl::unmap(thandle_t, tdata_t, toff_t)
{
}

static const int error_size=512;
static char error_msg[error_size]="";

static void tiff_error_handler(const char*, const char* fmt, va_list ap)
{
	int n = vsnprintf(error_msg, error_size, fmt, ap);
	if(n == error_size)
		error_msg[n-1] = 0;
}

//Compile-error free geryscale inverter.
//Does nothing for unknown types
template<class C> void invert(C* data, long num)
{
	for(long n=0; n< num; n++)
		data[n] =  Pixel::traits<C>::max_intensity - data[n];
}

void attempt_invert(...) {}
void attempt_invert(bool* data, long num) { invert(data, num);}
void attempt_invert(unsigned char* data, long num) { invert(data, num);}
void attempt_invert(unsigned short* data, long num) { invert(data, num);}
void attempt_invert(float* data, long num) { invert(data, num);}
void attempt_invert(double* data, long num) { invert(data, num);}

template<class T> void TIFFPimpl::get_raw_pixel_line(T* d)
{
	if(datatype() != PNM::type_name<T>::name())
		throw ReadTypeMismatch(datatype(), PNM::type_name<T>::name());

	if(row  > (unsigned long)my_size.y)
		throw InternalLibraryError("CVD", "Read past end of image.");

	if(use_cooked_rgba_interface)
	{
		uint32* raster = &raster_data[row*my_size.x];
		uint32* end = raster + my_size.x;

		//We will only ever get here if the type is Rgba
		Rgba<unsigned char>* data = reinterpret_cast<Rgba<unsigned char>* >(d);

		for(;raster < end; raster++, data++)
		{
			data->red   = TIFFGetR(*raster);
			data->green = TIFFGetG(*raster);
			data->blue  = TIFFGetB(*raster);
			data->alpha = TIFFGetA(*raster);
		}

		row ++;
	}
	else
	{
		if(TIFFReadScanline(tif, (void*)d, row) == -1)
			throw MalformedImage(error_msg);

		if(inverted_grey)
			attempt_invert(d, my_size.x);

		row++;
	}
}

void TIFFPimpl::get_raw_pixel_line(bool* d)
{
	if(datatype() != PNM::type_name<bool>::name())
		throw ReadTypeMismatch(datatype(), PNM::type_name<bool>::name());

	if(TIFFReadScanline(tif, (void*)&bool_rowbuf[0], row) == -1)
		throw MalformedImage(error_msg);

	//Unpack the bools
	for(int i=0; i < my_size.x  ;i++)
		d[i] = (bool_rowbuf[i/8] >> (7-i%8)) & 1;

	if(inverted_grey)
		invert(d, my_size.x);

	row++;
}

string TIFFPimpl::datatype()
{
	return type;
}

ImageRef TIFFPimpl::size()
{
	return my_size;
}

TIFFPimpl::~TIFFPimpl()
{
	TIFFClose(tif);
}

#ifdef CVD_INTERNAL_VERBOSE_TIFF
	#define LOG(X) do{ cerr << X; }while(0)
	#define VAR(X) #X << " = " << X
#else
	#define LOG(X)
	#define VAR(X)
#endif

TIFFPimpl::TIFFPimpl(istream& is)
:i(is),row(0)
{
	TIFFSetErrorHandler(tiff_error_handler);

	//Find out the file size, and the suitability of the stream
	i.seekg(0, ios_base::end);
	length = i.tellg();
	if(length == -1)
		throw UnseekableIstream("TIFF");
	i.seekg(0, ios_base::beg);

	tif = TIFFClientOpen("std::istream", "r", this,
						 read, write, seek, close, size, map, unmap);

	if(tif == NULL)
		throw MalformedImage(error_msg);

	//Libtiff types
	uint32 w=0, h=0;
	uint16 bitspersample=0, spp=0, sampleformat=0, photo=0, pl_type=0;

	TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
	TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
	TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);

	TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);

	TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photo);
	TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &pl_type);

	LOG(VAR(w) << endl);
	LOG(VAR(h) << endl);
	LOG(VAR(bitspersample) << endl);
	LOG(VAR(spp) << endl);
	LOG(VAR(photo) << endl);
	LOG(VAR(pl_type) << endl);

	//Read the sample format. If it is missing, then it
	//defaults to unsigned int as per the spec.
	if(TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat) == 0)
		sampleformat = SAMPLEFORMAT_UINT;

	LOG(VAR(sampleformat) << endl);

	my_size.x = w;
	my_size.y = h;
	use_cooked_rgba_interface=1; //This is the default
	inverted_grey=0;

	//Can we use our own interface?
	//The alternative is the cooked RGBA interface which assumes all
	//the world is 8 bits RGBA. This will load almost all images, but
	//special features, such as higher bit depths will be lost.
	//Also, the entire image has to be loaded at once, so this can use large
	//amounts of memory.
	if((photo == PHOTOMETRIC_RGB  || photo == PHOTOMETRIC_MINISWHITE || photo == PHOTOMETRIC_MINISBLACK))
	{
		//We stand a chane here...
		if(photo == PHOTOMETRIC_MINISWHITE)
			inverted_grey=1;

		//Figure out the basic datatype
		if(sampleformat == SAMPLEFORMAT_UINT)
		{
			if(bitspersample == 1)
				type = PNM::type_name<bool>::name();
			else if(bitspersample == 8)
				type = PNM::type_name<unsigned char>::name();
			else if(bitspersample == 16)
				type = PNM::type_name<unsigned short>::name();
			else
				goto keep_cooked;
		}
		else if(sampleformat == SAMPLEFORMAT_IEEEFP)
		{
			if(bitspersample == 32)
				type = PNM::type_name<float>::name();
			else if(bitspersample == 64)
				type = PNM::type_name<double>::name();
			else
				goto keep_cooked;
		}
		else
			goto keep_cooked;

		//Figure out the colourspace
		if(spp == 1)
			type = type;
		else if(spp == 2)
			type = "CVD::GreyAlpha<" + type  + ">";
		else if(spp == 3)
			type = "CVD::Rgb<" + type  + ">";
		else if(spp == 4)
			type = "CVD::Rgba<" + type  + ">";
		else
			goto keep_cooked;

		use_cooked_rgba_interface=0;
	}

	keep_cooked:;
	if(use_cooked_rgba_interface == 1)
	{
		//The format is "complex" and we don't know how to read it.
		type = "CVD::Rgba<unsigned char>";
		inverted_grey=0;
	}

	if(type == "bool")
		bool_rowbuf.resize((size().x + 7)/8);

	LOG(VAR(type) << endl);
	LOG(VAR(use_cooked_rgba_interface) << endl);

	if(use_cooked_rgba_interface)
	{
		raster_data.resize(my_size.x* my_size.y);

		#ifdef CVD_INTERNAL_HAVE_TIFF_ORIENTED
			//Read the whole image
			if(TIFFReadRGBAImageOriented(tif, my_size.x, my_size.y, &raster_data[0], 0, ORIENTATION_TOPLEFT) == -1)
				throw MalformedImage(error_msg);
		#else
			//Read the whole (upside-down) image
			if(TIFFReadRGBAImage(tif, my_size.x, my_size.y, &raster_data[0], 0) == -1)
				throw MalformedImage(error_msg);

			int xs = my_size.x, ys = my_size.y;

			//Flip the image, a row pair at a time
			vector<uint32> buffer(xs);
			for(int top=0, bot = ys-1; top < ys/2 && top != bot; top++,bot--)
			{
				uint32* bp, *tp;

				tp = &raster_data[0] + top * xs;
				bp = &raster_data[0] + bot * xs;

				memcpy(&buffer[0], tp, xs*sizeof(uint32));
				memcpy(tp, bp,  xs*sizeof(uint32));
				memcpy(bp, &buffer[0], xs*sizeof(uint32));
			}
		#endif
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// Implementation of public parts of TIFF reading
//

tiff_reader::tiff_reader(istream& i)
:t(new TIFFPimpl(i))
{}

tiff_reader::~tiff_reader()
{
}

string tiff_reader::datatype()
{
	return t->datatype();
}

string tiff_reader::name()
{
	return "TIFF";
}

ImageRef tiff_reader::size()
{
	return t->size();
};

//Mechanically generate the pixel reading calls.
#define GEN1(X) void tiff_reader::get_raw_pixel_line(X*d){t->get_raw_pixel_line(d);}
#define GEN3(X) GEN1(X) GEN1(Rgb<X>) GEN1(Rgba<X>)

GEN1(bool)
GEN3(unsigned char)
GEN3(unsigned short)
GEN3(float)
GEN3(double)
<commit_msg>Dump TIFF directory in verbose mode.<commit_after>/*
	This file is part of the CVD Library.

	Copyright (C) 2005 The Authors

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

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

	You should have received a copy of the GNU Lesser General Public
	License along with this library; if not, write to the Free Software
	Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
#include "cvd/internal/io/tiff.h"
#include "cvd/image_io.h"
#include "cvd/config.h"
#include <tiffio.h>
#include <algorithm>
#include <vector>
#include <iostream>

using namespace CVD;
using namespace CVD::TIFF;
using namespace CVD::Exceptions::Image_IO;
using namespace std;

////////////////////////////////////////////////////////////////////////////////
//
// Private implementation of TIFF reading
//

class CVD::TIFF::TIFFPimpl
{
	public:
		TIFFPimpl(istream&);
		~TIFFPimpl();
		ImageRef size();
		string datatype();
		template<class C> void get_raw_pixel_line(C* data);
		void get_raw_pixel_line(bool* data);

	private:
		istream& i;
		unsigned long row;
		ImageRef my_size;
		string   type;
		streamoff length;
		::TIFF* tif;
		bool use_cooked_rgba_interface;
		bool inverted_grey;

		vector<uint32> raster_data;
		vector<uint8>  bool_rowbuf;

		static tsize_t write(thandle_t vis, tdata_t data, tsize_t count);
		static tsize_t read(thandle_t vis, tdata_t data, tsize_t count);
		static toff_t seek(thandle_t vis, toff_t off, int dir);
		static toff_t size(thandle_t vis);
		static int close(thandle_t vis);
		static int map(thandle_t, tdata_t*, toff_t*);
		static void unmap(thandle_t, tdata_t, toff_t);
};

tsize_t TIFFPimpl::read(thandle_t vis, tdata_t data, tsize_t count)
{
	TIFFPimpl* i = (TIFFPimpl*)vis;
	i->i.read((char*)data, count);
	return i->i.gcount();
}

//tsize_t tiff_in::write(thandle_t vis, tdata_t data, tsize_t count)
tsize_t TIFFPimpl::write(thandle_t, tdata_t, tsize_t)
{
	return 0;
}

toff_t TIFFPimpl::seek(thandle_t vis, toff_t off, int dir)
{
	TIFFPimpl* i = (TIFFPimpl*)vis;

	if(dir == SEEK_SET)
		i->i.seekg(off, ios_base::beg);
	else if(dir == SEEK_CUR)
		i->i.seekg(off, ios_base::cur);
	else if(dir == SEEK_END)
		i->i.seekg(off, ios_base::end);

	return i->i.tellg();
}

toff_t TIFFPimpl::size(thandle_t vis)
{
	TIFFPimpl* ii = (TIFFPimpl*)vis;
	return ii->length;
}

int TIFFPimpl::close(thandle_t)
{
	return 0;
}

int TIFFPimpl::map(thandle_t, tdata_t*, toff_t*)
{
	return 0;
}

void TIFFPimpl::unmap(thandle_t, tdata_t, toff_t)
{
}

static const int error_size=512;
static char error_msg[error_size]="";

static void tiff_error_handler(const char*, const char* fmt, va_list ap)
{
	int n = vsnprintf(error_msg, error_size, fmt, ap);
	if(n == error_size)
		error_msg[n-1] = 0;
}

//Compile-error free geryscale inverter.
//Does nothing for unknown types
template<class C> void invert(C* data, long num)
{
	for(long n=0; n< num; n++)
		data[n] =  Pixel::traits<C>::max_intensity - data[n];
}

void attempt_invert(...) {}
void attempt_invert(bool* data, long num) { invert(data, num);}
void attempt_invert(unsigned char* data, long num) { invert(data, num);}
void attempt_invert(unsigned short* data, long num) { invert(data, num);}
void attempt_invert(float* data, long num) { invert(data, num);}
void attempt_invert(double* data, long num) { invert(data, num);}

template<class T> void TIFFPimpl::get_raw_pixel_line(T* d)
{
	if(datatype() != PNM::type_name<T>::name())
		throw ReadTypeMismatch(datatype(), PNM::type_name<T>::name());

	if(row  > (unsigned long)my_size.y)
		throw InternalLibraryError("CVD", "Read past end of image.");

	if(use_cooked_rgba_interface)
	{
		uint32* raster = &raster_data[row*my_size.x];
		uint32* end = raster + my_size.x;

		//We will only ever get here if the type is Rgba
		Rgba<unsigned char>* data = reinterpret_cast<Rgba<unsigned char>* >(d);

		for(;raster < end; raster++, data++)
		{
			data->red   = TIFFGetR(*raster);
			data->green = TIFFGetG(*raster);
			data->blue  = TIFFGetB(*raster);
			data->alpha = TIFFGetA(*raster);
		}

		row ++;
	}
	else
	{
		if(TIFFReadScanline(tif, (void*)d, row) == -1)
			throw MalformedImage(error_msg);

		if(inverted_grey)
			attempt_invert(d, my_size.x);

		row++;
	}
}

void TIFFPimpl::get_raw_pixel_line(bool* d)
{
	if(datatype() != PNM::type_name<bool>::name())
		throw ReadTypeMismatch(datatype(), PNM::type_name<bool>::name());

	if(TIFFReadScanline(tif, (void*)&bool_rowbuf[0], row) == -1)
		throw MalformedImage(error_msg);

	//Unpack the bools
	for(int i=0; i < my_size.x  ;i++)
		d[i] = (bool_rowbuf[i/8] >> (7-i%8)) & 1;

	if(inverted_grey)
		invert(d, my_size.x);

	row++;
}

string TIFFPimpl::datatype()
{
	return type;
}

ImageRef TIFFPimpl::size()
{
	return my_size;
}

TIFFPimpl::~TIFFPimpl()
{
	TIFFClose(tif);
}

//#define CVD_INTERNAL_VERBOSE_TIFF
#ifdef CVD_INTERNAL_VERBOSE_TIFF
	#define LOG(X) do{ cerr << X; }while(0)
	#define VAR(X) #X << " = " << X
#else
	#define LOG(X)
	#define VAR(X)
#endif

TIFFPimpl::TIFFPimpl(istream& is)
:i(is),row(0)
{
	TIFFSetErrorHandler(tiff_error_handler);

	//Find out the file size, and the suitability of the stream
	i.seekg(0, ios_base::end);
	length = i.tellg();
	if(length == -1)
		throw UnseekableIstream("TIFF");
	i.seekg(0, ios_base::beg);

	tif = TIFFClientOpen("std::istream", "r", this,
						 read, write, seek, close, size, map, unmap);

	if(tif == NULL)
		throw MalformedImage(error_msg);

	#ifdef CVD_INTERNAL_VERBOSE_TIFF
	{
		int dircount=1;
		for(; TIFFReadDirectory(tif); dircount++)
		{}

		LOG(VAR(dircount));
		TIFFSetDirectory(tif, 0);

	}
	#endif

	//Libtiff types
	uint32 w=0, h=0;
	uint16 bitspersample=0, spp=0, sampleformat=0, photo=0, pl_type=0;

	TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
	TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
	TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);

	TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &spp);

	TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photo);
	TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &pl_type);

	LOG(VAR(w) << endl);
	LOG(VAR(h) << endl);
	LOG(VAR(bitspersample) << endl);
	LOG(VAR(spp) << endl);
	LOG(VAR(photo) << endl);
	LOG(VAR(pl_type) << endl);

	//Read the sample format. If it is missing, then it
	//defaults to unsigned int as per the spec.
	if(TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat) == 0)
		sampleformat = SAMPLEFORMAT_UINT;

	LOG(VAR(sampleformat) << endl);

	my_size.x = w;
	my_size.y = h;
	use_cooked_rgba_interface=1; //This is the default
	inverted_grey=0;

	//Can we use our own interface?
	//The alternative is the cooked RGBA interface which assumes all
	//the world is 8 bits RGBA. This will load almost all images, but
	//special features, such as higher bit depths will be lost.
	//Also, the entire image has to be loaded at once, so this can use large
	//amounts of memory.
	if((photo == PHOTOMETRIC_RGB  || photo == PHOTOMETRIC_MINISWHITE || photo == PHOTOMETRIC_MINISBLACK))
	{
		//We stand a chane here...
		if(photo == PHOTOMETRIC_MINISWHITE)
			inverted_grey=1;

		//Figure out the basic datatype
		if(sampleformat == SAMPLEFORMAT_UINT)
		{
			if(bitspersample == 1)
				type = PNM::type_name<bool>::name();
			else if(bitspersample == 8)
				type = PNM::type_name<unsigned char>::name();
			else if(bitspersample == 16)
				type = PNM::type_name<unsigned short>::name();
			else
				goto keep_cooked;
		}
		else if(sampleformat == SAMPLEFORMAT_IEEEFP)
		{
			if(bitspersample == 32)
				type = PNM::type_name<float>::name();
			else if(bitspersample == 64)
				type = PNM::type_name<double>::name();
			else
				goto keep_cooked;
		}
		else
			goto keep_cooked;

		//Figure out the colourspace
		if(spp == 1)
			type = type;
		else if(spp == 2)
			type = "CVD::GreyAlpha<" + type  + ">";
		else if(spp == 3)
			type = "CVD::Rgb<" + type  + ">";
		else if(spp == 4)
			type = "CVD::Rgba<" + type  + ">";
		else
			goto keep_cooked;

		use_cooked_rgba_interface=0;
	}

	keep_cooked:;
	if(use_cooked_rgba_interface == 1)
	{
		//The format is "complex" and we don't know how to read it.
		type = "CVD::Rgba<unsigned char>";
		inverted_grey=0;
	}

	if(type == "bool")
		bool_rowbuf.resize((size().x + 7)/8);

	LOG(VAR(type) << endl);
	LOG(VAR(use_cooked_rgba_interface) << endl);

	if(use_cooked_rgba_interface)
	{
		raster_data.resize(my_size.x* my_size.y);

		#ifdef CVD_INTERNAL_HAVE_TIFF_ORIENTED
			//Read the whole image
			if(TIFFReadRGBAImageOriented(tif, my_size.x, my_size.y, &raster_data[0], 0, ORIENTATION_TOPLEFT) == -1)
				throw MalformedImage(error_msg);
		#else
			//Read the whole (upside-down) image
			if(TIFFReadRGBAImage(tif, my_size.x, my_size.y, &raster_data[0], 0) == -1)
				throw MalformedImage(error_msg);

			int xs = my_size.x, ys = my_size.y;

			//Flip the image, a row pair at a time
			vector<uint32> buffer(xs);
			for(int top=0, bot = ys-1; top < ys/2 && top != bot; top++,bot--)
			{
				uint32* bp, *tp;

				tp = &raster_data[0] + top * xs;
				bp = &raster_data[0] + bot * xs;

				memcpy(&buffer[0], tp, xs*sizeof(uint32));
				memcpy(tp, bp,  xs*sizeof(uint32));
				memcpy(bp, &buffer[0], xs*sizeof(uint32));
			}
		#endif
	}
}

////////////////////////////////////////////////////////////////////////////////
//
// Implementation of public parts of TIFF reading
//

tiff_reader::tiff_reader(istream& i)
:t(new TIFFPimpl(i))
{}

tiff_reader::~tiff_reader()
{
}

string tiff_reader::datatype()
{
	return t->datatype();
}

string tiff_reader::name()
{
	return "TIFF";
}

ImageRef tiff_reader::size()
{
	return t->size();
};

//Mechanically generate the pixel reading calls.
#define GEN1(X) void tiff_reader::get_raw_pixel_line(X*d){t->get_raw_pixel_line(d);}
#define GEN3(X) GEN1(X) GEN1(Rgb<X>) GEN1(Rgba<X>)

GEN1(bool)
GEN3(unsigned char)
GEN3(unsigned short)
GEN3(float)
GEN3(double)
<|endoftext|>


<commit_before>#include "library/common/engine.h"

#include "common/common/lock_guard.h"

namespace Envoy {

Engine::Engine(envoy_engine_callbacks callbacks, const char* config, const char* log_level,
               std::atomic<envoy_network_t>& preferred_network)
    : callbacks_(callbacks) {
  // Ensure static factory registration occurs on time.
  // TODO: ensure this is only called one time once multiple Engine objects can be allocated.
  // https://github.com/lyft/envoy-mobile/issues/332
  ExtensionRegistry::registerFactories();

  // Create the Http::Dispatcher first since it contains initial queueing logic.
  // TODO: consider centralizing initial queueing in this class.
  // https://github.com/lyft/envoy-mobile/issues/720
  http_dispatcher_ = std::make_unique<Http::Dispatcher>(preferred_network);

  // Start the Envoy on a dedicated thread.
  main_thread_ = std::thread(&Engine::run, this, std::string(config), std::string(log_level));
}

envoy_status_t Engine::run(const std::string config, const std::string log_level) {
  {
    Thread::LockGuard lock(mutex_);
    try {
      const std::string name = "envoy";
      const std::string config_flag = "--config-yaml";
      const std::string log_flag = "-l";
      const char* envoy_argv[] = {name.c_str(),     config_flag.c_str(), config.c_str(),
                                  log_flag.c_str(), log_level.c_str(),   nullptr};

      main_common_ = std::make_unique<MobileMainCommon>(5, envoy_argv);
      event_dispatcher_ = &main_common_->server()->dispatcher();
      cv_.notifyAll();
    } catch (const Envoy::NoServingException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    } catch (const Envoy::MalformedArgvException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    } catch (const Envoy::EnvoyException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    }

    // Note: We're waiting longer than we might otherwise to drain to the main thread's dispatcher.
    // This is because we're not simply waiting for its availability and for it to have started, but
    // also because we're waiting for clusters to have done their first attempt at DNS resolution.
    // When we improve synchronous failure handling and/or move to dynamic forwarding, we only need
    // to wait until the dispatcher is running (and can drain by enqueueing a drain callback on it,
    // as we did previously).
    postinit_callback_handler_ = main_common_->server()->lifecycleNotifier().registerCallback(
        Envoy::Server::ServerLifecycleNotifier::Stage::PostInit, [this]() -> void {
          server_ = TS_UNCHECKED_READ(main_common_)->server();
          client_scope_ = server_->serverFactoryContext().scope().createScope("client.");
          auto api_listener = server_->listenerManager().apiListener()->get().http();
          ASSERT(api_listener.has_value());
          http_dispatcher_->ready(server_->dispatcher(), server_->serverFactoryContext().scope(),
                                  api_listener.value());
          if (callbacks_.on_engine_running != nullptr) {
            callbacks_.on_engine_running(callbacks_.context);
          }
        });
  } // mutex_

  // The main run loop must run without holding the mutex, so that the destructor can acquire it.
  bool run_success = TS_UNCHECKED_READ(main_common_)->run();
  // The above call is blocking; at this point the event loop has exited.

  // Ensure destructors run on Envoy's main thread.
  postinit_callback_handler_.reset(nullptr);
  client_scope_.reset(nullptr);
  TS_UNCHECKED_READ(main_common_).reset(nullptr);

  callbacks_.on_exit(callbacks_.context);

  return run_success ? ENVOY_SUCCESS : ENVOY_FAILURE;
}

Engine::~Engine() {
  // If we're already on the main thread, it should be safe to simply destruct.
  if (!main_thread_.joinable()) {
    return;
  }

  // If we're not on the main thread, we need to be sure that MainCommon is finished being
  // constructed so we can dispatch shutdown.
  {
    Thread::LockGuard lock(mutex_);

    if (!main_common_) {
      cv_.wait(mutex_);
    }

    ASSERT(main_common_);

    // Exit the event loop and finish up in Engine::run(...)
    event_dispatcher_->exit();
  } // _mutex

  // Now we wait for the main thread to wrap things up.
  main_thread_.join();
}

envoy_status_t Engine::recordCounter(const std::string& elements, uint64_t count) {
  if (server_ && client_scope_) {
    server_->dispatcher().post([this, elements, count]() -> void {
      Stats::Utility::counterFromElements(*client_scope_, {Stats::DynamicName(elements)})
          .add(count);
    });
    return ENVOY_SUCCESS;
  }
  return ENVOY_FAILURE;
}

envoy_status_t Engine::recordGaugeSet(const std::string& elements, uint64_t value) {
  if (server_ && client_scope_) {
    server_->dispatcher().post([this, elements, value]() -> void {
      Stats::Utility::gaugeFromElements(*client_scope_, {Stats::DynamicName(elements)},
                                        Stats::Gauge::ImportMode::NeverImport)
          .set(value);
    });
    return ENVOY_SUCCESS;
  }
  return ENVOY_FAILURE;
}

envoy_status_t Engine::recordGaugeAdd(const std::string& elements, uint64_t amount) {
  if (server_ && client_scope_) {
    server_->dispatcher().post([this, elements, amount]() -> void {
      Stats::Utility::gaugeFromElements(*client_scope_, {Stats::DynamicName(elements)},
                                        Stats::Gauge::ImportMode::NeverImport)
          .add(amount);
    });
    return ENVOY_SUCCESS;
  }
  return ENVOY_FAILURE;
}

envoy_status_t Engine::recordGaugeSub(const std::string& elements, uint64_t amount) {
  if (server_ && client_scope_) {
    server_->dispatcher().post([this, elements, amount]() -> void {
      Stats::Utility::gaugeFromElements(*client_scope_, {Stats::DynamicName(elements)},
                                        Stats::Gauge::ImportMode::NeverImport)
          .sub(amount);
    });
    return ENVOY_SUCCESS;
  }
  return ENVOY_FAILURE;
}

Http::Dispatcher& Engine::httpDispatcher() { return *http_dispatcher_; }

} // namespace Envoy
<commit_msg>stats: sanitize incoming stat names (#1119)<commit_after>#include "library/common/engine.h"

#include "common/common/lock_guard.h"

namespace Envoy {

Engine::Engine(envoy_engine_callbacks callbacks, const char* config, const char* log_level,
               std::atomic<envoy_network_t>& preferred_network)
    : callbacks_(callbacks) {
  // Ensure static factory registration occurs on time.
  // TODO: ensure this is only called one time once multiple Engine objects can be allocated.
  // https://github.com/lyft/envoy-mobile/issues/332
  ExtensionRegistry::registerFactories();

  // Create the Http::Dispatcher first since it contains initial queueing logic.
  // TODO: consider centralizing initial queueing in this class.
  // https://github.com/lyft/envoy-mobile/issues/720
  http_dispatcher_ = std::make_unique<Http::Dispatcher>(preferred_network);

  // Start the Envoy on a dedicated thread.
  main_thread_ = std::thread(&Engine::run, this, std::string(config), std::string(log_level));
}

envoy_status_t Engine::run(const std::string config, const std::string log_level) {
  {
    Thread::LockGuard lock(mutex_);
    try {
      const std::string name = "envoy";
      const std::string config_flag = "--config-yaml";
      const std::string log_flag = "-l";
      const char* envoy_argv[] = {name.c_str(),     config_flag.c_str(), config.c_str(),
                                  log_flag.c_str(), log_level.c_str(),   nullptr};

      main_common_ = std::make_unique<MobileMainCommon>(5, envoy_argv);
      event_dispatcher_ = &main_common_->server()->dispatcher();
      cv_.notifyAll();
    } catch (const Envoy::NoServingException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    } catch (const Envoy::MalformedArgvException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    } catch (const Envoy::EnvoyException& e) {
      std::cerr << e.what() << std::endl;
      return ENVOY_FAILURE;
    }

    // Note: We're waiting longer than we might otherwise to drain to the main thread's dispatcher.
    // This is because we're not simply waiting for its availability and for it to have started, but
    // also because we're waiting for clusters to have done their first attempt at DNS resolution.
    // When we improve synchronous failure handling and/or move to dynamic forwarding, we only need
    // to wait until the dispatcher is running (and can drain by enqueueing a drain callback on it,
    // as we did previously).
    postinit_callback_handler_ = main_common_->server()->lifecycleNotifier().registerCallback(
        Envoy::Server::ServerLifecycleNotifier::Stage::PostInit, [this]() -> void {
          server_ = TS_UNCHECKED_READ(main_common_)->server();
          client_scope_ = server_->serverFactoryContext().scope().createScope("client.");
          auto api_listener = server_->listenerManager().apiListener()->get().http();
          ASSERT(api_listener.has_value());
          http_dispatcher_->ready(server_->dispatcher(), server_->serverFactoryContext().scope(),
                                  api_listener.value());
          if (callbacks_.on_engine_running != nullptr) {
            callbacks_.on_engine_running(callbacks_.context);
          }
        });
  } // mutex_

  // The main run loop must run without holding the mutex, so that the destructor can acquire it.
  bool run_success = TS_UNCHECKED_READ(main_common_)->run();
  // The above call is blocking; at this point the event loop has exited.

  // Ensure destructors run on Envoy's main thread.
  postinit_callback_handler_.reset(nullptr);
  client_scope_.reset(nullptr);
  TS_UNCHECKED_READ(main_common_).reset(nullptr);

  callbacks_.on_exit(callbacks_.context);

  return run_success ? ENVOY_SUCCESS : ENVOY_FAILURE;
}

Engine::~Engine() {
  // If we're already on the main thread, it should be safe to simply destruct.
  if (!main_thread_.joinable()) {
    return;
  }

  // If we're not on the main thread, we need to be sure that MainCommon is finished being
  // constructed so we can dispatch shutdown.
  {
    Thread::LockGuard lock(mutex_);

    if (!main_common_) {
      cv_.wait(mutex_);
    }

    ASSERT(main_common_);

    // Exit the event loop and finish up in Engine::run(...)
    event_dispatcher_->exit();
  } // _mutex

  // Now we wait for the main thread to wrap things up.
  main_thread_.join();
}

envoy_status_t Engine::recordCounter(const std::string& elements, uint64_t count) {
  if (server_ && client_scope_) {
    server_->dispatcher().post([this, elements, count]() -> void {
      Stats::DynamicName name{Stats::Utility::sanitizeStatsName(elements)};
      Stats::Utility::counterFromElements(*client_scope_, {name}).add(count);
    });
    return ENVOY_SUCCESS;
  }
  return ENVOY_FAILURE;
}

envoy_status_t Engine::recordGaugeSet(const std::string& elements, uint64_t value) {
  if (server_ && client_scope_) {
    server_->dispatcher().post([this, elements, value]() -> void {
      Stats::DynamicName name{Stats::Utility::sanitizeStatsName(elements)};
      Stats::Utility::gaugeFromElements(*client_scope_, {name},
                                        Stats::Gauge::ImportMode::NeverImport)
          .set(value);
    });
    return ENVOY_SUCCESS;
  }
  return ENVOY_FAILURE;
}

envoy_status_t Engine::recordGaugeAdd(const std::string& elements, uint64_t amount) {
  if (server_ && client_scope_) {
    server_->dispatcher().post([this, elements, amount]() -> void {
      Stats::DynamicName name{Stats::Utility::sanitizeStatsName(elements)};
      Stats::Utility::gaugeFromElements(*client_scope_, {name},
                                        Stats::Gauge::ImportMode::NeverImport)
          .add(amount);
    });
    return ENVOY_SUCCESS;
  }
  return ENVOY_FAILURE;
}

envoy_status_t Engine::recordGaugeSub(const std::string& elements, uint64_t amount) {
  if (server_ && client_scope_) {
    server_->dispatcher().post([this, elements, amount]() -> void {
      Stats::DynamicName name{Stats::Utility::sanitizeStatsName(elements)};
      Stats::Utility::gaugeFromElements(*client_scope_, {name},
                                        Stats::Gauge::ImportMode::NeverImport)
          .sub(amount);
    });
    return ENVOY_SUCCESS;
  }
  return ENVOY_FAILURE;
}

Http::Dispatcher& Engine::httpDispatcher() { return *http_dispatcher_; }

} // namespace Envoy
<|endoftext|>


<commit_before>#include <cassert>
#include <iostream>
#include <regex>
#include <unordered_map>
#include "util.h"
#include "core.h"
#include "parser.h"
#include "interp.h"

#ifdef HAVE_SDL2

#include <SDL.h>

#endif

HostFn::HostFn(std::string name, size_t numParams, void* fptr)
: name(name),
  numParams(numParams),
  fptr(fptr)
{
}

Value HostFn::call0()
{
    assert (fptr);
    assert (numParams == 0);
    auto f0 = (Value(*)()) fptr;
    return f0();
}

Value HostFn::call1(Value arg0)
{
    assert (fptr);
    assert (numParams == 1);
    auto f1 = (Value(*)(Value)) fptr;
    return f1(arg0);
}

Value HostFn::call2(Value arg0, Value arg1)
{
    assert (fptr);
    assert (numParams == 2);
    auto f2 = (Value(*)(Value,Value)) fptr;
    return f2(arg0, arg1);
}

Value HostFn::call3(Value arg0, Value arg1, Value arg2)
{
    assert (fptr);
    assert (numParams == 3);
    auto f3 = (Value(*)(Value,Value,Value)) fptr;
    return f3(arg0, arg1, arg2);
}

void setHostFn(
    Object pkgObj,
    std::string name,
    size_t numParams,
    void* fptr
)
{
    auto fnObj = new HostFn(name, numParams, fptr);

    auto fnVal = Value((refptr)fnObj, TAG_HOSTFN);

    assert (!pkgObj.hasField(name));

    pkgObj.setField(name, fnVal);
}

//============================================================================
// core/io package
//============================================================================

Value print_int32(Value val)
{
    assert (val.isInt32());
    std::cout << (int32_t)val;
    return Value::UNDEF;
}

Value print_float32(Value val)
{
    assert (val.isFloat32());
    std::cout << (float)val;
    return Value::UNDEF;
}

Value print_str(Value val)
{
    assert (val.isString());
    std::cout << (std::string)val;
    return Value::UNDEF;
}

Value read_file(Value fileName)
{
    assert (fileName.isString());
    auto nameStr = (std::string)fileName;

    std::cout << "reading file: " << nameStr << std::endl;

    FILE* file = fopen(nameStr.c_str(), "r");

    if (!file)
    {
        printf("failed to open file\n");
        return Value::FALSE;
    }

    // Get the file size in bytes
    fseek(file, 0, SEEK_END);
    size_t len = ftell(file);
    fseek(file, 0, SEEK_SET);

    char* buf = (char*)malloc(len+1);

    // Read into the allocated buffer
    int read = fread(buf, 1, len, file);

    if (read != len)
    {
        printf("failed to read file");
        return Value::FALSE;
    }

    // Add a null terminator to the string
    buf[len] = '\0';

    // Close the input file
    fclose(file);

    return String(buf);
}

Value get_core_io_pkg()
{
    auto exports = Object::newObject(32);
    setHostFn(exports, "print_int32", 1, (void*)print_int32);
    setHostFn(exports, "print_float32", 1, (void*)print_float32);
    setHostFn(exports, "print_str"  , 1, (void*)print_str);
    setHostFn(exports, "read_file"  , 1, (void*)read_file);
    return exports;
}

//============================================================================
// core/window package
//============================================================================

#ifdef HAVE_SDL2

size_t width = 0;
size_t height = 0;

std::vector<uint8_t> pixelBuffer;

SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
SDL_Texture* texture = nullptr;

Value create_window(
    Value titleVal,
    Value widthVal,
    Value heightVal
)
{
    SDL_Init(SDL_INIT_VIDEO);

    auto title = (std::string)titleVal;
    width = (size_t)(int32_t)widthVal;
    height = (size_t)(int32_t)heightVal;

    window = SDL_CreateWindow(
        title.c_str(),
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        width,
        height,
        0
    );

    renderer = SDL_CreateRenderer(window, -1, 0);

    texture = SDL_CreateTexture(
        renderer,
        SDL_PIXELFORMAT_RGBA8888,
        SDL_TEXTUREACCESS_STATIC,
        width,
        height
    );

    pixelBuffer.resize(width * height * 4, 0);

    SDL_ShowWindow(window);

    return Value::UNDEF;
}

Value destroy_window()
{
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    pixelBuffer.clear();

    return Value::UNDEF;
}

Value process_events()
{
    // FIXME
    // How do we know when quit happened? Return false then?
    // Maybe change go let users do the polling, write our own loop?

    SDL_Event event;

    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
            //quit = true;
            return Value::FALSE;
            break;

            default:
            break;
        }
    }

    return Value::TRUE;
}

Value draw_pixels(Value pixelsArray)
{
    auto pixels = (Array)pixelsArray;

    assert (pixels.length() == width * height * 3);

    for (size_t pixIdx = 0; pixIdx < width * height; ++pixIdx)
    {
        // SDL's RGBA888 is actually ABGR, because of course.
        pixelBuffer[4*pixIdx+0] = 255;
        pixelBuffer[4*pixIdx+1] = (uint8_t)(int32_t)pixels.getElem(3*pixIdx+2);
        pixelBuffer[4*pixIdx+2] = (uint8_t)(int32_t)pixels.getElem(3*pixIdx+1);
        pixelBuffer[4*pixIdx+3] = (uint8_t)(int32_t)pixels.getElem(3*pixIdx+0);
    }

    SDL_UpdateTexture(texture, NULL, &pixelBuffer[0], width * 4);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    SDL_RenderCopy(renderer, texture, NULL, NULL);
    SDL_RenderPresent(renderer);
    return Value::UNDEF;
}

#endif // HAVE_SDL2

Value get_core_window_pkg()
{
#ifdef HAVE_SDL2
    auto exports = Object::newObject(32);
    setHostFn(exports, "create_window"  , 3, (void*)create_window);
    setHostFn(exports, "destroy_window" , 0, (void*)destroy_window);
    setHostFn(exports, "process_events" , 0, (void*)process_events);
    setHostFn(exports, "draw_pixels"    , 1, (void*)draw_pixels);
    return exports;
#else
    return Value::UNDEF;
#endif
}

//============================================================================
// core/audio package
//============================================================================

#ifdef HAVE_SDL2

bool paused = true;

Value open_output_device(
    Value num_channels
)
{
    assert(num_channels.isInt32());

    SDL_Init(SDL_INIT_AUDIO);
    SDL_AudioSpec want, have;

    SDL_zero(want);
    want.freq = 44100;
    want.format = AUDIO_F32;
    want.channels = (uint8_t)(int32_t)num_channels;
    want.samples = 4096;
    want.callback = NULL;

    auto devId = (int32_t)SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);

    assert (want.format == have.format);
    assert (want.freq == have.freq);
    assert (want.channels = have.channels);

    return Value::int32(devId);
}

Value queue_samples(
    Value dev,
    Value samplesArray
)
{
    assert(dev.isInt32());
    assert(samplesArray.isArray());
    auto devID = (int32_t)dev;

    if (paused)
    {
        paused = false;
        SDL_PauseAudioDevice(devID, 0);
    }

    auto samples = (Array)samplesArray;

    if (samples.length() == 0)
        return Value::UNDEF;

    float samples_buf[samples.length()];

    for (int i = 0; i < samples.length(); i++)
    {
        auto elem = samples.getElem(i);

        if (!elem.isFloat32())
        {
            throw RunError("audio samples must be float32");
        }

        float sample = (float)elem > 1.0f ? 1.0f : elem;
        sample = sample < -1.0f ? -1.0f : sample;
        samples_buf[i] = sample;
    }

    int error = SDL_QueueAudio(devID, samples_buf, sizeof(samples_buf));

    return Value::UNDEF;

}

Value get_queue_size(
    Value dev
)
{
    assert(dev.isInt32());

    auto devID = (int32_t)dev;

    auto numBytes = SDL_GetQueuedAudioSize(devID);
    auto numSamples = numBytes / 4;

    return Value::int32((int32_t)numSamples);
}

Value close_output_device(
    Value dev
)
{
    assert(dev.isInt32());

    auto devID = (int32_t)dev;
    SDL_CloseAudioDevice(devID);

    return Value::UNDEF;
}

#endif // HAVE_SDL2

Value get_core_audio_pkg()
{
#ifdef HAVE_SDL2
    auto exports = Object::newObject(32);
    setHostFn(exports, "open_output_device" , 1, (void*)open_output_device);
    setHostFn(exports, "close_output_device", 1, (void*)close_output_device);
    setHostFn(exports, "queue_samples"      , 2, (void*)queue_samples);
    setHostFn(exports, "get_queue_size"     , 1, (void*)get_queue_size);
    return exports;
#else
    return Value::UNDEF;
#endif
}

//============================================================================

// Cache of loaded packages
std::unordered_map<std::string, Value> pkgCache;

/// Load a package based on its path
Object load(std::string pkgPath)
{
    Input input(pkgPath);

    Value exportVal;

    // Parse the language directive
    auto langPkgName = parseLang(input);

    // If a language package is specified
    if (langPkgName != "")
    {
        //std::cout << "Loading language package" << std::endl;

        auto langPkgVal = import(langPkgName);

        if (!langPkgVal.isObject())
        {
            throw RunError("failed to load parser package");
        }

        auto langPkg = Object(langPkgVal);

        if (!langPkg.hasField("parse_input"))
        {
            throw RunError(
                "parser packages must export a parse_input function"
            );
        }

        // Create an object to pass the input data
        auto inputObj = Object::newObject();
        inputObj.setField("src_name", String(input.getSrcName()));
        inputObj.setField("src_string", String(input.getInputStr()));
        inputObj.setField("str_idx", Value::int32(input.getInputIdx()));
        inputObj.setField("line_no", Value::int32(input.getLineNo()));
        inputObj.setField("col_no", Value::int32(input.getColNo()));

        //std::cout << "Calling parse_input" << std::endl;

        // Call the parse_input method exported by the parser package
        ValueVec args;
        args.push_back(inputObj);
        exportVal = callExportFn(langPkg, "parse_input", args);

        //std::cout << "Returned from parse_input" << std::endl;
    }
    else
    {
        // Parse the package file contents
        exportVal = parseInput(input);
    }

    if (!exportVal.isObject())
    {
        throw RunError("exports value is not an object");
    }

    auto pkg = Object(exportVal);

    return pkg;
}

std::string findPkgPath(std::string pkgName)
{
    // If the package name directly maps to a relative path
    if (fileExists(pkgName))
        return pkgName;

    // Look in the package directory
    auto pkgPath = PKGS_DIR + pkgName + "/package";
    if (fileExists(pkgPath))
        return pkgPath;

    // Not found
    return "";
}

Value getCorePkg(std::string pkgName)
{
    // Internal/core packages
    if (pkgName == "core/io")
        return get_core_io_pkg();
    if (pkgName == "core/window")
        return get_core_window_pkg();
    if (pkgName == "core/audio")
        return get_core_audio_pkg();

    return Value::UNDEF;
}

/// Import a package based on its name, and perform caching
Value import(std::string pkgName)
{
    // Package names may only contain lowercase identifiers
    // separated by single forward slashes
    std::regex ex("([a-z0-9]+/)*[a-z0-9]+.?[a-z0-9]+");
    if(!regex_match(pkgName, ex))
    {
        std::cout << "invalid package name: \"" << pkgName << "\"" << std::endl;
        return Value::FALSE;
    }

    // If the package is already loaded
    auto itr = pkgCache.find(pkgName);
    if (itr != pkgCache.end())
    {
        return itr->second;
    }

    // If we can find a package file for this name
    auto pkgPath = findPkgPath(pkgName);
    if (pkgPath != "")
    {
        // Load the package file
        auto pkg = load(pkgPath);

        // Cache the package
        pkgCache[pkgName] = pkg;

        // Initialize the package
        if (pkg.hasField("init"))
        {
            callExportFn(pkg, "init");
        }

        return pkg;
    }

    // If we can find a core package for this name
    auto corePkg = getCorePkg(pkgName);
    if (corePkg != Value::UNDEF)
    {
        pkgCache[pkgName] = corePkg;
        return corePkg;
    }

    // Package not found
    return Value::UNDEF;
}
<commit_msg>Added assertion on package names<commit_after>#include <cassert>
#include <iostream>
#include <regex>
#include <unordered_map>
#include "util.h"
#include "core.h"
#include "parser.h"
#include "interp.h"

#ifdef HAVE_SDL2

#include <SDL.h>

#endif

HostFn::HostFn(std::string name, size_t numParams, void* fptr)
: name(name),
  numParams(numParams),
  fptr(fptr)
{
}

Value HostFn::call0()
{
    assert (fptr);
    assert (numParams == 0);
    auto f0 = (Value(*)()) fptr;
    return f0();
}

Value HostFn::call1(Value arg0)
{
    assert (fptr);
    assert (numParams == 1);
    auto f1 = (Value(*)(Value)) fptr;
    return f1(arg0);
}

Value HostFn::call2(Value arg0, Value arg1)
{
    assert (fptr);
    assert (numParams == 2);
    auto f2 = (Value(*)(Value,Value)) fptr;
    return f2(arg0, arg1);
}

Value HostFn::call3(Value arg0, Value arg1, Value arg2)
{
    assert (fptr);
    assert (numParams == 3);
    auto f3 = (Value(*)(Value,Value,Value)) fptr;
    return f3(arg0, arg1, arg2);
}

void setHostFn(
    Object pkgObj,
    std::string name,
    size_t numParams,
    void* fptr
)
{
    auto fnObj = new HostFn(name, numParams, fptr);

    auto fnVal = Value((refptr)fnObj, TAG_HOSTFN);

    assert (!pkgObj.hasField(name));

    pkgObj.setField(name, fnVal);
}

//============================================================================
// core/io package
//============================================================================

Value print_int32(Value val)
{
    assert (val.isInt32());
    std::cout << (int32_t)val;
    return Value::UNDEF;
}

Value print_float32(Value val)
{
    assert (val.isFloat32());
    std::cout << (float)val;
    return Value::UNDEF;
}

Value print_str(Value val)
{
    assert (val.isString());
    std::cout << (std::string)val;
    return Value::UNDEF;
}

Value read_file(Value fileName)
{
    assert (fileName.isString());
    auto nameStr = (std::string)fileName;

    std::cout << "reading file: " << nameStr << std::endl;

    FILE* file = fopen(nameStr.c_str(), "r");

    if (!file)
    {
        printf("failed to open file\n");
        return Value::FALSE;
    }

    // Get the file size in bytes
    fseek(file, 0, SEEK_END);
    size_t len = ftell(file);
    fseek(file, 0, SEEK_SET);

    char* buf = (char*)malloc(len+1);

    // Read into the allocated buffer
    int read = fread(buf, 1, len, file);

    if (read != len)
    {
        printf("failed to read file");
        return Value::FALSE;
    }

    // Add a null terminator to the string
    buf[len] = '\0';

    // Close the input file
    fclose(file);

    return String(buf);
}

Value get_core_io_pkg()
{
    auto exports = Object::newObject(32);
    setHostFn(exports, "print_int32", 1, (void*)print_int32);
    setHostFn(exports, "print_float32", 1, (void*)print_float32);
    setHostFn(exports, "print_str"  , 1, (void*)print_str);
    setHostFn(exports, "read_file"  , 1, (void*)read_file);
    return exports;
}

//============================================================================
// core/window package
//============================================================================

#ifdef HAVE_SDL2

size_t width = 0;
size_t height = 0;

std::vector<uint8_t> pixelBuffer;

SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
SDL_Texture* texture = nullptr;

Value create_window(
    Value titleVal,
    Value widthVal,
    Value heightVal
)
{
    SDL_Init(SDL_INIT_VIDEO);

    auto title = (std::string)titleVal;
    width = (size_t)(int32_t)widthVal;
    height = (size_t)(int32_t)heightVal;

    window = SDL_CreateWindow(
        title.c_str(),
        SDL_WINDOWPOS_UNDEFINED,
        SDL_WINDOWPOS_UNDEFINED,
        width,
        height,
        0
    );

    renderer = SDL_CreateRenderer(window, -1, 0);

    texture = SDL_CreateTexture(
        renderer,
        SDL_PIXELFORMAT_RGBA8888,
        SDL_TEXTUREACCESS_STATIC,
        width,
        height
    );

    pixelBuffer.resize(width * height * 4, 0);

    SDL_ShowWindow(window);

    return Value::UNDEF;
}

Value destroy_window()
{
    SDL_DestroyTexture(texture);
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    SDL_Quit();

    pixelBuffer.clear();

    return Value::UNDEF;
}

Value process_events()
{
    // FIXME
    // How do we know when quit happened? Return false then?
    // Maybe change go let users do the polling, write our own loop?

    SDL_Event event;

    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
            //quit = true;
            return Value::FALSE;
            break;

            default:
            break;
        }
    }

    return Value::TRUE;
}

Value draw_pixels(Value pixelsArray)
{
    auto pixels = (Array)pixelsArray;

    assert (pixels.length() == width * height * 3);

    for (size_t pixIdx = 0; pixIdx < width * height; ++pixIdx)
    {
        // SDL's RGBA888 is actually ABGR, because of course.
        pixelBuffer[4*pixIdx+0] = 255;
        pixelBuffer[4*pixIdx+1] = (uint8_t)(int32_t)pixels.getElem(3*pixIdx+2);
        pixelBuffer[4*pixIdx+2] = (uint8_t)(int32_t)pixels.getElem(3*pixIdx+1);
        pixelBuffer[4*pixIdx+3] = (uint8_t)(int32_t)pixels.getElem(3*pixIdx+0);
    }

    SDL_UpdateTexture(texture, NULL, &pixelBuffer[0], width * 4);
    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
    SDL_RenderClear(renderer);
    SDL_RenderCopy(renderer, texture, NULL, NULL);
    SDL_RenderPresent(renderer);
    return Value::UNDEF;
}

#endif // HAVE_SDL2

Value get_core_window_pkg()
{
#ifdef HAVE_SDL2
    auto exports = Object::newObject(32);
    setHostFn(exports, "create_window"  , 3, (void*)create_window);
    setHostFn(exports, "destroy_window" , 0, (void*)destroy_window);
    setHostFn(exports, "process_events" , 0, (void*)process_events);
    setHostFn(exports, "draw_pixels"    , 1, (void*)draw_pixels);
    return exports;
#else
    return Value::UNDEF;
#endif
}

//============================================================================
// core/audio package
//============================================================================

#ifdef HAVE_SDL2

bool paused = true;

Value open_output_device(
    Value num_channels
)
{
    assert(num_channels.isInt32());

    SDL_Init(SDL_INIT_AUDIO);
    SDL_AudioSpec want, have;

    SDL_zero(want);
    want.freq = 44100;
    want.format = AUDIO_F32;
    want.channels = (uint8_t)(int32_t)num_channels;
    want.samples = 4096;
    want.callback = NULL;

    auto devId = (int32_t)SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_ANY_CHANGE);

    assert (want.format == have.format);
    assert (want.freq == have.freq);
    assert (want.channels = have.channels);

    return Value::int32(devId);
}

Value queue_samples(
    Value dev,
    Value samplesArray
)
{
    assert(dev.isInt32());
    assert(samplesArray.isArray());
    auto devID = (int32_t)dev;

    if (paused)
    {
        paused = false;
        SDL_PauseAudioDevice(devID, 0);
    }

    auto samples = (Array)samplesArray;

    if (samples.length() == 0)
        return Value::UNDEF;

    float samples_buf[samples.length()];

    for (int i = 0; i < samples.length(); i++)
    {
        auto elem = samples.getElem(i);

        if (!elem.isFloat32())
        {
            throw RunError("audio samples must be float32");
        }

        float sample = (float)elem > 1.0f ? 1.0f : elem;
        sample = sample < -1.0f ? -1.0f : sample;
        samples_buf[i] = sample;
    }

    int error = SDL_QueueAudio(devID, samples_buf, sizeof(samples_buf));

    return Value::UNDEF;

}

Value get_queue_size(
    Value dev
)
{
    assert(dev.isInt32());

    auto devID = (int32_t)dev;

    auto numBytes = SDL_GetQueuedAudioSize(devID);
    auto numSamples = numBytes / 4;

    return Value::int32((int32_t)numSamples);
}

Value close_output_device(
    Value dev
)
{
    assert(dev.isInt32());

    auto devID = (int32_t)dev;
    SDL_CloseAudioDevice(devID);

    return Value::UNDEF;
}

#endif // HAVE_SDL2

Value get_core_audio_pkg()
{
#ifdef HAVE_SDL2
    auto exports = Object::newObject(32);
    setHostFn(exports, "open_output_device" , 1, (void*)open_output_device);
    setHostFn(exports, "close_output_device", 1, (void*)close_output_device);
    setHostFn(exports, "queue_samples"      , 2, (void*)queue_samples);
    setHostFn(exports, "get_queue_size"     , 1, (void*)get_queue_size);
    return exports;
#else
    return Value::UNDEF;
#endif
}

//============================================================================

// Cache of loaded packages
std::unordered_map<std::string, Value> pkgCache;

/// Load a package based on its path
Object load(std::string pkgPath)
{
    Input input(pkgPath);

    Value exportVal;

    // Parse the language directive
    auto langPkgName = parseLang(input);

    // If a language package is specified
    if (langPkgName != "")
    {
        //std::cout << "Loading language package" << std::endl;

        auto langPkgVal = import(langPkgName);

        if (!langPkgVal.isObject())
        {
            throw RunError("failed to load parser package");
        }

        auto langPkg = Object(langPkgVal);

        if (!langPkg.hasField("parse_input"))
        {
            throw RunError(
                "parser packages must export a parse_input function"
            );
        }

        // Create an object to pass the input data
        auto inputObj = Object::newObject();
        inputObj.setField("src_name", String(input.getSrcName()));
        inputObj.setField("src_string", String(input.getInputStr()));
        inputObj.setField("str_idx", Value::int32(input.getInputIdx()));
        inputObj.setField("line_no", Value::int32(input.getLineNo()));
        inputObj.setField("col_no", Value::int32(input.getColNo()));

        //std::cout << "Calling parse_input" << std::endl;

        // Call the parse_input method exported by the parser package
        ValueVec args;
        args.push_back(inputObj);
        exportVal = callExportFn(langPkg, "parse_input", args);

        //std::cout << "Returned from parse_input" << std::endl;
    }
    else
    {
        // Parse the package file contents
        exportVal = parseInput(input);
    }

    if (!exportVal.isObject())
    {
        throw RunError("exports value is not an object");
    }

    auto pkg = Object(exportVal);

    return pkg;
}

std::string findPkgPath(std::string pkgName)
{
    // If the package name directly maps to a relative path
    if (fileExists(pkgName))
    {
        return pkgName;
    }

    // Look in the package directory
    auto pkgPath = PKGS_DIR + pkgName + "/package";
    if (fileExists(pkgPath))
    {
        assert (
            regex_match(pkgName, std::regex("([a-z0-9]+/)*[0-9]+")) &&
            "package found in package directory without version number"
        );

        return pkgPath;
    }

    // Not found
    return "";
}

Value getCorePkg(std::string pkgName)
{
    // Internal/core packages
    if (pkgName == "core/io")
        return get_core_io_pkg();
    if (pkgName == "core/window")
        return get_core_window_pkg();
    if (pkgName == "core/audio")
        return get_core_audio_pkg();

    return Value::UNDEF;
}

/// Import a package based on its name, and perform caching
Value import(std::string pkgName)
{
    // Package names may only contain lowercase identifiers
    // separated by single forward slashes. A file extension may be present.
    std::regex ex("([a-z0-9]+/)*[a-z0-9]+.?[a-z0-9]+");
    if(!regex_match(pkgName, ex))
    {
        std::cout << "invalid package name: \"" << pkgName << "\"" << std::endl;
        return Value::FALSE;
    }

    // If the package is already loaded
    auto itr = pkgCache.find(pkgName);
    if (itr != pkgCache.end())
    {
        return itr->second;
    }

    // If we can find a package file for this name
    auto pkgPath = findPkgPath(pkgName);
    if (pkgPath != "")
    {
        // Load the package file
        auto pkg = load(pkgPath);

        // Cache the package
        pkgCache[pkgName] = pkg;

        // Initialize the package
        if (pkg.hasField("init"))
        {
            callExportFn(pkg, "init");
        }

        return pkg;
    }

    // If we can find a core package for this name
    auto corePkg = getCorePkg(pkgName);
    if (corePkg != Value::UNDEF)
    {
        pkgCache[pkgName] = corePkg;
        return corePkg;
    }

    // Package not found
    return Value::UNDEF;
}
<|endoftext|>