<commit_before>#include <qt/test/wallettests.h>
#include <qt/test/util.h>

#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <qt/bitcoinamountfield.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
#include <qt/sendcoinsdialog.h>
#include <qt/sendcoinsentry.h>
#include <qt/transactiontablemodel.h>
#include <qt/transactionview.h>
#include <qt/walletmodel.h>
#include <key_io.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <wallet/wallet.h>
#include <qt/overviewpage.h>
#include <qt/receivecoinsdialog.h>
#include <qt/recentrequeststablemodel.h>
#include <qt/receiverequestdialog.h>

#include <memory>

#include <QAbstractButton>
#include <QAction>
#include <QApplication>
#include <QCheckBox>
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QListView>
#include <QDialogButtonBox>

namespace
{
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
void ConfirmSend(QString* text = nullptr, bool cancel = false)
{
    QTimer::singleShot(0, [text, cancel]() {
        for (QWidget* widget : QApplication::topLevelWidgets()) {
            if (widget->inherits("SendConfirmationDialog")) {
                SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
                if (text) *text = dialog->text();
                QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes);
                button->setEnabled(true);
                button->click();
            }
        }
    });
}

//! Send coins to address and return txid.
uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf)
{
    QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");
    SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
    entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(EncodeDestination(address)));
    entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount);
    sendCoinsDialog.findChild<QFrame*>("frameFee")
        ->findChild<QFrame*>("frameFeeSelection")
        ->findChild<QCheckBox*>("optInRBF")
        ->setCheckState(rbf ? Qt::Checked : Qt::Unchecked);
    uint256 txid;
    boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) {
        if (status == CT_NEW) txid = hash;
    }));
    ConfirmSend();
    bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked");
    assert(invoked);
    return txid;
}

//! Find index of txid in transaction list.
QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
{
    QString hash = QString::fromStdString(txid.ToString());
    int rows = model.rowCount({});
    for (int row = 0; row < rows; ++row) {
        QModelIndex index = model.index(row, 0, {});
        if (model.data(index, TransactionTableModel::TxHashRole) == hash) {
            return index;
        }
    }
    return {};
}

//! Invoke bumpfee on txid and check results.
void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, std::string expectError, bool cancel)
{
    QTableView* table = view.findChild<QTableView*>("transactionView");
    QModelIndex index = FindTx(*table->selectionModel()->model(), txid);
    QVERIFY2(index.isValid(), "Could not find BumpFee txid");

    // Select row in table, invoke context menu, and make sure bumpfee action is
    // enabled or disabled as expected.
    QAction* action = view.findChild<QAction*>("bumpFeeAction");
    table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
    action->setEnabled(expectDisabled);
    table->customContextMenuRequested({});
    QCOMPARE(action->isEnabled(), !expectDisabled);

    action->setEnabled(true);
    QString text;
    if (expectError.empty()) {
        ConfirmSend(&text, cancel);
    } else {
        ConfirmMessage(&text);
    }
    action->trigger();
    QVERIFY(text.indexOf(QString::fromStdString(expectError)) != -1);
}

//! Simple qt wallet tests.
//
// Test widgets can be debugged interactively calling show() on them and
// manually running the event loop, e.g.:
//
//     sendCoinsDialog.show();
//     QEventLoop().exec();
//
// This also requires overriding the default minimal Qt platform:
//
//     QT_QPA_PLATFORM=xcb     src/qt/test/test_bitcoin-qt  # Linux
//     QT_QPA_PLATFORM=windows src/qt/test/test_bitcoin-qt  # Windows
//     QT_QPA_PLATFORM=cocoa   src/qt/test/test_bitcoin-qt  # macOS
void TestGUI(interfaces::Node& node)
{
    // Set up wallet and chain with 105 blocks (5 mature blocks for spending).
    TestChain100Setup test;
    for (int i = 0; i < 5; ++i) {
        test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
    }
    node.context()->connman = std::move(test.m_node.connman);
    std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock());
    bool firstRun;
    wallet->LoadWallet(firstRun);
    {
        auto spk_man = wallet->GetLegacyScriptPubKeyMan();
        auto locked_chain = wallet->chain().lock();
        LOCK(wallet->cs_wallet);
        AssertLockHeld(spk_man->cs_wallet);
        wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
        spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
        wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
    }
    {
        auto locked_chain = wallet->chain().lock();
        LockAssertion lock(::cs_main);

        WalletRescanReserver reserver(wallet.get());
        reserver.reserve();
        CWallet::ScanResult result = wallet->ScanForWalletTransactions(locked_chain->getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */);
        QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
        QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
        QVERIFY(result.last_failed_block.IsNull());
    }
    wallet->SetBroadcastTransactions(true);

    // Create widgets for sending coins and listing transactions.
    std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
    SendCoinsDialog sendCoinsDialog(platformStyle.get());
    TransactionView transactionView(platformStyle.get());
    OptionsModel optionsModel(node);
    AddWallet(wallet);
    WalletModel walletModel(interfaces::MakeWallet(wallet), node, platformStyle.get(), &optionsModel);
    RemoveWallet(wallet);
    sendCoinsDialog.setModel(&walletModel);
    transactionView.setModel(&walletModel);

    // Send two transactions, and verify they are added to transaction list.
    TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
    QCOMPARE(transactionTableModel->rowCount({}), 105);
    uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, false /* rbf */);
    uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 10 * COIN, true /* rbf */);
    QCOMPARE(transactionTableModel->rowCount({}), 107);
    QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
    QVERIFY(FindTx(*transactionTableModel, txid2).isValid());

    // Call bumpfee. Test disabled, canceled, enabled, then failing cases.
    BumpFee(transactionView, txid1, true /* expect disabled */, "not BIP 125 replaceable" /* expected error */, false /* cancel */);
    BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, true /* cancel */);
    BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */);
    BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */);

    // Check current balance on OverviewPage
    OverviewPage overviewPage(platformStyle.get());
    overviewPage.setWalletModel(&walletModel);
    QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
    QString balanceText = balanceLabel->text();
    int unit = walletModel.getOptionsModel()->getDisplayUnit();
    CAmount balance = walletModel.wallet().getBalance();
    QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
    QCOMPARE(balanceText, balanceComparison);

    // Check Request Payment button
    ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get());
    receiveCoinsDialog.setModel(&walletModel);
    RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel();

    // Label input
    QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>("reqLabel");
    labelInput->setText("TEST_LABEL_1");

    // Amount input
    BitcoinAmountField* amountInput = receiveCoinsDialog.findChild<BitcoinAmountField*>("reqAmount");
    amountInput->setValue(1);

    // Message input
    QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>("reqMessage");
    messageInput->setText("TEST_MESSAGE_1");
    int initialRowCount = requestTableModel->rowCount({});
    QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton");
    requestPaymentButton->click();
    for (QWidget* widget : QApplication::topLevelWidgets()) {
        if (widget->inherits("ReceiveRequestDialog")) {
            ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);
            QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri");
            QString paymentText = rlist->toPlainText();
            QStringList paymentTextList = paymentText.split('\n');
            QCOMPARE(paymentTextList.at(0), QString("Payment information"));
            QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1);
            QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1);
            QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));
            QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1"));
            QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1"));
        }
    }

    // Clear button
    QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>("clearButton");
    clearButton->click();
    QCOMPARE(labelInput->text(), QString(""));
    QCOMPARE(amountInput->value(), CAmount(0));
    QCOMPARE(messageInput->text(), QString(""));

    // Check addition to history
    int currentRowCount = requestTableModel->rowCount({});
    QCOMPARE(currentRowCount, initialRowCount+1);

    // Check Remove button
    QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView");
    table->selectRow(currentRowCount-1);
    QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");
    removeRequestButton->click();
    QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
}

} // namespace

void WalletTests::walletTests()
{
#ifdef Q_OS_MAC
    if (QApplication::platformName() == "minimal") {
        // Disable for mac on "minimal" platform to avoid crashes inside the Qt
        // framework when it tries to look up unimplemented cocoa functions,
        // and fails to handle returned nulls
        // (https://bugreports.qt.io/browse/QTBUG-49686).
        QWARN("Skipping WalletTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
              "with 'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a linux or windows build.");
        return;
    }
#endif
    TestGUI(m_node);
}
<commit_msg>[test] qt: add send screen balance test<commit_after>#include <qt/test/wallettests.h>
#include <qt/test/util.h>

#include <interfaces/chain.h>
#include <interfaces/node.h>
#include <qt/bitcoinamountfield.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
#include <qt/qvalidatedlineedit.h>
#include <qt/sendcoinsdialog.h>
#include <qt/sendcoinsentry.h>
#include <qt/transactiontablemodel.h>
#include <qt/transactionview.h>
#include <qt/walletmodel.h>
#include <key_io.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <wallet/wallet.h>
#include <qt/overviewpage.h>
#include <qt/receivecoinsdialog.h>
#include <qt/recentrequeststablemodel.h>
#include <qt/receiverequestdialog.h>

#include <memory>

#include <QAbstractButton>
#include <QAction>
#include <QApplication>
#include <QCheckBox>
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
#include <QTextEdit>
#include <QListView>
#include <QDialogButtonBox>

