<commit_before>///
/// @file  pod_vector.hpp
///
/// Copyright (C) 2022 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef POD_VECTOR_HPP
#define POD_VECTOR_HPP

#include "macros.hpp"

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <utility>

namespace primesieve {

/// pod_vector is a dynamically growing array.
/// It has the same API (though not complete) as std::vector but its
/// resize() method does not default initialize memory for built-in
/// integer types. It does however default initialize classes and
/// struct types if they have a constructor. It also prevents
/// bounds checks which is important for primesieve's performance, e.g.
/// the Fedora Linux distribution compiles with -D_GLIBCXX_ASSERTIONS
/// which enables std::vector bounds checks.
///
template <typename T>
class pod_vector
{
public:
  static_assert(std::is_trivially_destructible<T>::value,
                "pod_vector<T> only supports types with trivial destructors!");

  pod_vector() noexcept = default;

  pod_vector(std::size_t size)
  {
    resize(size);
  }

  ~pod_vector()
  {
    delete [] array_;
  }

  /// Free all memory, the pod_vector
  /// can be reused afterwards.
  void free() noexcept
  {
    delete [] array_;
    array_ = nullptr;
    end_ = nullptr;
    capacity_ = nullptr;
  }

  /// Reset the pod_vector, but do not free its
  /// memory. Same as std::vector.clear().
  void clear() noexcept
  {
    end_ = array_;
  }

  /// Copying is slow, we prevent it
  pod_vector(const pod_vector&) = delete;
  pod_vector& operator=(const pod_vector&) = delete;

  /// Move constructor
  pod_vector(pod_vector&& other) noexcept
  {
    this->swap(other);
  }

  /// Move assignment operator
  pod_vector& operator=(pod_vector&& other) noexcept
  {
    if (this != &other)
      this->swap(other);

    return *this;
  }

  void swap(pod_vector& other) noexcept
  {
    std::swap(array_, other.array_);
    std::swap(end_, other.end_);
    std::swap(capacity_, other.capacity_);
  }

  bool empty() const noexcept
  {
    return array_ == end_;
  }

  T& operator[] (std::size_t pos) noexcept
  {
    return array_[pos];
  }

  T& operator[] (std::size_t pos) const noexcept
  {
    return array_[pos];
  }

  T* data() noexcept
  {
    return array_;
  }

  T* data() const noexcept
  {
    return array_;
  }

  std::size_t size() const noexcept
  {
    assert(end_ >= array_);
    return (std::size_t)(end_ - array_);
  }

  std::size_t capacity() const noexcept
  {
    assert(capacity_ >= array_);
    return (std::size_t)(capacity_ - array_);
  }

  T* begin() noexcept
  {
    return array_;
  }

  T* begin() const noexcept
  {
    return array_;
  }

  T* end() noexcept
  {
    return end_;
  }

  T* end() const noexcept
  {
    return end_;
  }

  T& front() noexcept
  {
    return *array_;
  }

  T& front() const noexcept
  {
    return *array_;
  }

  T& back() noexcept
  {
    assert(end_ != nullptr);
    return *(end_ - 1);
  }

  T& back() const noexcept
  {
    assert(end_ != nullptr);
    return *(end_ - 1);
  }

  ALWAYS_INLINE void push_back(const T& value)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = value;
  }

  ALWAYS_INLINE void push_back(T&& value)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = value;
  }

  template <class... Args>
  ALWAYS_INLINE void emplace_back(Args&&... args)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = T(std::forward<Args>(args)...);
  }

  template <typename TT>
  ALWAYS_INLINE typename std::enable_if<std::is_trivial<TT>::value>::type
  default_initialize_range(TT*, TT*)
  { }

  template <typename TT>
  ALWAYS_INLINE typename std::enable_if<!std::is_trivial<TT>::value>::type
  default_initialize_range(TT* first, TT* last)
  {
    std::fill(first, last, TT());
  }

  void reserve(std::size_t n)
  {
    if (n > capacity())
    {
      // GCC & Clang's std::vector grow the capacity by at least
      // 2x for every call to resize() with n > capacity(). We
      // grow by at least 1.5x as we tend to accurately calculate
      // the amount of memory we need upfront.
      n = std::max(n, (std::size_t)(capacity() * 1.5));
      std::size_t old_size = size();

      // This default initializes memory of classes and
      // structs with constructors. But it does not default
      // initialize memory for POD types like int, long.
      T* new_array = new T[n];

      if (array_)
      {
        std::copy(array_, end_, new_array);
        delete [] array_;
      }

      array_ = new_array;
      end_ = array_ + old_size;
      capacity_ = array_ + n;
    }
  }

  /// Resize without default initializing memory.
  /// If the pod_vector is not empty the current content
  /// will be copied into the new array.
  ///
  void resize(std::size_t n)
  {
    if (n == size())
      return;
    else if (n <= capacity())
    {
      assert(capacity() > 0);

      // This will only be used for classes
      // and structs with constructors.
      if (n > size())
        default_initialize_range(end_, array_ + n);

      end_ = array_ + n;
    }
    else
    {
      // GCC & Clang's std::vector grow the capacity by at least
      // 2x for every call to resize() with n > capacity(). We
      // grow by at least 1.5x as we tend to accurately calculate
      // the amount of memory we need upfront.
      assert(n > capacity());
      n = std::max(n, (std::size_t)(capacity() * 1.5));

      // This default initializes memory of classes and
      // structs with constructors. But it does not default
      // initialize memory for POD types like int, long.
      T* new_array = new T[n];

      if (array_)
      {
        std::copy(array_, end_, new_array);
        delete [] array_;
      }

      array_ = new_array;
      end_ = array_ + n;
      capacity_ = end_;
    }
  }

  using value_type = T;

private:
  T* array_ = nullptr;
  T* end_ = nullptr;
  T* capacity_ = nullptr;
};

} // namespace

#endif
<commit_msg>Fix resize()<commit_after>///
/// @file  pod_vector.hpp
///
/// Copyright (C) 2022 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#ifndef POD_VECTOR_HPP
#define POD_VECTOR_HPP

#include "macros.hpp"

#include <algorithm>
#include <cassert>
#include <cstddef>
#include <type_traits>
#include <utility>

namespace primesieve {

/// pod_vector is a dynamically growing array.
/// It has the same API (though not complete) as std::vector but its
/// resize() method does not default initialize memory for built-in
/// integer types. It does however default initialize classes and
/// struct types if they have a constructor. It also prevents
/// bounds checks which is important for primesieve's performance, e.g.
/// the Fedora Linux distribution compiles with -D_GLIBCXX_ASSERTIONS
/// which enables std::vector bounds checks.
///
template <typename T>
class pod_vector
{
public:
  static_assert(std::is_trivially_destructible<T>::value,
                "pod_vector<T> only supports types with trivial destructors!");

  pod_vector() noexcept = default;

  pod_vector(std::size_t size)
  {
    resize(size);
  }

  ~pod_vector()
  {
    delete [] array_;
  }

  /// Free all memory, the pod_vector
  /// can be reused afterwards.
  void free() noexcept
  {
    delete [] array_;
    array_ = nullptr;
    end_ = nullptr;
    capacity_ = nullptr;
  }

  /// Reset the pod_vector, but do not free its
  /// memory. Same as std::vector.clear().
  void clear() noexcept
  {
    end_ = array_;
  }

  /// Copying is slow, we prevent it
  pod_vector(const pod_vector&) = delete;
  pod_vector& operator=(const pod_vector&) = delete;

  /// Move constructor
  pod_vector(pod_vector&& other) noexcept
  {
    std::swap(array_, other.array_);
    std::swap(end_, other.end_);
    std::swap(capacity_, other.capacity_);
  }

  /// Move assignment operator
  pod_vector& operator=(pod_vector&& other) noexcept
  {
    if (this != &other)
    {
      std::swap(array_, other.array_);
      std::swap(end_, other.end_);
      std::swap(capacity_, other.capacity_);
    }

    return *this;
  }

  bool empty() const noexcept
  {
    return array_ == end_;
  }

  T& operator[] (std::size_t pos) noexcept
  {
    return array_[pos];
  }

  T& operator[] (std::size_t pos) const noexcept
  {
    return array_[pos];
  }

  T* data() noexcept
  {
    return array_;
  }

  T* data() const noexcept
  {
    return array_;
  }

  std::size_t size() const noexcept
  {
    assert(end_ >= array_);
    return (std::size_t)(end_ - array_);
  }

  std::size_t capacity() const noexcept
  {
    assert(capacity_ >= array_);
    return (std::size_t)(capacity_ - array_);
  }

  T* begin() noexcept
  {
    return array_;
  }

  T* begin() const noexcept
  {
    return array_;
  }

  T* end() noexcept
  {
    return end_;
  }

  T* end() const noexcept
  {
    return end_;
  }

  T& front() noexcept
  {
    return *array_;
  }

  T& front() const noexcept
  {
    return *array_;
  }

  T& back() noexcept
  {
    assert(end_ != nullptr);
    return *(end_ - 1);
  }

  T& back() const noexcept
  {
    assert(end_ != nullptr);
    return *(end_ - 1);
  }

  ALWAYS_INLINE void push_back(const T& value)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = value;
  }

