<commit_before>#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include <QScopedPointer>
#include <QDebug>
#include <QByteArray>
#include <string>

#include <SimpleAmqpClient/SimpleAmqpClient.h>

#include "CommunicationLayer/CommDeviceControl/UdpMessageForwarder.h"
#include "InfrastructureLayer/Settings/MockSettings.h"

using ::testing::Return;

namespace
{
    const QString MOCK_IP = QString("localhost");
    const quint16 MOCK_PORT = 5672;
    const QString MOCK_QUEUE = "test-queue";
    const QString MOCK_EXCHANGE = "amq.direct";
    const QString MOCK_ROUTING_KEY = "routing-key";
    const QString EXPECTED_1 = "Message Test";
    const QString EXPECTED_2 = "Second Message Test";
    const QString EXPECTED_3 = "Sp3c1a1** Ch4r4c74r5__ 7357";
}

class UdpMessageForwarderTest : public ::testing::Test
{
protected:

    QScopedPointer<MockSettings> settings_;
    QScopedPointer<UdpMessageForwarder> forwarder;
    AmqpClient::Channel::ptr_t receiver;

    // TODO test that disconnecting the sender does not destroy messages in the queue

    /**
   * @brief SetUp will set up the receiver to verify messages are being sent, as well as mocking the settings to be used by the UdpMessageForwarder
   */
    virtual void SetUp()
    {
        settings_.reset(new MockSettings());
        receiver = Channel::Create(MOCK_IP.toStdString(), MOCK_PORT);
        receiver->DeclareQueue(MOCK_QUEUE.toStdString(), false, true, false, false);

        receiver->BindQueue(MOCK_QUEUE.toStdString(), MOCK_EXCHANGE.toStdString(), MOCK_ROUTING_KEY.toStdString());

        EXPECT_CALL(*settings_, ipAddress())
                .WillRepeatedly(Return(MOCK_IP));
        EXPECT_CALL(*settings_, queueName())
                .WillRepeatedly(Return(MOCK_QUEUE));
        EXPECT_CALL(*settings_, udpPort())
                .WillRepeatedly(Return(MOCK_PORT));
        EXPECT_CALL(*settings_, routingKey())
                .WillRepeatedly(Return(MOCK_ROUTING_KEY));
    }

    virtual void TearDown() {
        receiver->PurgeQueue(MOCK_QUEUE.toStdString());
        receiver->DeleteQueue(MOCK_QUEUE.toStdString());
        receiver.reset();
    }

    // Test Methods

    void sendMessage(const QString& message, bool resetForwarder);
    void receiveMessage(QString& actual, bool setupConsume);
    void setupReceiver();
    void resetReceiver();
};

void UdpMessageForwarderTest::sendMessage(const QString& message, bool resetForwarder) {
    if (resetForwarder)
        forwarder.reset(new UdpMessageForwarder(*settings_));
    QByteArray expectedBytes = QByteArray();
    expectedBytes.append(message);
    try {
        forwarder->forwardData(expectedBytes);
    } catch (quint16 err) {
        qDebug() << "Failed to forward data with exception: " << err;
    }
}

void UdpMessageForwarderTest::receiveMessage(QString& actual, bool setupConsume) {
    // Receive message from local server
    if (setupConsume)
        receiver->BasicConsume(MOCK_QUEUE.toStdString());
    try {
        actual = QString::fromStdString(receiver->BasicConsumeMessage()->Message()->Body());
    } catch (quint16 err) {
        qDebug() << "Failed to receive data with exception: " << err;
    }
}

void UdpMessageForwarderTest::setupReceiver() {
    receiver = Channel::Create(MOCK_IP.toStdString(), MOCK_PORT);
    receiver->DeclareQueue(MOCK_QUEUE.toStdString(), false, true, false, false);

    receiver->BindQueue(MOCK_QUEUE.toStdString(), MOCK_EXCHANGE.toStdString(), MOCK_ROUTING_KEY.toStdString());
}

void UdpMessageForwarderTest::resetReceiver() {
    receiver->PurgeQueue(MOCK_QUEUE.toStdString());
    receiver->DeleteQueue(MOCK_QUEUE.toStdString());
    receiver.reset();
}

/**
 * @brief Send a single message representing a JSON string via UdpMessageForwarder and verify its success
 */
TEST_F(UdpMessageForwarderTest, testSendingMessage) {

    sendMessage(EXPECTED_1, true);

    QString ACTUAL;
    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_1.toStdString(), ACTUAL.toStdString());
}

/**
 * @brief Send a message before the receiver has setup
 */
TEST_F(UdpMessageForwarderTest, testSendingNoReceiver) {

    resetReceiver();

    sendMessage(EXPECTED_1, true);

    setupReceiver();

    QString ACTUAL;
    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_1.toStdString(), ACTUAL.toStdString());
}

/**
 * @brief Disconnect the receiver during delivery and ensure messages still arrive
 */
TEST_F(UdpMessageForwarderTest, testSendingReceiverDC) {
    sendMessage(EXPECTED_1, true);

    QString ACTUAL;
    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_1.toStdString(), ACTUAL.toStdString());

    receiver.reset();

    sendMessage(EXPECTED_2, false);

    setupReceiver();

    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_2.toStdString(), ACTUAL.toStdString());
}

/**
 * @brief Disconnect the receiver during delivery and ensure messages still arrive
 */
TEST_F(UdpMessageForwarderTest, testSendingSpecialChars) {
    sendMessage(EXPECTED_3, true);

    QString ACTUAL;
    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_3.toStdString(), ACTUAL.toStdString());
}
<commit_msg>Minor changes to improve clarification<commit_after>#include <gtest/gtest.h>
#include <gmock/gmock.h>

#include <QScopedPointer>
#include <QDebug>
#include <QByteArray>
#include <string>

#include <SimpleAmqpClient/SimpleAmqpClient.h>

#include "CommunicationLayer/CommDeviceControl/UdpMessageForwarder.h"
#include "InfrastructureLayer/Settings/MockSettings.h"

using ::testing::Return;

namespace
{
    const QString MOCK_IP = QString("localhost");
    const quint16 MOCK_PORT = 5672;
    const QString MOCK_QUEUE = "test-queue";
    const QString MOCK_EXCHANGE = "amq.direct";
    const QString MOCK_ROUTING_KEY = "routing-key";
    const QString EXPECTED_1 = "Message Test";
    const QString EXPECTED_2 = "Second Message Test";
    const QString EXPECTED_3 = "Sp3c1a1** Ch4r4c74r5__ 7357";
}

class UdpMessageForwarderTest : public ::testing::Test
{
protected:

    QScopedPointer<MockSettings> settings_;
    QScopedPointer<UdpMessageForwarder> forwarder;
    AmqpClient::Channel::ptr_t receiver;

    // TODO test that disconnecting the sender does not destroy messages in the queue

    /**
   * @brief SetUp will set up the receiver to verify messages are being sent, as well as mocking the settings to be used by the UdpMessageForwarder
   */
    virtual void SetUp()
    {
        settings_.reset(new MockSettings());
        receiver = Channel::Create(MOCK_IP.toStdString(), MOCK_PORT);
        receiver->DeclareQueue(MOCK_QUEUE.toStdString(), false, true, false, false);

        receiver->BindQueue(MOCK_QUEUE.toStdString(), MOCK_EXCHANGE.toStdString(), MOCK_ROUTING_KEY.toStdString());

        EXPECT_CALL(*settings_, ipAddress())
                .WillRepeatedly(Return(MOCK_IP));
        EXPECT_CALL(*settings_, queueName())
                .WillRepeatedly(Return(MOCK_QUEUE));
        EXPECT_CALL(*settings_, udpPort())
                .WillRepeatedly(Return(MOCK_PORT));
        EXPECT_CALL(*settings_, routingKey())
                .WillRepeatedly(Return(MOCK_ROUTING_KEY));
    }

    virtual void TearDown() {
        receiver->PurgeQueue(MOCK_QUEUE.toStdString());
        receiver->DeleteQueue(MOCK_QUEUE.toStdString());
        receiver.reset();
    }

    // Test Methods

    void sendMessage(const QString& message, bool resetForwarder);
    void receiveMessage(QString& actual, bool setupConsume);
    /**
     * @brief setup the receiver for usage
     */
    void setupReceiver();
    /**
     * @brief reset the receiver (clear and delete queue)
     */
    void cleanReceiver();
};

void UdpMessageForwarderTest::sendMessage(const QString& message, bool resetForwarder) {
    if (resetForwarder)
        forwarder.reset(new UdpMessageForwarder(*settings_));
    QByteArray expectedBytes = QByteArray();
    expectedBytes.append(message);
    try {
        forwarder->forwardData(expectedBytes);
    } catch (quint16 err) {
        qDebug() << "Failed to forward data with exception: " << err;
    }
}

void UdpMessageForwarderTest::receiveMessage(QString& actual, bool setupConsume) {
    // Receive message from local server
    if (setupConsume)
        receiver->BasicConsume(MOCK_QUEUE.toStdString());
    try {
        actual = QString::fromStdString(receiver->BasicConsumeMessage()->Message()->Body());
    } catch (quint16 err) {
        qDebug() << "Failed to receive data with exception: " << err;
    }
}

void UdpMessageForwarderTest::setupReceiver() {
    receiver = Channel::Create(MOCK_IP.toStdString(), MOCK_PORT);
    receiver->DeclareQueue(MOCK_QUEUE.toStdString(), false, true, false, false);

    receiver->BindQueue(MOCK_QUEUE.toStdString(), MOCK_EXCHANGE.toStdString(), MOCK_ROUTING_KEY.toStdString());
}

void UdpMessageForwarderTest::cleanReceiver() {
    receiver->PurgeQueue(MOCK_QUEUE.toStdString());
    receiver->DeleteQueue(MOCK_QUEUE.toStdString());
    receiver.reset();
}

/**
 * @brief Send a single message representing a JSON string via UdpMessageForwarder and verify its success
 */
TEST_F(UdpMessageForwarderTest, testSendingMessage) {

    sendMessage(EXPECTED_1, true);

    QString ACTUAL;
    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_1.toStdString(), ACTUAL.toStdString());
}

/**
 * @brief Send a message before the receiver has setup
 */
TEST_F(UdpMessageForwarderTest, testSendingNoReceiver) {

    cleanReceiver();

    sendMessage(EXPECTED_1, true);

    setupReceiver();

    QString ACTUAL;
    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_1.toStdString(), ACTUAL.toStdString());
}

/**
 * @brief Disconnect the receiver during delivery and ensure messages still arrive
 */
TEST_F(UdpMessageForwarderTest, testSendingReceiverDC) {
    sendMessage(EXPECTED_1, true);

    QString ACTUAL;
    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_1.toStdString(), ACTUAL.toStdString());

    receiver.reset();

    sendMessage(EXPECTED_2, false);

    setupReceiver();

    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_2.toStdString(), ACTUAL.toStdString());
}

/**
 * @brief test sending message with special characters
 */
TEST_F(UdpMessageForwarderTest, testSendingSpecialChars) {
    sendMessage(EXPECTED_3, true);

    QString ACTUAL;
    receiveMessage(ACTUAL, true);

    EXPECT_EQ(EXPECTED_3.toStdString(), ACTUAL.toStdString());
}
<|endoftext|>


<commit_before>#include "coldstakingmodel.h"
#include "addresstablemodel.h"
#include "base58.h"
#include "boost/thread/future.hpp"
#include <boost/atomic/atomic.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include <QIcon>
#include <QImage>
#include <QMetaType>