namespace
{
//! Press "Yes" or "Cancel" buttons in modal send confirmation dialog.
void ConfirmSend(QString* text = nullptr, bool cancel = false)
{
    QTimer::singleShot(0, [text, cancel]() {
        for (QWidget* widget : QApplication::topLevelWidgets()) {
            if (widget->inherits("SendConfirmationDialog")) {
                SendConfirmationDialog* dialog = qobject_cast<SendConfirmationDialog*>(widget);
                if (text) *text = dialog->text();
                QAbstractButton* button = dialog->button(cancel ? QMessageBox::Cancel : QMessageBox::Yes);
                button->setEnabled(true);
                button->click();
            }
        }
    });
}

//! Send coins to address and return txid.
uint256 SendCoins(CWallet& wallet, SendCoinsDialog& sendCoinsDialog, const CTxDestination& address, CAmount amount, bool rbf)
{
    QVBoxLayout* entries = sendCoinsDialog.findChild<QVBoxLayout*>("entries");
    SendCoinsEntry* entry = qobject_cast<SendCoinsEntry*>(entries->itemAt(0)->widget());
    entry->findChild<QValidatedLineEdit*>("payTo")->setText(QString::fromStdString(EncodeDestination(address)));
    entry->findChild<BitcoinAmountField*>("payAmount")->setValue(amount);
    sendCoinsDialog.findChild<QFrame*>("frameFee")
        ->findChild<QFrame*>("frameFeeSelection")
        ->findChild<QCheckBox*>("optInRBF")
        ->setCheckState(rbf ? Qt::Checked : Qt::Unchecked);
    uint256 txid;
    boost::signals2::scoped_connection c(wallet.NotifyTransactionChanged.connect([&txid](CWallet*, const uint256& hash, ChangeType status) {
        if (status == CT_NEW) txid = hash;
    }));
    ConfirmSend();
    bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked");
    assert(invoked);
    return txid;
}

//! Find index of txid in transaction list.
QModelIndex FindTx(const QAbstractItemModel& model, const uint256& txid)
{
    QString hash = QString::fromStdString(txid.ToString());
    int rows = model.rowCount({});
    for (int row = 0; row < rows; ++row) {
        QModelIndex index = model.index(row, 0, {});
        if (model.data(index, TransactionTableModel::TxHashRole) == hash) {
            return index;
        }
    }
    return {};
}

//! Invoke bumpfee on txid and check results.
void BumpFee(TransactionView& view, const uint256& txid, bool expectDisabled, std::string expectError, bool cancel)
{
    QTableView* table = view.findChild<QTableView*>("transactionView");
    QModelIndex index = FindTx(*table->selectionModel()->model(), txid);
    QVERIFY2(index.isValid(), "Could not find BumpFee txid");

    // Select row in table, invoke context menu, and make sure bumpfee action is
    // enabled or disabled as expected.
    QAction* action = view.findChild<QAction*>("bumpFeeAction");
    table->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
    action->setEnabled(expectDisabled);
    table->customContextMenuRequested({});
    QCOMPARE(action->isEnabled(), !expectDisabled);

    action->setEnabled(true);
    QString text;
    if (expectError.empty()) {
        ConfirmSend(&text, cancel);
    } else {
        ConfirmMessage(&text);
    }
    action->trigger();
    QVERIFY(text.indexOf(QString::fromStdString(expectError)) != -1);
}

//! Simple qt wallet tests.
//
// Test widgets can be debugged interactively calling show() on them and
// manually running the event loop, e.g.:
//
//     sendCoinsDialog.show();
//     QEventLoop().exec();
//
// This also requires overriding the default minimal Qt platform:
//
//     QT_QPA_PLATFORM=xcb     src/qt/test/test_bitcoin-qt  # Linux
//     QT_QPA_PLATFORM=windows src/qt/test/test_bitcoin-qt  # Windows
//     QT_QPA_PLATFORM=cocoa   src/qt/test/test_bitcoin-qt  # macOS
void TestGUI(interfaces::Node& node)
{
    // Set up wallet and chain with 105 blocks (5 mature blocks for spending).
    TestChain100Setup test;
    for (int i = 0; i < 5; ++i) {
        test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey()));
    }
    node.context()->connman = std::move(test.m_node.connman);
    std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>(node.context()->chain.get(), WalletLocation(), WalletDatabase::CreateMock());
    bool firstRun;
    wallet->LoadWallet(firstRun);
    {
        auto spk_man = wallet->GetLegacyScriptPubKeyMan();
        auto locked_chain = wallet->chain().lock();
        LOCK(wallet->cs_wallet);
        AssertLockHeld(spk_man->cs_wallet);
        wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive");
        spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey());
        wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash());
    }
    {
        auto locked_chain = wallet->chain().lock();
        LockAssertion lock(::cs_main);

        WalletRescanReserver reserver(wallet.get());
        reserver.reserve();
        CWallet::ScanResult result = wallet->ScanForWalletTransactions(locked_chain->getBlockHash(0), {} /* stop_block */, reserver, true /* fUpdate */);
        QCOMPARE(result.status, CWallet::ScanResult::SUCCESS);
        QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash());
        QVERIFY(result.last_failed_block.IsNull());
    }
    wallet->SetBroadcastTransactions(true);

    // Create widgets for sending coins and listing transactions.
    std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other"));
    SendCoinsDialog sendCoinsDialog(platformStyle.get());
    TransactionView transactionView(platformStyle.get());
    OptionsModel optionsModel(node);
    AddWallet(wallet);
    WalletModel walletModel(interfaces::MakeWallet(wallet), node, platformStyle.get(), &optionsModel);
    RemoveWallet(wallet);
    sendCoinsDialog.setModel(&walletModel);
    transactionView.setModel(&walletModel);

    {
        // Check balance in send dialog
        QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>("labelBalance");
        QString balanceText = balanceLabel->text();
        int unit = walletModel.getOptionsModel()->getDisplayUnit();
        CAmount balance = walletModel.wallet().getBalance();
        QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
        QCOMPARE(balanceText, balanceComparison);
    }

    // Send two transactions, and verify they are added to transaction list.
    TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
    QCOMPARE(transactionTableModel->rowCount({}), 105);
    uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 5 * COIN, false /* rbf */);
    uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, PKHash(), 10 * COIN, true /* rbf */);
    QCOMPARE(transactionTableModel->rowCount({}), 107);
    QVERIFY(FindTx(*transactionTableModel, txid1).isValid());
    QVERIFY(FindTx(*transactionTableModel, txid2).isValid());

    // Call bumpfee. Test disabled, canceled, enabled, then failing cases.
    BumpFee(transactionView, txid1, true /* expect disabled */, "not BIP 125 replaceable" /* expected error */, false /* cancel */);
    BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, true /* cancel */);
    BumpFee(transactionView, txid2, false /* expect disabled */, {} /* expected error */, false /* cancel */);
    BumpFee(transactionView, txid2, true /* expect disabled */, "already bumped" /* expected error */, false /* cancel */);

    // Check current balance on OverviewPage
    OverviewPage overviewPage(platformStyle.get());
    overviewPage.setWalletModel(&walletModel);
    QLabel* balanceLabel = overviewPage.findChild<QLabel*>("labelBalance");
    QString balanceText = balanceLabel->text();
    int unit = walletModel.getOptionsModel()->getDisplayUnit();
    CAmount balance = walletModel.wallet().getBalance();
    QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
    QCOMPARE(balanceText, balanceComparison);

    // Check Request Payment button
    ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get());
    receiveCoinsDialog.setModel(&walletModel);
    RecentRequestsTableModel* requestTableModel = walletModel.getRecentRequestsTableModel();

    // Label input
    QLineEdit* labelInput = receiveCoinsDialog.findChild<QLineEdit*>("reqLabel");
    labelInput->setText("TEST_LABEL_1");

    // Amount input
    BitcoinAmountField* amountInput = receiveCoinsDialog.findChild<BitcoinAmountField*>("reqAmount");
    amountInput->setValue(1);

    // Message input
    QLineEdit* messageInput = receiveCoinsDialog.findChild<QLineEdit*>("reqMessage");
    messageInput->setText("TEST_MESSAGE_1");
    int initialRowCount = requestTableModel->rowCount({});
    QPushButton* requestPaymentButton = receiveCoinsDialog.findChild<QPushButton*>("receiveButton");
    requestPaymentButton->click();
    for (QWidget* widget : QApplication::topLevelWidgets()) {
        if (widget->inherits("ReceiveRequestDialog")) {
            ReceiveRequestDialog* receiveRequestDialog = qobject_cast<ReceiveRequestDialog*>(widget);
            QTextEdit* rlist = receiveRequestDialog->QObject::findChild<QTextEdit*>("outUri");
            QString paymentText = rlist->toPlainText();
            QStringList paymentTextList = paymentText.split('\n');
            QCOMPARE(paymentTextList.at(0), QString("Payment information"));
            QVERIFY(paymentTextList.at(1).indexOf(QString("URI: bitcoin:")) != -1);
            QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1);
            QCOMPARE(paymentTextList.at(3), QString("Amount: 0.00000001 ") + QString::fromStdString(CURRENCY_UNIT));
            QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1"));
            QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1"));
        }
    }

    // Clear button
    QPushButton* clearButton = receiveCoinsDialog.findChild<QPushButton*>("clearButton");
    clearButton->click();
    QCOMPARE(labelInput->text(), QString(""));
    QCOMPARE(amountInput->value(), CAmount(0));
    QCOMPARE(messageInput->text(), QString(""));

    // Check addition to history
    int currentRowCount = requestTableModel->rowCount({});
    QCOMPARE(currentRowCount, initialRowCount+1);

    // Check Remove button
    QTableView* table = receiveCoinsDialog.findChild<QTableView*>("recentRequestsView");
    table->selectRow(currentRowCount-1);
    QPushButton* removeRequestButton = receiveCoinsDialog.findChild<QPushButton*>("removeRequestButton");
    removeRequestButton->click();
    QCOMPARE(requestTableModel->rowCount({}), currentRowCount-1);
}

} // namespace

void WalletTests::walletTests()
{
#ifdef Q_OS_MAC
    if (QApplication::platformName() == "minimal") {
        // Disable for mac on "minimal" platform to avoid crashes inside the Qt
        // framework when it tries to look up unimplemented cocoa functions,
        // and fails to handle returned nulls
        // (https://bugreports.qt.io/browse/QTBUG-49686).
        QWARN("Skipping WalletTests on mac build with 'minimal' platform set due to Qt bugs. To run AppTests, invoke "
              "with 'QT_QPA_PLATFORM=cocoa test_bitcoin-qt' on mac, or else use a linux or windows build.");
        return;
    }
#endif
    TestGUI(m_node);
}
<|endoftext|>


<commit_before>#ifndef SAVE_STATE_H
# define SAVE_STATE_H
# pragma once

struct statebuf
{
  void* sp;
  void* label;
};

#if defined(__GNUC__)
inline bool savestate(statebuf& ssb) noexcept
{
	bool r;

#if defined(i386) || defined(__i386) || defined(__i386__)
	asm volatile (
		"movl %%esp, %0\n\t" // store sp
		"movl $1f, %1\n\t" // store label
		"movb $0, %2\n\t" // return false
		"jmp 2f\n\t"
		"1:movb $1, %2\n\t" // return true
		"2:"
		: "=m" (ssb.sp), "=m" (ssb.label), "=r" (r)
		:
		: "memory"
		);
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
	asm volatile (
		"movq %%rsp, %0\n\t" // store sp
		"movq $1f, %1\n\t" // store label
		"movb $0, %2\n\t" // return false
		"jmp 2f\n\r"
		"1:movb $1, %2\n\t" // return true
		"2:"
		: "=m" (ssb.sp), "=m" (ssb.label), "=r" (r)
		:
		: "memory"
		);
#endif

	return r;
}
#elif defined(_MSC_VER)
__forceinline bool savestate(statebuf& ssb) noexcept
{
	register bool r;

	__asm {
		push ebp
		mov ebx, ssb
		mov [ebx]ssb.sp, esp
		mov [ebx]ssb.label, offset _1f
		mov r, 0x0
		jmp _2f
		_1f: pop ebp
			 mov r, 0x1
		_2f:
	}

	return r;
}
#else
# error "unsupported compiler"
#endif

