<commit_before>/*************************************************************************
 *
 *  $RCSfile: mailconfigpage.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2004-09-20 13:22:05 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _MAILCONFIGPAGE_HXX
#define _MAILCONFIGPAGE_HXX

#ifndef _SFXTABDLG_HXX
#include <sfx2/tabdlg.hxx>
#endif
#ifndef _BUTTON_HXX
#include <vcl/button.hxx>
#endif
#ifndef _LSTBOX_HXX
#include <vcl/lstbox.hxx>
#endif
#ifndef _FIELD_HXX
#include <vcl/field.hxx>
#endif
#ifndef _FIXED_HXX
#include <vcl/fixed.hxx>
#endif
#ifndef _BASEDLGS_HXX
#include <sfx2/basedlgs.hxx>
#endif

class SwTestAccountSettingsDialog;
class SwMailMergeConfigItem;
/*-- 05.05.2004 16:45:45---------------------------------------------------

  -----------------------------------------------------------------------*/
class SwMailConfigPage : public SfxTabPage
{
    friend class SwTestAccountSettingsDialog;

    FixedLine       m_aIdentityFL;

    FixedText       m_aDisplayNameFT;
    Edit            m_aDisplayNameED;
    FixedText       m_aAddressFT;
    Edit            m_aAddressED;

    CheckBox        m_aReplyToCB;
    FixedText       m_aReplyToFT;
    Edit            m_aReplyToED;

    FixedLine       m_aSMTPFL;

    FixedText       m_aServerFT;
    Edit            m_aServerED;
    FixedText       m_aPortFT;
    NumericField    m_aPortNF;

    FixedText       m_aSecureFT;
    ListBox         m_aSecureLB;

    PushButton      m_aServerAuthenticationPB;

    FixedLine       m_aSeparatorFL;
    PushButton      m_aTestPB;

    SwMailMergeConfigItem*  m_pConfigItem;

    DECL_LINK(ReplyToHdl, CheckBox*);
    DECL_LINK(AuthenticationHdl, PushButton*);
    DECL_LINK(TestHdl, PushButton*);

public:
    SwMailConfigPage( Window* pParent, const SfxItemSet& rSet );
    ~SwMailConfigPage();

    static SfxTabPage*  Create( Window* pParent,
                                const SfxItemSet& rAttrSet);

    virtual BOOL        FillItemSet( SfxItemSet& rSet );
    virtual void        Reset( const SfxItemSet& rSet );

};

/*-- 18.08.2004 12:02:02---------------------------------------------------

  -----------------------------------------------------------------------*/
class SwMailConfigDlg : public SfxSingleTabDialog
{
public:

    SwMailConfigDlg( Window* pParent, SfxItemSet& rSet );
    ~SwMailConfigDlg();
};

#endif

<commit_msg>INTEGRATION: CWS os51 (1.2.234); FILE MERGED 2005/01/21 10:57:07 mbu 1.2.234.1: #i40672<commit_after>/*************************************************************************
 *
 *  $RCSfile: mailconfigpage.hxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: vg $ $Date: 2005-02-16 17:04:59 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/
#ifndef _MAILCONFIGPAGE_HXX
#define _MAILCONFIGPAGE_HXX

#ifndef _SFXTABDLG_HXX
#include <sfx2/tabdlg.hxx>
#endif
#ifndef _BUTTON_HXX
#include <vcl/button.hxx>
#endif
#ifndef _LSTBOX_HXX
#include <vcl/lstbox.hxx>
#endif
#ifndef _FIELD_HXX
#include <vcl/field.hxx>
#endif
#ifndef _FIXED_HXX
#include <vcl/fixed.hxx>
#endif
#ifndef _BASEDLGS_HXX
#include <sfx2/basedlgs.hxx>
#endif

class SwTestAccountSettingsDialog;
class SwMailMergeConfigItem;
/*-- 05.05.2004 16:45:45---------------------------------------------------

  -----------------------------------------------------------------------*/
class SwMailConfigPage : public SfxTabPage
{
    friend class SwTestAccountSettingsDialog;

    FixedLine       m_aIdentityFL;

    FixedText       m_aDisplayNameFT;
    Edit            m_aDisplayNameED;
    FixedText       m_aAddressFT;
    Edit            m_aAddressED;

    CheckBox        m_aReplyToCB;
    FixedText       m_aReplyToFT;
    Edit            m_aReplyToED;

    FixedLine       m_aSMTPFL;

    FixedText       m_aServerFT;
    Edit            m_aServerED;
    FixedText       m_aPortFT;
    NumericField    m_aPortNF;

    CheckBox        m_aSecureCB;

    PushButton      m_aServerAuthenticationPB;

    FixedLine       m_aSeparatorFL;
    PushButton      m_aTestPB;

    SwMailMergeConfigItem*  m_pConfigItem;

    DECL_LINK(ReplyToHdl, CheckBox*);
    DECL_LINK(AuthenticationHdl, PushButton*);
    DECL_LINK(TestHdl, PushButton*);

public:
    SwMailConfigPage( Window* pParent, const SfxItemSet& rSet );
    ~SwMailConfigPage();

    static SfxTabPage*  Create( Window* pParent,
                                const SfxItemSet& rAttrSet);

    virtual BOOL        FillItemSet( SfxItemSet& rSet );
    virtual void        Reset( const SfxItemSet& rSet );

};

/*-- 18.08.2004 12:02:02---------------------------------------------------

  -----------------------------------------------------------------------*/
class SwMailConfigDlg : public SfxSingleTabDialog
{
public:

    SwMailConfigDlg( Window* pParent, SfxItemSet& rSet );
    ~SwMailConfigDlg();
};

#endif

<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
//
// CoinVault
//
// signatureactions.h
//
// Copyright (c) 2013-2014 Eric Lombrozo
//
// All Rights Reserved.

#include "signatureactions.h"

#include "signaturemodel.h"
#include "signatureview.h"
#include "signaturedialog.h"
#include "passphrasedialog.h"

#include "entropysource.h"

#include <CoinDB/SynchedVault.h>
#include <CoinDB/Passphrase.h>

#include <QAction>
#include <QMenu>
#include <QMessageBox>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>

#include <logger/logger.h>

SignatureActions::SignatureActions(CoinDB::SynchedVault& synchedVault, SignatureDialog& dialog)
    : m_synchedVault(synchedVault), m_dialog(dialog), m_currentRow(-1)
{
    createActions();
    createMenus();
    connect(m_dialog.getView()->selectionModel(), &QItemSelectionModel::currentChanged, this, &SignatureActions::updateCurrentKeychain);
}

SignatureActions::~SignatureActions()
{
    delete addSignatureAction;
    delete unlockKeychainAction;
    delete lockKeychainAction;
    delete menu;
}

void SignatureActions::updateCurrentKeychain(const QModelIndex& current, const QModelIndex& /*previous*/)
{
    m_currentRow = current.row();
    refreshCurrentKeychain();
}

void SignatureActions::refreshCurrentKeychain()
{
    if (m_currentRow != -1)
    {
        QStandardItem* keychainNameItem = m_dialog.getModel()->item(m_currentRow, 0);
        m_currentKeychain = keychainNameItem->text();

        if (m_synchedVault.isVaultOpen())
        {
            int keychainState = m_dialog.getModel()->getKeychainState(m_currentRow);
            bool hasSigned = m_dialog.getModel()->getKeychainHasSigned(m_currentRow);
            int sigsNeeded = m_dialog.getModel()->getSigsNeeded();

            addSignatureAction->setEnabled(keychainState != SignatureModel::PUBLIC && !hasSigned && sigsNeeded > 0);
            unlockKeychainAction->setEnabled(keychainState == SignatureModel::LOCKED);
            lockKeychainAction->setEnabled(keychainState == SignatureModel::UNLOCKED);
            return;
        }
    }
    else
    {
        m_currentKeychain = "";
    }

    addSignatureAction->setEnabled(false);
    unlockKeychainAction->setEnabled(false);
    lockKeychainAction->setEnabled(false);
}

void SignatureActions::addSignature()
{
    try
    {
        if (m_currentKeychain.isEmpty()) throw std::runtime_error("No keychain is selected.");
        if (!m_synchedVault.isVaultOpen()) throw std::runtime_error("No vault is open.");

        CoinDB::Vault* vault = m_synchedVault.getVault();
        if (!vault->isKeychainPrivate(m_currentKeychain.toStdString()))
        {
            QMessageBox::critical(nullptr, tr("Error"), tr("Keychain is public."));
            return;
        }

        QString currentKeychain = m_currentKeychain;
        if (vault->isKeychainLocked(m_currentKeychain.toStdString()))
        {
            unlockKeychain();
        }

        std::vector<std::string> keychainNames;
        keychainNames.push_back(currentKeychain.toStdString());

        seedEntropySource(false, &m_dialog);
        vault->signTx(m_dialog.getModel()->getTxHash(), keychainNames, true);

        if (keychainNames.empty())
        {
            QMessageBox::critical(nullptr, tr("Error"), tr("Signature could not be added."));
            return;
        }

        m_dialog.updateTx();
    }
    catch (const std::exception& e)
    {
        emit error(e.what());
    }
}

void SignatureActions::unlockKeychain()
{
    try
    {
        if (m_currentKeychain.isEmpty()) throw std::runtime_error("No keychain is selected.");
        if (!m_synchedVault.isVaultOpen()) throw std::runtime_error("No vault is open.");

        CoinDB::Vault* vault = m_synchedVault.getVault();
        std::string keychainName = m_currentKeychain.toStdString();
        if (!vault->isKeychainLocked(keychainName))
        {
            QMessageBox::critical(nullptr, tr("Error"), tr("Keychain is already unlocked."));
            return;
        }

        secure_bytes_t lockKey;
        if (vault->isKeychainEncrypted(keychainName))
        {
            PassphraseDialog dlg(tr("Please enter the decryption passphrase for ") + m_currentKeychain + ":");
            if (!dlg.exec()) return;
            lockKey = passphraseHash(dlg.getPassphrase().toStdString());
        }

        vault->unlockKeychain(keychainName, lockKey);
        refreshCurrentKeychain();
        m_dialog.updateKeychains();
    }
    catch (const CoinDB::KeychainPrivateKeyUnlockFailedException& e)
    {
        emit error(tr("Keychain decryption failed."));
    }
    catch (const std::exception& e)
    {
        emit error(e.what());
    }
}

void SignatureActions::lockKeychain()
{
    try
    {
        if (m_currentKeychain.isEmpty()) throw std::runtime_error("No keychain is selected.");
        if (!m_synchedVault.isVaultOpen()) throw std::runtime_error("No vault is open.");

        CoinDB::Vault* vault = m_synchedVault.getVault();
        std::string keychainName = m_currentKeychain.toStdString();
        if (vault->isKeychainLocked(keychainName))
        {
            QMessageBox::critical(nullptr, tr("Error"), tr("Keychain is already locked."));
            return;
        }

        vault->lockKeychain(keychainName);
        refreshCurrentKeychain();
        m_dialog.updateKeychains();
    }
    catch (const std::exception& e)
    {
        emit error(e.what());
    }
}

