<commit_before>/**************************************************************************
** This file is part of LiteIDE
**
** Copyright (c) 2011-2016 LiteIDE Team. All rights reserved.
**
** 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.
**
** In addition, as a special exception,  that plugins developed for LiteIDE,
** are allowed to remain closed sourced and can be distributed under any license .
** These rights are included in the file LGPL_EXCEPTION.txt in this package.
**
**************************************************************************/
// Module: fakevimedit.cpp
// Creator: jsuppe <jon.suppe@gmail.com>

#include "fakevim/fakevim/fakevimhandler.h"
#include "fakevim/fakevim/fakevimactions.h"
#include "fakevimedit.h"
#include "fakevimedit_global.h"
#include "qtc_editutil/uncommentselection.h"
#include "litebuildapi/litebuildapi.h"
#include "fileutil/fileutil.h"
#include <QMenu>
#include <QToolBar>
#include <QAction>
#include <QTextStream>
#include <QApplication>
#include <QToolTip>
#include <QLabel>
#include <QStatusBar>
#include "liteeditorapi/liteeditorapi.h"

using namespace FakeVim::Internal;

//lite_memory_check_begin
#if defined(WIN32) && defined(_MSC_VER) &&  defined(_DEBUG)
     #define _CRTDBG_MAP_ALLOC
     #include <stdlib.h>
     #include <crtdbg.h>
     #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
     #define new DEBUG_NEW
#endif
//lite_memory_check_end

FakeVimEdit::FakeVimEdit(LiteApi::IApplication *app, QObject *parent) :
    QObject(parent),
    m_liteApp(app),
    m_enableUseFakeVim(false),
    m_commandLabel(0)
{
    connect(m_liteApp->editorManager(),SIGNAL(editorCreated(LiteApi::IEditor*)),this,SLOT(editorCreated(LiteApi::IEditor*)));
    connect(m_liteApp->editorManager(),SIGNAL(currentEditorChanged(LiteApi::IEditor*)),this,SLOT(currentEditorChanged(LiteApi::IEditor*)));
    connect(m_liteApp->optionManager(),SIGNAL(applyOption(QString)),this,SLOT(applyOption(QString)));

    this->applyOption(OPTION_FAKEVIMEDIT);

    m_enableUseFakeVim = m_liteApp->settings()->value(FAKEVIMEDIT_USEFAKEVIM,false).toBool();

    m_enableUseFakeVimAct = new QAction(tr("Use FakeVim Editing"),this);
    m_enableUseFakeVimAct->setCheckable(true);
    m_enableUseFakeVimAct->setChecked(m_enableUseFakeVim);

    connect(m_enableUseFakeVimAct,SIGNAL(toggled(bool)),this,SLOT(toggledEnableUseFakeVim(bool)));

    if (m_enableUseFakeVim) {
        _enableFakeVim();
    }
}

void FakeVimEdit::toggledEnableUseFakeVim(bool b)
{
    m_enableUseFakeVim = b;
    m_liteApp->settings()->setValue(FAKEVIMEDIT_USEFAKEVIM,b);
    if(m_enableUseFakeVim){
        _enableFakeVim();
    }else{
        _disableFakeVim();
    }
}

void FakeVimEdit::applyOption(const QString &option)
{
    if (option != OPTION_FAKEVIMEDIT) {
        return;
    }
    m_initCommandList = m_liteApp->settings()->value(FAKEVIMEDIT_INITCOMMANDS,initCommandList()).toStringList();
}

void FakeVimEdit::_enableFakeVim(){
    LiteApi::IEditor *editor = m_liteApp->editorManager()->currentEditor();
    _addCommandLabel();
    _addFakeVimToEditor(editor);
}

void FakeVimEdit::_disableFakeVim(){
    LiteApi::IEditor *editor = m_liteApp->editorManager()->currentEditor();
    _removeFakeVimFromEditor(editor);
    _removeCommandLabel();
}

QFont FakeVimEdit::commandLabelFont(){
    QFont font;
    font.setStyleHint(QFont::Monospace);
    font.setBold(true);
    return font;
}

void FakeVimEdit::_addCommandLabel(){
    QFont font = commandLabelFont();

    _removeCommandLabel();
    m_commandLabel = new QLabel(m_liteApp->mainWindow());
    m_commandLabel->setFont(font);
    m_liteApp->mainWindow()->statusBar()->addPermanentWidget(m_commandLabel);
}

void FakeVimEdit::_removeCommandLabel(){
    if(!m_commandLabel){
        return;
    }
    m_liteApp->mainWindow()->statusBar()->removeWidget(m_commandLabel);
    delete m_commandLabel;
    m_commandLabel = NULL;
}

void FakeVimEdit::_removeFakeVimFromEditor(LiteApi::IEditor *editor){
    LiteApi::ILiteEditor  *ed = LiteApi::getLiteEditor(editor);

    if (!ed) {
        return;
    }

    QPlainTextEdit *ped = LiteApi::getPlainTextEdit(ed);

    if(!ped){
        return;
    }

    if(FakeVimHandler *fakeVimHandler = m_editorMap.value(ped)){
        delete fakeVimHandler;
        m_editorMap.remove(ped);
    }
}

void FakeVimEdit::_addFakeVimToEditor(LiteApi::IEditor *editor){
    LiteApi::ILiteEditor  *ed = LiteApi::getLiteEditor(editor);

    if (!ed) {
        return;
    }

    QPlainTextEdit *ped = LiteApi::getPlainTextEdit(ed);

    if(!ped){
        return;
    }

    if(m_editorMap.contains(ped)){
        return;
    }

    FakeVimHandler *fakeVimHandler;

    fakeVimHandler = new FakeVimHandler(ped,0);

    connect(fakeVimHandler, SIGNAL(handleExCommandRequested(bool*,ExCommand)),
            this, SLOT(handleExCommandRequested(bool*,ExCommand)));
    connect(fakeVimHandler, SIGNAL(commandBufferChanged(QString,int,int,int,QObject*)),
            this, SLOT(showMessage(QString,int)));
    connect(fakeVimHandler, SIGNAL(moveToMatchingParenthesis(bool *, bool *, QTextCursor *)),
            this, SLOT(moveToMatchingParenthesis(bool *, bool *,QTextCursor *)));

    //init command list
    {
        foreach(QString cmd, m_initCommandList) {
            if (cmd.startsWith("#")) {
                continue;
            }
            fakeVimHandler->handleCommand(cmd);
        }
        fakeVimHandler->handleInput("<esc>");
    }

    fakeVimHandler->setCurrentFileName(ed->filePath());
    fakeVimHandler->installEventFilter();
    fakeVimHandler->setupWidget();

    connect(ped, SIGNAL(destroyed(QObject*)), this, SLOT(plainTextEditDestroyed(QObject*)));

    m_editorMap[ped] = fakeVimHandler;
}

void FakeVimEdit::plainTextEditDestroyed(QObject *obj)
{
    m_editorMap.remove(obj);
}

void FakeVimEdit::handleExCommandRequested(bool *b, ExCommand c)
{
    // Save
    if(c.cmd == "w" ){
        m_liteApp->editorManager()->saveEditor(m_editor);
        *b = true;
    }

    // Save & Close
    if(c.cmd == "x"){
        m_liteApp->editorManager()->saveEditor(m_editor);
        m_liteApp->editorManager()->closeEditor(m_editor);
        *b = true;
    }

    // Close
    if(c.cmd == "q"){
        if(c.hasBang){
            m_editor->reload();
        }
        m_liteApp->editorManager()->closeEditor(m_editor);
        *b = true;
    }
}

void FakeVimEdit::moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor)
{
    LiteApi::IEditor *editor = m_editor;
    LiteApi::IActionContext *actionContext = m_liteApp->actionManager()->getActionContext(editor,"Editor");
    LiteApi::ActionInfo *info = actionContext->actionInfo("GotoMatchBrace");

    info->action->trigger();

    int oldPos = cursor->position();
    int newPos = this->m_editor->textCursor().position();
    cursor->setPosition(newPos);

    if(oldPos <= newPos){
        *forward = true;
    }else{
        *forward = false;
    }
    if(oldPos == newPos){
        *moved = false;
    }else{
        *moved = true;
    }
}

void FakeVimEdit::editorCreated(LiteApi::IEditor *editor)
{
    if (!editor) {
        return;
    }

    QMenu *menu = LiteApi::getEditMenu(editor);
    if (menu) {
        menu->addSeparator();
        menu->addAction(m_enableUseFakeVimAct);
    }

    if (!m_enableUseFakeVim){
        return;
    }

    m_editor = LiteApi::getLiteEditor(editor);
    if (m_editor) {
        m_plainTextEdit = LiteApi::getPlainTextEdit(editor);
    }else{
        return;
    }

    if(!m_enableUseFakeVim)
        return;

    _addFakeVimToEditor(editor);
}

void FakeVimEdit::currentEditorChanged(LiteApi::IEditor *editor)
{
    if (!editor) {
        return;
    }
    m_editor = LiteApi::getLiteEditor(editor);
    QPlainTextEdit *ped = LiteApi::getPlainTextEdit(editor);

    if (m_enableUseFakeVim){
        if(m_editorMap.contains(ped))
            return;
        else
            _addFakeVimToEditor(editor);
    }else{
        _removeFakeVimFromEditor(editor);
    }
}

void FakeVimEdit::showMessage(QString contents, int cursorPos)
{
    if(!m_commandLabel){
        return;
    }
    QString m_statusMessage = cursorPos == -1 ? contents
        : contents.left(cursorPos) + QChar(10073) + contents.mid(cursorPos);

    int slack = 14 - m_statusMessage.size();
    QString msg = m_statusMessage + QString(slack, QLatin1Char(' '));

    m_commandLabel->setText(msg);
}
<commit_msg>fakevim remove restore editor tab setup<commit_after>/**************************************************************************
** This file is part of LiteIDE
**
** Copyright (c) 2011-2016 LiteIDE Team. All rights reserved.
**
** 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.
**
** In addition, as a special exception,  that plugins developed for LiteIDE,
** are allowed to remain closed sourced and can be distributed under any license .
** These rights are included in the file LGPL_EXCEPTION.txt in this package.
**
**************************************************************************/
// Module: fakevimedit.cpp
// Creator: jsuppe <jon.suppe@gmail.com>

#include "fakevim/fakevim/fakevimhandler.h"
#include "fakevim/fakevim/fakevimactions.h"
#include "fakevimedit.h"
#include "fakevimedit_global.h"
#include "qtc_editutil/uncommentselection.h"
#include "litebuildapi/litebuildapi.h"
#include "fileutil/fileutil.h"
#include <QMenu>
#include <QToolBar>
#include <QAction>
#include <QTextStream>
#include <QApplication>
#include <QToolTip>
#include <QLabel>
#include <QStatusBar>
#include "liteeditorapi/liteeditorapi.h"
#include "../liteeditor/liteeditor_global.h"

using namespace FakeVim::Internal;

//lite_memory_check_begin
#if defined(WIN32) && defined(_MSC_VER) &&  defined(_DEBUG)
     #define _CRTDBG_MAP_ALLOC
     #include <stdlib.h>
     #include <crtdbg.h>
     #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
     #define new DEBUG_NEW
#endif
//lite_memory_check_end

FakeVimEdit::FakeVimEdit(LiteApi::IApplication *app, QObject *parent) :
    QObject(parent),
    m_liteApp(app),
    m_enableUseFakeVim(false),
    m_commandLabel(0)
{
    connect(m_liteApp->editorManager(),SIGNAL(editorCreated(LiteApi::IEditor*)),this,SLOT(editorCreated(LiteApi::IEditor*)));
    connect(m_liteApp->editorManager(),SIGNAL(currentEditorChanged(LiteApi::IEditor*)),this,SLOT(currentEditorChanged(LiteApi::IEditor*)));
    connect(m_liteApp->optionManager(),SIGNAL(applyOption(QString)),this,SLOT(applyOption(QString)));

    this->applyOption(OPTION_FAKEVIMEDIT);

    m_enableUseFakeVim = m_liteApp->settings()->value(FAKEVIMEDIT_USEFAKEVIM,false).toBool();

    m_enableUseFakeVimAct = new QAction(tr("Use FakeVim Editing"),this);
    m_enableUseFakeVimAct->setCheckable(true);
    m_enableUseFakeVimAct->setChecked(m_enableUseFakeVim);

    connect(m_enableUseFakeVimAct,SIGNAL(toggled(bool)),this,SLOT(toggledEnableUseFakeVim(bool)));

    if (m_enableUseFakeVim) {
        _enableFakeVim();
    }
}

