<commit_before>#include "TLogReplayLink.h"
#include <QFile>
#include <QDebug>
#include <QDateTime>
#include "UASManager.h"
#include "UAS.h"
#include "MainWindow.h"
#include "QGCMAVLinkUASFactory.h"
TLogReplayLink::TLogReplayLink(QObject *parent) :
    LinkInterface(),
    m_toBeDeleted(false),
    m_threadRun(false),
    m_speedVar(50),
    m_posVar(0),
    m_mavlinkDecoder(new MAVLinkDecoder()),
    m_mavlinkInspector(NULL)
{
}
int TLogReplayLink::getId() const
{
    return 1;
}
QString TLogReplayLink::getName() const
{
    return "AP2SimulationLink";
}
void TLogReplayLink::requestReset()
{

}
bool TLogReplayLink::isConnected() const
{
    return false;
}
qint64 TLogReplayLink::getConnectionSpeed() const
{
    return 115200;
}

bool TLogReplayLink::connect()
{
    start();
    return true;
}
bool TLogReplayLink::disconnect()
{
    return false;
}
qint64 TLogReplayLink::bytesAvailable()
{
    return 0;
}
void TLogReplayLink::writeBytes(const char *bytes, qint64 length)
{

}
void TLogReplayLink::readBytes()
{

}
void TLogReplayLink::setSpeed(int speed)
{
    m_variableAccessMutex.lock();
    m_speedVar = speed;
    m_variableAccessMutex.unlock();
}
void TLogReplayLink::setPosition(qint64 pos)
{
    m_variableAccessMutex.lock();
    m_posVar = pos;
    m_variableAccessMutex.unlock();
}

void TLogReplayLink::play()
{
    m_pause = false;
}

void TLogReplayLink::pause()
{
    m_pause = true;
}
bool TLogReplayLink::isPaused()
{
    return m_pause;
}
void TLogReplayLink::setMavlinkDecoder(MAVLinkDecoder *decoder)
{
    m_mavlinkDecoder = decoder;
}
void TLogReplayLink::setMavlinkInspector(QGCMAVLinkInspector *inspector)
{
    m_mavlinkInspector = inspector;
}

void TLogReplayLink::run()
{
    m_pause = false;
    m_threadRun = true;
    emit connected(this);
    emit connected(true);
    emit connected();
    QFile file(m_logFile);
    file.open(QIODevice::ReadOnly);
    int bytesize = 0;
    qint64 msecs = QDateTime::currentMSecsSinceEpoch();
    MainWindow::instance()->toolBar().disableConnectWidget(true);
    MainWindow::instance()->toolBar().overrideDisableConnectWidget(true);
    int privSpeedVar = 100;
    mavlink_message_t message;
    mavlink_status_t status;
    QByteArray timebuf;
    bool firsttime = true;
    int delay = 0;
    qint64 lastLogTime = 0;
    qint64 lastPcTime = 0;
    bool nexttime = false;
    qint64 privatepos = 0;
    while (!file.atEnd() && m_threadRun)
    {
        if (QDateTime::currentMSecsSinceEpoch() - msecs > 1000)
        {
            msecs = QDateTime::currentMSecsSinceEpoch();
            m_variableAccessMutex.lock();

            //m_speedVar is a value, between 25 and 175. These are speed percentages.
            privSpeedVar = m_speedVar; //50 - ((m_speedVar) / 4);
            if (privatepos != m_posVar)
            {
                privatepos = m_posVar;
                if (privatepos > 0 && privatepos < 100)
                {
                    file.seek((privatepos / 100.0) * file.size());
                }
            }
            m_variableAccessMutex.unlock();
        }
        emit logProgress(file.pos(),file.size());
        QByteArray bytes = file.read(128);
        bytesize+=128;

        for (int i=0;i<bytes.size();i++)
        {
            unsigned int decodeState = mavlink_parse_char(14, (uint8_t)(bytes[i]), &message, &status);
            if (decodeState != 1)
            {
                //Not a mavlink byte!
                if (nexttime)
                {
                    timebuf.append(bytes[i]);
                }
                if (timebuf.size() == 8)
                {
                    nexttime = false;

                    //Should be a timestamp for the next packet.
                    unsigned long logmsecs = static_cast<unsigned char>(timebuf.at(0)) << 56;
                    logmsecs += static_cast<unsigned char>(timebuf.at(1)) << 48;
                    logmsecs += static_cast<unsigned char>(timebuf.at(2)) << 40;
                    logmsecs += static_cast<unsigned char>(timebuf.at(3)) << 32;
                    logmsecs = static_cast<unsigned char>(timebuf.at(4)) << 24;
                    logmsecs += static_cast<unsigned char>(timebuf.at(5)) << 16;
                    logmsecs += static_cast<unsigned char>(timebuf.at(6)) << 8;
                    logmsecs += static_cast<unsigned char>(timebuf.at(7)) << 0;

                    timebuf.clear();

                    if (firsttime)
                    {
                        firsttime = false;
                        lastLogTime = logmsecs;
                        lastPcTime = QDateTime::currentMSecsSinceEpoch();
                    }
                    else
                    {
                        //Difference in time between the last time we read a timestamp, and this time
                        qint64 pcdiff = QDateTime::currentMSecsSinceEpoch() - lastPcTime;
                        lastPcTime = QDateTime::currentMSecsSinceEpoch();

                        //Difference in time between the last timestamp we fired off, and this one
                        qint64 logdiff = logmsecs - lastLogTime;
                        lastLogTime = logmsecs;
                        logdiff /= 1000;

                        if (logdiff < pcdiff)
                        {
                            //The next mavlink packet was fired faster than our loop is running, send it immediatly
                            //Fire immediatly
                            delay = 0;
                        }
                        else
                        {
                            //The next mavlink packet was sent logdiff-pcdiff millseconds after the current time
                            delay = logdiff-pcdiff;

                        }
                    }

                }
            }
            else if (decodeState == 1)
            {
                nexttime = true;
                //Good decode
                if (message.sysid == 255)
                {
                    //GCS packet, ignore it
                }
                else
                {
                    UASInterface* uas = UASManager::instance()->getUASForId(message.sysid);
                    if (!uas && message.msgid == MAVLINK_MSG_ID_HEARTBEAT)
                    {
                        mavlink_heartbeat_t heartbeat;
                        // Reset version field to 0
                        heartbeat.mavlink_version = 0;
                        mavlink_msg_heartbeat_decode(&message, &heartbeat);

                        // Create a new UAS object
                        if (heartbeat.autopilot == MAV_AUTOPILOT_ARDUPILOTMEGA)
                        {
                            ArduPilotMegaMAV* mav = new ArduPilotMegaMAV(0, message.sysid);
                            mav->setSystemType((int)heartbeat.type);
                            uas = mav;
                            // Make UAS aware that this link can be used to communicate with the actual robot
                            uas->addLink(this);

                            // Now add UAS to "official" list, which makes the whole application aware of it
                            UASManager::instance()->addUAS(uas);

                        }
                    }
                    else if (uas)
                    {
                        if (delay > 0 && delay < 10000)
                        {
                            //Split the delay into 100msec chunks, to allow for canceling.
                            int realdelay = delay / ((double)privSpeedVar / 100.0);
                            int repeat = realdelay / 100;
                            for (int i=0;i<repeat;i++)
                            {
                                msleep(100);
                                if (!m_threadRun)
                                {
                                    //Break out
                                    MainWindow::instance()->toolBar().overrideDisableConnectWidget(false);
                                    MainWindow::instance()->toolBar().disableConnectWidget(false);
                                    emit disconnected(this);
                                    emit disconnected();
                                    emit connected(false);
                                    UASManager::instance()->removeUAS(UASManager::instance()->getActiveUAS());
                                    return;
                                }
                            }
                            msleep(realdelay - repeat);
                            //msleep(delay / ((double)privSpeedVar / 100.0));
                        }
                        else
                        {
                            msleep(1);
                        }
                        uas->receiveMessage(this,message);
                        m_mavlinkDecoder->receiveMessage(this,message);
                        if (m_mavlinkInspector)
                        {
                            m_mavlinkInspector->receiveMessage(this,message);
                        }
                    }
                    else
                    {
                        //no UAS, and not a heartbeat
                    }
                }

            }
        }
        while (m_pause)
        {
            msleep(100);
        }
    }
    if (m_threadRun)
    {
        m_toBeDeleted = true;
    }
    MainWindow::instance()->toolBar().overrideDisableConnectWidget(false);
    MainWindow::instance()->toolBar().disableConnectWidget(false);
    emit disconnected(this);
    emit disconnected();
    emit connected(false);
    UASManager::instance()->removeUAS(UASManager::instance()->getActiveUAS());
}

void TLogReplayLink::setLog(QString logfile)
{
    m_logFile = logfile;
}
void TLogReplayLink::stop()
{
    m_toBeDeleted = false;
    m_threadRun = false;
}

bool TLogReplayLink::toBeDeleted()
{
    return m_toBeDeleted;
}
<commit_msg>Comms: Cannot shift a char by 56 bits; ulong not guaranteed to be 64 bits<commit_after>#include "TLogReplayLink.h"
#include <QFile>
#include <QDebug>
#include <QDateTime>
#include "UASManager.h"
#include "UAS.h"
#include "MainWindow.h"
#include "QGCMAVLinkUASFactory.h"
TLogReplayLink::TLogReplayLink(QObject *parent) :
    LinkInterface(),
    m_toBeDeleted(false),
    m_threadRun(false),
    m_speedVar(50),
    m_posVar(0),
    m_mavlinkDecoder(new MAVLinkDecoder()),
    m_mavlinkInspector(NULL)
{
}
int TLogReplayLink::getId() const
{
    return 1;
}
QString TLogReplayLink::getName() const
{
    return "AP2SimulationLink";
}
void TLogReplayLink::requestReset()
{

}
bool TLogReplayLink::isConnected() const
{
    return false;
}
qint64 TLogReplayLink::getConnectionSpeed() const
{
    return 115200;
}

bool TLogReplayLink::connect()
{
    start();
    return true;
}
bool TLogReplayLink::disconnect()
{
    return false;
}
qint64 TLogReplayLink::bytesAvailable()
{
    return 0;
}
void TLogReplayLink::writeBytes(const char *bytes, qint64 length)
{

}
void TLogReplayLink::readBytes()
{

}
void TLogReplayLink::setSpeed(int speed)
{
    m_variableAccessMutex.lock();
    m_speedVar = speed;
    m_variableAccessMutex.unlock();
}
void TLogReplayLink::setPosition(qint64 pos)
{
    m_variableAccessMutex.lock();
    m_posVar = pos;
    m_variableAccessMutex.unlock();
}

void TLogReplayLink::play()
{
    m_pause = false;
}

void TLogReplayLink::pause()
{
    m_pause = true;
}
bool TLogReplayLink::isPaused()
{
    return m_pause;
}
void TLogReplayLink::setMavlinkDecoder(MAVLinkDecoder *decoder)
{
    m_mavlinkDecoder = decoder;
}
void TLogReplayLink::setMavlinkInspector(QGCMAVLinkInspector *inspector)
{
    m_mavlinkInspector = inspector;
}