#if defined(__GNUC__)
#if defined(i386) || defined(__i386) || defined(__i386__)
#define restorestate(SSB)          \
  asm volatile (                   \
    "movl %0, %%esp\n\t"           \
    "jmp *%1"                      \
    :                              \
    : "m" (ssb.sp), "m" (ssb.label)\
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
#define restorestate(SSB)          \
  asm volatile (                   \
    "movq %0, %%rsp\n\t"           \
    "jmp *%1"                      \
    :                              \
    : "m" (SSB.sp), "m" (SSB.label)\
  );
#else
# error "unsupported architecture"
#endif
#elif defined(_MSC_VER)
#define restorestate(SSB)   \
  __asm mov ebx, this       \
  __asm add ebx, [SSB]      \
  __asm mov esp, [ebx]SSB.sp\
  __asm jmp [ebx]SSB.label
#else
# error "unsupported compiler"
#endif

#endif // SAVE_STATE_H
<commit_msg>some fixes<commit_after>#ifndef SAVE_STATE_H
# define SAVE_STATE_H
# pragma once

struct statebuf
{
  void* sp;
  void* label;
};

#if defined(__GNUC__)
inline bool __attribute__((always_inline)) savestate(statebuf& ssb) noexcept
{
	bool r;

#if defined(i386) || defined(__i386) || defined(__i386__)
	asm volatile (
		"movl %%esp, %0\n\t" // store sp
		"movl $1f, %1\n\t" // store label
		"movb $0, %2\n\t" // return false
		"jmp 2f\n\t"
		"1:movb $1, %2\n\t" // return true
		"2:"
		: "=m" (ssb.sp), "=m" (ssb.label), "=r" (r)
		:
		: "memory"
		);
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
	asm volatile (
		"movq %%rsp, %0\n\t" // store sp
		"movq $1f, %1\n\t" // store label
		"movb $0, %2\n\t" // return false
		"jmp 2f\n\r"
		"1:movb $1, %2\n\t" // return true
		"2:"
		: "=m" (ssb.sp), "=m" (ssb.label), "=r" (r)
		:
		: "memory"
		);
#endif

	return r;
}
#elif defined(_MSC_VER)
__forceinline bool savestate(statebuf& ssb) noexcept
{
	register bool r;

	__asm {
		push ebp
		mov ebx, ssb
		mov [ebx]ssb.sp, esp
		mov [ebx]ssb.label, offset _1f
		mov r, 0x0
		jmp _2f
		_1f: pop ebp
			 mov r, 0x1
		_2f:
	}

	return r;
}
#else
# error "unsupported compiler"
#endif

#if defined(__GNUC__)
#if defined(i386) || defined(__i386) || defined(__i386__)
#define restorestate(SSB)          \
  asm volatile (                   \
    "movl %0, %%esp\n\t"           \
    "jmp *%1"                      \
    :                              \
    : "m" (SSB.sp), "m" (SSB.label)\
  );
#elif defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64)
#define restorestate(SSB)          \
  asm volatile (                   \
    "movq %0, %%rsp\n\t"           \
    "jmp *%1"                      \
    :                              \
    : "m" (SSB.sp), "m" (SSB.label)\
  );
#else
# error "unsupported architecture"
#endif
#elif defined(_MSC_VER)
#define restorestate(SSB)   \
  __asm mov ebx, this       \
  __asm add ebx, [SSB]      \
  __asm mov esp, [ebx]SSB.sp\
  __asm jmp [ebx]SSB.label
#else
# error "unsupported compiler"
#endif

#endif // SAVE_STATE_H
<|endoftext|>


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

#include <algorithm>

#include "iree/compiler/Dialect/Flow/Analysis/Dispatchability.h"
#include "iree/compiler/Dialect/Flow/IR/FlowOps.h"
#include "iree/compiler/Dialect/Flow/Utils/DispatchUtils.h"
#include "iree/compiler/Dialect/Flow/Utils/WorkloadUtils.h"
#include "iree/compiler/Utils/GraphUtils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassRegistry.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Support/LogicalResult.h"
#include "mlir/Transforms/Utils.h"
#include "tensorflow/compiler/mlir/xla/ir/hlo_ops.h"

namespace mlir {
namespace iree_compiler {
namespace IREE {
namespace Flow {

namespace {

// Returns true if the given |op| can be dispatched in all cases.
// Other passes may handle special cases of these ops but this initial
// identification is conservative.
bool isDispatchableOp(Operation *op, Dispatchability &dispatchability) {
  // TODO(b/144530470): replace with tablegen attributes/interfaces.
  if (FlowDialect::isDialectOp(op)) {
    // Ignore things we've already produced as they should only relate to
    // sequencer operations.
    return false;
  } else if (op->isKnownTerminator()) {
    // Currently we skip all terminators as we want to leave them in the block
    // to keep it valid. Future folding passes may take care of them if they are
    // worth bringing into the dispatch region.
    return false;
  } else if (auto callOp = dyn_cast<CallOp>(op)) {
    return dispatchability.isDispatchable(callOp.getCallee());
  } else if (isa<CallIndirectOp>(op)) {
    // Indirect calls are not supported in dispatch code.
    return false;
  } else if (isa<ConstantOp>(op)) {
    // Constants are handled in the RematerializeDispatchConstants pass.
    // We do that independently so that we can more easily see the use of
    // constants across all dispatches instead of just on an individual basis
    // as we do here.
    return false;
  } else if (op->getNumResults() &&
             !op->getResult(0).getType().isa<ShapedType>()) {
    // We don't put scalar manipulation into dispatch regions.
    return false;
  } else if (!isOpOfKnownDialect(op)) {
    // Probably a custom op.
    return false;
  }
  return true;
}

// Returns true if the given |op| can have other ops fused into it.
// This is sketchy and it'd be nice to define this as an op property instead.
//
// What we are looking for in foldable ops is whether the execution of the op
// when fused has some possible benefit (or at least, a non-negative cost).
// Eventually we want to allow backends to vote on this and allow multiple
// folding strategies within the same executable. For now we just hardcode what
// we know for the ops we have.
//
// Preconditions: isDispatchableOp(op) == true.
bool isFusionRootOp(Operation *op) {
  // TODO(b/144530470): replace with tablegen attributes/interfaces.
  if (isa<xla_hlo::DotOp>(op) || isa<xla_hlo::ConvOp>(op)) {
    // We have hand-written kernels for these right now we want to stand alone.
    // When we do a bit more magic we should allow these ops to fold.
    return false;
  }
  return true;
}

// Returns true if the given |op| can be fused into other ops.
//
// Ops that perform narrowing on shapes (such as reduction ops) should not
// generally be fused with other downstream ops (probably...). This avoids
// potential oversampling and indexing issues and allows backends to perform
// more efficient rooted cascading reduction dispatches.
//
// Preconditions: isDispatchableOp(op) == true.
bool isFusableOp(Operation *op) {
  // TODO(b/144530470): replace with tablegen attributes/interfaces.
  if (isa<xla_hlo::DotOp>(op) || isa<xla_hlo::ConvOp>(op)) {
    return false;
  } else if (isa<xla_hlo::ReduceOp>(op)) {
    // Reduction is usually a dedicated root operation - we can shove things in
    // the front of it but not behind.
    return false;
  }
  return true;
}

// Recursively traverses the IR DAG along the operand edges to find ops we are
// able to fuse and appends them to |subgraph|.
void gatherFusionOps(Operation *op, Dispatchability &dispatchability,
                     llvm::SetVector<Operation *> *subgraph) {
  // Skip ops that are used outside of the subgraph we are building.
  for (auto result : op->getResults()) {
    if (result.use_empty() || result.hasOneUse()) continue;
    for (auto *user : result.getUsers()) {
      if (subgraph->count(user) == 0) {
        // Op that consumes the result is not (yet) in the subgraph.
        // For now we'll ignore these as it may represent a fork that we don't
        // want to join too early.
        return;
      }
    }
  }

  // Walk backward up to ops providing our input operands.
  for (auto operand : op->getOperands()) {
    auto *sourceOp = operand.getDefiningOp();
    if (!sourceOp) continue;
    if (subgraph->count(sourceOp) == 0) {
      if (isDispatchableOp(sourceOp, dispatchability) &&
          isFusableOp(sourceOp)) {
        gatherFusionOps(sourceOp, dispatchability, subgraph);
      }
    }
  }

  subgraph->insert(op);
}

// Finds all ops that can be fused together with the given |rootOp| by searching
// backwards in the op order through input edges.
// Returns a topologically sorted list of all fused ops with |rootOp| at the
// end.
std::vector<Operation *> findFusionSubgraphFromRoot(
    Operation *rootOp, Dispatchability &dispatchability) {
  if (!isFusionRootOp(rootOp)) {
    return {rootOp};
  }
  llvm::SetVector<Operation *> subgraph;
  subgraph.insert(rootOp);
  gatherFusionOps(rootOp, dispatchability, &subgraph);
  return sortOpsTopologically(subgraph);
}

// Identifies ranges of dispatchable ops and moves them into dispatch regions.
LogicalResult identifyBlockDispatchRegions(Block *block,
                                           Dispatchability &dispatchability) {
  // Fixed point iteration until we can no longer fuse anything.
  bool didFindAnyNewRegions;
  do {
    // Iterate in reverse so we root further along in the op list.
    didFindAnyNewRegions = false;
    for (auto &rootOp : llvm::reverse(*block)) {
      if (!isDispatchableOp(&rootOp, dispatchability)) {
        // Op should remain at the sequencer level.
        continue;
      }

      // Attempt to find all operations, including rootOp, that can be fused.
      // The ops will be sorted in topological order with rootOp as the last op.
      // Worst case we may end up with a subgraph of only the rootOp.
      auto fusedSubgraph = findFusionSubgraphFromRoot(&rootOp, dispatchability);

      // Compute the workload based on the output shape.
      // When variadic all output shapes match so we can just take the first.
      auto workload = calculateWorkload(&rootOp, rootOp.getResult(0));
      if (!workload) {
        return failure();
      }

      // Try to build a dispatch region from this root.
      if (failed(buildDispatchRegion(block, workload, fusedSubgraph))) {
        return failure();
      }

      // Successfully created a dispatch region from the ops and we must now
      // start over again as we've likely trashed the whole block structure.
      didFindAnyNewRegions = true;
      break;
    }
  } while (didFindAnyNewRegions);
  return success();
}

}  // namespace

// Identifies dispatchable ops and moves them into dispatch regions.
// Some ops, such as call, will be deferred until following passes.
class IdentifyDispatchRegionsPass
    : public FunctionPass<IdentifyDispatchRegionsPass> {
 public:
  void runOnFunction() override {
    // NOTE: we require the DispatchabilityAnalysisPass to have run first.
    auto dispatchability = getCachedParentAnalysis<Dispatchability>();
    if (!dispatchability.hasValue()) {
      getFunction().emitError()
          << "dispatchability analysis not performed "
             "on module; run -iree-flow-dispatchability-analysis first";
      return signalPassFailure();
    }

    for (auto &block : getFunction()) {
      if (failed(identifyBlockDispatchRegions(&block,
                                              dispatchability.getValue()))) {
        return signalPassFailure();
      }
    }
  }
};

std::unique_ptr<OpPassBase<FuncOp>> createIdentifyDispatchRegionsPass() {
  return std::make_unique<IdentifyDispatchRegionsPass>();
}

static PassRegistration<IdentifyDispatchRegionsPass> pass(
    "iree-flow-identify-dispatch-regions",
    "Conservatively identifies dispatch regions in functions");

}  // namespace Flow
}  // namespace IREE
}  // namespace iree_compiler
}  // namespace mlir
<commit_msg>Add some LLVM_DEBUG logging to the dispatch region analysis.<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <algorithm>

#include "iree/compiler/Dialect/Flow/Analysis/Dispatchability.h"
#include "iree/compiler/Dialect/Flow/IR/FlowOps.h"
#include "iree/compiler/Dialect/Flow/Utils/DispatchUtils.h"
#include "iree/compiler/Dialect/Flow/Utils/WorkloadUtils.h"
#include "iree/compiler/Utils/GraphUtils.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Debug.h"
#include "mlir/Dialect/StandardOps/IR/Ops.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Location.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/StandardTypes.h"
#include "mlir/Pass/Pass.h"
#include "mlir/Pass/PassRegistry.h"
#include "mlir/Support/LLVM.h"
#include "mlir/Support/LogicalResult.h"
#include "mlir/Transforms/Utils.h"
#include "tensorflow/compiler/mlir/xla/ir/hlo_ops.h"

#define DEBUG_TYPE "iree-dispatch"

namespace mlir {
namespace iree_compiler {
namespace IREE {
namespace Flow {

namespace {

// Returns true if the given |op| can be dispatched in all cases.
// Other passes may handle special cases of these ops but this initial
// identification is conservative.
bool isDispatchableOp(Operation *op, Dispatchability &dispatchability) {
  // TODO(b/144530470): replace with tablegen attributes/interfaces.
  if (FlowDialect::isDialectOp(op)) {
    // Ignore things we've already produced as they should only relate to
    // sequencer operations.
    LLVM_DEBUG(llvm::dbgs()
               << "NOT DISPATCHABLE (Flow Dialect): " << op->getName() << "\n");
    return false;
  } else if (op->isKnownTerminator()) {
    // Currently we skip all terminators as we want to leave them in the block
    // to keep it valid. Future folding passes may take care of them if they are
    // worth bringing into the dispatch region.
    LLVM_DEBUG(llvm::dbgs() << "NOT DISPATCHABLE (Known Terminator): "
                            << op->getName() << "\n");
    return false;
  } else if (auto callOp = dyn_cast<CallOp>(op)) {
    bool dispatchable = dispatchability.isDispatchable(callOp.getCallee());
    LLVM_DEBUG(llvm::dbgs()
               << (dispatchable ? "" : "NOT ")
               << "DISPATCHABLE (Call): " << op->getName() << "\n");
    return dispatchable;
  } else if (isa<CallIndirectOp>(op)) {
    // Indirect calls are not supported in dispatch code.
    LLVM_DEBUG(llvm::dbgs() << "NOT DISPATCHABLE (Call Indirect): "
                            << op->getName() << "\n");
    return false;
  } else if (isa<ConstantOp>(op)) {
    // Constants are handled in the RematerializeDispatchConstants pass.
    // We do that independently so that we can more easily see the use of
    // constants across all dispatches instead of just on an individual basis
    // as we do here.
    LLVM_DEBUG(llvm::dbgs()
               << "NOT DISPATCHABLE (Constant): " << op->getName() << "\n");
    return false;
  } else if (op->getNumResults() &&
             !op->getResult(0).getType().isa<ShapedType>()) {
    // We don't put scalar manipulation into dispatch regions.
    LLVM_DEBUG(llvm::dbgs()
               << "NOT DISPATCHABLE (Non Shaped): " << op->getName() << "\n");
    return false;
  } else if (!isOpOfKnownDialect(op)) {
    // Probably a custom op.
    LLVM_DEBUG(llvm::dbgs() << "NOT DISPATCHABLE (Unknown Dialect): "
                            << op->getName() << "\n");
    return false;
  }
  LLVM_DEBUG(llvm::dbgs() << "DISPATCHABLE: " << op->getName() << "\n");
  return true;
}

// Returns true if the given |op| can have other ops fused into it.
// This is sketchy and it'd be nice to define this as an op property instead.
//
// What we are looking for in foldable ops is whether the execution of the op
// when fused has some possible benefit (or at least, a non-negative cost).
// Eventually we want to allow backends to vote on this and allow multiple
// folding strategies within the same executable. For now we just hardcode what
// we know for the ops we have.
//
// Preconditions: isDispatchableOp(op) == true.
bool isFusionRootOp(Operation *op) {
  // TODO(b/144530470): replace with tablegen attributes/interfaces.
  if (isa<xla_hlo::DotOp>(op) || isa<xla_hlo::ConvOp>(op)) {
    // We have hand-written kernels for these right now we want to stand alone.
    // When we do a bit more magic we should allow these ops to fold.
    return false;
  }
  return true;
}

// Returns true if the given |op| can be fused into other ops.
//
// Ops that perform narrowing on shapes (such as reduction ops) should not
// generally be fused with other downstream ops (probably...). This avoids
// potential oversampling and indexing issues and allows backends to perform
// more efficient rooted cascading reduction dispatches.
//
// Preconditions: isDispatchableOp(op) == true.
bool isFusableOp(Operation *op) {
  // TODO(b/144530470): replace with tablegen attributes/interfaces.
  if (isa<xla_hlo::DotOp>(op) || isa<xla_hlo::ConvOp>(op)) {
    return false;
  } else if (isa<xla_hlo::ReduceOp>(op)) {
    // Reduction is usually a dedicated root operation - we can shove things in
    // the front of it but not behind.
    return false;
  }
  return true;
}

// Recursively traverses the IR DAG along the operand edges to find ops we are
// able to fuse and appends them to |subgraph|.
void gatherFusionOps(Operation *op, Dispatchability &dispatchability,
                     llvm::SetVector<Operation *> *subgraph) {
  // Skip ops that are used outside of the subgraph we are building.
  for (auto result : op->getResults()) {
    if (result.use_empty() || result.hasOneUse()) continue;
    for (auto *user : result.getUsers()) {
      if (subgraph->count(user) == 0) {
        // Op that consumes the result is not (yet) in the subgraph.
        // For now we'll ignore these as it may represent a fork that we don't
        // want to join too early.
        return;
      }
    }
  }

  // Walk backward up to ops providing our input operands.
  for (auto operand : op->getOperands()) {
    auto *sourceOp = operand.getDefiningOp();
    if (!sourceOp) continue;
    if (subgraph->count(sourceOp) == 0) {
      if (isDispatchableOp(sourceOp, dispatchability) &&
          isFusableOp(sourceOp)) {
        gatherFusionOps(sourceOp, dispatchability, subgraph);
      }
    }
  }

  subgraph->insert(op);
}

// Finds all ops that can be fused together with the given |rootOp| by searching
// backwards in the op order through input edges.
// Returns a topologically sorted list of all fused ops with |rootOp| at the
// end.
std::vector<Operation *> findFusionSubgraphFromRoot(
    Operation *rootOp, Dispatchability &dispatchability) {
  if (!isFusionRootOp(rootOp)) {
    return {rootOp};
  }
  llvm::SetVector<Operation *> subgraph;
  subgraph.insert(rootOp);
  gatherFusionOps(rootOp, dispatchability, &subgraph);
  return sortOpsTopologically(subgraph);
}

// Identifies ranges of dispatchable ops and moves them into dispatch regions.
LogicalResult identifyBlockDispatchRegions(Block *block,
                                           Dispatchability &dispatchability) {
  // Fixed point iteration until we can no longer fuse anything.
  bool didFindAnyNewRegions;
  do {
    // Iterate in reverse so we root further along in the op list.
    didFindAnyNewRegions = false;
    for (auto &rootOp : llvm::reverse(*block)) {
      if (!isDispatchableOp(&rootOp, dispatchability)) {
        // Op should remain at the sequencer level.
        continue;
      }

      // Attempt to find all operations, including rootOp, that can be fused.
      // The ops will be sorted in topological order with rootOp as the last op.
      // Worst case we may end up with a subgraph of only the rootOp.
      auto fusedSubgraph = findFusionSubgraphFromRoot(&rootOp, dispatchability);

      // Compute the workload based on the output shape.
      // When variadic all output shapes match so we can just take the first.
      auto workload = calculateWorkload(&rootOp, rootOp.getResult(0));
      if (!workload) {
        return failure();
      }

      // Try to build a dispatch region from this root.
      if (failed(buildDispatchRegion(block, workload, fusedSubgraph))) {
        return failure();
      }

      // Successfully created a dispatch region from the ops and we must now
      // start over again as we've likely trashed the whole block structure.
      didFindAnyNewRegions = true;
      break;
    }
  } while (didFindAnyNewRegions);
  return success();
}

}  // namespace

// Identifies dispatchable ops and moves them into dispatch regions.
// Some ops, such as call, will be deferred until following passes.
class IdentifyDispatchRegionsPass
    : public FunctionPass<IdentifyDispatchRegionsPass> {
 public:
  void runOnFunction() override {
    // NOTE: we require the DispatchabilityAnalysisPass to have run first.
    auto dispatchability = getCachedParentAnalysis<Dispatchability>();
    if (!dispatchability.hasValue()) {
      getFunction().emitError()
          << "dispatchability analysis not performed "
             "on module; run -iree-flow-dispatchability-analysis first";
      return signalPassFailure();
    }

    for (auto &block : getFunction()) {
      if (failed(identifyBlockDispatchRegions(&block,
                                              dispatchability.getValue()))) {
        return signalPassFailure();
      }
    }
  }
};

std::unique_ptr<OpPassBase<FuncOp>> createIdentifyDispatchRegionsPass() {
  return std::make_unique<IdentifyDispatchRegionsPass>();
}

static PassRegistration<IdentifyDispatchRegionsPass> pass(
    "iree-flow-identify-dispatch-regions",
    "Conservatively identifies dispatch regions in functions");

}  // namespace Flow
}  // namespace IREE
}  // namespace iree_compiler
}  // namespace mlir
<|endoftext|>


<commit_before>#include<iostream>
#include<cstdio>
#include<cctype>
#include<string>
#include<cstring>

using namespace std;

const int MAXN = 200 + 10;
int dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int m, n;
int graph[MAXN][MAXN];
char alpha[7] = "ADJKSW";
int con[6];
int dic[6][2] = {{1, 0}, {3, 2}, {5, 1}, {4, 4}, {0, 5}, {2, 3}};
int num[16][4] = {{0,0,0,0},{0,0,0,1},{0,0,1,0},{0,0,1,1},
                  {0,1,0,0},{0,1,0,1},{0,1,1,0},{0,1,1,1},
                  {1,0,0,0},{1,0,0,1},{1,0,1,0},{1,0,1,1},
                  {1,1,0,0},{1,1,0,1},{1,1,1,0},{1,1,1,1}};
int cnt;

void dfs_zero(int r, int c) {
    if (r < 0 || r >= n || c < 0 || c >= m || graph[r][c] != 0) {
        return;
    }
    graph[r][c] = -1;
    for (int i = 0; i < 4; i ++) {
        int xx = r + dir[i][0];
        int yy = c + dir[i][1];
        dfs_zero(xx, yy);
    }
}

void dfs(int r, int c) {
    if (r < 0 || r >= n || c < 0 || c >= m || graph[r][c] == -1) {
        return;
    }
    if (graph[r][c] == 0) {
        cnt ++;
        dfs_zero(r, c);
        return;
    }
    graph[r][c] = -1;
    for (int i = 0; i < 4; i ++) {
        int xx = r + dir[i][0];
        int yy = c + dir[i][1];
        dfs(xx, yy);
    }
}

int main() {
    int cas = 0;
    while (cin >> n >> m && n && m) {
        memset(graph, 0, sizeof(graph));
        memset(con, 0, sizeof(con));
        char str[MAXN];
        for (int i = 0; i < n; i ++) {
            cin >> str;
            int len = 0;
            for (int j = 0; j < m; j ++) {
                if (str[j] == '0') {
                    for (int k = 0; k < 4; k ++) {
                        graph[i][len ++] = 0;
                        continue;
                    }
                }
                int tmp;
                if (isalpha(str[j])) {
                    tmp = str[j] - 'a' + 10;
                } else {
                    tmp = str[j] - '0';
                }
                for (int k = 0; k < 4; k ++) {
                    graph[i][len ++] = num[tmp][k];
                }
            }
        }
        m *= 4;
        for (int i = 0; i < n; i ++) {
            if (graph[i][0] == 0) {
                dfs_zero(i, 0);
            }
            if (graph[i][m - 1] == 0) {
                dfs_zero(i, m - 1);
            }
        }

        for (int j = 0; j < m; j ++) {
            if (graph[0][j] == 0) {
                dfs_zero(0, j);
            }
            if (graph[n - 1][j] == 0) {
                dfs_zero(n - 1, j);
            }
        }

        for (int i = 0; i < n; i ++) {
            for (int j = 0; j < m; j ++) {
                if (graph[i][j] == 1) {
                    cnt = 0;
                    dfs(i, j);
                    for (int k = 1; k < 6; k ++) {
                        if (cnt == dic[k][0]) {
                            con[dic[k][1]] ++;
                            break;
                        }
                    }
                }
            }
        }

        cout << "Case " << ++ cas << ": ";
        for (int i = 0; i < 6; i ++) {
            for (int j = 0; j < con[i]; j ++) {
                cout << alpha[i];
            }
        }
        cout << endl;
    }
}
<commit_msg>Completed Uva1103<commit_after>#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <stack>
#include <cctype>
#include <string>
#include <malloc.h>
#include <queue>
#include <map>

using namespace std;
const int INF = 0xffffff;
const double Pi = 4 * atan(1);
const int Maxn = 200 + 10;
int dir2[8][2] = {{-1,0},{0,-1},{-1,1},{1,-1},{-1,-1},{1,0},{0,1},{1,1}};
int dir[4][2] = {{1,0},{0,1},{-1,0},{0,-1}};
int m,n;
int graph[Maxn][Maxn];
char alpha[] = "ADJKSW";
int con[6];
int dic[6][2] = {{1,0},{3,2},{5,1},{4,4},{0,5},{2,3}};
int num[16][4] = {{0,0,0,0},{0,0,0,1},{0,0,1,0},{0,0,1,1},
                  {0,1,0,0},{0,1,0,1},{0,1,1,0},{0,1,1,1},
                  {1,0,0,0},{1,0,0,1},{1,0,1,0},{1,0,1,1},
                  {1,1,0,0},{1,1,0,1},{1,1,1,0},{1,1,1,1}};
int cnt;

void dfsZero(int r,int c){
    if(r < 0 || r >= n || c < 0 || c >= m || graph[r][c] != 0)
        return;
    graph[r][c] = -1;
    for(int i = 0;i < 4;i++){
        int xx = dir[i][0] + r;
        int yy = dir[i][1] + c;
        dfsZero(xx,yy);
    }
}

void dfs(int r,int c){
    if(r < 0 || r >= n || c < 0 || c >= m || graph[r][c] == -1)
        return;
    if(graph[r][c] == 0){
        cnt++;
        dfsZero(r,c);
        return;
    }
    graph[r][c] = -1;
    for(int i = 0;i < 4;i++){
        int xx = dir[i][0] + r;
        int yy = dir[i][1] + c;
        dfs(xx,yy);
    }
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("inpt.txt","r",stdin);
#endif
    int cas = 0;
    while(cin >> n >> m && n && m){
        memset(graph,0,sizeof(graph));
        memset(con,0,sizeof(con));
        char str[Maxn];
        for(int i = 0;i < n;i++){
            cin >> str;
            int len = 0;
            for(int j = 0;j < m;j++){
                if(str[j] == '0'){
                    for(int k = 0;k < 4;k++)
                        graph[i][len++] = 0;
                    continue;
                }
                int tmp;
                if(isalpha(str[j]))
                    tmp = str[j] - 'a' + 10;
                else
                    tmp = str[j] - '0';
                for(int k = 0;k < 4;k++){
                    graph[i][len++] = num[tmp][k];
                }
            }
        }
        m *= 4;
        for(int i = 0;i < n;i++){
            if(graph[i][0] == 0)
                dfsZero(i,0);
            if(graph[i][m-1] == 0)
                dfsZero(i,m-1);
        }
        for(int j = 0;j < m;j++){
            if(graph[0][j] == 0)
                dfsZero(0,j);
            if(graph[n-1][j] == 0)
                dfsZero(n-1,j);
        }
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++){
                if(graph[i][j] == 1){
                    cnt = 0;
                    dfs(i,j);
                    for(int k = 0;k < 6;k++){
                        if(cnt == dic[k][0]){
                            con[ dic[k][1] ]++;
                            break;
                        }
                    }
                }
            }
        }
        cout << "Case " << ++cas << ": ";
        for(int i = 0;i < 6;i++){
            for(int j = 0;j < con[i];j++){
                cout << alpha[i];
            }
        }
        cout << endl;
    }
    return 0;
}<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <iostream>
#include <qi/log.hpp>

