<commit_before>#include "base.hpp"
#include "ButtonStatus.hpp"
#include "Client.hpp"
#include "CommonData.hpp"
#include "Config.hpp"
#include "Core.hpp"
#include "RemapUtil.hpp"
#include "remap.hpp"
#include "util/CallBackWrapper.hpp"
#include "util/KeyboardRepeat.hpp"
#include "util/KeyEventInputQueue.hpp"
#include "util/ListHookedConsumer.hpp"
#include "util/ListHookedKeyboard.hpp"
#include "util/ListHookedPointing.hpp"
#include "util/NumHeldDownKeys.hpp"
#include "util/TimerWrapper.hpp"
#include "VirtualKey.hpp"

#include <sys/errno.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOTimerEventSource.h>

namespace org_pqrs_KeyRemap4MacBook {
  namespace Core {
    namespace {
      IOWorkLoop* workLoop = NULL;
      TimerWrapper timer_refresh;

      // ------------------------------------------------------------
      enum {
        REFRESH_DEVICE_INTERVAL = 3000,
      };

      void
      refreshHookedDevice(OSObject* owner, IOTimerEventSource* sender)
      {
        IOLockWrapper::ScopedLock lk(timer_refresh.getlock());

        ListHookedKeyboard::instance().refresh();
        ListHookedConsumer::instance().refresh();
        ListHookedPointing::instance().refresh();

        timer_refresh.setTimeoutMS(REFRESH_DEVICE_INTERVAL);
      }

      // ----------------------------------------
      void
      cancelRepeat(void)
      {
        if (config.debug_devel) {
          IOLog("KeyRemap4MacBook -Info- cancelRepeat\n");
        }

        KeyboardRepeat::cancel();
      }
    }

    void
    initialize(void)
    {
      sysctl_register();
      remapfunc_initialize();
      EventWatcher::initialize();
      PressDownKeys::initialize();
      FlagStatus::initialize();
      ButtonStatus::initialize();
      KeyRemap4MacBook_client::initialize();
      CommonData::initialize();
    }

    void
    terminate(void)
    {
      CommonData::terminate();
      KeyRemap4MacBook_client::terminate();
      EventWatcher::terminate();
      remapfunc_terminate();
      sysctl_unregister();
    }

    void
    start(void)
    {
      ListHookedKeyboard::instance().initialize();
      ListHookedConsumer::instance().initialize();
      ListHookedPointing::instance().initialize();

      workLoop = IOWorkLoop::workLoop();
      if (! workLoop) {
        IOLog("[KeyRemap4MacBook ERROR] IOWorkLoop::workLoop failed\n");
      } else {
        timer_refresh.initialize(workLoop, NULL, refreshHookedDevice);
        timer_refresh.setTimeoutMS(REFRESH_DEVICE_INTERVAL);

        KeyboardRepeat::initialize(*workLoop);
        KeyEventInputQueue::initialize(*workLoop);
        VirtualKey::initialize(*workLoop);
        EventOutputQueue::initialize(*workLoop);
      }
    }

    void
    stop(void)
    {
      timer_refresh.terminate();
      ListHookedKeyboard::instance().terminate();
      ListHookedConsumer::instance().terminate();
      ListHookedPointing::instance().terminate();
      KeyboardRepeat::terminate();
      KeyEventInputQueue::terminate();
      VirtualKey::terminate();
      EventOutputQueue::terminate();

      if (workLoop) {
        workLoop->release();
        workLoop = NULL;
      }
    }

    // ======================================================================
    bool
    notifierfunc_hookKeyboard(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLog("KeyRemap4MacBook notifierfunc_hookKeyboard\n");

      IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, newService);
      ListHookedKeyboard::instance().append(kbd);
      ListHookedConsumer::instance().append(kbd);
      return true;
    }

    bool
    notifierfunc_unhookKeyboard(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLog("KeyRemap4MacBook notifierfunc_unhookKeyboard\n");

      IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, newService);
      ListHookedKeyboard::instance().terminate(kbd);
      ListHookedConsumer::instance().terminate(kbd);
      return true;
    }

    bool
    notifierfunc_hookPointing(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLog("KeyRemap4MacBook notifierfunc_hookPointing\n");

      IOHIPointing* pointing = OSDynamicCast(IOHIPointing, newService);
      return ListHookedPointing::instance().append(pointing);
    }

    bool
    notifierfunc_unhookPointing(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLog("KeyRemap4MacBook notifierfunc_unhookPointing\n");

      IOHIPointing* pointing = OSDynamicCast(IOHIPointing, newService);
      return ListHookedPointing::instance().terminate(pointing);
    }

    // ======================================================================
    void
    remap_KeyboardEventCallback(Params_KeyboardEventCallBack& params)
    {
      params.log();

      // ------------------------------------------------------------
      RemapParams remapParams(params);

      // ------------------------------------------------------------
      FlagStatus::set(params.key, params.flags);

      remap_core(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        params.flags = FlagStatus::makeFlags();
        KeyboardRepeat::set(params);
        RemapUtil::fireKey(params);
      }

      if (NumHeldDownKeys::iszero()) {
        NumHeldDownKeys::reset();
        cancelRepeat();
        EventWatcher::reset();
        FlagStatus::reset();
        ButtonStatus::reset();
        FireModifiers::fire(FlagStatus::makeFlags());
        PressDownKeys::clear();
      }
    }

    void
    remap_KeyboardSpecialEventCallback(Params_KeyboardSpecialEventCallback& params)
    {
      params.log();

      RemapConsumerParams remapParams(params);

      // ------------------------------------------------------------
      remap_consumer(remapParams);

      // ----------------------------------------
      if (! remapParams.isremapped) {
        params.flags = FlagStatus::makeFlags();
        KeyboardRepeat::set(params);
        RemapUtil::fireConsumer(params);
      }
    }

    void
    remap_RelativePointerEventCallback(Params_RelativePointerEventCallback& params)
    {
      params.log();

      RemapPointingParams_relative remapParams(params);

      remap_pointing_relative_core(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        FireRelativePointer::fire(ButtonStatus::makeButtons(), params.dx, params.dy);
      }
    }

    void
    remap_ScrollWheelEventCallback(Params_ScrollWheelEventCallback& params)
    {
      params.log();
      RemapUtil::fireScrollWheel(params);
    }
  }
}
<commit_msg>improve handling of NumHeldDownKeys::iszero() @ Core::remap_KeyboardEventCallback<commit_after>#include "base.hpp"
#include "ButtonStatus.hpp"
#include "Client.hpp"
#include "CommonData.hpp"
#include "Config.hpp"
#include "Core.hpp"
#include "RemapUtil.hpp"
#include "remap.hpp"
#include "util/CallBackWrapper.hpp"
#include "util/KeyboardRepeat.hpp"
#include "util/KeyEventInputQueue.hpp"
#include "util/ListHookedConsumer.hpp"
#include "util/ListHookedKeyboard.hpp"
#include "util/ListHookedPointing.hpp"
#include "util/NumHeldDownKeys.hpp"
#include "util/TimerWrapper.hpp"
#include "VirtualKey.hpp"

#include <sys/errno.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOTimerEventSource.h>

namespace org_pqrs_KeyRemap4MacBook {
  namespace Core {
    namespace {
      IOWorkLoop* workLoop = NULL;
      TimerWrapper timer_refresh;

      // ------------------------------------------------------------
      enum {
        REFRESH_DEVICE_INTERVAL = 3000,
      };

      void
      refreshHookedDevice(OSObject* owner, IOTimerEventSource* sender)
      {
        IOLockWrapper::ScopedLock lk(timer_refresh.getlock());

        ListHookedKeyboard::instance().refresh();
        ListHookedConsumer::instance().refresh();
        ListHookedPointing::instance().refresh();

        timer_refresh.setTimeoutMS(REFRESH_DEVICE_INTERVAL);
      }

      // ----------------------------------------
      void
      cancelRepeat(void)
      {
        if (config.debug_devel) {
          IOLog("KeyRemap4MacBook -Info- cancelRepeat\n");
        }

        KeyboardRepeat::cancel();
      }
    }

    void
    initialize(void)
    {
      sysctl_register();
      remapfunc_initialize();
      EventWatcher::initialize();
      PressDownKeys::initialize();
      FlagStatus::initialize();
      ButtonStatus::initialize();
      KeyRemap4MacBook_client::initialize();
      CommonData::initialize();
    }

    void
    terminate(void)
    {
      CommonData::terminate();
      KeyRemap4MacBook_client::terminate();
      EventWatcher::terminate();
      remapfunc_terminate();
      sysctl_unregister();
    }

    void
    start(void)
    {
      ListHookedKeyboard::instance().initialize();
      ListHookedConsumer::instance().initialize();
      ListHookedPointing::instance().initialize();

      workLoop = IOWorkLoop::workLoop();
      if (! workLoop) {
        IOLog("[KeyRemap4MacBook ERROR] IOWorkLoop::workLoop failed\n");
      } else {
        timer_refresh.initialize(workLoop, NULL, refreshHookedDevice);
        timer_refresh.setTimeoutMS(REFRESH_DEVICE_INTERVAL);

        KeyboardRepeat::initialize(*workLoop);
        KeyEventInputQueue::initialize(*workLoop);
        VirtualKey::initialize(*workLoop);
        EventOutputQueue::initialize(*workLoop);
      }
    }

    void
    stop(void)
    {
      timer_refresh.terminate();
      ListHookedKeyboard::instance().terminate();
      ListHookedConsumer::instance().terminate();
      ListHookedPointing::instance().terminate();
      KeyboardRepeat::terminate();
      KeyEventInputQueue::terminate();
      VirtualKey::terminate();
      EventOutputQueue::terminate();

      if (workLoop) {
        workLoop->release();
        workLoop = NULL;
      }
    }

    // ======================================================================
    bool
    notifierfunc_hookKeyboard(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLog("KeyRemap4MacBook notifierfunc_hookKeyboard\n");

      IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, newService);
      ListHookedKeyboard::instance().append(kbd);
      ListHookedConsumer::instance().append(kbd);
      return true;
    }

    bool
    notifierfunc_unhookKeyboard(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLog("KeyRemap4MacBook notifierfunc_unhookKeyboard\n");

      IOHIKeyboard* kbd = OSDynamicCast(IOHIKeyboard, newService);
      ListHookedKeyboard::instance().terminate(kbd);
      ListHookedConsumer::instance().terminate(kbd);
      return true;
    }

    bool
    notifierfunc_hookPointing(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLog("KeyRemap4MacBook notifierfunc_hookPointing\n");

      IOHIPointing* pointing = OSDynamicCast(IOHIPointing, newService);
      return ListHookedPointing::instance().append(pointing);
    }

    bool
    notifierfunc_unhookPointing(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLog("KeyRemap4MacBook notifierfunc_unhookPointing\n");

      IOHIPointing* pointing = OSDynamicCast(IOHIPointing, newService);
      return ListHookedPointing::instance().terminate(pointing);
    }

    // ======================================================================
    void
    remap_KeyboardEventCallback(Params_KeyboardEventCallBack& params)
    {
      params.log();

      // ------------------------------------------------------------
      RemapParams remapParams(params);

      // ------------------------------------------------------------
      FlagStatus::set(params.key, params.flags);

      remap_core(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        params.flags = FlagStatus::makeFlags();
        KeyboardRepeat::set(params);
        RemapUtil::fireKey(params);
      }

      if (NumHeldDownKeys::iszero()) {
        NumHeldDownKeys::reset();
        cancelRepeat();
        EventWatcher::reset();
        FlagStatus::reset();
        ButtonStatus::reset();
        FireModifiers::fire(FlagStatus::makeFlags());
        FireRelativePointer::fire();
        PressDownKeys::clear();
      }
    }