std::pair<QList<ColdStakingCachedItem>, CAmount>
ColdStakingModel::ProcessColdStakingUTXOList(const std::vector<COutput>& utxoList)
{
    QList<ColdStakingCachedItem> cachedItemsResult;
    CAmount                      cachedAmountResult = 0;

    if (!utxoList.empty()) {
        // Loop over each COutput into a CSDelegation
        for (const auto& utxo : utxoList) {

            const CWalletTx* wtx  = utxo.tx;
            const QString    txId = QString::fromStdString(wtx->GetHash().GetHex());
            const CTxOut&    out  = wtx->vout[utxo.i];

            // First parse the cs delegation
            boost::optional<ColdStakingCachedItem> item = ParseColdStakingCachedItem(out, txId, utxo.i);
            if (!item)
                continue;

            // it's spendable only when this wallet has the keys to spend it, a.k.a is the owner
            item->isSpendable =
                (IsMine(*pwalletMain, out.scriptPubKey) & isminetype::ISMINE_SPENDABLE_ALL) != 0;
            item->cachedTotalAmount += out.nValue;
            item->delegatedUtxo.insert(txId, utxo.i);

            // Now verify if the delegation exists in the cached list
            int indexDel = cachedItemsResult.indexOf(*item);
            if (indexDel == -1) {
                // If it doesn't, let's append it.
                cachedItemsResult.append(*item);
            } else {
                ColdStakingCachedItem& del = cachedItemsResult[indexDel];
                del.delegatedUtxo.unite(item->delegatedUtxo);
                del.cachedTotalAmount += item->cachedTotalAmount;
            }

            // add amount to cachedAmount if either:
            // - this is a owned delegation
            // - this is a staked delegation, and the owner is whitelisted
            //            if (!delegation.isSpendable &&
            //            !addressTableModel->isWhitelisted(delegation.ownerAddress))
            //                continue;
            cachedAmountResult += item->cachedTotalAmount;
        }
    }

    return std::make_pair(cachedItemsResult, cachedAmountResult);
}

WalletModel* ColdStakingModel::getWalletModel() { return walletModel; }

TransactionTableModel* ColdStakingModel::getTransactionTableModel() { return tableModel; }

AddressTableModel* ColdStakingModel::getAddressTableModel() { return addressTableModel; }

ColdStakingModel::ColdStakingModel()
{
    qRegisterMetaType<QSharedPointer<std::vector<COutput>>>("QSharedPointer<std::vector<COutput>>");
    qRegisterMetaType<QSharedPointer<AvailableP2CSCoinsWorker>>(
        "QSharedPointer<AvailableP2CSCoinsWorker>");
    qRegisterMetaType<QSharedPointer<std::pair<QList<ColdStakingCachedItem>, CAmount>>>(
        "QSharedPointer<std::pair<QList<ColdStakingCachedItem>, CAmount>>");
    retrieveOutputsThread.start();
}

ColdStakingModel::~ColdStakingModel()
{
    retrieveOutputsThread.quit();
    retrieveOutputsThread.wait();
}

int ColdStakingModel::rowCount(const QModelIndex& /*parent*/) const { return cachedItems.size(); }

int ColdStakingModel::columnCount(const QModelIndex& /*parent*/) const { return COLUMN_COUNT; }

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

    int row = index.row();
    if (row >= static_cast<int>(cachedItems.size()))
        return QVariant();

    const ColdStakingCachedItem& rec = cachedItems[row];

    if (role == ColdStakingModel::ColumnIndex::OWNER_ADDRESS) {
        return QString::fromStdString(rec.ownerAddress);
    }
    if (role == ColdStakingModel::ColumnIndex::OWNER_ADDRESS_LABEL) {
        return addressTableModel->labelForAddress(QString::fromStdString(rec.ownerAddress));
    }
    if (role == ColdStakingModel::ColumnIndex::STAKING_ADDRESS) {
        return QString::fromStdString(rec.stakingAddress);
    }
    if (role == ColdStakingModel::ColumnIndex::STAKING_ADDRESS_LABEL) {
        return addressTableModel->labelForAddress(QString::fromStdString(rec.stakingAddress));
    }
    if (role == ColdStakingModel::ColumnIndex::IS_WHITELISTED) {
        return addressTableModel->purposeForAddress(rec.ownerAddress)
                   .compare(AddressBook::AddressBookPurpose::DELEGATOR) == 0;
    }
    if (role == ColdStakingModel::ColumnIndex::TOTAL_STACKEABLE_AMOUNT_STR) {
        return QString::fromStdString(FormatMoney(rec.cachedTotalAmount));
    }
    if (role == ColdStakingModel::ColumnIndex::TOTAL_STACKEABLE_AMOUNT) {
        return qint64(rec.cachedTotalAmount);
    }
    if (role == ColdStakingModel::ColumnIndex::IS_RECEIVED_DELEGATION) {
        return rec.isSpendable;
    }
    return QVariant();
}

void ColdStakingModel::setWalletModel(WalletModel* wModel)
{
    walletModel       = wModel;
    addressTableModel = wModel->getAddressTableModel();
    tableModel        = wModel->getTransactionTableModel();
}

void ColdStakingModel::refresh()
{
    if (!appInitiated) {
        QTimer::singleShot(5000, this, &ColdStakingModel::refresh);
        return;
    }

    if (!pwalletMain) {
        QTimer::singleShot(5000, this, &ColdStakingModel::refresh);
        return;
    }

    // we want only one instane of the worker running
    if (isWorkerRunning) {
        QTimer::singleShot(5000, this, &ColdStakingModel::refresh);
        return;
    }

    isWorkerRunning = true;

    QSharedPointer<AvailableP2CSCoinsWorker> worker = QSharedPointer<AvailableP2CSCoinsWorker>::create();
    worker->moveToThread(&retrieveOutputsThread);
    connect(&retrieveOutputsThread, &QThread::finished, worker.data(), &QObject::deleteLater);
    connect(worker.data(), &AvailableP2CSCoinsWorker::resultReady, this,
            &ColdStakingModel::finishRefresh, Qt::QueuedConnection);
    connect(this, &ColdStakingModel::triggerWorkerRetrieveOutputs, worker.data(),
            &AvailableP2CSCoinsWorker::retrieveOutputs, Qt::QueuedConnection);
    emit triggerWorkerRetrieveOutputs(worker);
}

void ColdStakingModel::finishRefresh(
    QSharedPointer<std::pair<QList<ColdStakingCachedItem>, CAmount>> itemsAndAmount)
{
    assert(itemsAndAmount);

    // force sync since we got a vector from another thread
    std::atomic_thread_fence(std::memory_order_seq_cst);

    beginResetModel();
    // this is an RAII hack to guarantee that the function will end the model reset
    // WalletModel has nothing to do with this. It's just a dummy variable
    auto modelResetEnderFunctor = [this](WalletModel*) { endResetModel(); };
    std::unique_ptr<WalletModel, decltype(modelResetEnderFunctor)> txEnder(walletModel,
                                                                           modelResetEnderFunctor);

    std::tie(cachedItems, cachedAmount) = *itemsAndAmount;

    QMetaObject::invokeMethod(this, "emitDataSetChanged", Qt::QueuedConnection);

    isWorkerRunning = false;
}

boost::optional<ColdStakingCachedItem> ColdStakingModel::ParseColdStakingCachedItem(const CTxOut&  out,
                                                                                const QString& txId,
                                                                                const int& utxoIndex)
{
    txnouttype                  type;
    std::vector<CTxDestination> addresses;
    int                         nRequired;

    if (!ExtractDestinations(out.scriptPubKey, type, addresses, nRequired) || addresses.size() != 2) {
        printf("%s : Error extracting P2CS destinations for utxo: %s-%d", __func__,
               txId.toStdString().c_str(), utxoIndex);
        return boost::none;
    }

    std::string stakingAddressStr = CBitcoinAddress(addresses[0]).ToString();

    std::string ownerAddressStr = CBitcoinAddress(addresses[1]).ToString();

    return boost::make_optional(ColdStakingCachedItem(stakingAddressStr, ownerAddressStr));
}

bool ColdStakingModel::whitelist(const QModelIndex& modelIndex)
{
    QString address = modelIndex.data(ColumnIndex::OWNER_ADDRESS).toString();
    if (addressTableModel->isWhitelisted(address.toStdString())) {
        return error("trying to whitelist already whitelisted address");
    }

    if (!walletModel->whitelistAddressFromColdStaking(address))
        return false;

    // address whitelisted - update cached amount and row data
    const int idx = modelIndex.row();
    cachedAmount += cachedItems[idx].cachedTotalAmount;
    removeRowAndEmitDataChanged(idx);

    return true;
}

bool ColdStakingModel::blacklist(const QModelIndex& modelIndex)
{
    QString address = modelIndex.data(ColumnIndex::OWNER_ADDRESS).toString();
    if (!addressTableModel->isWhitelisted(address.toStdString())) {
        return error("trying to blacklist already blacklisted address");
    }

    if (!walletModel->blacklistAddressFromColdStaking(address))
        return false;

    // address blacklisted - update cached amount and row data
    const int idx = modelIndex.row();
    cachedAmount -= cachedItems[idx].cachedTotalAmount;
    removeRowAndEmitDataChanged(idx);

    return true;
}

void ColdStakingModel::removeRowAndEmitDataChanged(const int idx)
{
    beginRemoveRows(QModelIndex(), idx, idx);
    endRemoveRows();
    emit dataChanged(index(idx, 0, QModelIndex()), index(idx, COLUMN_COUNT, QModelIndex()));
}

void ColdStakingModel::updateCSList()
{
    QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection);
}

void ColdStakingModel::emitDataSetChanged()
{
    emit dataChanged(index(0, 0, QModelIndex()), index(cachedItems.size(), COLUMN_COUNT, QModelIndex()));
}

void AvailableP2CSCoinsWorker::retrieveOutputs(QSharedPointer<AvailableP2CSCoinsWorker> workerPtr)
{
    QSharedPointer<std::vector<COutput>> utxoList = QSharedPointer<std::vector<COutput>>::create();
    while (!fShutdown && !pwalletMain->GetAvailableP2CSCoins(*utxoList)) {
        QThread::msleep(100);
    }

    const auto result = ColdStakingModel::ProcessColdStakingUTXOList(*utxoList);

    emit resultReady(
        QSharedPointer<std::pair<QList<ColdStakingCachedItem>, CAmount>>::create(std::move(result)));
    workerPtr.reset();
}
<commit_msg>Rename thread for retrieving cold stake UTXOs<commit_after>#include "coldstakingmodel.h"
#include "addresstablemodel.h"
#include "base58.h"
#include "boost/thread/future.hpp"
#include <boost/atomic/atomic.hpp>
#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>

#include <QIcon>
#include <QImage>
#include <QMetaType>