#include <qimessaging/session.hpp>
#include <qimessaging/url.hpp>
#include "transportserver_p.hpp"
#include "transportserverdummy_p.hpp"

namespace qi
{
  bool TransportServerDummyPrivate::listen()
  {
    qiLogWarning("TransportServer") << "listen: You are currently running on dummy"
                                    << " TransportServer!";
    return true;
  }

  void TransportServerDummyPrivate::close()
  {
  }

  TransportServerDummyPrivate::TransportServerDummyPrivate(TransportServer* self,
                                                           const qi::Url &url,
                                                           EventLoop* ctx)
    : TransportServerPrivate(self, url, ctx)
  {
  }

  void TransportServerDummyPrivate::destroy()
  {
    delete this;
  }
  TransportServerDummyPrivate::~TransportServerDummyPrivate()
  {
  }
}
<commit_msg>Revert "TransportServerDummy: no log on close"<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <iostream>
#include <qi/log.hpp>

#include <qimessaging/session.hpp>
#include <qimessaging/url.hpp>
#include "transportserver_p.hpp"
#include "transportserverdummy_p.hpp"

namespace qi
{
  bool TransportServerDummyPrivate::listen()
  {
    qiLogWarning("TransportServer") << "listen: You are currently running on dummy"
                                    << " TransportServer!";
    return true;
  }

