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

#include "tests/utils/Gtest.h"

#include "joynr/Semaphore.h"
#include "joynr/SingleThreadedIOService.h"
#include "joynr/ThreadPoolDelayedScheduler.h"

#include "tests/JoynrTest.h"
#include "tests/mock/MockRunnable.h"
#include "tests/mock/MockRunnableWithAccuracy.h"
#include "tests/utils/PtrUtils.h"
#include "tests/utils/TestRunnable.h"

using namespace ::testing;
using namespace joynr;

using ::testing::StrictMock;

// Expected accuracy of the timer in milliseconds

class ThreadPoolDelayedSchedulerTest : public testing::Test
{
public:
    ThreadPoolDelayedSchedulerTest()
            : singleThreadedIOService(std::make_shared<SingleThreadedIOService>()), semaphore(0)
    {
        singleThreadedIOService->start();
    }

    ~ThreadPoolDelayedSchedulerTest()
    {
        singleThreadedIOService->stop();
    }

protected:
    std::shared_ptr<SingleThreadedIOService> singleThreadedIOService;
    Semaphore semaphore;
};

TEST_F(ThreadPoolDelayedSchedulerTest, startAndShutdownWithoutWork)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    scheduler->shutdown();
}

TEST_F(ThreadPoolDelayedSchedulerTest, startAndShutdownWithPendingWork_callDtorOfRunnablesCorrect)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    // Dtor should be called
    auto runnable1 = std::make_shared<StrictMock<MockRunnable>>();

    // Dtor called after scheduler was cleaned
    auto runnable2 = std::make_shared<StrictMock<MockRunnable>>();

    EXPECT_CALL(*runnable1, dtorCalled()).Times(1);
    EXPECT_CALL(*runnable2, dtorCalled()).Times(1);

    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());
    EXPECT_CALL(*runnable2, shutdown()).Times(AnyNumber());

    scheduler->schedule(runnable1, std::chrono::seconds(2));
    scheduler->schedule(runnable2, std::chrono::seconds(2));

    EXPECT_CALL(*runnable1, run()).Times(0);
    EXPECT_CALL(*runnable2, run()).Times(0);

    EXPECT_EQ(2, runnable1.use_count());
    EXPECT_EQ(2, runnable2.use_count());

    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable1);
    test::util::resetAndWaitUntilDestroyed(runnable2);
}

TEST_F(ThreadPoolDelayedSchedulerTest, testAccuracyOfDelayedScheduler)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    auto runnable1 = std::make_shared<StrictMock<MockRunnableWithAccuracy>>(1000);

    EXPECT_CALL(*runnable1, runCalled()).Times(1);
    EXPECT_CALL(*runnable1, runCalledInTime()).Times(1).WillOnce(ReleaseSemaphore(&semaphore));
    EXPECT_CALL(*runnable1, dtorCalled()).Times(1);
    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());

    scheduler->schedule(runnable1, std::chrono::seconds(1));

    // wait for the runnable's execution
    EXPECT_TRUE(semaphore.waitFor(std::chrono::seconds(2)));

    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable1);
}

TEST_F(ThreadPoolDelayedSchedulerTest, callDtorOfRunnablesAfterSchedulerHasExpired)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    auto runnable1 = std::make_shared<StrictMock<MockRunnable>>();

    EXPECT_CALL(*runnable1, run()).Times(1).WillOnce(ReleaseSemaphore(&semaphore));
    EXPECT_CALL(*runnable1, dtorCalled()).Times(1);
    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());

    scheduler->schedule(runnable1, std::chrono::milliseconds(5));

    EXPECT_TRUE(semaphore.waitFor(std::chrono::seconds(2)));

    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable1);
}

TEST_F(ThreadPoolDelayedSchedulerTest, scheduleAndUnscheduleRunnable)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    auto runnable1 = std::make_shared<StrictMock<MockRunnableWithAccuracy>>(1000);

    EXPECT_CALL(*runnable1, dtorCalled()).Times(1).WillOnce(ReleaseSemaphore(&semaphore));
    EXPECT_CALL(*runnable1, runCalled()).Times(0);
    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());

    joynr::DelayedScheduler::RunnableHandle handle =
            scheduler->schedule(runnable1, std::chrono::seconds(1));

    EXPECT_EQ(2, runnable1.use_count());

    runnable1.reset();

    scheduler->unschedule(handle);

    EXPECT_TRUE(semaphore.waitFor(std::chrono::seconds(2)));

    scheduler->shutdown();
}

TEST_F(ThreadPoolDelayedSchedulerTest, useDefaultDelay)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds(1000));

    auto runnable1 = std::make_shared<StrictMock<MockRunnableWithAccuracy>>(1000);

    EXPECT_CALL(*runnable1, runCalled()).Times(1);
    EXPECT_CALL(*runnable1, runCalledInTime()).Times(1).WillOnce(ReleaseSemaphore(&semaphore));
    EXPECT_CALL(*runnable1, dtorCalled()).Times(1);
    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());

    scheduler->schedule(runnable1);

    EXPECT_TRUE(semaphore.waitFor(std::chrono::seconds(3)));

    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable1);
}

TEST_F(ThreadPoolDelayedSchedulerTest, schedule_deletingRunnablesCorrectly)
{
    auto scheduler = std::make_shared<ThreadPoolDelayedScheduler>(
            3, "ThreadPool", singleThreadedIOService->getIOService());
    auto runnable = std::make_shared<TestRunnable>();
    scheduler->schedule(runnable, std::chrono::milliseconds(1));
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable);
}
<commit_msg>[c++] Fix ThreadPoolDelayedSchedulerTest<commit_after>/*
 * #%L
 * %%
 * Copyright (C) 2011 - 2017 BMW Car IT GmbH
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
#include <cassert>
#include <chrono>
#include <cstdint>

#include "tests/utils/Gtest.h"

#include "joynr/Semaphore.h"
#include "joynr/SingleThreadedIOService.h"
#include "joynr/ThreadPoolDelayedScheduler.h"

#include "tests/JoynrTest.h"
#include "tests/mock/MockRunnable.h"
#include "tests/mock/MockRunnableWithAccuracy.h"
#include "tests/utils/PtrUtils.h"
#include "tests/utils/TestRunnable.h"

using namespace ::testing;
using namespace joynr;

using ::testing::StrictMock;

// Expected accuracy of the timer in milliseconds

class ThreadPoolDelayedSchedulerTest : public testing::Test
{
public:
    ThreadPoolDelayedSchedulerTest()
            : singleThreadedIOService(std::make_shared<SingleThreadedIOService>()),
              semaphore(std::make_shared<Semaphore>(0))
    {
        singleThreadedIOService->start();
    }

    ~ThreadPoolDelayedSchedulerTest()
    {
        singleThreadedIOService->stop();
    }

protected:
    std::shared_ptr<SingleThreadedIOService> singleThreadedIOService;
    std::shared_ptr<Semaphore> semaphore;
};

TEST_F(ThreadPoolDelayedSchedulerTest, startAndShutdownWithoutWork)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    scheduler->shutdown();
}

TEST_F(ThreadPoolDelayedSchedulerTest, startAndShutdownWithPendingWork_callDtorOfRunnablesCorrect)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    // Dtor should be called
    auto runnable1 = std::make_shared<StrictMock<MockRunnable>>();

    // Dtor called after scheduler was cleaned
    auto runnable2 = std::make_shared<StrictMock<MockRunnable>>();

    EXPECT_CALL(*runnable1, dtorCalled()).Times(1);
    EXPECT_CALL(*runnable2, dtorCalled()).Times(1);

    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());
    EXPECT_CALL(*runnable2, shutdown()).Times(AnyNumber());

    EXPECT_CALL(*runnable1, run()).Times(0);
    EXPECT_CALL(*runnable2, run()).Times(0);

    scheduler->schedule(runnable1, std::chrono::seconds(2));
    scheduler->schedule(runnable2, std::chrono::seconds(2));

    EXPECT_EQ(2, runnable1.use_count());
    EXPECT_EQ(2, runnable2.use_count());

    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable1);
    test::util::resetAndWaitUntilDestroyed(runnable2);
}

TEST_F(ThreadPoolDelayedSchedulerTest, testAccuracyOfDelayedScheduler)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    auto runnable1 = std::make_shared<StrictMock<MockRunnableWithAccuracy>>(1000);

    EXPECT_CALL(*runnable1, runCalled()).Times(1);
    EXPECT_CALL(*runnable1, runCalledInTime()).Times(1).WillOnce(ReleaseSemaphore(semaphore));
    EXPECT_CALL(*runnable1, dtorCalled()).Times(1);
    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());

    scheduler->schedule(runnable1, std::chrono::seconds(1));

    // wait for the runnable's execution
    EXPECT_TRUE(semaphore->waitFor(std::chrono::seconds(2)));

    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable1);
}

TEST_F(ThreadPoolDelayedSchedulerTest, callDtorOfRunnablesAfterSchedulerHasExpired)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    auto runnable1 = std::make_shared<StrictMock<MockRunnable>>();

    EXPECT_CALL(*runnable1, run()).Times(1).WillOnce(ReleaseSemaphore(semaphore));
    EXPECT_CALL(*runnable1, dtorCalled()).Times(1);
    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());

    scheduler->schedule(runnable1, std::chrono::milliseconds(5));

    EXPECT_TRUE(semaphore->waitFor(std::chrono::seconds(2)));

    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable1);
}

TEST_F(ThreadPoolDelayedSchedulerTest, scheduleAndUnscheduleRunnable)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds::zero());

    auto runnable1 = std::make_shared<StrictMock<MockRunnableWithAccuracy>>(1000);

    EXPECT_CALL(*runnable1, dtorCalled()).Times(1).WillOnce(ReleaseSemaphore(semaphore));
    EXPECT_CALL(*runnable1, runCalled()).Times(0);
    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());

    joynr::DelayedScheduler::RunnableHandle handle =
            scheduler->schedule(runnable1, std::chrono::seconds(1));

    EXPECT_EQ(2, runnable1.use_count());

    runnable1.reset();

    scheduler->unschedule(handle);

    EXPECT_TRUE(semaphore->waitFor(std::chrono::seconds(2)));

    scheduler->shutdown();
}

TEST_F(ThreadPoolDelayedSchedulerTest, useDefaultDelay)
{
    auto scheduler =
            std::make_shared<ThreadPoolDelayedScheduler>(1,
                                                         "ThreadPoolDelayedScheduler",
                                                         singleThreadedIOService->getIOService(),
                                                         std::chrono::milliseconds(1000));

    auto runnable1 = std::make_shared<StrictMock<MockRunnableWithAccuracy>>(1000);

    EXPECT_CALL(*runnable1, runCalled()).Times(1);
    EXPECT_CALL(*runnable1, runCalledInTime()).Times(1).WillOnce(ReleaseSemaphore(semaphore));
    EXPECT_CALL(*runnable1, dtorCalled()).Times(1);
    EXPECT_CALL(*runnable1, shutdown()).Times(AnyNumber());

    scheduler->schedule(runnable1);

    EXPECT_TRUE(semaphore->waitFor(std::chrono::seconds(3)));

    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable1);
}

TEST_F(ThreadPoolDelayedSchedulerTest, schedule_deletingRunnablesCorrectly)
{
    auto scheduler = std::make_shared<ThreadPoolDelayedScheduler>(
            3, "ThreadPool", singleThreadedIOService->getIOService());
    auto runnable = std::make_shared<TestRunnable>();
    scheduler->schedule(runnable, std::chrono::milliseconds(1));
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    scheduler->shutdown();
    test::util::resetAndWaitUntilDestroyed(runnable);
}
<|endoftext|>


<commit_before>/*
   Copyright (C) 2014-2015 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

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

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

 */