    void
    remap_KeyboardSpecialEventCallback(Params_KeyboardSpecialEventCallback& params)
    {
      params.log();

      RemapConsumerParams remapParams(params);

      // ------------------------------------------------------------
      remap_consumer(remapParams);

      // ----------------------------------------
      if (! remapParams.isremapped) {
        params.flags = FlagStatus::makeFlags();
        KeyboardRepeat::set(params);
        RemapUtil::fireConsumer(params);
      }
    }

    void
    remap_RelativePointerEventCallback(Params_RelativePointerEventCallback& params)
    {
      params.log();

      RemapPointingParams_relative remapParams(params);

      remap_pointing_relative_core(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        FireRelativePointer::fire(ButtonStatus::makeButtons(), params.dx, params.dy);
      }
    }

    void
    remap_ScrollWheelEventCallback(Params_ScrollWheelEventCallback& params)
    {
      params.log();
      RemapUtil::fireScrollWheel(params);
    }
  }
}
<|endoftext|>


<commit_before>#include "base.hpp"
#include "ButtonStatus.hpp"
#include "Client.hpp"
#include "CommonData.hpp"
#include "Config.hpp"
#include "Core.hpp"
#include "RemapClass.hpp"
#include "remap.hpp"
#include "util/CallBackWrapper.hpp"
#include "util/EventInputQueue.hpp"
#include "util/EventOutputQueue.hpp"
#include "util/EventWatcher.hpp"
#include "util/KeyboardRepeat.hpp"
#include "util/ListHookedConsumer.hpp"
#include "util/ListHookedKeyboard.hpp"
#include "util/ListHookedPointing.hpp"
#include "util/NumHeldDownKeys.hpp"
#include "util/PressDownKeys.hpp"
#include "util/TimerWrapper.hpp"
#include "RemapFunc/HoldingKeyToKey.hpp"
#include "RemapFunc/KeyOverlaidModifier.hpp"
#include "RemapFunc/PointingRelativeToScroll.hpp"
#include "VirtualKey.hpp"

#include <sys/errno.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOTimerEventSource.h>

namespace org_pqrs_KeyRemap4MacBook {
  namespace Core {
    namespace {
      IOWorkLoop* workLoop = NULL;
      TimerWrapper timer_refresh;

      // ------------------------------------------------------------
      enum {
        REFRESH_DEVICE_INTERVAL = 3000,
      };

      void
      refreshHookedDevice(OSObject* owner, IOTimerEventSource* sender)
      {
        IOLockWrapper::ScopedLock lk(timer_refresh.getlock());

        ListHookedKeyboard::instance().refresh_callback();
        ListHookedConsumer::instance().refresh_callback();
        ListHookedPointing::instance().refresh_callback();

        timer_refresh.setTimeoutMS(REFRESH_DEVICE_INTERVAL);
      }
    }

    void
    start(void)
    {
      CommonData::initialize();
      EventWatcher::initialize();
      PressDownKeys::initialize();
      FlagStatus::initialize();
      ButtonStatus::initialize();
      KeyRemap4MacBook_client::initialize();

      ListHookedKeyboard::instance().initialize();
      ListHookedConsumer::instance().initialize();
      ListHookedPointing::instance().initialize();

      workLoop = IOWorkLoop::workLoop();
      if (! workLoop) {
        IOLOG_ERROR("IOWorkLoop::workLoop failed\n");
      } else {
        KeyboardRepeat::initialize(*workLoop);
        EventInputQueue::initialize(*workLoop);
        VirtualKey::initialize(*workLoop);
        EventOutputQueue::initialize(*workLoop);
        RemapFunc::HoldingKeyToKey::static_initialize(*workLoop);
        RemapFunc::KeyOverlaidModifier::static_initialize(*workLoop);
        RemapFunc::PointingRelativeToScroll::static_initialize(*workLoop);
        ListHookedKeyboard::static_initialize(*workLoop);
        RemapClassManager::initialize(*workLoop);

        timer_refresh.initialize(workLoop, NULL, refreshHookedDevice);
        timer_refresh.setTimeoutMS(REFRESH_DEVICE_INTERVAL);
      }

      sysctl_register();
    }

    void
    stop(void)
    {
      {
        IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

        sysctl_unregister();

        timer_refresh.terminate();
        RemapClassManager::terminate();
        ListHookedKeyboard::instance().terminate();
        ListHookedConsumer::instance().terminate();
        ListHookedPointing::instance().terminate();
        KeyboardRepeat::terminate();
        EventInputQueue::terminate();
        VirtualKey::terminate();
        EventOutputQueue::terminate();
        RemapFunc::HoldingKeyToKey::static_terminate();
        RemapFunc::KeyOverlaidModifier::static_terminate();
        RemapFunc::PointingRelativeToScroll::static_terminate();
        ListHookedKeyboard::static_terminate();

        if (workLoop) {
          workLoop->release();
          workLoop = NULL;
        }

        KeyRemap4MacBook_client::terminate();
        EventWatcher::terminate();
        PressDownKeys::terminate();
      }
      CommonData::terminate();
    }

    // ======================================================================
    bool
    notifierfunc_hookKeyboard(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

      IOLOG_DEBUG("notifierfunc_hookKeyboard newService:%p\n", newService);

      IOHIDevice* device = OSDynamicCast(IOHIKeyboard, newService);
      if (! device) return false;

      ListHookedKeyboard::instance().push_back(new ListHookedKeyboard::Item(device));
      ListHookedConsumer::instance().push_back(new ListHookedConsumer::Item(device));
      return true;
    }

    bool
    notifierfunc_unhookKeyboard(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

      IOLOG_DEBUG("notifierfunc_unhookKeyboard newService:%p\n", newService);

      IOHIDevice* device = OSDynamicCast(IOHIKeyboard, newService);
      if (! device) return false;

      ListHookedKeyboard::instance().erase(device);
      ListHookedConsumer::instance().erase(device);
      return true;
    }

    bool
    notifierfunc_hookPointing(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

      IOLOG_DEBUG("notifierfunc_hookPointing newService:%p\n", newService);

      IOHIDevice* device = OSDynamicCast(IOHIPointing, newService);
      if (! device) return false;

      ListHookedPointing::instance().push_back(new ListHookedPointing::Item(device));
      return true;
    }

    bool
    notifierfunc_unhookPointing(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

      IOLOG_DEBUG("notifierfunc_unhookPointing newService:%p\n", newService);

      IOHIDevice* device = OSDynamicCast(IOHIPointing, newService);
      if (! device) return false;

      ListHookedPointing::instance().erase(device);
      return true;
    }

    // ======================================================================
    void
    remap_KeyboardEventCallback(Params_KeyboardEventCallBack& params)
    {
      params.log();

      // ------------------------------------------------------------
      RemapParams remapParams(params);

      // ------------------------------------------------------------
      FlagStatus::set(params.key, params.flags);

      RemapClassManager::remap_key(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(params.eventType, FlagStatus::makeFlags(), params.key,
                                                                                params.charCode, params.charSet, params.origCharCode, params.origCharSet,
                                                                                params.keyboardType, false));
        if (ptr) {
          KeyboardRepeat::set(*ptr);
          EventOutputQueue::FireKey::fire(*ptr);
        }
      }

      if (NumHeldDownKeys::iszero()) {
        NumHeldDownKeys::reset();
        KeyboardRepeat::cancel();
        EventWatcher::reset();
        FlagStatus::reset();
        ButtonStatus::reset();
        VirtualKey::reset();
        EventOutputQueue::FireModifiers::fire(FlagStatus::makeFlags());
        EventOutputQueue::FireRelativePointer::fire();
        PressDownKeys::clear();
      }

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }

    void
    remap_KeyboardSpecialEventCallback(Params_KeyboardSpecialEventCallback& params)
    {
      params.log();

      RemapConsumerParams remapParams(params);

      // ------------------------------------------------------------
      RemapClassManager::remap_consumer(remapParams);

      // ----------------------------------------
      if (! remapParams.isremapped) {
        Params_KeyboardSpecialEventCallback::auto_ptr ptr(Params_KeyboardSpecialEventCallback::alloc(params.eventType, FlagStatus::makeFlags(), params.key,
                                                                                params.flavor, params.guid, false));
        if (ptr) {
          KeyboardRepeat::set(*ptr);
          EventOutputQueue::FireConsumer::fire(*ptr);
        }
      }

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }

    void
    remap_RelativePointerEventCallback(Params_RelativePointerEventCallback& params)
    {
      params.log();

      RemapPointingParams_relative remapParams(params);

      ButtonStatus::set(params.ex_button, params.ex_isbuttondown);

      RemapClassManager::remap_pointing(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        EventOutputQueue::FireRelativePointer::fire(ButtonStatus::makeButtons(), params.dx, params.dy);
      }
    }

    void
    remap_ScrollWheelEventCallback(Params_ScrollWheelEventCallback& params)
    {
      params.log();
      EventOutputQueue::FireScrollWheel::fire(params);

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }
  }
}
<commit_msg>improve Core.cpp<commit_after>#include "base.hpp"
#include "ButtonStatus.hpp"
#include "Client.hpp"
#include "CommonData.hpp"
#include "Config.hpp"
#include "Core.hpp"
#include "RemapClass.hpp"
#include "remap.hpp"
#include "util/CallBackWrapper.hpp"
#include "util/EventInputQueue.hpp"
#include "util/EventOutputQueue.hpp"
#include "util/EventWatcher.hpp"
#include "util/KeyboardRepeat.hpp"
#include "util/ListHookedConsumer.hpp"
#include "util/ListHookedKeyboard.hpp"
#include "util/ListHookedPointing.hpp"
#include "util/NumHeldDownKeys.hpp"
#include "util/PressDownKeys.hpp"
#include "util/TimerWrapper.hpp"
#include "RemapFunc/HoldingKeyToKey.hpp"
#include "RemapFunc/KeyOverlaidModifier.hpp"
#include "RemapFunc/PointingRelativeToScroll.hpp"
#include "VirtualKey.hpp"

#include <sys/errno.h>
#include <IOKit/IOWorkLoop.h>
#include <IOKit/IOTimerEventSource.h>

namespace org_pqrs_KeyRemap4MacBook {
  namespace Core {
    namespace {
      IOWorkLoop* workLoop = NULL;
      TimerWrapper timer_refresh;

      // ------------------------------------------------------------
      enum {
        REFRESH_DEVICE_INTERVAL = 3000,
      };

      void
      refreshHookedDevice(OSObject* owner, IOTimerEventSource* sender)
      {
        IOLockWrapper::ScopedLock lk(timer_refresh.getlock());

        ListHookedKeyboard::instance().refresh_callback();
        ListHookedConsumer::instance().refresh_callback();
        ListHookedPointing::instance().refresh_callback();

        timer_refresh.setTimeoutMS(REFRESH_DEVICE_INTERVAL);
      }
    }