  void TransportServerDummyPrivate::close()
  {
    qiLogVerbose("TransportServer") << "close: You are currently running on dummy"
                                    << " TransportServer!";
  }

  TransportServerDummyPrivate::TransportServerDummyPrivate(TransportServer* self,
                                                           const qi::Url &url,
                                                           EventLoop* ctx)
    : TransportServerPrivate(self, url, ctx)
  {
  }

  void TransportServerDummyPrivate::destroy()
  {
    delete this;
  }
  TransportServerDummyPrivate::~TransportServerDummyPrivate()
  {
  }
}
<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>

#include "zlib.h"

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/io.hpp"

namespace
{
	enum
	{
		udp_connection_retries = 4,
		udp_announce_retries = 15,
		udp_connect_timeout = 15,
		udp_announce_timeout = 10,
		udp_buffer_size = 2048
	};
}

using namespace boost::posix_time;

namespace libtorrent
{

	udp_tracker_connection::udp_tracker_connection(
		tracker_request const& req
		, std::string const& hostname
		, unsigned short port
		, boost::weak_ptr<request_callback> c
		, const http_settings& stn)
		: tracker_connection(c)
		, m_request_time(second_clock::universal_time())
		, m_request(req)
		, m_transaction_id(0)
		, m_connection_id(0)
		, m_settings(stn)
		, m_attempts(0)
	{
		// TODO: this is a problem. DNS-lookup is blocking!
		// (may block up to 5 seconds)
		address a(hostname.c_str(), port);
		if (has_requester()) requester().m_tracker_address = a;
		m_socket.reset(new socket(socket::udp, false));
		m_socket->connect(a);

		send_udp_connect();
	}