std::pair<QList<ColdStakingCachedItem>, CAmount>
ColdStakingModel::ProcessColdStakingUTXOList(const std::vector<COutput>& utxoList)
{
    QList<ColdStakingCachedItem> cachedItemsResult;
    CAmount                      cachedAmountResult = 0;

    if (!utxoList.empty()) {
        // Loop over each COutput into a CSDelegation
        for (const auto& utxo : utxoList) {

            const CWalletTx* wtx  = utxo.tx;
            const QString    txId = QString::fromStdString(wtx->GetHash().GetHex());
            const CTxOut&    out  = wtx->vout[utxo.i];

            // First parse the cs delegation
            boost::optional<ColdStakingCachedItem> item = ParseColdStakingCachedItem(out, txId, utxo.i);
            if (!item)
                continue;

            // it's spendable only when this wallet has the keys to spend it, a.k.a is the owner
            item->isSpendable =
                (IsMine(*pwalletMain, out.scriptPubKey) & isminetype::ISMINE_SPENDABLE_ALL) != 0;
            item->cachedTotalAmount += out.nValue;
            item->delegatedUtxo.insert(txId, utxo.i);

            // Now verify if the delegation exists in the cached list
            int indexDel = cachedItemsResult.indexOf(*item);
            if (indexDel == -1) {
                // If it doesn't, let's append it.
                cachedItemsResult.append(*item);
            } else {
                ColdStakingCachedItem& del = cachedItemsResult[indexDel];
                del.delegatedUtxo.unite(item->delegatedUtxo);
                del.cachedTotalAmount += item->cachedTotalAmount;
            }

            // add amount to cachedAmount if either:
            // - this is a owned delegation
            // - this is a staked delegation, and the owner is whitelisted
            //            if (!delegation.isSpendable &&
            //            !addressTableModel->isWhitelisted(delegation.ownerAddress))
            //                continue;
            cachedAmountResult += item->cachedTotalAmount;
        }
    }

    return std::make_pair(cachedItemsResult, cachedAmountResult);
}

WalletModel* ColdStakingModel::getWalletModel() { return walletModel; }

TransactionTableModel* ColdStakingModel::getTransactionTableModel() { return tableModel; }

AddressTableModel* ColdStakingModel::getAddressTableModel() { return addressTableModel; }

ColdStakingModel::ColdStakingModel()
{
    qRegisterMetaType<QSharedPointer<std::vector<COutput>>>("QSharedPointer<std::vector<COutput>>");
    qRegisterMetaType<QSharedPointer<AvailableP2CSCoinsWorker>>(
        "QSharedPointer<AvailableP2CSCoinsWorker>");
    qRegisterMetaType<QSharedPointer<std::pair<QList<ColdStakingCachedItem>, CAmount>>>(
        "QSharedPointer<std::pair<QList<ColdStakingCachedItem>, CAmount>>");
    retrieveOutputsThread.setObjectName("neblio-CsUTXORetriever"); // thread name
    retrieveOutputsThread.start();
}

ColdStakingModel::~ColdStakingModel()
{
    retrieveOutputsThread.quit();
    retrieveOutputsThread.wait();
}

int ColdStakingModel::rowCount(const QModelIndex& /*parent*/) const { return cachedItems.size(); }

int ColdStakingModel::columnCount(const QModelIndex& /*parent*/) const { return COLUMN_COUNT; }

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

    int row = index.row();
    if (row >= static_cast<int>(cachedItems.size()))
        return QVariant();

    const ColdStakingCachedItem& rec = cachedItems[row];

    if (role == ColdStakingModel::ColumnIndex::OWNER_ADDRESS) {
        return QString::fromStdString(rec.ownerAddress);
    }
    if (role == ColdStakingModel::ColumnIndex::OWNER_ADDRESS_LABEL) {
        return addressTableModel->labelForAddress(QString::fromStdString(rec.ownerAddress));
    }
    if (role == ColdStakingModel::ColumnIndex::STAKING_ADDRESS) {
        return QString::fromStdString(rec.stakingAddress);
    }
    if (role == ColdStakingModel::ColumnIndex::STAKING_ADDRESS_LABEL) {
        return addressTableModel->labelForAddress(QString::fromStdString(rec.stakingAddress));
    }
    if (role == ColdStakingModel::ColumnIndex::IS_WHITELISTED) {
        return addressTableModel->purposeForAddress(rec.ownerAddress)
                   .compare(AddressBook::AddressBookPurpose::DELEGATOR) == 0;
    }
    if (role == ColdStakingModel::ColumnIndex::TOTAL_STACKEABLE_AMOUNT_STR) {
        return QString::fromStdString(FormatMoney(rec.cachedTotalAmount));
    }
    if (role == ColdStakingModel::ColumnIndex::TOTAL_STACKEABLE_AMOUNT) {
        return qint64(rec.cachedTotalAmount);
    }
    if (role == ColdStakingModel::ColumnIndex::IS_RECEIVED_DELEGATION) {
        return rec.isSpendable;
    }
    return QVariant();
}

void ColdStakingModel::setWalletModel(WalletModel* wModel)
{
    walletModel       = wModel;
    addressTableModel = wModel->getAddressTableModel();
    tableModel        = wModel->getTransactionTableModel();
}

void ColdStakingModel::refresh()
{
    if (!appInitiated) {
        QTimer::singleShot(5000, this, &ColdStakingModel::refresh);
        return;
    }

    if (!pwalletMain) {
        QTimer::singleShot(5000, this, &ColdStakingModel::refresh);
        return;
    }

    // we want only one instane of the worker running
    if (isWorkerRunning) {
        QTimer::singleShot(5000, this, &ColdStakingModel::refresh);
        return;
    }

    isWorkerRunning = true;

    QSharedPointer<AvailableP2CSCoinsWorker> worker = QSharedPointer<AvailableP2CSCoinsWorker>::create();
    worker->moveToThread(&retrieveOutputsThread);
    connect(&retrieveOutputsThread, &QThread::finished, worker.data(), &QObject::deleteLater);
    connect(worker.data(), &AvailableP2CSCoinsWorker::resultReady, this,
            &ColdStakingModel::finishRefresh, Qt::QueuedConnection);
    connect(this, &ColdStakingModel::triggerWorkerRetrieveOutputs, worker.data(),
            &AvailableP2CSCoinsWorker::retrieveOutputs, Qt::QueuedConnection);
    emit triggerWorkerRetrieveOutputs(worker);
}

void ColdStakingModel::finishRefresh(
    QSharedPointer<std::pair<QList<ColdStakingCachedItem>, CAmount>> itemsAndAmount)
{
    assert(itemsAndAmount);

    // force sync since we got a vector from another thread
    std::atomic_thread_fence(std::memory_order_seq_cst);

    beginResetModel();
    // this is an RAII hack to guarantee that the function will end the model reset
    // WalletModel has nothing to do with this. It's just a dummy variable
    auto modelResetEnderFunctor = [this](WalletModel*) { endResetModel(); };
    std::unique_ptr<WalletModel, decltype(modelResetEnderFunctor)> txEnder(walletModel,
                                                                           modelResetEnderFunctor);

    std::tie(cachedItems, cachedAmount) = *itemsAndAmount;

    QMetaObject::invokeMethod(this, "emitDataSetChanged", Qt::QueuedConnection);

    isWorkerRunning = false;
}

boost::optional<ColdStakingCachedItem> ColdStakingModel::ParseColdStakingCachedItem(const CTxOut&  out,
                                                                                const QString& txId,
                                                                                const int& utxoIndex)
{
    txnouttype                  type;
    std::vector<CTxDestination> addresses;
    int                         nRequired;

    if (!ExtractDestinations(out.scriptPubKey, type, addresses, nRequired) || addresses.size() != 2) {
        printf("%s : Error extracting P2CS destinations for utxo: %s-%d", __func__,
               txId.toStdString().c_str(), utxoIndex);
        return boost::none;
    }

    std::string stakingAddressStr = CBitcoinAddress(addresses[0]).ToString();

    std::string ownerAddressStr = CBitcoinAddress(addresses[1]).ToString();

    return boost::make_optional(ColdStakingCachedItem(stakingAddressStr, ownerAddressStr));
}

bool ColdStakingModel::whitelist(const QModelIndex& modelIndex)
{
    QString address = modelIndex.data(ColumnIndex::OWNER_ADDRESS).toString();
    if (addressTableModel->isWhitelisted(address.toStdString())) {
        return error("trying to whitelist already whitelisted address");
    }

    if (!walletModel->whitelistAddressFromColdStaking(address))
        return false;

    // address whitelisted - update cached amount and row data
    const int idx = modelIndex.row();
    cachedAmount += cachedItems[idx].cachedTotalAmount;
    removeRowAndEmitDataChanged(idx);

    return true;
}

bool ColdStakingModel::blacklist(const QModelIndex& modelIndex)
{
    QString address = modelIndex.data(ColumnIndex::OWNER_ADDRESS).toString();
    if (!addressTableModel->isWhitelisted(address.toStdString())) {
        return error("trying to blacklist already blacklisted address");
    }

    if (!walletModel->blacklistAddressFromColdStaking(address))
        return false;

    // address blacklisted - update cached amount and row data
    const int idx = modelIndex.row();
    cachedAmount -= cachedItems[idx].cachedTotalAmount;
    removeRowAndEmitDataChanged(idx);

    return true;
}

void ColdStakingModel::removeRowAndEmitDataChanged(const int idx)
{
    beginRemoveRows(QModelIndex(), idx, idx);
    endRemoveRows();
    emit dataChanged(index(idx, 0, QModelIndex()), index(idx, COLUMN_COUNT, QModelIndex()));
}

void ColdStakingModel::updateCSList()
{
    QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection);
}

void ColdStakingModel::emitDataSetChanged()
{
    emit dataChanged(index(0, 0, QModelIndex()), index(cachedItems.size(), COLUMN_COUNT, QModelIndex()));
}

void AvailableP2CSCoinsWorker::retrieveOutputs(QSharedPointer<AvailableP2CSCoinsWorker> workerPtr)
{
    QSharedPointer<std::vector<COutput>> utxoList = QSharedPointer<std::vector<COutput>>::create();
    while (!fShutdown && !pwalletMain->GetAvailableP2CSCoins(*utxoList)) {
        QThread::msleep(100);
    }

    auto result = ColdStakingModel::ProcessColdStakingUTXOList(*utxoList);

    emit resultReady(
        QSharedPointer<std::pair<QList<ColdStakingCachedItem>, CAmount>>::create(std::move(result)));
    workerPtr.reset();
}
<|endoftext|>


<commit_before>#include "camera_view_painter.h"
#include <QPainter>
#include <QPoint>
#include <QPointF>
#include <QtCore/qmath.h>
#include "math/functions.h"
#include <math/constants.h>
#include <QPolygon>
#include <QVector>
#include "qt_util.h"
#include <iostream>
#include <QColor>
#include <QRect>

CameraViewPainter::CameraViewPainter(float const & roll, float const & yaw, float const & pitch) :
    _roll(roll),
    _yaw(yaw),
    _pitch(pitch),
    _backGround(&_horizon),
    _horizon(this),
    _cameraBackGround(this)
{
    // set blue background
    QPalette pal;
    QColor blue(25,25,112);
    pal.setColor(QPalette::Background, blue);
    setAutoFillBackground(true);
    setPalette(pal);
    show();
}

QPoint World2CameraCoord(QPointF point_world, QWidget* widget, float x_max, float y_max )
{
    //Converts world coordinates to camera coordinates for plotting
    QSize widgetSize = widget->size();
    QPoint point_camera;
    point_camera.setX((point_world.x() / x_max + 1) * widgetSize.width() / 2);
    point_camera.setY((-point_world.y() / y_max + 1) * widgetSize.height() / 2);
    return point_camera;
}

void CameraViewPainter::paintEvent(QPaintEvent* /*event*/)
{
    QPainter painter(this);

    _backGround->DrawGround(painter, _roll, _pitch, _x_max, _y_max);
    // Setup static middle line
    painter.setPen(Qt::black);
    QPoint p1 = World2CameraCoord(QPointF{-_x_max, 0.0}, this, _x_max, _y_max);
    QPoint p2 = World2CameraCoord(QPointF{_x_max, 0.0}, this, _x_max, _y_max);
    painter.drawLine(QLine(p1,p2));
    // Setup horizontal lines
    PaintHorizontalLine(painter, _roll, _pitch);
    // Setup vertical lines
    PaintVerticalLine(painter, _roll,  _yaw);
}