    void
    start(void)
    {
      CommonData::initialize();
      EventWatcher::initialize();
      PressDownKeys::initialize();
      FlagStatus::initialize();
      ButtonStatus::initialize();
      KeyRemap4MacBook_client::initialize();

      ListHookedKeyboard::instance().initialize();
      ListHookedConsumer::instance().initialize();
      ListHookedPointing::instance().initialize();

      workLoop = IOWorkLoop::workLoop();
      if (! workLoop) {
        IOLOG_ERROR("IOWorkLoop::workLoop failed\n");
      } else {
        KeyboardRepeat::initialize(*workLoop);
        EventInputQueue::initialize(*workLoop);
        VirtualKey::initialize(*workLoop);
        EventOutputQueue::initialize(*workLoop);
        RemapFunc::HoldingKeyToKey::static_initialize(*workLoop);
        RemapFunc::KeyOverlaidModifier::static_initialize(*workLoop);
        RemapFunc::PointingRelativeToScroll::static_initialize(*workLoop);
        ListHookedKeyboard::static_initialize(*workLoop);
        RemapClassManager::initialize(*workLoop);

        timer_refresh.initialize(workLoop, NULL, refreshHookedDevice);
        timer_refresh.setTimeoutMS(REFRESH_DEVICE_INTERVAL);
      }

      sysctl_register();
    }

    void
    stop(void)
    {
      // At first, restoreEventAction for all devices.
      {
        IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

        timer_refresh.terminate();
        ListHookedKeyboard::instance().terminate();
        ListHookedConsumer::instance().terminate();
        ListHookedPointing::instance().terminate();
      }

      // roughly sleep: waiting for finishing queued device events.
      IOSleep(200);

      // call terminate
      {
        IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

        sysctl_unregister();

        RemapClassManager::terminate();
        KeyboardRepeat::terminate();
        EventInputQueue::terminate();
        VirtualKey::terminate();
        EventOutputQueue::terminate();
        RemapFunc::HoldingKeyToKey::static_terminate();
        RemapFunc::KeyOverlaidModifier::static_terminate();
        RemapFunc::PointingRelativeToScroll::static_terminate();
        ListHookedKeyboard::static_terminate();

        if (workLoop) {
          workLoop->release();
          workLoop = NULL;
        }

        KeyRemap4MacBook_client::terminate();
        EventWatcher::terminate();
        PressDownKeys::terminate();
      }

      CommonData::terminate();
    }

    // ======================================================================
    bool
    notifierfunc_hookKeyboard(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

      IOLOG_DEBUG("notifierfunc_hookKeyboard newService:%p\n", newService);

      IOHIDevice* device = OSDynamicCast(IOHIKeyboard, newService);
      if (! device) return false;

      ListHookedKeyboard::instance().push_back(new ListHookedKeyboard::Item(device));
      ListHookedConsumer::instance().push_back(new ListHookedConsumer::Item(device));
      return true;
    }

    bool
    notifierfunc_unhookKeyboard(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

      IOLOG_DEBUG("notifierfunc_unhookKeyboard newService:%p\n", newService);

      IOHIDevice* device = OSDynamicCast(IOHIKeyboard, newService);
      if (! device) return false;

      ListHookedKeyboard::instance().erase(device);
      ListHookedConsumer::instance().erase(device);
      return true;
    }

    bool
    notifierfunc_hookPointing(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

      IOLOG_DEBUG("notifierfunc_hookPointing newService:%p\n", newService);

      IOHIDevice* device = OSDynamicCast(IOHIPointing, newService);
      if (! device) return false;

      ListHookedPointing::instance().push_back(new ListHookedPointing::Item(device));
      return true;
    }

    bool
    notifierfunc_unhookPointing(void* target, void* refCon, IOService* newService, IONotifier* notifier)
    {
      IOLockWrapper::ScopedLock lk_eventlock(CommonData::getEventLock());

      IOLOG_DEBUG("notifierfunc_unhookPointing newService:%p\n", newService);

      IOHIDevice* device = OSDynamicCast(IOHIPointing, newService);
      if (! device) return false;

      ListHookedPointing::instance().erase(device);
      return true;
    }

    // ======================================================================
    void
    remap_KeyboardEventCallback(Params_KeyboardEventCallBack& params)
    {
      params.log();

      // ------------------------------------------------------------
      RemapParams remapParams(params);

      // ------------------------------------------------------------
      FlagStatus::set(params.key, params.flags);

      RemapClassManager::remap_key(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        Params_KeyboardEventCallBack::auto_ptr ptr(Params_KeyboardEventCallBack::alloc(params.eventType, FlagStatus::makeFlags(), params.key,
                                                                                params.charCode, params.charSet, params.origCharCode, params.origCharSet,
                                                                                params.keyboardType, false));
        if (ptr) {
          KeyboardRepeat::set(*ptr);
          EventOutputQueue::FireKey::fire(*ptr);
        }
      }

      if (NumHeldDownKeys::iszero()) {
        NumHeldDownKeys::reset();
        KeyboardRepeat::cancel();
        EventWatcher::reset();
        FlagStatus::reset();
        ButtonStatus::reset();
        VirtualKey::reset();
        EventOutputQueue::FireModifiers::fire(FlagStatus::makeFlags());
        EventOutputQueue::FireRelativePointer::fire();
        PressDownKeys::clear();
      }

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }

    void
    remap_KeyboardSpecialEventCallback(Params_KeyboardSpecialEventCallback& params)
    {
      params.log();

      RemapConsumerParams remapParams(params);

      // ------------------------------------------------------------
      RemapClassManager::remap_consumer(remapParams);

      // ----------------------------------------
      if (! remapParams.isremapped) {
        Params_KeyboardSpecialEventCallback::auto_ptr ptr(Params_KeyboardSpecialEventCallback::alloc(params.eventType, FlagStatus::makeFlags(), params.key,
                                                                                params.flavor, params.guid, false));
        if (ptr) {
          KeyboardRepeat::set(*ptr);
          EventOutputQueue::FireConsumer::fire(*ptr);
        }
      }

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }

    void
    remap_RelativePointerEventCallback(Params_RelativePointerEventCallback& params)
    {
      params.log();

      RemapPointingParams_relative remapParams(params);

      ButtonStatus::set(params.ex_button, params.ex_isbuttondown);

      RemapClassManager::remap_pointing(remapParams);

      // ------------------------------------------------------------
      if (! remapParams.isremapped) {
        EventOutputQueue::FireRelativePointer::fire(ButtonStatus::makeButtons(), params.dx, params.dy);
      }
    }

    void
    remap_ScrollWheelEventCallback(Params_ScrollWheelEventCallback& params)
    {
      params.log();
      EventOutputQueue::FireScrollWheel::fire(params);

      RemapFunc::PointingRelativeToScroll::cancelScroll();
    }
  }
}
<|endoftext|>


<commit_before>#ifndef ALEPH_TOPOLOGY_IO_GML_HH__
#define ALEPH_TOPOLOGY_IO_GML_HH__

#include <fstream>
#include <map>
#include <set>
#include <regex>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>

#include <aleph/utilities/String.hh>

namespace aleph
{

namespace topology
{

namespace io
{

/**
  @class GMLReader
  @brief Parses files in GML (Graph Modeling Language) format

  This is a simple reader for graphs in GML format. It suports a basic
  subset of the GML specification, viz. the specification of different
  attributes for nodes, as well as weight specifications for edges.

  Currently, the following attributes will be read:

  - \c id (for nodes)
  - \c label (for nodes)
  - \c source (for edges)
  - \c target (for edges)
  - \c weight (for edges)
*/

class GMLReader
{
public:

  /**
    Reads a simplicial complex from a file, using the default maximum
    functor for weight assignment. If you want to change the functor,
    please refer to the overloaded variant of this method.

    @param filename Input filename
    @param  K       Simplicial complex

    @see operator()( const std::string&, SimplicialComplex& )
  */

  template <class SimplicialComplex> void operator()( const std::string& filename, SimplicialComplex& K )
  {
    using Simplex    = typename SimplicialComplex::ValueType;
    using DataType   = typename Simplex::DataType;

    this->operator()( filename, K, [] ( DataType a, DataType b ) { return std::max(a,b); } );
  }

  /**
    Reads a simplicial complex from a file while supporting arbitrary
    functors for weight assignment. The functor needs to support this
    interface:

    \code{.cpp}
    using SimplexType = typename SimplicialComplex::ValueType;
    using DataType    = typename Simplex::DataType;

    DataType Functor::operator()( DataType a, DataType b )
    {
      // Do something with a and b. Typically, this would be calculating
      // either the minimum or the maximum...
      return std::max(a, b);
    }
    \endcode

    Please refer to the documentation of SimplicialComplexReader::operator()( const std::string& SimplicialComplex&, Functor )
    for more details.
  */

  template <class SimplicialComplex, class Functor> void operator()( const std::string& filename, SimplicialComplex& K, Functor f )
  {
    std::ifstream in( filename );
    if( !in )
      throw std::runtime_error( "Unable to read input file" );

    this->operator()( in, K, f );
  }

  /** @overload operator()( const std::string&, SimplicialComplex& ) */
  template <class SimplicialComplex> void operator()( std::ifstream& in, SimplicialComplex& K )
  {
    using Simplex    = typename SimplicialComplex::ValueType;
    using DataType   = typename Simplex::DataType;

    this->operator()( in, K, [] ( DataType a, DataType b ) { return std::max(a,b); } );
  }