	bool udp_tracker_connection::send_finished() const
	{
		using namespace boost::posix_time;

		time_duration d = second_clock::universal_time() - m_request_time;
		return (m_transaction_id != 0
			&& m_connection_id != 0)
			|| d > seconds(m_settings.tracker_timeout);
	}

	bool udp_tracker_connection::tick()
	{
		using namespace boost::posix_time;

		time_duration d = second_clock::universal_time() - m_request_time;
		if (m_connection_id == 0
			&& d > seconds(udp_connect_timeout))
		{
			if (m_attempts >= udp_connection_retries)
			{
				if (has_requester())
					requester().tracker_request_timed_out(m_request);
				return true;
			}
			send_udp_connect();
			return false;
		}

		if (m_connection_id != 0
			&& d > seconds(udp_announce_timeout))
		{
			if (m_attempts >= udp_announce_retries)
			{
				if (has_requester())
					requester().tracker_request_timed_out(m_request);
				return true;
			}

			if (m_request.kind == tracker_request::announce_request)
				send_udp_announce();
			else if (m_request.kind == tracker_request::scrape_request)
				send_udp_scrape();
			return false;
		}

		char buf[udp_buffer_size];
		int ret = m_socket->receive(buf, udp_buffer_size);

		// if there was nothing to receive, return
		if (ret == 0) return false;
		if (ret < 0)
		{
			socket::error_code err = m_socket->last_error();
			if (err == socket::would_block) return false;
			throw network_error(m_socket->last_error());
		}
		if (ret == udp_buffer_size)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, "tracker reply too big");
			return true;
		}

		if (m_connection_id == 0)
		{
			return parse_connect_response(buf, ret);
		}
		else if (m_request.kind == tracker_request::announce_request)
		{
			return parse_announce_response(buf, ret);
		}
		else if (m_request.kind == tracker_request::scrape_request)
		{
			return parse_scrape_response(buf, ret);
		}

		assert(false);
		return false;
	}

	void udp_tracker_connection::send_udp_announce()
	{
		if (m_transaction_id == 0)
			m_transaction_id = rand() ^ (rand() << 16);

		std::vector<char> buf;
		std::back_insert_iterator<std::vector<char> > out(buf);

		// connection_id
		detail::write_int64(m_connection_id, out);
		// action (announce)
		detail::write_int32(announce, out);
		// transaction_id
		detail::write_int32(m_transaction_id, out);
		// info_hash
		std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), out);
		// peer_id
		std::copy(m_request.id.begin(), m_request.id.end(), out);
		// downloaded
		detail::write_int64(m_request.downloaded, out);
		// left
		detail::write_int64(m_request.left, out);
		// uploaded
		detail::write_int64(m_request.uploaded, out);
		// event
		detail::write_int32(m_request.event, out);
		// ip address
		detail::write_int32(0, out);
		// key
		detail::write_int32(m_request.key, out);
		// num_want
		detail::write_int32(m_request.num_want, out);
		// port
		detail::write_uint16(m_request.listen_port, out);
		// extensions
		detail::write_uint16(0, out);

		m_socket->send(&buf[0], buf.size());
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	void udp_tracker_connection::send_udp_scrape()
	{
		if (m_transaction_id == 0)
			m_transaction_id = rand() ^ (rand() << 16);

		std::vector<char> buf;
		std::back_insert_iterator<std::vector<char> > out(buf);

		// connection_id
		detail::write_int64(m_connection_id, out);
		// action (scrape)
		detail::write_int32(scrape, out);
		// transaction_id
		detail::write_int32(m_transaction_id, out);
		// info_hash
		std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), out);

		m_socket->send(&buf[0], buf.size());
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	void udp_tracker_connection::send_udp_connect()
	{
		char send_buf[16];
		char* ptr = send_buf;

		if (m_transaction_id == 0)
			m_transaction_id = rand() ^ (rand() << 16);

		// connection_id
		detail::write_int64(0x41727101980, ptr);
		// action (connect)
		detail::write_int32(connect, ptr);
		// transaction_id
		detail::write_int32(m_transaction_id, ptr);

		m_socket->send(send_buf, 16);
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	bool udp_tracker_connection::parse_announce_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}

		int action = detail::read_int32(buf);
		int transaction = detail::read_int32(buf);
		if (transaction != m_transaction_id)
		{
			return false;
		}

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(buf, buf + len - 8));
			return true;
		}
		if (action != announce) return false;

		if (len < 20)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 20, ignoring");
#endif
			return false;
		}
		int interval = detail::read_int32(buf);
		int incomplete = detail::read_int32(buf);
		int complete = detail::read_int32(buf);
		int num_peers = (len - 20) / 6;
		if ((len - 20) % 6 != 0)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, "invalid tracker response");
			return true;
		}

		if (!has_requester()) return true;

		std::vector<peer_entry> peer_list;
		for (int i = 0; i < num_peers; ++i)
		{
			peer_entry e;
			std::stringstream s;
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf);
			e.ip = s.str();
			e.port = detail::read_uint16(buf);
			e.id.clear();
			peer_list.push_back(e);
		}

		requester().tracker_response(m_request, peer_list, interval
			, complete, incomplete);
		return true;
	}

	bool udp_tracker_connection::parse_scrape_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}

		int action = detail::read_int32(buf);
		int transaction = detail::read_int32(buf);
		if (transaction != m_transaction_id)
		{
			return false;
		}

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(buf, buf + len - 8));
			return true;
		}
		if (action != scrape) return false;

		if (len < 20)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 20, ignoring");
#endif
			return false;
		}
		int complete = detail::read_int32(buf);
		/*int downloaded = */detail::read_int32(buf);
		int incomplete = detail::read_int32(buf);

		if (!has_requester()) return true;

		std::vector<peer_entry> peer_list;
		requester().tracker_response(m_request, peer_list, 0
			, complete, incomplete);
		return true;
	}

	bool udp_tracker_connection::parse_connect_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}
		const char* ptr = buf;
		int action = detail::read_int32(ptr);
		int transaction = detail::read_int32(ptr);

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(ptr, buf + len));
			return true;
		}
		if (action != connect) return false;
		if (m_transaction_id != transaction)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with incorrect transaction id, ignoring");
#endif
			return false;
		}

		if (len < 16)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a connection message size < 16, ignoring");
#endif
			return false;
		}
		// reset transaction
		m_transaction_id = 0;
		m_attempts = 0;
		m_connection_id = detail::read_int64(ptr);

		if (m_request.kind == tracker_request::announce_request)
			send_udp_announce();
		else if (m_request.kind == tracker_request::scrape_request)
			send_udp_scrape();

		return false;
	}

}

<commit_msg>*** empty log message ***<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <vector>
#include <iostream>
#include <cctype>
#include <iomanip>
#include <sstream>

#include "zlib.h"

#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/io.hpp"

namespace
{
	enum
	{
		udp_connection_retries = 4,
		udp_announce_retries = 15,
		udp_connect_timeout = 15,
		udp_announce_timeout = 10,
		udp_buffer_size = 2048
	};
}

using namespace boost::posix_time;

namespace libtorrent
{

	udp_tracker_connection::udp_tracker_connection(
		tracker_request const& req
		, std::string const& hostname
		, unsigned short port
		, boost::weak_ptr<request_callback> c
		, const http_settings& stn)
		: tracker_connection(c)
		, m_request_time(second_clock::universal_time())
		, m_request(req)
		, m_transaction_id(0)
		, m_connection_id(0)
		, m_settings(stn)
		, m_attempts(0)
	{
		// TODO: this is a problem. DNS-lookup is blocking!
		// (may block up to 5 seconds)
		address a(hostname.c_str(), port);
		if (has_requester()) requester().m_tracker_address = a;
		m_socket.reset(new socket(socket::udp, false));
		m_socket->connect(a);

		send_udp_connect();
	}

	bool udp_tracker_connection::send_finished() const
	{
		using namespace boost::posix_time;

		time_duration d = second_clock::universal_time() - m_request_time;
		return (m_transaction_id != 0
			&& m_connection_id != 0)
			|| d > seconds(m_settings.tracker_timeout);
	}