void TLogReplayLink::run()
{
    m_pause = false;
    m_threadRun = true;
    emit connected(this);
    emit connected(true);
    emit connected();
    QFile file(m_logFile);
    file.open(QIODevice::ReadOnly);
    int bytesize = 0;
    qint64 msecs = QDateTime::currentMSecsSinceEpoch();
    MainWindow::instance()->toolBar().disableConnectWidget(true);
    MainWindow::instance()->toolBar().overrideDisableConnectWidget(true);
    int privSpeedVar = 100;
    mavlink_message_t message;
    mavlink_status_t status;
    QByteArray timebuf;
    bool firsttime = true;
    int delay = 0;
    qint64 lastLogTime = 0;
    qint64 lastPcTime = 0;
    bool nexttime = false;
    qint64 privatepos = 0;
    while (!file.atEnd() && m_threadRun)
    {
        if (QDateTime::currentMSecsSinceEpoch() - msecs > 1000)
        {
            msecs = QDateTime::currentMSecsSinceEpoch();
            m_variableAccessMutex.lock();

            //m_speedVar is a value, between 25 and 175. These are speed percentages.
            privSpeedVar = m_speedVar; //50 - ((m_speedVar) / 4);
            if (privatepos != m_posVar)
            {
                privatepos = m_posVar;
                if (privatepos > 0 && privatepos < 100)
                {
                    file.seek((privatepos / 100.0) * file.size());
                }
            }
            m_variableAccessMutex.unlock();
        }
        emit logProgress(file.pos(),file.size());
        QByteArray bytes = file.read(128);
        bytesize+=128;

        for (int i=0;i<bytes.size();i++)
        {
            unsigned int decodeState = mavlink_parse_char(14, (uint8_t)(bytes[i]), &message, &status);
            if (decodeState != 1)
            {
                //Not a mavlink byte!
                if (nexttime)
                {
                    timebuf.append(bytes[i]);
                }
                if (timebuf.size() == 8)
                {
                    nexttime = false;

                    //Should be a timestamp for the next packet.
                    quint64 logmsecs = static_cast<quint64>(timebuf.at(0)) << 56;
                    logmsecs += static_cast<quint64>(timebuf.at(1)) << 48;
                    logmsecs += static_cast<quint64>(timebuf.at(2)) << 40;
                    logmsecs += static_cast<quint64>(timebuf.at(3)) << 32;
                    logmsecs = static_cast<quint64>(timebuf.at(4)) << 24;
                    logmsecs += static_cast<quint64>(timebuf.at(5)) << 16;
                    logmsecs += static_cast<quint64>(timebuf.at(6)) << 8;
                    logmsecs += static_cast<quint64>(timebuf.at(7)) << 0;

                    timebuf.clear();

                    if (firsttime)
                    {
                        firsttime = false;
                        lastLogTime = logmsecs;
                        lastPcTime = QDateTime::currentMSecsSinceEpoch();
                    }
                    else
                    {
                        //Difference in time between the last time we read a timestamp, and this time
                        qint64 pcdiff = QDateTime::currentMSecsSinceEpoch() - lastPcTime;
                        lastPcTime = QDateTime::currentMSecsSinceEpoch();

                        //Difference in time between the last timestamp we fired off, and this one
                        qint64 logdiff = logmsecs - lastLogTime;
                        lastLogTime = logmsecs;
                        logdiff /= 1000;

                        if (logdiff < pcdiff)
                        {
                            //The next mavlink packet was fired faster than our loop is running, send it immediatly
                            //Fire immediatly
                            delay = 0;
                        }
                        else
                        {
                            //The next mavlink packet was sent logdiff-pcdiff millseconds after the current time
                            delay = logdiff-pcdiff;

                        }
                    }

                }
            }
            else if (decodeState == 1)
            {
                nexttime = true;
                //Good decode
                if (message.sysid == 255)
                {
                    //GCS packet, ignore it
                }
                else
                {
                    UASInterface* uas = UASManager::instance()->getUASForId(message.sysid);
                    if (!uas && message.msgid == MAVLINK_MSG_ID_HEARTBEAT)
                    {
                        mavlink_heartbeat_t heartbeat;
                        // Reset version field to 0
                        heartbeat.mavlink_version = 0;
                        mavlink_msg_heartbeat_decode(&message, &heartbeat);

                        // Create a new UAS object
                        if (heartbeat.autopilot == MAV_AUTOPILOT_ARDUPILOTMEGA)
                        {
                            ArduPilotMegaMAV* mav = new ArduPilotMegaMAV(0, message.sysid);
                            mav->setSystemType((int)heartbeat.type);
                            uas = mav;
                            // Make UAS aware that this link can be used to communicate with the actual robot
                            uas->addLink(this);

                            // Now add UAS to "official" list, which makes the whole application aware of it
                            UASManager::instance()->addUAS(uas);

                        }
                    }
                    else if (uas)
                    {
                        if (delay > 0 && delay < 10000)
                        {
                            //Split the delay into 100msec chunks, to allow for canceling.
                            int realdelay = delay / ((double)privSpeedVar / 100.0);
                            int repeat = realdelay / 100;
                            for (int i=0;i<repeat;i++)
                            {
                                msleep(100);
                                if (!m_threadRun)
                                {
                                    //Break out
                                    MainWindow::instance()->toolBar().overrideDisableConnectWidget(false);
                                    MainWindow::instance()->toolBar().disableConnectWidget(false);
                                    emit disconnected(this);
                                    emit disconnected();
                                    emit connected(false);
                                    UASManager::instance()->removeUAS(UASManager::instance()->getActiveUAS());
                                    return;
                                }
                            }
                            msleep(realdelay - repeat);
                            //msleep(delay / ((double)privSpeedVar / 100.0));
                        }
                        else
                        {
                            msleep(1);
                        }
                        uas->receiveMessage(this,message);
                        m_mavlinkDecoder->receiveMessage(this,message);
                        if (m_mavlinkInspector)
                        {
                            m_mavlinkInspector->receiveMessage(this,message);
                        }
                    }
                    else
                    {
                        //no UAS, and not a heartbeat
                    }
                }

            }
        }
        while (m_pause)
        {
            msleep(100);
        }
    }
    if (m_threadRun)
    {
        m_toBeDeleted = true;
    }
    MainWindow::instance()->toolBar().overrideDisableConnectWidget(false);
    MainWindow::instance()->toolBar().disableConnectWidget(false);
    emit disconnected(this);
    emit disconnected();
    emit connected(false);
    UASManager::instance()->removeUAS(UASManager::instance()->getActiveUAS());
}

void TLogReplayLink::setLog(QString logfile)
{
    m_logFile = logfile;
}
void TLogReplayLink::stop()
{
    m_toBeDeleted = false;
    m_threadRun = false;
}