void SignatureActions::createActions()
{
    addSignatureAction = new QAction(tr("Add signature..."), this);
    addSignatureAction->setEnabled(false);
    connect(addSignatureAction, SIGNAL(triggered()), this, SLOT(addSignature()));

    unlockKeychainAction = new QAction(tr("Unlock keychain..."), this);
    unlockKeychainAction->setEnabled(false);
    connect(unlockKeychainAction, SIGNAL(triggered()), this, SLOT(unlockKeychain()));

    lockKeychainAction = new QAction(tr("Lock keychain"), this);
    lockKeychainAction->setEnabled(false);
    connect(lockKeychainAction, SIGNAL(triggered()), this, SLOT(lockKeychain()));
}

void SignatureActions::createMenus()
{
    menu = new QMenu();
    menu->addAction(addSignatureAction);
    menu->addSeparator();
    menu->addAction(unlockKeychainAction);
    menu->addAction(lockKeychainAction);
}

<commit_msg>Prompt to send transaction after signing from SignatureDialog.<commit_after>///////////////////////////////////////////////////////////////////////////////
//
// CoinVault
//
// signatureactions.h
//
// Copyright (c) 2013-2014 Eric Lombrozo
//
// All Rights Reserved.

#include "signatureactions.h"

#include "signaturemodel.h"
#include "signatureview.h"
#include "signaturedialog.h"
#include "passphrasedialog.h"

#include "entropysource.h"

#include <CoinDB/SynchedVault.h>
#include <CoinDB/Passphrase.h>

#include <QAction>
#include <QMenu>
#include <QMessageBox>
#include <QApplication>
#include <QClipboard>
#include <QFileDialog>

#include <logger/logger.h>

SignatureActions::SignatureActions(CoinDB::SynchedVault& synchedVault, SignatureDialog& dialog)
    : m_synchedVault(synchedVault), m_dialog(dialog), m_currentRow(-1)
{
    createActions();
    createMenus();
    connect(m_dialog.getView()->selectionModel(), &QItemSelectionModel::currentChanged, this, &SignatureActions::updateCurrentKeychain);
}

SignatureActions::~SignatureActions()
{
    delete addSignatureAction;
    delete unlockKeychainAction;
    delete lockKeychainAction;
    delete menu;
}

void SignatureActions::updateCurrentKeychain(const QModelIndex& current, const QModelIndex& /*previous*/)
{
    m_currentRow = current.row();
    refreshCurrentKeychain();
}

void SignatureActions::refreshCurrentKeychain()
{
    if (m_currentRow != -1)
    {
        QStandardItem* keychainNameItem = m_dialog.getModel()->item(m_currentRow, 0);
        m_currentKeychain = keychainNameItem->text();

        if (m_synchedVault.isVaultOpen())
        {
            int keychainState = m_dialog.getModel()->getKeychainState(m_currentRow);
            bool hasSigned = m_dialog.getModel()->getKeychainHasSigned(m_currentRow);
            int sigsNeeded = m_dialog.getModel()->getSigsNeeded();

            addSignatureAction->setEnabled(keychainState != SignatureModel::PUBLIC && !hasSigned && sigsNeeded > 0);
            unlockKeychainAction->setEnabled(keychainState == SignatureModel::LOCKED);
            lockKeychainAction->setEnabled(keychainState == SignatureModel::UNLOCKED);
            return;
        }
    }
    else
    {
        m_currentKeychain = "";
    }

    addSignatureAction->setEnabled(false);
    unlockKeychainAction->setEnabled(false);
    lockKeychainAction->setEnabled(false);
}

void SignatureActions::addSignature()
{
    try
    {
        if (m_currentKeychain.isEmpty()) throw std::runtime_error("No keychain is selected.");
        if (!m_synchedVault.isVaultOpen()) throw std::runtime_error("No vault is open.");

        CoinDB::Vault* vault = m_synchedVault.getVault();
        if (!vault->isKeychainPrivate(m_currentKeychain.toStdString()))
        {
            QMessageBox::critical(nullptr, tr("Error"), tr("Keychain is public."));
            return;
        }

        QString currentKeychain = m_currentKeychain;
        if (vault->isKeychainLocked(m_currentKeychain.toStdString()))
        {
            unlockKeychain();
        }

        std::vector<std::string> keychainNames;
        keychainNames.push_back(currentKeychain.toStdString());

        seedEntropySource(false, &m_dialog);
        bytes_t txhash = m_dialog.getModel()->getTxHash();
        vault->signTx(txhash, keychainNames, true);

        if (keychainNames.empty())
        {
            QMessageBox::critical(nullptr, tr("Error"), tr("Signature could not be added."));
            return;
        }

        m_dialog.updateTx();
        if (m_synchedVault.isConnected() && m_dialog.getModel()->getSigsNeeded() == 0)
        {
            QMessageBox sendPrompt;
            sendPrompt.setText(tr("Transaction is fully signed. Would you like to send?"));
            sendPrompt.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
            sendPrompt.setDefaultButton(QMessageBox::Yes);
            if (sendPrompt.exec() == QMessageBox::Yes)
            {
                if (!m_synchedVault.isConnected())
                {
                    QMessageBox::critical(nullptr, tr("Error"), tr("Connection was lost."));
                    return;
                }
                std::shared_ptr<CoinDB::Tx> tx = vault->getTx(txhash);
                Coin::Transaction coin_tx = tx->toCoinCore();
                m_synchedVault.sendTx(coin_tx);
            }
        }
    }
    catch (const std::exception& e)
    {
        emit error(e.what());
    }
}

void SignatureActions::unlockKeychain()
{
    try
    {
        if (m_currentKeychain.isEmpty()) throw std::runtime_error("No keychain is selected.");
        if (!m_synchedVault.isVaultOpen()) throw std::runtime_error("No vault is open.");

        CoinDB::Vault* vault = m_synchedVault.getVault();
        std::string keychainName = m_currentKeychain.toStdString();
        if (!vault->isKeychainLocked(keychainName))
        {
            QMessageBox::critical(nullptr, tr("Error"), tr("Keychain is already unlocked."));
            return;
        }

        secure_bytes_t lockKey;
        if (vault->isKeychainEncrypted(keychainName))
        {
            PassphraseDialog dlg(tr("Please enter the decryption passphrase for ") + m_currentKeychain + ":");
            if (!dlg.exec()) return;
            lockKey = passphraseHash(dlg.getPassphrase().toStdString());
        }

        vault->unlockKeychain(keychainName, lockKey);
        refreshCurrentKeychain();
        m_dialog.updateKeychains();
    }
    catch (const CoinDB::KeychainPrivateKeyUnlockFailedException& e)
    {
        emit error(tr("Keychain decryption failed."));
    }
    catch (const std::exception& e)
    {
        emit error(e.what());
    }
}

void SignatureActions::lockKeychain()
{
    try
    {
        if (m_currentKeychain.isEmpty()) throw std::runtime_error("No keychain is selected.");
        if (!m_synchedVault.isVaultOpen()) throw std::runtime_error("No vault is open.");

        CoinDB::Vault* vault = m_synchedVault.getVault();
        std::string keychainName = m_currentKeychain.toStdString();
        if (vault->isKeychainLocked(keychainName))
        {
            QMessageBox::critical(nullptr, tr("Error"), tr("Keychain is already locked."));
            return;
        }

        vault->lockKeychain(keychainName);
        refreshCurrentKeychain();
        m_dialog.updateKeychains();
    }
    catch (const std::exception& e)
    {
        emit error(e.what());
    }
}

void SignatureActions::createActions()
{
    addSignatureAction = new QAction(tr("Add signature..."), this);
    addSignatureAction->setEnabled(false);
    connect(addSignatureAction, SIGNAL(triggered()), this, SLOT(addSignature()));

    unlockKeychainAction = new QAction(tr("Unlock keychain..."), this);
    unlockKeychainAction->setEnabled(false);
    connect(unlockKeychainAction, SIGNAL(triggered()), this, SLOT(unlockKeychain()));

    lockKeychainAction = new QAction(tr("Lock keychain"), this);
    lockKeychainAction->setEnabled(false);
    connect(lockKeychainAction, SIGNAL(triggered()), this, SLOT(lockKeychain()));
}

void SignatureActions::createMenus()
{
    menu = new QMenu();
    menu->addAction(addSignatureAction);
    menu->addSeparator();
    menu->addAction(unlockKeychainAction);
    menu->addAction(lockKeychainAction);
}

<|endoftext|>


<commit_before>// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "backend/d3d12/BufferD3D12.h"

#include "backend/d3d12/D3D12Backend.h"
#include "backend/d3d12/ResourceAllocator.h"
#include "backend/d3d12/ResourceUploader.h"
#include "common/Assert.h"

namespace backend {
namespace d3d12 {

    namespace {
        D3D12_RESOURCE_STATES D3D12BufferUsage(nxt::BufferUsageBit usage) {
            D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON;

            if (usage & nxt::BufferUsageBit::TransferSrc) {
                resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE;
            }
            if (usage & nxt::BufferUsageBit::TransferDst) {
                resourceState |= D3D12_RESOURCE_STATE_COPY_DEST;
            }
            if (usage & (nxt::BufferUsageBit::Vertex | nxt::BufferUsageBit::Uniform)) {
                resourceState |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
            }
            if (usage & nxt::BufferUsageBit::Index) {
                resourceState |= D3D12_RESOURCE_STATE_INDEX_BUFFER;
            }
            if (usage & nxt::BufferUsageBit::Storage) {
                resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
            }

            return resourceState;
        }

        D3D12_HEAP_TYPE D3D12HeapType(nxt::BufferUsageBit allowedUsage) {
            if (allowedUsage & nxt::BufferUsageBit::MapRead) {
                return D3D12_HEAP_TYPE_READBACK;
            } else if (allowedUsage & nxt::BufferUsageBit::MapWrite) {
                return D3D12_HEAP_TYPE_UPLOAD;
            } else {
                return D3D12_HEAP_TYPE_DEFAULT;
            }
        }
    }

    Buffer::Buffer(Device* device, BufferBuilder* builder)
        : BufferBase(builder), device(device) {

        D3D12_RESOURCE_DESC resourceDescriptor;
        resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
        resourceDescriptor.Alignment = 0;
        resourceDescriptor.Width = GetD3D12Size();
        resourceDescriptor.Height = 1;
        resourceDescriptor.DepthOrArraySize = 1;
        resourceDescriptor.MipLevels = 1;
        resourceDescriptor.Format = DXGI_FORMAT_UNKNOWN;
        resourceDescriptor.SampleDesc.Count = 1;
        resourceDescriptor.SampleDesc.Quality = 0;
        resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
        resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE;

        auto heapType = D3D12HeapType(GetAllowedUsage());
        auto bufferUsage = D3D12BufferUsage(GetUsage());

        // D3D12 requires buffers on the READBACK heap to have the D3D12_RESOURCE_STATE_COPY_DEST state
        if (heapType == D3D12_HEAP_TYPE_READBACK) {
            bufferUsage |= D3D12_RESOURCE_STATE_COPY_DEST;
        }

        // D3D12 requires buffers on the UPLOAD heap to have the D3D12_RESOURCE_STATE_GENERIC_READ state
        if (heapType == D3D12_HEAP_TYPE_UPLOAD) {
            bufferUsage |= D3D12_RESOURCE_STATE_GENERIC_READ;
        }

        resource = device->GetResourceAllocator()->Allocate(heapType, resourceDescriptor, bufferUsage);
    }