#ifndef CTELEGRAMDISPATCHER_HPP
#define CTELEGRAMDISPATCHER_HPP

#include <QObject>

#include <QMap>
#include <QMultiMap>
#include <QNetworkProxy>
#include <QPair>
#include <QStringList>
#include <QVector>

#include "FileRequestDescriptor.hpp"
#include "TLTypes.hpp"
#include "TelegramNamespace.hpp"

class QTimer;
class QCryptographicHash;
class QIODevice;

class CAppInformation;
class CTelegramConnection;
class CTelegramModule;

class CTelegramDispatcher : public QObject
{
    Q_OBJECT
public:
    enum InitializationStep {
        StepFirst           = 0,
        StepDcConfiguration = 1 << 0,
        StepSignIn          = 1 << 1,
        StepKnowSelf        = 1 << 2,
        StepContactList     = 1 << 3,
        StepChatInfo        = 1 << 4,
        StepUpdates         = 1 << 5,
        StepDialogs         = 1 << 6,
        StepDone            = StepDialogs | (StepDialogs - 1)
    };

#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
    Q_ENUM(InitializationStep)
#endif
    Q_DECLARE_FLAGS(InitializationStepFlags, InitializationStep)

    explicit CTelegramDispatcher(QObject *parent = 0);
    ~CTelegramDispatcher();

    void plugModule(CTelegramModule *module);

    static QVector<Telegram::DcOption> builtInDcs();
    static quint32 defaultPingInterval();

    bool updatesEnabled() const;
    void setUpdatesEnabled(bool enable);

    QNetworkProxy proxy() const;
    void setProxy(const QNetworkProxy &proxy);

    void setAppInformation(const CAppInformation *newAppInfo);

    static qint32 localTypingRecommendedRepeatInterval();

    TelegramNamespace::ConnectionState connectionState() const { return m_connectionState; }

    QString selfPhone() const;
    quint32 selfId() const;

    quint32 maxMessageId() const;
    QVector<quint32> contactIdList() const;
    QVector<quint32> chatIdList() const;

    void addContacts(const QStringList &phoneNumbers, bool replace = false);
    void deleteContacts(const QVector<quint32> &userIds);

    QByteArray connectionSecretInfo() const;

    quint32 messageReceivingFilterFlags() const { return m_messageReceivingFilterFlags; }
    void setMessageReceivingFilter(TelegramNamespace::MessageFlags flags);
    void setAcceptableMessageTypes(TelegramNamespace::MessageTypeFlags types);
    void setAutoReconnection(bool enable);
    void setPingInterval(quint32 ms, quint32 serverDisconnectionAdditionTime);

    bool initConnection(const QVector<Telegram::DcOption> &dcs);
    bool restoreConnection(const QByteArray &secret);
    void closeConnection();

    bool requestHistory(const Telegram::Peer &peer, quint32 offset, quint32 limit);
    quint32 resolveUsername(const QString &userName);

    quint64 sendMessage(const Telegram::Peer &peer, const QString &message);
    quint64 sendMedia(const Telegram::Peer &peer, const TLInputMedia &inputMedia);
    quint64 forwardMessage(const Telegram::Peer &peer, quint32 messageId);

    void setTyping(const Telegram::Peer &peer, TelegramNamespace::MessageAction publicAction);
    void setMessageRead(const Telegram::Peer &peer, quint32 messageId);

    quint64 createChat(const QVector<quint32> &userIds, const QString chatName);
    bool addChatUser(quint32 chatId, quint32 userId, quint32 forwardMessages);

    void setOnlineStatus(bool onlineStatus);
    void checkUserName(const QString &userName);
    void setUserName(const QString &newUserName);

    QString chatTitle(quint32 chatId) const;

    // Connections API

    bool setWantedDc(quint32 dc);
    CTelegramConnection *activeConnection() const { return m_mainConnection; }
    CTelegramConnection *getExtraConnection(quint32 dc);

    CTelegramConnection *createConnection(const TLDcOption &dcInfo);
    void ensureSignedConnection(CTelegramConnection *connection);
    void clearMainConnection();
    void clearExtraConnections();
    void ensureMainConnectToWantedDc();

    TLDcOption dcInfoById(quint32 dc) const;

    // Getters
    const TLUser *getUser(quint32 userId) const;
    const TLMessage *getMessage(quint32 messageId) const;

    bool getUserInfo(Telegram::UserInfo *userInfo, quint32 userId) const;
    bool getChatInfo(Telegram::GroupChat *outputChat, quint32 chatId) const;
    bool getChatParticipants(QVector<quint32> *participants, quint32 chatId);

    // Common
    TLInputPeer toInputPeer(const Telegram::Peer &peer) const;
    Telegram::Peer toPublicPeer(const TLInputPeer &inputPeer) const;
    Telegram::Peer toPublicPeer(const TLPeer &peer) const;
    Telegram::Peer toPublicPeer(const TLUser &user) const;
    Telegram::Peer toPublicPeer(const TLChat &chat) const;
    TLPeer toTLPeer(const Telegram::Peer &peer) const;
    TLInputUser toInputUser(quint32 id) const;

signals:
    void connectionStateChanged(TelegramNamespace::ConnectionState status);
    void selfUserAvailable(quint32 userId);
    void userInfoReceived(quint32 userId);

    void userNameStatusUpdated(const QString &userName, TelegramNamespace::UserNameStatus status);
    void contactListChanged();
    void contactProfileChanged(quint32 userId);

    void messageReceived(const Telegram::Message &message);

    void contactStatusChanged(quint32 userId, TelegramNamespace::ContactStatus status);
    void contactMessageActionChanged(quint32 userId, TelegramNamespace::MessageAction action);
    void contactChatMessageActionChanged(quint32 chatId, quint32 userId, TelegramNamespace::MessageAction action);

    void sentMessageIdReceived(quint64 randomId, quint32 resolvedId);
    void messageReadInbox(Telegram::Peer peer, quint32 messageId);
    void messageReadOutbox(Telegram::Peer peer, quint32 messageId);

    void createdChatIdReceived(quint64 randomId, quint32 resolvedId);
    void chatAdded(quint32 chatId);
    void chatChanged(quint32 chatId);

protected slots:
    void onConnectionAuthChanged(int newState, quint32 dc);
    void onConnectionStatusChanged(int newStatus, int reason, quint32 dc);
    void onDcConfigurationUpdated();
    void onConnectionDcIdUpdated(quint32 connectionId, quint32 newDcId);
    void onPackageRedirected(const QByteArray &data, quint32 dc);

    void onUpdatesReceived(const TLUpdates &updates, quint64 id);
    void onAuthExportedAuthorizationReceived(quint32 dc, quint32 id, const QByteArray &data);

    void onSelfUserReceived(const TLUser &selfUser);
    void onUsersReceived(const QVector<TLUser> &users);

    void onContactListReceived(const QVector<quint32> &contactIdList);
    void onContactListChanged(const QVector<quint32> &added, const QVector<quint32> &removed);
    void messageActionTimerTimeout();

    void onMessagesHistoryReceived(const TLMessagesMessages &messages);
    void onMessagesDialogsReceived(const TLMessagesDialogs &dialogs, quint32 offsetDate, quint32 offsetId, const TLInputPeer &offsetPeer, quint32 limit);
    void onMessagesAffectedMessagesReceived(const TLMessagesAffectedMessages &affectedMessages);

    void getDcConfiguration();
    void getContacts();
    void getChatsInfo();
    void getUpdatesState();
    void onUpdatesStateReceived(const TLUpdatesState &updatesState);

    void getDifference();
    void onUpdatesDifferenceReceived(const TLUpdatesDifference &updatesDifference);

    void onChatsReceived(const QVector<TLChat> &chats);
    void onMessagesFullChatReceived(const TLChatFull &chat, const QVector<TLChat> &chats, const QVector<TLUser> &users);

protected:
    void setConnectionState(TelegramNamespace::ConnectionState state);

    void processUpdate(const TLUpdate &update);

    void processMessageReceived(const TLMessage &message);
    void internalProcessMessageReceived(const TLMessage &message);

    void emitChatChanged(quint32 id);
    void updateChat(const TLChat &newChat);
    void updateFullChat(const TLChatFull &newChat);

    void initConnectionSharedClear();
    void initConnectionSharedFinal();

    void getInitialUsers();
    void getInitialDialogs();

    bool filterReceivedMessage(quint32 messageFlags) const;