void FakeVimEdit::toggledEnableUseFakeVim(bool b)
{
    m_enableUseFakeVim = b;
    m_liteApp->settings()->setValue(FAKEVIMEDIT_USEFAKEVIM,b);
    if(m_enableUseFakeVim){
        _enableFakeVim();
    }else{
        _disableFakeVim();
    }
}

void FakeVimEdit::applyOption(const QString &option)
{
    if (option != OPTION_FAKEVIMEDIT) {
        return;
    }
    m_initCommandList = m_liteApp->settings()->value(FAKEVIMEDIT_INITCOMMANDS,initCommandList()).toStringList();
}

void FakeVimEdit::_enableFakeVim(){
    LiteApi::IEditor *editor = m_liteApp->editorManager()->currentEditor();
    _addCommandLabel();
    _addFakeVimToEditor(editor);
}

void FakeVimEdit::_disableFakeVim(){
    LiteApi::IEditor *editor = m_liteApp->editorManager()->currentEditor();
    _removeFakeVimFromEditor(editor);
    _removeCommandLabel();
}

QFont FakeVimEdit::commandLabelFont(){
    QFont font;
    font.setStyleHint(QFont::Monospace);
    font.setBold(true);
    return font;
}

void FakeVimEdit::_addCommandLabel(){
    QFont font = commandLabelFont();

    _removeCommandLabel();
    m_commandLabel = new QLabel(m_liteApp->mainWindow());
    m_commandLabel->setFont(font);
    m_liteApp->mainWindow()->statusBar()->addPermanentWidget(m_commandLabel);
}

void FakeVimEdit::_removeCommandLabel(){
    if(!m_commandLabel){
        return;
    }
    m_liteApp->mainWindow()->statusBar()->removeWidget(m_commandLabel);
    delete m_commandLabel;
    m_commandLabel = NULL;
}

void FakeVimEdit::_removeFakeVimFromEditor(LiteApi::IEditor *editor){
    LiteApi::ILiteEditor  *ed = LiteApi::getLiteEditor(editor);

    if (!ed) {
        return;
    }
    QString mime = editor->mimeType();
    int tabWidth = m_liteApp->settings()->value(EDITOR_TABWIDTH+mime,4).toInt();
    bool useSpace = m_liteApp->settings()->value(EDITOR_TABTOSPACES+mime,false).toBool();
    ed->setTabOption(tabWidth,useSpace);

    QPlainTextEdit *ped = LiteApi::getPlainTextEdit(ed);

    if(!ped){
        return;
    }

    if(FakeVimHandler *fakeVimHandler = m_editorMap.value(ped)){
        delete fakeVimHandler;
        m_editorMap.remove(ped);
    }
}

void FakeVimEdit::_addFakeVimToEditor(LiteApi::IEditor *editor){
    LiteApi::ILiteEditor  *ed = LiteApi::getLiteEditor(editor);

    if (!ed) {
        return;
    }

    QPlainTextEdit *ped = LiteApi::getPlainTextEdit(ed);

    if(!ped){
        return;
    }

    if(m_editorMap.contains(ped)){
        return;
    }

    FakeVimHandler *fakeVimHandler;

    fakeVimHandler = new FakeVimHandler(ped,0);

    connect(fakeVimHandler, SIGNAL(handleExCommandRequested(bool*,ExCommand)),
            this, SLOT(handleExCommandRequested(bool*,ExCommand)));
    connect(fakeVimHandler, SIGNAL(commandBufferChanged(QString,int,int,int,QObject*)),
            this, SLOT(showMessage(QString,int)));
    connect(fakeVimHandler, SIGNAL(moveToMatchingParenthesis(bool *, bool *, QTextCursor *)),
            this, SLOT(moveToMatchingParenthesis(bool *, bool *,QTextCursor *)));

    //init command list
    {
        foreach(QString cmd, m_initCommandList) {
            if (cmd.startsWith("#")) {
                continue;
            }
            fakeVimHandler->handleCommand(cmd);
        }
        fakeVimHandler->handleInput("<esc>");
    }

    fakeVimHandler->setCurrentFileName(ed->filePath());
    fakeVimHandler->installEventFilter();
    fakeVimHandler->setupWidget();

    connect(ped, SIGNAL(destroyed(QObject*)), this, SLOT(plainTextEditDestroyed(QObject*)));

    m_editorMap[ped] = fakeVimHandler;
}

void FakeVimEdit::plainTextEditDestroyed(QObject *obj)
{
    m_editorMap.remove(obj);
}

void FakeVimEdit::handleExCommandRequested(bool *b, ExCommand c)
{
    // Save
    if(c.cmd == "w" ){
        m_liteApp->editorManager()->saveEditor(m_editor);
        *b = true;
    }

    // Save & Close
    if(c.cmd == "x"){
        m_liteApp->editorManager()->saveEditor(m_editor);
        m_liteApp->editorManager()->closeEditor(m_editor);
        *b = true;
    }

    // Close
    if(c.cmd == "q"){
        if(c.hasBang){
            m_editor->reload();
        }
        m_liteApp->editorManager()->closeEditor(m_editor);
        *b = true;
    }
}

void FakeVimEdit::moveToMatchingParenthesis(bool *moved, bool *forward, QTextCursor *cursor)
{
    LiteApi::IEditor *editor = m_editor;
    LiteApi::IActionContext *actionContext = m_liteApp->actionManager()->getActionContext(editor,"Editor");
    LiteApi::ActionInfo *info = actionContext->actionInfo("GotoMatchBrace");

    info->action->trigger();

    int oldPos = cursor->position();
    int newPos = this->m_editor->textCursor().position();
    cursor->setPosition(newPos);

    if(oldPos <= newPos){
        *forward = true;
    }else{
        *forward = false;
    }
    if(oldPos == newPos){
        *moved = false;
    }else{
        *moved = true;
    }
}

void FakeVimEdit::editorCreated(LiteApi::IEditor *editor)
{
    if (!editor) {
        return;
    }

    QMenu *menu = LiteApi::getEditMenu(editor);
    if (menu) {
        menu->addSeparator();
        menu->addAction(m_enableUseFakeVimAct);
    }

    if (!m_enableUseFakeVim){
        return;
    }

    m_editor = LiteApi::getLiteEditor(editor);
    if (m_editor) {
        m_plainTextEdit = LiteApi::getPlainTextEdit(editor);
    }else{
        return;
    }

    if(!m_enableUseFakeVim)
        return;

    _addFakeVimToEditor(editor);
}

void FakeVimEdit::currentEditorChanged(LiteApi::IEditor *editor)
{
    if (!editor) {
        return;
    }
    m_editor = LiteApi::getLiteEditor(editor);
    QPlainTextEdit *ped = LiteApi::getPlainTextEdit(editor);

    if (m_enableUseFakeVim){
        if(m_editorMap.contains(ped))
            return;
        else
            _addFakeVimToEditor(editor);
    }else{
        _removeFakeVimFromEditor(editor);
    }
}