  /** @overload operator()( const std::string&, SimplicialComplex&, SimplicialComplex&, Functor ) */
  template <class SimplicialComplex, class Functor> void operator()( std::ifstream& in, SimplicialComplex& K, Functor f )
  {
    _nodes.clear();
    _edges.clear();

    using namespace aleph::utilities;

    using Simplex           = typename SimplicialComplex::ValueType;
    using DataType          = typename Simplex::DataType;
    using VertexType        = typename Simplex::VertexType;

    std::string line;
    std::set<std::string> levels     = { "graph", "node", "edge" };
    std::set<std::string> attributes = { "id", "label", "source", "target", "value", "weight" };

    auto isLevel = [&levels] ( const std::string& name )
    {
      return levels.find( name ) != levels.end();
    };

    auto isAttribute = [&attributes] ( const std::string& name )
    {
      return attributes.find( name ) != attributes.end();
    };

    // Specifies the current level the parser is in. May be either one
    // of the known levels above.
    std::stack<std::string> currentLevel;

    // Last level that was read by the parser. If an open bracket '[' is
    // identified, this will become the current level.
    std::string lastLevel;

    Graph graph;
    Node node;
    Edge edge;

    std::regex reAttribute = std::regex( "([_\\|[:alpha:]]+)[[:space:]]*.*" );
    std::regex reKeyValue  = std::regex( "([_\\|[:alpha:]]+)[[:space:]]+([[:alnum:]\\.]+)" );
    std::regex reLabel     = std::regex( "(label)[[:space:]]+\"([^\"]+)\"" );

    while( std::getline( in, line ) )
    {
      line        = trim( line );
      auto tokens = split( line );

      // Skip comment lines. This is somewhat wasteful because I am
      // splitting the complete string even though I merely need to
      // know the first token.
      if( tokens.empty() == false && ( tokens.front() == "comment" || tokens.front() == "Creator" ) )
        continue;

      // A new level may also be opened "inline" by specifying a name
      // and the opening bracket at the same time.
      bool newLevel = isLevel( line ) || ( tokens.size() == 2 && tokens.back() == "[" && isLevel( tokens.front() ) );

      // Detecting a new level
      if( newLevel )
      {
        auto level = tokens.size() == 2 ? tokens.front() : line;

        if( lastLevel.empty() )
          lastLevel = level;
        else
          throw std::runtime_error( "Encountered incorrectly-nested levels" );

        // Only store the level directly when it has been specified
        // inline
        if( tokens.size() == 2 )
        {
          currentLevel.push( level );
          lastLevel = "";
        }
      }

      // Opening a new level
      else if( line == "[" )
      {
        currentLevel.push( lastLevel );
        lastLevel = "";
      }

      // Closing a new level
      else if( line == "]" )
      {
        if( currentLevel.top() == "node" )
          _nodes.push_back( node );
        else if( currentLevel.top() == "edge" )
          _edges.push_back( edge );

        // Reset node and edge data structure to fill them again once
        // a new level is being encountered.
        node = {};
        edge = {};

        currentLevel.pop();
      }

      // Check for attributes
      else
      {
        if( currentLevel.empty() )
          throw std::runtime_error( "Expected a non-empty current level" );

        std::smatch matches;

        auto* dict = currentLevel.top() == "node" ? &node.dict
                                                  : currentLevel.top() == "edge" ? &edge.dict
                                                                                : currentLevel.top() == "graph" ? &graph.dict
                                                                                : throw std::runtime_error( "Current level is unknown" );

        if( std::regex_match( line, matches, reAttribute ) )
        {
          auto name = matches[1];
          if( isAttribute( name ) )
          {
            // Special matching for labels
            if( name == "label" )
              std::regex_match( line, matches, reLabel );

            // Regular matching for all other attributes
            else
              std::regex_match( line, matches, reKeyValue );

            auto value = matches[2];

            if( name == "id" )
              node.id = value;
            else if( name == "source" )
              edge.source = value;
            else if( name == "target" )
              edge.target = value;

            // Just add it to the dictionary of optional values
            else
             dict->operator[]( name ) = value;
          }

          // Attempt key--value matching for the unknown attribute and
          // store it in the dictionary.
          else
          {
            std::regex_match( line, matches, reKeyValue );

            if( matches.size() >= 3 )
            {
              auto value               = matches[2];
              dict->operator[]( name ) = value;
            }
          }
        }
      }
    }

    // Creates nodes (vertices) ----------------------------------------

    std::set<std::string> nodeIDs;

    for( auto&& node : _nodes )
    {
      auto pair = nodeIDs.insert( node.id );
      if( !pair.second )
        throw std::runtime_error( "Duplicate node id '" + node.id + "'" );
    }

    std::vector<Simplex> simplices;
    simplices.reserve( _nodes.size() + _edges.size() );

    // Maps an ID directly to its corresponding simplex in order to
    // facilitate the assignment of weights.
    std::unordered_map<VertexType, Simplex> id_to_simplex;

    for( auto&& node : _nodes )
    {
      auto id = static_cast<VertexType>( getID( nodeIDs, node.id ) );

      if( node.dict.find( "weight" ) != node.dict.end() )
        simplices.push_back( Simplex( id, convert<DataType>( node.dict.at( "weight" ) ) ) );
      else if( node.dict.find( "value" ) != node.dict.end() )
        simplices.push_back( Simplex( id, convert<DataType>( node.dict.at( "value" ) ) ) );
      else
        simplices.push_back( Simplex( id ) );

      id_to_simplex[id] = simplices.back();
    }

    // Create edges ----------------------------------------------------

    for( auto&& edge : _edges )
    {
      auto u = static_cast<VertexType>( getID( nodeIDs, edge.source ) );
      auto v = static_cast<VertexType>( getID( nodeIDs, edge.target ) );

      // No optional data attached; need to create weight based on node
      // weights, if those are available.
      if( edge.dict.find( "weight" ) == edge.dict.end() && edge.dict.find( "value" ) == edge.dict.end() )
      {
        auto uSimplex = id_to_simplex.at(u);
        auto vSimplex = id_to_simplex.at(v);

        simplices.push_back( Simplex( {u,v}, f( uSimplex.data(), vSimplex.data() ) ) );
      }

      // Use converted weight
      else if( edge.dict.find( "weight" ) != edge.dict.end() )
        simplices.push_back( Simplex( {u,v}, convert<DataType>( edge.dict.at( "weight" ) ) ) );
      else if( edge.dict.find( "value" ) != edge.dict.end() )
        simplices.push_back( Simplex( {u,v}, convert<DataType>( edge.dict.at( "value" ) ) ) );
    }

    _graph = graph;
    K      = SimplicialComplex( simplices.begin(), simplices.end() );
  }

  /**
    Retrieves a map of attribute values for each node. The attributes
    will be assigned to the node ID. Empty attributes signify that no
    such information is available.
  */

  std::map<std::string, std::string> getNodeAttribute( const std::string& attribute ) const
  {
    std::map<std::string, std::string> map;

    for( auto&& node : _nodes )
    {
      if( node.dict.find( attribute ) == node.dict.end() )
        map[ node.id ] = std::string();
      else
        map[ node.id ] = node.dict.at( attribute );
    }

    return map;
  }

  /**
    Returns a map that maps a node ID to an index. It corresponds
    to the order in which node IDs were allocated. It is *always*
    zero-indexed.
  */

  template <class VertexType> std::map<std::string, VertexType> id_to_index() const noexcept
  {
    std::set<std::string> nodeIDs;
    for( auto&& node : _nodes )
      nodeIDs.insert( node.id );

    std::map<std::string, VertexType> result;

    for( auto&& node : _nodes )
      result[ node.id ] = static_cast<VertexType>( getID( nodeIDs, node.id ) );

    return result;
  }

private:

  /**
    Auxiliary function for creating a numerical ID out of a parsed ID.
    In case non-numerical IDs are being used in the source file, this
    function ensures that internal IDs *always* start with a zero. If,
    however, numerical IDs are being used, they are converted as-is.
  */

  static std::size_t getID( const std::set<std::string>& ids, const std::string& id )
  {
    // Try to be smart: If the node ID can be converted into the
    // vertex type, we use the converted number instead.
    try
    {
      auto convertedID = std::stoll( id );
      return static_cast<std::size_t>( convertedID );
    }
    catch( std::out_of_range& e )
    {
      throw;
    }
    catch( std::invalid_argument& )
    {
      return static_cast<std::size_t>( std::distance( ids.begin(), ids.find( id ) ) );
    }
  };

  /** Describes a parsed graph along with all of its attributes */
  struct Graph
  {
    std::map<std::string, std::string> dict; // all remaining attributes
  };

  /** Describes a parsed node along with all of its attributes */
  struct Node
  {
    std::string id;

    std::map<std::string, std::string> dict; // all remaining attributes
  };

  /** Describes a parsed edge along with all of its attributes */
  struct Edge
  {
    std::string source;
    std::string target;

    std::map<std::string, std::string> dict; // all remaining attributes
  };

  // Local storage -----------------------------------------------------
  //
  // Variables in this section contain the result of the last parsing
  // process. This is useful when clients are querying attributes.

  Graph _graph;

  std::vector<Node> _nodes;
  std::vector<Edge> _edges;
};

/**
  @class GMLWriter
  @brief Writes files in GML (Graph Modeling Language) format

  This is a simple writer for graphs in GML format. It suports a basic
  subset of the GML specification, viz. the specification of different
  attributes for nodes, as well as weight specifications for edges.

  Given a simplicial complex, it will store it as a weighted graph.

  Currently, the following attributes will be written:

  * \c id (for nodes)
  * \c source (for edges)
  * \c target (for edges)
  * \c weight (for edges)
*/

class GMLWriter
{
public:

  template <class SimplicialComplex> void operator()( const std::string& filename, SimplicialComplex& K )
  {
    std::ofstream out( filename );
    if( !out )
      throw std::runtime_error( "Unable to open output file" );

    this->operator()( out, K );
  }

  template <class SimplicialComplex> void operator()( std::ostream& out, SimplicialComplex& K )
  {
    std::ostringstream streamNodes;
    std::ostringstream streamEdges;

    for( auto&& simplex : K )
    {
      if( simplex.dimension() == 0 )
      {
        streamNodes << "  node [\n"
                    << "    id " << *simplex.begin() << "\n"
                    << "  ]\n";
      }
      else if( simplex.dimension() == 1 )
      {
        auto u = *( simplex.begin()     );
        auto v = *( simplex.begin() + 1 );

        streamEdges << "  edge [\n"
                    << "    source " << u << "\n"
                    << "    target " << v << "\n"
                    << "    weight " << simplex.data() << "\n"
                    << "  ]\n";
      }
    }

    out << "graph [\n"
        << "  directed 0\n"
        << streamNodes.str() << "\n"
        << streamEdges.str() << "\n"
        << "]\n";
  }
};

} // namespace io

} // namespace topology

} // namespace aleph

#endif
<commit_msg>Added code to query available vertex and edge attributes<commit_after>#ifndef ALEPH_TOPOLOGY_IO_GML_HH__
#define ALEPH_TOPOLOGY_IO_GML_HH__

#include <fstream>
#include <map>
#include <set>
#include <regex>
#include <sstream>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>

#include <aleph/utilities/String.hh>

namespace aleph
{

namespace topology
{

namespace io
{

/**
  @class GMLReader
  @brief Parses files in GML (Graph Modeling Language) format

  This is a simple reader for graphs in GML format. It suports a basic
  subset of the GML specification, viz. the specification of different
  attributes for nodes, as well as weight specifications for edges.

  Currently, the following attributes will be read:

  - \c id (for nodes)
  - \c label (for nodes)
  - \c source (for edges)
  - \c target (for edges)
  - \c weight (for edges)
*/

class GMLReader
{
public:

  /**
    Reads a simplicial complex from a file, using the default maximum
    functor for weight assignment. If you want to change the functor,
    please refer to the overloaded variant of this method.

    @param filename Input filename
    @param  K       Simplicial complex

    @see operator()( const std::string&, SimplicialComplex& )
  */

  template <class SimplicialComplex> void operator()( const std::string& filename, SimplicialComplex& K )
  {
    using Simplex    = typename SimplicialComplex::ValueType;
    using DataType   = typename Simplex::DataType;

    this->operator()( filename, K, [] ( DataType a, DataType b ) { return std::max(a,b); } );
  }

  /**
    Reads a simplicial complex from a file while supporting arbitrary
    functors for weight assignment. The functor needs to support this
    interface:

    \code{.cpp}
    using SimplexType = typename SimplicialComplex::ValueType;
    using DataType    = typename Simplex::DataType;

    DataType Functor::operator()( DataType a, DataType b )
    {
      // Do something with a and b. Typically, this would be calculating
      // either the minimum or the maximum...
      return std::max(a, b);
    }
    \endcode

    Please refer to the documentation of SimplicialComplexReader::operator()( const std::string& SimplicialComplex&, Functor )
    for more details.
  */

  template <class SimplicialComplex, class Functor> void operator()( const std::string& filename, SimplicialComplex& K, Functor f )
  {
    std::ifstream in( filename );
    if( !in )
      throw std::runtime_error( "Unable to read input file" );

    this->operator()( in, K, f );
  }

  /** @overload operator()( const std::string&, SimplicialComplex& ) */
  template <class SimplicialComplex> void operator()( std::ifstream& in, SimplicialComplex& K )
  {
    using Simplex    = typename SimplicialComplex::ValueType;
    using DataType   = typename Simplex::DataType;

    this->operator()( in, K, [] ( DataType a, DataType b ) { return std::max(a,b); } );
  }