    void ensureTypingUpdateTimer(int interval);
    void ensureUpdateState(quint32 pts = 0, quint32 seq = 0, quint32 date = 0);
    void setUpdateState(quint32 pts, quint32 seq, quint32 date);

    void ensureMaxMessageId(quint32 id);

    void addSentMessageId(quint64 rpcMessagesId, quint64 randomId);
    void updateShortSentMessageId(quint64 rpcId, quint32 resolvedId);
    void updateSentMessageId(quint64 randomId, quint32 resolvedId);

    void checkStateAndCallGetDifference();
    void tryNextDcAddress();

    void continueInitialization(InitializationStep justDone);

    TelegramNamespace::MessageFlags getPublicMessageFlags(quint32 flags);

    struct TypingStatus {
        TypingStatus() : chatId(0), userId(0),
            typingTime(0), action(TelegramNamespace::MessageActionNone) { }

        quint32 chatId;
        quint32 userId;
        int typingTime; // (ms)
        TelegramNamespace::MessageAction action;

        static int indexForUser(const QVector<TypingStatus> &statusVector, quint32 userId) {
            for (int i = 0; i < statusVector.count(); ++i) {
                if (statusVector.at(i).chatId) {
                    continue;
                }

                if (statusVector.at(i).userId == userId) {
                    return i;
                }
            }

            return -1;
        }

        static int indexForChatAndUser(const QVector<TypingStatus> &statusVector, quint32 chatId, quint32 userId = 0) {
            for (int i = 0; i < statusVector.count(); ++i) {
                if ((statusVector.at(i).chatId == chatId) && (statusVector.at(i).userId == userId)) {
                    return i;
                }
            }

            return -1;
        }
    };

    TelegramNamespace::ConnectionState m_connectionState;

    QNetworkProxy m_proxy;
    const CAppInformation *m_appInformation;
    bool m_updatesEnabled;

    qint32 m_deltaTime;

    TelegramNamespace::MessageFlags m_messageReceivingFilterFlags;
    TelegramNamespace::MessageTypeFlags m_acceptableMessageTypes;
    bool m_autoReconnectionEnabled;
    quint32 m_pingInterval;
    quint32 m_pingServerAdditionDisconnectionTime;

    InitializationStepFlags m_initializationState;
    InitializationStepFlags m_requestedSteps;

    quint32 m_wantedActiveDc;
    int m_autoConnectionDcIndex;

    QVector<Telegram::DcOption> m_connectionAddresses;
    QVector<TLDcOption> m_dcConfiguration;
    CTelegramConnection *m_mainConnection;
    QVector<CTelegramConnection *> m_extraConnections;
    QString m_requestedCodeForPhone;

    quint64 m_updateRequestId;
    TLUpdatesState m_updatesState; // Current application update state (may be older than actual server-side message box state)
    TLUpdatesState m_actualState; // State reported by server as actual
    bool m_updatesStateIsLocked; // True if we are (going to) getting updatesDifference.
    bool m_emitOnlyIncomingUnreadMessages;

    QMap<quint32, QPair<quint32,QByteArray> > m_exportedAuthentications; // dc, <id, auth data>
    QMap<quint32, QByteArray> m_delayedPackages; // dc, package data
    QMap<quint32, TLUser*> m_users;
    QVector<quint32> m_askedUserIds;

    QMap<quint32, TLMessage*> m_knownMediaMessages; // message id, message

    quint32 m_selfUserId;
    quint32 m_maxMessageId;

    QVector<quint32> m_contactIdList;

    // fileId is program-specific handler, not related to Telegram.
    QMap<quint64,quint64> m_rpcIdToMessageRandomIdMap; // RPC Id, Random Id

    QTimer *m_typingUpdateTimer;
    QVector<TypingStatus> m_contactsMessageActions;
    QVector<TypingStatus> m_localMessageActions;

    TLVector<quint32> m_chatIds; // Telegram chat ids vector. Index is "public chat id".

    QMap<quint32, TLChat> m_chatInfo; // Telegram chat id to Chat map
    QMap<quint32, TLChatFull> m_chatFullInfo; // Telegram chat id to ChatFull map

    QVector<CTelegramModule*> m_modules;

};

#endif // CTELEGRAMDISPATCHER_HPP
<commit_msg>CTelegramDispatcher: Use QHash instead of QMap<commit_after>/*
   Copyright (C) 2014-2015 Alexandr Akulich <akulichalexander@gmail.com>

   This file is a part of TelegramQt library.

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

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

 */

#ifndef CTELEGRAMDISPATCHER_HPP
#define CTELEGRAMDISPATCHER_HPP

#include <QObject>

#include <QHash>
#include <QNetworkProxy>
#include <QPair>
#include <QStringList>
#include <QVector>

#include "FileRequestDescriptor.hpp"
#include "TLTypes.hpp"
#include "TelegramNamespace.hpp"

class QTimer;
class QCryptographicHash;
class QIODevice;

class CAppInformation;
class CTelegramConnection;
class CTelegramModule;

class CTelegramDispatcher : public QObject
{
    Q_OBJECT
public:
    enum InitializationStep {
        StepFirst           = 0,
        StepDcConfiguration = 1 << 0,
        StepSignIn          = 1 << 1,
        StepKnowSelf        = 1 << 2,
        StepContactList     = 1 << 3,
        StepChatInfo        = 1 << 4,
        StepUpdates         = 1 << 5,
        StepDialogs         = 1 << 6,
        StepDone            = StepDialogs | (StepDialogs - 1)
    };

#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0)
    Q_ENUM(InitializationStep)
#endif
    Q_DECLARE_FLAGS(InitializationStepFlags, InitializationStep)

    explicit CTelegramDispatcher(QObject *parent = 0);
    ~CTelegramDispatcher();

    void plugModule(CTelegramModule *module);

    static QVector<Telegram::DcOption> builtInDcs();
    static quint32 defaultPingInterval();

    bool updatesEnabled() const;
    void setUpdatesEnabled(bool enable);

    QNetworkProxy proxy() const;
    void setProxy(const QNetworkProxy &proxy);

    void setAppInformation(const CAppInformation *newAppInfo);

    static qint32 localTypingRecommendedRepeatInterval();

    TelegramNamespace::ConnectionState connectionState() const { return m_connectionState; }

    QString selfPhone() const;
    quint32 selfId() const;

    quint32 maxMessageId() const;
    QVector<quint32> contactIdList() const;
    QVector<quint32> chatIdList() const;

    void addContacts(const QStringList &phoneNumbers, bool replace = false);
    void deleteContacts(const QVector<quint32> &userIds);

    QByteArray connectionSecretInfo() const;

    quint32 messageReceivingFilterFlags() const { return m_messageReceivingFilterFlags; }
    void setMessageReceivingFilter(TelegramNamespace::MessageFlags flags);
    void setAcceptableMessageTypes(TelegramNamespace::MessageTypeFlags types);
    void setAutoReconnection(bool enable);
    void setPingInterval(quint32 ms, quint32 serverDisconnectionAdditionTime);

    bool initConnection(const QVector<Telegram::DcOption> &dcs);
    bool restoreConnection(const QByteArray &secret);
    void closeConnection();

    bool requestHistory(const Telegram::Peer &peer, quint32 offset, quint32 limit);
    quint32 resolveUsername(const QString &userName);

    quint64 sendMessage(const Telegram::Peer &peer, const QString &message);
    quint64 sendMedia(const Telegram::Peer &peer, const TLInputMedia &inputMedia);
    quint64 forwardMessage(const Telegram::Peer &peer, quint32 messageId);

    void setTyping(const Telegram::Peer &peer, TelegramNamespace::MessageAction publicAction);
    void setMessageRead(const Telegram::Peer &peer, quint32 messageId);

    quint64 createChat(const QVector<quint32> &userIds, const QString chatName);
    bool addChatUser(quint32 chatId, quint32 userId, quint32 forwardMessages);

    void setOnlineStatus(bool onlineStatus);
    void checkUserName(const QString &userName);
    void setUserName(const QString &newUserName);

    QString chatTitle(quint32 chatId) const;

    // Connections API

    bool setWantedDc(quint32 dc);
    CTelegramConnection *activeConnection() const { return m_mainConnection; }
    CTelegramConnection *getExtraConnection(quint32 dc);

    CTelegramConnection *createConnection(const TLDcOption &dcInfo);
    void ensureSignedConnection(CTelegramConnection *connection);
    void clearMainConnection();
    void clearExtraConnections();
    void ensureMainConnectToWantedDc();

    TLDcOption dcInfoById(quint32 dc) const;

    // Getters
    const TLUser *getUser(quint32 userId) const;
    const TLMessage *getMessage(quint32 messageId) const;

    bool getUserInfo(Telegram::UserInfo *userInfo, quint32 userId) const;
    bool getChatInfo(Telegram::GroupChat *outputChat, quint32 chatId) const;
    bool getChatParticipants(QVector<quint32> *participants, quint32 chatId);

    // Common
    TLInputPeer toInputPeer(const Telegram::Peer &peer) const;
    Telegram::Peer toPublicPeer(const TLInputPeer &inputPeer) const;
    Telegram::Peer toPublicPeer(const TLPeer &peer) const;
    Telegram::Peer toPublicPeer(const TLUser &user) const;
    Telegram::Peer toPublicPeer(const TLChat &chat) const;
    TLPeer toTLPeer(const Telegram::Peer &peer) const;
    TLInputUser toInputUser(quint32 id) const;

signals:
    void connectionStateChanged(TelegramNamespace::ConnectionState status);
    void selfUserAvailable(quint32 userId);
    void userInfoReceived(quint32 userId);

    void userNameStatusUpdated(const QString &userName, TelegramNamespace::UserNameStatus status);
    void contactListChanged();
    void contactProfileChanged(quint32 userId);

    void messageReceived(const Telegram::Message &message);

    void contactStatusChanged(quint32 userId, TelegramNamespace::ContactStatus status);
    void contactMessageActionChanged(quint32 userId, TelegramNamespace::MessageAction action);
    void contactChatMessageActionChanged(quint32 chatId, quint32 userId, TelegramNamespace::MessageAction action);