    Buffer::~Buffer() {
        device->GetResourceAllocator()->Release(resource);
    }

    uint32_t Buffer::GetD3D12Size() const {
        // TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level
        return ((GetSize() + 256 - 1) / 256) * 256; // size is required to be 256-byte aligned.
    }

    ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() {
        return resource;
    }

    bool Buffer::GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage, D3D12_RESOURCE_BARRIER* barrier) {
        if (GetAllowedUsage() & (nxt::BufferUsageBit::MapRead | nxt::BufferUsageBit::MapWrite)) {
            // Transitions are never needed for mapped buffers because they are created with and always need the Transfer(Dst|Src) state.
            // Mapped buffers cannot have states outside of (MapRead|TransferDst) and (MapWrite|TransferSrc)
            return false;
        }

        D3D12_RESOURCE_STATES stateBefore = D3D12BufferUsage(currentUsage);
        D3D12_RESOURCE_STATES stateAfter = D3D12BufferUsage(targetUsage);

        if (stateBefore == stateAfter) {
            return false;
        }

        barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
        barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
        barrier->Transition.pResource = resource.Get();
        barrier->Transition.StateBefore = stateBefore;
        barrier->Transition.StateAfter = stateAfter;
        barrier->Transition.Subresource = 0;

        return true;
    }

    D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const {
        return resource->GetGPUVirtualAddress();
    }

    void Buffer::OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data) {
        CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_READ_STATUS_SUCCESS, data);
    }

    void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) {
        device->GetResourceUploader()->BufferSubData(resource, start * sizeof(uint32_t), count * sizeof(uint32_t), data);
    }

    void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
        D3D12_RANGE readRange = { start, start + count };
        char* data = nullptr;
        ASSERT_SUCCESS(resource->Map(0, &readRange, reinterpret_cast<void**>(&data)));

        MapReadRequestTracker* tracker = ToBackend(GetDevice())->GetMapReadRequestTracker();
        tracker->Track(this, serial, data);
    }

    void Buffer::UnmapImpl() {
        // TODO(enga@google.com): When MapWrite is implemented, this should state the range that was modified
        D3D12_RANGE writeRange = {};
        resource->Unmap(0, &writeRange);
        device->GetResourceAllocator()->Release(resource);
    }

    void Buffer::TransitionUsageImpl(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage) {
        D3D12_RESOURCE_BARRIER barrier;
        if (GetResourceTransitionBarrier(currentUsage, targetUsage, &barrier)) {
            device->GetPendingCommandList()->ResourceBarrier(1, &barrier);
        }
    }

    BufferView::BufferView(BufferViewBuilder* builder)
        : BufferViewBase(builder) {

        cbvDesc.BufferLocation = ToBackend(GetBuffer())->GetVA() + GetOffset();
        cbvDesc.SizeInBytes = GetD3D12Size();

        uavDesc.Format = DXGI_FORMAT_UNKNOWN;
        uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uavDesc.Buffer.FirstElement = GetOffset();
        uavDesc.Buffer.NumElements = GetD3D12Size();
        uavDesc.Buffer.StructureByteStride = 1;
        uavDesc.Buffer.CounterOffsetInBytes = 0;
        uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    }

    uint32_t BufferView::GetD3D12Size() const {
        // TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level
        return ((GetSize() + 256 - 1) / 256) * 256; // size is required to be 256-byte aligned.
    }

    const D3D12_CONSTANT_BUFFER_VIEW_DESC& BufferView::GetCBVDescriptor() const {
        return cbvDesc;
    }

    const D3D12_UNORDERED_ACCESS_VIEW_DESC& BufferView::GetUAVDescriptor() const {
        return uavDesc;
    }

    MapReadRequestTracker::MapReadRequestTracker(Device* device)
        : device(device) {
    }

    MapReadRequestTracker::~MapReadRequestTracker() {
        ASSERT(inflightRequests.Empty());
    }

    void MapReadRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, const void* data) {
        Request request;
        request.buffer = buffer;
        request.mapSerial = mapSerial;
        request.data = data;

        inflightRequests.Enqueue(std::move(request), device->GetSerial());
    }

    void MapReadRequestTracker::Tick(Serial finishedSerial) {
        for (auto& request : inflightRequests.IterateUpTo(finishedSerial)) {
            request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data);
        }
        inflightRequests.ClearUpTo(finishedSerial);
    }

}
}
<commit_msg>Update BufferD3D12 to use Align helper<commit_after>// Copyright 2017 The NXT Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "backend/d3d12/BufferD3D12.h"

#include "backend/d3d12/D3D12Backend.h"
#include "backend/d3d12/ResourceAllocator.h"
#include "backend/d3d12/ResourceUploader.h"
#include "common/Assert.h"
#include "common/Constants.h"
#include "common/Math.h"

namespace backend {
namespace d3d12 {

    namespace {
        D3D12_RESOURCE_STATES D3D12BufferUsage(nxt::BufferUsageBit usage) {
            D3D12_RESOURCE_STATES resourceState = D3D12_RESOURCE_STATE_COMMON;

            if (usage & nxt::BufferUsageBit::TransferSrc) {
                resourceState |= D3D12_RESOURCE_STATE_COPY_SOURCE;
            }
            if (usage & nxt::BufferUsageBit::TransferDst) {
                resourceState |= D3D12_RESOURCE_STATE_COPY_DEST;
            }
            if (usage & (nxt::BufferUsageBit::Vertex | nxt::BufferUsageBit::Uniform)) {
                resourceState |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
            }
            if (usage & nxt::BufferUsageBit::Index) {
                resourceState |= D3D12_RESOURCE_STATE_INDEX_BUFFER;
            }
            if (usage & nxt::BufferUsageBit::Storage) {
                resourceState |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
            }

            return resourceState;
        }

        D3D12_HEAP_TYPE D3D12HeapType(nxt::BufferUsageBit allowedUsage) {
            if (allowedUsage & nxt::BufferUsageBit::MapRead) {
                return D3D12_HEAP_TYPE_READBACK;
            } else if (allowedUsage & nxt::BufferUsageBit::MapWrite) {
                return D3D12_HEAP_TYPE_UPLOAD;
            } else {
                return D3D12_HEAP_TYPE_DEFAULT;
            }
        }
    }

    Buffer::Buffer(Device* device, BufferBuilder* builder)
        : BufferBase(builder), device(device) {

        D3D12_RESOURCE_DESC resourceDescriptor;
        resourceDescriptor.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
        resourceDescriptor.Alignment = 0;
        resourceDescriptor.Width = GetD3D12Size();
        resourceDescriptor.Height = 1;
        resourceDescriptor.DepthOrArraySize = 1;
        resourceDescriptor.MipLevels = 1;
        resourceDescriptor.Format = DXGI_FORMAT_UNKNOWN;
        resourceDescriptor.SampleDesc.Count = 1;
        resourceDescriptor.SampleDesc.Quality = 0;
        resourceDescriptor.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
        resourceDescriptor.Flags = D3D12_RESOURCE_FLAG_NONE;

        auto heapType = D3D12HeapType(GetAllowedUsage());
        auto bufferUsage = D3D12BufferUsage(GetUsage());

        // D3D12 requires buffers on the READBACK heap to have the D3D12_RESOURCE_STATE_COPY_DEST state
        if (heapType == D3D12_HEAP_TYPE_READBACK) {
            bufferUsage |= D3D12_RESOURCE_STATE_COPY_DEST;
        }

        // D3D12 requires buffers on the UPLOAD heap to have the D3D12_RESOURCE_STATE_GENERIC_READ state
        if (heapType == D3D12_HEAP_TYPE_UPLOAD) {
            bufferUsage |= D3D12_RESOURCE_STATE_GENERIC_READ;
        }

        resource = device->GetResourceAllocator()->Allocate(heapType, resourceDescriptor, bufferUsage);
    }

    Buffer::~Buffer() {
        device->GetResourceAllocator()->Release(resource);
    }

    uint32_t Buffer::GetD3D12Size() const {
        // TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level
        return Align(GetSize(), 256);
    }

    ComPtr<ID3D12Resource> Buffer::GetD3D12Resource() {
        return resource;
    }

    bool Buffer::GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage, D3D12_RESOURCE_BARRIER* barrier) {
        if (GetAllowedUsage() & (nxt::BufferUsageBit::MapRead | nxt::BufferUsageBit::MapWrite)) {
            // Transitions are never needed for mapped buffers because they are created with and always need the Transfer(Dst|Src) state.
            // Mapped buffers cannot have states outside of (MapRead|TransferDst) and (MapWrite|TransferSrc)
            return false;
        }

        D3D12_RESOURCE_STATES stateBefore = D3D12BufferUsage(currentUsage);
        D3D12_RESOURCE_STATES stateAfter = D3D12BufferUsage(targetUsage);

        if (stateBefore == stateAfter) {
            return false;
        }

        barrier->Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
        barrier->Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
        barrier->Transition.pResource = resource.Get();
        barrier->Transition.StateBefore = stateBefore;
        barrier->Transition.StateAfter = stateAfter;
        barrier->Transition.Subresource = 0;

        return true;
    }

    D3D12_GPU_VIRTUAL_ADDRESS Buffer::GetVA() const {
        return resource->GetGPUVirtualAddress();
    }