  /** @overload operator()( const std::string&, SimplicialComplex&, SimplicialComplex&, Functor ) */
  template <class SimplicialComplex, class Functor> void operator()( std::ifstream& in, SimplicialComplex& K, Functor f )
  {
    _nodes.clear();
    _edges.clear();

    using namespace aleph::utilities;

    using Simplex           = typename SimplicialComplex::ValueType;
    using DataType          = typename Simplex::DataType;
    using VertexType        = typename Simplex::VertexType;

    std::string line;
    std::set<std::string> levels     = { "graph", "node", "edge" };
    std::set<std::string> attributes = { "id", "label", "source", "target", "value", "weight" };

    auto isLevel = [&levels] ( const std::string& name )
    {
      return levels.find( name ) != levels.end();
    };

    auto isAttribute = [&attributes] ( const std::string& name )
    {
      return attributes.find( name ) != attributes.end();
    };

    // Specifies the current level the parser is in. May be either one
    // of the known levels above.
    std::stack<std::string> currentLevel;

    // Last level that was read by the parser. If an open bracket '[' is
    // identified, this will become the current level.
    std::string lastLevel;

    Graph graph;
    Node node;
    Edge edge;

    std::regex reAttribute = std::regex( "([_\\|[:alpha:]]+)[[:space:]]*.*" );
    std::regex reKeyValue  = std::regex( "([_\\|[:alpha:]]+)[[:space:]]+([[:alnum:]\\.]+)" );
    std::regex reLabel     = std::regex( "(label)[[:space:]]+\"([^\"]+)\"" );

    while( std::getline( in, line ) )
    {
      line        = trim( line );
      auto tokens = split( line );

      // Skip comment lines. This is somewhat wasteful because I am
      // splitting the complete string even though I merely need to
      // know the first token.
      if( tokens.empty() == false && ( tokens.front() == "comment" || tokens.front() == "Creator" ) )
        continue;

      // A new level may also be opened "inline" by specifying a name
      // and the opening bracket at the same time.
      bool newLevel = isLevel( line ) || ( tokens.size() == 2 && tokens.back() == "[" && isLevel( tokens.front() ) );

      // Detecting a new level
      if( newLevel )
      {
        auto level = tokens.size() == 2 ? tokens.front() : line;

        if( lastLevel.empty() )
          lastLevel = level;
        else
          throw std::runtime_error( "Encountered incorrectly-nested levels" );

        // Only store the level directly when it has been specified
        // inline
        if( tokens.size() == 2 )
        {
          currentLevel.push( level );
          lastLevel = "";
        }
      }

      // Opening a new level
      else if( line == "[" )
      {
        currentLevel.push( lastLevel );
        lastLevel = "";
      }

      // Closing a new level
      else if( line == "]" )
      {
        if( currentLevel.top() == "node" )
          _nodes.push_back( node );
        else if( currentLevel.top() == "edge" )
          _edges.push_back( edge );

        // Reset node and edge data structure to fill them again once
        // a new level is being encountered.
        node = {};
        edge = {};

        currentLevel.pop();
      }

      // Check for attributes
      else
      {
        if( currentLevel.empty() )
          throw std::runtime_error( "Expected a non-empty current level" );

        std::smatch matches;

        auto* dict = currentLevel.top() == "node" ? &node.dict
                                                  : currentLevel.top() == "edge" ? &edge.dict
                                                                                : currentLevel.top() == "graph" ? &graph.dict
                                                                                : throw std::runtime_error( "Current level is unknown" );

        if( std::regex_match( line, matches, reAttribute ) )
        {
          auto name = matches[1];
          if( isAttribute( name ) )
          {
            // Special matching for labels
            if( name == "label" )
              std::regex_match( line, matches, reLabel );

            // Regular matching for all other attributes
            else
              std::regex_match( line, matches, reKeyValue );

            auto value = matches[2];

            if( name == "id" )
              node.id = value;
            else if( name == "source" )
              edge.source = value;
            else if( name == "target" )
              edge.target = value;

            // Just add it to the dictionary of optional values
            else
             dict->operator[]( name ) = value;
          }

          // Attempt key--value matching for the unknown attribute and
          // store it in the dictionary.
          else
          {
            std::regex_match( line, matches, reKeyValue );

            if( matches.size() >= 3 )
            {
              auto value               = matches[2];
              dict->operator[]( name ) = value;
            }
          }
        }
      }
    }

    // Creates nodes (vertices) ----------------------------------------

    std::set<std::string> nodeIDs;

    for( auto&& node : _nodes )
    {
      auto pair = nodeIDs.insert( node.id );
      if( !pair.second )
        throw std::runtime_error( "Duplicate node id '" + node.id + "'" );
    }

    std::vector<Simplex> simplices;
    simplices.reserve( _nodes.size() + _edges.size() );

    // Maps an ID directly to its corresponding simplex in order to
    // facilitate the assignment of weights.
    std::unordered_map<VertexType, Simplex> id_to_simplex;

    for( auto&& node : _nodes )
    {
      auto id = static_cast<VertexType>( getID( nodeIDs, node.id ) );

      if( node.dict.find( "weight" ) != node.dict.end() )
        simplices.push_back( Simplex( id, convert<DataType>( node.dict.at( "weight" ) ) ) );
      else if( node.dict.find( "value" ) != node.dict.end() )
        simplices.push_back( Simplex( id, convert<DataType>( node.dict.at( "value" ) ) ) );
      else
        simplices.push_back( Simplex( id ) );

      id_to_simplex[id] = simplices.back();
    }

    // Create edges ----------------------------------------------------

    for( auto&& edge : _edges )
    {
      auto u = static_cast<VertexType>( getID( nodeIDs, edge.source ) );
      auto v = static_cast<VertexType>( getID( nodeIDs, edge.target ) );

      // No optional data attached; need to create weight based on node
      // weights, if those are available.
      if( edge.dict.find( "weight" ) == edge.dict.end() && edge.dict.find( "value" ) == edge.dict.end() )
      {
        auto uSimplex = id_to_simplex.at(u);
        auto vSimplex = id_to_simplex.at(v);

        simplices.push_back( Simplex( {u,v}, f( uSimplex.data(), vSimplex.data() ) ) );
      }

      // Use converted weight
      else if( edge.dict.find( "weight" ) != edge.dict.end() )
        simplices.push_back( Simplex( {u,v}, convert<DataType>( edge.dict.at( "weight" ) ) ) );
      else if( edge.dict.find( "value" ) != edge.dict.end() )
        simplices.push_back( Simplex( {u,v}, convert<DataType>( edge.dict.at( "value" ) ) ) );
    }

    _graph = graph;
    K      = SimplicialComplex( simplices.begin(), simplices.end() );
  }

  /** Retrieves attribute names for the node attributes. */
  std::vector<std::string> getNodeAttributeNames() const
  {
    std::set<std::string> names;

    for( auto&& node : _nodes )
    {
      for( auto&& pair : node.dict )
        names.insert( pair.first );
    }

    return { names.begin(), names.end() };
  }

  /** Retrieves attribute names for the edge attributes */
  std::vector<std::string> getEdgeAttributeNames() const
  {
    std::set<std::string> names;

    for( auto&& edge : _edges )
    {
      for( auto&& pair : edge.dict )
        names.insert( pair.first );
    }

    return { names.begin(), names.end() };
  }

  /**
    Retrieves a map of attribute values for each node. The attributes
    will be assigned to the node ID. Empty attributes signify that no
    such information is available.
  */

  std::map<std::string, std::string> getNodeAttribute( const std::string& attribute ) const
  {
    std::map<std::string, std::string> map;

    for( auto&& node : _nodes )
    {
      if( node.dict.find( attribute ) == node.dict.end() )
        map[ node.id ] = std::string();
      else
        map[ node.id ] = node.dict.at( attribute );
    }

    return map;
  }

  /**
    Returns a map that maps a node ID to an index. It corresponds
    to the order in which node IDs were allocated. It is *always*
    zero-indexed.
  */

  template <class VertexType> std::map<std::string, VertexType> id_to_index() const noexcept
  {
    std::set<std::string> nodeIDs;
    for( auto&& node : _nodes )
      nodeIDs.insert( node.id );

    std::map<std::string, VertexType> result;

    for( auto&& node : _nodes )
      result[ node.id ] = static_cast<VertexType>( getID( nodeIDs, node.id ) );

    return result;
  }

private:

  /**
    Auxiliary function for creating a numerical ID out of a parsed ID.
    In case non-numerical IDs are being used in the source file, this
    function ensures that internal IDs *always* start with a zero. If,
    however, numerical IDs are being used, they are converted as-is.
  */

  static std::size_t getID( const std::set<std::string>& ids, const std::string& id )
  {
    // Try to be smart: If the node ID can be converted into the
    // vertex type, we use the converted number instead.
    try
    {
      auto convertedID = std::stoll( id );
      return static_cast<std::size_t>( convertedID );
    }
    catch( std::out_of_range& e )
    {
      throw;
    }
    catch( std::invalid_argument& )
    {
      return static_cast<std::size_t>( std::distance( ids.begin(), ids.find( id ) ) );
    }
  };

  /** Describes a parsed graph along with all of its attributes */
  struct Graph
  {
    std::map<std::string, std::string> dict; // all remaining attributes
  };

  /** Describes a parsed node along with all of its attributes */
  struct Node
  {
    std::string id;

    std::map<std::string, std::string> dict; // all remaining attributes
  };

  /** Describes a parsed edge along with all of its attributes */
  struct Edge
  {
    std::string source;
    std::string target;

    std::map<std::string, std::string> dict; // all remaining attributes
  };

  // Local storage -----------------------------------------------------
  //
  // Variables in this section contain the result of the last parsing
  // process. This is useful when clients are querying attributes.

  Graph _graph;

  std::vector<Node> _nodes;
  std::vector<Edge> _edges;
};

/**
  @class GMLWriter
  @brief Writes files in GML (Graph Modeling Language) format

  This is a simple writer for graphs in GML format. It suports a basic
  subset of the GML specification, viz. the specification of different
  attributes for nodes, as well as weight specifications for edges.

  Given a simplicial complex, it will store it as a weighted graph.

  Currently, the following attributes will be written:

  * \c id (for nodes)
  * \c source (for edges)
  * \c target (for edges)
  * \c weight (for edges)
*/

class GMLWriter
{
public:

  template <class SimplicialComplex> void operator()( const std::string& filename, SimplicialComplex& K )
  {
    std::ofstream out( filename );
    if( !out )
      throw std::runtime_error( "Unable to open output file" );

    this->operator()( out, K );
  }

  template <class SimplicialComplex> void operator()( std::ostream& out, SimplicialComplex& K )
  {
    std::ostringstream streamNodes;
    std::ostringstream streamEdges;

    for( auto&& simplex : K )
    {
      if( simplex.dimension() == 0 )
      {
        streamNodes << "  node [\n"
                    << "    id " << *simplex.begin() << "\n"
                    << "  ]\n";
      }
      else if( simplex.dimension() == 1 )
      {
        auto u = *( simplex.begin()     );
        auto v = *( simplex.begin() + 1 );

        streamEdges << "  edge [\n"
                    << "    source " << u << "\n"
                    << "    target " << v << "\n"
                    << "    weight " << simplex.data() << "\n"
                    << "  ]\n";
      }
    }

    out << "graph [\n"
        << "  directed 0\n"
        << streamNodes.str() << "\n"
        << streamEdges.str() << "\n"
        << "]\n";
  }
};

} // namespace io

} // namespace topology

} // namespace aleph