    void sentMessageIdReceived(quint64 randomId, quint32 resolvedId);
    void messageReadInbox(Telegram::Peer peer, quint32 messageId);
    void messageReadOutbox(Telegram::Peer peer, quint32 messageId);

    void createdChatIdReceived(quint64 randomId, quint32 resolvedId);
    void chatAdded(quint32 chatId);
    void chatChanged(quint32 chatId);

protected slots:
    void onConnectionAuthChanged(int newState, quint32 dc);
    void onConnectionStatusChanged(int newStatus, int reason, quint32 dc);
    void onDcConfigurationUpdated();
    void onConnectionDcIdUpdated(quint32 connectionId, quint32 newDcId);
    void onPackageRedirected(const QByteArray &data, quint32 dc);

    void onUpdatesReceived(const TLUpdates &updates, quint64 id);
    void onAuthExportedAuthorizationReceived(quint32 dc, quint32 id, const QByteArray &data);

    void onSelfUserReceived(const TLUser &selfUser);
    void onUsersReceived(const QVector<TLUser> &users);

    void onContactListReceived(const QVector<quint32> &contactIdList);
    void onContactListChanged(const QVector<quint32> &added, const QVector<quint32> &removed);
    void messageActionTimerTimeout();

    void onMessagesHistoryReceived(const TLMessagesMessages &messages);
    void onMessagesDialogsReceived(const TLMessagesDialogs &dialogs, quint32 offsetDate, quint32 offsetId, const TLInputPeer &offsetPeer, quint32 limit);
    void onMessagesAffectedMessagesReceived(const TLMessagesAffectedMessages &affectedMessages);

    void getDcConfiguration();
    void getContacts();
    void getChatsInfo();
    void getUpdatesState();
    void onUpdatesStateReceived(const TLUpdatesState &updatesState);

    void getDifference();
    void onUpdatesDifferenceReceived(const TLUpdatesDifference &updatesDifference);

    void onChatsReceived(const QVector<TLChat> &chats);
    void onMessagesFullChatReceived(const TLChatFull &chat, const QVector<TLChat> &chats, const QVector<TLUser> &users);

protected:
    void setConnectionState(TelegramNamespace::ConnectionState state);

    void processUpdate(const TLUpdate &update);

    void processMessageReceived(const TLMessage &message);
    void internalProcessMessageReceived(const TLMessage &message);

    void emitChatChanged(quint32 id);
    void updateChat(const TLChat &newChat);
    void updateFullChat(const TLChatFull &newChat);

    void initConnectionSharedClear();
    void initConnectionSharedFinal();

    void getInitialUsers();
    void getInitialDialogs();

    bool filterReceivedMessage(quint32 messageFlags) const;

    void ensureTypingUpdateTimer(int interval);
    void ensureUpdateState(quint32 pts = 0, quint32 seq = 0, quint32 date = 0);
    void setUpdateState(quint32 pts, quint32 seq, quint32 date);

    void ensureMaxMessageId(quint32 id);

    void addSentMessageId(quint64 rpcMessagesId, quint64 randomId);
    void updateShortSentMessageId(quint64 rpcId, quint32 resolvedId);
    void updateSentMessageId(quint64 randomId, quint32 resolvedId);

    void checkStateAndCallGetDifference();
    void tryNextDcAddress();

    void continueInitialization(InitializationStep justDone);

    TelegramNamespace::MessageFlags getPublicMessageFlags(quint32 flags);

    struct TypingStatus {
        TypingStatus() : chatId(0), userId(0),
            typingTime(0), action(TelegramNamespace::MessageActionNone) { }

        quint32 chatId;
        quint32 userId;
        int typingTime; // (ms)
        TelegramNamespace::MessageAction action;

        static int indexForUser(const QVector<TypingStatus> &statusVector, quint32 userId) {
            for (int i = 0; i < statusVector.count(); ++i) {
                if (statusVector.at(i).chatId) {
                    continue;
                }

                if (statusVector.at(i).userId == userId) {
                    return i;
                }
            }

            return -1;
        }

        static int indexForChatAndUser(const QVector<TypingStatus> &statusVector, quint32 chatId, quint32 userId = 0) {
            for (int i = 0; i < statusVector.count(); ++i) {
                if ((statusVector.at(i).chatId == chatId) && (statusVector.at(i).userId == userId)) {
                    return i;
                }
            }

            return -1;
        }
    };

    TelegramNamespace::ConnectionState m_connectionState;

    QNetworkProxy m_proxy;
    const CAppInformation *m_appInformation;
    bool m_updatesEnabled;

    qint32 m_deltaTime;

    TelegramNamespace::MessageFlags m_messageReceivingFilterFlags;
    TelegramNamespace::MessageTypeFlags m_acceptableMessageTypes;
    bool m_autoReconnectionEnabled;
    quint32 m_pingInterval;
    quint32 m_pingServerAdditionDisconnectionTime;

    InitializationStepFlags m_initializationState;
    InitializationStepFlags m_requestedSteps;

    quint32 m_wantedActiveDc;
    int m_autoConnectionDcIndex;

    QVector<Telegram::DcOption> m_connectionAddresses;
    QVector<TLDcOption> m_dcConfiguration;
    CTelegramConnection *m_mainConnection;
    QVector<CTelegramConnection *> m_extraConnections;
    QString m_requestedCodeForPhone;

    quint64 m_updateRequestId;
    TLUpdatesState m_updatesState; // Current application update state (may be older than actual server-side message box state)
    TLUpdatesState m_actualState; // State reported by server as actual
    bool m_updatesStateIsLocked; // True if we are (going to) getting updatesDifference.
    bool m_emitOnlyIncomingUnreadMessages;

    QHash<quint32, QPair<quint32,QByteArray> > m_exportedAuthentications; // dc, <id, auth data>
    QHash<quint32, QByteArray> m_delayedPackages; // dc, package data
    QHash<quint32, TLUser*> m_users;
    QVector<quint32> m_askedUserIds;

    QHash<quint32, TLMessage*> m_knownMediaMessages; // message id, message

    quint32 m_selfUserId;
    quint32 m_maxMessageId;

    QVector<quint32> m_contactIdList;

    // fileId is program-specific handler, not related to Telegram.
    QHash<quint64,quint64> m_rpcIdToMessageRandomIdMap; // RPC Id, Random Id

    QTimer *m_typingUpdateTimer;
    QVector<TypingStatus> m_contactsMessageActions;
    QVector<TypingStatus> m_localMessageActions;

    TLVector<quint32> m_chatIds; // Telegram chat ids vector. Index is "public chat id".

    QHash<quint32, TLChat> m_chatInfo; // Telegram chat id to Chat map
    QHash<quint32, TLChatFull> m_chatFullInfo; // Telegram chat id to ChatFull map

    QVector<CTelegramModule*> m_modules;

};

#endif // CTELEGRAMDISPATCHER_HPP
<|endoftext|>