  ALWAYS_INLINE void push_back(T&& value)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = value;
  }

  template <class... Args>
  ALWAYS_INLINE void emplace_back(Args&&... args)
  {
    if_unlikely(end_ >= capacity_)
      reserve(std::max((std::size_t) 1, capacity() * 2));
    *end_++ = T(std::forward<Args>(args)...);
  }

  template <typename U>
  ALWAYS_INLINE typename std::enable_if<std::is_trivial<U>::value>::type
  default_initialize_range(U*, U*)
  { }

  template <typename U>
  ALWAYS_INLINE typename std::enable_if<!std::is_trivial<U>::value>::type
  default_initialize_range(U* first, U* last)
  {
    std::fill(first, last, U());
  }

  template <typename U>
  ALWAYS_INLINE typename std::enable_if<std::is_trivial<U>::value, std::size_t>::type
  get_new_capacity(std::size_t size)
  {
    // GCC & Clang's std::vector grow the capacity by at least
    // 2x for every call to resize() with n > capacity(). We
    // grow by at least 1.5x as we tend to accurately calculate
    // the amount of memory we need upfront.
    assert(size > 0);
    std::size_t new_capacity = (std::size_t)(capacity() * 1.5);
    constexpr std::size_t min_alignment = (sizeof(long) * 2) / sizeof(U);
    return std::max({min_alignment, size, new_capacity});
  }

  template <typename U>
  ALWAYS_INLINE typename std::enable_if<!std::is_trivial<U>::value, std::size_t>::type
  get_new_capacity(std::size_t size)
  {
    // GCC & Clang's std::vector grow the capacity by at least
    // 2x for every call to resize() with n > capacity(). We
    // grow by at least 1.5x as we tend to accurately calculate
    // the amount of memory we need upfront.
    assert(size > 0);
    std::size_t new_capacity = (std::size_t)(capacity() * 1.5);
    return std::max(size, new_capacity);
  }

  void reserve(std::size_t n)
  {
    if (n > capacity())
    {
      std::size_t old_size = size();
      std::size_t new_capacity = get_new_capacity<T>(n);
      assert(new_capacity >= n);
      assert(new_capacity > old_size);

      // This default initializes memory of classes and
      // structs with constructors. But it does not default
      // initialize memory for POD types like int, long.
      T* new_array = new T[new_capacity];

      if (array_)
      {
        std::copy(array_, end_, new_array);
        delete [] array_;
      }

      array_ = new_array;
      end_ = array_ + old_size;
      capacity_ = array_ + new_capacity;
    }
  }

  /// Resize without default initializing memory.
  /// If the pod_vector is not empty the current content
  /// will be copied into the new array.
  ///
  void resize(std::size_t n)
  {
    if (n == size())
      return;
    else if (n <= capacity())
    {
      assert(capacity() > 0);

      // This will only be used for classes
      // and structs with constructors.
      if (n > size())
        default_initialize_range(end_, array_ + n);

      end_ = array_ + n;
    }
    else
    {
      std::size_t new_capacity = get_new_capacity<T>(n);
      assert(new_capacity >= n);
      assert(new_capacity > size());

      // This default initializes memory of classes and
      // structs with constructors. But it does not default
      // initialize memory for POD types like int, long.
      T* new_array = new T[new_capacity];

      if (array_)
      {
        std::copy(array_, end_, new_array);
        delete [] array_;
      }

      array_ = new_array;
      end_ = array_ + n;
      capacity_ = array_ + new_capacity;
    }
  }

  using value_type = T;

private:
  T* array_ = nullptr;
  T* end_ = nullptr;
  T* capacity_ = nullptr;
};

} // namespace

#endif
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2008 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "contextregistryinfo.h"
#include "infobackend.h"
#include "sconnect.h"
#include <QMutex>
#include <QMutexLocker>
#include <QCoreApplication>

/*!
    \class ContextRegistryInfo

    \brief A class to introspect the registry contents.

    This is a singelton class used to obtain information about the keys (properties)
    in the registry database. The information can be provided either from xml files
    or from a cdb database. It's possible to list all the keys in the registry and
    also list all keys belonging to a one particular provider.
*/

ContextRegistryInfo* ContextRegistryInfo::registryInstance = NULL;

/* Public */

/// Returns the singleton instance of the ContextRegistryInfo. The object
/// is constructed automaticall on first access.
/// \param backendName the optional name of the backend to use (force).

ContextRegistryInfo* ContextRegistryInfo::instance(const QString &backendName)
{
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    if (! registryInstance) {
        InfoBackend::instance(backendName);
        registryInstance = new ContextRegistryInfo;

        InfoBackend* infoBackend = InfoBackend::instance();

        sconnect(infoBackend, SIGNAL(keysChanged(QStringList)),
                 registryInstance, SLOT(onKeysChanged(QStringList)));

        sconnect(infoBackend, SIGNAL(keysAdded(QStringList)),
                 registryInstance, SLOT(onKeysAdded(QStringList)));

        sconnect(infoBackend, SIGNAL(keysRemoved(QStringList)),
                 registryInstance, SLOT(onKeysRemoved(QStringList)));

        // Move the backend to the main thread
        registryInstance->moveToThread(QCoreApplication::instance()->thread());
    }

    return registryInstance;
}

/// Returns the list of all the keys currently availible in the registry.
QStringList ContextRegistryInfo::listKeys() const
{
    return InfoBackend::instance()->listKeys();
}

/// Returns the list of all the keys associated with the given provider.
QStringList ContextRegistryInfo::listKeys(QString providerName) const
{
    // TBD: obsolete this?
    QStringList keys = InfoBackend::instance()->listKeysForPlugin("contextkit-dbus");
    QStringList toReturn;
    foreach (const QString& key, keys) {
        QString constructionString = InfoBackend::instance()->constructionStringForKey(key);
        if (constructionString.split(":").last() == providerName) {
            toReturn << key;
        }
    }
    return toReturn;
}

/// Returns the list of all the keys associated with the given plugin
QStringList ContextRegistryInfo::listKeysForPlugin(QString plugin) const
{
    return InfoBackend::instance()->listKeysForPlugin(plugin);
}

/// Returns the list of all unique providers in the registry.
/// The lists consist of strings with dbus names of the providers.
QStringList ContextRegistryInfo::listProviders() const
{
    // TBD: obsolete this?
    QStringList keys = InfoBackend::instance()->listKeysForPlugin("contextkit-dbus");
    QSet<QString> foundProviders;
    foreach (const QString& key, keys) {
        QString constructionString = InfoBackend::instance()->constructionStringForKey(key);
        foundProviders.insert(constructionString.split(":").last());
    }
    QStringList toReturn(foundProviders.toList());
    return toReturn;
}

/// Returns the list of all unique plugins in the registry.
QStringList ContextRegistryInfo::listPlugins() const
{
    return InfoBackend::instance()->listPlugins();
}

/// Returns the name of the currently used registry backend. Ie. "cdb" or "xml".
QString ContextRegistryInfo::backendName() const
{
    return InfoBackend::instance()->name();
}

/* Slots */

/// This is connected to the \a onKeysChanged of the actual info backend instance.
void ContextRegistryInfo::onKeysChanged(const QStringList& currentKeys)
{
    emit(keysChanged(currentKeys));
}

/// This is connected to the \a onKeysAdded of the actual info backend instance.
void ContextRegistryInfo::onKeysAdded(const QStringList& newKeys)
{
    emit(keysAdded(newKeys));
}

/// This is connected to the \a onKeysRemoved of the actual info backend instance.
void ContextRegistryInfo::onKeysRemoved(const QStringList& removedKeys)
{
    emit(keysRemoved(removedKeys));
}

<commit_msg>Deprecating ContextRegistryInfo::listKeysForPlugin()<commit_after>/*
 * Copyright (C) 2008 Nokia Corporation.
 *
 * Contact: Marius Vollmer <marius.vollmer@nokia.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include "contextregistryinfo.h"
#include "infobackend.h"
#include "sconnect.h"
#include "logging.h"
#include "loggingfeatures.h"
#include "contextproviderinfo.h"
#include <QMutex>
#include <QMutexLocker>
#include <QCoreApplication>

/*!
    \class ContextRegistryInfo

    \brief A class to introspect the registry contents.

    This is a singelton class used to obtain information about the keys (properties)
    in the registry database. The information can be provided either from xml files
    or from a cdb database. It's possible to list all the keys in the registry and
    also list all keys belonging to a one particular provider.
*/

ContextRegistryInfo* ContextRegistryInfo::registryInstance = NULL;

/* Public */

/// Returns the singleton instance of the ContextRegistryInfo. The object
/// is constructed automaticall on first access.
/// \param backendName the optional name of the backend to use (force).

ContextRegistryInfo* ContextRegistryInfo::instance(const QString &backendName)
{
    static QMutex mutex;
    QMutexLocker locker(&mutex);
    if (! registryInstance) {
        InfoBackend::instance(backendName);
        registryInstance = new ContextRegistryInfo;

        InfoBackend* infoBackend = InfoBackend::instance();

        sconnect(infoBackend, SIGNAL(keysChanged(QStringList)),
                 registryInstance, SLOT(onKeysChanged(QStringList)));

        sconnect(infoBackend, SIGNAL(keysAdded(QStringList)),
                 registryInstance, SLOT(onKeysAdded(QStringList)));

        sconnect(infoBackend, SIGNAL(keysRemoved(QStringList)),
                 registryInstance, SLOT(onKeysRemoved(QStringList)));

        // Move the backend to the main thread
        registryInstance->moveToThread(QCoreApplication::instance()->thread());
    }

    return registryInstance;
}

/// Returns the list of all the keys currently availible in the registry.
QStringList ContextRegistryInfo::listKeys() const
{
    return InfoBackend::instance()->listKeys();
}

/// Returns the list of all the keys associated with the given provider.
QStringList ContextRegistryInfo::listKeys(QString providerName) const
{
    // TBD: obsolete this?
    QStringList keys = InfoBackend::instance()->listKeysForPlugin("contextkit-dbus");
    QStringList toReturn;
    foreach (const QString& key, keys) {
        QString constructionString = InfoBackend::instance()->constructionStringForKey(key);
        if (constructionString.split(":").last() == providerName) {
            toReturn << key;
        }
    }
    return toReturn;
}