#endif
<|endoftext|>


<commit_before>/**
 * x86_energy.hpp
 *
 *  Created on: 26.06.2018
 *      Author: bmario
 */

#ifndef INCLUDE_X86_ENERGY_HPP_
#define INCLUDE_X86_ENERGY_HPP_

#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <system_error>
#include <vector>

extern "C" {
#include <x86_energy.h>
}

namespace x86_energy
{

enum class Granularity
{
    SYSTEM = X86_ENERGY_GRANULARITY_SYSTEM,
    SOCKET = X86_ENERGY_GRANULARITY_SOCKET,
    DIE = X86_ENERGY_GRANULARITY_DIE,
    MODULE = X86_ENERGY_GRANULARITY_MODULE,
    CORE = X86_ENERGY_GRANULARITY_CORE,
    THREAD = X86_ENERGY_GRANULARITY_THREAD,
    DEVICE = X86_ENERGY_GRANULARITY_DEVICE,
    SIZE = X86_ENERGY_GRANULARITY_SIZE
};

inline std::ostream& operator<<(std::ostream& s, Granularity g)
{
    switch (g)
    {
    case Granularity::SYSTEM:
        s << "SYSTEM";
        break;
    case Granularity::SOCKET:
        s << "SOCKET";
        break;
    case Granularity::DIE:
        s << "DIE";
        break;
    case Granularity::MODULE:
        s << "MODULE";
        break;
    case Granularity::CORE:
        s << "CORE";
        break;
    case Granularity::THREAD:
        s << "THREAD";
        break;
    case Granularity::DEVICE:
        s << "DEVICE";
        break;
    default:
        s << "INVALID";
        break;
    }
    return s;
}

/**
 * Enum for different types of energy counters
 */
enum class Counter
{
    PCKG = X86_ENERGY_COUNTER_PCKG,
    CORES = X86_ENERGY_COUNTER_CORES,
    DRAM = X86_ENERGY_COUNTER_DRAM,
    GPU = X86_ENERGY_COUNTER_GPU,
    PLATFORM = X86_ENERGY_COUNTER_PLATFORM,
    SINGLE_CORE = X86_ENERGY_COUNTER_SINGLE_CORE,
    SIZE = X86_ENERGY_COUNTER_SIZE
};

inline std::ostream& operator<<(std::ostream& s, Counter c)
{
    switch (c)
    {
    case Counter::PCKG:
        s << "PCKG";
        break;
    case Counter::CORES:
        s << "CORES";
        break;
    case Counter::DRAM:
        s << "DRAM";
        break;
    case Counter::GPU:
        s << "GPU";
        break;
    case Counter::PLATFORM:
        s << "PLATFORM";
        break;
    case Counter::SINGLE_CORE:
        s << "SINGLE_CORE";
        break;
    default:
        s << "INVALID";
        break;
    }

    return s;
}

class Architecture;

class ArchitectureNode
{
private:
    ArchitectureNode() = default;

    ArchitectureNode(x86_energy_architecture_node_t* node) : node_(node)
    {
        initialize_children();
    }

    void initialize_children()
    {
        for (auto i = 0u; i < node_->nr_children; i++)
        {
            children_.emplace_back(ArchitectureNode(&node_->children[i]));
        }
    }

public:
    Granularity granularity() const
    {
        return static_cast<Granularity>(node_->granularity);
    }

    std::int32_t id() const
    {
        return node_->id;
    }

    std::string name() const
    {
        return node_->name;
    }

    const std::vector<ArchitectureNode>& children() const
    {
        return children_;
    }

    friend class Architecture;

private:
    x86_energy_architecture_node_t* node_;
    std::vector<ArchitectureNode> children_;
};

class Architecture : public ArchitectureNode
{
    struct ArchitectureNodeDeleter
    {
        void operator()(x86_energy_architecture_node_t* p) const
        {
            x86_energy_free_architecture_nodes(p);
        }
    };

public:
    Architecture() : root_node_(x86_energy_init_architecture_nodes())
    {
        node_ = root_node_.get();
        initialize_children();

        if (node_ == nullptr)
        {
            throw std::runtime_error("Couldn't get architecture description of the system.");
        }
    }

    ArchitectureNode get_arch_for_cpu(Granularity granularity, int cpu) const
    {
        return ArchitectureNode(x86_energy_find_arch_for_cpu(
            root_node_.get(), static_cast<x86_energy_granularity>(granularity), cpu));
    }

    int size(Granularity granularity) const
    {
        return x86_energy_arch_count(root_node_.get(),
                                     static_cast<x86_energy_granularity>(granularity));
    }

    void print_tree(int lvl = 0) const
    {
        x86_energy_print(root_node_.get(), lvl);
    }

private:
    std::unique_ptr<x86_energy_architecture_node_t, ArchitectureNodeDeleter> root_node_;
};

class SourceCounter
{
public:
    SourceCounter() = delete;

    SourceCounter(x86_energy_access_source_t* source, x86_energy_single_counter_t source_counter)
    : source_(source), source_counter_(source_counter)
    {
        if (source_ == nullptr || source_counter_ == nullptr)
        {
            throw std::runtime_error("Trying to construct a source counter with invalid values.");
        }
    }

    ~SourceCounter()
    {
        if (source_)
        {
            source_->close(source_counter_);
        }
    }

    SourceCounter(const SourceCounter&) = delete;
    SourceCounter& operator=(const SourceCounter&) = delete;

    SourceCounter(SourceCounter&& other)
    : source_(other.source_), source_counter_(other.source_counter_)
    {
        other.source_ = nullptr;
    }

    SourceCounter& operator=(SourceCounter&& other)
    {
        std::swap(source_, other.source_);
        std::swap(source_counter_, other.source_counter_);

        return *this;
    }

public:
    double read()
    {
        auto result = source_->read(source_counter_);

        if (result < 0)
        {
            throw std::runtime_error("Couldn't read from source counter");
        }

        return result;
    }

private:
    x86_energy_access_source_t* source_;
    x86_energy_single_counter_t source_counter_;
};

class AccessSource
{
public:
    AccessSource() = delete;

    AccessSource(x86_energy_access_source_t* source) : source_(source)
    {
        if (source_ == nullptr)
        {
            throw std::runtime_error("Trying to construct an access source from a null pointer.");
        }
    }

    ~AccessSource()
    {
        if (initialized_)
        {
            source_->fini();
        }
    }

    AccessSource(const AccessSource&) = delete;
    AccessSource& operator=(const AccessSource&) = delete;

    AccessSource(AccessSource&& other) : source_(other.source_), initialized_(other.initialized_)
    {
        other.source_ = nullptr;
        other.initialized_ = false;
    }

    AccessSource& operator=(AccessSource&& other)
    {
        std::swap(source_, other.source_);
        std::swap(initialized_, other.initialized_);

        return *this;
    }

public:
    void init()
    {
        int result = source_->init();
        if (result != 0)
        {
            throw std::system_error(result, std::system_category());
        }

        initialized_ = true;
    }

    std::string name() const
    {
        return source_->name;
    }

    SourceCounter get(Counter counter, std::size_t index)
    {
        if (!initialized_)
        {
            throw std::runtime_error("Trying to use an uninitialized access source");
        }

        x86_energy_single_counter_t result =
            source_->setup(static_cast<x86_energy_counter>(counter), index);
        if (result == nullptr)
        {
            throw std::runtime_error("could not set up source");
        }
        return { source_, result };
    }

private:
    x86_energy_access_source_t* source_;
    bool initialized_ = false;
};

class Mechanism
{
public:
    Mechanism() : mechanism_(x86_energy_get_avail_mechanism())
    {
        if (mechanism_ == nullptr)
        {
            throw std::runtime_error("Couldn't get the available mechanism.");
        }
    }

public:
    std::string name() const
    {
        return mechanism_->name;
    }

    Granularity granularity(Counter counter) const
    {
        return static_cast<Granularity>(
            mechanism_->source_granularities[static_cast<x86_energy_counter>(counter)]);
    }

    std::vector<AccessSource> available_sources() const
    {
        std::vector<AccessSource> result;

        for (size_t i = 0; i < mechanism_->nr_avail_sources; i++)
        {
            result.emplace_back(&mechanism_->avail_sources[i]);
        }

        return result;
    }

private:
    x86_energy_mechanisms_t* mechanism_;
};

} // namespace x86_energy
#endif /* INCLUDE_X86_ENERGY_HPP_ */
<commit_msg>Improve error handling in cxx header<commit_after>/**
 * x86_energy.hpp
 *
 *  Created on: 26.06.2018
 *      Author: bmario
 */

#ifndef INCLUDE_X86_ENERGY_HPP_
#define INCLUDE_X86_ENERGY_HPP_

#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <system_error>
#include <vector>

extern "C"
{
#include <x86_energy.h>
}

namespace x86_energy
{

enum class Granularity
{
    SYSTEM = X86_ENERGY_GRANULARITY_SYSTEM,
    SOCKET = X86_ENERGY_GRANULARITY_SOCKET,
    DIE = X86_ENERGY_GRANULARITY_DIE,
    MODULE = X86_ENERGY_GRANULARITY_MODULE,
    CORE = X86_ENERGY_GRANULARITY_CORE,
    THREAD = X86_ENERGY_GRANULARITY_THREAD,
    DEVICE = X86_ENERGY_GRANULARITY_DEVICE,
    SIZE = X86_ENERGY_GRANULARITY_SIZE
};

inline std::ostream& operator<<(std::ostream& s, Granularity g)
{
    switch (g)
    {
    case Granularity::SYSTEM:
        s << "SYSTEM";
        break;
    case Granularity::SOCKET:
        s << "SOCKET";
        break;
    case Granularity::DIE:
        s << "DIE";
        break;
    case Granularity::MODULE:
        s << "MODULE";
        break;
    case Granularity::CORE:
        s << "CORE";
        break;
    case Granularity::THREAD:
        s << "THREAD";
        break;
    case Granularity::DEVICE:
        s << "DEVICE";
        break;
    default:
        s << "INVALID";
        break;
    }
    return s;
}

/**
 * Enum for different types of energy counters
 */
enum class Counter
{
    PCKG = X86_ENERGY_COUNTER_PCKG,
    CORES = X86_ENERGY_COUNTER_CORES,
    DRAM = X86_ENERGY_COUNTER_DRAM,
    GPU = X86_ENERGY_COUNTER_GPU,
    PLATFORM = X86_ENERGY_COUNTER_PLATFORM,
    SINGLE_CORE = X86_ENERGY_COUNTER_SINGLE_CORE,
    SIZE = X86_ENERGY_COUNTER_SIZE
};

inline std::ostream& operator<<(std::ostream& s, Counter c)
{
    switch (c)
    {
    case Counter::PCKG:
        s << "PCKG";
        break;
    case Counter::CORES:
        s << "CORES";
        break;
    case Counter::DRAM:
        s << "DRAM";
        break;
    case Counter::GPU:
        s << "GPU";
        break;
    case Counter::PLATFORM:
        s << "PLATFORM";
        break;
    case Counter::SINGLE_CORE:
        s << "SINGLE_CORE";
        break;
    default:
        s << "INVALID";
        break;
    }

    return s;
}

class Architecture;

class ArchitectureNode
{
private:
    ArchitectureNode() = default;

    ArchitectureNode(x86_energy_architecture_node_t* node) : node_(node)
    {
        initialize_children();
    }