<commit_before>/**
 * This file is part of TelepathyQt4
 *
 * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
 * @copyright Copyright (C) 2011 Nokia Corporation
 * @license LGPL 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt4/ContactMessenger>

#include "TelepathyQt4/_gen/contact-messenger.moc.hpp"

#include "TelepathyQt4/debug-internal.h"

#include "TelepathyQt4/future-internal.h"

#include <TelepathyQt4/Account>
#include <TelepathyQt4/ChannelDispatcher>
#include <TelepathyQt4/ClientRegistrar>
#include <TelepathyQt4/MessageContentPartList>
#include <TelepathyQt4/PendingSendMessage>
#include <TelepathyQt4/SimpleTextObserver>

namespace Tp
{

struct TELEPATHY_QT4_NO_EXPORT ContactMessenger::Private
{
    Private(ContactMessenger *parent, const AccountPtr &account, const QString &contactIdentifier)
        : parent(parent),
          account(account),
          contactIdentifier(contactIdentifier),
          cdMessagesInterface(0)
    {
    }

    PendingSendMessage *sendMessage(const Message &message, MessageSendingFlags flags);

    ContactMessenger *parent;
    AccountPtr account;
    QString contactIdentifier;
    SimpleTextObserverPtr observer;
    TpFuture::Client::ChannelDispatcherInterfaceMessagesInterface *cdMessagesInterface;
};

PendingSendMessage *ContactMessenger::Private::sendMessage(const Message &message,
        MessageSendingFlags flags)
{
    if (!cdMessagesInterface) {
        cdMessagesInterface = new TpFuture::Client::ChannelDispatcherInterfaceMessagesInterface(
                account->dbusConnection(),
                TP_QT4_CHANNEL_DISPATCHER_BUS_NAME, TP_QT4_CHANNEL_DISPATCHER_OBJECT_PATH, parent);
    }

    PendingSendMessage *op = new PendingSendMessage(ContactMessengerPtr(parent), message);

    // TODO: is there a way to avoid this? Ideally TpFuture classes should use Tp types.
    TpFuture::MessagePartList parts;
    foreach (const Tp::MessagePart &part, message.parts()) {
        parts << static_cast<QMap<QString, QDBusVariant> >(part);
    }

    connect(new QDBusPendingCallWatcher(
                cdMessagesInterface->SendMessage(QDBusObjectPath(account->objectPath()),
                    contactIdentifier, parts, (uint) flags)),
            SIGNAL(finished(QDBusPendingCallWatcher*)),
            op,
            SLOT(onMessageSent(QDBusPendingCallWatcher*)));
    return op;
}

ContactMessengerPtr ContactMessenger::create(const AccountPtr &account,
        const ContactPtr &contact)
{
    if (!contact) {
        warning() << "Contact used to create a ContactMessenger object must be "
            "valid";
        return ContactMessengerPtr();
    }
    return ContactMessengerPtr(new ContactMessenger(account, contact->id()));
}

ContactMessengerPtr ContactMessenger::create(const AccountPtr &account,
        const QString &contactIdentifier)
{
    if (contactIdentifier.isEmpty()) {
        warning() << "Contact identifier used to create a ContactMessenger object must be "
            "non-empty";
        return ContactMessengerPtr();
    }
    return ContactMessengerPtr(new ContactMessenger(account, contactIdentifier));
}

ContactMessenger::ContactMessenger(const AccountPtr &account, const QString &contactIdentifier)
    : mPriv(new Private(this, account, contactIdentifier))
{
    mPriv->observer = SimpleTextObserver::create(account, contactIdentifier);
    connect(mPriv->observer.data(),
            SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString,Tp::TextChannelPtr)),
            SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString,Tp::TextChannelPtr)));
    connect(mPriv->observer.data(),
            SIGNAL(messageReceived(Tp::ReceivedMessage,Tp::TextChannelPtr)),
            SIGNAL(messageReceived(Tp::ReceivedMessage,Tp::TextChannelPtr)));
}

ContactMessenger::~ContactMessenger()
{
    delete mPriv;
}

AccountPtr ContactMessenger::account() const
{
    return mPriv->account;
}

QString ContactMessenger::contactIdentifier() const
{
    return mPriv->contactIdentifier;
}

PendingSendMessage *ContactMessenger::sendMessage(const QString &text,
        ChannelTextMessageType type,
        MessageSendingFlags flags)
{
    Message message(type, text);
    return mPriv->sendMessage(message, flags);
}

PendingSendMessage *ContactMessenger::sendMessage(const MessageContentPartList &parts,
        MessageSendingFlags flags)
{
    Message message(parts.bareParts());
    return mPriv->sendMessage(message, flags);
}

} // Tp
<commit_msg>ContactMessenger: Add docs.<commit_after>/**
 * This file is part of TelepathyQt4
 *
 * @copyright Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
 * @copyright Copyright (C) 2011 Nokia Corporation
 * @license LGPL 2.1
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <TelepathyQt4/ContactMessenger>

#include "TelepathyQt4/_gen/contact-messenger.moc.hpp"

#include "TelepathyQt4/debug-internal.h"

#include "TelepathyQt4/future-internal.h"

#include <TelepathyQt4/Account>
#include <TelepathyQt4/ChannelDispatcher>
#include <TelepathyQt4/ClientRegistrar>
#include <TelepathyQt4/MessageContentPartList>
#include <TelepathyQt4/PendingSendMessage>
#include <TelepathyQt4/SimpleTextObserver>

namespace Tp
{

struct TELEPATHY_QT4_NO_EXPORT ContactMessenger::Private
{
    Private(ContactMessenger *parent, const AccountPtr &account, const QString &contactIdentifier)
        : parent(parent),
          account(account),
          contactIdentifier(contactIdentifier),
          cdMessagesInterface(0)
    {
    }

    PendingSendMessage *sendMessage(const Message &message, MessageSendingFlags flags);

    ContactMessenger *parent;
    AccountPtr account;
    QString contactIdentifier;
    SimpleTextObserverPtr observer;
    TpFuture::Client::ChannelDispatcherInterfaceMessagesInterface *cdMessagesInterface;
};

PendingSendMessage *ContactMessenger::Private::sendMessage(const Message &message,
        MessageSendingFlags flags)
{
    if (!cdMessagesInterface) {
        cdMessagesInterface = new TpFuture::Client::ChannelDispatcherInterfaceMessagesInterface(
                account->dbusConnection(),
                TP_QT4_CHANNEL_DISPATCHER_BUS_NAME, TP_QT4_CHANNEL_DISPATCHER_OBJECT_PATH, parent);
    }

    PendingSendMessage *op = new PendingSendMessage(ContactMessengerPtr(parent), message);

    // TODO: is there a way to avoid this? Ideally TpFuture classes should use Tp types.
    TpFuture::MessagePartList parts;
    foreach (const Tp::MessagePart &part, message.parts()) {
        parts << static_cast<QMap<QString, QDBusVariant> >(part);
    }

    connect(new QDBusPendingCallWatcher(
                cdMessagesInterface->SendMessage(QDBusObjectPath(account->objectPath()),
                    contactIdentifier, parts, (uint) flags)),
            SIGNAL(finished(QDBusPendingCallWatcher*)),
            op,
            SLOT(onMessageSent(QDBusPendingCallWatcher*)));
    return op;
}

/**
 * Create a new ContactMessenger object.
 *
 * \param account The account used to listen to events.
 * \param contact The contact used to filter events.
 * \return An ContactMessengerPtr object pointing to the newly created ContactMessenger object,
 *         or a null ContactMessengerPtr if \a contact is null.
 */
ContactMessengerPtr ContactMessenger::create(const AccountPtr &account,
        const ContactPtr &contact)
{
    if (!contact) {
        warning() << "Contact used to create a ContactMessenger object must be "
            "valid";
        return ContactMessengerPtr();
    }
    return ContactMessengerPtr(new ContactMessenger(account, contact->id()));
}

/**
 * Create a new ContactMessenger object.
 *
 * \param account The account used to listen to events.
 * \param contactIdentifier The identifier of the contact used to filter events.
 * \return An ContactMessengerPtr object pointing to the newly created ContactMessenger object,
 *         or a null ContactMessengerPtr if \a contact is null.
 */

ContactMessengerPtr ContactMessenger::create(const AccountPtr &account,
        const QString &contactIdentifier)
{
    if (contactIdentifier.isEmpty()) {
        warning() << "Contact identifier used to create a ContactMessenger object must be "
            "non-empty";
        return ContactMessengerPtr();
    }
    return ContactMessengerPtr(new ContactMessenger(account, contactIdentifier));
}

/**
 * Construct a new ContactMessenger object.
 *
 * \param account The account used to listen to events.
 * \param contactIdentifier The identifier of the contact used to filter events.
 */
ContactMessenger::ContactMessenger(const AccountPtr &account, const QString &contactIdentifier)
    : mPriv(new Private(this, account, contactIdentifier))
{
    mPriv->observer = SimpleTextObserver::create(account, contactIdentifier);
    connect(mPriv->observer.data(),
            SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString,Tp::TextChannelPtr)),
            SIGNAL(messageSent(Tp::Message,Tp::MessageSendingFlags,QString,Tp::TextChannelPtr)));
    connect(mPriv->observer.data(),
            SIGNAL(messageReceived(Tp::ReceivedMessage,Tp::TextChannelPtr)),
            SIGNAL(messageReceived(Tp::ReceivedMessage,Tp::TextChannelPtr)));
}

/**
 * Class destructor.
 */
ContactMessenger::~ContactMessenger()
{
    delete mPriv;
}

/**
 * Return the account used to listen to events.
 *
 * \return The account used to listen to events.
 */
AccountPtr ContactMessenger::account() const
{
    return mPriv->account;
}

/**
 * Return the identifier of the contact used to filter events, or an empty string if none was
 * provided at construction.
 *
 * \return The identifier of the contact used to filter events.
 */
QString ContactMessenger::contactIdentifier() const
{
    return mPriv->contactIdentifier;
}

/**
 * Send a message to the contact identified by contactIdentifier() using account().
 *
 * Note that the return from this method isn't ordered in any sane way, meaning that
 * messageSent() can be signalled either before or after the returned PendingSendMessage object
 * finishes.
 *
 * \param text The message text.
 * \param type The message type.
 * \param flags The message flags.
 * \return A PendingSendMessage which will emit PendingSendMessage::finished
 *         once the reply is received and that can be used to check whether sending the
 *         message succeeded or not.
 */
PendingSendMessage *ContactMessenger::sendMessage(const QString &text,
        ChannelTextMessageType type,
        MessageSendingFlags flags)
{
    Message message(type, text);
    return mPriv->sendMessage(message, flags);
}

/**
 * Send a message to the contact identified by contactIdentifier() using account().
 *
 * Note that the return from this method isn't ordered in any sane way, meaning that
 * messageSent() can be signalled either before or after the returned PendingSendMessage object
 * finishes.
 *
 * \param parts The message parts.
 * \param flags The message flags.
 * \return A PendingSendMessage which will emit PendingSendMessage::finished
 *         once the reply is received and that can be used to check whether sending the
 *         message succeeded or not.
 */
PendingSendMessage *ContactMessenger::sendMessage(const MessageContentPartList &parts,
        MessageSendingFlags flags)
{
    Message message(parts.bareParts());
    return mPriv->sendMessage(message, flags);
}

/**
 * \fn void ContactMessenger::messageSent(const Tp::Message &message,
 *                  Tp::MessageSendingFlags flags, const QString &sentMessageToken,
 *                  const Tp::TextChannelPtr &channel);
 *
 * This signal is emitted whenever a text message on account() is sent to the contact
 * identified by contactIdentifier().
 *
 * \param message The message sent.
 * \param flags The flags of the message that was sent.
 * \param sentMessageToken The token of the message that was sent.
 * \param channel The channel from which the message was sent.
 */

/**
 * \fn void ContactMessenger::messageReceived(const Tp::ReceivedMessage &message,
 *                  const Tp::TextChannelPtr &channel);
 *
 * This signal is emitted whenever a text message on account() is received from the contact
 * identified by contactIdentifier().
 *
 * \param message The message received.
 * \param channel The channel from which the message was received.
 */

} // Tp
<|endoftext|>


<commit_before>// Draws 2D star
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>
#include <cmath>
#include "common.hpp"

#define PI 3.14159265359

void par(double hx, double hy, double lx, double ly, int colornum);
void addPar(int);
void star();
void star2();
void updateWindows();
void Mouse( int button, int state, int x, int y );
void keyboard(unsigned char key, int a, int b);
void display();
void display2();
void init();

int color1 = 3, color2 = 2;
int clicks = 1;
int win1, win2;
double IncFactor = PI;

void star()
{
  glBegin(GL_TRIANGLE_FAN);
    glColor3fv(color[color1]);
    glVertex2f(0.0, 0.0);
    glColor3fv(color[color2]);
    for (double deg = 0.0; deg < 2*PI; deg += IncFactor)
    {
      glVertex2f(cos(deg), sin(deg));
      glVertex2f(0.2 * cos(deg + IncFactor/2.0), 0.2 * sin(deg + IncFactor/2.0));
    }
    glVertex2f(1.0, 0.0);
  glEnd();
  glLoadIdentity();
}

void par(double hx, double hy, double lx, double ly, int colornum)
{
  glBegin(GL_TRIANGLE_FAN);
    glColor3fv(color[colornum]);
    glVertex2d(0.0,0.0);
    glVertex2d(hx,hy);
    glVertex2d(lx,ly);
  glEnd();
  glLoadIdentity();
}