void FakeVimEdit::showMessage(QString contents, int cursorPos)
{
    if(!m_commandLabel){
        return;
    }
    QString m_statusMessage = cursorPos == -1 ? contents
        : contents.left(cursorPos) + QChar(10073) + contents.mid(cursorPos);

    int slack = 14 - m_statusMessage.size();
    QString msg = m_statusMessage + QString(slack, QLatin1Char(' '));

    m_commandLabel->setText(msg);
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "OpenwireAdvisorysTest.h"

#include <activemq/core/ActiveMQConnectionFactory.h>
#include <activemq/core/ActiveMQConnection.h>
#include <activemq/core/ActiveMQSession.h>
#include <activemq/commands/Message.h>
#include <activemq/commands/ConnectionInfo.h>
#include <activemq/exceptions/ActiveMQException.h>

#include <decaf/lang/exceptions/ClassCastException.h>
#include <decaf/lang/Pointer.h>
#include <decaf/lang/Thread.h>
#include <decaf/util/UUID.h>

#include <cms/ConnectionFactory.h>
#include <cms/Connection.h>
#include <cms/Session.h>
#include <cms/MessageConsumer.h>
#include <cms/MessageProducer.h>
#include <cms/MessageListener.h>
#include <cms/ConnectionFactory.h>
#include <cms/Connection.h>
#include <cms/Message.h>
#include <cms/TextMessage.h>

#include <memory>

using namespace cms;
using namespace std;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
using namespace decaf::util;
using namespace activemq;
using namespace activemq::core;
using namespace activemq::commands;
using namespace activemq::exceptions;
using namespace activemq::test;
using namespace activemq::test::openwire;

////////////////////////////////////////////////////////////////////////////////
OpenwireAdvisorysTest::OpenwireAdvisorysTest() {
}

////////////////////////////////////////////////////////////////////////////////
OpenwireAdvisorysTest::~OpenwireAdvisorysTest() {
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireAdvisorysTest::testConnectionAdvisories() {

    std::auto_ptr<ConnectionFactory> factory(
        ConnectionFactory::createCMSConnectionFactory( getBrokerURL() ) );
    CPPUNIT_ASSERT( factory.get() != NULL );

    std::auto_ptr<Connection> connection( factory->createConnection() );
    CPPUNIT_ASSERT( connection.get() != NULL );

    std::auto_ptr<Session> session( connection->createSession() );
    CPPUNIT_ASSERT( session.get() != NULL );

    std::auto_ptr<Destination> destination( session->createTopic("ActiveMQ.Advisory.Connection") );
    std::auto_ptr<MessageConsumer> consumer( session->createConsumer( destination.get() ) );

    connection->start();

    std::auto_ptr<Connection> otherConnection( factory->createConnection() );
    CPPUNIT_ASSERT( otherConnection.get() != NULL );

    std::auto_ptr<cms::Message> message;
    int connectionInfoCount = 0;

    do {
        message.reset( consumer->receive(2000) );

        commands::Message* amqMessage = dynamic_cast<commands::Message*>( message.get() );
        if(amqMessage != NULL) {
            try {
                Pointer<ConnectionInfo> connectionInfo =
                    amqMessage->getDataStructure().dynamicCast<commands::ConnectionInfo>();

                if(connectionInfo != NULL) {
                    connectionInfoCount++;
                }

            } catch(ClassCastException& ex) {
            }
        }

    } while(message.get() != NULL);

    CPPUNIT_ASSERT_EQUAL(2, connectionInfoCount);

    otherConnection->close();
    connection->close();
}

<commit_msg>Update the test, the Connection doesn't send the ConnectionInfo right away to allow setClientId to have effect.  Add call to start() to ensure that the second connection generates an advisory.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "OpenwireAdvisorysTest.h"

#include <activemq/core/ActiveMQConnectionFactory.h>
#include <activemq/core/ActiveMQConnection.h>
#include <activemq/core/ActiveMQSession.h>
#include <activemq/commands/Message.h>
#include <activemq/commands/ConnectionInfo.h>
#include <activemq/exceptions/ActiveMQException.h>

#include <decaf/lang/exceptions/ClassCastException.h>
#include <decaf/lang/Pointer.h>
#include <decaf/lang/Thread.h>
#include <decaf/util/UUID.h>

#include <cms/ConnectionFactory.h>
#include <cms/Connection.h>
#include <cms/Session.h>
#include <cms/MessageConsumer.h>
#include <cms/MessageProducer.h>
#include <cms/MessageListener.h>
#include <cms/ConnectionFactory.h>
#include <cms/Connection.h>
#include <cms/Message.h>
#include <cms/TextMessage.h>

#include <memory>

using namespace cms;
using namespace std;
using namespace decaf;
using namespace decaf::lang;
using namespace decaf::lang::exceptions;
using namespace decaf::util;
using namespace activemq;
using namespace activemq::core;
using namespace activemq::commands;
using namespace activemq::exceptions;
using namespace activemq::test;
using namespace activemq::test::openwire;

////////////////////////////////////////////////////////////////////////////////
OpenwireAdvisorysTest::OpenwireAdvisorysTest() {
}

////////////////////////////////////////////////////////////////////////////////
OpenwireAdvisorysTest::~OpenwireAdvisorysTest() {
}

////////////////////////////////////////////////////////////////////////////////
void OpenwireAdvisorysTest::testConnectionAdvisories() {

    std::auto_ptr<ConnectionFactory> factory(
        ConnectionFactory::createCMSConnectionFactory( getBrokerURL() ) );
    CPPUNIT_ASSERT( factory.get() != NULL );

    std::auto_ptr<Connection> connection( factory->createConnection() );
    CPPUNIT_ASSERT( connection.get() != NULL );

    std::auto_ptr<Session> session( connection->createSession() );
    CPPUNIT_ASSERT( session.get() != NULL );

    std::auto_ptr<Destination> destination( session->createTopic("ActiveMQ.Advisory.Connection") );
    std::auto_ptr<MessageConsumer> consumer( session->createConsumer( destination.get() ) );

    connection->start();

    std::auto_ptr<Connection> otherConnection( factory->createConnection() );
    CPPUNIT_ASSERT( otherConnection.get() != NULL );
    otherConnection->start();

    std::auto_ptr<cms::Message> message;
    int connectionInfoCount = 0;

    do {
        message.reset( consumer->receive(3000) );

        commands::Message* amqMessage = dynamic_cast<commands::Message*>( message.get() );
        if(amqMessage != NULL) {
            try {
                Pointer<ConnectionInfo> connectionInfo =
                    amqMessage->getDataStructure().dynamicCast<commands::ConnectionInfo>();

                if(connectionInfo != NULL) {
                    connectionInfoCount++;
                }

            } catch(ClassCastException& ex) {
            }
        }

    } while(message.get() != NULL);

    CPPUNIT_ASSERT_EQUAL(2, connectionInfoCount);

    otherConnection->close();
    connection->close();
}

<|endoftext|>


<commit_before>#include "dbusinterface.h"
#include <QtDBus>
#include "log.h"

class DBusInterfacePrivate : public QObject
{
    Q_OBJECT
public:
    DBusInterfacePrivate()
        : QObject(QCoreApplication::instance()), interface(0)
    {
        qDeleteAll(remoteSlots);
        qDeleteAll(remoteSignals);
    }

public slots:
    void onSenderDestroyed(QObject *o)
    {
        for (int i=remoteSlots.size() - 1; i>=0; --i) {
            if (remoteSlots.at(i)->object == o)
                delete remoteSlots.takeAt(i);
        }
    }

    void onReceiverDestroyed(QObject *o)
    {
        for (int i=remoteSignals.size() - 1; i>=0; --i) {
            if (remoteSignals.at(i)->object == o)
                delete remoteSignals.takeAt(i);
        }
    }

public:
    struct Connection {
        Connection(QObject *o, const char *f, const char *t) : object(o), from(f), to(t) {}
        QObject *object;
        const char *from;
        const char *to;
    };

    bool ensureInterface(int maxWait = 10000)
    {
        if (interface && interface->isValid())
            return true;
        QTime timer;
        timer.start();
        do {
            delete interface;
            interface = new QDBusInterface(SERVICE_NAME, "/", QString(), QDBusConnection::sessionBus(), this);
            if (interface->isValid()) {
                foreach(const Connection *sig, remoteSignals) {
                    interface->connection().connect(SERVICE_NAME, "/", QString(), sig->from, sig->object, sig->to);
                }

                foreach(const Connection *sig, remoteSlots) {
                    connect(sig->object, sig->from, interface, sig->to);
                }
                return true;
            }
        } while (timer.elapsed() < maxWait);
        return false;
    }

    QList<Connection*> remoteSignals, remoteSlots;
    QDBusInterface *interface;
};

#include "dbusinterface.moc"

DBusInterfacePrivate *DBusInterface::instance = 0;

bool DBusInterface::connectToRemoteSignal(const char *sig, QObject *receiver, const char *member)
{
    if (!init()) {
        Log::log(0) << "DBus Interface error" << __FUNCTION__;
        return false;
    }

    if (instance->interface->connection().connect(SERVICE_NAME, "/", QString(), sig, receiver, member)) {
        instance->remoteSignals.append(new DBusInterfacePrivate::Connection(receiver, sig, member));
        return true;
    }
    Log::log(0) << "Can't make connection to remote slot" << sig << receiver << member;
    return false;
}

bool DBusInterface::connectToRemoteSlot(QObject *sender, const char *sig, const char *remoteMember)
{
    if (!init()) {
        Log::log(0) << "DBus Interface error" << __FUNCTION__;
        return false;
    }
    if (QObject::connect(sender, SIGNAL(destroyed(QObject*)), instance, SLOT(onSenderDestroyed(QObject*)))) {
        instance->remoteSlots.append(new DBusInterfacePrivate::Connection(sender, sig, remoteMember));
        return true;
    }
    Log::log(0) << "Can't make connection to remote slot" << sender << sig << remoteMember;
    return false;
}

QDBusMessage DBusInterface::callWithArgumentList(QDBus::CallMode mode,
                                                 const QString &method,
                                                 const QList<QVariant> &args)
{
    if (!init()) {
        Log::log(0) << "DBus Interface error" << __FUNCTION__;
        return QDBusMessage();
    }
    return instance->interface->callWithArgumentList(mode, method, args);
}

bool DBusInterface::callWithCallback(const QString &method,
                                     const QList<QVariant> &args,
                                     QObject *receiver, const char *member)
{
    if (!init()) {
        Log::log(0) << "DBus Interface error" << __FUNCTION__;
        return false;
    }
    return instance->interface->callWithCallback(method, args, receiver, member);
}

bool DBusInterface::init()
{
    if (!instance)
        instance = new DBusInterfacePrivate;
    return instance->ensureInterface();
}

//  LocalWords:  include
<commit_msg>small fix<commit_after>#include "dbusinterface.h"
#include <QtDBus>
#include "log.h"

class DBusInterfacePrivate : public QObject
{
    Q_OBJECT
public:
    DBusInterfacePrivate()
        : QObject(QCoreApplication::instance()), interface(0)
    {
        qDeleteAll(remoteSlots);
        qDeleteAll(remoteSignals);
    }

public slots:
    void onSenderDestroyed(QObject *o)
    {
        for (int i=remoteSlots.size() - 1; i>=0; --i) {
            if (remoteSlots.at(i)->object == o)
                delete remoteSlots.takeAt(i);
        }
    }

    void onReceiverDestroyed(QObject *o)
    {
        for (int i=remoteSignals.size() - 1; i>=0; --i) {
            if (remoteSignals.at(i)->object == o)
                delete remoteSignals.takeAt(i);
        }
    }

public:
    struct Connection {
        Connection(QObject *o, const char *f, const char *t) : object(o), from(f), to(t) {}
        QObject *object;
        const char *from;
        const char *to;
    };

    bool ensureInterface(int maxWait = 10000)
    {
        if (interface && interface->isValid())
            return true;
        QTime timer;
        timer.start();
        do {
            delete interface;
            interface = new QDBusInterface(SERVICE_NAME, "/", QString(), QDBusConnection::sessionBus(), this);
            if (interface->isValid()) {
                foreach(const Connection *sig, remoteSignals) {
                    interface->connection().connect(SERVICE_NAME, "/", QString(), sig->from, sig->object, sig->to);
                }

                foreach(const Connection *sig, remoteSlots) {
                    connect(sig->object, sig->from, interface, sig->to);
                }
                return true;
            }
        } while (maxWait < 0 || timer.elapsed() < maxWait);
        return false;
    }

    QList<Connection*> remoteSignals, remoteSlots;
    QDBusInterface *interface;
};

#include "dbusinterface.moc"

DBusInterfacePrivate *DBusInterface::instance = 0;

bool DBusInterface::connectToRemoteSignal(const char *sig, QObject *receiver, const char *member)
{
    if (!init()) {
        Log::log(0) << "DBus Interface error" << __FUNCTION__;
        return false;
    }

    if (instance->interface->connection().connect(SERVICE_NAME, "/", QString(), sig, receiver, member)) {
        instance->remoteSignals.append(new DBusInterfacePrivate::Connection(receiver, sig, member));
        return true;
    }
    Log::log(0) << "Can't make connection to remote slot" << sig << receiver << member;
    return false;
}

bool DBusInterface::connectToRemoteSlot(QObject *sender, const char *sig, const char *remoteMember)
{
    if (!init()) {
        Log::log(0) << "DBus Interface error" << __FUNCTION__;
        return false;
    }
    if (QObject::connect(sender, SIGNAL(destroyed(QObject*)), instance, SLOT(onSenderDestroyed(QObject*)))) {
        instance->remoteSlots.append(new DBusInterfacePrivate::Connection(sender, sig, remoteMember));
        return true;
    }
    Log::log(0) << "Can't make connection to remote slot" << sender << sig << remoteMember;
    return false;
}

QDBusMessage DBusInterface::callWithArgumentList(QDBus::CallMode mode,
                                                 const QString &method,
                                                 const QList<QVariant> &args)
{
    if (!init()) {
        Log::log(0) << "DBus Interface error" << __FUNCTION__;
        return QDBusMessage();
    }
    return instance->interface->callWithArgumentList(mode, method, args);
}

bool DBusInterface::callWithCallback(const QString &method,
                                     const QList<QVariant> &args,
                                     QObject *receiver, const char *member)
{
    if (!init()) {
        Log::log(0) << "DBus Interface error" << __FUNCTION__;
        return false;
    }
    return instance->interface->callWithCallback(method, args, receiver, member);
}

bool DBusInterface::init()
{
    if (!instance)
        instance = new DBusInterfacePrivate;
    return instance->ensureInterface();
}

//  LocalWords:  include
<|endoftext|>


<commit_before>#include "hecl/Compilers.hpp"
#include "boo/graphicsdev/GLSLMacros.hpp"
#include "logvisor/logvisor.hpp"
#include <glslang/Public/ShaderLang.h>
#include <StandAlone/ResourceLimits.h>
#include <SPIRV/GlslangToSpv.h>
#include <SPIRV/disassemble.h>
#if _WIN32
#include <d3dcompiler.h>
extern pD3DCompile D3DCompilePROC;
#endif
#if __APPLE__
#include <unistd.h>
#include <memory>
#endif

namespace hecl {
logvisor::Module Log("hecl::Compilers");

template <typename P>
struct ShaderCompiler {};

template <>
struct ShaderCompiler<PlatformType::OpenGL> {
  template <typename S>
  static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
    std::string str = "#version 330\n";
    str += BOO_GLSL_BINDING_HEAD;
    str += text;
    std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(str.size() + 1), str.size() + 1);
    memcpy(ret.first.get(), str.data(), ret.second);
    return ret;
  }
};

template <>
struct ShaderCompiler<PlatformType::Vulkan> {
  static constexpr EShLanguage ShaderTypes[] = {EShLangVertex, /* Invalid */
                                                EShLangVertex,      EShLangFragment,      EShLangGeometry,
                                                EShLangTessControl, EShLangTessEvaluation};

  template <typename S>
  static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
    EShLanguage lang = ShaderTypes[int(S::Enum)];
    const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
    glslang::TShader shader(lang);
    const char* strings[] = {"#version 330\n", BOO_GLSL_BINDING_HEAD, text.data()};
    shader.setStrings(strings, 3);
    if (!shader.parse(&glslang::DefaultTBuiltInResource, 110, false, messages)) {
      fmt::print(fmt("{}\n"), text);
      Log.report(logvisor::Fatal, fmt("unable to compile shader\n{}"), shader.getInfoLog());
      return {};
    }

    glslang::TProgram prog;
    prog.addShader(&shader);
    if (!prog.link(messages)) {
      Log.report(logvisor::Fatal, fmt("unable to link shader program\n{}"), prog.getInfoLog());
      return {};
    }

    std::vector<unsigned int> out;
    glslang::GlslangToSpv(*prog.getIntermediate(lang), out);
    std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(out.size() * 4), out.size() * 4);
    memcpy(ret.first.get(), out.data(), ret.second);
    return ret;
  }
};