    void initialize_children()
    {
        for (auto i = 0u; i < node_->nr_children; i++)
        {
            children_.emplace_back(ArchitectureNode(&node_->children[i]));
        }
    }

public:
    Granularity granularity() const
    {
        return static_cast<Granularity>(node_->granularity);
    }

    std::int32_t id() const
    {
        return node_->id;
    }

    std::string name() const
    {
        return node_->name;
    }

    const std::vector<ArchitectureNode>& children() const
    {
        return children_;
    }

    friend class Architecture;

private:
    x86_energy_architecture_node_t* node_;
    std::vector<ArchitectureNode> children_;
};

class Architecture : public ArchitectureNode
{
    struct ArchitectureNodeDeleter
    {
        void operator()(x86_energy_architecture_node_t* p) const
        {
            x86_energy_free_architecture_nodes(p);
        }
    };

public:
    Architecture() : root_node_(x86_energy_init_architecture_nodes())
    {
        node_ = root_node_.get();
        if (node_ == nullptr)
        {
            throw std::runtime_error(x86_energy_error_string());
        }

        initialize_children();
    }

    ArchitectureNode get_arch_for_cpu(Granularity granularity, int cpu) const
    {
        auto node = x86_energy_find_arch_for_cpu(
            root_node_.get(), static_cast<x86_energy_granularity>(granularity), cpu);

        if (node == nullptr)
        {
            throw std::runtime_error(x86_energy_error_string());
        }

        return ArchitectureNode(node);
    }

    int size(Granularity granularity) const
    {
        return x86_energy_arch_count(root_node_.get(),
                                     static_cast<x86_energy_granularity>(granularity));
    }

    void print_tree(int lvl = 0) const
    {
        x86_energy_print(root_node_.get(), lvl);
    }

private:
    std::unique_ptr<x86_energy_architecture_node_t, ArchitectureNodeDeleter> root_node_;
};

class SourceCounter
{
public:
    SourceCounter() = delete;

    SourceCounter(x86_energy_access_source_t* source, x86_energy_single_counter_t source_counter)
    : source_(source), source_counter_(source_counter)
    {
        if (source_ == nullptr || source_counter_ == nullptr)
        {
            throw std::runtime_error("Trying to construct a source counter with invalid values.");
        }
    }

    ~SourceCounter()
    {
        if (source_)
        {
            source_->close(source_counter_);
        }
    }

    SourceCounter(const SourceCounter&) = delete;
    SourceCounter& operator=(const SourceCounter&) = delete;

    SourceCounter(SourceCounter&& other)
    : source_(other.source_), source_counter_(other.source_counter_)
    {
        other.source_ = nullptr;
    }

    SourceCounter& operator=(SourceCounter&& other)
    {
        std::swap(source_, other.source_);
        std::swap(source_counter_, other.source_counter_);

        return *this;
    }

public:
    double read()
    {
        auto result = source_->read(source_counter_);

        if (result < 0)
        {
            throw std::runtime_error("Couldn't read from source counter");
        }

        return result;
    }

private:
    x86_energy_access_source_t* source_;
    x86_energy_single_counter_t source_counter_;
};

class AccessSource
{
public:
    AccessSource() = delete;

    AccessSource(x86_energy_access_source_t* source) : source_(source)
    {
        if (source_ == nullptr)
        {
            throw std::runtime_error("Trying to construct an access source from a null pointer.");
        }
    }

    ~AccessSource()
    {
        if (initialized_)
        {
            source_->fini();
        }
    }

    AccessSource(const AccessSource&) = delete;
    AccessSource& operator=(const AccessSource&) = delete;

    AccessSource(AccessSource&& other) : source_(other.source_), initialized_(other.initialized_)
    {
        other.source_ = nullptr;
        other.initialized_ = false;
    }

    AccessSource& operator=(AccessSource&& other)
    {
        std::swap(source_, other.source_);
        std::swap(initialized_, other.initialized_);

        return *this;
    }

public:
    void init()
    {
        int result = source_->init();
        if (result != 0)
        {
            throw std::system_error(result, std::system_category());
        }

        initialized_ = true;
    }

    std::string name() const
    {
        return source_->name;
    }

    SourceCounter get(Counter counter, std::size_t index)
    {
        if (!initialized_)
        {
            throw std::runtime_error("Trying to use an uninitialized access source");
        }

        x86_energy_single_counter_t result =
            source_->setup(static_cast<x86_energy_counter>(counter), index);
        if (result == nullptr)
        {
            throw std::runtime_error(x86_energy_error_string());
        }
        return { source_, result };
    }

private:
    x86_energy_access_source_t* source_;
    bool initialized_ = false;
};

class Mechanism
{
public:
    Mechanism() : mechanism_(x86_energy_get_avail_mechanism())
    {
        if (mechanism_ == nullptr)
        {
            throw std::runtime_error("Couldn't get the available mechanism.");
        }
    }

public:
    std::string name() const
    {
        return mechanism_->name;
    }

    Granularity granularity(Counter counter) const
    {
        return static_cast<Granularity>(
            mechanism_->source_granularities[static_cast<x86_energy_counter>(counter)]);
    }

    std::vector<AccessSource> available_sources() const
    {
        std::vector<AccessSource> result;

        for (size_t i = 0; i < mechanism_->nr_avail_sources; i++)
        {
            result.emplace_back(&mechanism_->avail_sources[i]);
        }

        return result;
    }

private:
    x86_energy_mechanisms_t* mechanism_;
};

} // namespace x86_energy
#endif /* INCLUDE_X86_ENERGY_HPP_ */
<|endoftext|>


<commit_before>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <google/protobuf/message.h>
#include <grpc/support/alloc.h>
#include <grpc++/impl/call.h>
#include <grpc++/client_context.h>
#include <grpc++/channel_interface.h>

#include "src/cpp/proto/proto_utils.h"

namespace grpc {

void CallOpBuffer::Reset(void* next_return_tag) {
  return_tag_ = next_return_tag;

  send_initial_metadata_ = false;
  initial_metadata_count_ = 0;
  gpr_free(initial_metadata_);

  recv_initial_metadata_ = nullptr;
  recv_initial_metadata_arr_.count = 0;

  send_message_ = nullptr;
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
    send_message_buf_ = nullptr;
  }

  recv_message_ = nullptr;
  got_message = false;
  if (recv_message_buf_) {
    grpc_byte_buffer_destroy(recv_message_buf_);
    recv_message_buf_ = nullptr;
  }

  client_send_close_ = false;

  recv_trailing_metadata_ = nullptr;
  recv_status_ = nullptr;
  recv_trailing_metadata_arr_.count = 0;

  status_code_ = GRPC_STATUS_OK;

  send_status_ = nullptr;
  trailing_metadata_count_ = 0;
  trailing_metadata_ = nullptr;

  recv_closed_ = nullptr;
}

CallOpBuffer::~CallOpBuffer() {
  gpr_free(status_details_);
  gpr_free(recv_initial_metadata_arr_.metadata);
  gpr_free(recv_trailing_metadata_arr_.metadata);
  if (recv_message_buf_) {
    grpc_byte_buffer_destroy(recv_message_buf_);
  }
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
  }
}

namespace {
// TODO(yangg) if the map is changed before we send, the pointers will be a
// mess. Make sure it does not happen.
grpc_metadata* FillMetadataArray(
    std::multimap<grpc::string, grpc::string>* metadata) {
  if (metadata->empty()) {
    return nullptr;
  }
  grpc_metadata* metadata_array =
      (grpc_metadata*)gpr_malloc(metadata->size() * sizeof(grpc_metadata));
  size_t i = 0;
  for (auto iter = metadata->cbegin(); iter != metadata->cend(); ++iter, ++i) {
    metadata_array[i].key = iter->first.c_str();
    metadata_array[i].value = iter->second.c_str();
    metadata_array[i].value_length = iter->second.size();
  }
  return metadata_array;
}

void FillMetadataMap(grpc_metadata_array* arr,
                     std::multimap<grpc::string, grpc::string>* metadata) {
  for (size_t i = 0; i < arr->count; i++) {
    // TODO(yangg) handle duplicates?
    metadata->insert(std::pair<grpc::string, grpc::string>(
        arr->metadata[i].key,
        {arr->metadata[i].value, arr->metadata[i].value_length}));
  }
  grpc_metadata_array_destroy(arr);
  grpc_metadata_array_init(arr);
}
}  // namespace

void CallOpBuffer::AddSendInitialMetadata(
    std::multimap<grpc::string, grpc::string>* metadata) {
  send_initial_metadata_ = true;
  initial_metadata_count_ = metadata->size();
  initial_metadata_ = FillMetadataArray(metadata);
}

void CallOpBuffer::AddRecvInitialMetadata(
    std::multimap<grpc::string, grpc::string>* metadata) {
  recv_initial_metadata_ = metadata;
}

void CallOpBuffer::AddSendInitialMetadata(ClientContext* ctx) {
  AddSendInitialMetadata(&ctx->send_initial_metadata_);
}

void CallOpBuffer::AddSendMessage(const google::protobuf::Message& message) {
  send_message_ = &message;
}

void CallOpBuffer::AddRecvMessage(google::protobuf::Message* message) {
  recv_message_ = message;
  recv_message_->Clear();
}

void CallOpBuffer::AddClientSendClose() { client_send_close_ = true; }

void CallOpBuffer::AddServerRecvClose(bool* cancelled) {
  recv_closed_ = cancelled;
}

void CallOpBuffer::AddClientRecvStatus(
    std::multimap<grpc::string, grpc::string>* metadata, Status* status) {
  recv_trailing_metadata_ = metadata;
  recv_status_ = status;
}

void CallOpBuffer::AddServerSendStatus(
    std::multimap<grpc::string, grpc::string>* metadata, const Status& status) {
  if (metadata != NULL) {
    trailing_metadata_count_ = metadata->size();
    trailing_metadata_ = FillMetadataArray(metadata);
  } else {
    trailing_metadata_count_ = 0;
  }
  send_status_ = &status;
}

void CallOpBuffer::FillOps(grpc_op* ops, size_t* nops) {
  *nops = 0;
  if (send_initial_metadata_) {
    ops[*nops].op = GRPC_OP_SEND_INITIAL_METADATA;
    ops[*nops].data.send_initial_metadata.count = initial_metadata_count_;
    ops[*nops].data.send_initial_metadata.metadata = initial_metadata_;
    (*nops)++;
  }
  if (recv_initial_metadata_) {
    ops[*nops].op = GRPC_OP_RECV_INITIAL_METADATA;
    ops[*nops].data.recv_initial_metadata = &recv_initial_metadata_arr_;
    (*nops)++;
  }
  if (send_message_) {
    bool success = SerializeProto(*send_message_, &send_message_buf_);
    if (!success) {
      abort();
      // TODO handle parse failure
    }
    ops[*nops].op = GRPC_OP_SEND_MESSAGE;
    ops[*nops].data.send_message = send_message_buf_;
    (*nops)++;
  }
  if (recv_message_) {
    ops[*nops].op = GRPC_OP_RECV_MESSAGE;
    ops[*nops].data.recv_message = &recv_message_buf_;
    (*nops)++;
  }
  if (client_send_close_) {
    ops[*nops].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
    (*nops)++;
  }
  if (recv_status_) {
    ops[*nops].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
    ops[*nops].data.recv_status_on_client.trailing_metadata =
        &recv_trailing_metadata_arr_;
    ops[*nops].data.recv_status_on_client.status = &status_code_;
    ops[*nops].data.recv_status_on_client.status_details = &status_details_;
    ops[*nops].data.recv_status_on_client.status_details_capacity =
        &status_details_capacity_;
    (*nops)++;
  }
  if (send_status_) {
    ops[*nops].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
    ops[*nops].data.send_status_from_server.trailing_metadata_count =
        trailing_metadata_count_;
    ops[*nops].data.send_status_from_server.trailing_metadata =
        trailing_metadata_;
    ops[*nops].data.send_status_from_server.status =
        static_cast<grpc_status_code>(send_status_->code());
    ops[*nops].data.send_status_from_server.status_details =
        send_status_->details().c_str();
    (*nops)++;
  }
  if (recv_closed_) {
    ops[*nops].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
    ops[*nops].data.recv_close_on_server.cancelled = &cancelled_buf_;
    (*nops)++;
  }
}