/// Returns the list of all the keys associated with the given plugin
QStringList ContextRegistryInfo::listKeysForPlugin(QString plugin) const
{
    contextWarning() << F_DEPRECATION << "ContextRegistryInfo::listKeysForPlugin() is deprecated.";

    QStringList keys;

    foreach (QString key, listKeys()) {
        QList <ContextProviderInfo> providers = InfoBackend::instance()->listProviders(key);
        foreach (ContextProviderInfo info, providers) {
            if (info.plugin == plugin) {
                keys << key;
                break;
            }
        }

    }

    return keys;
}

/// Returns the list of all unique providers in the registry.
/// The lists consist of strings with dbus names of the providers.
QStringList ContextRegistryInfo::listProviders() const
{
    // TBD: obsolete this?
    QStringList keys = InfoBackend::instance()->listKeysForPlugin("contextkit-dbus");
    QSet<QString> foundProviders;
    foreach (const QString& key, keys) {
        QString constructionString = InfoBackend::instance()->constructionStringForKey(key);
        foundProviders.insert(constructionString.split(":").last());
    }
    QStringList toReturn(foundProviders.toList());
    return toReturn;
}

/// Returns the list of all unique plugins in the registry.
QStringList ContextRegistryInfo::listPlugins() const
{
    return InfoBackend::instance()->listPlugins();
}

/// Returns the name of the currently used registry backend. Ie. "cdb" or "xml".
QString ContextRegistryInfo::backendName() const
{
    return InfoBackend::instance()->name();
}

/* Slots */

/// This is connected to the \a onKeysChanged of the actual info backend instance.
void ContextRegistryInfo::onKeysChanged(const QStringList& currentKeys)
{
    emit(keysChanged(currentKeys));
}

/// This is connected to the \a onKeysAdded of the actual info backend instance.
void ContextRegistryInfo::onKeysAdded(const QStringList& newKeys)
{
    emit(keysAdded(newKeys));
}

/// This is connected to the \a onKeysRemoved of the actual info backend instance.
void ContextRegistryInfo::onKeysRemoved(const QStringList& removedKeys)
{
    emit(keysRemoved(removedKeys));
}

<|endoftext|>


<commit_before>/*
 *   Copyright (C) 2014-2016 Pelagicore AB
 *   All rights reserved.
 */
#include "dbusgateway.h"

DBusGateway::DBusGateway(ProxyType type
                       , const std::string &gatewayDir
                       , const std::string &name)
    : Gateway(ID)
    , m_type(type)
{
    m_state = GatewayState::CREATED;
    m_socket = gatewayDir
             + (m_type == SessionProxy ? "/sess_" : "/sys_")
             + name
             + ".sock";

    m_sessionBusConfig = json_array();
    m_systemBusConfig = json_array();
}

DBusGateway::~DBusGateway()
{
    if (m_state == GatewayState::ACTIVATED) {
        teardown();
    }
}

static constexpr const char *SESSION_CONFIG = "dbus-gateway-config-session";
static constexpr const char *SYSTEM_CONFIG = "dbus-gateway-config-system";

ReturnCode DBusGateway::readConfigElement(const JSonElement &element)
{
    JSonElement sessionConfig = element[SESSION_CONFIG];
    if (sessionConfig.isValid() && sessionConfig.isArray()) {
        for(unsigned int i = 0; i < sessionConfig.elementCount(); i++) {
            JSonElement child = sessionConfig.arrayElementAt(i);
            json_array_append(m_sessionBusConfig, (json_t*)child.root());
        }
    }

    JSonElement systemConfig = element[SYSTEM_CONFIG];
    if (systemConfig.isValid() && systemConfig.isArray()) {
        for(unsigned int i = 0; i < systemConfig.elementCount(); i++) {
            JSonElement child = systemConfig.arrayElementAt(i);
            json_array_append(m_systemBusConfig, (json_t*)child.root());
        }
    }

    m_state = GatewayState::CONFIGURED;
    return ReturnCode::SUCCESS;
}

bool DBusGateway::activateGateway()
{
    // set DBUS_{SESSION,SYSTEM}_BUS_ADDRESS env variable
    std::string variable = std::string("DBUS_")
                         + (m_type == SessionProxy ? "SESSION" : "SYSTEM")
                         + std::string("_BUS_ADDRESS");
    std::string value = "unix:path=/gateways/" + socketName();
    setEnvironmentVariable(variable, value);

    std::vector<std::string> commandVec = { "dbus-proxy"
                                          , m_socket
                                          , m_type == SessionProxy ? "session" : "system" };

    // Give the dbus-proxy access to the real dbus bus address.
    std::vector<std::string> envVec;
    if (char *envValue = getenv(variable.c_str())) {
        envVec.push_back(variable + "=" + envValue);
    } else if (m_type == SessionProxy) {
        log_error() << "Using DBus gateway in session mode"
                    << " and no " + variable + " set in host environment, dbus-proxy won't work";
        return false;
    } else {
        log_warn() << "Using DBus gateway in system mode"
                   << " and no " + variable + " set in host environment, this could be a problem";
    }

    if (!startDBusProxy(commandVec, envVec)) {
        return false;
    }

    // Write configuration
    json_t *jsonConfig = json_object();
    json_object_set(jsonConfig, SESSION_CONFIG, m_sessionBusConfig);
    json_object_set(jsonConfig, SYSTEM_CONFIG, m_systemBusConfig);
    std::string config = std::string(json_dumps(jsonConfig, JSON_COMPACT));

    return testDBusConnection(config);
}

bool DBusGateway::testDBusConnection(const std::string &config)
{
    log_debug() << "Sending config " << config;
    unsigned int count = sizeof(char) * config.length();
    ssize_t written = write(m_infp, config.c_str(), count);

    // writing didn't work at all
    if (written == -1) {
        log_error() << "Failed to write to STDIN of dbus-proxy: " << strerror(errno);
        return false;
    } else if (written == (ssize_t)count) {
        // writing has written exact amout of bytes
        close(m_infp);
        m_infp = INVALID_FD;
        // dbus-proxy might take some time to create the bus socket
        if (isSocketCreated()) {
            log_debug() << "Found D-Bus socket: " << m_socket;
            return true;
        } else {
            log_error() << "Did not find any D-Bus socket: " << m_socket;
            return false;
        }
    } else {
        // something went wrong during the write
        log_error() << "Failed to write to STDIN of dbus-proxy!";
        return false;
    }
}

bool DBusGateway::startDBusProxy(const std::vector<std::string> &commandVec, const std::vector<std::string> &envVec)
{
    // Spawn dbus-proxy with access to its stdin.
    try {
        Glib::spawn_async_with_pipes( "."
                                    , commandVec
                                    , envVec
                                    , Glib::SPAWN_STDOUT_TO_DEV_NULL // Redirect stdout
                                        | Glib::SPAWN_STDERR_TO_DEV_NULL // Redirect stderr
                                        | Glib::SPAWN_SEARCH_PATH // Search $PATH
                                        | Glib::SPAWN_DO_NOT_REAP_CHILD // Lets us do waitpid
                                    , sigc::slot<void>() // child setup
                                    , &m_pid
                                    , &m_infp // stdin
                                    );
    } catch (const Glib::Error &ex) {
        log_error() << "Failed to launch dbus-proxy";
        return false;
    }

    log_debug() << "Started dbus-proxy: " << m_pid;
    m_state = GatewayState::ACTIVATED;

    return true;
}

bool DBusGateway::isSocketCreated() const
{
    int maxCount = 1000;
    int count = 0;
    do {
        if (count >= maxCount) {
            return false;
        }
        count++;
        usleep(1000 * 10);
    } while (access(m_socket.c_str(), F_OK) == -1);
    return true;
}

bool DBusGateway::teardownGateway()
{
    bool success = true;

    if (m_pid != INVALID_PID) {
        log_debug() << "Killing dbus-proxy with pid " << m_pid;
        /*
         * For some reason, it does not work using SIGTERM, as that causes
         * the system to hang on waitpid - even if Glib::SPAWN_DO_NOT_REAP_CHILD
         * is set. It seems dbus-proxy does not react to SIGTERM.
         */
        kill(m_pid, SIGTERM);
        waitpid(m_pid, nullptr, 0); // Wait until it exits
        Glib::spawn_close_pid(m_pid);
    } else {
        log_debug() << "dbus-proxy pid not set or already dead: " << m_pid;
    }

    if (access(m_socket.c_str(), F_OK) != -1) {
        if (unlink(m_socket.c_str()) == -1) {
            log_error() << "Could not remove " << m_socket << ": " << strerror(errno);
            success = false;
        }
    } else {
        log_debug() << "Socket not accessible, has it been removed already?";
    }

    return success;
}

std::string DBusGateway::socketName()
{
    // Return the filename after stripping directory info
    std::string socket(m_socket.c_str());
    return socket.substr(socket.rfind('/') + 1);
}

<commit_msg>dbusgateway.cpp: use SIGKILL, some configs fail with SIGTERM<commit_after>/*
 *   Copyright (C) 2014-2016 Pelagicore AB
 *   All rights reserved.
 */
#include "dbusgateway.h"

DBusGateway::DBusGateway(ProxyType type
                       , const std::string &gatewayDir
                       , const std::string &name)
    : Gateway(ID)
    , m_type(type)
{
    m_state = GatewayState::CREATED;
    m_socket = gatewayDir
             + (m_type == SessionProxy ? "/sess_" : "/sys_")
             + name
             + ".sock";

    m_sessionBusConfig = json_array();
    m_systemBusConfig = json_array();
}