void CameraViewPainter::PaintHorizontalLine(QPainter & painter, float roll, float pitch)// angles in degree
{
    auto roll_r = deg2rad(roll);
    painter.setPen(Qt::white);
    float length = _x_max * 0.75;
    float distance_between_lines = 10;
    // draw above
    float y = WrapAround(pitch, 0.0f, distance_between_lines);
    y = distance_between_lines - y;
    while(y < _y_max)
    {
        QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{-length, y}, -roll_r),this, _x_max, _y_max);
        QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{length, y}, -roll_r),this, _x_max, _y_max);
        painter.drawLine(QLine(p1,p2));
        y += distance_between_lines;
    }
    // draw below
    y = WrapAround(pitch, 0.0f, distance_between_lines);
    y =  - y;
    while(y > -_y_max)
    {
        QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{-length, y}, -roll_r),this, _x_max, _y_max);
        QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{length, y}, -roll_r),this, _x_max, _y_max);
        painter.drawLine(QLine(p1,p2));
        y -= distance_between_lines;
    }
}
void CameraViewPainter::PaintVerticalLine(QPainter & painter, float roll, float yaw) // angles in degree
{
    auto roll_r = deg2rad(roll);
    painter.setPen(Qt::white);
    float length= 1;
    float distance_between_lines = 10;
    QPointF text_offset{0,2};
    // draw to the right
    float x = WrapAround(yaw, 0.0f, distance_between_lines);
    x = distance_between_lines - x;
    while(x < _x_max)
    {
        QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{x,-length}, -roll_r),this, _x_max, _y_max);
        QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{x, length}, -roll_r),this, _x_max, _y_max);
        painter.drawLine(QLine(p1,p2));
        QPoint p3 = World2CameraCoord(qt_utils::rotate(QPointF{x, 0} + text_offset,-roll_r),this, _x_max, _y_max);
        float num = WrapAround(x+yaw,-180.0f, 180.0f);
        // Wrapping may not return exactly zero:
        if( std::abs(num) < 0.0005)
        {
            num = 0.0;
        }
        painter.drawText(p3,QString::number(num));
        x += distance_between_lines;
    }
    // draw to the left
    x = WrapAround(yaw, 0.0f, distance_between_lines);
    x =  - x;
    while(x > -_x_max)
    {
        QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{x, -length}, -roll_r),this, _x_max, _y_max);
        QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{x, length}, -roll_r),this, _x_max, _y_max);
        painter.drawLine(QLine(p1,p2));
        QPoint p3 = World2CameraCoord(qt_utils::rotate(QPointF{x, 0} + text_offset,-roll_r),this, _x_max, _y_max);
        painter.drawText(p3,QString::number(WrapAround(x+yaw,-180.0f, 180.0f)));
        x -= distance_between_lines;
    }
    // Line in the middle
    QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{0.0,-2.0*length}, -roll_r),this, _x_max, _y_max);
    QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{0.0, 2.0*length}, -roll_r),this, _x_max, _y_max);
    painter.drawLine(QLine(p1,p2));
}

void Horizon::DrawGround(QPainter & painter, float roll, float pitch,float x_max, float y_max) // roll, pitch in degree
{
    auto roll_r = deg2rad(roll);
    QVector<QPoint> points;
    // Magic number 10, so that the ground is always there
    QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{-10.0*x_max, -pitch}, -roll_r), _widget, x_max, y_max);
    QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{-10.0*x_max, -4.0*y_max-pitch}, -roll_r), _widget, x_max, y_max);
    QPoint p3 = World2CameraCoord(qt_utils::rotate(QPointF{10.0*x_max, -4.0*y_max-pitch}, -roll_r), _widget, x_max, y_max);
    QPoint p4 = World2CameraCoord(qt_utils::rotate(QPointF{10.0*x_max, -pitch}, -roll_r), _widget, x_max, y_max);
    points.append(p1);
    points.append(p2);
    points.append(p3);
    points.append(p4);
    QColor green(0,100,0);
    painter.setPen(green);
    painter.setBrush(green);
    QPolygon ground(points);
    painter.drawPolygon(ground);
}
void CameraBackGround::DrawGround(QPainter & painter, float /*roll*/, float /*pitch*/,float /*x_max*/, float /*y_max*/) // roll, pitch in degree
{
    QSize widgetSize = _widget->size();
    QRect rect(0,0,widgetSize.width(),widgetSize.height());
    painter.drawImage(rect, _image);

}
<commit_msg>Fix drawing of yaw<commit_after>#include "camera_view_painter.h"
#include <QPainter>
#include <QPoint>
#include <QPointF>
#include <QtCore/qmath.h>
#include "math/functions.h"
#include <math/constants.h>
#include <QPolygon>
#include <QVector>
#include "qt_util.h"
#include <iostream>
#include <QColor>
#include <QRect>

CameraViewPainter::CameraViewPainter(float const & roll, float const & yaw, float const & pitch) :
    _roll(roll),
    _yaw(yaw),
    _pitch(pitch),
    _backGround(&_horizon),
    _horizon(this),
    _cameraBackGround(this)
{
    // set blue background
    QPalette pal;
    QColor blue(25,25,112);
    pal.setColor(QPalette::Background, blue);
    setAutoFillBackground(true);
    setPalette(pal);
    show();
}

QPoint World2CameraCoord(QPointF point_world, QWidget* widget, float x_max, float y_max )
{
    //Converts world coordinates to camera coordinates for plotting
    QSize widgetSize = widget->size();
    QPoint point_camera;
    point_camera.setX((point_world.x() / x_max + 1) * widgetSize.width() / 2);
    point_camera.setY((-point_world.y() / y_max + 1) * widgetSize.height() / 2);
    return point_camera;
}

void CameraViewPainter::paintEvent(QPaintEvent* /*event*/)
{
    QPainter painter(this);

    _backGround->DrawGround(painter, _roll, _pitch, _x_max, _y_max);
    // Setup static middle line
    painter.setPen(Qt::black);
    QPoint p1 = World2CameraCoord(QPointF{-_x_max, 0.0}, this, _x_max, _y_max);
    QPoint p2 = World2CameraCoord(QPointF{_x_max, 0.0}, this, _x_max, _y_max);
    painter.drawLine(QLine(p1,p2));
    // Setup horizontal lines
    PaintHorizontalLine(painter, _roll, _pitch);
    // Setup vertical lines
    PaintVerticalLine(painter, _roll,  _yaw);
}

void CameraViewPainter::PaintHorizontalLine(QPainter & painter, float roll, float pitch)// angles in degree
{
    auto roll_r = deg2rad(roll);
    painter.setPen(Qt::white);
    float length = _x_max * 0.75;
    float distance_between_lines = 10;
    // draw above
    float y = WrapAround(pitch, 0.0f, distance_between_lines);
    y = distance_between_lines - y;
    while(y < _y_max)
    {
        QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{-length, y}, -roll_r),this, _x_max, _y_max);
        QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{length, y}, -roll_r),this, _x_max, _y_max);
        painter.drawLine(QLine(p1,p2));
        y += distance_between_lines;
    }
    // draw below
    y = WrapAround(pitch, 0.0f, distance_between_lines);
    y =  - y;
    while(y > -_y_max)
    {
        QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{-length, y}, -roll_r),this, _x_max, _y_max);
        QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{length, y}, -roll_r),this, _x_max, _y_max);
        painter.drawLine(QLine(p1,p2));
        y -= distance_between_lines;
    }
}
void CameraViewPainter::PaintVerticalLine(QPainter & painter, float roll, float yaw) // angles in degree
{
    auto roll_r = deg2rad(roll);
    painter.setPen(Qt::white);
    float length= 1;
    float distance_between_lines = 10;
    QPointF text_offset{0,2};
    // draw to the right
    float x = WrapAround(yaw, 0.0f, distance_between_lines);
    while(x < _x_max)
    {
        QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{x,-length}, -roll_r),this, _x_max, _y_max);
        QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{x, length}, -roll_r),this, _x_max, _y_max);
        painter.drawLine(QLine(p1,p2));
        QPoint p3 = World2CameraCoord(qt_utils::rotate(QPointF{x, 0} + text_offset,-roll_r),this, _x_max, _y_max);
        float num = WrapAround(yaw-x,-180.0f, 180.0f);
        // Wrapping may not return exactly zero:
        if( std::abs(num) < 0.00005)
        {
            num = 0.0;
        }
        painter.drawText(p3,QString::number(num));
        x += distance_between_lines;
    }
    // draw to the left
    x = WrapAround(yaw, 0.0f, distance_between_lines);
    x = x - distance_between_lines;
    while(x > -_x_max)
    {
        QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{x, -length}, -roll_r),this, _x_max, _y_max);
        QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{x, length}, -roll_r),this, _x_max, _y_max);
        painter.drawLine(QLine(p1,p2));
        QPoint p3 = World2CameraCoord(qt_utils::rotate(QPointF{x, 0} + text_offset,-roll_r),this, _x_max, _y_max);
        float num = WrapAround(yaw-x,-180.0f, 180.0f);
        // Wrapping may not return exactly zero:
        if( std::abs(num) < 0.00005)
        {
            num = 0.0;
        }
        painter.drawText(p3,QString::number(num));
        x -= distance_between_lines;
    }
    // Line in the middle
    QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{0.0,-2.0*length}, -roll_r),this, _x_max, _y_max);
    QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{0.0, 2.0*length}, -roll_r),this, _x_max, _y_max);
    painter.drawLine(QLine(p1,p2));
}

void Horizon::DrawGround(QPainter & painter, float roll, float pitch,float x_max, float y_max) // roll, pitch in degree
{
    auto roll_r = deg2rad(roll);
    QVector<QPoint> points;
    // Magic number 10, so that the ground is always there
    QPoint p1 = World2CameraCoord(qt_utils::rotate(QPointF{-10.0*x_max, -pitch}, -roll_r), _widget, x_max, y_max);
    QPoint p2 = World2CameraCoord(qt_utils::rotate(QPointF{-10.0*x_max, -4.0*y_max-pitch}, -roll_r), _widget, x_max, y_max);
    QPoint p3 = World2CameraCoord(qt_utils::rotate(QPointF{10.0*x_max, -4.0*y_max-pitch}, -roll_r), _widget, x_max, y_max);
    QPoint p4 = World2CameraCoord(qt_utils::rotate(QPointF{10.0*x_max, -pitch}, -roll_r), _widget, x_max, y_max);
    points.append(p1);
    points.append(p2);
    points.append(p3);
    points.append(p4);
    QColor green(0,100,0);
    painter.setPen(green);
    painter.setBrush(green);
    QPolygon ground(points);
    painter.drawPolygon(ground);
}
void CameraBackGround::DrawGround(QPainter & painter, float /*roll*/, float /*pitch*/,float /*x_max*/, float /*y_max*/) // roll, pitch in degree
{
    QSize widgetSize = _widget->size();
    QRect rect(0,0,widgetSize.width(),widgetSize.height());
    painter.drawImage(rect, _image);

}
<|endoftext|>


<commit_before>#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>

using namespace std;

const int NX=256, NY=NX;
int T_FINAL;

const int X_MASK = NX-1, Y_MASK=NY-1;

const int NT=32;
const int NTO=NX/NT;
const int NF=NX/4;