#if _WIN32
static const char* D3DShaderTypes[] = {nullptr, "vs_5_0", "ps_5_0", "gs_5_0", "hs_5_0", "ds_5_0"};
template <>
struct ShaderCompiler<PlatformType::D3D11> {
#if _DEBUG && 0
#define BOO_D3DCOMPILE_FLAG D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL0
#else
#define BOO_D3DCOMPILE_FLAG D3DCOMPILE_OPTIMIZATION_LEVEL3
#endif
  template <typename S>
  static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
    ComPtr<ID3DBlob> errBlob;
    ComPtr<ID3DBlob> blobOut;
    if (FAILED(D3DCompilePROC(text.data(), text.size(), "Boo HLSL Source", nullptr, nullptr, "main",
                              D3DShaderTypes[int(S::Enum)], BOO_D3DCOMPILE_FLAG, 0, &blobOut, &errBlob))) {
      printf("%s\n", text.data());
      Log.report(logvisor::Fatal, fmt("error compiling shader: {}"), (char*)errBlob->GetBufferPointer());
      return {};
    }
    std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(blobOut->GetBufferSize()), blobOut->GetBufferSize());
    memcpy(ret.first.get(), blobOut->GetBufferPointer(), blobOut->GetBufferSize());
    return ret;
  }
};
#endif

#if __APPLE__
template <>
struct ShaderCompiler<PlatformType::Metal> {
  static bool m_didCompilerSearch;
  static bool m_hasCompiler;

  static bool SearchForCompiler() {
    m_didCompilerSearch = true;
    const char* no_metal_compiler = getenv("HECL_NO_METAL_COMPILER");
    if (no_metal_compiler && atoi(no_metal_compiler))
      return false;

    pid_t pid = fork();
    if (!pid) {
      execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", "--version", NULL);
      /* xcrun returns 72 if metal command not found;
       * emulate that if xcrun not found */
      exit(72);
    }

    int status, ret;
    while ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) {}
    if (ret < 0)
      return false;
    return WEXITSTATUS(status) == 0;
  }

  template <typename S>
  static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
    if (!m_didCompilerSearch)
      m_hasCompiler = SearchForCompiler();

    std::string str =
        "#include <metal_stdlib>\n"
        "using namespace metal;\n";
    str += text;
    std::pair<StageBinaryData, size_t> ret;

    if (!m_hasCompiler) {
      /* First byte unset to indicate source data */
      ret.first = MakeStageBinaryData(str.size() + 2);
      ret.first.get()[0] = 0;
      ret.second = str.size() + 2;
      memcpy(&ret.first.get()[1], str.data(), str.size() + 1);
    } else {
      int compilerOut[2];
      int compilerIn[2];
      pipe(compilerOut);
      pipe(compilerIn);

      pid_t pid = getpid();
      const char* tmpdir = getenv("TMPDIR");
      std::string libFile = fmt::format(fmt("{}boo_metal_shader{}.metallib"), tmpdir, pid);

      /* Pipe source write to compiler */
      pid_t compilerPid = fork();
      if (!compilerPid) {
        dup2(compilerIn[0], STDIN_FILENO);
        dup2(compilerOut[1], STDOUT_FILENO);

        close(compilerOut[0]);
        close(compilerOut[1]);
        close(compilerIn[0]);
        close(compilerIn[1]);

        execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", "-o", "/dev/stdout", "-Wno-unused-variable",
               "-Wno-unused-const-variable", "-Wno-unused-function", "-c", "-x", "metal",
#ifndef NDEBUG
               "-gline-tables-only", "-MO",
#endif
               "-", NULL);
        fprintf(stderr, "execlp fail %s\n", strerror(errno));
        exit(1);
      }
      close(compilerIn[0]);
      close(compilerOut[1]);

      /* Pipe compiler to linker */
      pid_t linkerPid = fork();
      if (!linkerPid) {
        dup2(compilerOut[0], STDIN_FILENO);

        close(compilerOut[0]);
        close(compilerIn[1]);

        /* metallib doesn't like outputting to a pipe, so temp file will have to do */
        execlp("xcrun", "xcrun", "-sdk", "macosx", "metallib", "-", "-o", libFile.c_str(), NULL);
        fprintf(stderr, "execlp fail %s\n", strerror(errno));
        exit(1);
      }
      close(compilerOut[0]);

      /* Stream in source */
      const char* inPtr = str.data();
      size_t inRem = str.size();
      while (inRem) {
        ssize_t writeRes = write(compilerIn[1], inPtr, inRem);
        if (writeRes < 0) {
          fprintf(stderr, "write fail %s\n", strerror(errno));
          break;
        }
        inPtr += writeRes;
        inRem -= writeRes;
      }
      close(compilerIn[1]);

      /* Wait for completion */
      int compilerStat, linkerStat;
      while (waitpid(compilerPid, &compilerStat, 0) < 0) {
        if (errno == EINTR)
          continue;
        Log.report(logvisor::Fatal, fmt("waitpid fail %s"), strerror(errno));
        return {};
      }

      if (WEXITSTATUS(compilerStat)) {
        Log.report(logvisor::Fatal, fmt("compile fail"));
        return {};
      }

      while (waitpid(linkerPid, &linkerStat, 0) < 0) {
        if (errno == EINTR)
          continue;
        Log.report(logvisor::Fatal, fmt("waitpid fail %s"), strerror(errno));
        return {};
      }

      if (WEXITSTATUS(linkerStat)) {
        Log.report(logvisor::Fatal, fmt("link fail"));
        return {};
      }

      /* Copy temp file into buffer with first byte set to indicate binary data */
      FILE* fin = fopen(libFile.c_str(), "rb");
      fseek(fin, 0, SEEK_END);
      long libLen = ftell(fin);
      fseek(fin, 0, SEEK_SET);
      ret.first = MakeStageBinaryData(libLen + 1);
      ret.first.get()[0] = 1;
      ret.second = libLen + 1;
      fread(&ret.first.get()[1], 1, libLen, fin);
      fclose(fin);
      unlink(libFile);
    }

    return ret;
  }
};
bool ShaderCompiler<PlatformType::Metal>::m_didCompilerSearch = false;
bool ShaderCompiler<PlatformType::Metal>::m_hasCompiler = false;
#endif

#if HECL_NOUVEAU_NX
template <>
struct ShaderCompiler<PlatformType::NX> {
  template <typename S>
  static std::pair<std::shared_ptr<uint8_t[]>, size_t> Compile(std::string_view text) {
    std::string str = "#version 330\n";
    str += BOO_GLSL_BINDING_HEAD;
    str += text;
    std::pair<std::shared_ptr<uint8_t[]>, size_t> ret(new uint8_t[str.size() + 1], str.size() + 1);
    memcpy(ret.first.get(), str.data(), str.size() + 1);
    return ret;
  }
};
#endif

template <typename P, typename S>
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text) {
  return ShaderCompiler<P>::template Compile<S>(text);
}
#define SPECIALIZE_COMPILE_SHADER(P)                                                                                \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Vertex>(std::string_view text);          \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Fragment>(std::string_view text);        \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Geometry>(std::string_view text);        \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Control>(std::string_view text);         \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Evaluation>(std::string_view text);
SPECIALIZE_COMPILE_SHADER(PlatformType::OpenGL)
SPECIALIZE_COMPILE_SHADER(PlatformType::Vulkan)
#if _WIN32
SPECIALIZE_COMPILE_SHADER(PlatformType::D3D11)
#endif
#if __APPLE__
SPECIALIZE_COMPILE_SHADER(PlatformType::Metal)
#endif
#if HECL_NOUVEAU_NX
SPECIALIZE_COMPILE_SHADER(PlatformType::NX)
#endif

} // namespace hecl
<commit_msg>hecl/Compilers: Convert printf call over to fmt::print<commit_after>#include "hecl/Compilers.hpp"
#include <boo/graphicsdev/GLSLMacros.hpp>
#include <logvisor/logvisor.hpp>

#include <glslang/Public/ShaderLang.h>
#include <StandAlone/ResourceLimits.h>
#include <SPIRV/GlslangToSpv.h>
#include <SPIRV/disassemble.h>

#if _WIN32
#include <d3dcompiler.h>
extern pD3DCompile D3DCompilePROC;
#endif

#if __APPLE__
#include <unistd.h>
#include <memory>
#endif

namespace hecl {
logvisor::Module Log("hecl::Compilers");

template <typename P>
struct ShaderCompiler {};

template <>
struct ShaderCompiler<PlatformType::OpenGL> {
  template <typename S>
  static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
    std::string str = "#version 330\n";
    str += BOO_GLSL_BINDING_HEAD;
    str += text;
    std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(str.size() + 1), str.size() + 1);
    memcpy(ret.first.get(), str.data(), ret.second);
    return ret;
  }
};

template <>
struct ShaderCompiler<PlatformType::Vulkan> {
  static constexpr EShLanguage ShaderTypes[] = {EShLangVertex, /* Invalid */
                                                EShLangVertex,      EShLangFragment,      EShLangGeometry,
                                                EShLangTessControl, EShLangTessEvaluation};

  template <typename S>
  static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
    EShLanguage lang = ShaderTypes[int(S::Enum)];
    const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules);
    glslang::TShader shader(lang);
    const char* strings[] = {"#version 330\n", BOO_GLSL_BINDING_HEAD, text.data()};
    shader.setStrings(strings, 3);
    if (!shader.parse(&glslang::DefaultTBuiltInResource, 110, false, messages)) {
      fmt::print(fmt("{}\n"), text);
      Log.report(logvisor::Fatal, fmt("unable to compile shader\n{}"), shader.getInfoLog());
      return {};
    }

    glslang::TProgram prog;
    prog.addShader(&shader);
    if (!prog.link(messages)) {
      Log.report(logvisor::Fatal, fmt("unable to link shader program\n{}"), prog.getInfoLog());
      return {};
    }

    std::vector<unsigned int> out;
    glslang::GlslangToSpv(*prog.getIntermediate(lang), out);
    std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(out.size() * 4), out.size() * 4);
    memcpy(ret.first.get(), out.data(), ret.second);
    return ret;
  }
};

#if _WIN32
static const char* D3DShaderTypes[] = {nullptr, "vs_5_0", "ps_5_0", "gs_5_0", "hs_5_0", "ds_5_0"};
template <>
struct ShaderCompiler<PlatformType::D3D11> {
#if _DEBUG && 0
#define BOO_D3DCOMPILE_FLAG D3DCOMPILE_DEBUG | D3DCOMPILE_OPTIMIZATION_LEVEL0
#else
#define BOO_D3DCOMPILE_FLAG D3DCOMPILE_OPTIMIZATION_LEVEL3
#endif
  template <typename S>
  static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
    ComPtr<ID3DBlob> errBlob;
    ComPtr<ID3DBlob> blobOut;
    if (FAILED(D3DCompilePROC(text.data(), text.size(), "Boo HLSL Source", nullptr, nullptr, "main",
                              D3DShaderTypes[int(S::Enum)], BOO_D3DCOMPILE_FLAG, 0, &blobOut, &errBlob))) {
      fmt::print(fmt("{}\n"), text);
      Log.report(logvisor::Fatal, fmt("error compiling shader: {}"), (char*)errBlob->GetBufferPointer());
      return {};
    }
    std::pair<StageBinaryData, size_t> ret(MakeStageBinaryData(blobOut->GetBufferSize()), blobOut->GetBufferSize());
    memcpy(ret.first.get(), blobOut->GetBufferPointer(), blobOut->GetBufferSize());
    return ret;
  }
};
#endif

#if __APPLE__
template <>
struct ShaderCompiler<PlatformType::Metal> {
  static bool m_didCompilerSearch;
  static bool m_hasCompiler;

  static bool SearchForCompiler() {
    m_didCompilerSearch = true;
    const char* no_metal_compiler = getenv("HECL_NO_METAL_COMPILER");
    if (no_metal_compiler && atoi(no_metal_compiler))
      return false;

    pid_t pid = fork();
    if (!pid) {
      execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", "--version", NULL);
      /* xcrun returns 72 if metal command not found;
       * emulate that if xcrun not found */
      exit(72);
    }

    int status, ret;
    while ((ret = waitpid(pid, &status, 0)) < 0 && errno == EINTR) {}
    if (ret < 0)
      return false;
    return WEXITSTATUS(status) == 0;
  }