DBusGateway::~DBusGateway()
{
    if (m_state == GatewayState::ACTIVATED) {
        teardown();
    }
}

static constexpr const char *SESSION_CONFIG = "dbus-gateway-config-session";
static constexpr const char *SYSTEM_CONFIG = "dbus-gateway-config-system";

ReturnCode DBusGateway::readConfigElement(const JSonElement &element)
{
    JSonElement sessionConfig = element[SESSION_CONFIG];
    if (sessionConfig.isValid() && sessionConfig.isArray()) {
        for(unsigned int i = 0; i < sessionConfig.elementCount(); i++) {
            JSonElement child = sessionConfig.arrayElementAt(i);
            json_array_append(m_sessionBusConfig, (json_t*)child.root());
        }
    }

    JSonElement systemConfig = element[SYSTEM_CONFIG];
    if (systemConfig.isValid() && systemConfig.isArray()) {
        for(unsigned int i = 0; i < systemConfig.elementCount(); i++) {
            JSonElement child = systemConfig.arrayElementAt(i);
            json_array_append(m_systemBusConfig, (json_t*)child.root());
        }
    }

    m_state = GatewayState::CONFIGURED;
    return ReturnCode::SUCCESS;
}

bool DBusGateway::activateGateway()
{
    // set DBUS_{SESSION,SYSTEM}_BUS_ADDRESS env variable
    std::string variable = std::string("DBUS_")
                         + (m_type == SessionProxy ? "SESSION" : "SYSTEM")
                         + std::string("_BUS_ADDRESS");
    std::string value = "unix:path=/gateways/" + socketName();
    setEnvironmentVariable(variable, value);

    std::vector<std::string> commandVec = { "dbus-proxy"
                                          , m_socket
                                          , m_type == SessionProxy ? "session" : "system" };

    // Give the dbus-proxy access to the real dbus bus address.
    std::vector<std::string> envVec;
    if (char *envValue = getenv(variable.c_str())) {
        envVec.push_back(variable + "=" + envValue);
    } else if (m_type == SessionProxy) {
        log_error() << "Using DBus gateway in session mode"
                    << " and no " + variable + " set in host environment, dbus-proxy won't work";
        return false;
    } else {
        log_warn() << "Using DBus gateway in system mode"
                   << " and no " + variable + " set in host environment, this could be a problem";
    }

    if (!startDBusProxy(commandVec, envVec)) {
        return false;
    }

    // Write configuration
    json_t *jsonConfig = json_object();
    json_object_set(jsonConfig, SESSION_CONFIG, m_sessionBusConfig);
    json_object_set(jsonConfig, SYSTEM_CONFIG, m_systemBusConfig);
    std::string config = std::string(json_dumps(jsonConfig, JSON_COMPACT));

    return testDBusConnection(config);
}

bool DBusGateway::testDBusConnection(const std::string &config)
{
    log_debug() << "Sending config " << config;
    unsigned int count = sizeof(char) * config.length();
    ssize_t written = write(m_infp, config.c_str(), count);

    // writing didn't work at all
    if (written == -1) {
        log_error() << "Failed to write to STDIN of dbus-proxy: " << strerror(errno);
        return false;
    } else if (written == (ssize_t)count) {
        // writing has written exact amout of bytes
        close(m_infp);
        m_infp = INVALID_FD;
        // dbus-proxy might take some time to create the bus socket
        if (isSocketCreated()) {
            log_debug() << "Found D-Bus socket: " << m_socket;
            return true;
        } else {
            log_error() << "Did not find any D-Bus socket: " << m_socket;
            return false;
        }
    } else {
        // something went wrong during the write
        log_error() << "Failed to write to STDIN of dbus-proxy!";
        return false;
    }
}

bool DBusGateway::startDBusProxy(const std::vector<std::string> &commandVec, const std::vector<std::string> &envVec)
{
    // Spawn dbus-proxy with access to its stdin.
    try {
        Glib::spawn_async_with_pipes( "."
                                    , commandVec
                                    , envVec
                                    , Glib::SPAWN_STDOUT_TO_DEV_NULL // Redirect stdout
                                        | Glib::SPAWN_STDERR_TO_DEV_NULL // Redirect stderr
                                        | Glib::SPAWN_SEARCH_PATH // Search $PATH
                                        | Glib::SPAWN_DO_NOT_REAP_CHILD // Lets us do waitpid
                                    , sigc::slot<void>() // child setup
                                    , &m_pid
                                    , &m_infp // stdin
                                    );
    } catch (const Glib::Error &ex) {
        log_error() << "Failed to launch dbus-proxy";
        return false;
    }

    log_debug() << "Started dbus-proxy: " << m_pid;
    m_state = GatewayState::ACTIVATED;

    return true;
}

bool DBusGateway::isSocketCreated() const
{
    int maxCount = 1000;
    int count = 0;
    do {
        if (count >= maxCount) {
            return false;
        }
        count++;
        usleep(1000 * 10);
    } while (access(m_socket.c_str(), F_OK) == -1);
    return true;
}

bool DBusGateway::teardownGateway()
{
    bool success = true;

    if (m_pid != INVALID_PID) {
        log_debug() << "Killing dbus-proxy with pid " << m_pid;

        kill(m_pid, SIGKILL); // In some configurations, hangs if using SIGTERM instead
        waitpid(m_pid, nullptr, 0); // Wait until it exits
        Glib::spawn_close_pid(m_pid);
    } else {
        log_debug() << "dbus-proxy pid not set or already dead: " << m_pid;
    }

    if (access(m_socket.c_str(), F_OK) != -1) {
        if (unlink(m_socket.c_str()) == -1) {
            log_error() << "Could not remove " << m_socket << ": " << strerror(errno);
            success = false;
        }
    } else {
        log_debug() << "Socket not accessible, has it been removed already?";
    }

    return success;
}

std::string DBusGateway::socketName()
{
    // Return the filename after stripping directory info
    std::string socket(m_socket.c_str());
    return socket.substr(socket.rfind('/') + 1);
}

<|endoftext|>


<commit_before>#include "config_manager.h"

using namespace std;
using namespace TOOLS;

ConfigDataKeeper::ConfigDataKeeper(const ConfigDataKeeper& obj)
  : storage(obj.storage), tinfo(obj.tinfo) { }

ConfigDataKeeper::ConfigDataKeeper(void* data, const string& tinfo)
  : storage(data), tinfo(tinfo) { }

ConfigDataKeeper::ConfigDataKeeper(const string& tinfo)
  : storage(nullptr), tinfo(tinfo) { }

// return verbose type string
string ConfigDataKeeper::verbose_type() const {
  if(tinfo == typeid(int).name())
    return "integer";
  else if(tinfo == typeid(double).name())
    return "float";
  else if(tinfo == typeid(string).name())
    return "string";
  else if(tinfo == typeid(tStringList).name())
    return "string list";
  else if(tinfo == typeid(tStringMap).name())
    return "string map";
  else if(tinfo == typeid(bool).name())
    return "boolean";
  return "unknown data type!!! ";
}

// return verbose data string representation
string ConfigDataKeeper::verbose_data(void* raw_data) const {
  string out;
  const ConfigDataKeeper* cdk;
  if(raw_data != nullptr)
    cdk = new ConfigDataKeeper(raw_data, tinfo);
  else
    cdk = this;

  if(tinfo == typeid(int).name())
    out = str(cdk->get<int>());
  else if(tinfo == typeid(double).name())
    out = str(cdk->get<double>());
  else if(tinfo == typeid(string).name())
    out = cdk->get<string>();
  else if(tinfo == typeid(tStringList).name())
    out = XString(",").join(cdk->get<tStringList>());
  else if(tinfo == typeid(tStringMap).name()) {
    tStringList tmp;
    for(auto& key_val : cdk->get<tStringMap>())
      tmp.push_back(key_val.first + ":" + key_val.second);
    out = XString(",").join(tmp);
  } else if(tinfo == typeid(bool).name())
    out = (cdk->get<bool>()) ? "true" : "false";
  else
    out = "unknown data";

  if(raw_data != nullptr)
    delete cdk;
  return out;
}

ConfigOption::ConfigOption(const std::string& id, const std::string& desc, \
  const std::string& tinfo, const std::string& scmd)
  : data(tinfo), default_data(tinfo), id(id), cmd_short(scmd), desc(desc),
    was_set(false), has_default(false), parent(NULL) { }

string ConfigOption::verbose_type() const {
  return data.verbose_type();
}

string ConfigOption::verbose_data() const {
  return data.verbose_data();
}

string ConfigOption::verbose_default() const {
  if(has_default)
    return default_data.verbose_data();
  return "<no default set>";
}

ConfigGroup::ConfigGroup(const std::string& name, ConfigManager* par)
  : name(name), parent(par)  {}

ConfigGroup::iterator ConfigGroup::begin() {
  return members.begin();
}

ConfigGroup::const_iterator ConfigGroup::begin() const {
  return members.begin();
}

ConfigGroup::iterator ConfigGroup::end() {
  return members.end();
}

ConfigGroup::const_iterator ConfigGroup::end() const {
  return members.end();
}

ConfigOption& ConfigGroup::get_option(const std::string& id) {
  tOptionIter it = members.find(id);
  if(it != members.end())
    return *it->second;
  throw OptionNotFound(id);
}

ConfigGroup& ConfigManager::new_group(const std::string& name) {
  groups[name] = new ConfigGroup(name, this);
  return *groups[name];
}

ConfigGroup& ConfigManager::get_group(const string& name) {
  tGroupIter it = groups.find(name);
  if(it == groups.end())
    throw GroupNotFound(name);
  return *it->second;
}