	bool udp_tracker_connection::tick()
	{
		using namespace boost::posix_time;

		time_duration d = second_clock::universal_time() - m_request_time;
		if (m_connection_id == 0
			&& d > seconds(udp_connect_timeout))
		{
			if (m_attempts >= udp_connection_retries)
			{
				if (has_requester())
					requester().tracker_request_timed_out(m_request);
				return true;
			}
			send_udp_connect();
			return false;
		}

		if (m_connection_id != 0
			&& d > seconds(udp_announce_timeout))
		{
			if (m_attempts >= udp_announce_retries)
			{
				if (has_requester())
					requester().tracker_request_timed_out(m_request);
				return true;
			}

			if (m_request.kind == tracker_request::announce_request)
				send_udp_announce();
			else if (m_request.kind == tracker_request::scrape_request)
				send_udp_scrape();
			return false;
		}

		char buf[udp_buffer_size];
		int ret = m_socket->receive(buf, udp_buffer_size);

		// if there was nothing to receive, return
		if (ret == 0) return false;
		if (ret < 0)
		{
			socket::error_code err = m_socket->last_error();
			if (err == socket::would_block) return false;
			throw network_error(m_socket->last_error());
		}
		if (ret == udp_buffer_size)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, "tracker reply too big");
			return true;
		}

		if (m_connection_id == 0)
		{
			return parse_connect_response(buf, ret);
		}
		else if (m_request.kind == tracker_request::announce_request)
		{
			return parse_announce_response(buf, ret);
		}
		else if (m_request.kind == tracker_request::scrape_request)
		{
			return parse_scrape_response(buf, ret);
		}

		assert(false);
		return false;
	}

	void udp_tracker_connection::send_udp_announce()
	{
		if (m_transaction_id == 0)
			m_transaction_id = rand() ^ (rand() << 16);

		std::vector<char> buf;
		std::back_insert_iterator<std::vector<char> > out(buf);

		// connection_id
		detail::write_int64(m_connection_id, out);
		// action (announce)
		detail::write_int32(announce, out);
		// transaction_id
		detail::write_int32(m_transaction_id, out);
		// info_hash
		std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), out);
		// peer_id
		std::copy(m_request.id.begin(), m_request.id.end(), out);
		// downloaded
		detail::write_int64(m_request.downloaded, out);
		// left
		detail::write_int64(m_request.left, out);
		// uploaded
		detail::write_int64(m_request.uploaded, out);
		// event
		detail::write_int32(m_request.event, out);
		// ip address
		detail::write_int32(0, out);
		// key
		detail::write_int32(m_request.key, out);
		// num_want
		detail::write_int32(m_request.num_want, out);
		// port
		detail::write_uint16(m_request.listen_port, out);
		// extensions
		detail::write_uint16(0, out);

		m_socket->send(&buf[0], buf.size());
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	void udp_tracker_connection::send_udp_scrape()
	{
		if (m_transaction_id == 0)
			m_transaction_id = rand() ^ (rand() << 16);

		std::vector<char> buf;
		std::back_insert_iterator<std::vector<char> > out(buf);

		// connection_id
		detail::write_int64(m_connection_id, out);
		// action (scrape)
		detail::write_int32(scrape, out);
		// transaction_id
		detail::write_int32(m_transaction_id, out);
		// info_hash
		std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), out);

		m_socket->send(&buf[0], buf.size());
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	void udp_tracker_connection::send_udp_connect()
	{
		char send_buf[16];
		char* ptr = send_buf;

		if (m_transaction_id == 0)
			m_transaction_id = rand() ^ (rand() << 16);

		// connection_id
		detail::write_uint32(0x417, ptr);
		detail::write_uint32(0x27101980, ptr);
		// action (connect)
		detail::write_int32(connect, ptr);
		// transaction_id
		detail::write_int32(m_transaction_id, ptr);

		m_socket->send(send_buf, 16);
		m_request_time = second_clock::universal_time();
		++m_attempts;
	}

	bool udp_tracker_connection::parse_announce_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}

		int action = detail::read_int32(buf);
		int transaction = detail::read_int32(buf);
		if (transaction != m_transaction_id)
		{
			return false;
		}

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(buf, buf + len - 8));
			return true;
		}
		if (action != announce) return false;

		if (len < 20)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 20, ignoring");
#endif
			return false;
		}
		int interval = detail::read_int32(buf);
		int incomplete = detail::read_int32(buf);
		int complete = detail::read_int32(buf);
		int num_peers = (len - 20) / 6;
		if ((len - 20) % 6 != 0)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, "invalid tracker response");
			return true;
		}

		if (!has_requester()) return true;

		std::vector<peer_entry> peer_list;
		for (int i = 0; i < num_peers; ++i)
		{
			peer_entry e;
			std::stringstream s;
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf) << ".";
			s << (int)detail::read_uint8(buf);
			e.ip = s.str();
			e.port = detail::read_uint16(buf);
			e.id.clear();
			peer_list.push_back(e);
		}

		requester().tracker_response(m_request, peer_list, interval
			, complete, incomplete);
		return true;
	}

	bool udp_tracker_connection::parse_scrape_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}

		int action = detail::read_int32(buf);
		int transaction = detail::read_int32(buf);
		if (transaction != m_transaction_id)
		{
			return false;
		}

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(buf, buf + len - 8));
			return true;
		}
		if (action != scrape) return false;

		if (len < 20)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 20, ignoring");
#endif
			return false;
		}
		int complete = detail::read_int32(buf);
		/*int downloaded = */detail::read_int32(buf);
		int incomplete = detail::read_int32(buf);

		if (!has_requester()) return true;

		std::vector<peer_entry> peer_list;
		requester().tracker_response(m_request, peer_list, 0
			, complete, incomplete);
		return true;
	}

	bool udp_tracker_connection::parse_connect_response(const char* buf, int len)
	{
		assert(buf != 0);
		assert(len > 0);

		if (len < 8)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with size < 8, ignoring");
#endif
			return false;
		}
		const char* ptr = buf;
		int action = detail::read_int32(ptr);
		int transaction = detail::read_int32(ptr);

		if (action == error)
		{
			if (has_requester())
				requester().tracker_request_error(
					m_request, -1, std::string(ptr, buf + len));
			return true;
		}
		if (action != connect) return false;
		if (m_transaction_id != transaction)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a message with incorrect transaction id, ignoring");
#endif
			return false;
		}

		if (len < 16)
		{
#ifdef TORRENT_VERBOSE_LOGGING
			if (has_requester())
				requester().debug_log("udp_tracker_connection: "
				"got a connection message size < 16, ignoring");
#endif
			return false;
		}
		// reset transaction
		m_transaction_id = 0;
		m_attempts = 0;
		m_connection_id = detail::read_int64(ptr);

		if (m_request.kind == tracker_request::announce_request)
			send_udp_announce();
		else if (m_request.kind == tracker_request::scrape_request)
			send_udp_scrape();

		return false;
	}

}

<|endoftext|>


<commit_before>//===-- OptionGroupFormat.cpp -----------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/lldb-python.h"

#include "lldb/Interpreter/OptionGroupFormat.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/ArchSpec.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/Utils.h"

using namespace lldb;
using namespace lldb_private;

OptionGroupFormat::OptionGroupFormat (lldb::Format default_format,
                                      uint64_t default_byte_size,
                                      uint64_t default_count) :
    m_format (default_format, default_format),
    m_byte_size (default_byte_size, default_byte_size),
    m_count (default_count, default_count),
    m_prev_gdb_format('x'),
    m_prev_gdb_size('w')
{
}

OptionGroupFormat::~OptionGroupFormat ()
{
}

static OptionDefinition
g_option_table[] =
{
{ LLDB_OPT_SET_1, false, "format"    ,'f', OptionParser::eRequiredArgument, NULL, 0, eArgTypeFormat   , "Specify a format to be used for display."},
{ LLDB_OPT_SET_2, false, "gdb-format",'G', OptionParser::eRequiredArgument, NULL, 0, eArgTypeGDBFormat, "Specify a format using a GDB format specifier string."},
{ LLDB_OPT_SET_3, false, "size"      ,'s', OptionParser::eRequiredArgument, NULL, 0, eArgTypeByteSize , "The size in bytes to use when displaying with the selected format."},
{ LLDB_OPT_SET_4, false, "count"     ,'c', OptionParser::eRequiredArgument, NULL, 0, eArgTypeCount    , "The number of total items to display."},
};

uint32_t
OptionGroupFormat::GetNumDefinitions ()
{
    if (m_byte_size.GetDefaultValue() < UINT64_MAX)
    {
        if (m_count.GetDefaultValue() < UINT64_MAX)
            return 4;
        else
            return 3;
    }
    return 2;
}

const OptionDefinition *
OptionGroupFormat::GetDefinitions ()
{
    return g_option_table;
}

Error
OptionGroupFormat::SetOptionValue (CommandInterpreter &interpreter,
                                   uint32_t option_idx,
                                   const char *option_arg)
{
    Error error;
    const int short_option = g_option_table[option_idx].short_option;

    switch (short_option)
    {
        case 'f':
            error = m_format.SetValueFromCString (option_arg);
            break;

        case 'c':
            if (m_count.GetDefaultValue() == 0)
            {
                error.SetErrorString ("--count option is disabled");
            }
            else
            {
                error = m_count.SetValueFromCString (option_arg);
                if (m_count.GetCurrentValue() == 0)
                    error.SetErrorStringWithFormat("invalid --count option value '%s'", option_arg);
            }
            break;

        case 's':
            if (m_byte_size.GetDefaultValue() == 0)
            {
                error.SetErrorString ("--size option is disabled");
            }
            else
            {
                error = m_byte_size.SetValueFromCString (option_arg);
                if (m_byte_size.GetCurrentValue() == 0)
                    error.SetErrorStringWithFormat("invalid --size option value '%s'", option_arg);
            }
            break;

        case 'G':
            {
                char *end = NULL;
                const char *gdb_format_cstr = option_arg;
                uint64_t count = 0;
                if (::isdigit (gdb_format_cstr[0]))
                {
                    count = strtoull (gdb_format_cstr, &end, 0);

                    if (option_arg != end)
                        gdb_format_cstr = end;  // We have a valid count, advance the string position
                    else
                        count = 0;
                }

                Format format = eFormatDefault;
                uint32_t byte_size = 0;

                while (ParserGDBFormatLetter (interpreter, gdb_format_cstr[0], format, byte_size))
                {
                    ++gdb_format_cstr;
                }

                // We the first character of the "gdb_format_cstr" is not the
                // NULL terminator, we didn't consume the entire string and
                // something is wrong. Also, if none of the format, size or count
                // was specified correctly, then abort.
                if (gdb_format_cstr[0] || (format == eFormatInvalid && byte_size == 0 && count == 0))
                {
                    // Nothing got set correctly
                    error.SetErrorStringWithFormat ("invalid gdb format string '%s'", option_arg);
                    return error;
                }

                // At least one of the format, size or count was set correctly.
                // Anything that wasn't set correctly should be set to the
                // previous default
                if (format == eFormatInvalid)
                    ParserGDBFormatLetter (interpreter, m_prev_gdb_format, format, byte_size);

                const bool byte_size_enabled = m_byte_size.GetDefaultValue() < UINT64_MAX;
                const bool count_enabled = m_count.GetDefaultValue() < UINT64_MAX;
                if (byte_size_enabled)
                {
                    // Byte size is enabled
                    if (byte_size == 0)
                        ParserGDBFormatLetter (interpreter, m_prev_gdb_size, format, byte_size);
                }
                else
                {
                    // Byte size is disabled, make sure it wasn't specified
                    if (byte_size > 0)
                    {
                        error.SetErrorString ("this command doesn't support specifying a byte size");
                        return error;
                    }
                }

                if (count_enabled)
                {
                    // Count is enabled and was not set, set it to the default for gdb format statements (which is 1).
                    if (count == 0)
                        count = 1;
                }
                else
                {
                    // Count is disabled, make sure it wasn't specified
                    if (count > 0)
                    {
                        error.SetErrorString ("this command doesn't support specifying a count");
                        return error;
                    }
                }

                m_format.SetCurrentValue (format);
                m_format.SetOptionWasSet ();
                if (byte_size_enabled)
                {
                    m_byte_size.SetCurrentValue (byte_size);
                    m_byte_size.SetOptionWasSet ();
                }
                if (count_enabled)
                {
                    m_count.SetCurrentValue(count);
                    m_count.SetOptionWasSet ();
                }
            }
            break;

        default:
            error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
            break;
    }

    return error;
}