void CallOpBuffer::FinalizeResult(void** tag, bool* status) {
  // Release send buffers.
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
    send_message_buf_ = nullptr;
  }
  if (initial_metadata_) {
    gpr_free(initial_metadata_);
    initial_metadata_ = nullptr;
  }
  if (trailing_metadata_count_) {
    gpr_free(trailing_metadata_);
    trailing_metadata_ = nullptr;
  }
  // Set user-facing tag.
  *tag = return_tag_;
  // Process received initial metadata
  if (recv_initial_metadata_) {
    FillMetadataMap(&recv_initial_metadata_arr_, recv_initial_metadata_);
  }
  // Parse received message if any.
  if (recv_message_) {
    if (recv_message_buf_) {
      got_message = true;
      *status = *status && DeserializeProto(recv_message_buf_, recv_message_);
      grpc_byte_buffer_destroy(recv_message_buf_);
      recv_message_buf_ = nullptr;
    } else {
      // Read failed
      got_message = false;
      *status = false;
    }
  }
  // Parse received status.
  if (recv_status_) {
    FillMetadataMap(&recv_trailing_metadata_arr_, recv_trailing_metadata_);
    *recv_status_ = Status(
        static_cast<StatusCode>(status_code_),
        status_details_ ? grpc::string(status_details_) : grpc::string());
  }
  if (recv_closed_) {
    *recv_closed_ = cancelled_buf_ != 0;
  }
}

Call::Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
    : call_hook_(call_hook), cq_(cq), call_(call) {}

void Call::PerformOps(CallOpBuffer* buffer) {
  call_hook_->PerformOpsOnCall(buffer, this);
}

}  // namespace grpc
<commit_msg>also set got_message<commit_after>/*
 *
 * Copyright 2014, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include <google/protobuf/message.h>
#include <grpc/support/alloc.h>
#include <grpc++/impl/call.h>
#include <grpc++/client_context.h>
#include <grpc++/channel_interface.h>

#include "src/cpp/proto/proto_utils.h"

namespace grpc {

void CallOpBuffer::Reset(void* next_return_tag) {
  return_tag_ = next_return_tag;

  send_initial_metadata_ = false;
  initial_metadata_count_ = 0;
  gpr_free(initial_metadata_);

  recv_initial_metadata_ = nullptr;
  recv_initial_metadata_arr_.count = 0;

  send_message_ = nullptr;
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
    send_message_buf_ = nullptr;
  }

  recv_message_ = nullptr;
  got_message = false;
  if (recv_message_buf_) {
    grpc_byte_buffer_destroy(recv_message_buf_);
    recv_message_buf_ = nullptr;
  }

  client_send_close_ = false;

  recv_trailing_metadata_ = nullptr;
  recv_status_ = nullptr;
  recv_trailing_metadata_arr_.count = 0;

  status_code_ = GRPC_STATUS_OK;

  send_status_ = nullptr;
  trailing_metadata_count_ = 0;
  trailing_metadata_ = nullptr;

  recv_closed_ = nullptr;
}

CallOpBuffer::~CallOpBuffer() {
  gpr_free(status_details_);
  gpr_free(recv_initial_metadata_arr_.metadata);
  gpr_free(recv_trailing_metadata_arr_.metadata);
  if (recv_message_buf_) {
    grpc_byte_buffer_destroy(recv_message_buf_);
  }
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
  }
}

namespace {
// TODO(yangg) if the map is changed before we send, the pointers will be a
// mess. Make sure it does not happen.
grpc_metadata* FillMetadataArray(
    std::multimap<grpc::string, grpc::string>* metadata) {
  if (metadata->empty()) {
    return nullptr;
  }
  grpc_metadata* metadata_array =
      (grpc_metadata*)gpr_malloc(metadata->size() * sizeof(grpc_metadata));
  size_t i = 0;
  for (auto iter = metadata->cbegin(); iter != metadata->cend(); ++iter, ++i) {
    metadata_array[i].key = iter->first.c_str();
    metadata_array[i].value = iter->second.c_str();
    metadata_array[i].value_length = iter->second.size();
  }
  return metadata_array;
}

void FillMetadataMap(grpc_metadata_array* arr,
                     std::multimap<grpc::string, grpc::string>* metadata) {
  for (size_t i = 0; i < arr->count; i++) {
    // TODO(yangg) handle duplicates?
    metadata->insert(std::pair<grpc::string, grpc::string>(
        arr->metadata[i].key,
        {arr->metadata[i].value, arr->metadata[i].value_length}));
  }
  grpc_metadata_array_destroy(arr);
  grpc_metadata_array_init(arr);
}
}  // namespace

void CallOpBuffer::AddSendInitialMetadata(
    std::multimap<grpc::string, grpc::string>* metadata) {
  send_initial_metadata_ = true;
  initial_metadata_count_ = metadata->size();
  initial_metadata_ = FillMetadataArray(metadata);
}

void CallOpBuffer::AddRecvInitialMetadata(
    std::multimap<grpc::string, grpc::string>* metadata) {
  recv_initial_metadata_ = metadata;
}

void CallOpBuffer::AddSendInitialMetadata(ClientContext* ctx) {
  AddSendInitialMetadata(&ctx->send_initial_metadata_);
}

void CallOpBuffer::AddSendMessage(const google::protobuf::Message& message) {
  send_message_ = &message;
}

void CallOpBuffer::AddRecvMessage(google::protobuf::Message* message) {
  recv_message_ = message;
  recv_message_->Clear();
}

void CallOpBuffer::AddClientSendClose() { client_send_close_ = true; }

void CallOpBuffer::AddServerRecvClose(bool* cancelled) {
  recv_closed_ = cancelled;
}

void CallOpBuffer::AddClientRecvStatus(
    std::multimap<grpc::string, grpc::string>* metadata, Status* status) {
  recv_trailing_metadata_ = metadata;
  recv_status_ = status;
}

void CallOpBuffer::AddServerSendStatus(
    std::multimap<grpc::string, grpc::string>* metadata, const Status& status) {
  if (metadata != NULL) {
    trailing_metadata_count_ = metadata->size();
    trailing_metadata_ = FillMetadataArray(metadata);
  } else {
    trailing_metadata_count_ = 0;
  }
  send_status_ = &status;
}

void CallOpBuffer::FillOps(grpc_op* ops, size_t* nops) {
  *nops = 0;
  if (send_initial_metadata_) {
    ops[*nops].op = GRPC_OP_SEND_INITIAL_METADATA;
    ops[*nops].data.send_initial_metadata.count = initial_metadata_count_;
    ops[*nops].data.send_initial_metadata.metadata = initial_metadata_;
    (*nops)++;
  }
  if (recv_initial_metadata_) {
    ops[*nops].op = GRPC_OP_RECV_INITIAL_METADATA;
    ops[*nops].data.recv_initial_metadata = &recv_initial_metadata_arr_;
    (*nops)++;
  }
  if (send_message_) {
    bool success = SerializeProto(*send_message_, &send_message_buf_);
    if (!success) {
      abort();
      // TODO handle parse failure
    }
    ops[*nops].op = GRPC_OP_SEND_MESSAGE;
    ops[*nops].data.send_message = send_message_buf_;
    (*nops)++;
  }
  if (recv_message_) {
    ops[*nops].op = GRPC_OP_RECV_MESSAGE;
    ops[*nops].data.recv_message = &recv_message_buf_;
    (*nops)++;
  }
  if (client_send_close_) {
    ops[*nops].op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
    (*nops)++;
  }
  if (recv_status_) {
    ops[*nops].op = GRPC_OP_RECV_STATUS_ON_CLIENT;
    ops[*nops].data.recv_status_on_client.trailing_metadata =
        &recv_trailing_metadata_arr_;
    ops[*nops].data.recv_status_on_client.status = &status_code_;
    ops[*nops].data.recv_status_on_client.status_details = &status_details_;
    ops[*nops].data.recv_status_on_client.status_details_capacity =
        &status_details_capacity_;
    (*nops)++;
  }
  if (send_status_) {
    ops[*nops].op = GRPC_OP_SEND_STATUS_FROM_SERVER;
    ops[*nops].data.send_status_from_server.trailing_metadata_count =
        trailing_metadata_count_;
    ops[*nops].data.send_status_from_server.trailing_metadata =
        trailing_metadata_;
    ops[*nops].data.send_status_from_server.status =
        static_cast<grpc_status_code>(send_status_->code());
    ops[*nops].data.send_status_from_server.status_details =
        send_status_->details().c_str();
    (*nops)++;
  }
  if (recv_closed_) {
    ops[*nops].op = GRPC_OP_RECV_CLOSE_ON_SERVER;
    ops[*nops].data.recv_close_on_server.cancelled = &cancelled_buf_;
    (*nops)++;
  }
}

void CallOpBuffer::FinalizeResult(void** tag, bool* status) {
  // Release send buffers.
  if (send_message_buf_) {
    grpc_byte_buffer_destroy(send_message_buf_);
    send_message_buf_ = nullptr;
  }
  if (initial_metadata_) {
    gpr_free(initial_metadata_);
    initial_metadata_ = nullptr;
  }
  if (trailing_metadata_count_) {
    gpr_free(trailing_metadata_);
    trailing_metadata_ = nullptr;
  }
  // Set user-facing tag.
  *tag = return_tag_;
  // Process received initial metadata
  if (recv_initial_metadata_) {
    FillMetadataMap(&recv_initial_metadata_arr_, recv_initial_metadata_);
  }
  // Parse received message if any.
  if (recv_message_) {
    if (recv_message_buf_) {
      got_message = *status;
      *status = *status && DeserializeProto(recv_message_buf_, recv_message_);
      grpc_byte_buffer_destroy(recv_message_buf_);
      recv_message_buf_ = nullptr;
    } else {
      // Read failed
      got_message = false;
      *status = false;
    }
  }
  // Parse received status.
  if (recv_status_) {
    FillMetadataMap(&recv_trailing_metadata_arr_, recv_trailing_metadata_);
    *recv_status_ = Status(
        static_cast<StatusCode>(status_code_),
        status_details_ ? grpc::string(status_details_) : grpc::string());
  }
  if (recv_closed_) {
    *recv_closed_ = cancelled_buf_ != 0;
  }
}

Call::Call(grpc_call* call, CallHook* call_hook, CompletionQueue* cq)
    : call_hook_(call_hook), cq_(cq), call_(call) {}

void Call::PerformOps(CallOpBuffer* buffer) {
  call_hook_->PerformOpsOnCall(buffer, this);
}

}  // namespace grpc
<|endoftext|>