ConfigGroup& ConfigManager::get_group_from_option(const string& name) {
  return *(get_option(name).parent);
}

ConfigOption& ConfigManager::get_option(const std::string& id) {
  tOptionIter it = members.find(id);
  if(it == members.end())
    throw OptionNotFound(id);
  return *(it->second);
}

ConfigManager::iterator ConfigManager::begin() {
  return groups.begin();
}

ConfigManager::const_iterator ConfigManager::begin() const {
  return groups.begin();
}

ConfigManager::iterator ConfigManager::end() {
  return groups.end();
}

ConfigManager::const_iterator ConfigManager::end() const {
  return groups.end();
}

ConfigManager::ConfigManager(const std::string& start_command)
  : command(start_command) { }

void ConfigManager::parse(tStringList* args) {
  string cmd = args->at(0);

  if(cmdmap.find(cmd) == cmdmap.end())
    throw UnknownParameter(cmd);

  string id = cmdmap[cmd];
  ConfigDataKeeper* tmp = &members[id]->data;

  // catch boolean value as this doesn't need an arg
  if(tmp->same_data_types<bool>()) {
    // even if possible, a arg for bools may be passed
    if(args->size() > 1) {
      bool explicit_set = false;
      if(args->at(1) == "true") {
        explicit_set = true;
        set<bool>(id, true);
      } else if(args->at(1) == "false") {
        explicit_set = true;
        set<bool>(id, false);
      }
      if(explicit_set)
        args->erase(args->begin(), args->begin()+2);

    } else {
      // if not passed, just toggle or set to true
      if(members[id]->has_default)
        set<bool>(id, !get<bool>(id));
      else
        set<bool>(id, true);
      args->erase(args->begin());
    }
    return;
  }

  if(args->size() < 2)
    throw MissingParameter(cmd + " - found no more args");

  string arg = args->at(1);

  // check for integer and double
  try {
    if(tmp->same_data_types<int>()) {
      set<int>(id, integer(arg));
      args->erase(args->begin(), args->begin()+2);
      return;
    } else if(tmp->same_data_types<double>()) {
      set<double>(id, real(arg));
      args->erase(args->begin(), args->begin()+2);
      return;
    }
  } catch(ConvertValueError e) {
    throw IncompatibleDataTypes("data: " + tmp->verbose_type() + " passed: " + arg);
  }

  // get string
  if(tmp->same_data_types<string>()) {
    set<string>(id, str(arg));
    args->erase(args->begin(), args->begin()+2);
    return;
  }

  // build tStringList from "," separated input
  if(tmp->same_data_types<tStringList>()) {
    set<tStringList>(id, XString(arg).split(","));
    args->erase(args->begin(), args->begin()+2);
    return;
    // build tStringMap from "," and ":" separated input
  }
  if(tmp->same_data_types<tStringMap>()) {
    tStringList tmp = XString(arg).split(",");
    tStringMap tmpmap;
    for(tStringIter i=tmp.begin(); i!=tmp.end(); ++i) {
      tStringList two = XString(*i).split(":");
      tmpmap[two[0]] = two[1];
    }
    set<tStringMap>(id, tmpmap);
    args->erase(args->begin(), args->begin()+2);
    return;
  }
  throw IncompatibleDataTypes("data: " + tmp->verbose_type() + " passed: " + arg);
}

void ConfigManager::parse_cmdline(int argc, char* argv[]) {
  tStringList args;
  for(int i=1; i<argc; ++i)
    args.push_back(argv[i]);

  while(args.size() > 0)
    parse(&args);
}

void ConfigManager::write_config_file(ostream& fd, bool shorter) {
  fd << endl;
  fd << "###" << endl;
  fd << "### config file for: " << command << endl;
  fd << "###" << endl;
  fd << endl;
  for(tGroupPair& grp : groups) {
    if(!shorter)
      fd << "####################################################" << endl;
    else
      fd << endl;
    fd << "# [ " << grp.first << " ]" << endl;
    for(tOptionPair& opt : *grp.second) {
      const ConfigOption* c = opt.second;
      const string& id = opt.first;

      // write desc/help text
      if(!shorter) {
        fd << "# " << c->desc << " [" << c->verbose_type() << "]";
        // write if ConfigOption has default
        if(c->has_default)
          fd << " default: " << c->verbose_default();
        fd << endl;
      }

      // ConfigOption was set, output its value
      if(c->was_set) {
        fd << id << " = " << c->verbose_data() << endl;
      // ConfigOption was _not_ set, output commented out line and default
      } else if(c->has_default) {
          fd << "# " << id << " = " << c->verbose_default() << endl;
      // ConfigOption was neither set nor has a default value
      } else {
        if(!shorter)
          fd << "# " << id << " = " << "<not set>" << endl;
      }
      if(!shorter)
        fd << endl;
    }
  }
}

void ConfigManager::parse_config_file(const string& fn) {
  ifstream fd(fn.c_str(), ios::in);
  XString line;
  tStringList tokens;
  while(fd.good()) {
    getline(fd, line);
    line.strip().strip("\n");
    if(line.length() == 0)
      continue;

    if(line.startswith("#"))
      continue;

    if(line.find("=") == string::npos) {
      tokens.push_back("--" + line);
      continue;
    }

    tStringList lr = line.split("=");
    XString left(lr[0]), right(lr[1]);
    left.strip();
    right.strip();

    tokens.push_back("--" + left);
    tokens.push_back(right);
  }
  fd.close();

  try {
    while(tokens.size() > 0)
      parse(&tokens);

  } catch(IncompatibleDataTypes& e) {
    e.message += " (inside configfile)";
    throw e;
  }
}

bool ConfigManager::is_group_active(const std::string& name) {
  tOptionMap& opts = groups[name]->members;
  for(tOptionIter i=opts.begin(); i!=opts.end(); ++i)
    if(i->second->was_set)
      return true;
  return false;
}

bool ConfigManager::is_option_set(const std::string& id) {
  return (get_option(id).was_set || get_option(id).has_default);
}

void ConfigManager::usage(ostream& ss) {
  ss << "Usage: " << command << " <options>" << endl;
  ss << endl << "Options:" << endl;

  string::size_type id_len = 0, scmd_len = 0, desc_len = 0;
  for(tStringMapIter i=cmdmap.begin(); i!=cmdmap.end(); ++i) {
    ConfigOption* opt = members[i->second];
    if(i->first.find("--") != string::npos)
      id_len = max(opt->id.length(), id_len);
    else
      scmd_len = max(opt->cmd_short.length(), scmd_len);
    desc_len = max(opt->desc.length(), desc_len);
  }

  for(tGroupIter g=groups.begin(); g!=groups.end(); ++g) {
    ConfigGroup* grp = g->second;
    ss << endl << "--- Group: " << grp->name << endl;
    for(tOptionIter i=grp->members.begin(); i!=grp->members.end(); ++i) {
      ConfigOption* opt = grp->members[i->first];

      ss << right << setw(scmd_len+1) << ((opt->cmd_short.size() > 0) ? \
                                          ("-" + opt->cmd_short) : "") << " | " << flush;
      ss << left << setw(id_len+2) << ("--" + opt->id) << "   " << opt->desc;
      ss << " [" << opt->data.verbose_type() << "]";
      ss << endl;
    }
  }
  ss << endl;
}
<commit_msg>ConfigManager catching case two bools without args in cmdline<commit_after>#include "config_manager.h"

using namespace std;
using namespace TOOLS;

ConfigDataKeeper::ConfigDataKeeper(const ConfigDataKeeper& obj)
  : storage(obj.storage), tinfo(obj.tinfo) { }

ConfigDataKeeper::ConfigDataKeeper(void* data, const string& tinfo)
  : storage(data), tinfo(tinfo) { }

ConfigDataKeeper::ConfigDataKeeper(const string& tinfo)
  : storage(nullptr), tinfo(tinfo) { }

// return verbose type string
string ConfigDataKeeper::verbose_type() const {
  if(tinfo == typeid(int).name())
    return "integer";
  else if(tinfo == typeid(double).name())
    return "float";
  else if(tinfo == typeid(string).name())
    return "string";
  else if(tinfo == typeid(tStringList).name())
    return "string list";
  else if(tinfo == typeid(tStringMap).name())
    return "string map";
  else if(tinfo == typeid(bool).name())
    return "boolean";
  return "unknown data type!!! ";
}

// return verbose data string representation
string ConfigDataKeeper::verbose_data(void* raw_data) const {
  string out;
  const ConfigDataKeeper* cdk;
  if(raw_data != nullptr)
    cdk = new ConfigDataKeeper(raw_data, tinfo);
  else
    cdk = this;

  if(tinfo == typeid(int).name())
    out = str(cdk->get<int>());
  else if(tinfo == typeid(double).name())
    out = str(cdk->get<double>());
  else if(tinfo == typeid(string).name())
    out = cdk->get<string>();
  else if(tinfo == typeid(tStringList).name())
    out = XString(",").join(cdk->get<tStringList>());
  else if(tinfo == typeid(tStringMap).name()) {
    tStringList tmp;
    for(auto& key_val : cdk->get<tStringMap>())
      tmp.push_back(key_val.first + ":" + key_val.second);
    out = XString(",").join(tmp);
  } else if(tinfo == typeid(bool).name())
    out = (cdk->get<bool>()) ? "true" : "false";
  else
    out = "unknown data";

  if(raw_data != nullptr)
    delete cdk;
  return out;
}