bool TLogReplayLink::toBeDeleted()
{
    return m_toBeDeleted;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#pragma once
#include <memory>
#include <utility> // for std::move
namespace game
{
  template <class T> struct Maybe_Owned;

  /*!
   * Named to be consistent with make_shared and make_unique.
   */
  template <class T, class... Args>
  Maybe_Owned<T> make_maybe_owned(Args&&... args) noexcept
  {
    return Maybe_Owned<T>(new T(std::forward<Args>(args)...), true);
  }
  /*!
   * Named to contrast and stress the owned nature of the pointer passed in.
   */
  template <class T>
  Maybe_Owned<T> make_owned_maybe(T* ptr) noexcept
  {
    return Maybe_Owned<T>(ptr, true);
  }

  // TODO Support a custom deleter.
  template <class T>
  struct Maybe_Owned
  {
    // ** PREFER THE ABOVE FUNCTION MAKE_MAYBE_OWNED **
    template <class... Args>
    explicit Maybe_Owned(Args&&... args) noexcept;

    explicit Maybe_Owned(T&& t) noexcept;

    /*!
     * \brief Construct the maybe owned with a borrowed/unowned ptr.
     *
     * \note If you are using this constructor to initialize a maybe owned
     * of type base with a new pointer to a derived instance, make sure to
     * set the second parameter to true. Better yet use make_maybe_owned
     * declared above! make_owned_maybe can also be used if the pointer
     * cannot be constructed by the client.
     */
    /* implicit */ Maybe_Owned(T* t, bool owned) noexcept;

    Maybe_Owned() noexcept;

    template <class R>
    Maybe_Owned(std::unique_ptr<R>) noexcept;

    template <class R>
    Maybe_Owned(Maybe_Owned<R>&&) noexcept;
    Maybe_Owned(Maybe_Owned const&) noexcept = delete;

    template <class R>
    Maybe_Owned& operator=(std::unique_ptr<R>) noexcept;

    template <class R>
    Maybe_Owned& operator=(Maybe_Owned<R>&&) noexcept;
    Maybe_Owned& operator=(Maybe_Owned const&&) noexcept = delete;

    ~Maybe_Owned() noexcept;

    void set_owned(T&& t) noexcept;
    void set_owned(T const& t) noexcept;
    void set_owned(T* t) noexcept;

    void set_pointer(T* t) noexcept;

    template <class R>
    void set_pointer(Maybe_Owned<R> const&) noexcept;

    template <class R, class... Args>
    void emplace_owned(Args&&... args) noexcept;

    T* get() const noexcept;
    T&& unwrap() noexcept;
    T* operator->() const noexcept;
    T& operator*() const noexcept;

    operator bool() const noexcept;

    bool is_owned() const noexcept;
    bool is_pointer() const noexcept;

    // TODO: Remove this function, it is inconsistent from the unique_ptr's
    // reset somehow, I think.
    void reset() noexcept;
  private:
    bool owned_ = false;
    T* ptr_ = nullptr;
  };

  template <class T>
  template <class... Args>
  Maybe_Owned<T>::Maybe_Owned(Args&&... args) noexcept
    : owned_(true), ptr_(new T{std::forward<Args>(args)...}) {}

  template <class T>
  Maybe_Owned<T>::Maybe_Owned(T&& t) noexcept
    : owned_(true), ptr_(new T(std::move(t))) {}

  template <class T>
  Maybe_Owned<T>::Maybe_Owned(T* t, bool o) noexcept : owned_(o), ptr_(t) {}

  template <class T>
  Maybe_Owned<T>::Maybe_Owned() noexcept : owned_(false), ptr_(nullptr) {}

  template <class T>
  template <class R>
  Maybe_Owned<T>::Maybe_Owned(std::unique_ptr<R> ptr) noexcept
    : owned_(true), ptr_(ptr.release()) {}

  template <class T>
  Maybe_Owned<T>::~Maybe_Owned() noexcept
  {
    reset();
  }

  template <class T>
  template <class R>
  Maybe_Owned<T>::Maybe_Owned(Maybe_Owned<R>&& mo1) noexcept
    : owned_(mo1.owned_), ptr_(mo1.ptr_)
  {
    // Check out the move operator= for reasoning as to why we don't null-ify
    // the mo1 pointer.

    mo1.owned_ = false;
  }

  template <class T>
  template <class R>
  Maybe_Owned<T>& Maybe_Owned<T>::operator=(std::unique_ptr<R> ptr) noexcept
  {
    reset();

    owned_ = true;
    ptr_ = ptr.release();

    return *this;
  }

  template <class T>
  template <class R>
  Maybe_Owned<T>& Maybe_Owned<T>::operator=(Maybe_Owned<R>&& mo1) noexcept
  {
    // Release ourselves, possibly unallocating our own pointer (if we own it).
    reset();

    owned_ = mo1.owned_;
    ptr_ = mo1.ptr_;

    // We don't need to null their pointer, just kill their ownership, which is
    // enough. It also has the lovely side effect of leaving mo1 in a
    // non-owning but functionally-equivalent state!
    mo1.owned_ = false;

    return *this;
  }

  template <class T>
  void Maybe_Owned<T>::set_owned(T&& t) noexcept
  {
    *this = Maybe_Owned<T>(new T(std::move(t)), true);
  }
  template <class T>
  void Maybe_Owned<T>::set_owned(T const& t) noexcept
  {
    *this = Maybe_Owned<T>(new T(t), true);
  }
  template <class T>
  void Maybe_Owned<T>::set_owned(T* t) noexcept
  {
    *this = Maybe_Owned<T>(t, true);
  }
  template <class T>
  void Maybe_Owned<T>::set_pointer(T* t) noexcept
  {
    *this = Maybe_Owned<T>(t, false);
  }
  template <class T>
  template <class R>
  void Maybe_Owned<T>::set_pointer(Maybe_Owned<R> const& mo) noexcept
  {
    *this = Maybe_Owned<T>(mo.get(), false);
  }

  template <class T>
  template <class R, class... Args>
  void Maybe_Owned<T>::emplace_owned(Args&&... args) noexcept
  {
    *this = make_maybe_owned<T>(std::forward<Args>(args)...);
  }

  template <class T>
  T* Maybe_Owned<T>::get() const noexcept
  {
    return ptr_;
  }

  template <class T>
  T&& Maybe_Owned<T>::unwrap() noexcept
  {
    T&& old_t = std::move(*ptr_);
    reset();
    return std::move(old_t);
  }

  template <class T>
  T* Maybe_Owned<T>::operator->() const noexcept
  {
    return ptr_;
  }
  template <class T>
  T& Maybe_Owned<T>::operator*() const noexcept
  {
    return *ptr_;
  }

  template <class T>
  bool Maybe_Owned<T>::is_owned() const noexcept
  {
    return owned_;
  }
  template <class T>
  bool Maybe_Owned<T>::is_pointer() const noexcept
  {
    return !owned_;
  }

  template <class T>
  Maybe_Owned<T>::operator bool() const noexcept
  {
    return get();
  }
  template <class T>
  void Maybe_Owned<T>::reset() noexcept
  {
    if(owned_)
    {
      delete ptr_;
    }
    ptr_ = nullptr;
    owned_ = false;
  }
}
<commit_msg>We can now easily create a Maybe_Owned pointing to another!<commit_after>/*
 * Copyright (C) 2015 Luke San Antonio
 * All rights reserved.
 */
#pragma once
#include <memory>
#include <utility> // for std::move
namespace game
{
  template <class T> struct Maybe_Owned;

  /*!
   * Named to be consistent with make_shared and make_unique.
   */
  template <class T, class... Args>
  Maybe_Owned<T> make_maybe_owned(Args&&... args) noexcept
  {
    return Maybe_Owned<T>(new T(std::forward<Args>(args)...), true);
  }
  /*!
   * Named to contrast and stress the owned nature of the pointer passed in.
   */
  template <class T>
  Maybe_Owned<T> make_owned_maybe(T* ptr) noexcept
  {
    return Maybe_Owned<T>(ptr, true);
  }

  template <class T>
  Maybe_Owned<T> ref_mo(Maybe_Owned<T> const& mo) noexcept
  {
    return Maybe_Owned<T>{mo.get(), false};
  }

  // TODO Support a custom deleter.
  template <class T>
  struct Maybe_Owned
  {
    // ** PREFER THE ABOVE FUNCTION MAKE_MAYBE_OWNED **
    template <class... Args>
    explicit Maybe_Owned(Args&&... args) noexcept;

    explicit Maybe_Owned(T&& t) noexcept;

    /*!
     * \brief Construct the maybe owned with a borrowed/unowned ptr.
     *
     * \note If you are using this constructor to initialize a maybe owned
     * of type base with a new pointer to a derived instance, make sure to
     * set the second parameter to true. Better yet use make_maybe_owned
     * declared above! make_owned_maybe can also be used if the pointer
     * cannot be constructed by the client.
     */
    /* implicit */ Maybe_Owned(T* t, bool owned) noexcept;

    Maybe_Owned() noexcept;

    template <class R>
    Maybe_Owned(std::unique_ptr<R>) noexcept;

    template <class R>
    Maybe_Owned(Maybe_Owned<R>&&) noexcept;
    Maybe_Owned(Maybe_Owned const&) noexcept = delete;

    template <class R>
    Maybe_Owned& operator=(std::unique_ptr<R>) noexcept;

    template <class R>
    Maybe_Owned& operator=(Maybe_Owned<R>&&) noexcept;
    Maybe_Owned& operator=(Maybe_Owned const&&) noexcept = delete;

    ~Maybe_Owned() noexcept;

    void set_owned(T&& t) noexcept;
    void set_owned(T const& t) noexcept;
    void set_owned(T* t) noexcept;

    void set_pointer(T* t) noexcept;

    template <class R>
    void set_pointer(Maybe_Owned<R> const&) noexcept;

    template <class R, class... Args>
    void emplace_owned(Args&&... args) noexcept;

    T* get() const noexcept;
    T&& unwrap() noexcept;
    T* operator->() const noexcept;
    T& operator*() const noexcept;

    operator bool() const noexcept;

    bool is_owned() const noexcept;
    bool is_pointer() const noexcept;

    // TODO: Remove this function, it is inconsistent from the unique_ptr's
    // reset somehow, I think.
    void reset() noexcept;
  private:
    bool owned_ = false;
    T* ptr_ = nullptr;
  };

  template <class T>
  template <class... Args>
  Maybe_Owned<T>::Maybe_Owned(Args&&... args) noexcept
    : owned_(true), ptr_(new T{std::forward<Args>(args)...}) {}

  template <class T>
  Maybe_Owned<T>::Maybe_Owned(T&& t) noexcept
    : owned_(true), ptr_(new T(std::move(t))) {}

  template <class T>
  Maybe_Owned<T>::Maybe_Owned(T* t, bool o) noexcept : owned_(o), ptr_(t) {}

  template <class T>
  Maybe_Owned<T>::Maybe_Owned() noexcept : owned_(false), ptr_(nullptr) {}

  template <class T>
  template <class R>
  Maybe_Owned<T>::Maybe_Owned(std::unique_ptr<R> ptr) noexcept
    : owned_(true), ptr_(ptr.release()) {}

  template <class T>
  Maybe_Owned<T>::~Maybe_Owned() noexcept
  {
    reset();
  }

  template <class T>
  template <class R>
  Maybe_Owned<T>::Maybe_Owned(Maybe_Owned<R>&& mo1) noexcept
    : owned_(mo1.owned_), ptr_(mo1.ptr_)
  {
    // Check out the move operator= for reasoning as to why we don't null-ify
    // the mo1 pointer.

    mo1.owned_ = false;
  }

  template <class T>
  template <class R>
  Maybe_Owned<T>& Maybe_Owned<T>::operator=(std::unique_ptr<R> ptr) noexcept
  {
    reset();

    owned_ = true;
    ptr_ = ptr.release();

    return *this;
  }

  template <class T>
  template <class R>
  Maybe_Owned<T>& Maybe_Owned<T>::operator=(Maybe_Owned<R>&& mo1) noexcept
  {
    // Release ourselves, possibly unallocating our own pointer (if we own it).
    reset();

    owned_ = mo1.owned_;
    ptr_ = mo1.ptr_;

    // We don't need to null their pointer, just kill their ownership, which is
    // enough. It also has the lovely side effect of leaving mo1 in a
    // non-owning but functionally-equivalent state!
    mo1.owned_ = false;

    return *this;
  }

  template <class T>
  void Maybe_Owned<T>::set_owned(T&& t) noexcept
  {
    *this = Maybe_Owned<T>(new T(std::move(t)), true);
  }
  template <class T>
  void Maybe_Owned<T>::set_owned(T const& t) noexcept
  {
    *this = Maybe_Owned<T>(new T(t), true);
  }
  template <class T>
  void Maybe_Owned<T>::set_owned(T* t) noexcept
  {
    *this = Maybe_Owned<T>(t, true);
  }
  template <class T>
  void Maybe_Owned<T>::set_pointer(T* t) noexcept
  {
    *this = Maybe_Owned<T>(t, false);
  }
  template <class T>
  template <class R>
  void Maybe_Owned<T>::set_pointer(Maybe_Owned<R> const& mo) noexcept
  {
    *this = Maybe_Owned<T>(mo.get(), false);
  }

  template <class T>
  template <class R, class... Args>
  void Maybe_Owned<T>::emplace_owned(Args&&... args) noexcept
  {
    *this = make_maybe_owned<T>(std::forward<Args>(args)...);
  }

  template <class T>
  T* Maybe_Owned<T>::get() const noexcept
  {
    return ptr_;
  }

  template <class T>
  T&& Maybe_Owned<T>::unwrap() noexcept
  {
    T&& old_t = std::move(*ptr_);
    reset();
    return std::move(old_t);
  }

  template <class T>
  T* Maybe_Owned<T>::operator->() const noexcept
  {
    return ptr_;
  }
  template <class T>
  T& Maybe_Owned<T>::operator*() const noexcept
  {
    return *ptr_;
  }

  template <class T>
  bool Maybe_Owned<T>::is_owned() const noexcept
  {
    return owned_;
  }
  template <class T>
  bool Maybe_Owned<T>::is_pointer() const noexcept
  {
    return !owned_;
  }

  template <class T>
  Maybe_Owned<T>::operator bool() const noexcept
  {
    return get();
  }
  template <class T>
  void Maybe_Owned<T>::reset() noexcept
  {
    if(owned_)
    {
      delete ptr_;
    }
    ptr_ = nullptr;
    owned_ = false;
  }
}
<|endoftext|>


<commit_before>//===- tools/seec-trace-view/StateEvaluationTree.cpp ----------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#include "seec/Clang/MappedAST.hpp"
#include "seec/Clang/MappedFunctionState.hpp"
#include "seec/Clang/MappedThreadState.hpp"
#include "seec/Clang/MappedValue.hpp"

// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
  #include <wx/wx.h>
#endif
#include "seec/wxWidgets/CleanPreprocessor.h"

#include "clang/AST/Expr.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/RecursiveASTVisitor.h"

#include "llvm/Support/raw_ostream.h"

#include "NotifyContext.hpp"
#include "StateAccessToken.hpp"
#include "StateEvaluationTree.hpp"

#include <string>

BEGIN_EVENT_TABLE(StateEvaluationTreePanel, wxScrolled<wxPanel>)
  EVT_PAINT(StateEvaluationTreePanel::OnPaint)
  EVT_MOTION(StateEvaluationTreePanel::OnMouseMoved)
  EVT_LEAVE_WINDOW(StateEvaluationTreePanel::OnMouseLeftWindow)
END_EVENT_TABLE()

StateEvaluationTreePanel::DisplaySettings::DisplaySettings()
: PageBorderHorizontal(1.0),
  PageBorderVertical(1.0),
  NodeBorderVertical(0.5),
  CodeFontSize(12),
  NodeBackground(204, 204, 204),
  NodeBorder(102, 102, 102),
  NodeHighlightedBackground(102, 204, 204),
  NodeHighlightedBorder(51, 102, 102)
{}

StateEvaluationTreePanel::StateEvaluationTreePanel()
: Settings(),
  Notifier(nullptr),
  CurrentAccess(),
  CurrentProcess(nullptr),
  CurrentThread(nullptr),
  ActiveFn(nullptr),
  CodeFont(),
  Statement(),
  Nodes(),
  HoverNodeIt(Nodes.end())
{}

StateEvaluationTreePanel::StateEvaluationTreePanel(wxWindow *Parent,
                                                   ContextNotifier &TheNotifier,
                                                   wxWindowID ID,
                                                   wxPoint const &Position,
                                                   wxSize const &Size)
: Settings(),
  Notifier(nullptr),
  CurrentAccess(),
  CurrentProcess(nullptr),
  CurrentThread(nullptr),
  ActiveFn(nullptr),
  CodeFont(),
  Statement(),
  Nodes(),
  HoverNodeIt(Nodes.end())
{
  Create(Parent, TheNotifier, ID, Position, Size);
}

StateEvaluationTreePanel::~StateEvaluationTreePanel() = default;

bool StateEvaluationTreePanel::Create(wxWindow *Parent,
                                      ContextNotifier &WithNotifier,
                                      wxWindowID ID,
                                      wxPoint const &Position,
                                      wxSize const &Size)
{
  if (!wxScrolled<wxPanel>::Create(Parent, ID, Position, Size))
    return false;

  SetBackgroundStyle(wxBG_STYLE_PAINT);
  CodeFont = wxFont{wxFontInfo(Settings.CodeFontSize)
                    .Family(wxFONTFAMILY_MODERN)
                    .AntiAliased(true)};
  SetScrollRate(10, 10);

  return true;
}

/// \brief Determine if a Stmt is suitable for evaluation tree display.
///
static bool isSuitableEvaluationRoot(clang::Stmt const * const S)
{
  return llvm::isa<clang::Expr>(S);
}

/// \brief Find the "top-level" Stmt suitable for evaluation tree display.
///
static
clang::Stmt const *getEvaluationRoot(clang::Stmt const *S,
                                     seec::seec_clang::MappedAST const &AST)
{
  if (!isSuitableEvaluationRoot(S))
    return nullptr;

  while (true) {
    auto const MaybeParent = AST.getParent(S);
    if (!MaybeParent.assigned<clang::Stmt const *>())
      break;

    auto const Parent = MaybeParent.get<clang::Stmt const *>();
    if (!Parent || !isSuitableEvaluationRoot(Parent))
      break;

    S = Parent;
  }

  return S;
}

/// \brief Records the range of each Stmt in a pretty-printed Stmt.
///
class SubRangeRecorder : public clang::PrinterHelper
{
  clang::PrintingPolicy &Policy;

  std::string Buffer;

  llvm::raw_string_ostream BufferOS;

  llvm::DenseMap<clang::Stmt *, std::pair<uint64_t, std::size_t>> Ranges;

public:
  SubRangeRecorder(clang::PrintingPolicy &WithPolicy)
  : Policy(WithPolicy),
    Buffer(),
    BufferOS(Buffer),
    Ranges()
  {}

  virtual bool handledStmt(clang::Stmt *E, llvm::raw_ostream &OS) {
    // Print the Stmt to determine the length of its printed text.
    Buffer.clear();
    E->printPretty(BufferOS, nullptr, Policy);
    BufferOS.flush();

    // Record the start and length of the Stmt's printed text.
    Ranges.insert(std::make_pair(E, std::make_pair(OS.tell(), Buffer.size())));

    return false;
  }

  decltype(Ranges) &getRanges() {
    return Ranges;
  }

  decltype(Ranges) const &getRanges() const {
    return Ranges;
  }
};

/// \brief Records the depth of each sub-node in a Stmt.
///
class DepthRecorder : public clang::RecursiveASTVisitor<DepthRecorder>
{
  unsigned CurrentDepth;

  unsigned MaxDepth;

  llvm::DenseMap<clang::Stmt const *, unsigned> Depths;

public:
  DepthRecorder()
  : CurrentDepth(0),
    MaxDepth(0),
    Depths()
  {}

  bool shouldUseDataRecursionFor(clang::Stmt *S) {
    return false;
  }

  bool TraverseStmt(clang::Stmt *S) {
    if (!S)
      return true;

    if (CurrentDepth > MaxDepth)
      MaxDepth = CurrentDepth;

    Depths[S] = CurrentDepth;

    ++CurrentDepth;

    clang::RecursiveASTVisitor<DepthRecorder>::TraverseStmt(S);

    --CurrentDepth;

    return true;
  }

  unsigned getMaxDepth() const {
    return MaxDepth;
  }

  decltype(Depths) &getDepths() {
    return Depths;
  }

  decltype(Depths) const &getDepths() const {
    return Depths;
  }
};

void StateEvaluationTreePanel::show(std::shared_ptr<StateAccessToken> Access,
                                    seec::cm::ProcessState const &Process,
                                    seec::cm::ThreadState const &Thread)
{
  CurrentAccess = std::move(Access);
  CurrentProcess = &Process;
  CurrentThread = &Thread;
  ActiveFn = nullptr;
  Statement.clear();
  Nodes.clear();
  HoverNodeIt = Nodes.end();

  wxClientDC dc(this);

  // Recalculate the data here.
  if (!CurrentThread) {
    render(dc);
    return;
  }

  auto &Stack = CurrentThread->getCallStack();
  if (Stack.empty()) {
    render(dc);
    return;
  }

  ActiveFn = &(Stack.back().get());
  auto const MappedAST = ActiveFn->getMappedAST();
  auto const ActiveStmt = ActiveFn->getActiveStmt();
  if (!ActiveStmt) {
    render(dc);
    return;
  }

  auto const TopStmt = getEvaluationRoot(ActiveStmt, *MappedAST);
  if (!TopStmt) {
    render(dc);
    return;
  }

  std::string PrettyPrintedStmt;
  llvm::raw_string_ostream StmtOS(PrettyPrintedStmt);

  // TODO: get these from the ASTContext.
  clang::LangOptions LangOpts;

  clang::PrintingPolicy Policy(LangOpts);
  Policy.Indentation = 0;
  Policy.Bool = true;
  Policy.ConstantArraySizeAsWritten = true;

  SubRangeRecorder PrinterHelper(Policy);

  TopStmt->printPretty(StmtOS, &PrinterHelper, Policy);

  StmtOS.flush();

  // Determine the "depth" of each sub-Stmt.
  DepthRecorder DepthRecord;
  DepthRecord.TraverseStmt(const_cast<clang::Stmt *>(TopStmt));
  auto const &Depths = DepthRecord.getDepths();
  auto const MaxDepth = DepthRecord.getMaxDepth();

  // Now save all of the calculated information for the render method.
  Statement = PrettyPrintedStmt;
  auto const &Ranges = PrinterHelper.getRanges();

  // Calculate the new size of the display.
  dc.SetFont(CodeFont);
  auto const StatementExtent = dc.GetTextExtent(Statement);
  auto const CharWidth = dc.GetCharWidth();
  auto const CharHeight = dc.GetCharHeight();

  wxCoord const PageBorderH = CharWidth * Settings.PageBorderHorizontal;
  wxCoord const PageBorderV = CharHeight * Settings.PageBorderVertical;
  wxCoord const NodeBorderV = CharHeight * Settings.NodeBorderVertical;

  auto const TotalWidth = StatementExtent.GetWidth() + (2 * PageBorderH);

  // Depth is zero-based, so there are (MaxDepth+1) lines for sub-nodes, plus
  // one line for the pretty-printed top-level node.
  auto const TotalHeight = ((MaxDepth + 2) * CharHeight)
                           + ((MaxDepth + 1) * NodeBorderV)
                           + (2 * PageBorderV);

  SetVirtualSize(TotalWidth, TotalHeight);

  // Calculate the position of each node in the display.
  for (auto const &StmtRange : Ranges) {
    auto const DepthIt = Depths.find(StmtRange.first);
    if (DepthIt == Depths.end()) {
      wxLogDebug("Couldn't get depth for sub-Stmt.");
      continue;
    }

    auto const Depth = DepthIt->second;
    auto const XStart = PageBorderH + (StmtRange.second.first * CharWidth);
    auto const XEnd = XStart + (StmtRange.second.second * CharWidth);
    auto const YStart = TotalHeight - PageBorderV - CharHeight
                        - (Depth * (CharHeight + NodeBorderV));

    Nodes.emplace_back(StmtRange.first,
                       ActiveFn->getStmtValue(StmtRange.first),
                       StmtRange.second.first,
                       StmtRange.second.second,
                       Depth,
                       XStart,
                       XEnd,
                       YStart,
                       YStart + CharHeight);
  }

  HoverNodeIt = Nodes.end();

  // Create a new DC because we've changed the virtual size.
  wxClientDC newdc(this);
  render(newdc);
}

void StateEvaluationTreePanel::clear()
{
  CurrentAccess.reset();
  CurrentProcess = nullptr;
  CurrentThread = nullptr;
  Statement.clear();
  Nodes.clear();

  SetVirtualSize(1, 1);

  wxClientDC dc(this);
  render(dc);
}

void StateEvaluationTreePanel::render(wxDC &dc)
{
  PrepareDC(dc);

  dc.Clear();
  if (Statement.empty()) {
    wxLogDebug("empty stmt");
    return;
  }

  auto const CharWidth = dc.GetCharWidth();
  auto const CharHeight = dc.GetCharHeight();

  wxCoord const PageBorderH = CharWidth * Settings.PageBorderHorizontal;
  wxCoord const PageBorderV = CharHeight * Settings.PageBorderVertical;

  dc.SetFont(CodeFont);
  dc.SetTextForeground(*wxBLACK);

  wxPen TreeLinePen{*wxBLACK};

  wxPen TreeBackPen{Settings.NodeBorder};
  wxBrush TreeBackBrush{Settings.NodeBackground};

  wxPen HighlightedBackPen{Settings.NodeHighlightedBorder};
  wxBrush HighlightedBackBrush{Settings.NodeHighlightedBackground};

  // Draw the sub-Stmt's node's backgrounds.
  for (auto const &Node : Nodes) {
    dc.SetPen(TreeBackPen);
    dc.SetBrush(TreeBackBrush);
    dc.DrawRectangle(Node.XStart, Node.YStart,
                     Node.XEnd - Node.XStart, Node.YEnd - Node.YStart);
  }

  // Draw the hovered node's background.
  if (HoverNodeIt != Nodes.end()) {
    auto const &Node = *HoverNodeIt;
    dc.SetPen(HighlightedBackPen);
    dc.SetBrush(HighlightedBackBrush);
    dc.DrawRectangle(Node.XStart, Node.YStart,
                     Node.XEnd - Node.XStart, Node.YEnd - Node.YStart);

    // Also highlight this node's area in the pretty-printed Stmt.
    dc.DrawRectangle(Node.XStart, PageBorderV,
                     Node.XEnd - Node.XStart, CharHeight);
  }

  // Draw the pretty-printed Stmt's string.
  dc.DrawText(Statement, PageBorderH, PageBorderV);

  // Draw the individual sub-Stmts' strings.
  for (auto const &Node : Nodes) {
    dc.SetPen(TreeLinePen);
    dc.DrawLine(Node.XStart, Node.YStart, Node.XEnd, Node.YStart);

    if (Node.Value) {
      auto ValText = Node.Value->getValueAsStringShort();
      if (ValText.size() > Node.RangeLength) {
        ValText.erase(Node.RangeLength);
      }

      auto const TextWidth = CharWidth * ValText.size();
      auto const NodeWidth = CharWidth * Node.RangeLength;
      auto const Offset = (NodeWidth - TextWidth) / 2;
      dc.DrawText(ValText, Node.XStart + Offset, Node.YStart);
    }
  }
}

void StateEvaluationTreePanel::OnPaint(wxPaintEvent &Ev)
{
  wxPaintDC dc(this);
  render(dc);
}

void StateEvaluationTreePanel::OnMouseMoved(wxMouseEvent &Ev)
{
  bool DisplayChanged = false;
  auto const Pos = CalcUnscrolledPosition(Ev.GetPosition());

  // TODO: Find if the Pos is over the pretty-printed Stmt.

  // Find if the Pos is over a node's rectangle.
  auto const NewHoverNodeIt = std::find_if(Nodes.begin(), Nodes.end(),
    [&Pos] (NodeInfo const &Node) -> bool {
      return Node.XStart <= Pos.x && Pos.x <= Node.XEnd
          && Node.YStart <= Pos.y && Pos.y <= Node.YEnd;
    });

  if (NewHoverNodeIt != HoverNodeIt) {
    HoverNodeIt = NewHoverNodeIt;
    DisplayChanged = true;
  }

  // Redraw the display.
  if (DisplayChanged) {
    wxClientDC dc(this);
    render(dc);
  }
}

void StateEvaluationTreePanel::OnMouseLeftWindow(wxMouseEvent &Ev)
{
  bool DisplayChanged = false;

  if (HoverNodeIt != Nodes.end()) {
    HoverNodeIt = Nodes.end();
    DisplayChanged = true;
  }

  // Redraw the display.
  if (DisplayChanged) {
    wxClientDC dc(this);
    render(dc);
  }
}
<commit_msg>Send a context notification to highlight a node's Stmt when the user hovers over the node.<commit_after>//===- tools/seec-trace-view/StateEvaluationTree.cpp ----------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#include "seec/Clang/MappedAST.hpp"
#include "seec/Clang/MappedFunctionState.hpp"
#include "seec/Clang/MappedThreadState.hpp"
#include "seec/Clang/MappedValue.hpp"

// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
  #include <wx/wx.h>
#endif
#include "seec/wxWidgets/CleanPreprocessor.h"

#include "clang/AST/Expr.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/RecursiveASTVisitor.h"

#include "llvm/Support/raw_ostream.h"

#include "NotifyContext.hpp"
#include "StateAccessToken.hpp"
#include "StateEvaluationTree.hpp"

#include <string>

BEGIN_EVENT_TABLE(StateEvaluationTreePanel, wxScrolled<wxPanel>)
  EVT_PAINT(StateEvaluationTreePanel::OnPaint)
  EVT_MOTION(StateEvaluationTreePanel::OnMouseMoved)
  EVT_LEAVE_WINDOW(StateEvaluationTreePanel::OnMouseLeftWindow)
END_EVENT_TABLE()

StateEvaluationTreePanel::DisplaySettings::DisplaySettings()
: PageBorderHorizontal(1.0),
  PageBorderVertical(1.0),
  NodeBorderVertical(0.5),
  CodeFontSize(12),
  NodeBackground(204, 204, 204),
  NodeBorder(102, 102, 102),
  NodeHighlightedBackground(102, 204, 204),
  NodeHighlightedBorder(51, 102, 102)
{}

StateEvaluationTreePanel::StateEvaluationTreePanel()
: Settings(),
  Notifier(nullptr),
  CurrentAccess(),
  CurrentProcess(nullptr),
  CurrentThread(nullptr),
  ActiveFn(nullptr),
  CodeFont(),
  Statement(),
  Nodes(),
  HoverNodeIt(Nodes.end())
{}

StateEvaluationTreePanel::StateEvaluationTreePanel(wxWindow *Parent,
                                                   ContextNotifier &TheNotifier,
                                                   wxWindowID ID,
                                                   wxPoint const &Position,
                                                   wxSize const &Size)
: Settings(),
  Notifier(nullptr),
  CurrentAccess(),
  CurrentProcess(nullptr),
  CurrentThread(nullptr),
  ActiveFn(nullptr),
  CodeFont(),
  Statement(),
  Nodes(),
  HoverNodeIt(Nodes.end())
{
  Create(Parent, TheNotifier, ID, Position, Size);
}

StateEvaluationTreePanel::~StateEvaluationTreePanel() = default;

bool StateEvaluationTreePanel::Create(wxWindow *Parent,
                                      ContextNotifier &WithNotifier,
                                      wxWindowID ID,
                                      wxPoint const &Position,
                                      wxSize const &Size)
{
  if (!wxScrolled<wxPanel>::Create(Parent, ID, Position, Size))
    return false;

  Notifier = &WithNotifier;

  SetBackgroundStyle(wxBG_STYLE_PAINT);
  CodeFont = wxFont{wxFontInfo(Settings.CodeFontSize)
                    .Family(wxFONTFAMILY_MODERN)
                    .AntiAliased(true)};
  SetScrollRate(10, 10);

  return true;
}

/// \brief Determine if a Stmt is suitable for evaluation tree display.
///
static bool isSuitableEvaluationRoot(clang::Stmt const * const S)
{
  return llvm::isa<clang::Expr>(S);
}

/// \brief Find the "top-level" Stmt suitable for evaluation tree display.
///
static
clang::Stmt const *getEvaluationRoot(clang::Stmt const *S,
                                     seec::seec_clang::MappedAST const &AST)
{
  if (!isSuitableEvaluationRoot(S))
    return nullptr;

  while (true) {
    auto const MaybeParent = AST.getParent(S);
    if (!MaybeParent.assigned<clang::Stmt const *>())
      break;

    auto const Parent = MaybeParent.get<clang::Stmt const *>();
    if (!Parent || !isSuitableEvaluationRoot(Parent))
      break;

    S = Parent;
  }

  return S;
}

/// \brief Records the range of each Stmt in a pretty-printed Stmt.
///
class SubRangeRecorder : public clang::PrinterHelper
{
  clang::PrintingPolicy &Policy;

  std::string Buffer;

  llvm::raw_string_ostream BufferOS;

  llvm::DenseMap<clang::Stmt *, std::pair<uint64_t, std::size_t>> Ranges;

public:
  SubRangeRecorder(clang::PrintingPolicy &WithPolicy)
  : Policy(WithPolicy),
    Buffer(),
    BufferOS(Buffer),
    Ranges()
  {}

  virtual bool handledStmt(clang::Stmt *E, llvm::raw_ostream &OS) {
    // Print the Stmt to determine the length of its printed text.
    Buffer.clear();
    E->printPretty(BufferOS, nullptr, Policy);
    BufferOS.flush();

    // Record the start and length of the Stmt's printed text.
    Ranges.insert(std::make_pair(E, std::make_pair(OS.tell(), Buffer.size())));

    return false;
  }

  decltype(Ranges) &getRanges() {
    return Ranges;
  }

  decltype(Ranges) const &getRanges() const {
    return Ranges;
  }
};

/// \brief Records the depth of each sub-node in a Stmt.
///
class DepthRecorder : public clang::RecursiveASTVisitor<DepthRecorder>
{
  unsigned CurrentDepth;

  unsigned MaxDepth;

  llvm::DenseMap<clang::Stmt const *, unsigned> Depths;

public:
  DepthRecorder()
  : CurrentDepth(0),
    MaxDepth(0),
    Depths()
  {}

  bool shouldUseDataRecursionFor(clang::Stmt *S) {
    return false;
  }

  bool TraverseStmt(clang::Stmt *S) {
    if (!S)
      return true;

    if (CurrentDepth > MaxDepth)
      MaxDepth = CurrentDepth;

    Depths[S] = CurrentDepth;

    ++CurrentDepth;

    clang::RecursiveASTVisitor<DepthRecorder>::TraverseStmt(S);

    --CurrentDepth;

    return true;
  }

  unsigned getMaxDepth() const {
    return MaxDepth;
  }

  decltype(Depths) &getDepths() {
    return Depths;
  }

  decltype(Depths) const &getDepths() const {
    return Depths;
  }
};

void StateEvaluationTreePanel::show(std::shared_ptr<StateAccessToken> Access,
                                    seec::cm::ProcessState const &Process,
                                    seec::cm::ThreadState const &Thread)
{
  CurrentAccess = std::move(Access);
  CurrentProcess = &Process;
  CurrentThread = &Thread;
  ActiveFn = nullptr;
  Statement.clear();
  Nodes.clear();
  HoverNodeIt = Nodes.end();

  wxClientDC dc(this);

  // Recalculate the data here.
  if (!CurrentThread) {
    render(dc);
    return;
  }

  auto &Stack = CurrentThread->getCallStack();
  if (Stack.empty()) {
    render(dc);
    return;
  }

  ActiveFn = &(Stack.back().get());
  auto const MappedAST = ActiveFn->getMappedAST();
  auto const ActiveStmt = ActiveFn->getActiveStmt();
  if (!ActiveStmt) {
    render(dc);
    return;
  }

  auto const TopStmt = getEvaluationRoot(ActiveStmt, *MappedAST);
  if (!TopStmt) {
    render(dc);
    return;
  }

  std::string PrettyPrintedStmt;
  llvm::raw_string_ostream StmtOS(PrettyPrintedStmt);

  // TODO: get these from the ASTContext.
  clang::LangOptions LangOpts;

  clang::PrintingPolicy Policy(LangOpts);
  Policy.Indentation = 0;
  Policy.Bool = true;
  Policy.ConstantArraySizeAsWritten = true;

  SubRangeRecorder PrinterHelper(Policy);

  TopStmt->printPretty(StmtOS, &PrinterHelper, Policy);

  StmtOS.flush();

  // Determine the "depth" of each sub-Stmt.
  DepthRecorder DepthRecord;
  DepthRecord.TraverseStmt(const_cast<clang::Stmt *>(TopStmt));
  auto const &Depths = DepthRecord.getDepths();
  auto const MaxDepth = DepthRecord.getMaxDepth();

  // Now save all of the calculated information for the render method.
  Statement = PrettyPrintedStmt;
  auto const &Ranges = PrinterHelper.getRanges();

  // Calculate the new size of the display.
  dc.SetFont(CodeFont);
  auto const StatementExtent = dc.GetTextExtent(Statement);
  auto const CharWidth = dc.GetCharWidth();
  auto const CharHeight = dc.GetCharHeight();

  wxCoord const PageBorderH = CharWidth * Settings.PageBorderHorizontal;
  wxCoord const PageBorderV = CharHeight * Settings.PageBorderVertical;
  wxCoord const NodeBorderV = CharHeight * Settings.NodeBorderVertical;

  auto const TotalWidth = StatementExtent.GetWidth() + (2 * PageBorderH);

  // Depth is zero-based, so there are (MaxDepth+1) lines for sub-nodes, plus
  // one line for the pretty-printed top-level node.
  auto const TotalHeight = ((MaxDepth + 2) * CharHeight)
                           + ((MaxDepth + 1) * NodeBorderV)
                           + (2 * PageBorderV);

  SetVirtualSize(TotalWidth, TotalHeight);

  // Calculate the position of each node in the display.
  for (auto const &StmtRange : Ranges) {
    auto const DepthIt = Depths.find(StmtRange.first);
    if (DepthIt == Depths.end()) {
      wxLogDebug("Couldn't get depth for sub-Stmt.");
      continue;
    }

    auto const Depth = DepthIt->second;
    auto const XStart = PageBorderH + (StmtRange.second.first * CharWidth);
    auto const XEnd = XStart + (StmtRange.second.second * CharWidth);
    auto const YStart = TotalHeight - PageBorderV - CharHeight
                        - (Depth * (CharHeight + NodeBorderV));

    Nodes.emplace_back(StmtRange.first,
                       ActiveFn->getStmtValue(StmtRange.first),
                       StmtRange.second.first,
                       StmtRange.second.second,
                       Depth,
                       XStart,
                       XEnd,
                       YStart,
                       YStart + CharHeight);
  }

  HoverNodeIt = Nodes.end();

  // Create a new DC because we've changed the virtual size.
  wxClientDC newdc(this);
  render(newdc);
}

void StateEvaluationTreePanel::clear()
{
  CurrentAccess.reset();
  CurrentProcess = nullptr;
  CurrentThread = nullptr;
  Statement.clear();
  Nodes.clear();

  SetVirtualSize(1, 1);

  wxClientDC dc(this);
  render(dc);
}

void StateEvaluationTreePanel::render(wxDC &dc)
{
  PrepareDC(dc);

  dc.Clear();
  if (Statement.empty()) {
    wxLogDebug("empty stmt");
    return;
  }

  auto const CharWidth = dc.GetCharWidth();
  auto const CharHeight = dc.GetCharHeight();

  wxCoord const PageBorderH = CharWidth * Settings.PageBorderHorizontal;
  wxCoord const PageBorderV = CharHeight * Settings.PageBorderVertical;

  dc.SetFont(CodeFont);
  dc.SetTextForeground(*wxBLACK);

  wxPen TreeLinePen{*wxBLACK};

  wxPen TreeBackPen{Settings.NodeBorder};
  wxBrush TreeBackBrush{Settings.NodeBackground};

  wxPen HighlightedBackPen{Settings.NodeHighlightedBorder};
  wxBrush HighlightedBackBrush{Settings.NodeHighlightedBackground};

  // Draw the sub-Stmt's node's backgrounds.
  for (auto const &Node : Nodes) {
    dc.SetPen(TreeBackPen);
    dc.SetBrush(TreeBackBrush);
    dc.DrawRectangle(Node.XStart, Node.YStart,
                     Node.XEnd - Node.XStart, Node.YEnd - Node.YStart);
  }

  // Draw the hovered node's background.
  if (HoverNodeIt != Nodes.end()) {
    auto const &Node = *HoverNodeIt;
    dc.SetPen(HighlightedBackPen);
    dc.SetBrush(HighlightedBackBrush);
    dc.DrawRectangle(Node.XStart, Node.YStart,
                     Node.XEnd - Node.XStart, Node.YEnd - Node.YStart);

    // Also highlight this node's area in the pretty-printed Stmt.
    dc.DrawRectangle(Node.XStart, PageBorderV,
                     Node.XEnd - Node.XStart, CharHeight);
  }

  // Draw the pretty-printed Stmt's string.
  dc.DrawText(Statement, PageBorderH, PageBorderV);

  // Draw the individual sub-Stmts' strings.
  for (auto const &Node : Nodes) {
    dc.SetPen(TreeLinePen);
    dc.DrawLine(Node.XStart, Node.YStart, Node.XEnd, Node.YStart);

    if (Node.Value) {
      auto ValText = Node.Value->getValueAsStringShort();
      if (ValText.size() > Node.RangeLength) {
        ValText.erase(Node.RangeLength);
      }

      auto const TextWidth = CharWidth * ValText.size();
      auto const NodeWidth = CharWidth * Node.RangeLength;
      auto const Offset = (NodeWidth - TextWidth) / 2;
      dc.DrawText(ValText, Node.XStart + Offset, Node.YStart);
    }
  }
}

void StateEvaluationTreePanel::OnPaint(wxPaintEvent &Ev)
{
  wxPaintDC dc(this);
  render(dc);
}

void StateEvaluationTreePanel::OnMouseMoved(wxMouseEvent &Ev)
{
  bool DisplayChanged = false;
  auto const Pos = CalcUnscrolledPosition(Ev.GetPosition());

  // TODO: Find if the Pos is over the pretty-printed Stmt.

  // Find if the Pos is over a node's rectangle.
  auto const NewHoverNodeIt = std::find_if(Nodes.begin(), Nodes.end(),
    [&Pos] (NodeInfo const &Node) -> bool {
      return Node.XStart <= Pos.x && Pos.x <= Node.XEnd
          && Node.YStart <= Pos.y && Pos.y <= Node.YEnd;
    });

  if (NewHoverNodeIt != HoverNodeIt) {
    HoverNodeIt = NewHoverNodeIt;
    DisplayChanged = true;

    if (Notifier) {
      auto const TheStmt = HoverNodeIt != Nodes.end() ? HoverNodeIt->Statement
                                                      : nullptr;
      Notifier->createNotify<ConEvHighlightStmt>(TheStmt);
    }
  }

  // Redraw the display.
  if (DisplayChanged) {
    wxClientDC dc(this);
    render(dc);
  }
}

void StateEvaluationTreePanel::OnMouseLeftWindow(wxMouseEvent &Ev)
{
  bool DisplayChanged = false;

  if (HoverNodeIt != Nodes.end()) {
    HoverNodeIt = Nodes.end();
    DisplayChanged = true;
  }

  // Redraw the display.
  if (DisplayChanged) {
    wxClientDC dc(this);
    render(dc);
  }
}
<|endoftext|>


<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

using namespace std;

#include "config.h"

#include "common/ConfUtils.h"
#include "common/common_init.h"
#include "auth/Crypto.h"
#include "auth/Auth.h"
#include "auth/KeyRing.h"

void usage()
{
  cout << " usage: [--create-keyring] [--gen-key] [--name=<name>] [--caps=<filename>] [--list] [--print-key] <filename>" << std::endl;
  exit(1);
}

int main(int argc, const char **argv)
{
  vector<const char*> args;
  argv_to_vec(argc, argv, args);
  env_to_vec(args);
  DEFINE_CONF_VARS(usage);
  common_init(args, "osdmaptool", false, false);

  const char *me = argv[0];

  const char *fn = 0;
  bool gen_key = false;
  const char *add_key = 0;
  bool list = false;
  bool print_key = false;
  bool create_keyring = false;
  const char *name = "";
  const char *caps_fn = NULL;
  const char *import_keyring = NULL;

  FOR_EACH_ARG(args) {
    if (CONF_ARG_EQ("gen-key", 'g')) {
      CONF_SAFE_SET_ARG_VAL(&gen_key, OPT_BOOL);
    } else if (CONF_ARG_EQ("add-key", 'a')) {
      CONF_SAFE_SET_ARG_VAL(&add_key, OPT_STR);
    } else if (CONF_ARG_EQ("name", 'n')) {
      CONF_SAFE_SET_ARG_VAL(&name, OPT_STR);
    } else if (CONF_ARG_EQ("list", 'l')) {
      CONF_SAFE_SET_ARG_VAL(&list, OPT_BOOL);
    } else if (CONF_ARG_EQ("caps", '\0')) {
      CONF_SAFE_SET_ARG_VAL(&caps_fn, OPT_STR);
    } else if (CONF_ARG_EQ("print-key", 'p')) {
      CONF_SAFE_SET_ARG_VAL(&print_key, OPT_BOOL);
    } else if (CONF_ARG_EQ("create-keyring", 'c')) {
      CONF_SAFE_SET_ARG_VAL(&create_keyring, OPT_BOOL);
    } else if (CONF_ARG_EQ("import-keyring", '\0')) {
      CONF_SAFE_SET_ARG_VAL(&import_keyring, OPT_STR);
    } else if (!fn) {
      fn = args[i];
    } else
      usage();
  }
  if (!fn) {
    cerr << me << ": must specify filename" << std::endl;
    usage();
  }
  if (!(gen_key ||
	add_key ||
	list ||
	caps_fn ||
	print_key ||
	create_keyring ||
	import_keyring)) {
    cerr << "no command specified" << std::endl;
    usage();
  }
  if (gen_key && add_key) {
    cerr << "can't both gen_key and add_key" << std::endl;
    usage();
  }
  if (caps_fn || add_key || gen_key || print_key) {
    if (!name || !(*name)) {
      cerr << "must specify entity name" << std::endl;
      usage();
    }
  }

  // keyring --------
  bool modified = false;
  KeyRing keyring;
  string s = name;
  EntityName ename;
  if (name[0] && !ename.from_str(s)) {
    cerr << "'" << s << "' is not a valid entity name" << std::endl;
    exit(1);
  }

  bufferlist bl;
  int r = 0;
  if (create_keyring) {
    cout << "creating " << fn << std::endl;
    modified = true;
  } else {
    r = bl.read_file(fn, true);
    if (r >= 0) {
      try {
	bufferlist::iterator iter = bl.begin();
	::decode(keyring, iter);
      } catch (buffer::error *err) {
	cerr << "error reading file " << fn << std::endl;
	exit(1);
      }
    } else {
      cerr << "can't open " << fn << ": " << strerror(-r) << std::endl;
      exit(1);
    }
  }

  // write commands
  if (import_keyring) {
    KeyRing other;
    bufferlist obl;
    int r = obl.read_file(import_keyring);
    if (r >= 0) {
      try {
	bufferlist::iterator iter = obl.begin();
	::decode(other, iter);
      } catch (buffer::error *err) {
	cerr << "error reading file " << import_keyring << std::endl;
	exit(1);
      }

      cout << "importing contents of " << import_keyring << " into " << fn << std::endl;
      //other.print(cout);
      keyring.import(other);
      modified = true;
    } else {
      cerr << "can't open " << import_keyring << ": " << strerror(-r) << std::endl;
      exit(1);
    }
  }
  if (gen_key) {
    EntityAuth eauth;
    eauth.key.create(CEPH_CRYPTO_AES);
    keyring.add(ename, eauth);
    modified = true;
  }
  if (add_key) {
    if (!name) {
      cerr << "must speicfy a name to add a key" << std::endl;
      exit(1);
    }
    EntityAuth eauth;
    string ekey(add_key);
    try {
      eauth.key.decode_base64(ekey);
    } catch (buffer::error *err) {
      cerr << "can't decode key '" << add_key << "'" << std::endl;
      exit(1);
    }
    keyring.add(ename, eauth);
    modified = true;
    cout << "added entity " << ename << " auth " << eauth << std::endl;
  }
  if (caps_fn) {
    ConfFile *cf = new ConfFile(caps_fn);
    if (!cf->parse()) {
      cerr << "could not parse caps file " << caps_fn << std::endl;
      exit(1);
    }
    map<string, bufferlist> caps;
    const char *key_names[] = { "mon", "osd", "mds", NULL };
    for (int i=0; key_names[i]; i++) {
      char *val;
      cf->read("global", key_names[i], &val, NULL);
      if (val) {
        bufferlist bl;
        ::encode(val, bl);
        string s(key_names[i]);
        caps[s] = bl;
        free(val);
      }
    }
    keyring.set_caps(ename, caps);
    modified = true;
  }

  // read commands
  if (list) {
    keyring.print(cout);
  }
  if (print_key) {
    CryptoKey key;
    if (keyring.get_secret(ename, key)) {
      string a;
      key.encode_base64(a);
      cout << a << std::endl;
    } else {
      cerr << "entity " << ename << " not found" << std::endl;
    }
  }

  // write result?
  if (modified) {
    bufferlist bl;
    ::encode(keyring, bl);
    r = bl.write_file(fn, 0600);
    if (r < 0) {
      cerr << "could not write " << fn << std::endl;
    }
    //cout << "wrote " << bl.length() << " bytes to " << fn << std::endl;
  }

  return 0;
}
<commit_msg>authtool: set generated key to specific uid if one is given<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

using namespace std;

#include "config.h"

#include "common/ConfUtils.h"
#include "common/common_init.h"
#include "auth/Crypto.h"
#include "auth/Auth.h"
#include "auth/KeyRing.h"

void usage()
{
  cout << " usage: [--create-keyring] [--gen-key --set-uid=x] [--name=<name>] [--caps=<filename>] [--list] [--print-key] <filename>" << std::endl;
  exit(1);
}

int main(int argc, const char **argv)
{
  vector<const char*> args;
  argv_to_vec(argc, argv, args);
  env_to_vec(args);
  DEFINE_CONF_VARS(usage);
  common_init(args, "osdmaptool", false, false);

  const char *me = argv[0];

  const char *fn = 0;
  bool gen_key = false;
  const char *add_key = 0;
  bool list = false;
  bool print_key = false;
  bool create_keyring = false;
  const char *name = "";
  const char *caps_fn = NULL;
  const char *import_keyring = NULL;
  __u64 auth_uid = 0;

  FOR_EACH_ARG(args) {
    if (CONF_ARG_EQ("gen-key", 'g')) {
      CONF_SAFE_SET_ARG_VAL(&gen_key, OPT_BOOL);
    } else if (CONF_ARG_EQ("add-key", 'a')) {
      CONF_SAFE_SET_ARG_VAL(&add_key, OPT_STR);
    } else if (CONF_ARG_EQ("name", 'n')) {
      CONF_SAFE_SET_ARG_VAL(&name, OPT_STR);
    } else if (CONF_ARG_EQ("list", 'l')) {
      CONF_SAFE_SET_ARG_VAL(&list, OPT_BOOL);
    } else if (CONF_ARG_EQ("caps", '\0')) {
      CONF_SAFE_SET_ARG_VAL(&caps_fn, OPT_STR);
    } else if (CONF_ARG_EQ("print-key", 'p')) {
      CONF_SAFE_SET_ARG_VAL(&print_key, OPT_BOOL);
    } else if (CONF_ARG_EQ("create-keyring", 'c')) {
      CONF_SAFE_SET_ARG_VAL(&create_keyring, OPT_BOOL);
    } else if (CONF_ARG_EQ("import-keyring", '\0')) {
      CONF_SAFE_SET_ARG_VAL(&import_keyring, OPT_STR);
    } else if (CONF_ARG_EQ("set-uid", 'u')) {
      CONF_SAFE_SET_ARG_VAL(&auth_uid, OPT_LONGLONG);
    } else if (!fn) {
      fn = args[i];
    } else
      usage();
  }
  if (!fn) {
    cerr << me << ": must specify filename" << std::endl;
    usage();
  }
  if (!(gen_key ||
	add_key ||
	list ||
	caps_fn ||
	print_key ||
	create_keyring ||
	import_keyring)) {
    cerr << "no command specified" << std::endl;
    usage();
  }
  if (gen_key && add_key) {
    cerr << "can't both gen_key and add_key" << std::endl;
    usage();
  }
  if (caps_fn || add_key || gen_key || print_key) {
    if (!name || !(*name)) {
      cerr << "must specify entity name" << std::endl;
      usage();
    }
  }

  // keyring --------
  bool modified = false;
  KeyRing keyring;
  string s = name;
  EntityName ename;
  if (name[0] && !ename.from_str(s)) {
    cerr << "'" << s << "' is not a valid entity name" << std::endl;
    exit(1);
  }

  bufferlist bl;
  int r = 0;
  if (create_keyring) {
    cout << "creating " << fn << std::endl;
    modified = true;
  } else {
    r = bl.read_file(fn, true);
    if (r >= 0) {
      try {
	bufferlist::iterator iter = bl.begin();
	::decode(keyring, iter);
      } catch (buffer::error *err) {
	cerr << "error reading file " << fn << std::endl;
	exit(1);
      }
    } else {
      cerr << "can't open " << fn << ": " << strerror(-r) << std::endl;
      exit(1);
    }
  }

  // write commands
  if (import_keyring) {
    KeyRing other;
    bufferlist obl;
    int r = obl.read_file(import_keyring);
    if (r >= 0) {
      try {
	bufferlist::iterator iter = obl.begin();
	::decode(other, iter);
      } catch (buffer::error *err) {
	cerr << "error reading file " << import_keyring << std::endl;
	exit(1);
      }

      cout << "importing contents of " << import_keyring << " into " << fn << std::endl;
      //other.print(cout);
      keyring.import(other);
      modified = true;
    } else {
      cerr << "can't open " << import_keyring << ": " << strerror(-r) << std::endl;
      exit(1);
    }
  }
  if (gen_key) {
    EntityAuth eauth;
    eauth.key.create(CEPH_CRYPTO_AES);
    if (auth_uid)
      eauth.auth_uid = (__u64)auth_uid;
    keyring.add(ename, eauth);
    modified = true;
  }
  if (add_key) {
    if (!name) {
      cerr << "must speicfy a name to add a key" << std::endl;
      exit(1);
    }
    EntityAuth eauth;
    string ekey(add_key);
    try {
      eauth.key.decode_base64(ekey);
    } catch (buffer::error *err) {
      cerr << "can't decode key '" << add_key << "'" << std::endl;
      exit(1);
    }
    keyring.add(ename, eauth);
    modified = true;
    cout << "added entity " << ename << " auth " << eauth << std::endl;
  }
  if (caps_fn) {
    ConfFile *cf = new ConfFile(caps_fn);
    if (!cf->parse()) {
      cerr << "could not parse caps file " << caps_fn << std::endl;
      exit(1);
    }
    map<string, bufferlist> caps;
    const char *key_names[] = { "mon", "osd", "mds", NULL };
    for (int i=0; key_names[i]; i++) {
      char *val;
      cf->read("global", key_names[i], &val, NULL);
      if (val) {
        bufferlist bl;
        ::encode(val, bl);
        string s(key_names[i]);
        caps[s] = bl;
        free(val);
      }
    }
    keyring.set_caps(ename, caps);
    modified = true;
  }

  // read commands
  if (list) {
    keyring.print(cout);
  }
  if (print_key) {
    CryptoKey key;
    if (keyring.get_secret(ename, key)) {
      string a;
      key.encode_base64(a);
      cout << a << std::endl;
    } else {
      cerr << "entity " << ename << " not found" << std::endl;
    }
  }

  // write result?
  if (modified) {
    bufferlist bl;
    ::encode(keyring, bl);
    r = bl.write_file(fn, 0600);
    if (r < 0) {
      cerr << "could not write " << fn << std::endl;
    }
    //cout << "wrote " << bl.length() << " bytes to " << fn << std::endl;
  }

  return 0;
}
<|endoftext|>


<commit_before>#include "testsettings.hpp"
#ifdef TEST_ENCRYPTED_FILE_MAPPING

#include <realm/util/encrypted_file_mapping.hpp>

#include "test.hpp"

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

// Test independence and thread-safety
// -----------------------------------
//
// All tests must be thread safe and independent of each other. This
// is required because it allows for both shuffling of the execution
// order and for parallelized testing.
//
// In particular, avoid using std::rand() since it is not guaranteed
// to be thread safe. Instead use the API offered in
// `test/util/random.hpp`.
//
// All files created in tests must use the TEST_PATH macro (or one of
// its friends) to obtain a suitable file system path. See
// `test/util/test_path.hpp`.
//
//
// Debugging and the ONLY() macro
// ------------------------------
//
// A simple way of disabling all tests except one called `Foo`, is to
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
// test suite. Note that you can also use filtering by setting the
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
// this.
//
// Another way to debug a particular test, is to copy that test into
// `experiments/testcase.cpp` and then run `sh build.sh
// check-testcase` (or one of its friends) from the command line.

#ifdef REALM_ENABLE_ENCRYPTION

using namespace realm::util;

namespace {
const uint8_t test_key[] = "1234567890123456789012345678901123456789012345678901234567890123";
}

TEST(EncryptedFile_CryptorBasic)
{
    TEST_PATH(path);

    AESCryptor cryptor(test_key);
    cryptor.set_file_size(16);
    const char data[4096] = "test data";
    char buffer[4096];

    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    cryptor.write(fd, 0, data, sizeof(data));
    cryptor.read(fd, 0, buffer, sizeof(buffer));
    CHECK(memcmp(buffer, data, strlen(data)) == 0);
    close(fd);
}

TEST(EncryptedFile_CryptorRepeatedWrites)
{
    TEST_PATH(path);
    AESCryptor cryptor(test_key);
    cryptor.set_file_size(16);

    const char data[4096] = "test data";
    char raw_buffer_1[8192] = {0}, raw_buffer_2[9192] = {0};
    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

    cryptor.write(fd, 0, data, sizeof(data));
    lseek(fd, 0, SEEK_SET);
    read(fd, raw_buffer_1, sizeof(raw_buffer_1));

    cryptor.write(fd, 0, data, sizeof(data));
    lseek(fd, 0, SEEK_SET);
    read(fd, raw_buffer_2, sizeof(raw_buffer_2));

    CHECK(memcmp(raw_buffer_1, raw_buffer_2, sizeof(raw_buffer_1)) != 0);

    close(fd);
}

TEST(EncryptedFile_SeparateCryptors)
{
    TEST_PATH(path);

    const char data[4096] = "test data";
    char buffer[4096];

    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    {
        AESCryptor cryptor(test_key);
        cryptor.set_file_size(16);
        cryptor.write(fd, 0, data, sizeof(data));
    }
    {
        AESCryptor cryptor(test_key);
        cryptor.set_file_size(16);
        cryptor.read(fd, 0, buffer, sizeof(buffer));
    }

    CHECK(memcmp(buffer, data, strlen(data)) == 0);
    close(fd);
}

TEST(EncryptedFile_InterruptedWrite)
{
    TEST_PATH(path);

    const char data[4096] = "test data";

    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    {
        AESCryptor cryptor(test_key);
        cryptor.set_file_size(16);
        cryptor.write(fd, 0, data, sizeof(data));
    }

    // Fake an interrupted write which updates the IV table but not the data
    char buffer[4096];
    pread(fd, buffer, 64, 0);
    memcpy(buffer + 32, buffer, 32);
    buffer[5]++; // first byte of "hmac1" field in iv table
    pwrite(fd, buffer, 64, 0);

    {
        AESCryptor cryptor(test_key);
        cryptor.set_file_size(16);
        cryptor.read(fd, 0, buffer, sizeof(buffer));
        CHECK(memcmp(buffer, data, strlen(data)) == 0);
    }

    close(fd);
}

TEST(EncryptedFile_LargePages)
{
    TEST_PATH(path);

    char data[4096*4];
    for (size_t i = 0; i < sizeof(data); ++i)
        data[i] = static_cast<char>(i);

    AESCryptor cryptor(test_key);
    cryptor.set_file_size(sizeof(data));
    char buffer[sizeof(data)];

    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    cryptor.write(fd, 0, data, sizeof(data));
    cryptor.read(fd, 0, buffer, sizeof(buffer));
    CHECK(memcmp(buffer, data, sizeof(data)) == 0);
    close(fd);
}

#endif // REALM_ENABLE_ENCRYPTION
#endif // TEST_ENCRYPTED_FILE_MAPPING
<commit_msg>Fixed a few warnings in `test_encrypted_file_mapping.cpp` by adding a few checks for the return values<commit_after>#include "testsettings.hpp"
#ifdef TEST_ENCRYPTED_FILE_MAPPING

#include <realm/util/encrypted_file_mapping.hpp>

#include "test.hpp"

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

// Test independence and thread-safety
// -----------------------------------
//
// All tests must be thread safe and independent of each other. This
// is required because it allows for both shuffling of the execution
// order and for parallelized testing.
//
// In particular, avoid using std::rand() since it is not guaranteed
// to be thread safe. Instead use the API offered in
// `test/util/random.hpp`.
//
// All files created in tests must use the TEST_PATH macro (or one of
// its friends) to obtain a suitable file system path. See
// `test/util/test_path.hpp`.
//
//
// Debugging and the ONLY() macro
// ------------------------------
//
// A simple way of disabling all tests except one called `Foo`, is to
// replace TEST(Foo) with ONLY(Foo) and then recompile and rerun the
// test suite. Note that you can also use filtering by setting the
// environment varible `UNITTEST_FILTER`. See `README.md` for more on
// this.
//
// Another way to debug a particular test, is to copy that test into
// `experiments/testcase.cpp` and then run `sh build.sh
// check-testcase` (or one of its friends) from the command line.

#ifdef REALM_ENABLE_ENCRYPTION

using namespace realm::util;

namespace {
const uint8_t test_key[] = "1234567890123456789012345678901123456789012345678901234567890123";
}

TEST(EncryptedFile_CryptorBasic)
{
    TEST_PATH(path);

    AESCryptor cryptor(test_key);
    cryptor.set_file_size(16);
    const char data[4096] = "test data";
    char buffer[4096];

    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    cryptor.write(fd, 0, data, sizeof(data));
    cryptor.read(fd, 0, buffer, sizeof(buffer));
    CHECK(memcmp(buffer, data, strlen(data)) == 0);
    close(fd);
}

TEST(EncryptedFile_CryptorRepeatedWrites)
{
    TEST_PATH(path);
    AESCryptor cryptor(test_key);
    cryptor.set_file_size(16);

    const char data[4096] = "test data";
    char raw_buffer_1[8192] = {0}, raw_buffer_2[9192] = {0};
    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);

    cryptor.write(fd, 0, data, sizeof(data));
    lseek(fd, 0, SEEK_SET);
    ssize_t actual_read_1 = read(fd, raw_buffer_1, sizeof(raw_buffer_1));
    CHECK_EQUAL(actual_read_1, sizeof(raw_buffer_1));

    cryptor.write(fd, 0, data, sizeof(data));
    lseek(fd, 0, SEEK_SET);
    ssize_t actual_read_2 = read(fd, raw_buffer_2, sizeof(raw_buffer_2));
    CHECK_EQUAL(actual_read_2, sizeof(raw_buffer_2));

    CHECK(memcmp(raw_buffer_1, raw_buffer_2, sizeof(raw_buffer_1)) != 0);

    close(fd);
}