  template <typename S>
  static std::pair<StageBinaryData, size_t> Compile(std::string_view text) {
    if (!m_didCompilerSearch)
      m_hasCompiler = SearchForCompiler();

    std::string str =
        "#include <metal_stdlib>\n"
        "using namespace metal;\n";
    str += text;
    std::pair<StageBinaryData, size_t> ret;

    if (!m_hasCompiler) {
      /* First byte unset to indicate source data */
      ret.first = MakeStageBinaryData(str.size() + 2);
      ret.first.get()[0] = 0;
      ret.second = str.size() + 2;
      memcpy(&ret.first.get()[1], str.data(), str.size() + 1);
    } else {
      int compilerOut[2];
      int compilerIn[2];
      pipe(compilerOut);
      pipe(compilerIn);

      pid_t pid = getpid();
      const char* tmpdir = getenv("TMPDIR");
      std::string libFile = fmt::format(fmt("{}boo_metal_shader{}.metallib"), tmpdir, pid);

      /* Pipe source write to compiler */
      pid_t compilerPid = fork();
      if (!compilerPid) {
        dup2(compilerIn[0], STDIN_FILENO);
        dup2(compilerOut[1], STDOUT_FILENO);

        close(compilerOut[0]);
        close(compilerOut[1]);
        close(compilerIn[0]);
        close(compilerIn[1]);

        execlp("xcrun", "xcrun", "-sdk", "macosx", "metal", "-o", "/dev/stdout", "-Wno-unused-variable",
               "-Wno-unused-const-variable", "-Wno-unused-function", "-c", "-x", "metal",
#ifndef NDEBUG
               "-gline-tables-only", "-MO",
#endif
               "-", NULL);
        fprintf(stderr, "execlp fail %s\n", strerror(errno));
        exit(1);
      }
      close(compilerIn[0]);
      close(compilerOut[1]);

      /* Pipe compiler to linker */
      pid_t linkerPid = fork();
      if (!linkerPid) {
        dup2(compilerOut[0], STDIN_FILENO);

        close(compilerOut[0]);
        close(compilerIn[1]);

        /* metallib doesn't like outputting to a pipe, so temp file will have to do */
        execlp("xcrun", "xcrun", "-sdk", "macosx", "metallib", "-", "-o", libFile.c_str(), NULL);
        fprintf(stderr, "execlp fail %s\n", strerror(errno));
        exit(1);
      }
      close(compilerOut[0]);

      /* Stream in source */
      const char* inPtr = str.data();
      size_t inRem = str.size();
      while (inRem) {
        ssize_t writeRes = write(compilerIn[1], inPtr, inRem);
        if (writeRes < 0) {
          fprintf(stderr, "write fail %s\n", strerror(errno));
          break;
        }
        inPtr += writeRes;
        inRem -= writeRes;
      }
      close(compilerIn[1]);

      /* Wait for completion */
      int compilerStat, linkerStat;
      while (waitpid(compilerPid, &compilerStat, 0) < 0) {
        if (errno == EINTR)
          continue;
        Log.report(logvisor::Fatal, fmt("waitpid fail %s"), strerror(errno));
        return {};
      }

      if (WEXITSTATUS(compilerStat)) {
        Log.report(logvisor::Fatal, fmt("compile fail"));
        return {};
      }

      while (waitpid(linkerPid, &linkerStat, 0) < 0) {
        if (errno == EINTR)
          continue;
        Log.report(logvisor::Fatal, fmt("waitpid fail %s"), strerror(errno));
        return {};
      }

      if (WEXITSTATUS(linkerStat)) {
        Log.report(logvisor::Fatal, fmt("link fail"));
        return {};
      }

      /* Copy temp file into buffer with first byte set to indicate binary data */
      FILE* fin = fopen(libFile.c_str(), "rb");
      fseek(fin, 0, SEEK_END);
      long libLen = ftell(fin);
      fseek(fin, 0, SEEK_SET);
      ret.first = MakeStageBinaryData(libLen + 1);
      ret.first.get()[0] = 1;
      ret.second = libLen + 1;
      fread(&ret.first.get()[1], 1, libLen, fin);
      fclose(fin);
      unlink(libFile);
    }

    return ret;
  }
};
bool ShaderCompiler<PlatformType::Metal>::m_didCompilerSearch = false;
bool ShaderCompiler<PlatformType::Metal>::m_hasCompiler = false;
#endif

#if HECL_NOUVEAU_NX
template <>
struct ShaderCompiler<PlatformType::NX> {
  template <typename S>
  static std::pair<std::shared_ptr<uint8_t[]>, size_t> Compile(std::string_view text) {
    std::string str = "#version 330\n";
    str += BOO_GLSL_BINDING_HEAD;
    str += text;
    std::pair<std::shared_ptr<uint8_t[]>, size_t> ret(new uint8_t[str.size() + 1], str.size() + 1);
    memcpy(ret.first.get(), str.data(), str.size() + 1);
    return ret;
  }
};
#endif

template <typename P, typename S>
std::pair<StageBinaryData, size_t> CompileShader(std::string_view text) {
  return ShaderCompiler<P>::template Compile<S>(text);
}
#define SPECIALIZE_COMPILE_SHADER(P)                                                                                \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Vertex>(std::string_view text);          \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Fragment>(std::string_view text);        \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Geometry>(std::string_view text);        \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Control>(std::string_view text);         \
  template std::pair<StageBinaryData, size_t> CompileShader<P, PipelineStage::Evaluation>(std::string_view text);
SPECIALIZE_COMPILE_SHADER(PlatformType::OpenGL)
SPECIALIZE_COMPILE_SHADER(PlatformType::Vulkan)
#if _WIN32
SPECIALIZE_COMPILE_SHADER(PlatformType::D3D11)
#endif
#if __APPLE__
SPECIALIZE_COMPILE_SHADER(PlatformType::Metal)
#endif
#if HECL_NOUVEAU_NX
SPECIALIZE_COMPILE_SHADER(PlatformType::NX)
#endif

} // namespace hecl
<|endoftext|>


<commit_before>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "Test.h"
#include "SkData.h"
#include "SkDataSet.h"
#include "SkDataTable.h"
#include "SkStream.h"
#include "SkOrderedReadBuffer.h"
#include "SkOrderedWriteBuffer.h"

template <typename T> class SkTUnref {
public:
    SkTUnref(T* ref) : fRef(ref) {}
    ~SkTUnref() { fRef->unref(); }

    operator T*() { return fRef; }
    operator const T*() { return fRef; }

private:
    T*  fRef;
};

static void test_is_equal(skiatest::Reporter* reporter,
                          const SkDataTable* a, const SkDataTable* b) {
    REPORTER_ASSERT(reporter, a->count() == b->count());
    for (int i = 0; i < a->count(); ++i) {
        size_t sizea, sizeb;
        const void* mema = a->at(i, &sizea);
        const void* memb = b->at(i, &sizeb);
        REPORTER_ASSERT(reporter, sizea == sizeb);
        REPORTER_ASSERT(reporter, !memcmp(mema, memb, sizea));
    }
}

static void test_datatable_flatten(skiatest::Reporter* reporter,
                                   SkDataTable* table) {
    SkOrderedWriteBuffer wb(1024);
    wb.writeFlattenable(table);

    size_t wsize = wb.size();
    SkAutoMalloc storage(wsize);
    wb.writeToMemory(storage.get());

    SkOrderedReadBuffer rb(storage.get(), wsize);
    SkAutoTUnref<SkDataTable> newTable((SkDataTable*)rb.readFlattenable());

    SkDebugf("%d entries, %d flatten-size\n", table->count(), wsize);
    test_is_equal(reporter, table, newTable);
}

static void test_datatable_is_empty(skiatest::Reporter* reporter,
                                    SkDataTable* table) {
    REPORTER_ASSERT(reporter, table->isEmpty());
    REPORTER_ASSERT(reporter, 0 == table->count());
    test_datatable_flatten(reporter, table);
}

static void test_emptytable(skiatest::Reporter* reporter) {
    SkAutoTUnref<SkDataTable> table0(SkDataTable::NewEmpty());
    SkAutoTUnref<SkDataTable> table1(SkDataTable::NewCopyArrays(NULL, NULL, 0));
    SkAutoTUnref<SkDataTable> table2(SkDataTable::NewCopyArray(NULL, NULL, 0));
    SkAutoTUnref<SkDataTable> table3(SkDataTable::NewArrayProc(NULL, NULL, 0,
                                                               NULL, NULL));

    test_datatable_is_empty(reporter, table0);
    test_datatable_is_empty(reporter, table1);
    test_datatable_is_empty(reporter, table2);
    test_datatable_is_empty(reporter, table3);

    test_is_equal(reporter, table0, table1);
    test_is_equal(reporter, table0, table2);
    test_is_equal(reporter, table0, table3);
}

static void test_simpletable(skiatest::Reporter* reporter) {
    const int idata[] = { 1, 4, 9, 16, 25, 63 };
    int icount = SK_ARRAY_COUNT(idata);
    SkAutoTUnref<SkDataTable> itable(SkDataTable::NewCopyArray(idata,
                                                               sizeof(idata[0]),
                                                               icount));
    REPORTER_ASSERT(reporter, itable->count() == icount);
    for (int i = 0; i < icount; ++i) {
        size_t size;
        REPORTER_ASSERT(reporter, sizeof(int) == itable->atSize(i));
        REPORTER_ASSERT(reporter, *itable->atT<int>(i, &size) == idata[i]);
        REPORTER_ASSERT(reporter, sizeof(int) == size);
    }
    test_datatable_flatten(reporter, itable);
}

static void test_vartable(skiatest::Reporter* reporter) {
    const char* str[] = {
        "", "a", "be", "see", "deigh", "ef", "ggggggggggggggggggggggggggg"
    };
    int count = SK_ARRAY_COUNT(str);
    size_t sizes[SK_ARRAY_COUNT(str)];
    for (int i = 0; i < count; ++i) {
        sizes[i] = strlen(str[i]) + 1;
    }

    SkAutoTUnref<SkDataTable> table(SkDataTable::NewCopyArrays(
                                        (const void*const*)str, sizes, count));

    REPORTER_ASSERT(reporter, table->count() == count);
    for (int i = 0; i < count; ++i) {
        size_t size;
        REPORTER_ASSERT(reporter, table->atSize(i) == sizes[i]);
        REPORTER_ASSERT(reporter, !strcmp(table->atT<const char>(i, &size),
                                          str[i]));
        REPORTER_ASSERT(reporter, size == sizes[i]);

        const char* s = table->atStr(i);
        REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
    }
    test_datatable_flatten(reporter, table);
}

static void test_tablebuilder(skiatest::Reporter* reporter) {
    const char* str[] = {
        "", "a", "be", "see", "deigh", "ef", "ggggggggggggggggggggggggggg"
    };
    int count = SK_ARRAY_COUNT(str);

    SkDataTableBuilder builder(16);

    for (int i = 0; i < count; ++i) {
        builder.append(str[i], strlen(str[i]) + 1);
    }
    SkAutoTUnref<SkDataTable> table(builder.detachDataTable());

    REPORTER_ASSERT(reporter, table->count() == count);
    for (int i = 0; i < count; ++i) {
        size_t size;
        REPORTER_ASSERT(reporter, table->atSize(i) == strlen(str[i]) + 1);
        REPORTER_ASSERT(reporter, !strcmp(table->atT<const char>(i, &size),
                                          str[i]));
        REPORTER_ASSERT(reporter, size == strlen(str[i]) + 1);

        const char* s = table->atStr(i);
        REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
    }
    test_datatable_flatten(reporter, table);
}