ConfigOption::ConfigOption(const std::string& id, const std::string& desc, \
  const std::string& tinfo, const std::string& scmd)
  : data(tinfo), default_data(tinfo), id(id), cmd_short(scmd), desc(desc),
    was_set(false), has_default(false), parent(NULL) { }

string ConfigOption::verbose_type() const {
  return data.verbose_type();
}

string ConfigOption::verbose_data() const {
  return data.verbose_data();
}

string ConfigOption::verbose_default() const {
  if(has_default)
    return default_data.verbose_data();
  return "<no default set>";
}

ConfigGroup::ConfigGroup(const std::string& name, ConfigManager* par)
  : name(name), parent(par)  {}

ConfigGroup::iterator ConfigGroup::begin() {
  return members.begin();
}

ConfigGroup::const_iterator ConfigGroup::begin() const {
  return members.begin();
}

ConfigGroup::iterator ConfigGroup::end() {
  return members.end();
}

ConfigGroup::const_iterator ConfigGroup::end() const {
  return members.end();
}

ConfigOption& ConfigGroup::get_option(const std::string& id) {
  tOptionIter it = members.find(id);
  if(it != members.end())
    return *it->second;
  throw OptionNotFound(id);
}

ConfigGroup& ConfigManager::new_group(const std::string& name) {
  groups[name] = new ConfigGroup(name, this);
  return *groups[name];
}

ConfigGroup& ConfigManager::get_group(const string& name) {
  tGroupIter it = groups.find(name);
  if(it == groups.end())
    throw GroupNotFound(name);
  return *it->second;
}

ConfigGroup& ConfigManager::get_group_from_option(const string& name) {
  return *(get_option(name).parent);
}

ConfigOption& ConfigManager::get_option(const std::string& id) {
  tOptionIter it = members.find(id);
  if(it == members.end())
    throw OptionNotFound(id);
  return *(it->second);
}

ConfigManager::iterator ConfigManager::begin() {
  return groups.begin();
}

ConfigManager::const_iterator ConfigManager::begin() const {
  return groups.begin();
}

ConfigManager::iterator ConfigManager::end() {
  return groups.end();
}

ConfigManager::const_iterator ConfigManager::end() const {
  return groups.end();
}

ConfigManager::ConfigManager(const std::string& start_command)
  : command(start_command) { }

void ConfigManager::parse(tStringList* args) {
  string cmd = args->at(0);

  if(cmdmap.find(cmd) == cmdmap.end())
    throw UnknownParameter(cmd);

  string id = cmdmap[cmd];
  ConfigDataKeeper* tmp = &members[id]->data;

  // catch boolean value as this doesn't need an arg
  if(tmp->same_data_types<bool>()) {
    // even if possible, a arg for bools may be passed
    if(args->size() > 1) {
      bool explicit_set = false;
      if(args->at(1) == "true") {
        explicit_set = true;
        set<bool>(id, true);
      } else if(args->at(1) == "false") {
        explicit_set = true;
        set<bool>(id, false);
      }
      if(explicit_set) {
        args->erase(args->begin(), args->begin()+2);
        return;
      }
    }

    // if not passed, just toggle or set to true
    if(members[id]->has_default)
      set<bool>(id, !get<bool>(id));
    else
      set<bool>(id, true);
    args->erase(args->begin());
    return;
  }

  if(args->size() < 2)
    throw MissingParameter(cmd + " - found no more args");

  string arg = args->at(1);

  // check for integer and double
  try {
    if(tmp->same_data_types<int>()) {
      set<int>(id, integer(arg));
      args->erase(args->begin(), args->begin()+2);
      return;
    } else if(tmp->same_data_types<double>()) {
      set<double>(id, real(arg));
      args->erase(args->begin(), args->begin()+2);
      return;
    }
  } catch(ConvertValueError e) {
    throw IncompatibleDataTypes("data: " + tmp->verbose_type() + " passed: " + arg);
  }

  // get string
  if(tmp->same_data_types<string>()) {
    set<string>(id, str(arg));
    args->erase(args->begin(), args->begin()+2);
    return;
  }

  // build tStringList from "," separated input
  if(tmp->same_data_types<tStringList>()) {
    set<tStringList>(id, XString(arg).split(","));
    args->erase(args->begin(), args->begin()+2);
    return;
    // build tStringMap from "," and ":" separated input
  }
  if(tmp->same_data_types<tStringMap>()) {
    tStringList tmp = XString(arg).split(",");
    tStringMap tmpmap;
    for(tStringIter i=tmp.begin(); i!=tmp.end(); ++i) {
      tStringList two = XString(*i).split(":");
      tmpmap[two[0]] = two[1];
    }
    set<tStringMap>(id, tmpmap);
    args->erase(args->begin(), args->begin()+2);
    return;
  }
  throw IncompatibleDataTypes("data: " + tmp->verbose_type() + " passed: " + arg);
}

void ConfigManager::parse_cmdline(int argc, char* argv[]) {
  tStringList args;
  for(int i=1; i<argc; ++i)
    args.push_back(argv[i]);

  while(args.size() > 0)
    parse(&args);
}

void ConfigManager::write_config_file(ostream& fd, bool shorter) {
  fd << endl;
  fd << "###" << endl;
  fd << "### config file for: " << command << endl;
  fd << "###" << endl;
  fd << endl;
  for(tGroupPair& grp : groups) {
    if(!shorter)
      fd << "####################################################" << endl;
    else
      fd << endl;
    fd << "# [ " << grp.first << " ]" << endl;
    for(tOptionPair& opt : *grp.second) {
      const ConfigOption* c = opt.second;
      const string& id = opt.first;

      // write desc/help text
      if(!shorter) {
        fd << "# " << c->desc << " [" << c->verbose_type() << "]";
        // write if ConfigOption has default
        if(c->has_default)
          fd << " default: " << c->verbose_default();
        fd << endl;
      }

      // ConfigOption was set, output its value
      if(c->was_set) {
        fd << id << " = " << c->verbose_data() << endl;
      // ConfigOption was _not_ set, output commented out line and default
      } else if(c->has_default) {
          fd << "# " << id << " = " << c->verbose_default() << endl;
      // ConfigOption was neither set nor has a default value
      } else {
        if(!shorter)
          fd << "# " << id << " = " << "<not set>" << endl;
      }
      if(!shorter)
        fd << endl;
    }
  }
}

void ConfigManager::parse_config_file(const string& fn) {
  ifstream fd(fn.c_str(), ios::in);
  XString line;
  tStringList tokens;
  while(fd.good()) {
    getline(fd, line);
    line.strip().strip("\n");
    if(line.length() == 0)
      continue;

    if(line.startswith("#"))
      continue;

    if(line.find("=") == string::npos) {
      tokens.push_back("--" + line);
      continue;
    }

    tStringList lr = line.split("=");
    XString left(lr[0]), right(lr[1]);
    left.strip();
    right.strip();

    tokens.push_back("--" + left);
    tokens.push_back(right);
  }
  fd.close();

  try {
    while(tokens.size() > 0)
      parse(&tokens);

  } catch(IncompatibleDataTypes& e) {
    e.message += " (inside configfile)";
    throw e;
  }
}

bool ConfigManager::is_group_active(const std::string& name) {
  tOptionMap& opts = groups[name]->members;
  for(tOptionIter i=opts.begin(); i!=opts.end(); ++i)
    if(i->second->was_set)
      return true;
  return false;
}

bool ConfigManager::is_option_set(const std::string& id) {
  return (get_option(id).was_set || get_option(id).has_default);
}

void ConfigManager::usage(ostream& ss) {
  ss << "Usage: " << command << " <options>" << endl;
  ss << endl << "Options:" << endl;

  string::size_type id_len = 0, scmd_len = 0, desc_len = 0;
  for(tStringMapIter i=cmdmap.begin(); i!=cmdmap.end(); ++i) {
    ConfigOption* opt = members[i->second];
    if(i->first.find("--") != string::npos)
      id_len = max(opt->id.length(), id_len);
    else
      scmd_len = max(opt->cmd_short.length(), scmd_len);
    desc_len = max(opt->desc.length(), desc_len);
  }

  for(tGroupIter g=groups.begin(); g!=groups.end(); ++g) {
    ConfigGroup* grp = g->second;
    ss << endl << "--- Group: " << grp->name << endl;
    for(tOptionIter i=grp->members.begin(); i!=grp->members.end(); ++i) {
      ConfigOption* opt = grp->members[i->first];

      ss << right << setw(scmd_len+1) << ((opt->cmd_short.size() > 0) ? \
                                          ("-" + opt->cmd_short) : "") << " | " << flush;
      ss << left << setw(id_len+2) << ("--" + opt->id) << "   " << opt->desc;
      ss << " [" << opt->data.verbose_type() << "]";
      ss << endl;
    }
  }
  ss << endl;
}
<|endoftext|>


<commit_before>

// This file contains definition of C++ new/delete operators

#include <stddef.h>
#include <malloc.h>
#include "stdexcept.h"

namespace std {
    void terminate();
}

typedef void (*new_handler)();
static new_handler new_handl = &std::terminate;

namespace std {
    new_handler set_new_handler(new_handler handl) {
        new_handler old = new_handl;
        new_handl = handl;
        return old;
    }
}

__attribute__((weak))
void * operator new(size_t size) {

    void * mem = malloc(size);
    while(mem == NULL) {
        new_handl();
        mem = malloc(size);
    }

    return mem;
}

__attribute__((weak))
void operator delete(void * ptr) {
    free(ptr);
}

__attribute__((weak))
void * operator new[](size_t size) {
    return ::operator new(size);
}

__attribute__((weak))
void operator delete[](void * ptr) {
    ::operator delete(ptr);
}

<commit_msg>fix for COMPILER-8905: operator new should throw std::bad_alloc if new handler is not set<commit_after>