    void Buffer::OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data) {
        CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_READ_STATUS_SUCCESS, data);
    }

    void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) {
        device->GetResourceUploader()->BufferSubData(resource, start * sizeof(uint32_t), count * sizeof(uint32_t), data);
    }

    void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
        D3D12_RANGE readRange = { start, start + count };
        char* data = nullptr;
        ASSERT_SUCCESS(resource->Map(0, &readRange, reinterpret_cast<void**>(&data)));

        MapReadRequestTracker* tracker = ToBackend(GetDevice())->GetMapReadRequestTracker();
        tracker->Track(this, serial, data);
    }

    void Buffer::UnmapImpl() {
        // TODO(enga@google.com): When MapWrite is implemented, this should state the range that was modified
        D3D12_RANGE writeRange = {};
        resource->Unmap(0, &writeRange);
        device->GetResourceAllocator()->Release(resource);
    }

    void Buffer::TransitionUsageImpl(nxt::BufferUsageBit currentUsage, nxt::BufferUsageBit targetUsage) {
        D3D12_RESOURCE_BARRIER barrier;
        if (GetResourceTransitionBarrier(currentUsage, targetUsage, &barrier)) {
            device->GetPendingCommandList()->ResourceBarrier(1, &barrier);
        }
    }

    BufferView::BufferView(BufferViewBuilder* builder)
        : BufferViewBase(builder) {

        cbvDesc.BufferLocation = ToBackend(GetBuffer())->GetVA() + GetOffset();
        cbvDesc.SizeInBytes = GetD3D12Size();

        uavDesc.Format = DXGI_FORMAT_UNKNOWN;
        uavDesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER;
        uavDesc.Buffer.FirstElement = GetOffset();
        uavDesc.Buffer.NumElements = GetD3D12Size();
        uavDesc.Buffer.StructureByteStride = 1;
        uavDesc.Buffer.CounterOffsetInBytes = 0;
        uavDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE;
    }

    uint32_t BufferView::GetD3D12Size() const {
        // TODO(enga@google.com): TODO investigate if this needs to be a constraint at the API level
        return Align(GetSize(), 256);
    }

    const D3D12_CONSTANT_BUFFER_VIEW_DESC& BufferView::GetCBVDescriptor() const {
        return cbvDesc;
    }

    const D3D12_UNORDERED_ACCESS_VIEW_DESC& BufferView::GetUAVDescriptor() const {
        return uavDesc;
    }

    MapReadRequestTracker::MapReadRequestTracker(Device* device)
        : device(device) {
    }

    MapReadRequestTracker::~MapReadRequestTracker() {
        ASSERT(inflightRequests.Empty());
    }

    void MapReadRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, const void* data) {
        Request request;
        request.buffer = buffer;
        request.mapSerial = mapSerial;
        request.data = data;

        inflightRequests.Enqueue(std::move(request), device->GetSerial());
    }

    void MapReadRequestTracker::Tick(Serial finishedSerial) {
        for (auto& request : inflightRequests.IterateUpTo(finishedSerial)) {
            request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data);
        }
        inflightRequests.ClearUpTo(finishedSerial);
    }

}
}
<|endoftext|>


<commit_before>/*
 * File:   virusscan.c
 *
 * Copyright 2013 Heinrich Schuchardt <xypron.glpk@gmx.de>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file VirusScan.cc
 * @brief Scans files for viruses.
 */
#include <cstring>
#include <ctime>
#include <limits.h>
#include <signal.h>
#include <sstream>
#include <stdio.h>
#include <syslog.h>
#include "unistd.h"
#include "VirusScan.h"
#include "Messaging.h"

/**
 * @brief Initializes virus scan engine.
 */
VirusScan::VirusScan(Environment * e) {
    int ret;

    env = e;
    status = RUNNING;
    engineRefCount = 0;
    pthread_mutex_init(&mutexEngine, NULL);
    pthread_mutex_init(&mutexUpdate, NULL);

    ret = cl_init(CL_INIT_DEFAULT);
    if (ret != CL_SUCCESS) {
        std::stringstream msg;
        msg << "cl_init() error: " << cl_strerror(ret);
        Messaging::message(Messaging::ERROR, msg.str());
        throw SCANERROR;
    }

    // Create virus scan engine.
    engine = createEngine();
    // Initialize monitoring of pattern update.
    dbstat_clear();

    if (createThread()) {
        Messaging::message(Messaging::ERROR, "Cannot create thread.");
        cl_engine_free(engine);
        throw SCANERROR;
    }
}

/**
 * @brief Creates a new virus scan engine.
 *
 * @return virus scan engine
 */
struct cl_engine *VirusScan::createEngine() {
    int ret;
    unsigned int sigs;
    cl_engine *e;

    Messaging::message(Messaging::DEBUG, "Loading virus database");
    e = cl_engine_new();
    if (e == NULL) {
        Messaging::message(Messaging::ERROR,
                "Can't create new virus scan engine.");
        throw SCANERROR;
    }
    // sigs must be zero before calling cl_load.
    sigs = 0;
    ret = cl_load(cl_retdbdir(), e, &sigs, CL_DB_STDOPT);
    if (ret != CL_SUCCESS) {
        std::stringstream msg;
        msg << "cl_retdbdir() error: " << cl_strerror(ret);
        Messaging::message(Messaging::ERROR, msg.str());
        cl_engine_free(e);
        throw SCANERROR;
    } else {
        std::stringstream msg;
        msg << sigs << "  signatures loaded";
        Messaging::message(Messaging::DEBUG, msg.str());
    }
    if ((ret = cl_engine_compile(e)) != CL_SUCCESS) {
        std::stringstream msg;
        msg << "cl_engine_compile() error: " << cl_strerror(ret);
        Messaging::message(Messaging::ERROR, msg.str());
        cl_engine_free(e);
        throw SCANERROR;
    }
    {
        int err;
        time_t db_time;
        struct tm *timeinfo;
        uint version;
        std::stringstream msg;
        char buffer[80];

        do {
            version = (uint) cl_engine_get_num(e, CL_ENGINE_DB_VERSION, &err);
            if (err != CL_SUCCESS) {
                break;
            }
            db_time = (time_t) cl_engine_get_num(e, CL_ENGINE_DB_TIME, &err);
            if (err != CL_SUCCESS) {
                break;
            }
            timeinfo = gmtime(&db_time);
            strftime(buffer, sizeof (buffer), "%F %T UTC", timeinfo);
            msg << "ClamAV database version " << version << ", " << buffer;
            Messaging::message(Messaging::INFORMATION, msg.str());
        } while (0);
    }
    return e;
}

/**
 * Destroys virus scan engine.
 * @param e virus scan engine
 */
void VirusScan::destroyEngine(cl_engine * e) {
    int ret;
    ret = cl_engine_free(e);
    if (ret != 0) {
        std::stringstream msg;
        msg << "cl_engine_free() error: " << cl_strerror(ret);
        Messaging::message(Messaging::ERROR, msg.str());
        throw SCANERROR;
    }
}

/**
 * @brief Gets reference to virus scan engine.
 *
 * @return scan engine
 */
struct cl_engine * VirusScan::getEngine() {
    struct cl_engine *ret = NULL;

    // Wait for update to complete
    pthread_mutex_lock(&mutexUpdate);
    pthread_mutex_unlock(&mutexUpdate);

    pthread_mutex_lock(&mutexEngine);
    ret = engine;
    engineRefCount++;
    pthread_mutex_unlock(&mutexEngine);
    return ret;
}

/**
 * @brief Creates a new thread for managing the scan engine.
 *
 * @return success = 0
 */
int VirusScan::createThread() {
    int ret;

    if (pthread_create(&updateThread, NULL, updater, this)) {
        ret = 1;
    } else {
        ret = 0;
    }
    return ret;
}

/**
 * @brief Checks if database has changed.
 * @returned 0 = unchanged, 1 = changed
 */
int VirusScan::dbstat_check() {
    int ret = 0;
    if (cl_statchkdir(&dbstat) == 1) {
        ret = 1;
        cl_statfree(&dbstat);
        cl_statinidir(cl_retdbdir(), &dbstat);
    }
    return ret;
}

/**
 * @brief Clears database status.
 */
void VirusScan::dbstat_clear() {
    memset(&dbstat, 0, sizeof (struct cl_stat));
    cl_statinidir(cl_retdbdir(), &dbstat);
}

/**
 * @brief Frees database status.
 */
void VirusScan::dbstat_free() {
    cl_statfree(&dbstat);
}

/**
 * @brief Writes log entry.
 *
 * @param fd file descriptor
 * @param virname name of virus
 */
void VirusScan::log_virus_found(const int fd, const char *virname) {
    int path_len;
    char path[PATH_MAX + 1];
    std::stringstream msg;

    snprintf(path, sizeof (path), "/proc/self/fd/%d", fd);
    path_len = readlink(path, path, sizeof (path) - 1);
    if (path_len < 0) {
        path_len = 0;
    }
    path[path_len] = '\0';
    msg << "Virus \"" << virname << "\" detected in file \"" << path << "\".";
    Messaging::message(Messaging::ERROR, msg.str());
}

/**
 * Decreases the viurs engine reference count.
 */
void VirusScan::releaseEngine() {
    pthread_mutex_lock(&mutexEngine);
    engineRefCount--;
    pthread_mutex_unlock(&mutexEngine);
}

/**
 * @brief Scans file for virus.
 *
 * @return success
 */
int VirusScan::scan(const int fd) {
    int success = SCANOK;
    int ret;
    const char *virname;

    ret = cl_scandesc(fd, &virname, NULL, getEngine(), CL_SCAN_STDOPT);
    switch (ret) {
        case CL_CLEAN:
            success = SCANOK;
            break;
        case CL_VIRUS:
            log_virus_found(fd, virname);
            success = SCANVIRUS;
            break;
        default:
            std::stringstream msg;
            msg << "cl_scandesc() error: " << cl_strerror(ret);
            Messaging::message(Messaging::ERROR, msg.str());
            success = SCANOK;
            break;
    }
    releaseEngine();
    return success;
}

/**
 * @brief Thread to update engine.
 *
 * @param threadPool thread pool
 * @return return value
 */
void * VirusScan::updater(void *virusScan) {
    int count = 0;
    cl_engine *e;
    struct timespec interval = {
        1,
        0
    };
    struct timespec interval2 = {
        0,
        100000
    };
    VirusScan *vs;

    vs = (VirusScan *) virusScan;

    for (;;) {
        if (vs->status == STOPPING) {
            break;
        }
        nanosleep(&interval, NULL);
        count++;
        // Every minute check for virus database updates
        if (count >= 60) {
            if (vs->dbstat_check()) {
                Messaging::message(Messaging::INFORMATION,
                        "ClamAV database update detected.");
                try {
                    // Create the new engine.
                    e = vs->createEngine();
                    // Stop scanning.
                    pthread_mutex_lock(&(vs->mutexUpdate));
                    // Wait for all running scans to be finished.
                    for (;;) {
                        pthread_mutex_lock(&(vs->mutexEngine));
                        if (vs->engineRefCount == 0) {
                            break;
                        }
                        pthread_mutex_unlock(&(vs->mutexEngine));
                        nanosleep(&interval2, NULL);
                    }
                    // Destroy the old engine
                    vs->destroyEngine(vs->engine);
                    vs->engine = e;
                    vs->env->getScanCache()->clear();
                    pthread_mutex_unlock(&(vs->mutexEngine));
                    Messaging::message(Messaging::INFORMATION,
                            "Using updated ClamAV database.");
                } catch (Status e) {
                }
                // Allow scanning.
                pthread_mutex_unlock(&(vs->mutexUpdate));
            }
        }
    }
    vs->status = STOPPED;
    return NULL;
}

/**
 * @brief Deletes the virus scanner.
 */
VirusScan::~VirusScan() {
    struct timespec interval = {
        0,
        1000000
    };

    status = STOPPING;
    do {
        nanosleep(&interval, NULL);
    } while (status == STOPPING);

    destroyEngine(engine);
    pthread_mutex_destroy(&mutexUpdate);
    pthread_mutex_destroy(&mutexEngine);
    dbstat_free();
}
<commit_msg>Correct usage of update mutex in VirusScan<commit_after>/*
 * File:   virusscan.c
 *
 * Copyright 2013 Heinrich Schuchardt <xypron.glpk@gmx.de>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file VirusScan.cc
 * @brief Scans files for viruses.
 */