static void test_globaltable(skiatest::Reporter* reporter) {
    static const int gData[] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
    };
    int count = SK_ARRAY_COUNT(gData);

    SkAutoTUnref<SkDataTable> table(SkDataTable::NewArrayProc(gData,
                                          sizeof(gData[0]), count, NULL, NULL));

    REPORTER_ASSERT(reporter, table->count() == count);
    for (int i = 0; i < count; ++i) {
        size_t size;
        REPORTER_ASSERT(reporter, table->atSize(i) == sizeof(int));
        REPORTER_ASSERT(reporter, *table->atT<const char>(i, &size) == i);
        REPORTER_ASSERT(reporter, sizeof(int) == size);
    }
    test_datatable_flatten(reporter, table);
}

static void TestDataTable(skiatest::Reporter* reporter) {
    test_emptytable(reporter);
    test_simpletable(reporter);
    test_vartable(reporter);
    test_tablebuilder(reporter);
    test_globaltable(reporter);
}

static void unrefAll(const SkDataSet::Pair pairs[], int count) {
    for (int i = 0; i < count; ++i) {
        pairs[i].fValue->unref();
    }
}

// asserts that inner is a subset of outer
static void test_dataset_subset(skiatest::Reporter* reporter,
                                const SkDataSet& outer, const SkDataSet& inner) {
    SkDataSet::Iter iter(inner);
    for (; !iter.done(); iter.next()) {
        SkData* outerData = outer.find(iter.key());
        REPORTER_ASSERT(reporter, outerData);
        REPORTER_ASSERT(reporter, outerData->equals(iter.value()));
    }
}

static void test_datasets_equal(skiatest::Reporter* reporter,
                                const SkDataSet& ds0, const SkDataSet& ds1) {
    REPORTER_ASSERT(reporter, ds0.count() == ds1.count());

    test_dataset_subset(reporter, ds0, ds1);
    test_dataset_subset(reporter, ds1, ds0);
}

static void test_dataset(skiatest::Reporter* reporter, const SkDataSet& ds,
                         int count) {
    REPORTER_ASSERT(reporter, ds.count() == count);

    SkDataSet::Iter iter(ds);
    int index = 0;
    for (; !iter.done(); iter.next()) {
//        const char* name = iter.key();
//        SkData* data = iter.value();
//        SkDebugf("[%d] %s:%s\n", index, name, (const char*)data->bytes());
        index += 1;
    }
    REPORTER_ASSERT(reporter, index == count);

    SkDynamicMemoryWStream ostream;
    ds.writeToStream(&ostream);
    SkMemoryStream istream;
    istream.setData(ostream.copyToData())->unref();
    SkDataSet copy(&istream);

    test_datasets_equal(reporter, ds, copy);
}

static void test_dataset(skiatest::Reporter* reporter) {
    SkDataSet set0(NULL, 0);
    SkDataSet set1("hello", SkTUnref<SkData>(SkData::NewWithCString("world")));

    const SkDataSet::Pair pairs[] = {
        { "one", SkData::NewWithCString("1") },
        { "two", SkData::NewWithCString("2") },
        { "three", SkData::NewWithCString("3") },
    };
    SkDataSet set3(pairs, 3);
    unrefAll(pairs, 3);

    test_dataset(reporter, set0, 0);
    test_dataset(reporter, set1, 1);
    test_dataset(reporter, set3, 3);
}

static void* gGlobal;

static void delete_int_proc(const void* ptr, size_t len, void* context) {
    int* data = (int*)ptr;
    SkASSERT(context == gGlobal);
    delete[] data;
}

static void assert_len(skiatest::Reporter* reporter, SkData* ref, size_t len) {
    REPORTER_ASSERT(reporter, ref->size() == len);
}

static void assert_data(skiatest::Reporter* reporter, SkData* ref,
                        const void* data, size_t len) {
    REPORTER_ASSERT(reporter, ref->size() == len);
    REPORTER_ASSERT(reporter, !memcmp(ref->data(), data, len));
}

static void test_cstring(skiatest::Reporter* reporter) {
    const char str[] = "Hello world";
    size_t     len = strlen(str);

    SkAutoTUnref<SkData> r0(SkData::NewWithCopy(str, len + 1));
    SkAutoTUnref<SkData> r1(SkData::NewWithCString(str));

    REPORTER_ASSERT(reporter, r0->equals(r1));

    SkAutoTUnref<SkData> r2(SkData::NewWithCString(NULL));
    REPORTER_ASSERT(reporter, 1 == r2->size());
    REPORTER_ASSERT(reporter, 0 == *r2->bytes());
}

static void TestData(skiatest::Reporter* reporter) {
    const char* str = "We the people, in order to form a more perfect union.";
    const int N = 10;

    SkAutoTUnref<SkData> r0(SkData::NewEmpty());
    SkAutoTUnref<SkData> r1(SkData::NewWithCopy(str, strlen(str)));
    SkAutoTUnref<SkData> r2(SkData::NewWithProc(new int[N], N*sizeof(int),
                                           delete_int_proc, gGlobal));
    SkAutoTUnref<SkData> r3(SkData::NewSubset(r1, 7, 6));

    assert_len(reporter, r0, 0);
    assert_len(reporter, r1, strlen(str));
    assert_len(reporter, r2, N * sizeof(int));
    assert_len(reporter, r3, 6);

    assert_data(reporter, r1, str, strlen(str));
    assert_data(reporter, r3, "people", 6);

    SkData* tmp = SkData::NewSubset(r1, strlen(str), 10);
    assert_len(reporter, tmp, 0);
    tmp->unref();
    tmp = SkData::NewSubset(r1, 0, 0);
    assert_len(reporter, tmp, 0);
    tmp->unref();

    test_cstring(reporter);
    test_dataset(reporter);
}

#include "TestClassDef.h"
DEFINE_TESTCLASS("Data", DataTestClass, TestData)
DEFINE_TESTCLASS("DataTable", DataTableTestClass, TestDataTable)
<commit_msg>pass 0 instead of NULL for size_t parameter<commit_after>
/*
 * Copyright 2011 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "Test.h"
#include "SkData.h"
#include "SkDataSet.h"
#include "SkDataTable.h"
#include "SkStream.h"
#include "SkOrderedReadBuffer.h"
#include "SkOrderedWriteBuffer.h"

template <typename T> class SkTUnref {
public:
    SkTUnref(T* ref) : fRef(ref) {}
    ~SkTUnref() { fRef->unref(); }

    operator T*() { return fRef; }
    operator const T*() { return fRef; }

private:
    T*  fRef;
};

static void test_is_equal(skiatest::Reporter* reporter,
                          const SkDataTable* a, const SkDataTable* b) {
    REPORTER_ASSERT(reporter, a->count() == b->count());
    for (int i = 0; i < a->count(); ++i) {
        size_t sizea, sizeb;
        const void* mema = a->at(i, &sizea);
        const void* memb = b->at(i, &sizeb);
        REPORTER_ASSERT(reporter, sizea == sizeb);
        REPORTER_ASSERT(reporter, !memcmp(mema, memb, sizea));
    }
}

static void test_datatable_flatten(skiatest::Reporter* reporter,
                                   SkDataTable* table) {
    SkOrderedWriteBuffer wb(1024);
    wb.writeFlattenable(table);

    size_t wsize = wb.size();
    SkAutoMalloc storage(wsize);
    wb.writeToMemory(storage.get());

    SkOrderedReadBuffer rb(storage.get(), wsize);
    SkAutoTUnref<SkDataTable> newTable((SkDataTable*)rb.readFlattenable());

    SkDebugf("%d entries, %d flatten-size\n", table->count(), wsize);
    test_is_equal(reporter, table, newTable);
}

static void test_datatable_is_empty(skiatest::Reporter* reporter,
                                    SkDataTable* table) {
    REPORTER_ASSERT(reporter, table->isEmpty());
    REPORTER_ASSERT(reporter, 0 == table->count());
    test_datatable_flatten(reporter, table);
}

static void test_emptytable(skiatest::Reporter* reporter) {
    SkAutoTUnref<SkDataTable> table0(SkDataTable::NewEmpty());
    SkAutoTUnref<SkDataTable> table1(SkDataTable::NewCopyArrays(NULL, NULL, 0));
    SkAutoTUnref<SkDataTable> table2(SkDataTable::NewCopyArray(NULL, 0, 0));
    SkAutoTUnref<SkDataTable> table3(SkDataTable::NewArrayProc(NULL, 0, 0,
                                                               NULL, NULL));

    test_datatable_is_empty(reporter, table0);
    test_datatable_is_empty(reporter, table1);
    test_datatable_is_empty(reporter, table2);
    test_datatable_is_empty(reporter, table3);

    test_is_equal(reporter, table0, table1);
    test_is_equal(reporter, table0, table2);
    test_is_equal(reporter, table0, table3);
}

static void test_simpletable(skiatest::Reporter* reporter) {
    const int idata[] = { 1, 4, 9, 16, 25, 63 };
    int icount = SK_ARRAY_COUNT(idata);
    SkAutoTUnref<SkDataTable> itable(SkDataTable::NewCopyArray(idata,
                                                               sizeof(idata[0]),
                                                               icount));
    REPORTER_ASSERT(reporter, itable->count() == icount);
    for (int i = 0; i < icount; ++i) {
        size_t size;
        REPORTER_ASSERT(reporter, sizeof(int) == itable->atSize(i));
        REPORTER_ASSERT(reporter, *itable->atT<int>(i, &size) == idata[i]);
        REPORTER_ASSERT(reporter, sizeof(int) == size);
    }
    test_datatable_flatten(reporter, itable);
}

static void test_vartable(skiatest::Reporter* reporter) {
    const char* str[] = {
        "", "a", "be", "see", "deigh", "ef", "ggggggggggggggggggggggggggg"
    };
    int count = SK_ARRAY_COUNT(str);
    size_t sizes[SK_ARRAY_COUNT(str)];
    for (int i = 0; i < count; ++i) {
        sizes[i] = strlen(str[i]) + 1;
    }

    SkAutoTUnref<SkDataTable> table(SkDataTable::NewCopyArrays(
                                        (const void*const*)str, sizes, count));

    REPORTER_ASSERT(reporter, table->count() == count);
    for (int i = 0; i < count; ++i) {
        size_t size;
        REPORTER_ASSERT(reporter, table->atSize(i) == sizes[i]);
        REPORTER_ASSERT(reporter, !strcmp(table->atT<const char>(i, &size),
                                          str[i]));
        REPORTER_ASSERT(reporter, size == sizes[i]);

        const char* s = table->atStr(i);
        REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
    }
    test_datatable_flatten(reporter, table);
}

static void test_tablebuilder(skiatest::Reporter* reporter) {
    const char* str[] = {
        "", "a", "be", "see", "deigh", "ef", "ggggggggggggggggggggggggggg"
    };
    int count = SK_ARRAY_COUNT(str);

    SkDataTableBuilder builder(16);

    for (int i = 0; i < count; ++i) {
        builder.append(str[i], strlen(str[i]) + 1);
    }
    SkAutoTUnref<SkDataTable> table(builder.detachDataTable());

    REPORTER_ASSERT(reporter, table->count() == count);
    for (int i = 0; i < count; ++i) {
        size_t size;
        REPORTER_ASSERT(reporter, table->atSize(i) == strlen(str[i]) + 1);
        REPORTER_ASSERT(reporter, !strcmp(table->atT<const char>(i, &size),
                                          str[i]));
        REPORTER_ASSERT(reporter, size == strlen(str[i]) + 1);

        const char* s = table->atStr(i);
        REPORTER_ASSERT(reporter, strlen(s) == strlen(str[i]));
    }
    test_datatable_flatten(reporter, table);
}

static void test_globaltable(skiatest::Reporter* reporter) {
    static const int gData[] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
    };
    int count = SK_ARRAY_COUNT(gData);

    SkAutoTUnref<SkDataTable> table(SkDataTable::NewArrayProc(gData,
                                          sizeof(gData[0]), count, NULL, NULL));

    REPORTER_ASSERT(reporter, table->count() == count);
    for (int i = 0; i < count; ++i) {
        size_t size;
        REPORTER_ASSERT(reporter, table->atSize(i) == sizeof(int));
        REPORTER_ASSERT(reporter, *table->atT<const char>(i, &size) == i);
        REPORTER_ASSERT(reporter, sizeof(int) == size);
    }
    test_datatable_flatten(reporter, table);
}

static void TestDataTable(skiatest::Reporter* reporter) {
    test_emptytable(reporter);
    test_simpletable(reporter);
    test_vartable(reporter);
    test_tablebuilder(reporter);
    test_globaltable(reporter);
}

static void unrefAll(const SkDataSet::Pair pairs[], int count) {
    for (int i = 0; i < count; ++i) {
        pairs[i].fValue->unref();
    }
}

// asserts that inner is a subset of outer
static void test_dataset_subset(skiatest::Reporter* reporter,
                                const SkDataSet& outer, const SkDataSet& inner) {
    SkDataSet::Iter iter(inner);
    for (; !iter.done(); iter.next()) {
        SkData* outerData = outer.find(iter.key());
        REPORTER_ASSERT(reporter, outerData);
        REPORTER_ASSERT(reporter, outerData->equals(iter.value()));
    }
}

static void test_datasets_equal(skiatest::Reporter* reporter,
                                const SkDataSet& ds0, const SkDataSet& ds1) {
    REPORTER_ASSERT(reporter, ds0.count() == ds1.count());

    test_dataset_subset(reporter, ds0, ds1);
    test_dataset_subset(reporter, ds1, ds0);
}

static void test_dataset(skiatest::Reporter* reporter, const SkDataSet& ds,
                         int count) {
    REPORTER_ASSERT(reporter, ds.count() == count);

    SkDataSet::Iter iter(ds);
    int index = 0;
    for (; !iter.done(); iter.next()) {
//        const char* name = iter.key();
//        SkData* data = iter.value();
//        SkDebugf("[%d] %s:%s\n", index, name, (const char*)data->bytes());
        index += 1;
    }
    REPORTER_ASSERT(reporter, index == count);

    SkDynamicMemoryWStream ostream;
    ds.writeToStream(&ostream);
    SkMemoryStream istream;
    istream.setData(ostream.copyToData())->unref();
    SkDataSet copy(&istream);

    test_datasets_equal(reporter, ds, copy);
}

static void test_dataset(skiatest::Reporter* reporter) {
    SkDataSet set0(NULL, 0);
    SkDataSet set1("hello", SkTUnref<SkData>(SkData::NewWithCString("world")));

    const SkDataSet::Pair pairs[] = {
        { "one", SkData::NewWithCString("1") },
        { "two", SkData::NewWithCString("2") },
        { "three", SkData::NewWithCString("3") },
    };
    SkDataSet set3(pairs, 3);
    unrefAll(pairs, 3);

    test_dataset(reporter, set0, 0);
    test_dataset(reporter, set1, 1);
    test_dataset(reporter, set3, 3);
}

static void* gGlobal;

static void delete_int_proc(const void* ptr, size_t len, void* context) {
    int* data = (int*)ptr;
    SkASSERT(context == gGlobal);
    delete[] data;
}

static void assert_len(skiatest::Reporter* reporter, SkData* ref, size_t len) {
    REPORTER_ASSERT(reporter, ref->size() == len);
}

static void assert_data(skiatest::Reporter* reporter, SkData* ref,
                        const void* data, size_t len) {
    REPORTER_ASSERT(reporter, ref->size() == len);
    REPORTER_ASSERT(reporter, !memcmp(ref->data(), data, len));
}

static void test_cstring(skiatest::Reporter* reporter) {
    const char str[] = "Hello world";
    size_t     len = strlen(str);

    SkAutoTUnref<SkData> r0(SkData::NewWithCopy(str, len + 1));
    SkAutoTUnref<SkData> r1(SkData::NewWithCString(str));

    REPORTER_ASSERT(reporter, r0->equals(r1));

    SkAutoTUnref<SkData> r2(SkData::NewWithCString(NULL));
    REPORTER_ASSERT(reporter, 1 == r2->size());
    REPORTER_ASSERT(reporter, 0 == *r2->bytes());
}

static void TestData(skiatest::Reporter* reporter) {
    const char* str = "We the people, in order to form a more perfect union.";
    const int N = 10;

    SkAutoTUnref<SkData> r0(SkData::NewEmpty());
    SkAutoTUnref<SkData> r1(SkData::NewWithCopy(str, strlen(str)));
    SkAutoTUnref<SkData> r2(SkData::NewWithProc(new int[N], N*sizeof(int),
                                           delete_int_proc, gGlobal));
    SkAutoTUnref<SkData> r3(SkData::NewSubset(r1, 7, 6));

    assert_len(reporter, r0, 0);
    assert_len(reporter, r1, strlen(str));
    assert_len(reporter, r2, N * sizeof(int));
    assert_len(reporter, r3, 6);

    assert_data(reporter, r1, str, strlen(str));
    assert_data(reporter, r3, "people", 6);

    SkData* tmp = SkData::NewSubset(r1, strlen(str), 10);
    assert_len(reporter, tmp, 0);
    tmp->unref();
    tmp = SkData::NewSubset(r1, 0, 0);
    assert_len(reporter, tmp, 0);
    tmp->unref();

    test_cstring(reporter);
    test_dataset(reporter);
}

#include "TestClassDef.h"
DEFINE_TESTCLASS("Data", DataTestClass, TestData)
DEFINE_TESTCLASS("DataTable", DataTableTestClass, TestDataTable)
<|endoftext|>


<commit_before>#include "ProcessEvent.h"

// PUT
#include <specialized/osdetect.h>
#include <specialized/eventbackend.h>

#if defined(FORCE_PROC_POLLING)
# pragma message("Forcing use of polling /proc to detect process events.")
# define FALLBACK_ON_PROC_POLLING

#elif defined(__linux__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,6,15) /* Linux 2.6.15+ */