TEST(EncryptedFile_SeparateCryptors)
{
    TEST_PATH(path);

    const char data[4096] = "test data";
    char buffer[4096];

    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    {
        AESCryptor cryptor(test_key);
        cryptor.set_file_size(16);
        cryptor.write(fd, 0, data, sizeof(data));
    }
    {
        AESCryptor cryptor(test_key);
        cryptor.set_file_size(16);
        cryptor.read(fd, 0, buffer, sizeof(buffer));
    }

    CHECK(memcmp(buffer, data, strlen(data)) == 0);
    close(fd);
}

TEST(EncryptedFile_InterruptedWrite)
{
    TEST_PATH(path);

    const char data[4096] = "test data";

    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    {
        AESCryptor cryptor(test_key);
        cryptor.set_file_size(16);
        cryptor.write(fd, 0, data, sizeof(data));
    }

    // Fake an interrupted write which updates the IV table but not the data
    char buffer[4096];
    ssize_t actual_pread = pread(fd, buffer, 64, 0);
    CHECK_EQUAL(actual_pread, 64);

    memcpy(buffer + 32, buffer, 32);
    buffer[5]++; // first byte of "hmac1" field in iv table
    ssize_t actual_pwrite = pwrite(fd, buffer, 64, 0);
    CHECK_EQUAL(actual_pwrite, 64);

    {
        AESCryptor cryptor(test_key);
        cryptor.set_file_size(16);
        cryptor.read(fd, 0, buffer, sizeof(buffer));
        CHECK(memcmp(buffer, data, strlen(data)) == 0);
    }

    close(fd);
}