#include <cstring>
#include <ctime>
#include <limits.h>
#include <signal.h>
#include <sstream>
#include <stdio.h>
#include <syslog.h>
#include "unistd.h"
#include "VirusScan.h"
#include "Messaging.h"

/**
 * @brief Initializes virus scan engine.
 */
VirusScan::VirusScan(Environment * e) {
    int ret;

    env = e;
    status = RUNNING;
    engineRefCount = 0;
    pthread_mutex_init(&mutexEngine, NULL);
    pthread_mutex_init(&mutexUpdate, NULL);

    ret = cl_init(CL_INIT_DEFAULT);
    if (ret != CL_SUCCESS) {
        std::stringstream msg;
        msg << "cl_init() error: " << cl_strerror(ret);
        Messaging::message(Messaging::ERROR, msg.str());
        throw SCANERROR;
    }

    // Create virus scan engine.
    engine = createEngine();
    // Initialize monitoring of pattern update.
    dbstat_clear();

    if (createThread()) {
        Messaging::message(Messaging::ERROR, "Cannot create thread.");
        cl_engine_free(engine);
        throw SCANERROR;
    }
}

/**
 * @brief Creates a new virus scan engine.
 *
 * @return virus scan engine
 */
struct cl_engine *VirusScan::createEngine() {
    int ret;
    unsigned int sigs;
    cl_engine *e;

    Messaging::message(Messaging::DEBUG, "Loading virus database");
    e = cl_engine_new();
    if (e == NULL) {
        Messaging::message(Messaging::ERROR,
                "Can't create new virus scan engine.");
        throw SCANERROR;
    }
    // sigs must be zero before calling cl_load.
    sigs = 0;
    ret = cl_load(cl_retdbdir(), e, &sigs, CL_DB_STDOPT);
    if (ret != CL_SUCCESS) {
        std::stringstream msg;
        msg << "cl_retdbdir() error: " << cl_strerror(ret);
        Messaging::message(Messaging::ERROR, msg.str());
        cl_engine_free(e);
        throw SCANERROR;
    } else {
        std::stringstream msg;
        msg << sigs << "  signatures loaded";
        Messaging::message(Messaging::DEBUG, msg.str());
    }
    if ((ret = cl_engine_compile(e)) != CL_SUCCESS) {
        std::stringstream msg;
        msg << "cl_engine_compile() error: " << cl_strerror(ret);
        Messaging::message(Messaging::ERROR, msg.str());
        cl_engine_free(e);
        throw SCANERROR;
    }
    {
        int err;
        time_t db_time;
        struct tm *timeinfo;
        uint version;
        std::stringstream msg;
        char buffer[80];

        do {
            version = (uint) cl_engine_get_num(e, CL_ENGINE_DB_VERSION, &err);
            if (err != CL_SUCCESS) {
                break;
            }
            db_time = (time_t) cl_engine_get_num(e, CL_ENGINE_DB_TIME, &err);
            if (err != CL_SUCCESS) {
                break;
            }
            timeinfo = gmtime(&db_time);
            strftime(buffer, sizeof (buffer), "%F %T UTC", timeinfo);
            msg << "ClamAV database version " << version << ", " << buffer;
            Messaging::message(Messaging::INFORMATION, msg.str());
        } while (0);
    }
    return e;
}

/**
 * Destroys virus scan engine.
 * @param e virus scan engine
 */
void VirusScan::destroyEngine(cl_engine * e) {
    int ret;
    ret = cl_engine_free(e);
    if (ret != 0) {
        std::stringstream msg;
        msg << "cl_engine_free() error: " << cl_strerror(ret);
        Messaging::message(Messaging::ERROR, msg.str());
        throw SCANERROR;
    }
}

/**
 * @brief Gets reference to virus scan engine.
 *
 * @return scan engine
 */
struct cl_engine * VirusScan::getEngine() {
    struct cl_engine *ret = NULL;

    // Wait for update to complete
    pthread_mutex_lock(&mutexUpdate);
    pthread_mutex_lock(&mutexEngine);

    ret = engine;
    // Increase reference count.
    engineRefCount++;

    pthread_mutex_unlock(&mutexEngine);
    pthread_mutex_unlock(&mutexUpdate);
    return ret;
}

/**
 * @brief Creates a new thread for managing the scan engine.
 *
 * @return success = 0
 */
int VirusScan::createThread() {
    int ret;

    if (pthread_create(&updateThread, NULL, updater, this)) {
        ret = 1;
    } else {
        ret = 0;
    }
    return ret;
}

/**
 * @brief Checks if database has changed.
 * @returned 0 = unchanged, 1 = changed
 */
int VirusScan::dbstat_check() {
    int ret = 0;
    if (cl_statchkdir(&dbstat) == 1) {
        ret = 1;
        cl_statfree(&dbstat);
        cl_statinidir(cl_retdbdir(), &dbstat);
    }
    return ret;
}

/**
 * @brief Clears database status.
 */
void VirusScan::dbstat_clear() {
    memset(&dbstat, 0, sizeof (struct cl_stat));
    cl_statinidir(cl_retdbdir(), &dbstat);
}

/**
 * @brief Frees database status.
 */
void VirusScan::dbstat_free() {
    cl_statfree(&dbstat);
}

/**
 * @brief Writes log entry.
 *
 * @param fd file descriptor
 * @param virname name of virus
 */
void VirusScan::log_virus_found(const int fd, const char *virname) {
    int path_len;
    char path[PATH_MAX + 1];
    std::stringstream msg;

    snprintf(path, sizeof (path), "/proc/self/fd/%d", fd);
    path_len = readlink(path, path, sizeof (path) - 1);
    if (path_len < 0) {
        path_len = 0;
    }
    path[path_len] = '\0';
    msg << "Virus \"" << virname << "\" detected in file \"" << path << "\".";
    Messaging::message(Messaging::ERROR, msg.str());
}

/**
 * Decreases the viurs engine reference count.
 */
void VirusScan::releaseEngine() {
    pthread_mutex_lock(&mutexEngine);
    engineRefCount--;
    pthread_mutex_unlock(&mutexEngine);
}

/**
 * @brief Scans file for virus.
 *
 * @return success
 */
int VirusScan::scan(const int fd) {
    int success = SCANOK;
    int ret;
    const char *virname;

    ret = cl_scandesc(fd, &virname, NULL, getEngine(), CL_SCAN_STDOPT);
    switch (ret) {
        case CL_CLEAN:
            success = SCANOK;
            break;
        case CL_VIRUS:
            log_virus_found(fd, virname);
            success = SCANVIRUS;
            break;
        default:
            std::stringstream msg;
            msg << "cl_scandesc() error: " << cl_strerror(ret);
            Messaging::message(Messaging::ERROR, msg.str());
            success = SCANOK;
            break;
    }
    releaseEngine();
    return success;
}

/**
 * @brief Thread to update engine.
 *
 * @param threadPool thread pool
 * @return return value
 */
void * VirusScan::updater(void *virusScan) {
    int count = 0;
    cl_engine *e;
    struct timespec interval = {
        1,
        0
    };
    struct timespec interval2 = {
        0,
        100000
    };
    VirusScan *vs;

    vs = (VirusScan *) virusScan;

    for (;;) {
        if (vs->status == STOPPING) {
            break;
        }
        nanosleep(&interval, NULL);
        count++;
        // Every minute check for virus database updates
        if (count >= 60) {
            if (vs->dbstat_check()) {
                Messaging::message(Messaging::INFORMATION,
                        "ClamAV database update detected.");
                try {
                    // Create the new engine.
                    e = vs->createEngine();
                    // Stop scanning.
                    pthread_mutex_lock(&(vs->mutexUpdate));
                    // Wait for all running scans to be finished.
                    for (;;) {
                        pthread_mutex_lock(&(vs->mutexEngine));
                        if (vs->engineRefCount == 0) {
                            break;
                        }
                        pthread_mutex_unlock(&(vs->mutexEngine));
                        nanosleep(&interval2, NULL);
                    }
                    // Destroy the old engine
                    vs->destroyEngine(vs->engine);
                    vs->engine = e;
                    vs->env->getScanCache()->clear();
                    pthread_mutex_unlock(&(vs->mutexEngine));
                    Messaging::message(Messaging::INFORMATION,
                            "Using updated ClamAV database.");
                } catch (Status e) {
                }
                // Allow scanning.
                pthread_mutex_unlock(&(vs->mutexUpdate));
            }
        }
    }
    vs->status = STOPPED;
    return NULL;
}

/**
 * @brief Deletes the virus scanner.
 */
VirusScan::~VirusScan() {
    struct timespec interval = {
        0,
        1000000
    };

    status = STOPPING;
    do {
        nanosleep(&interval, NULL);
    } while (status == STOPPING);

    destroyEngine(engine);
    pthread_mutex_destroy(&mutexUpdate);
    pthread_mutex_destroy(&mutexEngine);
    dbstat_free();
}
<|endoftext|>


<commit_before>/**
 * Copyright 2011,  Filipe David Manana  <fdmanana@apache.org>
 * Web:  http://github.com/fdmanana/snappy-erlang-nif
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 **/

#include <iostream>
#include <cstring>

#include "erl_nif_compat.h"
#include "google-snappy/snappy.h"
#include "google-snappy/snappy-sinksource.h"

#ifdef OTP_R13B03
#error OTP R13B03 not supported. Upgrade to R13B04 or later.
#endif

#ifdef __cplusplus
#define BEGIN_C extern "C" {
#define END_C }
#else
#define BEGIN_C
#define END_C
#endif

#define SC_PTR(c) reinterpret_cast<char *>(c)

class SnappyNifSink : public snappy::Sink
{
    public:
        SnappyNifSink(ErlNifEnv* e);
        ~SnappyNifSink();

        void Append(const char* data, size_t n);
        char* GetAppendBuffer(size_t len, char* scratch);
        ErlNifBinary& getBin();

    private:
        ErlNifEnv* env;
        ErlNifBinary bin;
        size_t length;
};

SnappyNifSink::SnappyNifSink(ErlNifEnv* e) : env(e), length(0)
{
    if(!enif_alloc_binary_compat(env, 0, &bin)) {
        env = NULL;
        throw std::bad_alloc();
    }
}

SnappyNifSink::~SnappyNifSink()
{
    if(env != NULL) {
        enif_release_binary_compat(env, &bin);
    }
}

void
SnappyNifSink::Append(const char *data, size_t n)
{
    if(data != (SC_PTR(bin.data) + length)) {
        memcpy(bin.data + length, data, n);
    }
    length += n;
}

char*
SnappyNifSink::GetAppendBuffer(size_t len, char* scratch)
{
    size_t sz;

    if((length + len) > bin.size) {
        sz = (len * 4) < 8192 ? 8192 : (len * 4);

        if(!enif_realloc_binary_compat(env, &bin, bin.size + sz)) {
            throw std::bad_alloc();
        }
    }

    return SC_PTR(bin.data) + length;
}