void star2()
{
  for (double deg = 0.0; deg < 2*PI; deg += IncFactor)
    {
      par(cos(deg), sin(deg), 0.2 * cos(deg + IncFactor/2.0), 0.2 * sin(deg + IncFactor/2.0), color1);
      par(0.2 * sin(deg + IncFactor/2.0), 0.2 * cos(deg + IncFactor/2.0), sin(deg), cos(deg), color2);
    }
}

void addPar(int no = 1)
{
  clicks += no;
  IncFactor = PI/(clicks/2.0);
  updateWindows();
}

void init()
{
  glClearColor(0.2 ,0.2 ,0.2 ,0.0);
	glLoadIdentity();
}

int main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);

  // window one
  glutInitWindowSize(500, 500);
  glutInitWindowPosition(0,0);
  win1 = glutCreateWindow("Star fade color");
  glutDisplayFunc(display);

  glutMouseFunc(Mouse);
  glutKeyboardFunc(keyboard);
  init();

  // window two
  glutInitWindowSize(500, 500);
  glutInitWindowPosition(600, 0);
  win2 = glutCreateWindow("Star solid color");
  glutDisplayFunc(display2);

  glutMouseFunc(Mouse);
  glutKeyboardFunc(keyboard);
  init();

  glutMainLoop();
}

void updateWindows()
{
  glutSetWindow(win1);
  glutPostRedisplay();
  glutSetWindow(win2);
  glutPostRedisplay();
}

void keyboard(unsigned char key, int a, int b)
{
  switch (key)
  {
    case('a'):
    case('A'):
      color1 = (color1 < 8)? color1 + 1 : color1;
    break;

    case('q'):
    case('Q'):
      color1 = (color1 > 0)? color1 - 1 : color1;
    break;

    case('w'):
    case('W'):
      color2 = (color2 < 8)? color2 + 1 : color2;
    break;

    case('s'):
    case('S'):
      color2 = (color2 > 0)? color2 - 1 : color2;
    break;

    case(' '):
    case('.'):
    case('>'):
      addPar();
    break;

    case(','):
    case('<'):
      addPar(-1);
    break;

    case 27:			// Escape key
      exit(0);
    break;
  }

  updateWindows();
}

void Mouse( int button, int state, int x, int y ) {
	if ( button==GLUT_LEFT_BUTTON && state==GLUT_DOWN ) {
    addPar();
	}
}

void display()
{
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  star();
  glutSwapBuffers();
}

void display2()
{
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  star2();
  glutSwapBuffers();
}
<commit_msg>now prints out the IncFactor<commit_after>// Draws 2D star
#include <iostream>
#include <GL/glut.h>
#include <cmath>
#include "common.hpp"

#define PI 3.14159265359

void par(double hx, double hy, double lx, double ly, int colornum);
void addPar(int);
void star();
void star2();
void updateWindows();
void Mouse( int button, int state, int x, int y );
void keyboard(unsigned char key, int a, int b);
void display();
void display2();
void init();

int color1 = 3, color2 = 2;
int clicks = 1;
int win1, win2;
double IncFactor = PI;

void star()
{
  glBegin(GL_TRIANGLE_FAN);
    glColor3fv(color[color1]);
    glVertex2f(0.0, 0.0);
    glColor3fv(color[color2]);
    for (double deg = 0.0; deg < 2*PI; deg += IncFactor)
    {
      glVertex2f(cos(deg), sin(deg));
      glVertex2f(0.2 * cos(deg + IncFactor/2.0), 0.2 * sin(deg + IncFactor/2.0));
    }
    glVertex2f(1.0, 0.0);
  glEnd();
  glLoadIdentity();
  std::cout << "IncFactor: " << IncFactor / PI << " * PI" << std::endl;
}

void par(double hx, double hy, double lx, double ly, int colornum)
{
  glBegin(GL_TRIANGLE_FAN);
    glColor3fv(color[colornum]);
    glVertex2d(0.0,0.0);
    glVertex2d(hx,hy);
    glVertex2d(lx,ly);
  glEnd();
  glLoadIdentity();
}

void star2()
{
  for (double deg = 0.0; deg < 2*PI; deg += IncFactor)
    {
      par(cos(deg), sin(deg), 0.2 * cos(deg + IncFactor/2.0), 0.2 * sin(deg + IncFactor/2.0), color1);
      par(0.2 * sin(deg + IncFactor/2.0), 0.2 * cos(deg + IncFactor/2.0), sin(deg), cos(deg), color2);
    }
}

void addPar(int no = 1)
{
  clicks += no;
  IncFactor = PI/(clicks/2.0);
  updateWindows();
}

void init()
{
  glClearColor(0.2 ,0.2 ,0.2 ,0.0);
	glLoadIdentity();
}

int main(int argc, char **argv)
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);

  // window one
  glutInitWindowSize(500, 500);
  glutInitWindowPosition(0,0);
  win1 = glutCreateWindow("Star fade color");
  glutDisplayFunc(display);

  glutMouseFunc(Mouse);
  glutKeyboardFunc(keyboard);
  init();

  // window two
  glutInitWindowSize(500, 500);
  glutInitWindowPosition(600, 0);
  win2 = glutCreateWindow("Star solid color");
  glutDisplayFunc(display2);

  glutMouseFunc(Mouse);
  glutKeyboardFunc(keyboard);
  init();

  glutMainLoop();
}

void updateWindows()
{
  glutSetWindow(win1);
  glutPostRedisplay();
  glutSetWindow(win2);
  glutPostRedisplay();
}

void keyboard(unsigned char key, int a, int b)
{
  switch (key)
  {
    case('a'):
    case('A'):
      color1 = (color1 < 8)? color1 + 1 : color1;
    break;

    case('q'):
    case('Q'):
      color1 = (color1 > 0)? color1 - 1 : color1;
    break;

    case('w'):
    case('W'):
      color2 = (color2 < 8)? color2 + 1 : color2;
    break;

    case('s'):
    case('S'):
      color2 = (color2 > 0)? color2 - 1 : color2;
    break;

    case(' '):
    case('.'):
    case('>'):
      addPar();
    break;

    case(','):
    case('<'):
      addPar(-1);
    break;

    case 27:			// Escape key
      exit(0);
    break;
  }
  updateWindows();
}

void Mouse( int button, int state, int x, int y ) {
	if ( button==GLUT_LEFT_BUTTON && state==GLUT_DOWN ) {
    addPar();
	}
}

void display()
{
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  star();
  glutSwapBuffers();
}

void display2()
{
  glClear(GL_COLOR_BUFFER_BIT);
  glLoadIdentity();
  star2();
  glutSwapBuffers();
}
<|endoftext|>


<commit_before>//===--------- MipsOptimizePICCall.cpp - Optimize PIC Calls ---------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass eliminates unnecessary instructions that set up $gp and replace
// instructions that load target function addresses with copy instructions.
//
//===----------------------------------------------------------------------===//

#include "Mips.h"
#include "MCTargetDesc/MipsBaseInfo.h"
#include "MipsMachineFunction.h"
#include "MipsTargetMachine.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/CommandLine.h"

using namespace llvm;

#define DEBUG_TYPE "optimize-mips-pic-call"

static cl::opt<bool> LoadTargetFromGOT("mips-load-target-from-got",
                                       cl::init(true),
                                       cl::desc("Load target address from GOT"),
                                       cl::Hidden);

static cl::opt<bool> EraseGPOpnd("mips-erase-gp-opnd",
                                 cl::init(true), cl::desc("Erase GP Operand"),
                                 cl::Hidden);

namespace {
typedef PointerUnion<const Value *, const PseudoSourceValue *> ValueType;

typedef std::pair<unsigned, unsigned> CntRegP;
typedef RecyclingAllocator<BumpPtrAllocator,
                           ScopedHashTableVal<ValueType, CntRegP> >
AllocatorTy;
typedef ScopedHashTable<ValueType, CntRegP, DenseMapInfo<ValueType>,
                        AllocatorTy> ScopedHTType;

class MBBInfo {
public:
  MBBInfo(MachineDomTreeNode *N);
  const MachineDomTreeNode *getNode() const;
  bool isVisited() const;
  void preVisit(ScopedHTType &ScopedHT);
  void postVisit();

private:
  MachineDomTreeNode *Node;
  ScopedHTType::ScopeTy *HTScope;
};

class OptimizePICCall : public MachineFunctionPass {
public:
  OptimizePICCall(TargetMachine &tm) : MachineFunctionPass(ID) {}

  const char *getPassName() const override { return "Mips OptimizePICCall"; }

  bool runOnMachineFunction(MachineFunction &F) override;

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.addRequired<MachineDominatorTree>();
    MachineFunctionPass::getAnalysisUsage(AU);
  }

private:
  /// \brief Visit MBB.
  bool visitNode(MBBInfo &MBBI);

  /// \brief Test if MI jumps to a function via a register.
  ///
  /// Also, return the virtual register containing the target function's address
  /// and the underlying object in Reg and Val respectively, if the function's
  /// address can be resolved lazily.
  bool isCallViaRegister(MachineInstr &MI, unsigned &Reg,
                         ValueType &Val) const;

  /// \brief Return the number of instructions that dominate the current
  /// instruction and load the function address from object Entry.
  unsigned getCount(ValueType Entry);

  /// \brief Return the destination virtual register of the last instruction
  /// that loads from object Entry.
  unsigned getReg(ValueType Entry);

  /// \brief Update ScopedHT.
  void incCntAndSetReg(ValueType Entry, unsigned Reg);

  ScopedHTType ScopedHT;
  static char ID;
};

char OptimizePICCall::ID = 0;
} // end of anonymous namespace

/// Return the first MachineOperand of MI if it is a used virtual register.
static MachineOperand *getCallTargetRegOpnd(MachineInstr &MI) {
  if (MI.getNumOperands() == 0)
    return nullptr;

  MachineOperand &MO = MI.getOperand(0);

  if (!MO.isReg() || !MO.isUse() ||
      !TargetRegisterInfo::isVirtualRegister(MO.getReg()))
    return nullptr;

  return &MO;
}