bool
OptionGroupFormat::ParserGDBFormatLetter (CommandInterpreter &interpreter, char format_letter, Format &format, uint32_t &byte_size)
{
    m_has_gdb_format = true;
    switch (format_letter)
    {
        case 'o': format = eFormatOctal;        m_prev_gdb_format = format_letter; return true;
        case 'x': format = eFormatHex;          m_prev_gdb_format = format_letter; return true;
        case 'd': format = eFormatDecimal;      m_prev_gdb_format = format_letter; return true;
        case 'u': format = eFormatUnsigned;     m_prev_gdb_format = format_letter; return true;
        case 't': format = eFormatBinary;       m_prev_gdb_format = format_letter; return true;
        case 'f': format = eFormatFloat;        m_prev_gdb_format = format_letter; return true;
        case 'a': format = eFormatAddressInfo;
        {
            ExecutionContext exe_ctx(interpreter.GetExecutionContext());
            Target *target = exe_ctx.GetTargetPtr();
            if (target)
                byte_size = target->GetArchitecture().GetAddressByteSize();
            m_prev_gdb_format = format_letter;
            return true;
        }
        case 'i': format = eFormatInstruction;  m_prev_gdb_format = format_letter; return true;
        case 'c': format = eFormatChar;         m_prev_gdb_format = format_letter; return true;
        case 's': format = eFormatCString;      m_prev_gdb_format = format_letter; return true;
        case 'T': format = eFormatOSType;       m_prev_gdb_format = format_letter; return true;
        case 'A': format = eFormatHexFloat;     m_prev_gdb_format = format_letter; return true;
        case 'b': byte_size = 1;                m_prev_gdb_size = format_letter;   return true;
        case 'h': byte_size = 2;                m_prev_gdb_size = format_letter;   return true;
        case 'w': byte_size = 4;                m_prev_gdb_size = format_letter;   return true;
        case 'g': byte_size = 8;                m_prev_gdb_size = format_letter;   return true;
        default:  break;
    }
    return false;
}

void
OptionGroupFormat::OptionParsingStarting (CommandInterpreter &interpreter)
{
    m_format.Clear();
    m_byte_size.Clear();
    m_count.Clear();
    m_has_gdb_format = false;
}
<commit_msg><rdar://problem/15192088><commit_after>//===-- OptionGroupFormat.cpp -----------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/lldb-python.h"

#include "lldb/Interpreter/OptionGroupFormat.h"

// C Includes
// C++ Includes
// Other libraries and framework includes
// Project includes
#include "lldb/Core/ArchSpec.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/Utils.h"

using namespace lldb;
using namespace lldb_private;

OptionGroupFormat::OptionGroupFormat (lldb::Format default_format,
                                      uint64_t default_byte_size,
                                      uint64_t default_count) :
    m_format (default_format, default_format),
    m_byte_size (default_byte_size, default_byte_size),
    m_count (default_count, default_count),
    m_prev_gdb_format('x'),
    m_prev_gdb_size('w')
{
}

OptionGroupFormat::~OptionGroupFormat ()
{
}

static OptionDefinition
g_option_table[] =
{
{ LLDB_OPT_SET_1, false, "format"    ,'f', OptionParser::eRequiredArgument, NULL, 0, eArgTypeFormat   , "Specify a format to be used for display."},
{ LLDB_OPT_SET_2, false, "gdb-format",'G', OptionParser::eRequiredArgument, NULL, 0, eArgTypeGDBFormat, "Specify a format using a GDB format specifier string."},
{ LLDB_OPT_SET_3, false, "size"      ,'s', OptionParser::eRequiredArgument, NULL, 0, eArgTypeByteSize , "The size in bytes to use when displaying with the selected format."},
{ LLDB_OPT_SET_4, false, "count"     ,'c', OptionParser::eRequiredArgument, NULL, 0, eArgTypeCount    , "The number of total items to display."},
};

uint32_t
OptionGroupFormat::GetNumDefinitions ()
{
    if (m_byte_size.GetDefaultValue() < UINT64_MAX)
    {
        if (m_count.GetDefaultValue() < UINT64_MAX)
            return 4;
        else
            return 3;
    }
    return 2;
}

const OptionDefinition *
OptionGroupFormat::GetDefinitions ()
{
    return g_option_table;
}

Error
OptionGroupFormat::SetOptionValue (CommandInterpreter &interpreter,
                                   uint32_t option_idx,
                                   const char *option_arg)
{
    Error error;
    const int short_option = g_option_table[option_idx].short_option;

    switch (short_option)
    {
        case 'f':
            error = m_format.SetValueFromCString (option_arg);
            break;

        case 'c':
            if (m_count.GetDefaultValue() == 0)
            {
                error.SetErrorString ("--count option is disabled");
            }
            else
            {
                error = m_count.SetValueFromCString (option_arg);
                if (m_count.GetCurrentValue() == 0)
                    error.SetErrorStringWithFormat("invalid --count option value '%s'", option_arg);
            }
            break;

        case 's':
            if (m_byte_size.GetDefaultValue() == 0)
            {
                error.SetErrorString ("--size option is disabled");
            }
            else
            {
                error = m_byte_size.SetValueFromCString (option_arg);
                if (m_byte_size.GetCurrentValue() == 0)
                    error.SetErrorStringWithFormat("invalid --size option value '%s'", option_arg);
            }
            break;

        case 'G':
            {
                char *end = NULL;
                const char *gdb_format_cstr = option_arg;
                uint64_t count = 0;
                if (::isdigit (gdb_format_cstr[0]))
                {
                    count = strtoull (gdb_format_cstr, &end, 0);

                    if (option_arg != end)
                        gdb_format_cstr = end;  // We have a valid count, advance the string position
                    else
                        count = 0;
                }

                Format format = eFormatDefault;
                uint32_t byte_size = 0;

                while (ParserGDBFormatLetter (interpreter, gdb_format_cstr[0], format, byte_size))
                {
                    ++gdb_format_cstr;
                }

                // We the first character of the "gdb_format_cstr" is not the
                // NULL terminator, we didn't consume the entire string and
                // something is wrong. Also, if none of the format, size or count
                // was specified correctly, then abort.
                if (gdb_format_cstr[0] || (format == eFormatInvalid && byte_size == 0 && count == 0))
                {
                    // Nothing got set correctly
                    error.SetErrorStringWithFormat ("invalid gdb format string '%s'", option_arg);
                    return error;
                }

                // At least one of the format, size or count was set correctly.
                // Anything that wasn't set correctly should be set to the
                // previous default
                if (format == eFormatInvalid)
                    ParserGDBFormatLetter (interpreter, m_prev_gdb_format, format, byte_size);

                const bool byte_size_enabled = m_byte_size.GetDefaultValue() < UINT64_MAX;
                const bool count_enabled = m_count.GetDefaultValue() < UINT64_MAX;
                if (byte_size_enabled)
                {
                    // Byte size is enabled
                    if (byte_size == 0)
                        ParserGDBFormatLetter (interpreter, m_prev_gdb_size, format, byte_size);
                }
                else
                {
                    // Byte size is disabled, make sure it wasn't specified
                    // but if this is an address, it's actually necessary to
                    // specify one so don't error out
                    if (byte_size > 0 && format != lldb::eFormatAddressInfo)
                    {
                        error.SetErrorString ("this command doesn't support specifying a byte size");
                        return error;
                    }
                }

                if (count_enabled)
                {
                    // Count is enabled and was not set, set it to the default for gdb format statements (which is 1).
                    if (count == 0)
                        count = 1;
                }
                else
                {
                    // Count is disabled, make sure it wasn't specified
                    if (count > 0)
                    {
                        error.SetErrorString ("this command doesn't support specifying a count");
                        return error;
                    }
                }

                m_format.SetCurrentValue (format);
                m_format.SetOptionWasSet ();
                if (byte_size_enabled)
                {
                    m_byte_size.SetCurrentValue (byte_size);
                    m_byte_size.SetOptionWasSet ();
                }
                if (count_enabled)
                {
                    m_count.SetCurrentValue(count);
                    m_count.SetOptionWasSet ();
                }
            }
            break;

        default:
            error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
            break;
    }

    return error;
}

bool
OptionGroupFormat::ParserGDBFormatLetter (CommandInterpreter &interpreter, char format_letter, Format &format, uint32_t &byte_size)
{
    m_has_gdb_format = true;
    switch (format_letter)
    {
        case 'o': format = eFormatOctal;        m_prev_gdb_format = format_letter; return true;
        case 'x': format = eFormatHex;          m_prev_gdb_format = format_letter; return true;
        case 'd': format = eFormatDecimal;      m_prev_gdb_format = format_letter; return true;
        case 'u': format = eFormatUnsigned;     m_prev_gdb_format = format_letter; return true;
        case 't': format = eFormatBinary;       m_prev_gdb_format = format_letter; return true;
        case 'f': format = eFormatFloat;        m_prev_gdb_format = format_letter; return true;
        case 'a': format = eFormatAddressInfo;
        {
            ExecutionContext exe_ctx(interpreter.GetExecutionContext());
            Target *target = exe_ctx.GetTargetPtr();
            if (target)
                byte_size = target->GetArchitecture().GetAddressByteSize();
            m_prev_gdb_format = format_letter;
            return true;
        }
        case 'i': format = eFormatInstruction;  m_prev_gdb_format = format_letter; return true;
        case 'c': format = eFormatChar;         m_prev_gdb_format = format_letter; return true;
        case 's': format = eFormatCString;      m_prev_gdb_format = format_letter; return true;
        case 'T': format = eFormatOSType;       m_prev_gdb_format = format_letter; return true;
        case 'A': format = eFormatHexFloat;     m_prev_gdb_format = format_letter; return true;
        case 'b': byte_size = 1;                m_prev_gdb_size = format_letter;   return true;
        case 'h': byte_size = 2;                m_prev_gdb_size = format_letter;   return true;
        case 'w': byte_size = 4;                m_prev_gdb_size = format_letter;   return true;
        case 'g': byte_size = 8;                m_prev_gdb_size = format_letter;   return true;
        default:  break;
    }
    return false;
}

void
OptionGroupFormat::OptionParsingStarting (CommandInterpreter &interpreter)
{
    m_format.Clear();
    m_byte_size.Clear();
    m_count.Clear();
    m_has_gdb_format = false;
}
<|endoftext|>