ErlNifBinary&
SnappyNifSink::getBin()
{
    if(bin.size > length) {
        if(!enif_realloc_binary_compat(env, &bin, length)) {
            throw std::bad_alloc();
        }
    }
    return bin;
}

BEGIN_C

ERL_NIF_TERM
make_atom(ErlNifEnv* env, const char* name)
{
    ERL_NIF_TERM ret;
    if(enif_make_existing_atom_compat(env, name, &ret, ERL_NIF_LATIN1)) {
        return ret;
    }
    return enif_make_atom(env, name);
}

ERL_NIF_TERM
make_ok(ErlNifEnv* env, ERL_NIF_TERM mesg)
{
    ERL_NIF_TERM ok = make_atom(env, "ok");
    return enif_make_tuple2(env, ok, mesg);
}

ERL_NIF_TERM
make_error(ErlNifEnv* env, const char* mesg)
{
    ERL_NIF_TERM error = make_atom(env, "error");
    return enif_make_tuple2(env, error, make_atom(env, mesg));
}

ERL_NIF_TERM
snappy_compress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary input;

    if(!enif_inspect_iolist_as_binary(env, argv[0], &input)) {
        return enif_make_badarg(env);
    }

    try {
        snappy::ByteArraySource source(SC_PTR(input.data), input.size);
        SnappyNifSink sink(env);
        snappy::Compress(&source, &sink);
        return make_ok(env, enif_make_binary(env, &sink.getBin()));
    } catch(std::bad_alloc e) {
        return make_error(env, "insufficient_memory");
    } catch(...) {
        return make_error(env, "unknown");
    }
}

ERL_NIF_TERM
snappy_decompress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary bin;
    ErlNifBinary ret;
    size_t len;

    if(!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
        return enif_make_badarg(env);
    }

    try {
        if(!snappy::GetUncompressedLength(SC_PTR(bin.data), bin.size, &len)) {
            return make_error(env, "data_not_compressed");
        }

        if(!enif_alloc_binary_compat(env, len, &ret)) {
            return make_error(env, "insufficient_memory");
        }

        if(!snappy::RawUncompress(SC_PTR(bin.data), bin.size,
                                            SC_PTR(ret.data))) {
            return make_error(env, "corrupted_data");
        }

        return make_ok(env, enif_make_binary(env, &ret));
    } catch(...) {
        return make_error(env, "unknown");
    }
}

ERL_NIF_TERM
snappy_uncompressed_length(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary bin;
    size_t len;

    if(!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
        return enif_make_badarg(env);
    }

    try {
        if(!snappy::GetUncompressedLength(SC_PTR(bin.data), bin.size, &len)) {
            return make_error(env, "data_not_compressed");
        }
        return make_ok(env, enif_make_ulong(env, len));
    } catch(...) {
        return make_error(env, "unknown");
    }
}

ERL_NIF_TERM
snappy_is_valid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary bin;

    if (!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
        return enif_make_badarg(env);
    }

    try {
        if(snappy::IsValidCompressedBuffer(SC_PTR(bin.data), bin.size)) {
            return make_atom(env, "true");
        } else {
            return make_atom(env, "false");
        }
    } catch(...) {
        return make_error(env, "unknown");
    }
}

int
on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
    return 0;
}

int
on_reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
    return 0;
}

int
on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
{
    return 0;
}

static ErlNifFunc nif_functions[] = {
    {"compress", 1, snappy_compress},
    {"decompress", 1, snappy_decompress},
    {"uncompressed_length", 1, snappy_uncompressed_length},
    {"is_valid", 1, snappy_is_valid}
};

ERL_NIF_INIT(snappy, nif_functions, &on_load, &on_reload, &on_upgrade, NULL);

END_C
<commit_msg>Added missing static qualifier, and inline hint, to internal functions<commit_after>/**
 * Copyright 2011,  Filipe David Manana  <fdmanana@apache.org>
 * Web:  http://github.com/fdmanana/snappy-erlang-nif
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 **/

#include <iostream>
#include <cstring>

#include "erl_nif_compat.h"
#include "google-snappy/snappy.h"
#include "google-snappy/snappy-sinksource.h"

#ifdef OTP_R13B03
#error OTP R13B03 not supported. Upgrade to R13B04 or later.
#endif

#ifdef __cplusplus
#define BEGIN_C extern "C" {
#define END_C }
#else
#define BEGIN_C
#define END_C
#endif

#define SC_PTR(c) reinterpret_cast<char *>(c)

class SnappyNifSink : public snappy::Sink
{
    public:
        SnappyNifSink(ErlNifEnv* e);
        ~SnappyNifSink();

        void Append(const char* data, size_t n);
        char* GetAppendBuffer(size_t len, char* scratch);
        ErlNifBinary& getBin();

    private:
        ErlNifEnv* env;
        ErlNifBinary bin;
        size_t length;
};

SnappyNifSink::SnappyNifSink(ErlNifEnv* e) : env(e), length(0)
{
    if(!enif_alloc_binary_compat(env, 0, &bin)) {
        env = NULL;
        throw std::bad_alloc();
    }
}

SnappyNifSink::~SnappyNifSink()
{
    if(env != NULL) {
        enif_release_binary_compat(env, &bin);
    }
}

void
SnappyNifSink::Append(const char *data, size_t n)
{
    if(data != (SC_PTR(bin.data) + length)) {
        memcpy(bin.data + length, data, n);
    }
    length += n;
}

char*
SnappyNifSink::GetAppendBuffer(size_t len, char* scratch)
{
    size_t sz;

    if((length + len) > bin.size) {
        sz = (len * 4) < 8192 ? 8192 : (len * 4);

        if(!enif_realloc_binary_compat(env, &bin, bin.size + sz)) {
            throw std::bad_alloc();
        }
    }

    return SC_PTR(bin.data) + length;
}

ErlNifBinary&
SnappyNifSink::getBin()
{
    if(bin.size > length) {
        if(!enif_realloc_binary_compat(env, &bin, length)) {
            throw std::bad_alloc();
        }
    }
    return bin;
}

BEGIN_C

static inline ERL_NIF_TERM
make_atom(ErlNifEnv* env, const char* name)
{
    ERL_NIF_TERM ret;
    if(enif_make_existing_atom_compat(env, name, &ret, ERL_NIF_LATIN1)) {
        return ret;
    }
    return enif_make_atom(env, name);
}

static inline ERL_NIF_TERM
make_ok(ErlNifEnv* env, ERL_NIF_TERM mesg)
{
    ERL_NIF_TERM ok = make_atom(env, "ok");
    return enif_make_tuple2(env, ok, mesg);
}

static inline ERL_NIF_TERM
make_error(ErlNifEnv* env, const char* mesg)
{
    ERL_NIF_TERM error = make_atom(env, "error");
    return enif_make_tuple2(env, error, make_atom(env, mesg));
}

ERL_NIF_TERM
snappy_compress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary input;

    if(!enif_inspect_iolist_as_binary(env, argv[0], &input)) {
        return enif_make_badarg(env);
    }

    try {
        snappy::ByteArraySource source(SC_PTR(input.data), input.size);
        SnappyNifSink sink(env);
        snappy::Compress(&source, &sink);
        return make_ok(env, enif_make_binary(env, &sink.getBin()));
    } catch(std::bad_alloc e) {
        return make_error(env, "insufficient_memory");
    } catch(...) {
        return make_error(env, "unknown");
    }
}

ERL_NIF_TERM
snappy_decompress(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary bin;
    ErlNifBinary ret;
    size_t len;

    if(!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
        return enif_make_badarg(env);
    }

    try {
        if(!snappy::GetUncompressedLength(SC_PTR(bin.data), bin.size, &len)) {
            return make_error(env, "data_not_compressed");
        }

        if(!enif_alloc_binary_compat(env, len, &ret)) {
            return make_error(env, "insufficient_memory");
        }

        if(!snappy::RawUncompress(SC_PTR(bin.data), bin.size,
                                            SC_PTR(ret.data))) {
            return make_error(env, "corrupted_data");
        }

        return make_ok(env, enif_make_binary(env, &ret));
    } catch(...) {
        return make_error(env, "unknown");
    }
}

ERL_NIF_TERM
snappy_uncompressed_length(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary bin;
    size_t len;

    if(!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
        return enif_make_badarg(env);
    }

    try {
        if(!snappy::GetUncompressedLength(SC_PTR(bin.data), bin.size, &len)) {
            return make_error(env, "data_not_compressed");
        }
        return make_ok(env, enif_make_ulong(env, len));
    } catch(...) {
        return make_error(env, "unknown");
    }
}

ERL_NIF_TERM
snappy_is_valid(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary bin;

    if (!enif_inspect_iolist_as_binary(env, argv[0], &bin)) {
        return enif_make_badarg(env);
    }

    try {
        if(snappy::IsValidCompressedBuffer(SC_PTR(bin.data), bin.size)) {
            return make_atom(env, "true");
        } else {
            return make_atom(env, "false");
        }
    } catch(...) {
        return make_error(env, "unknown");
    }
}

int
on_load(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
    return 0;
}

int
on_reload(ErlNifEnv* env, void** priv, ERL_NIF_TERM info)
{
    return 0;
}

int
on_upgrade(ErlNifEnv* env, void** priv, void** old_priv, ERL_NIF_TERM info)
{
    return 0;
}

static ErlNifFunc nif_functions[] = {
    {"compress", 1, snappy_compress},
    {"decompress", 1, snappy_decompress},
    {"uncompressed_length", 1, snappy_uncompressed_length},
    {"is_valid", 1, snappy_is_valid}
};

ERL_NIF_INIT(snappy, nif_functions, &on_load, &on_reload, &on_upgrade, NULL);

END_C
<|endoftext|>


<commit_before>// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Used for building with external snapshots.

#include "src/snapshot.h"

#include "src/serialize.h"
#include "src/snapshot-source-sink.h"
#include "src/v8.h"  // for V8::Initialize

#ifndef V8_USE_EXTERNAL_STARTUP_DATA
#error snapshot-external.cc is used only for the external snapshot build.
#endif  // V8_USE_EXTERNAL_STARTUP_DATA

namespace v8 {
namespace internal {

static v8::StartupData external_startup_blob = {NULL, 0};

void SetSnapshotFromFile(StartupData* snapshot_blob) {
  DCHECK(snapshot_blob);
  DCHECK(snapshot_blob->data);
  DCHECK(snapshot_blob->raw_size > 0);
  DCHECK(!external_startup_blob.data);
  DCHECK(Snapshot::SnapshotIsValid(snapshot_blob));
  external_startup_blob = *snapshot_blob;
}

const v8::StartupData Snapshot::SnapshotBlob() { return external_startup_blob; }
} }  // namespace v8::internal
<commit_msg>Protect access to the external_snapshot_blob global with a lock.<commit_after>// Copyright 2006-2008 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Used for building with external snapshots.

#include "src/snapshot.h"