double dens_initial[NX][NX];
double dens_final[NX][NX];

double dens_final_pitch[NX][NX];

double yuka[NTO][NTO][1][NT+2][NT+2];

const int N_KABE=NF+NT/2+2;
double yuka_tmp[1][NT+2][NT+2];
double kabe_y[NTO][NTO][N_KABE][2][NT+2];
double kabe_x[NTO][NTO][N_KABE][NT+2][2];

static double second(){
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return double(tv.tv_sec) + 1.e-6*double(tv.tv_usec);
}

void initialize() {
  for (int y=0; y<NY; ++y) {
    for (int x=0; x<NX; ++x) {
      dens_initial[y][x]=(rand()/double(INT_MAX)*2 > 1 ? 1 : 0);
      dens_final[y][x]=424242;
    }
  }
}
void dump(const char* fn) {
  ofstream ofs(fn);
  for (int y=0; y<NY; ++y) {
    for(int x=0;x<NX;++x) {
      ofs << x << " " << y << " " << dens_final[y][x] << endl;
    }
    ofs << endl;
  }
}

inline double stencil_function(double o, double a, double b, double c, double d) {
  return 0.5*o+0.125*(a+b+c+d);
}

double ref_buf[NX][NX];
double ref_buf2[NX][NX];

void compute_reference() {
  for (int y=0;y<NY;++y) {
    for (int x=0;x<NX;++x) {
      ref_buf[y][x]=dens_initial[y][x];
    }
  }

  for (int t=1; t <=T_FINAL; ++t) {
    for (int y=0;y<NY;++y) {
      for (int x=0;x<NX;++x) {
        ref_buf2[y][x]=stencil_function
          (ref_buf[y][x],
           ref_buf[(y-1)&Y_MASK][x],
           ref_buf[(y+1)&Y_MASK][x],
           ref_buf[y][(x-1)&X_MASK],
           ref_buf[y][(x+1)&X_MASK]
           );
      }
    }
    swap(ref_buf, ref_buf2);
  }
  for (int y=0;y<NY;++y) {
    for (int x=0;x<NX;++x) {
      dens_final[y][x] = ref_buf[y][x];
    }
  }
}

double work[N_KABE][NT+2][NT+2];

void pitch_kernel
(int t_orig, int y_orig, int x_orig,
 double yuka_in[1][NT+2][NT+2], double kabe_y_in[N_KABE][2][NT+2], double kabe_x_in[N_KABE][NT+2][2],
 double yuka_out[1][NT+2][NT+2], double kabe_y_out[N_KABE][2][NT+2], double kabe_x_out[N_KABE][NT+2][2])
{
  for(int t=0; t<NF+NT/4+2;++t) {
    for(int y=0; y<NT+2; ++y) {
      work[t][y][0] = kabe_x_in[t+NT/4][y][0];
      work[t][y][1] = kabe_x_in[t+NT/4][y][1];
    }
    for(int x=0; x<NT+2; ++x) {
      work[t][0][x] = kabe_y_in[t+NT/4][0][x];
      work[t][1][x] = kabe_y_in[t+NT/4][1][x];
    }
  }

  for(int t=0; t<NF+NT/2+2;++t) {
    for(int y=0; y<NT+2; ++y) {
      for(int x=0; x<NT+2; ++x) {
        int t_k=t, y_k = y-t, x_k = x-t;
        int t_dash = (2*t_k-x_k-y_k)>>2;
        int y_dash = y;
        int x_dash = x;
        const bool in_region = t_dash >=0 && t_dash < NF+1;

        if (in_region) {
          double ret=work[t][y][x];
          if (t_k + t_orig == 0) {
            ret = dens_initial[(y_k+y_orig) & Y_MASK][(x_k+x_orig) & X_MASK];
          } else if (t_dash == 0) {
            ret = yuka_in[0][y_dash][x_dash];
          } else if (t+t_orig>0 && y>=2 && x>=2) {
            asm volatile("#kernel");
            ret = stencil_function(work[t-1][y-1][x-1],work[t-1][y-2][x-1],work[t-1][y][x-1],work[t-1][y-1][x-2],work[t-1][y-1][x]);
          }

          work[t][y][x] = ret;

          if (t_dash == NF) {
            yuka_out[0][y_dash][x_dash] = ret;
          }
          if (y_dash >= NT) {
            kabe_y_out[t][y_dash-NT][x_dash] = ret;
          }
          if (x_dash >= NT) {
            kabe_x_out[t][y_dash][x_dash-NT] = ret;
          }
          if (t_k + t_orig == T_FINAL) {
            dens_final[(y_k+y_orig) & Y_MASK][(x_k+x_orig) & X_MASK] = ret;
          }
        }
      }
    }
  }
}

void compute_pitch(){
  for(int t_orig=-2*NX; t_orig <= T_FINAL; t_orig+=NF) {
    int y_orig = -t_orig;
    int x_orig = -t_orig;
    for (int yo=0;yo<NTO;++yo) {
      for (int xo=0;xo<NTO;++xo) {
        int dy = yo*NT, dx = xo*NT;
        pitch_kernel
          (t_orig+(dx+dy)/4,
           y_orig+(3*dy-dx)/4,
           x_orig+(3*dx-dy)/4,
           yuka[yo][xo],kabe_y[yo][xo],kabe_x[yo][xo],
           yuka_tmp,kabe_y[(yo+1)%NTO][xo],kabe_x[yo][(xo+1)%NTO]);
        swap(yuka[yo][xo], yuka_tmp);
      }
    }
  }
}

int main ()
{
  double n_flop[2], wct_pitch[2], wct_ref[2];

  for(int part=0; part<2; ++part) {
    T_FINAL = NX*(2+part);
    initialize();
    n_flop[part]=6.0*NX*NX*double(T_FINAL);
    double t1 = second();
    compute_pitch();
    double t2 = second();
    cout << "PiTCH: " << n_flop[part] << " flop " << (t2-t1) << " second" << endl;
    wct_pitch[part] = t2-t1;

    swap(dens_final, dens_final_pitch);

    double t3 = second();
    compute_reference();
    double t4 = second();
    cout << "Ref: " << n_flop[part] << " flop " << (t4-t3) << " second" << endl;
    wct_ref[part] = t4-t3;
    for (int y=0;y<NY;++y) {
      for (int x=0;x<NX;++x){
        assert(dens_final[y][x]==dens_final_pitch[y][x]);
      }
    }
  }

  cerr << "PiTCH: " << (n_flop[1]-n_flop[0])/(wct_pitch[1]-wct_pitch[0]) << " flop/s " << endl;
  cerr << "Ref: " << (n_flop[1]-n_flop[0])/(wct_ref[1]-wct_ref[0]) << " flop/s " << endl;
}
<commit_msg>confirm shrinking of the region<commit_after>#include <algorithm>
#include <cassert>
#include <climits>
#include <cstdlib>
#include <sys/time.h>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <sstream>

using namespace std;

const int NX=256, NY=NX;
int T_FINAL;

const int X_MASK = NX-1, Y_MASK=NY-1;

const int NT=32;
const int NTO=NX/NT;
const int NF=NX/4;

double dens_initial[NX][NX];
double dens_final[NX][NX];

double dens_final_pitch[NX][NX];

double yuka[NTO][NTO][1][NT+2][NT+2];

const int N_KABE=NF+NT/2+2;
double yuka_tmp[1][NT+2][NT+2];
double kabe_y[NTO][NTO][N_KABE][2][NT+2];
double kabe_x[NTO][NTO][N_KABE][NT+2][2];

static double second(){
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return double(tv.tv_sec) + 1.e-6*double(tv.tv_usec);
}

void initialize() {
  for (int y=0; y<NY; ++y) {
    for (int x=0; x<NX; ++x) {
      dens_initial[y][x]=(rand()/double(INT_MAX)*2 > 1 ? 1 : 0);
      dens_final[y][x]=424242;
    }
  }
}
void dump(const char* fn) {
  ofstream ofs(fn);
  for (int y=0; y<NY; ++y) {
    for(int x=0;x<NX;++x) {
      ofs << x << " " << y << " " << dens_final[y][x] << endl;
    }
    ofs << endl;
  }
}

inline double stencil_function(double o, double a, double b, double c, double d) {
  return 0.5*o+0.125*(a+b+c+d);
}

double ref_buf[NX][NX];
double ref_buf2[NX][NX];

void compute_reference() {
  for (int y=0;y<NY;++y) {
    for (int x=0;x<NX;++x) {
      ref_buf[y][x]=dens_initial[y][x];
    }
  }

  for (int t=1; t <=T_FINAL; ++t) {
    for (int y=0;y<NY;++y) {
      for (int x=0;x<NX;++x) {
        ref_buf2[y][x]=stencil_function
          (ref_buf[y][x],
           ref_buf[(y-1)&Y_MASK][x],
           ref_buf[(y+1)&Y_MASK][x],
           ref_buf[y][(x-1)&X_MASK],
           ref_buf[y][(x+1)&X_MASK]
           );
      }
    }
    swap(ref_buf, ref_buf2);
  }
  for (int y=0;y<NY;++y) {
    for (int x=0;x<NX;++x) {
      dens_final[y][x] = ref_buf[y][x];
    }
  }
}

double work[N_KABE][NT+2][NT+2];

void pitch_kernel
(int t_orig, int y_orig, int x_orig,
 double yuka_in[1][NT+2][NT+2], double kabe_y_in[N_KABE][2][NT+2], double kabe_x_in[N_KABE][NT+2][2],
 double yuka_out[1][NT+2][NT+2], double kabe_y_out[N_KABE][2][NT+2], double kabe_x_out[N_KABE][NT+2][2])
{
  for(int t=0; t<NF+NT/4+2;++t) {
    for(int y=0; y<NT+2; ++y) {
      work[t][y][0] = kabe_x_in[t+NT/4][y][0];
      work[t][y][1] = kabe_x_in[t+NT/4][y][1];
    }
    for(int x=0; x<NT+2; ++x) {
      work[t][0][x] = kabe_y_in[t+NT/4][0][x];
      work[t][1][x] = kabe_y_in[t+NT/4][1][x];
    }
  }

  for(int t=1; t<NF+NT/2+2;++t) {
    for(int y=2; y<NT+2; ++y) {
      for(int x=2; x<NT+2; ++x) {
        int t_k=t, y_k = y-t, x_k = x-t;
        int t_dash = (2*t_k-x_k-y_k)>>2;
        int y_dash = y;
        int x_dash = x;
        const bool in_region = t_dash >=0 && t_dash < NF+1;

        if (in_region) {
          double ret=work[t][y][x];
          if (t_k + t_orig == 0) {
            ret = dens_initial[(y_k+y_orig) & Y_MASK][(x_k+x_orig) & X_MASK];
          } else if (t_dash == 0) {
            ret = yuka_in[0][y_dash][x_dash];
          } else if (t+t_orig>0 && y>=2 && x>=2) {
            asm volatile("#kernel");
            ret = stencil_function(work[t-1][y-1][x-1],work[t-1][y-2][x-1],work[t-1][y][x-1],work[t-1][y-1][x-2],work[t-1][y-1][x]);
          }

          work[t][y][x] = ret;

          if (t_dash == NF) {
            yuka_out[0][y_dash][x_dash] = ret;
          }
          if (t_k + t_orig == T_FINAL) {
            dens_final[(y_k+y_orig) & Y_MASK][(x_k+x_orig) & X_MASK] = ret;
          }
        }
      }
    }
  }
  for(int t=0; t<NF+NT/2+2;++t) {
    for(int x=0; x<NT+2; ++x) {
      kabe_y_out[t][0][x] = work[t][NT+0][x];
      kabe_y_out[t][1][x] = work[t][NT+1][x];
    }
    for(int y=0; y<NT+2; ++y) {
      kabe_x_out[t][y][0] = work[t][y][NT+0];
      kabe_x_out[t][y][1] = work[t][y][NT+1];
    }
  }

}