TEST(EncryptedFile_LargePages)
{
    TEST_PATH(path);

    char data[4096*4];
    for (size_t i = 0; i < sizeof(data); ++i)
        data[i] = static_cast<char>(i);

    AESCryptor cryptor(test_key);
    cryptor.set_file_size(sizeof(data));
    char buffer[sizeof(data)];

    int fd = open(path.c_str(), O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    cryptor.write(fd, 0, data, sizeof(data));
    cryptor.read(fd, 0, buffer, sizeof(buffer));
    CHECK(memcmp(buffer, data, sizeof(data)) == 0);
    close(fd);
}

#endif // REALM_ENABLE_ENCRYPTION
#endif // TEST_ENCRYPTED_FILE_MAPPING
<|endoftext|>


<commit_before>/*************************************************************************************
    garlic-player: SMIL Player for Digital Signage
    Copyright (C) 2016 Nikolaos Saghiadinos <ns@smil-control.com>
    This file is part of the garlic-player source code

    This program is free software: you can redistribute it and/or  modify
    it under the terms of the GNU Affero General Public License, version 3,
    as published by the Free Software Foundation.

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

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*************************************************************************************/
#include <QQmlApplicationEngine>

#include "tools/logger.h"
#include "tools/resource_monitor.h"
#include "../player-common/cmdparser.h"
#include "../player-common/screen.h"
#include "../player-common/player_configuration.h"

#if defined  Q_OS_ANDROID
    #include "Java2Cpp.h"
    #include "android_manager.h"
#endif

#include <QtWebView>
#include "mainwindow.h"

void handleMessages(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    Logger& MyLogger = Logger::getInstance();
    MyLogger.dispatchMessages(type, context, msg);
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // Raspberry and POT needs this http://thebugfreeblog.blogspot.de/2018/01/pot-570-with-qt-5100-built-for-armv8.html

    QApplication app(argc, argv);

// must be checked and before app create directories
#if defined Q_OS_ANDROID
    AndroidManager *MyAndroidManager = new AndroidManager();
    if (!MyAndroidManager->checkPermissiones())
    {
        MyAndroidManager->sendCloseCorrect();
        app.quit();
        return -1;
    }
    MyAndroidManager->disableScreenSaver();
#endif

    MainConfiguration    *MyMainConfiguration   = new MainConfiguration(
                                                        new QSettings(QSettings::IniFormat, QSettings::UserScope, "SmilControl", "garlic-player"),
                                                        PlayerConfiguration::determineDefaultContentUrlName(),
                                                        PlayerConfiguration::determineDefaultContentUrl()

    );

    MyMainConfiguration->createDirectories();
    qInstallMessageHandler(handleMessages); // must set after createDiretories

    PlayerConfiguration  *MyPlayerConfiguration = new PlayerConfiguration(MyMainConfiguration);
    QtWebView::initialize();

    LibFacade  *MyLibFacade = new LibFacade();

#if defined Q_OS_ANDROID
    if (MyAndroidManager->hasLauncher())
    {
        MyAndroidManager->fetchDeviceInformation();
        // needed for launcher stuff to init or fetch
        QTime dieTime= QTime::currentTime().addSecs(5);
        while (MyAndroidManager->getUUIDFromLauncher().isEmpty() && QTime::currentTime() < dieTime)
        {
            QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
        }
        MyLibFacade->toggleLauncher(MyAndroidManager->hasLauncher());
        MyPlayerConfiguration->setHasLauncher(MyAndroidManager->hasLauncher());

        MyPlayerConfiguration->setUuidFromLauncher(MyAndroidManager->getUUIDFromLauncher());

        MyPlayerConfiguration->setSmilIndexUriFromLauncher(MyAndroidManager->getSmilIndexFromLauncher());
        MyPlayerConfiguration->setVersionFromLauncher(MyAndroidManager->getLauncherVersion());
    }

    setGlobalLibFaceForJava(MyLibFacade);
#endif

    // This must can only be be done after Launcher inits
    MyPlayerConfiguration->determineInitConfigValues();

    MyLibFacade->init(MyMainConfiguration);
    MyPlayerConfiguration->printVersionInformation();

    qmlRegisterType<LibFacade>("com.garlic.LibFacade", 1, 0, "LibFacade");

    TCmdParser MyParser(MyMainConfiguration);
    MyParser.addOptions();

    if (!MyParser.parse(MyLibFacade))
        return 1;
#ifdef QT_DEBUG
    QLoggingCategory::setFilterRules("*.debug=true\nqt.*=false");
#else
    QLoggingCategory::setFilterRules("*.debug=false");
#endif

    TScreen    MyScreen(Q_NULLPTR);
    MyScreen.selectCurrentScreen(MyParser.getScreenSelect());
    MyLibFacade->initWebserver(&app);

    MainWindow w(&MyScreen, MyLibFacade, MyPlayerConfiguration);

    QQmlEngine::setObjectOwnership(&w, QQmlEngine::CppOwnership);

    if (MyMainConfiguration->getIndexUri().isEmpty() && w.openConfigDialog() == QDialog::Rejected)
    {
        return 0;
    }
    w.init();

#if defined Q_OS_ANDROID || defined Q_OS_IOS
    w.showFullScreen();
#else

    w.show();

    QString val = MyParser.getWindowMode();
    if (val == "fullscreen")
        w.resizeAsNormalFullScreen();
    else if (val == "bigscreen")
        w.resizeAsBigFullScreen();
    else if (val == "windowed")
    {
        w.setMainWindowSize(MyParser.getWindowSize());
        w.resizeAsWindow();
    }
#endif

    return app.exec();
}
<commit_msg>test wait for philips firmware version<commit_after>/*************************************************************************************
    garlic-player: SMIL Player for Digital Signage
    Copyright (C) 2016 Nikolaos Saghiadinos <ns@smil-control.com>
    This file is part of the garlic-player source code

    This program is free software: you can redistribute it and/or  modify
    it under the terms of the GNU Affero General Public License, version 3,
    as published by the Free Software Foundation.

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

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*************************************************************************************/
#include <QQmlApplicationEngine>

#include "tools/logger.h"
#include "tools/resource_monitor.h"
#include "../player-common/cmdparser.h"
#include "../player-common/screen.h"
#include "../player-common/player_configuration.h"

#if defined  Q_OS_ANDROID
    #include "Java2Cpp.h"
    #include "android_manager.h"
#endif

#include <QtWebView>
#include "mainwindow.h"

void handleMessages(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
    Logger& MyLogger = Logger::getInstance();
    MyLogger.dispatchMessages(type, context, msg);
}

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); // Raspberry and POT needs this http://thebugfreeblog.blogspot.de/2018/01/pot-570-with-qt-5100-built-for-armv8.html

    QApplication app(argc, argv);