// Linux
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>

// PUT
# include <cxxutils/posix_helpers.h>
# include <cxxutils/socket_helpers.h>
# include <cxxutils/vterm.h>

enum {
  Read = 0,
  Write = 1,
};

# pragma pack(push, 1)
struct message_t
{
  union {
    ProcessEvent::Flags action;
    uint32_t : 0;
  };
  pid_t pid;
};
# pragma pack(pop)
static_assert(sizeof(message_t) == sizeof(uint64_t), "unexpected struct size");

// process flags
static constexpr uint8_t from_native_flags(const native_flags_t flags) noexcept
{
  return
      (flags & proc_event::PROC_EVENT_EXEC ? ProcessEvent::Exec : 0) |
      (flags & proc_event::PROC_EVENT_EXIT ? ProcessEvent::Exit : 0) |
      (flags & proc_event::PROC_EVENT_FORK ? ProcessEvent::Fork : 0) ;
}
/*
static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? native_flags_t(proc_event::PROC_EVENT_EXEC) : 0) | // Process called exec*()
      (flags & ProcessEvent::Exit ? native_flags_t(proc_event::PROC_EVENT_EXIT) : 0) | // Process exited
      (flags & ProcessEvent::Fork ? native_flags_t(proc_event::PROC_EVENT_FORK) : 0) ; // Process forked
}
*/

struct ProcessEvent::platform_dependant // process notification (process events connector)
{
  posix::fd_t fd;
  struct eventinfo_t
  {
    posix::fd_t fd[2]; // two fds for pipe based communication
    ProcessEvent::Flags_t flags;
  };

  std::unordered_map<pid_t, eventinfo_t> events;

  platform_dependant(void) noexcept
  {
    fd = posix::socket(EDomain::netlink, EType::datagram, EProtocol::connector);
    flaw(fd == posix::invalid_descriptor,
         terminal::warning,,,
         "Unable to open a netlink socket for Process Events Connector: %s", std::strerror(errno))

    sockaddr_nl sa_nl;
    sa_nl.nl_family = PF_NETLINK;
    sa_nl.nl_groups = CN_IDX_PROC;
    sa_nl.nl_pid = uint32_t(getpid());

    flaw(!posix::bind(fd, reinterpret_cast<struct sockaddr *>(&sa_nl), sizeof(sa_nl)),
         terminal::warning,,,
         "Process Events Connector requires root level access: %s", std::strerror(errno));

#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procconn_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_cn_mcast_op operation;
    } procconn;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_cn_mcast_op) == sizeof(procconn_t), "compiler needs to pack struct");

    std::memset(&procconn, 0, sizeof(procconn));
    procconn.header.nlmsg_len = sizeof(procconn);
    procconn.header.nlmsg_pid = uint32_t(getpid());
    procconn.header.nlmsg_type = NLMSG_DONE;
    procconn.message.id.idx = CN_IDX_PROC;
    procconn.message.id.val = CN_VAL_PROC;
    procconn.message.len = sizeof(proc_cn_mcast_op);
    procconn.operation = PROC_CN_MCAST_LISTEN;

    flaw(posix::send(fd, &procconn, sizeof(procconn)) == posix::error_response,
         terminal::warning,,,
         "Failed to enable Process Events Connector notifications: %s", std::strerror(errno));

    EventBackend::add(fd, EventBackend::SimplePollReadFlags,
                      [this](posix::fd_t lambda_fd, native_flags_t) noexcept { read(lambda_fd); });

    std::fprintf(stderr, "%s%s\n", terminal::information, "Process Events Connector active");
  }

  ~platform_dependant(void) noexcept
  {
    EventBackend::remove(fd, EventBackend::SimplePollReadFlags);
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  posix::fd_t add(pid_t pid, ProcessEvent::Flags_t flags) noexcept
  {
    eventinfo_t event;
    event.flags = flags;
    if(!posix::pipe(event.fd))
      return posix::invalid_descriptor;

    auto iter = events.emplace(pid, event);

    // add filter installation code here

    return event.fd[Read];
  }

  bool remove(pid_t pid) noexcept
  {
    auto iter = events.find(pid);
    if(iter == events.end())
      return false;

    // add filter removal code here

    posix::close(iter->second.fd[Read]);
    posix::close(iter->second.fd[Write]);
    events.erase(iter);
    return true;
  }

  void read(posix::fd_t procfd) noexcept
  {
#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procmsg_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_event event;
    } procmsg;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_event) == sizeof(procmsg_t), "compiler needs to pack struct");

    pollfd fds = { procfd, POLLIN, 0 };
    while(posix::poll(&fds, 1, 0) > 0 && // while there are messages AND
          posix::recv(procfd, reinterpret_cast<void*>(&procmsg), sizeof(procmsg_t), 0) > 0) // read process event message
    {
      auto iter = events.find(procmsg.event.event_data.id.process_pid); // find event info for this PID
      if(iter != events.end()) // if found...
        posix::write(iter->second.fd[Write], &procmsg.event, sizeof(procmsg.event)); // write process event info into the communications pipe
    }
  }
} ProcessEvent::s_platform;

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  m_fd = s_platform.add(m_pid, m_flags); // add PID to monitor and return communications pipe

  EventBackend::add(m_fd, EventBackend::SimplePollReadFlags, // connect communications pipe to a lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t) noexcept
                    {
                      proc_event data;
                      pollfd fds = { lambda_fd, POLLIN, 0 };
                      while(posix::poll(&fds, 1, 0) > 0 && // while there is another event to be read
                            posix::read(lambda_fd, &data, sizeof(data)) > 0) // read the event
                        switch(from_native_flags(data.what)) // find the type of event
                        {
                          case Flags::Exec: // queue exec signal with PID
                            Object::enqueue(execed,
                                            data.event_data.exec.process_pid);
                            break;
                          case Flags::Exit: // queue exit signal with PID and exit code
                            if(data.event_data.exit.exit_signal) // if killed by a signal
                              Object::enqueue(killed,
                                              data.event_data.exit.process_pid,
                                              *reinterpret_cast<posix::signal::EId*>(&data.event_data.exit.exit_signal));
                            else // else exited by itself
                              Object::enqueue(exited,
                                              data.event_data.exit.process_pid,
                                              *reinterpret_cast<posix::error_t*>(&data.event_data.exit.exit_code));
                            break;
                          case Flags::Fork: // queue fork signal with PID and child PID
                            Object::enqueue(forked,
                                            data.event_data.fork.parent_pid,
                                            data.event_data.fork.child_pid);
                            break;
                        }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_fd, EventBackend::SimplePollReadFlags);
  s_platform.remove(m_pid);
}