void compute_pitch(){
  for(int t_orig=-2*NX; t_orig <= T_FINAL; t_orig+=NF) {
    int y_orig = -t_orig;
    int x_orig = -t_orig;
    for (int yo=0;yo<NTO;++yo) {
      for (int xo=0;xo<NTO;++xo) {
        int dy = yo*NT, dx = xo*NT;
        pitch_kernel
          (t_orig+(dx+dy)/4,
           y_orig+(3*dy-dx)/4,
           x_orig+(3*dx-dy)/4,
           yuka[yo][xo],kabe_y[yo][xo],kabe_x[yo][xo],
           yuka_tmp,kabe_y[(yo+1)%NTO][xo],kabe_x[yo][(xo+1)%NTO]);
        swap(yuka[yo][xo], yuka_tmp);
      }
    }
  }
}

int main ()
{
  double n_flop[2], wct_pitch[2], wct_ref[2];

  for(int part=0; part<2; ++part) {
    T_FINAL = NX*(2+part);
    initialize();
    n_flop[part]=6.0*NX*NX*double(T_FINAL);
    double t1 = second();
    compute_pitch();
    double t2 = second();
    cout << "PiTCH: " << n_flop[part] << " flop " << (t2-t1) << " second" << endl;
    wct_pitch[part] = t2-t1;

    swap(dens_final, dens_final_pitch);

    double t3 = second();
    compute_reference();
    double t4 = second();
    cout << "Ref: " << n_flop[part] << " flop " << (t4-t3) << " second" << endl;
    wct_ref[part] = t4-t3;
    for (int y=0;y<NY;++y) {
      for (int x=0;x<NX;++x){
        assert(dens_final[y][x]==dens_final_pitch[y][x]);
      }
    }
  }

  cerr << "PiTCH: " << (n_flop[1]-n_flop[0])/(wct_pitch[1]-wct_pitch[0]) << " flop/s " << endl;
  cerr << "Ref: " << (n_flop[1]-n_flop[0])/(wct_ref[1]-wct_ref[0]) << " flop/s " << endl;
}
<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "FsDiscoverer.h"

#include <algorithm>
#include <queue>
#include <utility>

#include "factory/FileSystemFactory.h"
#include "medialibrary/filesystem/IDevice.h"
#include "Media.h"
#include "File.h"
#include "Device.h"
#include "Folder.h"
#include "logging/Logger.h"
#include "MediaLibrary.h"
#include "probe/CrawlerProbe.h"
#include "utils/Filename.h"

namespace medialibrary
{

FsDiscoverer::FsDiscoverer( std::shared_ptr<fs::IFileSystemFactory> fsFactory, MediaLibrary* ml, IMediaLibraryCb* cb, std::unique_ptr<prober::IProbe> probe )
    : m_ml( ml )
    , m_fsFactory( std::move( fsFactory ))
    , m_cb( cb )
    , m_probe( std::move( probe ) )
{
}

bool FsDiscoverer::discover( const std::string& entryPoint,
                             const IInterruptProbe& interruptProbe )
{
    if ( m_fsFactory->isMrlSupported( entryPoint ) == false )
        return false;

    std::shared_ptr<fs::IDirectory> fsDir;
    try
    {
        fsDir = m_fsFactory->createDirectory( entryPoint );
    }
    catch ( std::system_error& ex )
    {
        LOG_WARN( entryPoint, " discovery aborted because of a filesystem error: ", ex.what() );
        return true;
    }
    auto fsDirMrl = fsDir->mrl(); // Saving MRL now since we might need it after fsDir is moved
    auto f = Folder::fromMrl( m_ml, fsDirMrl );
    // If the folder exists, we assume it will be handled by reload()
    if ( f != nullptr )
        return true;
    try
    {
        if ( m_probe->proceedOnDirectory( *fsDir ) == false || m_probe->isHidden( *fsDir ) == true )
            return true;
        // Fetch files explicitly
        fsDir->files();
        auto res = addFolder( std::move( fsDir ), m_probe->getFolderParent().get(),
                              interruptProbe );
        m_ml->getCb()->onEntryPointAdded( entryPoint, res );
        return res;
    }
    catch ( sqlite::errors::ConstraintViolation& ex )
    {
        LOG_DEBUG( fsDirMrl, " discovery aborted (assuming banned folder): ", ex.what() );
    }
    catch ( fs::DeviceRemovedException& )
    {
        // Simply ignore, the device has already been marked as removed and the DB updated accordingly
        LOG_DEBUG( "Discovery of ", fsDirMrl, " was stopped after the device was removed" );
    }
    return true;
}

bool FsDiscoverer::reloadFolder( std::shared_ptr<Folder> f,
                                 const IInterruptProbe& probe )
{
    assert( f->isPresent() );
    auto mrl = f->mrl();

    std::shared_ptr<fs::IDirectory> directory;
    try
    {
        directory = m_fsFactory->createDirectory( mrl );
        assert( directory->device() != nullptr );
        if ( directory->device() == nullptr )
            return false;
    }
    catch ( const std::system_error& ex )
    {
        LOG_INFO( "Failed to instanciate a directory for ", mrl, ": ", ex.what(),
                  ". Can't reload the folder." );
    }
    if ( directory == nullptr )
    {
        auto device = m_fsFactory->createDeviceFromMrl( mrl );
        if ( device == nullptr || device->isRemovable() == false )
        {
            LOG_DEBUG( "Failed to find folder matching entrypoint ", mrl, ". "
                      "Removing that folder" );
            m_ml->deleteFolder( *f );
            return false;
        }
    }
    try
    {
        checkFolder( std::move( directory ), std::move( f ), false, probe );
    }
    catch ( fs::DeviceRemovedException& )
    {
        LOG_INFO( "Reloading of ", mrl, " was stopped after the device was removed" );
        return false;
    }
    return true;
}

bool FsDiscoverer::reload( const IInterruptProbe& interruptProbe )
{
    LOG_INFO( "Reloading all folders" );
    auto rootFolders = Folder::fetchRootFolders( m_ml );
    for ( const auto& f : rootFolders )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        // fetchRootFolders only returns present folders
        assert( f->isPresent() == true );
        auto mrl = f->mrl();
        if ( m_fsFactory->isMrlSupported( mrl ) == false )
            continue;
        m_cb->onReloadStarted( mrl );
        auto res = reloadFolder( f, interruptProbe );
        m_cb->onReloadCompleted( mrl, res );
    }
    return true;
}

bool FsDiscoverer::reload( const std::string& entryPoint,
                           const IInterruptProbe& interruptProbe )
{
    if ( m_fsFactory->isMrlSupported( entryPoint ) == false )
        return false;
    auto folder = Folder::fromMrl( m_ml, entryPoint );
    if ( folder == nullptr )
    {
        LOG_ERROR( "Can't reload ", entryPoint, ": folder wasn't found in database" );
        return false;
    }
    if ( folder->isPresent() == false )
    {
        LOG_INFO( "Folder ", entryPoint, " isn't present, and therefore won't "
                  "be reloaded" );
        return false;
    }
    reloadFolder( std::move( folder ), interruptProbe );
    return true;
}

void FsDiscoverer::checkFolder( std::shared_ptr<fs::IDirectory> currentFolderFs,
                                std::shared_ptr<Folder> currentFolder,
                                bool newFolder,
                                const IInterruptProbe& interruptProbe ) const
{
    try
    {
        // We already know of this folder, though it may now contain a .nomedia file.
        // In this case, simply delete the folder.
        if ( m_probe->isHidden( *currentFolderFs ) == true )
        {
            if ( newFolder == false )
                m_ml->deleteFolder( *currentFolder );
            return;
        }
        // Ensuring that the file fetching is done in this scope, to catch errors
        currentFolderFs->files();
    }
    // Only check once for a system_error. They are bound to happen when we list the files/folders
    // within, and IProbe::isHidden is the first place when this is done
    catch ( std::system_error& ex )
    {
        LOG_WARN( "Failed to browse ", currentFolderFs->mrl(), ": ", ex.what() );
        // Even when we're discovering a new folder, we want to rule out device removal as the cause of
        // an IO error. If this is the cause, simply abort the discovery. All the folder we have
        // discovered so far will be marked as non-present through sqlite hooks, and we'll resume the
        // discovery when the device gets plugged back in
        auto device = currentFolderFs->device();
        // The device might not be present at all, and therefor we might miss a
        // representation for it.
        if ( device == nullptr || device->isRemovable() )
        {
            // If the device is removable/missing, check if it was indeed removed.
            LOG_INFO( "The device containing ", currentFolderFs->mrl(), " is ",
                      device != nullptr ? "removable" : "not found",
                      ". Refreshing device cache..." );

            m_ml->refreshDevices( *m_fsFactory );
            // If the device was missing, refresh our list of devices in case
            // the device was plugged back and/or we missed a notification for it
            if ( device == nullptr )
                device = currentFolderFs->device();
            // The device presence flag will be changed in place, so simply retest it
            if ( device == nullptr || device->isPresent() == false )
                throw fs::DeviceRemovedException();
            LOG_INFO( "Device was not removed" );
        }
        // However if the device isn't removable, we want to:
        // - ignore it when we're discovering a new folder.
        // - delete it when it was discovered in the past. This is likely to be due to a permission change
        //   as we would not check the folder if it wasn't present during the parent folder browsing
        //   but it might also be that we're checking an entry point.
        //   The error won't arise earlier, as we only perform IO when reading the folder from this function.
        if ( newFolder == false )
        {
            // If we ever came across this folder, its content is now unaccessible: let's remove it.
            m_ml->deleteFolder( *currentFolder );
        }
        return;
    }

    if ( m_cb != nullptr )
        m_cb->onDiscoveryProgress( currentFolderFs->mrl() );
    // Load the folders we already know of:
    LOG_DEBUG( "Checking for modifications in ", currentFolderFs->mrl() );
    // Don't try to fetch any potential sub folders if the folder was freshly added
    std::vector<std::shared_ptr<Folder>> subFoldersInDB;
    if ( newFolder == false )
        subFoldersInDB = currentFolder->folders();
    for ( const auto& subFolder : currentFolderFs->dirs() )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        if ( subFolder->device() == nullptr )
            continue;
        if ( m_probe->stopFileDiscovery() == true )
            break;
        if ( m_probe->proceedOnDirectory( *subFolder ) == false )
            continue;
        auto it = std::find_if( begin( subFoldersInDB ), end( subFoldersInDB ), [&subFolder](const std::shared_ptr<Folder>& f) {
            return f->mrl() == subFolder->mrl();
        });
        // We don't know this folder, it's a new one
        if ( it == end( subFoldersInDB ) )
        {
            if ( m_probe->isHidden( *subFolder ) )
                continue;
            LOG_DEBUG( "New folder detected: ", subFolder->mrl() );
            try
            {
                addFolder( subFolder, currentFolder.get(), interruptProbe );
                continue;
            }
            catch ( sqlite::errors::ConstraintViolation& ex )
            {
                // Best attempt to detect a foreign key violation, indicating the
                // parent folders have been deleted due to being banned
                if ( strstr( ex.what(), "foreign key" ) != nullptr )
                {
                    LOG_WARN( "Creation of a folder failed because the parent is non existing: ", ex.what(),
                              ". Assuming it was deleted due to being banned" );
                    return;
                }
                LOG_WARN( "Creation of a duplicated folder failed: ", ex.what(), ". Assuming it was banned" );
                continue;
            }
        }
        auto folderInDb = *it;
        // In any case, check for modifications, as a change related to a mountpoint might
        // not update the folder modification date.
        // Also, relying on the modification date probably isn't portable
        checkFolder( subFolder, folderInDb, false, interruptProbe );
        subFoldersInDB.erase( it );
    }
    if ( m_probe->deleteUnseenFolders() == true )
    {
        // Now all folders we had in DB but haven't seen from the FS must have been deleted.
        for ( const auto& f : subFoldersInDB )
        {
            LOG_DEBUG( "Folder ", f->mrl(), " not found in FS, deleting it" );
            m_ml->deleteFolder( *f );
        }
    }
    checkFiles( currentFolderFs, currentFolder, interruptProbe );
    LOG_DEBUG( "Done checking subfolders in ", currentFolderFs->mrl() );
}