// This file contains definition of C++ new/delete operators

#include <stddef.h>
#include <malloc.h>
#include "stdexcept.h"

namespace std {
    void terminate();
}

typedef void (*new_handler)();
static new_handler new_handl = &std::terminate;

namespace std {
    new_handler set_new_handler(new_handler handl) {
        new_handler old = new_handl;
        new_handl = handl;
        return old;
    }
}

__attribute__((weak))
void * operator new(size_t size) {

    void * mem = malloc(size);
    while(mem == NULL) {
        if(new_handl != NULL) {
            new_handl();
        } else {
            throw std::bad_alloc();
        }
        mem = malloc(size);
    }

    return mem;
}

__attribute__((weak))
void operator delete(void * ptr) {
    free(ptr);
}

__attribute__((weak))
void * operator new[](size_t size) {
    return ::operator new(size);
}

__attribute__((weak))
void operator delete[](void * ptr) {
    ::operator delete(ptr);
}

<|endoftext|>


<commit_before>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/video/capture/fake_video_capture_device.h"

#include <string>

#include "base/memory/scoped_ptr.h"
#include "base/stringprintf.h"

namespace media {

static const int kFakeCaptureTimeoutMs = 100;
enum { kNumberOfFakeDevices = 2 };

void FakeVideoCaptureDevice::GetDeviceNames(Names* const device_names) {
  // Empty the name list.
  device_names->erase(device_names->begin(), device_names->end());

  for (int n = 0; n < kNumberOfFakeDevices; n++) {
    Name name;
    name.unique_id = StringPrintf("/dev/video%d", n);
    name.device_name = StringPrintf("fake_device_%d", n);
    device_names->push_back(name);
  }
}

VideoCaptureDevice* FakeVideoCaptureDevice::Create(const Name& device_name) {
  for (int n = 0; n < kNumberOfFakeDevices; ++n) {
    std::string possible_id = StringPrintf("/dev/video%d", n);
    if (device_name.unique_id.compare(possible_id) == 0) {
      return new FakeVideoCaptureDevice(device_name);
    }
  }
  return NULL;
}

FakeVideoCaptureDevice::FakeVideoCaptureDevice(const Name& device_name)
    : device_name_(device_name),
      state_(kIdle),
      capture_thread_("CaptureThread") {
}

FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
  // Check if the thread is running.
  // This means that the device have not been DeAllocated properly.
  DCHECK(!capture_thread_.IsRunning());
}

void FakeVideoCaptureDevice::Allocate(int width,
                                       int height,
                                       int frame_rate,
                                       EventHandler* observer) {
  if (state_ != kIdle) {
    return;  // Wrong state.
  }

  observer_ = observer;
  Capability current_settings;
  current_settings.color = kI420;
  if (width > 320) {  // VGA
    current_settings.width = 640;
    current_settings.height = 480;
    current_settings.frame_rate = 30;
  } else {  // QVGA
    current_settings.width = 320;
    current_settings.height = 240;
    current_settings.frame_rate = 30;
  }

  fake_frame_.reset(new uint8[current_settings.width *
                              current_settings.height * 3 / 2]);
  memset(fake_frame_.get(), 0, sizeof(fake_frame_.get()));

  state_ = kAllocated;
  observer_->OnFrameInfo(current_settings);
}

void FakeVideoCaptureDevice::Start() {
  if (state_ != kAllocated) {
      return;  // Wrong state.
  }
  state_ = kCapturing;
  capture_thread_.Start();
  capture_thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &FakeVideoCaptureDevice::OnCaptureTask));
}

void FakeVideoCaptureDevice::Stop() {
  if (state_ != kCapturing) {
      return;  // Wrong state.
  }
  capture_thread_.Stop();
  state_ = kAllocated;
}

void FakeVideoCaptureDevice::DeAllocate() {
  if (state_ != kAllocated && state_ != kCapturing) {
      return;  // Wrong state.
  }
  capture_thread_.Stop();
  state_ = kIdle;
}

const VideoCaptureDevice::Name& FakeVideoCaptureDevice::device_name() {
  return device_name_;
}

void FakeVideoCaptureDevice::OnCaptureTask() {
  if (state_ != kCapturing) {
    return;
  }
  // Give the captured frame to the observer.
  observer_->OnIncomingCapturedFrame(fake_frame_.get(),
                                     sizeof(fake_frame_.get()),
                                     base::Time::Now());
  // Reschedule next CaptureTask.
  capture_thread_.message_loop()->PostDelayedTask(
        FROM_HERE,
        NewRunnableMethod(this, &FakeVideoCaptureDevice::OnCaptureTask),
        kFakeCaptureTimeoutMs);
}

}  // namespace media
<commit_msg>Uninitialized member in FakeVideoCaptureDevice.<commit_after>// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/video/capture/fake_video_capture_device.h"

#include <string>

#include "base/memory/scoped_ptr.h"
#include "base/stringprintf.h"

namespace media {

static const int kFakeCaptureTimeoutMs = 100;
enum { kNumberOfFakeDevices = 2 };

void FakeVideoCaptureDevice::GetDeviceNames(Names* const device_names) {
  // Empty the name list.
  device_names->erase(device_names->begin(), device_names->end());

  for (int n = 0; n < kNumberOfFakeDevices; n++) {
    Name name;
    name.unique_id = StringPrintf("/dev/video%d", n);
    name.device_name = StringPrintf("fake_device_%d", n);
    device_names->push_back(name);
  }
}

VideoCaptureDevice* FakeVideoCaptureDevice::Create(const Name& device_name) {
  for (int n = 0; n < kNumberOfFakeDevices; ++n) {
    std::string possible_id = StringPrintf("/dev/video%d", n);
    if (device_name.unique_id.compare(possible_id) == 0) {
      return new FakeVideoCaptureDevice(device_name);
    }
  }
  return NULL;
}

FakeVideoCaptureDevice::FakeVideoCaptureDevice(const Name& device_name)
    : device_name_(device_name),
      observer_(NULL),
      state_(kIdle),
      capture_thread_("CaptureThread") {
}

FakeVideoCaptureDevice::~FakeVideoCaptureDevice() {
  // Check if the thread is running.
  // This means that the device have not been DeAllocated properly.
  DCHECK(!capture_thread_.IsRunning());
}

void FakeVideoCaptureDevice::Allocate(int width,
                                       int height,
                                       int frame_rate,
                                       EventHandler* observer) {
  if (state_ != kIdle) {
    return;  // Wrong state.
  }

  observer_ = observer;
  Capability current_settings;
  current_settings.color = kI420;
  if (width > 320) {  // VGA
    current_settings.width = 640;
    current_settings.height = 480;
    current_settings.frame_rate = 30;
  } else {  // QVGA
    current_settings.width = 320;
    current_settings.height = 240;
    current_settings.frame_rate = 30;
  }

  fake_frame_.reset(new uint8[current_settings.width *
                              current_settings.height * 3 / 2]);
  memset(fake_frame_.get(), 0, sizeof(fake_frame_.get()));

  state_ = kAllocated;
  observer_->OnFrameInfo(current_settings);
}

void FakeVideoCaptureDevice::Start() {
  if (state_ != kAllocated) {
      return;  // Wrong state.
  }
  state_ = kCapturing;
  capture_thread_.Start();
  capture_thread_.message_loop()->PostTask(
      FROM_HERE,
      NewRunnableMethod(this, &FakeVideoCaptureDevice::OnCaptureTask));
}

void FakeVideoCaptureDevice::Stop() {
  if (state_ != kCapturing) {
      return;  // Wrong state.
  }
  capture_thread_.Stop();
  state_ = kAllocated;
}

void FakeVideoCaptureDevice::DeAllocate() {
  if (state_ != kAllocated && state_ != kCapturing) {
      return;  // Wrong state.
  }
  capture_thread_.Stop();
  state_ = kIdle;
}

const VideoCaptureDevice::Name& FakeVideoCaptureDevice::device_name() {
  return device_name_;
}

void FakeVideoCaptureDevice::OnCaptureTask() {
  if (state_ != kCapturing) {
    return;
  }
  // Give the captured frame to the observer.
  observer_->OnIncomingCapturedFrame(fake_frame_.get(),
                                     sizeof(fake_frame_.get()),
                                     base::Time::Now());
  // Reschedule next CaptureTask.
  capture_thread_.message_loop()->PostDelayedTask(
        FROM_HERE,
        NewRunnableMethod(this, &FakeVideoCaptureDevice::OnCaptureTask),
        kFakeCaptureTimeoutMs);
}

}  // namespace media
<|endoftext|>


<commit_before>//
//  ex9_31.cpp
//  Exercise 9.31
//
//  Created by pezy on 12/3/14.
//
//  @Brief  The program on page 354 to remove even-valued elements and
//          duplicate odd ones will not work on a list or forward_list. Why?
//          Revise the program so that it works on these types as well.
//  @forward_list   1. vi.insert -> vi.insert_after
//                  2. iter += 2; -> advance(iter, 2);
//                  3. vi.erase -> vi.erase_after
//                  4. added iterator prev, and edit some detail

#include <iostream>
#include <forward_list>

using std::forward_list; using std::cout; using std::endl; using std::advance;

int main()
{
    forward_list<int> vi = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    auto iter = vi.begin(), prev = vi.before_begin();
    while (iter != vi.end()) {
        if (*iter % 2) {
            iter = vi.insert_after(prev, *iter);
            advance(iter, 2);
            advance(prev, 2);
        }
        else
            iter = vi.erase_after(prev);
    }

    for (auto i : vi)
        cout << i << " ";

    return 0;
}
<commit_msg>Update ex9_31_2.cpp<commit_after>//
//  ex9_31.cpp
//  Exercise 9.31
//
//  Created by pezy on 12/3/14.
//
//  @Brief  The program on page 354 to remove even-valued elements and
//          duplicate odd ones will not work on a list or forward_list. Why?
//          Revise the program so that it works on these types as well.
//
//  Refactored by Yue Wang Oct 2015
//