#elif defined(__darwin__)     /* Darwin 7+     */ || \
      defined(__DragonFly__)  /* DragonFly BSD */ || \
      (defined(__FreeBSD__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(4,1,0))  /* FreeBSD 4.1+  */ || \
      (defined(__OpenBSD__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,9,0))  /* OpenBSD 2.9+  */ || \
      (defined(__NetBSD__)  && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,0,0))  /* NetBSD 2+     */

#include <sys/event.h> // kqueue

static constexpr native_flags_t composite_flag(uint16_t actions, int16_t filters, uint32_t flags) noexcept
  { return native_flags_t(actions) | (native_flags_t(uint16_t(filters)) << 16) | (native_flags_t(flags) << 32); }

static constexpr bool flag_subset(native_flags_t flags, native_flags_t subset)
  { return (flags & subset) == subset; }

static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? composite_flag(0, EVFILT_PROC, NOTE_EXEC) : 0) |
      (flags & ProcessEvent::Exit ? composite_flag(0, EVFILT_PROC, NOTE_EXIT) : 0) |
      (flags & ProcessEvent::Fork ? composite_flag(0, EVFILT_PROC, NOTE_FORK) : 0) ;
}

static constexpr ushort extract_filter(native_flags_t flags) noexcept
  { return (flags >> 16) & 0xFFFF; }

template<typename rtype>
static constexpr rtype extract_flags(native_flags_t flags) noexcept
  { return flags >> 32; }

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  EventBackend::add(m_pid, to_native_flags(m_flags), // connect PID event to lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t lambda_flags) noexcept
                    {
                      switch(extract_filter(lambda_flags)) // switch by filtered event type
                      {
                        case Flags::Exec: // queue exec signal with PID
                          Object::enqueue_copy(execed, m_pid);
                          break;
                        case Flags::Exit: // queue exit signal with PID and exit code
                          Object::enqueue_copy(exited, m_pid, extract_flags<posix::error_t>(lambda_flags));
                          break;
                        case Flags::Fork: // queue fork signal with PID and child PID
                          Object::enqueue_copy(forked, m_pid, extract_flags<pid_t>(lambda_flags));
                          break;
                      }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_pid, to_native_flags(m_flags)); // disconnect PID with flags
}

#elif defined(__solaris__) /* Solaris */
# pragma message("No process event backend code exists in PUT for Solaris!  Please submit a patch!")
# define FALLBACK_ON_PROC_POLLING

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
# pragma message("No process event backend code exists in PUT for QNX!  Please submit a patch!")
# define FALLBACK_ON_PROC_POLLING

#else
# pragma message("No platform specific process event detection code! Falling back on polling /proc.")
# define FALLBACK_ON_PROC_POLLING
#endif

#if defined(FALLBACK_ON_PROC_POLLING)
#error TODO: implement process event detection code by polling /proc.
#endif
<commit_msg>remove unused code<commit_after>#include "ProcessEvent.h"

// PUT
#include <specialized/osdetect.h>
#include <specialized/eventbackend.h>

#if defined(FORCE_PROC_POLLING)
# pragma message("Forcing use of polling /proc to detect process events.")
# define FALLBACK_ON_PROC_POLLING

#elif defined(__linux__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,6,15) /* Linux 2.6.15+ */

// Linux
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>

// PUT
# include <cxxutils/posix_helpers.h>
# include <cxxutils/socket_helpers.h>
# include <cxxutils/vterm.h>

enum {
  Read = 0,
  Write = 1,
};

// process flags
static constexpr uint8_t from_native_flags(const native_flags_t flags) noexcept
{
  return
      (flags & proc_event::PROC_EVENT_EXEC ? ProcessEvent::Exec : 0) |
      (flags & proc_event::PROC_EVENT_EXIT ? ProcessEvent::Exit : 0) |
      (flags & proc_event::PROC_EVENT_FORK ? ProcessEvent::Fork : 0) ;
}
/*
static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? native_flags_t(proc_event::PROC_EVENT_EXEC) : 0) | // Process called exec*()
      (flags & ProcessEvent::Exit ? native_flags_t(proc_event::PROC_EVENT_EXIT) : 0) | // Process exited
      (flags & ProcessEvent::Fork ? native_flags_t(proc_event::PROC_EVENT_FORK) : 0) ; // Process forked
}
*/

struct ProcessEvent::platform_dependant // process notification (process events connector)
{
  posix::fd_t fd;
  struct eventinfo_t
  {
    posix::fd_t fd[2]; // two fds for pipe based communication
    ProcessEvent::Flags_t flags;
  };

  std::unordered_map<pid_t, eventinfo_t> events;

  platform_dependant(void) noexcept
  {
    fd = posix::socket(EDomain::netlink, EType::datagram, EProtocol::connector);
    flaw(fd == posix::invalid_descriptor,
         terminal::warning,,,
         "Unable to open a netlink socket for Process Events Connector: %s", std::strerror(errno))

    sockaddr_nl sa_nl;
    sa_nl.nl_family = PF_NETLINK;
    sa_nl.nl_groups = CN_IDX_PROC;
    sa_nl.nl_pid = uint32_t(getpid());

    flaw(!posix::bind(fd, reinterpret_cast<struct sockaddr *>(&sa_nl), sizeof(sa_nl)),
         terminal::warning,,,
         "Process Events Connector requires root level access: %s", std::strerror(errno));

#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procconn_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_cn_mcast_op operation;
    } procconn;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_cn_mcast_op) == sizeof(procconn_t), "compiler needs to pack struct");

    std::memset(&procconn, 0, sizeof(procconn));
    procconn.header.nlmsg_len = sizeof(procconn);
    procconn.header.nlmsg_pid = uint32_t(getpid());
    procconn.header.nlmsg_type = NLMSG_DONE;
    procconn.message.id.idx = CN_IDX_PROC;
    procconn.message.id.val = CN_VAL_PROC;
    procconn.message.len = sizeof(proc_cn_mcast_op);
    procconn.operation = PROC_CN_MCAST_LISTEN;

    flaw(posix::send(fd, &procconn, sizeof(procconn)) == posix::error_response,
         terminal::warning,,,
         "Failed to enable Process Events Connector notifications: %s", std::strerror(errno));

    EventBackend::add(fd, EventBackend::SimplePollReadFlags,
                      [this](posix::fd_t lambda_fd, native_flags_t) noexcept { read(lambda_fd); });

    std::fprintf(stderr, "%s%s\n", terminal::information, "Process Events Connector active");
  }

  ~platform_dependant(void) noexcept
  {
    EventBackend::remove(fd, EventBackend::SimplePollReadFlags);
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  posix::fd_t add(pid_t pid, ProcessEvent::Flags_t flags) noexcept
  {
    eventinfo_t event;
    event.flags = flags;
    if(!posix::pipe(event.fd))
      return posix::invalid_descriptor;

    auto iter = events.emplace(pid, event);

    // add filter installation code here

    return event.fd[Read];
  }

  bool remove(pid_t pid) noexcept
  {
    auto iter = events.find(pid);
    if(iter == events.end())
      return false;

    // add filter removal code here

    posix::close(iter->second.fd[Read]);
    posix::close(iter->second.fd[Write]);
    events.erase(iter);
    return true;
  }

  void read(posix::fd_t procfd) noexcept
  {
#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procmsg_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_event event;
    } procmsg;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_event) == sizeof(procmsg_t), "compiler needs to pack struct");

    pollfd fds = { procfd, POLLIN, 0 };
    while(posix::poll(&fds, 1, 0) > 0 && // while there are messages AND
          posix::recv(procfd, reinterpret_cast<void*>(&procmsg), sizeof(procmsg_t), 0) > 0) // read process event message
    {
      auto iter = events.find(procmsg.event.event_data.id.process_pid); // find event info for this PID
      if(iter != events.end()) // if found...
        posix::write(iter->second.fd[Write], &procmsg.event, sizeof(procmsg.event)); // write process event info into the communications pipe
    }
  }
} ProcessEvent::s_platform;

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  m_fd = s_platform.add(m_pid, m_flags); // add PID to monitor and return communications pipe

  EventBackend::add(m_fd, EventBackend::SimplePollReadFlags, // connect communications pipe to a lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t) noexcept
                    {
                      proc_event data;
                      pollfd fds = { lambda_fd, POLLIN, 0 };
                      while(posix::poll(&fds, 1, 0) > 0 && // while there is another event to be read
                            posix::read(lambda_fd, &data, sizeof(data)) > 0) // read the event
                        switch(from_native_flags(data.what)) // find the type of event
                        {
                          case Flags::Exec: // queue exec signal with PID
                            Object::enqueue(execed,
                                            data.event_data.exec.process_pid);
                            break;
                          case Flags::Exit: // queue exit signal with PID and exit code
                            if(data.event_data.exit.exit_signal) // if killed by a signal
                              Object::enqueue(killed,
                                              data.event_data.exit.process_pid,
                                              *reinterpret_cast<posix::signal::EId*>(&data.event_data.exit.exit_signal));
                            else // else exited by itself
                              Object::enqueue(exited,
                                              data.event_data.exit.process_pid,
                                              *reinterpret_cast<posix::error_t*>(&data.event_data.exit.exit_code));
                            break;
                          case Flags::Fork: // queue fork signal with PID and child PID
                            Object::enqueue(forked,
                                            data.event_data.fork.parent_pid,
                                            data.event_data.fork.child_pid);
                            break;
                        }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_fd, EventBackend::SimplePollReadFlags);
  s_platform.remove(m_pid);
}

#elif defined(__darwin__)     /* Darwin 7+     */ || \
      defined(__DragonFly__)  /* DragonFly BSD */ || \
      (defined(__FreeBSD__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(4,1,0))  /* FreeBSD 4.1+  */ || \
      (defined(__OpenBSD__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,9,0))  /* OpenBSD 2.9+  */ || \
      (defined(__NetBSD__)  && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,0,0))  /* NetBSD 2+     */

#include <sys/event.h> // kqueue

static constexpr native_flags_t composite_flag(uint16_t actions, int16_t filters, uint32_t flags) noexcept
  { return native_flags_t(actions) | (native_flags_t(uint16_t(filters)) << 16) | (native_flags_t(flags) << 32); }

static constexpr bool flag_subset(native_flags_t flags, native_flags_t subset)
  { return (flags & subset) == subset; }

static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? composite_flag(0, EVFILT_PROC, NOTE_EXEC) : 0) |
      (flags & ProcessEvent::Exit ? composite_flag(0, EVFILT_PROC, NOTE_EXIT) : 0) |
      (flags & ProcessEvent::Fork ? composite_flag(0, EVFILT_PROC, NOTE_FORK) : 0) ;
}

static constexpr ushort extract_filter(native_flags_t flags) noexcept
  { return (flags >> 16) & 0xFFFF; }

template<typename rtype>
static constexpr rtype extract_flags(native_flags_t flags) noexcept
  { return flags >> 32; }

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  EventBackend::add(m_pid, to_native_flags(m_flags), // connect PID event to lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t lambda_flags) noexcept
                    {
                      switch(extract_filter(lambda_flags)) // switch by filtered event type
                      {
                        case Flags::Exec: // queue exec signal with PID
                          Object::enqueue_copy(execed, m_pid);
                          break;
                        case Flags::Exit: // queue exit signal with PID and exit code
                          Object::enqueue_copy(exited, m_pid, extract_flags<posix::error_t>(lambda_flags));
                          break;
                        case Flags::Fork: // queue fork signal with PID and child PID
                          Object::enqueue_copy(forked, m_pid, extract_flags<pid_t>(lambda_flags));
                          break;
                      }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_pid, to_native_flags(m_flags)); // disconnect PID with flags
}

#elif defined(__solaris__) /* Solaris */
# pragma message("No process event backend code exists in PUT for Solaris!  Please submit a patch!")
# define FALLBACK_ON_PROC_POLLING

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
# pragma message("No process event backend code exists in PUT for QNX!  Please submit a patch!")
# define FALLBACK_ON_PROC_POLLING

#else
# pragma message("No platform specific process event detection code! Falling back on polling /proc.")
# define FALLBACK_ON_PROC_POLLING
#endif

#if defined(FALLBACK_ON_PROC_POLLING)
#error TODO: implement process event detection code by polling /proc.
#endif
<|endoftext|>