void FsDiscoverer::checkFiles( std::shared_ptr<fs::IDirectory> parentFolderFs,
                               std::shared_ptr<Folder> parentFolder,
                               const IInterruptProbe& interruptProbe) const
{
    LOG_DEBUG( "Checking file in ", parentFolderFs->mrl() );

    auto files = File::fromParentFolder( m_ml, parentFolder->id() );
    std::vector<std::shared_ptr<fs::IFile>> filesToAdd;
    std::vector<std::pair<std::shared_ptr<File>, std::shared_ptr<fs::IFile>>> filesToRefresh;
    for ( const auto& fileFs: parentFolderFs->files() )
    {
        if ( interruptProbe.isInterrupted() == true )
            return;
        if ( m_probe->stopFileDiscovery() == true )
            break;
        if ( m_probe->proceedOnFile( *fileFs ) == false )
            continue;
        auto it = std::find_if( begin( files ), end( files ), [fileFs](const std::shared_ptr<File>& f) {
            return f->mrl() == fileFs->mrl();
        });
        if ( it == end( files ) || m_probe->forceFileRefresh() == true )
        {
            if ( MediaLibrary::isExtensionSupported( fileFs->extension().c_str() ) == true )
                filesToAdd.push_back( fileFs );
            continue;
        }
        if ( fileFs->lastModificationDate() != (*it)->lastModificationDate() )
        {
            LOG_DEBUG( "Forcing file refresh ", fileFs->mrl() );
            filesToRefresh.emplace_back( std::move( *it ), fileFs );
        }
        files.erase( it );
    }
    if ( m_probe->deleteUnseenFiles() == false )
        files.clear();

    auto t = m_ml->getConn()->newTransaction();
    for ( const auto& file : files )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        LOG_DEBUG( "File ", file->mrl(), " not found on filesystem, deleting it" );
        auto media = file->media();
        if ( media != nullptr )
            media->removeFile( *file );
        else
        {
            // This is unexpected, as the file should have been deleted when the media was
            // removed.
            LOG_WARN( "Deleting a file without an associated media." );
            file->destroy();
        }
    }
    for ( auto& p: filesToRefresh )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        m_ml->onUpdatedFile( std::move( p.first ), std::move( p.second ) );
    }
    // Insert all files at once to avoid SQL write contention
    for ( auto& p : filesToAdd )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        m_ml->onDiscoveredFile( p, parentFolder, parentFolderFs,
                                IFile::Type::Main, m_probe->getPlaylistParent() );
    }
    t->commit();
    LOG_DEBUG( "Done checking files in ", parentFolderFs->mrl() );
}

bool FsDiscoverer::addFolder( std::shared_ptr<fs::IDirectory> folder,
                              Folder* parentFolder,
                              const IInterruptProbe& interruptProbe ) const
{
    auto deviceFs = folder->device();
    // We are creating a folder, there has to be a device containing it.
    assert( deviceFs != nullptr );
    // But gracefully handle failure in release mode
    if( deviceFs == nullptr )
        return false;
    auto device = Device::fromUuid( m_ml, deviceFs->uuid() );
    if ( device == nullptr )
    {
        LOG_INFO( "Creating new device in DB ", deviceFs->uuid() );
        device = Device::create( m_ml, deviceFs->uuid(),
                                 utils::file::scheme( folder->mrl() ),
                                 deviceFs->isRemovable() );
        if ( device == nullptr )
            return false;
    }

    auto f = Folder::create( m_ml, folder->mrl(),
                             parentFolder != nullptr ? parentFolder->id() : 0,
                             *device, *deviceFs );
    if ( f == nullptr )
        return false;
    checkFolder( std::move( folder ), std::move( f ), true, interruptProbe );
    return true;
}

}
<commit_msg>FsDiscoverer: Don't insert files in a transaction.<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2015-2019 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "FsDiscoverer.h"

#include <algorithm>
#include <queue>
#include <utility>

#include "factory/FileSystemFactory.h"
#include "medialibrary/filesystem/IDevice.h"
#include "Media.h"
#include "File.h"
#include "Device.h"
#include "Folder.h"
#include "logging/Logger.h"
#include "MediaLibrary.h"
#include "probe/CrawlerProbe.h"
#include "utils/Filename.h"

namespace medialibrary
{

FsDiscoverer::FsDiscoverer( std::shared_ptr<fs::IFileSystemFactory> fsFactory, MediaLibrary* ml, IMediaLibraryCb* cb, std::unique_ptr<prober::IProbe> probe )
    : m_ml( ml )
    , m_fsFactory( std::move( fsFactory ))
    , m_cb( cb )
    , m_probe( std::move( probe ) )
{
}

bool FsDiscoverer::discover( const std::string& entryPoint,
                             const IInterruptProbe& interruptProbe )
{
    if ( m_fsFactory->isMrlSupported( entryPoint ) == false )
        return false;

    std::shared_ptr<fs::IDirectory> fsDir;
    try
    {
        fsDir = m_fsFactory->createDirectory( entryPoint );
    }
    catch ( std::system_error& ex )
    {
        LOG_WARN( entryPoint, " discovery aborted because of a filesystem error: ", ex.what() );
        return true;
    }
    auto fsDirMrl = fsDir->mrl(); // Saving MRL now since we might need it after fsDir is moved
    auto f = Folder::fromMrl( m_ml, fsDirMrl );
    // If the folder exists, we assume it will be handled by reload()
    if ( f != nullptr )
        return true;
    try
    {
        if ( m_probe->proceedOnDirectory( *fsDir ) == false || m_probe->isHidden( *fsDir ) == true )
            return true;
        // Fetch files explicitly
        fsDir->files();
        auto res = addFolder( std::move( fsDir ), m_probe->getFolderParent().get(),
                              interruptProbe );
        m_ml->getCb()->onEntryPointAdded( entryPoint, res );
        return res;
    }
    catch ( sqlite::errors::ConstraintViolation& ex )
    {
        LOG_DEBUG( fsDirMrl, " discovery aborted (assuming banned folder): ", ex.what() );
    }
    catch ( fs::DeviceRemovedException& )
    {
        // Simply ignore, the device has already been marked as removed and the DB updated accordingly
        LOG_DEBUG( "Discovery of ", fsDirMrl, " was stopped after the device was removed" );
    }
    return true;
}

bool FsDiscoverer::reloadFolder( std::shared_ptr<Folder> f,
                                 const IInterruptProbe& probe )
{
    assert( f->isPresent() );
    auto mrl = f->mrl();

    std::shared_ptr<fs::IDirectory> directory;
    try
    {
        directory = m_fsFactory->createDirectory( mrl );
        assert( directory->device() != nullptr );
        if ( directory->device() == nullptr )
            return false;
    }
    catch ( const std::system_error& ex )
    {
        LOG_INFO( "Failed to instanciate a directory for ", mrl, ": ", ex.what(),
                  ". Can't reload the folder." );
    }
    if ( directory == nullptr )
    {
        auto device = m_fsFactory->createDeviceFromMrl( mrl );
        if ( device == nullptr || device->isRemovable() == false )
        {
            LOG_DEBUG( "Failed to find folder matching entrypoint ", mrl, ". "
                      "Removing that folder" );
            m_ml->deleteFolder( *f );
            return false;
        }
    }
    try
    {
        checkFolder( std::move( directory ), std::move( f ), false, probe );
    }
    catch ( fs::DeviceRemovedException& )
    {
        LOG_INFO( "Reloading of ", mrl, " was stopped after the device was removed" );
        return false;
    }
    return true;
}

bool FsDiscoverer::reload( const IInterruptProbe& interruptProbe )
{
    LOG_INFO( "Reloading all folders" );
    auto rootFolders = Folder::fetchRootFolders( m_ml );
    for ( const auto& f : rootFolders )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        // fetchRootFolders only returns present folders
        assert( f->isPresent() == true );
        auto mrl = f->mrl();
        if ( m_fsFactory->isMrlSupported( mrl ) == false )
            continue;
        m_cb->onReloadStarted( mrl );
        auto res = reloadFolder( f, interruptProbe );
        m_cb->onReloadCompleted( mrl, res );
    }
    return true;
}

bool FsDiscoverer::reload( const std::string& entryPoint,
                           const IInterruptProbe& interruptProbe )
{
    if ( m_fsFactory->isMrlSupported( entryPoint ) == false )
        return false;
    auto folder = Folder::fromMrl( m_ml, entryPoint );
    if ( folder == nullptr )
    {
        LOG_ERROR( "Can't reload ", entryPoint, ": folder wasn't found in database" );
        return false;
    }
    if ( folder->isPresent() == false )
    {
        LOG_INFO( "Folder ", entryPoint, " isn't present, and therefore won't "
                  "be reloaded" );
        return false;
    }
    reloadFolder( std::move( folder ), interruptProbe );
    return true;
}