#include <iostream>
#include <forward_list>

using std::forward_list;
using std::cout;
using std::advance;

auto remove_evens_and_double_odds(forward_list<int>& data)
{
    for(auto cur = data.begin(), prv = data.before_begin(); cur != data.end();)
        if (*cur & 0x1)
            cur = data.insert_after(prv, *cur),
            advance(cur, 2),
            advance(prv, 2);
        else
            cur = data.erase_after(prv);
}

int main()
{
    forward_list<int> data { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    remove_evens_and_double_odds(data);
    for (auto i : data)
        cout << i << " ";

    return 0;
}
<|endoftext|>


<commit_before>//
//  Amiga.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 16/07/2021.
//  Copyright © 2021 Thomas Harte. All rights reserved.
//

#include "Amiga.hpp"

#include "../MachineTypes.hpp"

#include "../../Processors/68000/68000.hpp"

#include "../../Analyser/Static/Amiga/Target.hpp"

#include "../Utility/MemoryPacker.hpp"
#include "../Utility/MemoryFuzzer.hpp"

namespace Amiga {

class ConcreteMachine:
	public CPU::MC68000::BusHandler,
	public MachineTypes::ScanProducer,
	public MachineTypes::TimedMachine,
	public Machine {
	public:
		ConcreteMachine(const Analyser::Static::Amiga::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
			mc68000_(*this)
		{
			(void)target;

			// Temporary: use a hard-coded Kickstart selection.
			constexpr ROM::Name rom_name = ROM::Name::AmigaA500Kickstart13;
			ROM::Request request(rom_name);
			auto roms = rom_fetcher(request);
			if(!request.validate(roms)) {
				throw ROMMachine::Error::MissingROMs;
			}
			Memory::PackBigEndian16(roms.find(rom_name)->second, kickstart_.data());

			// NTSC clock rate: 2*3.579545 = 7.15909Mhz.
			// PAL clock rate: 7.09379Mhz.
			set_clock_rate(7'093'790.0);
		}

		// MARK: - MC68000::BusHandler.
		using Microcycle = CPU::MC68000::Microcycle;
		HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
			// Do nothing if no address is exposed.
			if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);

			// TODO: interrupt acknowledgement though?

			// Grab the target address to pick a memory source.
			const uint32_t address = cycle.host_endian_byte_address();
			(void)address;

			// Address spaces that matter:
			//
			//	00'0000 – 08'0000:	chip RAM.	[or overlayed KickStart]
			//	– 10'0000: extended chip ram for ECS.
			//	– 20'0000: auto-config space (/fast RAM).
			//	...
			//	bf'd000 – c0'0000: 8250s.
			//	c0'0000 – d8'0000: pseudo-fast RAM.
			//	...
			//	dc'0000 – dd'0000: optional real-time clock.
			//	df'f000 - e0'0000: custom chip registers.
			//	...
			//	f0'0000 — : 512kb Kickstart (or possibly just an extra 512kb reserved for hypothetical 1mb Kickstart?).
			//	f8'0000 — : 256kb Kickstart if 2.04 or higher.
			//	fc'0000 – : 256kb Kickstart otherwise.

			return HalfCycles(0);
		}

	private:
		CPU::MC68000::Processor<ConcreteMachine, true> mc68000_;

		// MARK: - Memory map.
		std::array<uint8_t, 512*1024> ram_;
		std::array<uint8_t, 512*1024> kickstart_;

		struct MemoryRegion {

		} regions_[64];	// i.e. top six bits are used as an index.

		// MARK: - MachineTypes::ScanProducer.

		void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
			(void)scan_target;
		}

		Outputs::Display::ScanStatus get_scaled_scan_status() const {
			return Outputs::Display::ScanStatus();
		}

		// MARK: - MachineTypes::TimedMachine.

		void run_for(const Cycles cycles) {
			mc68000_.run_for(cycles);
		}
};

}

using namespace Amiga;

Machine *Machine::Amiga(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
	using Target = Analyser::Static::Amiga::Target;
	const Target *const amiga_target = dynamic_cast<const Target *>(target);
	return new Amiga::ConcreteMachine(*amiga_target, rom_fetcher);
}

Machine::~Machine() {}
<commit_msg>Begins sketching out a memory mapper.<commit_after>//
//  Amiga.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 16/07/2021.
//  Copyright © 2021 Thomas Harte. All rights reserved.
//

#include "Amiga.hpp"

#include "../MachineTypes.hpp"

#include "../../Processors/68000/68000.hpp"

#include "../../Analyser/Static/Amiga/Target.hpp"

#include "../Utility/MemoryPacker.hpp"
#include "../Utility/MemoryFuzzer.hpp"

namespace Amiga {

class ConcreteMachine:
	public CPU::MC68000::BusHandler,
	public MachineTypes::ScanProducer,
	public MachineTypes::TimedMachine,
	public Machine {
	public:
		ConcreteMachine(const Analyser::Static::Amiga::Target &target, const ROMMachine::ROMFetcher &rom_fetcher) :
			mc68000_(*this)
		{
			(void)target;

			// Temporary: use a hard-coded Kickstart selection.
			constexpr ROM::Name rom_name = ROM::Name::AmigaA500Kickstart13;
			ROM::Request request(rom_name);
			auto roms = rom_fetcher(request);
			if(!request.validate(roms)) {
				throw ROMMachine::Error::MissingROMs;
			}
			Memory::PackBigEndian16(roms.find(rom_name)->second, kickstart_.data());

			// Address spaces that matter:
			//
			//	00'0000 – 08'0000:	chip RAM.	[or overlayed KickStart]
			//	– 10'0000: extended chip ram for ECS.
			//	– 20'0000: auto-config space (/fast RAM).
			//	...
			//	bf'd000 – c0'0000: 8250s.
			//	c0'0000 – d8'0000: pseudo-fast RAM.
			//	...
			//	dc'0000 – dd'0000: optional real-time clock.
			//	df'f000 - e0'0000: custom chip registers.
			//	...
			//	f0'0000 — : 512kb Kickstart (or possibly just an extra 512kb reserved for hypothetical 1mb Kickstart?).
			//	f8'0000 — : 256kb Kickstart if 2.04 or higher.
			//	fc'0000 – : 256kb Kickstart otherwise.
			set_region(0x00'0000, 0x08'00000, kickstart_.data(), CPU::MC68000::Microcycle::PermitRead);
			set_region(0xfc'0000, 0x1'00'0000, kickstart_.data(), CPU::MC68000::Microcycle::PermitRead);

			// NTSC clock rate: 2*3.579545 = 7.15909Mhz.
			// PAL clock rate: 7.09379Mhz.
			set_clock_rate(7'093'790.0);
		}

		// MARK: - MC68000::BusHandler.
		using Microcycle = CPU::MC68000::Microcycle;
		HalfCycles perform_bus_operation(const CPU::MC68000::Microcycle &cycle, int) {
			// Do nothing if no address is exposed.
			if(!(cycle.operation & (Microcycle::NewAddress | Microcycle::SameAddress))) return HalfCycles(0);

			// Otherwise, intended 1-2 step here is:
			//
			//	(1) determine when this CPU access will be scheduled;
			//	(2)	do all the other actions prior to this CPU access being scheduled.
			//
			// (or at least enqueue them, JIT-wise).

			// TODO: interrupt acknowledgement.

			// Grab the target address to pick a memory source.
			const uint32_t address = cycle.host_endian_byte_address();
			if(!regions_[address >> 18].read_write_mask) {
				// TODO: registers, etc, here.
				assert(false);
			}
			cycle.apply(&regions_[address >> 18].contents[address], regions_[address >> 18].read_write_mask);

			return HalfCycles(0);
		}

	private:
		CPU::MC68000::Processor<ConcreteMachine, true> mc68000_;

		// MARK: - Memory map.
		std::array<uint8_t, 512*1024> ram_;
		std::array<uint8_t, 512*1024> kickstart_;

		struct MemoryRegion {
			uint8_t *contents = nullptr;
			int read_write_mask = 0;
		} regions_[64];	// i.e. top six bits are used as an index.

		void set_region(int start, int end, uint8_t *base, int read_write_mask) {
			assert(!(start & ~0xfc'0000));
			assert(!((end - (1 << 18)) & ~0xfc'0000));

			for(int c = start >> 18; c < end >> 18; c++) {
				regions_[c].contents = base - (c << 18);
				regions_[c].read_write_mask = read_write_mask;
			}
		}

		// MARK: - MachineTypes::ScanProducer.

		void set_scan_target(Outputs::Display::ScanTarget *scan_target) final {
			(void)scan_target;
		}

		Outputs::Display::ScanStatus get_scaled_scan_status() const {
			return Outputs::Display::ScanStatus();
		}

		// MARK: - MachineTypes::TimedMachine.

		void run_for(const Cycles cycles) {
			mc68000_.run_for(cycles);
		}
};

}

using namespace Amiga;

Machine *Machine::Amiga(const Analyser::Static::Target *target, const ROMMachine::ROMFetcher &rom_fetcher) {
	using Target = Analyser::Static::Amiga::Target;
	const Target *const amiga_target = dynamic_cast<const Target *>(target);
	return new Amiga::ConcreteMachine(*amiga_target, rom_fetcher);
}

Machine::~Machine() {}
<|endoftext|>