#include "src/base/platform/mutex.h"
#include "src/serialize.h"
#include "src/snapshot-source-sink.h"
#include "src/v8.h"  // for V8::Initialize

#ifndef V8_USE_EXTERNAL_STARTUP_DATA
#error snapshot-external.cc is used only for the external snapshot build.
#endif  // V8_USE_EXTERNAL_STARTUP_DATA

namespace v8 {
namespace internal {

static base::LazyMutex external_startup_data_mutex = LAZY_MUTEX_INITIALIZER;
static v8::StartupData external_startup_blob = {NULL, 0};

void SetSnapshotFromFile(StartupData* snapshot_blob) {
  base::LockGuard<base::Mutex> lock_guard(
      external_startup_data_mutex.Pointer());
  DCHECK(snapshot_blob);
  DCHECK(snapshot_blob->data);
  DCHECK(snapshot_blob->raw_size > 0);
  DCHECK(!external_startup_blob.data);
  DCHECK(Snapshot::SnapshotIsValid(snapshot_blob));
  external_startup_blob = *snapshot_blob;
}

const v8::StartupData Snapshot::SnapshotBlob() {
  base::LockGuard<base::Mutex> lock_guard(
      external_startup_data_mutex.Pointer());
  return external_startup_blob;
}
} }  // namespace v8::internal
<|endoftext|>


<commit_before>/**
	Copyright (c) 2009 James Wynn (james@jameswynn.com)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

	James Wynn james@jameswynn.com
*/

#include <FileWatcher/FileWatcherLinux.h>

#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_LINUX

#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <sys/inotify.h>

#define BUFF_SIZE ((sizeof(struct inotify_event)+FILENAME_MAX)*1024)

namespace FW
{

	struct WatchStruct
	{
		WatchID mWatchID;
		String mDirName;
		FileWatchListener* mListener;
	};

	//--------
	FileWatcherLinux::FileWatcherLinux()
	{
		mFD = inotify_init();
		if (mFD < 0)
			fprintf (stderr, "Error: %s\n", strerror(errno));

		mTimeOut.tv_sec = 0;
		mTimeOut.tv_usec = 0;

		FD_ZERO(&mDescriptorSet);
	}

	//--------
	FileWatcherLinux::~FileWatcherLinux()
	{
		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			delete iter->second;
		}
		mWatches.clear();
	}

	//--------
	WatchID FileWatcherLinux::addWatch(const String& directory, FileWatchListener* watcher, bool recursive)
	{
		int wd = inotify_add_watch (mFD, directory.c_str(),
			IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE);
		if (wd < 0)
		{
			if(errno == ENOENT)
				throw FileNotFoundException(directory);
			else
				throw Exception(strerror(errno));

//			fprintf (stderr, "Error: %s\n", strerror(errno));
//			return -1;
		}

		WatchStruct* pWatch = new WatchStruct();
		pWatch->mListener = watcher;
		pWatch->mWatchID = wd;
		pWatch->mDirName = directory;

		mWatches.insert(std::make_pair(wd, pWatch));

		return wd;
	}

	//--------
	void FileWatcherLinux::removeWatch(const String& directory)
	{
		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			if(directory == iter->second->mDirName)
			{
				removeWatch(iter->first);
				return;
			}
		}
	}

	//--------
	void FileWatcherLinux::removeWatch(WatchID watchid)
	{
		WatchMap::iterator iter = mWatches.find(watchid);

		if(iter == mWatches.end())
			return;

		WatchStruct* watch = iter->second;
		mWatches.erase(iter);

		inotify_rm_watch(mFD, watchid);

		delete watch;
		watch = 0;
	}

	//--------
	void FileWatcherLinux::update()
	{
		FD_SET(mFD, &mDescriptorSet);

		int ret = select(mFD + 1, &mDescriptorSet, NULL, NULL, &mTimeOut);
		if(ret < 0)
		{
			perror("select");
		}
		else if(FD_ISSET(mFD, &mDescriptorSet))
		{
			ssize_t len, i = 0;
			char action[81+FILENAME_MAX] = {0};
			char buff[BUFF_SIZE] = {0};

			len = read (mFD, buff, BUFF_SIZE);

			while (i < len)
			{
				struct inotify_event *pevent = (struct inotify_event *)&buff[i];

				WatchStruct* watch = mWatches[pevent->wd];
				handleAction(watch, pevent->name, pevent->mask);
				i += sizeof(struct inotify_event) + pevent->len;
			}
		}
	}

	//--------
	void FileWatcherLinux::handleAction(WatchStruct* watch, const String& filename, unsigned long action)
	{
		if(!watch->mListener)
			return;

		if(IN_CLOSE_WRITE & action)
		{
			watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
								Actions::Modified);
		}
		if(IN_MOVED_TO & action || IN_CREATE & action)
		{
			watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
								Actions::Add);
		}
		if(IN_MOVED_FROM & action || IN_DELETE & action)
		{
			watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
								Actions::Delete);
		}
	}

};//namespace FW

#endif//FILEWATCHER_PLATFORM_LINUX
<commit_msg>fixed missing include on linux<commit_after>/**
	Copyright (c) 2009 James Wynn (james@jameswynn.com)

	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

	James Wynn james@jameswynn.com
*/

#include <FileWatcher/FileWatcherLinux.h>

#if FILEWATCHER_PLATFORM == FILEWATCHER_PLATFORM_LINUX

#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h> // read
#include <sys/inotify.h>

#define BUFF_SIZE ((sizeof(struct inotify_event)+FILENAME_MAX)*1024)

namespace FW
{

	struct WatchStruct
	{
		WatchID mWatchID;
		String mDirName;
		FileWatchListener* mListener;
	};

	//--------
	FileWatcherLinux::FileWatcherLinux()
	{
		mFD = inotify_init();
		if (mFD < 0)
			fprintf (stderr, "Error: %s\n", strerror(errno));

		mTimeOut.tv_sec = 0;
		mTimeOut.tv_usec = 0;

		FD_ZERO(&mDescriptorSet);
	}

	//--------
	FileWatcherLinux::~FileWatcherLinux()
	{
		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			delete iter->second;
		}
		mWatches.clear();
	}

	//--------
	WatchID FileWatcherLinux::addWatch(const String& directory, FileWatchListener* watcher, bool recursive)
	{
		int wd = inotify_add_watch (mFD, directory.c_str(),
			IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE);
		if (wd < 0)
		{
			if(errno == ENOENT)
				throw FileNotFoundException(directory);
			else
				throw Exception(strerror(errno));

//			fprintf (stderr, "Error: %s\n", strerror(errno));
//			return -1;
		}

		WatchStruct* pWatch = new WatchStruct();
		pWatch->mListener = watcher;
		pWatch->mWatchID = wd;
		pWatch->mDirName = directory;

		mWatches.insert(std::make_pair(wd, pWatch));

		return wd;
	}

	//--------
	void FileWatcherLinux::removeWatch(const String& directory)
	{
		WatchMap::iterator iter = mWatches.begin();
		WatchMap::iterator end = mWatches.end();
		for(; iter != end; ++iter)
		{
			if(directory == iter->second->mDirName)
			{
				removeWatch(iter->first);
				return;
			}
		}
	}

	//--------
	void FileWatcherLinux::removeWatch(WatchID watchid)
	{
		WatchMap::iterator iter = mWatches.find(watchid);

		if(iter == mWatches.end())
			return;

		WatchStruct* watch = iter->second;
		mWatches.erase(iter);

		inotify_rm_watch(mFD, watchid);

		delete watch;
		watch = 0;
	}

	//--------
	void FileWatcherLinux::update()
	{
		FD_SET(mFD, &mDescriptorSet);

		int ret = select(mFD + 1, &mDescriptorSet, NULL, NULL, &mTimeOut);
		if(ret < 0)
		{
			perror("select");
		}
		else if(FD_ISSET(mFD, &mDescriptorSet))
		{
			ssize_t len, i = 0;
			char action[81+FILENAME_MAX] = {0};
			char buff[BUFF_SIZE] = {0};

			len = read (mFD, buff, BUFF_SIZE);

			while (i < len)
			{
				struct inotify_event *pevent = (struct inotify_event *)&buff[i];

				WatchStruct* watch = mWatches[pevent->wd];
				handleAction(watch, pevent->name, pevent->mask);
				i += sizeof(struct inotify_event) + pevent->len;
			}
		}
	}

	//--------
	void FileWatcherLinux::handleAction(WatchStruct* watch, const String& filename, unsigned long action)
	{
		if(!watch->mListener)
			return;

		if(IN_CLOSE_WRITE & action)
		{
			watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
								Actions::Modified);
		}
		if(IN_MOVED_TO & action || IN_CREATE & action)
		{
			watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
								Actions::Add);
		}
		if(IN_MOVED_FROM & action || IN_DELETE & action)
		{
			watch->mListener->handleFileAction(watch->mWatchID, watch->mDirName, filename,
								Actions::Delete);
		}
	}

};//namespace FW

#endif//FILEWATCHER_PLATFORM_LINUX
<|endoftext|>


<commit_before>#include "main.hpp"

#include "ParticleRenderer.hpp"
#include "Timeline.hpp"

void ParticleRenderer::reset() {
  graphicsPipeline.destroy();
  uniforms.clear();
}