/// Return type of register Reg.
static MVT::SimpleValueType getRegTy(unsigned Reg, MachineFunction &MF) {
  const TargetRegisterClass *RC = MF.getRegInfo().getRegClass(Reg);
  assert(RC->vt_end() - RC->vt_begin() == 1);
  return *RC->vt_begin();
}

/// Do the following transformation:
///
/// jalr $vreg
/// =>
/// copy $t9, $vreg
/// jalr $t9
static void setCallTargetReg(MachineBasicBlock *MBB,
                             MachineBasicBlock::iterator I) {
  MachineFunction &MF = *MBB->getParent();
  const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
  unsigned SrcReg = I->getOperand(0).getReg();
  unsigned DstReg = getRegTy(SrcReg, MF) == MVT::i32 ? Mips::T9 : Mips::T9_64;
  BuildMI(*MBB, I, I->getDebugLoc(), TII.get(TargetOpcode::COPY), DstReg)
      .addReg(SrcReg);
  I->getOperand(0).setReg(DstReg);
}

/// Search MI's operands for register GP and erase it.
static void eraseGPOpnd(MachineInstr &MI) {
  if (!EraseGPOpnd)
    return;

  MachineFunction &MF = *MI.getParent()->getParent();
  MVT::SimpleValueType Ty = getRegTy(MI.getOperand(0).getReg(), MF);
  unsigned Reg = Ty == MVT::i32 ? Mips::GP : Mips::GP_64;

  for (unsigned I = 0; I < MI.getNumOperands(); ++I) {
    MachineOperand &MO = MI.getOperand(I);
    if (MO.isReg() && MO.getReg() == Reg) {
      MI.RemoveOperand(I);
      return;
    }
  }

  llvm_unreachable(nullptr);
}

MBBInfo::MBBInfo(MachineDomTreeNode *N) : Node(N), HTScope(nullptr) {}

const MachineDomTreeNode *MBBInfo::getNode() const { return Node; }

bool MBBInfo::isVisited() const { return HTScope; }

void MBBInfo::preVisit(ScopedHTType &ScopedHT) {
  HTScope = new ScopedHTType::ScopeTy(ScopedHT);
}

void MBBInfo::postVisit() {
  delete HTScope;
}

// OptimizePICCall methods.
bool OptimizePICCall::runOnMachineFunction(MachineFunction &F) {
  if (static_cast<const MipsSubtarget &>(F.getSubtarget()).inMips16Mode())
    return false;

  // Do a pre-order traversal of the dominator tree.
  MachineDominatorTree *MDT = &getAnalysis<MachineDominatorTree>();
  bool Changed = false;

  SmallVector<MBBInfo, 8> WorkList(1, MBBInfo(MDT->getRootNode()));

  while (!WorkList.empty()) {
    MBBInfo &MBBI = WorkList.back();

    // If this MBB has already been visited, destroy the scope for the MBB and
    // pop it from the work list.
    if (MBBI.isVisited()) {
      MBBI.postVisit();
      WorkList.pop_back();
      continue;
    }

    // Visit the MBB and add its children to the work list.
    MBBI.preVisit(ScopedHT);
    Changed |= visitNode(MBBI);
    const MachineDomTreeNode *Node = MBBI.getNode();
    const std::vector<MachineDomTreeNode *> &Children = Node->getChildren();
    WorkList.append(Children.begin(), Children.end());
  }

  return Changed;
}

bool OptimizePICCall::visitNode(MBBInfo &MBBI) {
  bool Changed = false;
  MachineBasicBlock *MBB = MBBI.getNode()->getBlock();

  for (MachineBasicBlock::iterator I = MBB->begin(), E = MBB->end(); I != E;
       ++I) {
    unsigned Reg;
    ValueType Entry;

    // Skip instructions that are not call instructions via registers.
    if (!isCallViaRegister(*I, Reg, Entry))
      continue;

    Changed = true;
    unsigned N = getCount(Entry);

    if (N != 0) {
      // If a function has been called more than twice, we do not have to emit a
      // load instruction to get the function address from the GOT, but can
      // instead reuse the address that has been loaded before.
      if (N >= 2 && !LoadTargetFromGOT)
        getCallTargetRegOpnd(*I)->setReg(getReg(Entry));

      // Erase the $gp operand if this isn't the first time a function has
      // been called. $gp needs to be set up only if the function call can go
      // through a lazy binding stub.
      eraseGPOpnd(*I);
    }

    if (Entry)
      incCntAndSetReg(Entry, Reg);

    setCallTargetReg(MBB, I);
  }

  return Changed;
}

bool OptimizePICCall::isCallViaRegister(MachineInstr &MI, unsigned &Reg,
                                        ValueType &Val) const {
  if (!MI.isCall())
    return false;

  MachineOperand *MO = getCallTargetRegOpnd(MI);

  // Return if MI is not a function call via a register.
  if (!MO)
    return false;

  // Get the instruction that loads the function address from the GOT.
  Reg = MO->getReg();
  Val = (Value*)nullptr;
  MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo();
  MachineInstr *DefMI = MRI.getVRegDef(Reg);

  assert(DefMI);

  // See if DefMI is an instruction that loads from a GOT entry that holds the
  // address of a lazy binding stub.
  if (!DefMI->mayLoad() || DefMI->getNumOperands() < 3)
    return true;

  unsigned Flags = DefMI->getOperand(2).getTargetFlags();

  if (Flags != MipsII::MO_GOT_CALL && Flags != MipsII::MO_CALL_LO16)
    return true;

  // Return the underlying object for the GOT entry in Val.
  assert(DefMI->hasOneMemOperand());
  Val = (*DefMI->memoperands_begin())->getValue();
  if (!Val)
    Val = (*DefMI->memoperands_begin())->getPseudoValue();
  return true;
}

unsigned OptimizePICCall::getCount(ValueType Entry) {
  return ScopedHT.lookup(Entry).first;
}

unsigned OptimizePICCall::getReg(ValueType Entry) {
  unsigned Reg = ScopedHT.lookup(Entry).second;
  assert(Reg);
  return Reg;
}

void OptimizePICCall::incCntAndSetReg(ValueType Entry, unsigned Reg) {
  CntRegP P = ScopedHT.lookup(Entry);
  ScopedHT.insert(Entry, std::make_pair(P.first + 1, Reg));
}

/// Return an OptimizeCall object.
FunctionPass *llvm::createMipsOptimizePICCallPass(MipsTargetMachine &TM) {
  return new OptimizePICCall(TM);
}
<commit_msg>Add optimization bisect support to an optional Mips pass<commit_after>//===--------- MipsOptimizePICCall.cpp - Optimize PIC Calls ---------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This pass eliminates unnecessary instructions that set up $gp and replace
// instructions that load target function addresses with copy instructions.
//
//===----------------------------------------------------------------------===//

#include "Mips.h"
#include "MCTargetDesc/MipsBaseInfo.h"
#include "MipsMachineFunction.h"
#include "MipsTargetMachine.h"
#include "llvm/ADT/ScopedHashTable.h"
#include "llvm/CodeGen/MachineDominators.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/Support/CommandLine.h"

using namespace llvm;

#define DEBUG_TYPE "optimize-mips-pic-call"

static cl::opt<bool> LoadTargetFromGOT("mips-load-target-from-got",
                                       cl::init(true),
                                       cl::desc("Load target address from GOT"),
                                       cl::Hidden);

static cl::opt<bool> EraseGPOpnd("mips-erase-gp-opnd",
                                 cl::init(true), cl::desc("Erase GP Operand"),
                                 cl::Hidden);

namespace {
typedef PointerUnion<const Value *, const PseudoSourceValue *> ValueType;

typedef std::pair<unsigned, unsigned> CntRegP;
typedef RecyclingAllocator<BumpPtrAllocator,
                           ScopedHashTableVal<ValueType, CntRegP> >
AllocatorTy;
typedef ScopedHashTable<ValueType, CntRegP, DenseMapInfo<ValueType>,
                        AllocatorTy> ScopedHTType;

class MBBInfo {
public:
  MBBInfo(MachineDomTreeNode *N);
  const MachineDomTreeNode *getNode() const;
  bool isVisited() const;
  void preVisit(ScopedHTType &ScopedHT);
  void postVisit();

private:
  MachineDomTreeNode *Node;
  ScopedHTType::ScopeTy *HTScope;
};

class OptimizePICCall : public MachineFunctionPass {
public:
  OptimizePICCall(TargetMachine &tm) : MachineFunctionPass(ID) {}

  const char *getPassName() const override { return "Mips OptimizePICCall"; }

  bool runOnMachineFunction(MachineFunction &F) override;

  void getAnalysisUsage(AnalysisUsage &AU) const override {
    AU.addRequired<MachineDominatorTree>();
    MachineFunctionPass::getAnalysisUsage(AU);
  }

private:
  /// \brief Visit MBB.
  bool visitNode(MBBInfo &MBBI);

  /// \brief Test if MI jumps to a function via a register.
  ///
  /// Also, return the virtual register containing the target function's address
  /// and the underlying object in Reg and Val respectively, if the function's
  /// address can be resolved lazily.
  bool isCallViaRegister(MachineInstr &MI, unsigned &Reg,
                         ValueType &Val) const;

  /// \brief Return the number of instructions that dominate the current
  /// instruction and load the function address from object Entry.
  unsigned getCount(ValueType Entry);

  /// \brief Return the destination virtual register of the last instruction
  /// that loads from object Entry.
  unsigned getReg(ValueType Entry);

  /// \brief Update ScopedHT.
  void incCntAndSetReg(ValueType Entry, unsigned Reg);

  ScopedHTType ScopedHT;
  static char ID;
};

char OptimizePICCall::ID = 0;
} // end of anonymous namespace

/// Return the first MachineOperand of MI if it is a used virtual register.
static MachineOperand *getCallTargetRegOpnd(MachineInstr &MI) {
  if (MI.getNumOperands() == 0)
    return nullptr;

  MachineOperand &MO = MI.getOperand(0);

  if (!MO.isReg() || !MO.isUse() ||
      !TargetRegisterInfo::isVirtualRegister(MO.getReg()))
    return nullptr;

  return &MO;
}