// must be checked and before app create directories
#if defined Q_OS_ANDROID
    AndroidManager *MyAndroidManager = new AndroidManager();
    if (!MyAndroidManager->checkPermissiones())
    {
        MyAndroidManager->sendCloseCorrect();
        app.quit();
        return -1;
    }
    MyAndroidManager->disableScreenSaver();
#endif

    MainConfiguration    *MyMainConfiguration   = new MainConfiguration(
                                                        new QSettings(QSettings::IniFormat, QSettings::UserScope, "SmilControl", "garlic-player"),
                                                        PlayerConfiguration::determineDefaultContentUrlName(),
                                                        PlayerConfiguration::determineDefaultContentUrl()

    );

    MyMainConfiguration->createDirectories();
    qInstallMessageHandler(handleMessages); // must set after createDiretories

    PlayerConfiguration  *MyPlayerConfiguration = new PlayerConfiguration(MyMainConfiguration);
    QtWebView::initialize();

    LibFacade  *MyLibFacade = new LibFacade();

#if defined Q_OS_ANDROID
    if (MyAndroidManager->hasLauncher())
    {
        MyAndroidManager->fetchDeviceInformation();
        // needed for launcher stuff to init or fetch
        QTime dieTime= QTime::currentTime().addSecs(5);
        while (MyAndroidManager->getUUIDFromLauncher().isEmpty() && QTime::currentTime() < dieTime)
        {
            QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
        }
        MyLibFacade->toggleLauncher(MyAndroidManager->hasLauncher());
        MyPlayerConfiguration->setHasLauncher(MyAndroidManager->hasLauncher());

        MyPlayerConfiguration->setUuidFromLauncher(MyAndroidManager->getUUIDFromLauncher());

        MyPlayerConfiguration->setSmilIndexUriFromLauncher(MyAndroidManager->getSmilIndexFromLauncher());
        while (MyAndroidManager->getLauncherVersion().isEmpty() && QTime::currentTime() < dieTime)
        {
            QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
        }
        MyPlayerConfiguration->setVersionFromLauncher(MyAndroidManager->getLauncherVersion());
    }

    setGlobalLibFaceForJava(MyLibFacade);