void ParticleRenderer::setTimeline(std::unique_ptr<Timeline> _timeline) {
  reset();

  timeline = std::move(_timeline);

  state.clock.setPeriod(timeline->getPeriod());

  std::vector<UniformDescription> uniforms;
  ShaderBuilder vertexShader, fragmentShader;

  uniforms.emplace_back("invImageAspectRatio", GLSLType::Float,
                        [](const RenderProps &props) {
                          return UniformValue((float)props.webcam_height / props.webcam_width);
                        });
  uniforms.emplace_back("invScreenAspectRatio", GLSLType::Float,
                        [](const RenderProps &props) {
                          return UniformValue((float)props.screen_height / props.screen_width);
                        });
  uniforms.emplace_back("viewProjectionMatrix", GLSLType::Mat4,
                        [](const RenderProps &props) {
                          const auto aspect = (float)props.screen_width / props.screen_height;
                          const auto underscan = 1 - ((float)props.screen_height / props.screen_width) /
                                                         ((float)props.webcam_height / props.webcam_width);
                          return UniformValue(glm::mat4(
                              2.f / aspect, 0.f, 0.f, 0.f,
                              0.f, 2.f, 0.f, 0.f,
                              0.f, 0.f, 0.f, 0.f,
                              underscan - 1.f, -1.f, 0.f, 1.f
                          ));
                        });
  uniforms.emplace_back("invViewProjectionMatrix", GLSLType::Mat4,
                        [](const RenderProps &props) {
                          const auto aspect = (float)props.screen_width / props.screen_height;
                          const auto underscan = 1 - ((float)props.screen_height / props.screen_width) /
                                                         ((float)props.webcam_height / props.webcam_width);
                          return UniformValue(glm::mat4(
                            .5f * aspect, 0.f, 0.f, 0.f,
                            0.f, .5f, 0.f, 0.f,
                            0.f, 0.f, 0.f, 0.f,
                            (-.5f * (underscan - 1.f)) * aspect, .5f, 0.f, 1.f
                          ));
                        });
  uniforms.emplace_back("particleSize", GLSLType::Float,
                        [](const RenderProps &props) {
                          //TODO: particleScaling config
                          return UniformValue(((float)props.screen_height / props.webcam_height) * 2 /* * particleScaling*/);
                        });
  uniforms.emplace_back("globalTime", GLSLType::Float,
                        [](const RenderProps &props) {
                          return UniformValue(props.state.clock.getTime());
                        });

  vertexShader.appendIn("texcoord", GLSLType::Vec2);
  vertexShader.appendIn("rgb", GLSLType::Vec3);
  vertexShader.appendIn("hsv", GLSLType::Vec3);

  vertexShader.appendOut("color", GLSLType::Vec3);

  vertexShader.appendGlobal("PI", GLSLType::Float, std::to_string(PI));

  vertexShader.appendFunction(R"glsl(
    vec2 getDirectionVector(float angle) {
      return vec2(cos(angle), sin(angle));
    }
  )glsl");

  fragmentShader.appendIn("color", GLSLType::Vec3);

  fragmentShader.appendOut("frag_color", GLSLType::Vec4);

  vertexShader.appendMainBody(R"glsl(
    vec3 initialPosition = vec3(texcoord, 0);
    initialPosition.y *= invImageAspectRatio;
    float pointSize = max(particleSize, 0.);

    vec3 position = initialPosition;
  )glsl");

  fragmentShader.appendMainBody(R"glsl(
    float v = pow(max(1. - 2. * length(gl_PointCoord - vec2(.5)), 0.), 1.5);
  )glsl");

  unsigned instanceId = 0;
  timeline->forEachInstance([&](const IEffect &i) {
    Uniforms instanceUniforms(uniforms, instanceId++);
    vertexShader.appendMainBody(("if(" + std::to_string(i.getTimeBegin()) +
                                 "<= globalTime && globalTime <=" +
                                 std::to_string(i.getTimeEnd()) + ") {")
                                    .c_str());
#if 1
    vertexShader.appendMainBody("\n#line 0\n");
    fragmentShader.appendMainBody("\n#line 0\n");
#endif
    i.registerEffect(instanceUniforms, vertexShader, fragmentShader);
    vertexShader.appendMainBody("}");
  });

  vertexShader.appendMainBody(R"glsl(
    color = rgb;
    gl_PointSize = pointSize;
    gl_Position = viewProjectionMatrix * vec4(position, 1.);
  )glsl");

  // TODO: different overlap modes
  fragmentShader.appendMainBody(R"glsl(
    frag_color = vec4(color * v, 1);
  )glsl");

  for (const auto &u : uniforms) {
    vertexShader.appendUniform(u);
    fragmentShader.appendUniform(u);
  }

  const auto vertexShaderSource = vertexShader.assemble();
  const auto fragmentShaderSource = fragmentShader.assemble();

  std::cout << vertexShaderSource << "\n" << fragmentShaderSource << "\n";

  graphicsPipeline.create(vertexShaderSource.c_str(),
                          fragmentShaderSource.c_str());

  for (const auto &u : uniforms) {
    UniformElement newElement;
    newElement.location = graphicsPipeline.getUniformLocation(u.name.c_str());
    newElement.value = u.value;
    this->uniforms.push_back(newElement);
  }
}

void ParticleRenderer::update(float dt) {
  state.clock.frame(dt);
}

void ParticleRenderer::render(const RendererParameters &parameters) {
  RenderProps props(parameters, state);

  graphicsPipeline.bind();

  for (const auto &uniform : uniforms) {
    const auto value = uniform.value(props);
    switch (value.type) {
    case GLSLType::Float:
      glUniform1fv(uniform.location, 1, &value.data.f);
      break;
    case GLSLType::Vec2:
      glUniform2fv(uniform.location, 1, &value.data.v2[0]);
      break;
    case GLSLType::Vec3:
      glUniform3fv(uniform.location, 1, &value.data.v3[0]);
      break;
    case GLSLType::Vec4:
      glUniform4fv(uniform.location, 1, &value.data.v4[0]);
      break;
    case GLSLType::Mat4:
      glUniformMatrix4fv(uniform.location, 1, GL_FALSE, &value.data.m4[0][0]);
      break;
    }
  }

  parameters.particle_buffer.draw();
}
<commit_msg>Add clang-format off comments to matrix definitions<commit_after>#include "main.hpp"

#include "ParticleRenderer.hpp"
#include "Timeline.hpp"

void ParticleRenderer::reset() {
  graphicsPipeline.destroy();
  uniforms.clear();
}

void ParticleRenderer::setTimeline(std::unique_ptr<Timeline> _timeline) {
  reset();

  timeline = std::move(_timeline);

  state.clock.setPeriod(timeline->getPeriod());

  std::vector<UniformDescription> uniforms;
  ShaderBuilder vertexShader, fragmentShader;

  uniforms.emplace_back("invImageAspectRatio", GLSLType::Float,
                        [](const RenderProps &props) {
                          return UniformValue((float)props.webcam_height / props.webcam_width);
                        });
  uniforms.emplace_back("invScreenAspectRatio", GLSLType::Float,
                        [](const RenderProps &props) {
                          return UniformValue((float)props.screen_height / props.screen_width);
                        });
  uniforms.emplace_back("viewProjectionMatrix", GLSLType::Mat4,
                        [](const RenderProps &props) {
                          const auto aspect = (float)props.screen_width / props.screen_height;
                          const auto underscan = 1 - ((float)props.screen_height / props.screen_width) /
                                                         ((float)props.webcam_height / props.webcam_width);
                          // clang-format off
                          return UniformValue(glm::mat4(
                              2.f / aspect, 0.f, 0.f, 0.f,
                              0.f, 2.f, 0.f, 0.f,
                              0.f, 0.f, 0.f, 0.f,
                              underscan - 1.f, -1.f, 0.f, 1.f
                          ));
                          // clang-format on
                        });
  uniforms.emplace_back("invViewProjectionMatrix", GLSLType::Mat4,
                        [](const RenderProps &props) {
                          const auto aspect = (float)props.screen_width / props.screen_height;
                          const auto underscan = 1 - ((float)props.screen_height / props.screen_width) /
                                                         ((float)props.webcam_height / props.webcam_width);
                          // clang-format off
                          return UniformValue(glm::mat4(
                            .5f * aspect, 0.f, 0.f, 0.f,
                            0.f, .5f, 0.f, 0.f,
                            0.f, 0.f, 0.f, 0.f,
                            (-.5f * (underscan - 1.f)) * aspect, .5f, 0.f, 1.f
                          ));
                          // clang-format on
                        });
  uniforms.emplace_back("particleSize", GLSLType::Float,
                        [](const RenderProps &props) {
                          //TODO: particleScaling config
                          return UniformValue(((float)props.screen_height / props.webcam_height) * 2 /* * particleScaling*/);
                        });
  uniforms.emplace_back("globalTime", GLSLType::Float,
                        [](const RenderProps &props) {
                          return UniformValue(props.state.clock.getTime());
                        });

  vertexShader.appendIn("texcoord", GLSLType::Vec2);
  vertexShader.appendIn("rgb", GLSLType::Vec3);
  vertexShader.appendIn("hsv", GLSLType::Vec3);

  vertexShader.appendOut("color", GLSLType::Vec3);

  vertexShader.appendGlobal("PI", GLSLType::Float, std::to_string(PI));

  vertexShader.appendFunction(R"glsl(
    vec2 getDirectionVector(float angle) {
      return vec2(cos(angle), sin(angle));
    }
  )glsl");

  fragmentShader.appendIn("color", GLSLType::Vec3);

  fragmentShader.appendOut("frag_color", GLSLType::Vec4);

  vertexShader.appendMainBody(R"glsl(
    vec3 initialPosition = vec3(texcoord, 0);
    initialPosition.y *= invImageAspectRatio;
    float pointSize = max(particleSize, 0.);

    vec3 position = initialPosition;
  )glsl");

  fragmentShader.appendMainBody(R"glsl(
    float v = pow(max(1. - 2. * length(gl_PointCoord - vec2(.5)), 0.), 1.5);
  )glsl");

  unsigned instanceId = 0;
  timeline->forEachInstance([&](const IEffect &i) {
    Uniforms instanceUniforms(uniforms, instanceId++);
    vertexShader.appendMainBody(("if(" + std::to_string(i.getTimeBegin()) +
                                 "<= globalTime && globalTime <=" +
                                 std::to_string(i.getTimeEnd()) + ") {")
                                    .c_str());
#if 1
    vertexShader.appendMainBody("\n#line 0\n");
    fragmentShader.appendMainBody("\n#line 0\n");
#endif
    i.registerEffect(instanceUniforms, vertexShader, fragmentShader);
    vertexShader.appendMainBody("}");
  });

  vertexShader.appendMainBody(R"glsl(
    color = rgb;
    gl_PointSize = pointSize;
    gl_Position = viewProjectionMatrix * vec4(position, 1.);
  )glsl");

  // TODO: different overlap modes
  fragmentShader.appendMainBody(R"glsl(
    frag_color = vec4(color * v, 1);
  )glsl");

  for (const auto &u : uniforms) {
    vertexShader.appendUniform(u);
    fragmentShader.appendUniform(u);
  }

  const auto vertexShaderSource = vertexShader.assemble();
  const auto fragmentShaderSource = fragmentShader.assemble();

  std::cout << vertexShaderSource << "\n" << fragmentShaderSource << "\n";

  graphicsPipeline.create(vertexShaderSource.c_str(),
                          fragmentShaderSource.c_str());

  for (const auto &u : uniforms) {
    UniformElement newElement;
    newElement.location = graphicsPipeline.getUniformLocation(u.name.c_str());
    newElement.value = u.value;
    this->uniforms.push_back(newElement);
  }
}

void ParticleRenderer::update(float dt) {
  state.clock.frame(dt);
}

void ParticleRenderer::render(const RendererParameters &parameters) {
  RenderProps props(parameters, state);

  graphicsPipeline.bind();

  for (const auto &uniform : uniforms) {
    const auto value = uniform.value(props);
    switch (value.type) {
    case GLSLType::Float:
      glUniform1fv(uniform.location, 1, &value.data.f);
      break;
    case GLSLType::Vec2:
      glUniform2fv(uniform.location, 1, &value.data.v2[0]);
      break;
    case GLSLType::Vec3:
      glUniform3fv(uniform.location, 1, &value.data.v3[0]);
      break;
    case GLSLType::Vec4:
      glUniform4fv(uniform.location, 1, &value.data.v4[0]);
      break;
    case GLSLType::Mat4:
      glUniformMatrix4fv(uniform.location, 1, GL_FALSE, &value.data.m4[0][0]);
      break;
    }
  }

  parameters.particle_buffer.draw();
}
<|endoftext|>