/// Return type of register Reg.
static MVT::SimpleValueType getRegTy(unsigned Reg, MachineFunction &MF) {
  const TargetRegisterClass *RC = MF.getRegInfo().getRegClass(Reg);
  assert(RC->vt_end() - RC->vt_begin() == 1);
  return *RC->vt_begin();
}

/// Do the following transformation:
///
/// jalr $vreg
/// =>
/// copy $t9, $vreg
/// jalr $t9
static void setCallTargetReg(MachineBasicBlock *MBB,
                             MachineBasicBlock::iterator I) {
  MachineFunction &MF = *MBB->getParent();
  const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo();
  unsigned SrcReg = I->getOperand(0).getReg();
  unsigned DstReg = getRegTy(SrcReg, MF) == MVT::i32 ? Mips::T9 : Mips::T9_64;
  BuildMI(*MBB, I, I->getDebugLoc(), TII.get(TargetOpcode::COPY), DstReg)
      .addReg(SrcReg);
  I->getOperand(0).setReg(DstReg);
}

/// Search MI's operands for register GP and erase it.
static void eraseGPOpnd(MachineInstr &MI) {
  if (!EraseGPOpnd)
    return;

  MachineFunction &MF = *MI.getParent()->getParent();
  MVT::SimpleValueType Ty = getRegTy(MI.getOperand(0).getReg(), MF);
  unsigned Reg = Ty == MVT::i32 ? Mips::GP : Mips::GP_64;

  for (unsigned I = 0; I < MI.getNumOperands(); ++I) {
    MachineOperand &MO = MI.getOperand(I);
    if (MO.isReg() && MO.getReg() == Reg) {
      MI.RemoveOperand(I);
      return;
    }
  }

  llvm_unreachable(nullptr);
}

MBBInfo::MBBInfo(MachineDomTreeNode *N) : Node(N), HTScope(nullptr) {}

const MachineDomTreeNode *MBBInfo::getNode() const { return Node; }

bool MBBInfo::isVisited() const { return HTScope; }

void MBBInfo::preVisit(ScopedHTType &ScopedHT) {
  HTScope = new ScopedHTType::ScopeTy(ScopedHT);
}

void MBBInfo::postVisit() {
  delete HTScope;
}

// OptimizePICCall methods.
bool OptimizePICCall::runOnMachineFunction(MachineFunction &F) {
  if (skipFunction(*F.getFunction()))
    return false;

  if (static_cast<const MipsSubtarget &>(F.getSubtarget()).inMips16Mode())
    return false;

  // Do a pre-order traversal of the dominator tree.
  MachineDominatorTree *MDT = &getAnalysis<MachineDominatorTree>();
  bool Changed = false;

  SmallVector<MBBInfo, 8> WorkList(1, MBBInfo(MDT->getRootNode()));

  while (!WorkList.empty()) {
    MBBInfo &MBBI = WorkList.back();

    // If this MBB has already been visited, destroy the scope for the MBB and
    // pop it from the work list.
    if (MBBI.isVisited()) {
      MBBI.postVisit();
      WorkList.pop_back();
      continue;
    }

    // Visit the MBB and add its children to the work list.
    MBBI.preVisit(ScopedHT);
    Changed |= visitNode(MBBI);
    const MachineDomTreeNode *Node = MBBI.getNode();
    const std::vector<MachineDomTreeNode *> &Children = Node->getChildren();
    WorkList.append(Children.begin(), Children.end());
  }

  return Changed;
}

bool OptimizePICCall::visitNode(MBBInfo &MBBI) {
  bool Changed = false;
  MachineBasicBlock *MBB = MBBI.getNode()->getBlock();

  for (MachineBasicBlock::iterator I = MBB->begin(), E = MBB->end(); I != E;
       ++I) {
    unsigned Reg;
    ValueType Entry;

    // Skip instructions that are not call instructions via registers.
    if (!isCallViaRegister(*I, Reg, Entry))
      continue;

    Changed = true;
    unsigned N = getCount(Entry);

    if (N != 0) {
      // If a function has been called more than twice, we do not have to emit a
      // load instruction to get the function address from the GOT, but can
      // instead reuse the address that has been loaded before.
      if (N >= 2 && !LoadTargetFromGOT)
        getCallTargetRegOpnd(*I)->setReg(getReg(Entry));

      // Erase the $gp operand if this isn't the first time a function has
      // been called. $gp needs to be set up only if the function call can go
      // through a lazy binding stub.
      eraseGPOpnd(*I);
    }

    if (Entry)
      incCntAndSetReg(Entry, Reg);

    setCallTargetReg(MBB, I);
  }

  return Changed;
}

bool OptimizePICCall::isCallViaRegister(MachineInstr &MI, unsigned &Reg,
                                        ValueType &Val) const {
  if (!MI.isCall())
    return false;

  MachineOperand *MO = getCallTargetRegOpnd(MI);

  // Return if MI is not a function call via a register.
  if (!MO)
    return false;

  // Get the instruction that loads the function address from the GOT.
  Reg = MO->getReg();
  Val = (Value*)nullptr;
  MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo();
  MachineInstr *DefMI = MRI.getVRegDef(Reg);

  assert(DefMI);

  // See if DefMI is an instruction that loads from a GOT entry that holds the
  // address of a lazy binding stub.
  if (!DefMI->mayLoad() || DefMI->getNumOperands() < 3)
    return true;

  unsigned Flags = DefMI->getOperand(2).getTargetFlags();

  if (Flags != MipsII::MO_GOT_CALL && Flags != MipsII::MO_CALL_LO16)
    return true;

  // Return the underlying object for the GOT entry in Val.
  assert(DefMI->hasOneMemOperand());
  Val = (*DefMI->memoperands_begin())->getValue();
  if (!Val)
    Val = (*DefMI->memoperands_begin())->getPseudoValue();
  return true;
}

unsigned OptimizePICCall::getCount(ValueType Entry) {
  return ScopedHT.lookup(Entry).first;
}

unsigned OptimizePICCall::getReg(ValueType Entry) {
  unsigned Reg = ScopedHT.lookup(Entry).second;
  assert(Reg);
  return Reg;
}

void OptimizePICCall::incCntAndSetReg(ValueType Entry, unsigned Reg) {
  CntRegP P = ScopedHT.lookup(Entry);
  ScopedHT.insert(Entry, std::make_pair(P.first + 1, Reg));
}

/// Return an OptimizeCall object.
FunctionPass *llvm::createMipsOptimizePICCallPass(MipsTargetMachine &TM) {
  return new OptimizePICCall(TM);
}
<|endoftext|>


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

#include "PPCTargetAsmInfo.h"
using namespace llvm;

PPCDarwinTargetAsmInfo::PPCDarwinTargetAsmInfo(bool is64Bit) {
  PCSymbol = ".";
  CommentString = ";";
  ExceptionsType = ExceptionHandling::Dwarf;

  if (!is64Bit)
    Data64bitsDirective = 0;      // We can't emit a 64-bit unit in PPC32 mode.
  AssemblerDialect = 0;           // Old-Style mnemonics.
}

PPCLinuxTargetAsmInfo::PPCLinuxTargetAsmInfo(bool is64Bit) {
  CommentString = "#";
  GlobalPrefix = "";
  PrivateGlobalPrefix = ".L";
  UsedDirective = "\t# .no_dead_strip\t";
  WeakRefDirective = "\t.weak\t";

  // Debug Information
  AbsoluteDebugSectionOffsets = true;
  SupportsDebugInformation = true;

  PCSymbol = ".";

  // Set up DWARF directives
  HasLEB128 = true;  // Target asm supports leb128 directives (little-endian)

  // Exceptions handling
  if (!is64Bit)
    ExceptionsType = ExceptionHandling::Dwarf;
  AbsoluteEHSectionOffsets = false;

  ZeroDirective = "\t.space\t";
  SetDirective = "\t.set";
  Data64bitsDirective = is64Bit ? "\t.quad\t" : 0;
  AlignmentIsInBytes = false;
  LCOMMDirective = "\t.lcomm\t";
  AssemblerDialect = 1;   // New-Style mnemonics.
}

<commit_msg>fix CodeGen/PowerPC/2007-01-15-AsmDialect.ll, fallout from r78742<commit_after>//===-- PPCTargetAsmInfo.cpp - PPC asm properties ---------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file contains the declarations of the DarwinTargetAsmInfo properties.
//
//===----------------------------------------------------------------------===//

#include "PPCTargetAsmInfo.h"
using namespace llvm;

PPCDarwinTargetAsmInfo::PPCDarwinTargetAsmInfo(bool is64Bit) {
  PCSymbol = ".";
  CommentString = ";";
  ExceptionsType = ExceptionHandling::Dwarf;

  if (!is64Bit)
    Data64bitsDirective = 0;      // We can't emit a 64-bit unit in PPC32 mode.
  AssemblerDialect = 1;           // New-Style mnemonics.
}

PPCLinuxTargetAsmInfo::PPCLinuxTargetAsmInfo(bool is64Bit) {
  CommentString = "#";
  GlobalPrefix = "";
  PrivateGlobalPrefix = ".L";
  UsedDirective = "\t# .no_dead_strip\t";
  WeakRefDirective = "\t.weak\t";

  // Debug Information
  AbsoluteDebugSectionOffsets = true;
  SupportsDebugInformation = true;

  PCSymbol = ".";

  // Set up DWARF directives
  HasLEB128 = true;  // Target asm supports leb128 directives (little-endian)

  // Exceptions handling
  if (!is64Bit)
    ExceptionsType = ExceptionHandling::Dwarf;
  AbsoluteEHSectionOffsets = false;

  ZeroDirective = "\t.space\t";
  SetDirective = "\t.set";
  Data64bitsDirective = is64Bit ? "\t.quad\t" : 0;
  AlignmentIsInBytes = false;
  LCOMMDirective = "\t.lcomm\t";
  AssemblerDialect = 0;           // Old-Style mnemonics.
}

<|endoftext|>