#endif

    // This must can only be be done after Launcher inits
    MyPlayerConfiguration->determineInitConfigValues();

    MyLibFacade->init(MyMainConfiguration);
    MyPlayerConfiguration->printVersionInformation();

    qmlRegisterType<LibFacade>("com.garlic.LibFacade", 1, 0, "LibFacade");

    TCmdParser MyParser(MyMainConfiguration);
    MyParser.addOptions();

    if (!MyParser.parse(MyLibFacade))
        return 1;
#ifdef QT_DEBUG
    QLoggingCategory::setFilterRules("*.debug=true\nqt.*=false");
#else
    QLoggingCategory::setFilterRules("*.debug=false");
#endif

    TScreen    MyScreen(Q_NULLPTR);
    MyScreen.selectCurrentScreen(MyParser.getScreenSelect());
    MyLibFacade->initWebserver(&app);

    MainWindow w(&MyScreen, MyLibFacade, MyPlayerConfiguration);

    QQmlEngine::setObjectOwnership(&w, QQmlEngine::CppOwnership);

    if (MyMainConfiguration->getIndexUri().isEmpty() && w.openConfigDialog() == QDialog::Rejected)
    {
        return 0;
    }
    w.init();

#if defined Q_OS_ANDROID || defined Q_OS_IOS
    w.showFullScreen();
#else

    w.show();

    QString val = MyParser.getWindowMode();
    if (val == "fullscreen")
        w.resizeAsNormalFullScreen();
    else if (val == "bigscreen")
        w.resizeAsBigFullScreen();
    else if (val == "windowed")
    {
        w.setMainWindowSize(MyParser.getWindowSize());
        w.resizeAsWindow();
    }
#endif

    return app.exec();
}
<|endoftext|>