void FsDiscoverer::checkFolder( std::shared_ptr<fs::IDirectory> currentFolderFs,
                                std::shared_ptr<Folder> currentFolder,
                                bool newFolder,
                                const IInterruptProbe& interruptProbe ) const
{
    try
    {
        // We already know of this folder, though it may now contain a .nomedia file.
        // In this case, simply delete the folder.
        if ( m_probe->isHidden( *currentFolderFs ) == true )
        {
            if ( newFolder == false )
                m_ml->deleteFolder( *currentFolder );
            return;
        }
        // Ensuring that the file fetching is done in this scope, to catch errors
        currentFolderFs->files();
    }
    // Only check once for a system_error. They are bound to happen when we list the files/folders
    // within, and IProbe::isHidden is the first place when this is done
    catch ( std::system_error& ex )
    {
        LOG_WARN( "Failed to browse ", currentFolderFs->mrl(), ": ", ex.what() );
        // Even when we're discovering a new folder, we want to rule out device removal as the cause of
        // an IO error. If this is the cause, simply abort the discovery. All the folder we have
        // discovered so far will be marked as non-present through sqlite hooks, and we'll resume the
        // discovery when the device gets plugged back in
        auto device = currentFolderFs->device();
        // The device might not be present at all, and therefor we might miss a
        // representation for it.
        if ( device == nullptr || device->isRemovable() )
        {
            // If the device is removable/missing, check if it was indeed removed.
            LOG_INFO( "The device containing ", currentFolderFs->mrl(), " is ",
                      device != nullptr ? "removable" : "not found",
                      ". Refreshing device cache..." );

            m_ml->refreshDevices( *m_fsFactory );
            // If the device was missing, refresh our list of devices in case
            // the device was plugged back and/or we missed a notification for it
            if ( device == nullptr )
                device = currentFolderFs->device();
            // The device presence flag will be changed in place, so simply retest it
            if ( device == nullptr || device->isPresent() == false )
                throw fs::DeviceRemovedException();
            LOG_INFO( "Device was not removed" );
        }
        // However if the device isn't removable, we want to:
        // - ignore it when we're discovering a new folder.
        // - delete it when it was discovered in the past. This is likely to be due to a permission change
        //   as we would not check the folder if it wasn't present during the parent folder browsing
        //   but it might also be that we're checking an entry point.
        //   The error won't arise earlier, as we only perform IO when reading the folder from this function.
        if ( newFolder == false )
        {
            // If we ever came across this folder, its content is now unaccessible: let's remove it.
            m_ml->deleteFolder( *currentFolder );
        }
        return;
    }

    if ( m_cb != nullptr )
        m_cb->onDiscoveryProgress( currentFolderFs->mrl() );
    // Load the folders we already know of:
    LOG_DEBUG( "Checking for modifications in ", currentFolderFs->mrl() );
    // Don't try to fetch any potential sub folders if the folder was freshly added
    std::vector<std::shared_ptr<Folder>> subFoldersInDB;
    if ( newFolder == false )
        subFoldersInDB = currentFolder->folders();
    for ( const auto& subFolder : currentFolderFs->dirs() )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        if ( subFolder->device() == nullptr )
            continue;
        if ( m_probe->stopFileDiscovery() == true )
            break;
        if ( m_probe->proceedOnDirectory( *subFolder ) == false )
            continue;
        auto it = std::find_if( begin( subFoldersInDB ), end( subFoldersInDB ), [&subFolder](const std::shared_ptr<Folder>& f) {
            return f->mrl() == subFolder->mrl();
        });
        // We don't know this folder, it's a new one
        if ( it == end( subFoldersInDB ) )
        {
            if ( m_probe->isHidden( *subFolder ) )
                continue;
            LOG_DEBUG( "New folder detected: ", subFolder->mrl() );
            try
            {
                addFolder( subFolder, currentFolder.get(), interruptProbe );
                continue;
            }
            catch ( sqlite::errors::ConstraintViolation& ex )
            {
                // Best attempt to detect a foreign key violation, indicating the
                // parent folders have been deleted due to being banned
                if ( strstr( ex.what(), "foreign key" ) != nullptr )
                {
                    LOG_WARN( "Creation of a folder failed because the parent is non existing: ", ex.what(),
                              ". Assuming it was deleted due to being banned" );
                    return;
                }
                LOG_WARN( "Creation of a duplicated folder failed: ", ex.what(), ". Assuming it was banned" );
                continue;
            }
        }
        auto folderInDb = *it;
        // In any case, check for modifications, as a change related to a mountpoint might
        // not update the folder modification date.
        // Also, relying on the modification date probably isn't portable
        checkFolder( subFolder, folderInDb, false, interruptProbe );
        subFoldersInDB.erase( it );
    }
    if ( m_probe->deleteUnseenFolders() == true )
    {
        // Now all folders we had in DB but haven't seen from the FS must have been deleted.
        for ( const auto& f : subFoldersInDB )
        {
            LOG_DEBUG( "Folder ", f->mrl(), " not found in FS, deleting it" );
            m_ml->deleteFolder( *f );
        }
    }
    checkFiles( currentFolderFs, currentFolder, interruptProbe );
    LOG_DEBUG( "Done checking subfolders in ", currentFolderFs->mrl() );
}

void FsDiscoverer::checkFiles( std::shared_ptr<fs::IDirectory> parentFolderFs,
                               std::shared_ptr<Folder> parentFolder,
                               const IInterruptProbe& interruptProbe) const
{
    LOG_DEBUG( "Checking file in ", parentFolderFs->mrl() );

    auto files = File::fromParentFolder( m_ml, parentFolder->id() );
    std::vector<std::shared_ptr<fs::IFile>> filesToAdd;
    std::vector<std::pair<std::shared_ptr<File>, std::shared_ptr<fs::IFile>>> filesToRefresh;
    for ( const auto& fileFs: parentFolderFs->files() )
    {
        if ( interruptProbe.isInterrupted() == true )
            return;
        if ( m_probe->stopFileDiscovery() == true )
            break;
        if ( m_probe->proceedOnFile( *fileFs ) == false )
            continue;
        auto it = std::find_if( begin( files ), end( files ), [fileFs](const std::shared_ptr<File>& f) {
            return f->mrl() == fileFs->mrl();
        });
        if ( it == end( files ) || m_probe->forceFileRefresh() == true )
        {
            if ( MediaLibrary::isExtensionSupported( fileFs->extension().c_str() ) == true )
                filesToAdd.push_back( fileFs );
            continue;
        }
        if ( fileFs->lastModificationDate() != (*it)->lastModificationDate() )
        {
            LOG_DEBUG( "Forcing file refresh ", fileFs->mrl() );
            filesToRefresh.emplace_back( std::move( *it ), fileFs );
        }
        files.erase( it );
    }
    if ( m_probe->deleteUnseenFiles() == false )
        files.clear();

    for ( const auto& file : files )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        LOG_DEBUG( "File ", file->mrl(), " not found on filesystem, deleting it" );
        auto media = file->media();
        if ( media != nullptr )
            media->removeFile( *file );
        else
        {
            // This is unexpected, as the file should have been deleted when the media was
            // removed.
            LOG_WARN( "Deleting a file without an associated media." );
            file->destroy();
        }
    }
    for ( auto& p: filesToRefresh )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        m_ml->onUpdatedFile( std::move( p.first ), std::move( p.second ) );
    }
    for ( auto& p : filesToAdd )
    {
        if ( interruptProbe.isInterrupted() == true )
            break;
        m_ml->onDiscoveredFile( p, parentFolder, parentFolderFs,
                                IFile::Type::Main, m_probe->getPlaylistParent() );
    }
    LOG_DEBUG( "Done checking files in ", parentFolderFs->mrl() );
}

bool FsDiscoverer::addFolder( std::shared_ptr<fs::IDirectory> folder,
                              Folder* parentFolder,
                              const IInterruptProbe& interruptProbe ) const
{
    auto deviceFs = folder->device();
    // We are creating a folder, there has to be a device containing it.
    assert( deviceFs != nullptr );
    // But gracefully handle failure in release mode
    if( deviceFs == nullptr )
        return false;
    auto device = Device::fromUuid( m_ml, deviceFs->uuid() );
    if ( device == nullptr )
    {
        LOG_INFO( "Creating new device in DB ", deviceFs->uuid() );
        device = Device::create( m_ml, deviceFs->uuid(),
                                 utils::file::scheme( folder->mrl() ),
                                 deviceFs->isRemovable() );
        if ( device == nullptr )
            return false;
    }

    auto f = Folder::create( m_ml, folder->mrl(),
                             parentFolder != nullptr ? parentFolder->id() : 0,
                             *device, *deviceFs );
    if ( f == nullptr )
        return false;
    checkFolder( std::move( folder ), std::move( f ), true, interruptProbe );
    return true;
}

}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: loc_dir.hxx,v $
 *
 *  $Revision: 1.1.1.1 $
 *
 *  last change: $Author: np $ $Date: 2002-03-08 14:45:15 $
 *
 *  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 ARY_LOC_LOC_DIR_HXX
#define ARY_LOC_LOC_DIR_HXX

// USED SERVICES
    // BASE CLASSES
    // COMPONENTS
#include <ary/ids.hxx>
    // PARAMETERS

namespace ary
{
namespace loc
{

class Directory
{
  public:
    typedef std::map< udmstri, Lid >   Map_Children;

    // LIFECYCLE
    virtual             ~Directory() {}

    // OPERATIONS
    void                Add_ChildDir(
                            const udmstri &     i_sName,
                            Lid                 i_nId );
    void                Add_File(
                            const udmstri &     i_sName,
                            Lid                 i_nId );
    //INQUIRY
    Lid                 Id() const;
    const Map_Children &
                        ChildDirs() const;

    const Map_Children &
                        Files() const;
    // ACCESS
    Map_Children &      ChildDirs();
    Map_Children &      Files();

  protected:
                        Directory(
                            Lid                 i_nId );
  private:
    Map_Children        aChildDirs;
    Map_Children        aFiles;
    Lid                 nId;
};

class SubDirectory : public Directory
{
  public:
                        SubDirectory(
                            Lid                 i_nId,
                            const udmstri &     i_sName,
                            Lid                 i_nParentDirectory );
    //INQUIRY
    const udmstri &     Name() const;

  private:
    udmstri             sName;
    Lid                 nParentDirectory;
};

// IMPLEMENTATION

inline Lid
Directory::Id() const
    { return nId; }
inline const Directory::Map_Children &
Directory::ChildDirs() const
    { return aChildDirs; }
inline const Directory::Map_Children &
Directory::Files() const
    { return aFiles; }
inline Directory::Map_Children &
Directory::ChildDirs()
    { return aChildDirs; }
inline Directory::Map_Children &
Directory::Files()
    { return aFiles; }
inline const udmstri &
SubDirectory::Name() const
    { return sName; }

} // namespace loc
} // namespace ary

#endif

<commit_msg>INTEGRATION: CWS ooo19126 (1.1.1.1.146); FILE MERGED 2005/09/05 13:09:36 rt 1.1.1.1.146.1: #i54170# Change license header: remove SISSL<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: loc_dir.hxx,v $
 *
 *  $Revision: 1.2 $
 *
 *  last change: $Author: rt $ $Date: 2005-09-07 16:23:07 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 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
 *
 ************************************************************************/

#ifndef ARY_LOC_LOC_DIR_HXX
#define ARY_LOC_LOC_DIR_HXX

// USED SERVICES
    // BASE CLASSES
    // COMPONENTS
#include <ary/ids.hxx>
    // PARAMETERS

namespace ary
{
namespace loc
{

class Directory
{
  public:
    typedef std::map< udmstri, Lid >   Map_Children;

    // LIFECYCLE
    virtual             ~Directory() {}

    // OPERATIONS
    void                Add_ChildDir(
                            const udmstri &     i_sName,
                            Lid                 i_nId );
    void                Add_File(
                            const udmstri &     i_sName,
                            Lid                 i_nId );
    //INQUIRY
    Lid                 Id() const;
    const Map_Children &
                        ChildDirs() const;

    const Map_Children &
                        Files() const;
    // ACCESS
    Map_Children &      ChildDirs();
    Map_Children &      Files();

  protected:
                        Directory(
                            Lid                 i_nId );
  private:
    Map_Children        aChildDirs;
    Map_Children        aFiles;
    Lid                 nId;
};

class SubDirectory : public Directory
{
  public:
                        SubDirectory(
                            Lid                 i_nId,
                            const udmstri &     i_sName,
                            Lid                 i_nParentDirectory );
    //INQUIRY
    const udmstri &     Name() const;

  private:
    udmstri             sName;
    Lid                 nParentDirectory;
};

// IMPLEMENTATION

inline Lid
Directory::Id() const
    { return nId; }
inline const Directory::Map_Children &
Directory::ChildDirs() const
    { return aChildDirs; }
inline const Directory::Map_Children &
Directory::Files() const
    { return aFiles; }
inline Directory::Map_Children &
Directory::ChildDirs()
    { return aChildDirs; }
inline Directory::Map_Children &
Directory::Files()
    { return aFiles; }
inline const udmstri &
SubDirectory::Name() const
    { return sName; }

} // namespace loc
} // namespace ary

#endif

<|endoftext|>