<commit_before>#include "eventbackend.h"

#define MAX_EVENTS 1024

#if defined(__linux__)

// epoll needs kernel 2.5.44

// Linux
#include <sys/epoll.h>

// POSIX
#include <fcntl.h>

// POSIX++
#include <cstring> // for strerror()
#include <cstdlib> // for exit

// PDTK
#include <cxxutils/vterm.h>

struct EventBackend::platform_dependant // poll notification (epoll)
{
  posix::fd_t fd;
  struct epoll_event output[MAX_EVENTS];

  platform_dependant(void) noexcept
    : fd(posix::invalid_descriptor)
  {
    fd = ::epoll_create(MAX_EVENTS);
    flaw(fd == posix::invalid_descriptor, terminal::critical, std::exit(errno),,
         "Unable to create an instance of epoll! %s", std::strerror(errno))
  }

  ~platform_dependant(void) noexcept
  {
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  bool add(posix::fd_t wd, native_flags_t flags) noexcept
  {
    struct epoll_event native_event;
    native_event.data.fd = wd;
    native_event.events = flags; // be sure to convert to native events
    return ::epoll_ctl(fd, EPOLL_CTL_ADD, wd, &native_event) == posix::success_response;
  }

  bool modify(posix::fd_t wd, native_flags_t flags) noexcept
  {
    struct epoll_event native_event;
    native_event.data.fd = wd;
    native_event.events = flags; // be sure to convert to native events

    return ::epoll_ctl(fd, EPOLL_CTL_MOD, wd, &native_event) == posix::success_response; // try to modify FD first
  }

  bool remove(posix::fd_t wd) noexcept
  {
    struct epoll_event event;
    return ::epoll_ctl(fd, EPOLL_CTL_DEL, wd, &event) == posix::success_response; // try to delete entry
  }
} EventBackend::s_platform;

lockable<std::unordered_multimap<posix::fd_t, EventBackend::callback_info_t>> EventBackend::queue;
std::list<std::pair<posix::fd_t, native_flags_t>> EventBackend::results;

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  if(s_platform.add(fd, flags))
    queue.emplace(fd, (callback_info_t){flags, function});
  else if(errno == EEXIST && s_platform.modify(fd, flags)) // if entry exist and modification worked
  {
    bool found = false;
    auto entries = queue.equal_range(fd); // find modified entry!
    for(auto& pos = entries.first; pos != entries.second; ++pos)
    {
      found |= &(pos->second.function) == &function; // save if function was ever found
      if(&(pos->second.function) == &function) // if the FD is in the queue and it's callback function matches
        pos->second.flags |= flags; // simply modify the flags to include the current
    }

    if(!found) // function wasn't found
      queue.emplace(fd, (callback_info_t){flags, function}); // make a new entry for this FD
  }
  else // unable to add an entry or modify the entry for this FD!
    return false; // fail

  return true;
}

bool EventBackend::remove(posix::fd_t fd, native_flags_t flags) noexcept
{
  auto entries = queue.equal_range(fd); // find modified entry!
  for(auto& pos = entries.first; pos != entries.second; ++pos)
  {
    if(pos->second.flags == flags)
      queue.erase(pos);
    else if(pos->second.flags & flags)
    {
      pos->second.flags ^= pos->second.flags & flags;
      if(pos->second.flags) // if some flags are still set
        s_platform.modify(fd, pos->second.flags);
      else // if no flags are set then remove it completely
        s_platform.remove(fd);
    }
  }
  return true;
}

bool EventBackend::poll(int timeout) noexcept
{
  int count = ::epoll_wait(s_platform.fd, s_platform.output, MAX_EVENTS, timeout); // wait for new results
  results.clear(); // clear old results

  if(count == posix::error_response) // if error/timeout occurred
    return false; //fail

  const epoll_event* end = s_platform.output + count;
  for(epoll_event* pos = s_platform.output; pos != end; ++pos) // iterate through results
    results.emplace_back(std::make_pair(posix::fd_t(pos->data.fd), native_flags_t(pos->events))); // save result (in native format)
  return true;
}

namespace EventBackend
{
  static posix::fd_t s_pipeio[2] = { posix::invalid_descriptor }; //  execution stepper pipe
  static vfunc stepper_function = nullptr;
  enum {
    Read = 0,
    Write = 1,
  };

  void readstep(posix::fd_t fd, native_flags_t) noexcept
  {
    uint64_t discard;
    while(posix::read(fd, &discard, sizeof(discard)) != posix::error_response);
    stepper_function();
  }

  bool setstepper(vfunc function) noexcept
  {
    stepper_function = function;
    if(s_pipeio[Read] == posix::invalid_descriptor) // if execution stepper pipe  hasn't been initialized yet
    {
      flaw(::pipe(s_pipeio) == posix::error_response, terminal::critical, std::exit(errno), false,
           "Unable to create pipe for execution stepper: %s", std::strerror(errno))
      ::fcntl(s_pipeio[Read], F_SETFD, FD_CLOEXEC);
      ::fcntl(s_pipeio[Read], F_SETFL, O_NONBLOCK);
      return EventBackend::add(s_pipeio[Read], EPOLLIN, readstep); // watch for when execution stepper pipe has been triggered
    }
    return stepper_function != nullptr;
  }

  void step(void) noexcept
  {
    static const uint8_t dummydata = 0; // dummy content
    flaw(posix::write(s_pipeio[Write], &dummydata, 1) != 1, terminal::critical, /*std::exit(errno)*/,, // triggers execution stepper FD
         "Unable to trigger Object signal queue processor: %s", std::strerror(errno))
  }
}

#elif defined(__APPLE__)      /* Darwin 7+     */ || \
      defined(__FreeBSD__)    /* FreeBSD 4.1+  */ || \
      defined(__DragonFly__)  /* DragonFly BSD */ || \
      defined(__OpenBSD__)    /* OpenBSD 2.9+  */ || \
      defined(__NetBSD__)     /* NetBSD 2+     */

// BSD
#include <sys/time.h>
#include <sys/event.h>

// POSIX
#include <sys/socket.h>
#include <unistd.h>

// POSIX++
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>

// STL
#include <vector>

// PDTK
#include <cxxutils/vterm.h>
#include <cxxutils/error_helpers.h>

lockable<std::unordered_multimap<posix::fd_t, EventBackend::callback_info_t>> EventBackend::queue;
std::list<std::pair<posix::fd_t, native_flags_t>> EventBackend::results;

struct EventBackend::platform_dependant // poll notification (epoll)
{
  posix::fd_t kq;
  std::vector<struct kevent> kinput;    // events we want to monitor
  std::vector<struct kevent> koutput;   // events that were triggered

  platform_dependant(void)
  {
    kq = posix::ignore_interruption(::kqueue);
    flaw(kq == posix::error_response, terminal::critical, std::exit(errno),,
         "Unable to create a new kqueue: %s", std::strerror(errno))
    kinput .reserve(1024);
    koutput.reserve(1024);
  }

  ~platform_dependant(void)
  {
    posix::close(kq);
  }
} EventBackend::s_platform;

static constexpr native_flags_t composite_flag(short filters, ushort flags) noexcept
  { return native_flags_t(uint16_t(flags) << 16) | native_flags_t(flags); }

static constexpr short extract_filter(native_flags_t flags) noexcept
  { return flags & 0xFFFF; }

static constexpr ushort extract_flags(native_flags_t flags) noexcept
  { return flags >> 16; }

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  struct kevent ev;
  EV_SET(&ev, fd, extract_filter(flags), EV_ADD, extract_flags(flags), 0, nullptr);
  s_platform.kinput.push_back(ev);
  s_platform.koutput.resize(s_platform.kinput.size());
  queue.emplace(fd, (callback_info_t){flags, function});
  return true;
}

bool EventBackend::remove(posix::fd_t fd, native_flags_t flags) noexcept
{
  return false;
}

bool EventBackend::poll(int timeout) noexcept
{
  uint32_t data;
  timespec tout;
  tout.tv_sec = timeout / 1000;
  tout.tv_nsec = (timeout % 1000) * 1000;

  int count = 0;
  count = kevent(s_platform.kq,
             s_platform.kinput.data(), s_platform.kinput.size(),
             s_platform.koutput.data(), s_platform.koutput.size(),
             &tout);

  if(count <= 0)
    return false;

  struct kevent* end = s_platform.koutput.data() + count;

  for(struct kevent* pos = s_platform.koutput.data(); pos != end; ++pos) // iterate through results
    results.emplace_back(std::make_pair(posix::fd_t(pos->ident), composite_flag(pos->filter, pos->flags)));
  return true;
}

#elif defined(__sun) && defined(__SVR4) // Solaris / OpenSolaris / OpenIndiana / illumos

#pragma message Not implemented, yet!
#pragma message See: http://docs.oracle.com/cd/E19253-01/816-5168/port-get-3c/index.html
#error The backend code in PDTK for Solaris / OpenSolaris / OpenIndiana / illumos is non-functional!  Please submit a patch!

// Solaris
#include <port.h>

// POSIX
#include <sys/socket.h>
#include <unistd.h>

// POSIX++
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>

// STL
#include <vector>

// PDTK
#include <cxxutils/vterm.h>
#include <cxxutils/error_helpers.h>

struct platform_dependant
{
  posix::fd_t port;
  std::vector<port_event_t> pinput;   // events we want to monitor
  std::vector<port_event_t> poutput;  // events that were triggered

  platform_dependant(void)
  {
    port = ::port_create();
    flaw(port == posix::error_response, terminal::critical, std::exit(errno),,
         "Unable to create a new kqueue: %s", std::strerror(errno))
   pinput .reserve(1024);
   poutput.reserve(1024);
  }

  ~platform_dependant(void)
  {
    posix::close(port);
  }
} EventBackend::s_platform;

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  port_event_t pev;

  s_platform.pinput.push_back(pev);
  queue.emplace(fd, (callback_info_t){flags, function});
  return true;
}

bool EventBackend::remove(posix::fd_t fd, native_flags_t flags) noexcept
{
  return false;
}

bool EventBackend::poll(int timeout) noexcept
{
  native_flags_t flags;
  uint_t count = 0;
  timespec tout;
  tout.tv_sec = timeout / 1000;
  tout.tv_nsec = (timeout % 1000) * 1000;

  if(::port_getn(s_platform.port,
                 &s_platform.pinput.data(), s_platform.pinput.size(),
                 s_platform.poutput, MAX_EVENTS, &count
                 &tout) == posix::error_response)
    return false;

  port_event_t* end = s_platform.poutput + count;

  for(port_event_t* pos = s_platform.poutput; pos != end; ++pos) // iterate through results
  {
    //flags = from_kevent(*pos);

    results.emplace(posix::fd_t(pos->ident), flags);
  }
  return true;
}

#elif defined(__minix) // MINIX
#error No event backend code exists in PDTK for MINIX!  Please submit a patch!

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
#error No event backend code exists in PDTK for QNX!  Please submit a patch!

#elif defined(__hpux) // HP-UX
// see http://nixdoc.net/man-pages/HP-UX/man7/poll.7.html
// and https://www.freebsd.org/cgi/man.cgi?query=poll&sektion=7&apropos=0&manpath=HP-UX+11.22
// uses /dev/poll
#error No event backend code exists in PDTK for HP-UX!  Please submit a patch!

#include <sys/devpoll.h>

#elif defined(_AIX) // IBM AIX
// see https://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.basetrf1/pollset.htm
// uses pollset_* functions

#include <sys/poll.h>
#include <sys/pollset.h>

  pollset_t n;
  n = pollset_create(-1);
  pollset_destroy(n);

#error No event backend code exists in PDTK for IBM AIX!  Please submit a patch!

#elif defined(BSD)
#error Unrecognized BSD derivative!

#elif defined(__unix__)
#error Unrecognized UNIX variant!

#else
#error This platform is not supported.
#endif
<commit_msg>wrote remove() for kqueue backend<commit_after>#include "eventbackend.h"

#define MAX_EVENTS 1024

#if defined(__linux__)

// epoll needs kernel 2.5.44

// Linux
#include <sys/epoll.h>

// POSIX
#include <fcntl.h>

// POSIX++
#include <cstring> // for strerror()
#include <cstdlib> // for exit

// PDTK
#include <cxxutils/vterm.h>

struct EventBackend::platform_dependant // poll notification (epoll)
{
  posix::fd_t fd;
  struct epoll_event output[MAX_EVENTS];

  platform_dependant(void) noexcept
    : fd(posix::invalid_descriptor)
  {
    fd = ::epoll_create(MAX_EVENTS);
    flaw(fd == posix::invalid_descriptor, terminal::critical, std::exit(errno),,
         "Unable to create an instance of epoll! %s", std::strerror(errno))
  }

  ~platform_dependant(void) noexcept
  {
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  bool add(posix::fd_t wd, native_flags_t flags) noexcept
  {
    struct epoll_event native_event;
    native_event.data.fd = wd;
    native_event.events = flags; // be sure to convert to native events
    return ::epoll_ctl(fd, EPOLL_CTL_ADD, wd, &native_event) == posix::success_response;
  }

  bool modify(posix::fd_t wd, native_flags_t flags) noexcept
  {
    struct epoll_event native_event;
    native_event.data.fd = wd;
    native_event.events = flags; // be sure to convert to native events

    return ::epoll_ctl(fd, EPOLL_CTL_MOD, wd, &native_event) == posix::success_response; // try to modify FD first
  }

  bool remove(posix::fd_t wd) noexcept
  {
    struct epoll_event event;
    return ::epoll_ctl(fd, EPOLL_CTL_DEL, wd, &event) == posix::success_response; // try to delete entry
  }
} EventBackend::s_platform;

lockable<std::unordered_multimap<posix::fd_t, EventBackend::callback_info_t>> EventBackend::queue;
std::list<std::pair<posix::fd_t, native_flags_t>> EventBackend::results;

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  if(s_platform.add(fd, flags))
    queue.emplace(fd, (callback_info_t){flags, function});
  else if(errno == EEXIST && s_platform.modify(fd, flags)) // if entry exist and modification worked
  {
    bool found = false;
    auto entries = queue.equal_range(fd); // find modified entry!
    for(auto& pos = entries.first; pos != entries.second; ++pos)
    {
      found |= &(pos->second.function) == &function; // save if function was ever found
      if(&(pos->second.function) == &function) // if the FD is in the queue and it's callback function matches
        pos->second.flags |= flags; // simply modify the flags to include the current
    }

    if(!found) // function wasn't found
      queue.emplace(fd, (callback_info_t){flags, function}); // make a new entry for this FD
  }
  else // unable to add an entry or modify the entry for this FD!
    return false; // fail

  return true;
}

bool EventBackend::remove(posix::fd_t fd, native_flags_t flags) noexcept
{
  auto entries = queue.equal_range(fd); // find modified entry!
  for(auto& pos = entries.first; pos != entries.second; ++pos)
  {
    if(pos->second.flags == flags)
      queue.erase(pos);
    else if(pos->second.flags & flags)
    {
      pos->second.flags ^= pos->second.flags & flags;
      if(pos->second.flags) // if some flags are still set
        s_platform.modify(fd, pos->second.flags);
      else // if no flags are set then remove it completely
        s_platform.remove(fd);
    }
  }
  return true;
}

bool EventBackend::poll(int timeout) noexcept
{
  int count = ::epoll_wait(s_platform.fd, s_platform.output, MAX_EVENTS, timeout); // wait for new results
  results.clear(); // clear old results

  if(count == posix::error_response) // if error/timeout occurred
    return false; //fail

  const epoll_event* end = s_platform.output + count;
  for(epoll_event* pos = s_platform.output; pos != end; ++pos) // iterate through results
    results.emplace_back(std::make_pair(posix::fd_t(pos->data.fd), native_flags_t(pos->events))); // save result (in native format)
  return true;
}

namespace EventBackend
{
  static posix::fd_t s_pipeio[2] = { posix::invalid_descriptor }; //  execution stepper pipe
  static vfunc stepper_function = nullptr;
  enum {
    Read = 0,
    Write = 1,
  };

  void readstep(posix::fd_t fd, native_flags_t) noexcept
  {
    uint64_t discard;
    while(posix::read(fd, &discard, sizeof(discard)) != posix::error_response);
    stepper_function();
  }

  bool setstepper(vfunc function) noexcept
  {
    stepper_function = function;
    if(s_pipeio[Read] == posix::invalid_descriptor) // if execution stepper pipe  hasn't been initialized yet
    {
      flaw(::pipe(s_pipeio) == posix::error_response, terminal::critical, std::exit(errno), false,
           "Unable to create pipe for execution stepper: %s", std::strerror(errno))
      ::fcntl(s_pipeio[Read], F_SETFD, FD_CLOEXEC);
      ::fcntl(s_pipeio[Read], F_SETFL, O_NONBLOCK);
      return EventBackend::add(s_pipeio[Read], EPOLLIN, readstep); // watch for when execution stepper pipe has been triggered
    }
    return stepper_function != nullptr;
  }

  void step(void) noexcept
  {
    static const uint8_t dummydata = 0; // dummy content
    flaw(posix::write(s_pipeio[Write], &dummydata, 1) != 1, terminal::critical, /*std::exit(errno)*/,, // triggers execution stepper FD
         "Unable to trigger Object signal queue processor: %s", std::strerror(errno))
  }
}

#elif defined(__APPLE__)      /* Darwin 7+     */ || \
      defined(__FreeBSD__)    /* FreeBSD 4.1+  */ || \
      defined(__DragonFly__)  /* DragonFly BSD */ || \
      defined(__OpenBSD__)    /* OpenBSD 2.9+  */ || \
      defined(__NetBSD__)     /* NetBSD 2+     */

// BSD
#include <sys/time.h>
#include <sys/event.h>

// POSIX
#include <sys/socket.h>
#include <unistd.h>

// POSIX++
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>

// STL
#include <vector>

// PDTK
#include <cxxutils/vterm.h>
#include <cxxutils/error_helpers.h>

lockable<std::unordered_multimap<posix::fd_t, EventBackend::callback_info_t>> EventBackend::queue;
std::list<std::pair<posix::fd_t, native_flags_t>> EventBackend::results;

struct EventBackend::platform_dependant // poll notification (epoll)
{
  posix::fd_t kq;
  std::vector<struct kevent> kinput;    // events we want to monitor
  std::vector<struct kevent> koutput;   // events that were triggered

  platform_dependant(void)
  {
    kq = posix::ignore_interruption(::kqueue);
    flaw(kq == posix::error_response, terminal::critical, std::exit(errno),,
         "Unable to create a new kqueue: %s", std::strerror(errno))
    kinput .reserve(1024);
    koutput.reserve(1024);
  }

  ~platform_dependant(void)
  {
    posix::close(kq);
  }
} EventBackend::s_platform;

static constexpr native_flags_t composite_flag(short filters, ushort flags) noexcept
  { return native_flags_t(uint16_t(flags) << 16) | native_flags_t(flags); }

static constexpr short extract_filter(native_flags_t flags) noexcept
  { return flags & 0xFFFF; }

static constexpr ushort extract_flags(native_flags_t flags) noexcept
  { return flags >> 16; }

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  struct kevent ev;
  EV_SET(&ev, fd, extract_filter(flags), EV_ADD, extract_flags(flags), 0, nullptr);
  s_platform.kinput.push_back(ev);
  s_platform.koutput.resize(s_platform.kinput.size());
  queue.emplace(fd, (callback_info_t){flags, function});
  return true;
}

bool EventBackend::remove(posix::fd_t fd, native_flags_t flags) noexcept
{
  struct kevent ev;
  EV_SET(&ev, fd, extract_filter(flags), EV_ADD, extract_flags(flags), 0, nullptr);
  auto iter = s_platform.kinput.find(ev);
  if(iter == s_platform.kinput.end())
    return false;
  s_platform.kinput.erase(iter);
  return true;
}

bool EventBackend::poll(int timeout) noexcept
{
  uint32_t data;
  timespec tout;
  tout.tv_sec = timeout / 1000;
  tout.tv_nsec = (timeout % 1000) * 1000;

  int count = 0;
  count = kevent(s_platform.kq,
             s_platform.kinput.data(), s_platform.kinput.size(),
             s_platform.koutput.data(), s_platform.koutput.size(),
             &tout);

  if(count <= 0)
    return false;

  struct kevent* end = s_platform.koutput.data() + count;

  for(struct kevent* pos = s_platform.koutput.data(); pos != end; ++pos) // iterate through results
    results.emplace_back(std::make_pair(posix::fd_t(pos->ident), composite_flag(pos->filter, pos->flags)));
  return true;
}

#elif defined(__sun) && defined(__SVR4) // Solaris / OpenSolaris / OpenIndiana / illumos

#pragma message Not implemented, yet!
#pragma message See: http://docs.oracle.com/cd/E19253-01/816-5168/port-get-3c/index.html
#error The backend code in PDTK for Solaris / OpenSolaris / OpenIndiana / illumos is non-functional!  Please submit a patch!

// Solaris
#include <port.h>

// POSIX
#include <sys/socket.h>
#include <unistd.h>

// POSIX++
#include <cstdlib>
#include <cstdio>
#include <climits>
#include <cstring>

// STL
#include <vector>

// PDTK
#include <cxxutils/vterm.h>
#include <cxxutils/error_helpers.h>

struct platform_dependant
{
  posix::fd_t port;
  std::vector<port_event_t> pinput;   // events we want to monitor
  std::vector<port_event_t> poutput;  // events that were triggered

  platform_dependant(void)
  {
    port = ::port_create();
    flaw(port == posix::error_response, terminal::critical, std::exit(errno),,
         "Unable to create a new kqueue: %s", std::strerror(errno))
   pinput .reserve(1024);
   poutput.reserve(1024);
  }

  ~platform_dependant(void)
  {
    posix::close(port);
  }
} EventBackend::s_platform;

bool EventBackend::add(posix::fd_t fd, native_flags_t flags, callback_t function) noexcept
{
  port_event_t pev;

  s_platform.pinput.push_back(pev);
  queue.emplace(fd, (callback_info_t){flags, function});
  return true;
}

bool EventBackend::remove(posix::fd_t fd, native_flags_t flags) noexcept
{
  return false;
}

bool EventBackend::poll(int timeout) noexcept
{
  native_flags_t flags;
  uint_t count = 0;
  timespec tout;
  tout.tv_sec = timeout / 1000;
  tout.tv_nsec = (timeout % 1000) * 1000;

  if(::port_getn(s_platform.port,
                 &s_platform.pinput.data(), s_platform.pinput.size(),
                 s_platform.poutput, MAX_EVENTS, &count
                 &tout) == posix::error_response)
    return false;

  port_event_t* end = s_platform.poutput + count;

  for(port_event_t* pos = s_platform.poutput; pos != end; ++pos) // iterate through results
  {
    //flags = from_kevent(*pos);

    results.emplace(posix::fd_t(pos->ident), flags);
  }
  return true;
}

#elif defined(__minix) // MINIX
#error No event backend code exists in PDTK for MINIX!  Please submit a patch!

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
#error No event backend code exists in PDTK for QNX!  Please submit a patch!

#elif defined(__hpux) // HP-UX
// see http://nixdoc.net/man-pages/HP-UX/man7/poll.7.html
// and https://www.freebsd.org/cgi/man.cgi?query=poll&sektion=7&apropos=0&manpath=HP-UX+11.22
// uses /dev/poll
#error No event backend code exists in PDTK for HP-UX!  Please submit a patch!

#include <sys/devpoll.h>

#elif defined(_AIX) // IBM AIX
// see https://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.basetrf1/pollset.htm
// uses pollset_* functions

#include <sys/poll.h>
#include <sys/pollset.h>

  pollset_t n;
  n = pollset_create(-1);
  pollset_destroy(n);

#error No event backend code exists in PDTK for IBM AIX!  Please submit a patch!

#elif defined(BSD)
#error Unrecognized BSD derivative!

#elif defined(__unix__)
#error Unrecognized UNIX variant!

#else
#error This platform is not supported.
#endif
<|endoftext|>


<commit_before>#include "processevent.h"

// PUT
#include <specialized/osdetect.h>
#include <specialized/eventbackend.h>

#if defined(FORCE_PROCESS_POLLING)
# pragma message("Forcing use of polling /proc to detect process events.")
# define FALLBACK_ON_PROCESS_POLLING

#elif defined(__linux__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,6,15) /* Linux 2.6.15+ */

// Linux
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>

// PUT
# include <cxxutils/posix_helpers.h>
# include <cxxutils/socket_helpers.h>
# include <cxxutils/vterm.h>

// process flags
static constexpr uint8_t from_native_flags(const native_flags_t flags) noexcept
{
  return
      (flags & proc_event::PROC_EVENT_EXEC ? ProcessEvent::Exec : 0) |
      (flags & proc_event::PROC_EVENT_EXIT ? ProcessEvent::Exit : 0) |
      (flags & proc_event::PROC_EVENT_FORK ? ProcessEvent::Fork : 0) ;
}
/*
static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? native_flags_t(proc_event::PROC_EVENT_EXEC) : 0) | // Process called exec*()
      (flags & ProcessEvent::Exit ? native_flags_t(proc_event::PROC_EVENT_EXIT) : 0) | // Process exited
      (flags & ProcessEvent::Fork ? native_flags_t(proc_event::PROC_EVENT_FORK) : 0) ; // Process forked
}
*/

struct ProcessEvent::platform_dependant // process notification (process events connector)
{
  enum {
    Read = 0,
    Write = 1,
  };

  bool permitted;
  posix::fd_t fd;
  struct eventinfo_t
  {
    posix::fd_t fd[2]; // two fds for pipe based communication
    Flags_t flags;
  };

  std::unordered_map<pid_t, eventinfo_t> events;

  platform_dependant(void) noexcept
  {
    fd = posix::socket(EDomain::netlink, EType::datagram, EProtocol::connector);
    flaw(fd == posix::invalid_descriptor,
         terminal::warning,,,
         "Unable to open a netlink socket for Process Events Connector: %s", posix::strerror(errno))

    sockaddr_nl sa_nl;
    sa_nl.nl_family = PF_NETLINK;
    sa_nl.nl_groups = CN_IDX_PROC;
    sa_nl.nl_pid = uint32_t(getpid());

    permitted = posix::bind(fd, reinterpret_cast<struct sockaddr *>(&sa_nl), sizeof(sa_nl));
    if(!permitted)
    {
      flaw(errno != std::errc::operation_not_permitted,
           terminal::warning,,,
           "Unable to bind socket for Process Events Connector: %s", posix::strerror(errno))
      errno = posix::success_response; // clear error
    }
    else
    {
#pragma pack(push, 1)
      struct alignas(NLMSG_ALIGNTO) procconn_t // 32-bit alignment
      {
        nlmsghdr header; // 16 bytes
        cn_msg message;
        proc_cn_mcast_op operation;
      } procconn;
#pragma pack(pop)
      static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_cn_mcast_op) == sizeof(procconn_t), "compiler needs to pack struct");

      posix::memset(&procconn, 0, sizeof(procconn));
      procconn.header.nlmsg_len = sizeof(procconn);
      procconn.header.nlmsg_pid = uint32_t(getpid());
      procconn.header.nlmsg_type = NLMSG_DONE;
      procconn.message.id.idx = CN_IDX_PROC;
      procconn.message.id.val = CN_VAL_PROC;
      procconn.message.len = sizeof(proc_cn_mcast_op);
      procconn.operation = PROC_CN_MCAST_LISTEN;

      flaw(posix::send(fd, &procconn, sizeof(procconn)) == posix::error_response,
           terminal::warning,,,
           "Failed to enable Process Events Connector notifications: %s", posix::strerror(errno));

      EventBackend::add(fd, EventBackend::SimplePollReadFlags,
                        [this](posix::fd_t lambda_fd, native_flags_t) noexcept { read(lambda_fd); });

      posix::fprintf(stderr, "%s%s\n", terminal::information, "Process Events Connector active");
    }
  }

  ~platform_dependant(void) noexcept
  {
    if(permitted)
      EventBackend::remove(fd, EventBackend::SimplePollReadFlags);
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  posix::fd_t add(pid_t pid, Flags_t flags) noexcept
  {
    eventinfo_t data;
    data.flags = flags;
    if(!permitted || !posix::pipe(data.fd))
      return posix::invalid_descriptor;

    posix::fcntl(data.fd[Read ], F_SETFD, FD_CLOEXEC); // close on exec*()
    posix::fcntl(data.fd[Write], F_SETFD, FD_CLOEXEC); // close on exec*()

    auto iter = events.emplace(pid, data);

    // add Berkeley Packet Filter installation code here

    return data.fd[Read];
  }

  bool remove(pid_t pid) noexcept
  {
    auto iter = events.find(pid);
    if(!permitted || iter == events.end())
      return false;

    // add Berkeley Packet Filter removal code here

    posix::close(iter->second.fd[Read]);
    posix::close(iter->second.fd[Write]);
    events.erase(iter);
    return true;
  }

  void read(posix::fd_t procfd) noexcept
  {
#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procmsg_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_event event;
    } procmsg;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_event) == sizeof(procmsg_t), "compiler needs to pack struct");

    pollfd fds = { procfd, POLLIN, 0 };
    while(posix::poll(&fds, 1, 0) > 0 && // while there are messages AND
          posix::recv(procfd, reinterpret_cast<void*>(&procmsg), sizeof(procmsg_t), 0) > 0) // read process event message
    {
      auto iter = events.find(procmsg.event.event_data.id.process_pid); // find event info for this PID
      if(iter != events.end()) // if found...
        posix::write(iter->second.fd[Write], &procmsg.event, sizeof(procmsg.event)); // write process event info into the communications pipe
    }
  }
} ProcessEvent::s_platform;

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  m_fd = s_platform.add(m_pid, m_flags); // add PID to monitor and return communications pipe
  if(m_fd != posix::invalid_descriptor)
    EventBackend::add(m_fd, EventBackend::SimplePollReadFlags, // connect communications pipe to a lambda function
                      [this](posix::fd_t lambda_fd, native_flags_t) noexcept
                      {
                        proc_event data;
                        pollfd fds = { lambda_fd, POLLIN, 0 };
                        while(posix::poll(&fds, 1, 0) > 0 && // while there is another event to be read
                              posix::read(lambda_fd, &data, sizeof(data)) > 0) // read the event
                          switch(from_native_flags(data.what)) // find the type of event
                          {
                            case Flags::Exec: // queue exec signal with PID
                              Object::enqueue(execed,
                                              data.event_data.exec.process_pid);
                              break;
                            case Flags::Exit: // queue exit signal with PID and exit code
                              if(data.event_data.exit.exit_signal) // if killed by a signal
                                Object::enqueue(killed,
                                                data.event_data.exit.process_pid,
                                                *reinterpret_cast<posix::Signal::EId*>(&data.event_data.exit.exit_signal));
                              else // else exited by itself
                                Object::enqueue(exited,
                                                data.event_data.exit.process_pid,
                                                *reinterpret_cast<posix::error_t*>(&data.event_data.exit.exit_code));
                              break;
                            case Flags::Fork: // queue fork signal with PID and child PID
                              Object::enqueue(forked,
                                              data.event_data.fork.parent_pid,
                                              data.event_data.fork.child_pid);
                              break;
                          }
                      });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  if(m_fd != posix::invalid_descriptor)
  {
    EventBackend::remove(m_fd, EventBackend::SimplePollReadFlags);
    s_platform.remove(m_pid);
  }
}

#elif defined(__darwin__)     /* Darwin 7+     */ || \
      defined(__DragonFly__)  /* DragonFly BSD */ || \
      (defined(__FreeBSD__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(4,1,0))  /* FreeBSD 4.1+  */ || \
      (defined(__OpenBSD__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,9,0))  /* OpenBSD 2.9+  */ || \
      (defined(__NetBSD__)  && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,0,0))  /* NetBSD 2+     */

#include <sys/event.h> // kqueue

static constexpr native_flags_t composite_flag(uint16_t actions, int16_t filters, uint32_t flags) noexcept
  { return native_flags_t(actions) | (native_flags_t(uint16_t(filters)) << 16) | (native_flags_t(flags) << 32); }

static constexpr bool flag_subset(native_flags_t flags, native_flags_t subset)
  { return (flags & subset) == subset; }

static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? composite_flag(0, EVFILT_PROC, NOTE_EXEC) : 0) |
      (flags & ProcessEvent::Exit ? composite_flag(0, EVFILT_PROC, NOTE_EXIT) : 0) |
      (flags & ProcessEvent::Fork ? composite_flag(0, EVFILT_PROC, NOTE_FORK) : 0) ;
}

static constexpr ushort extract_filter(native_flags_t flags) noexcept
  { return (flags >> 16) & 0xFFFF; }

template<typename rtype>
static constexpr rtype extract_flags(native_flags_t flags) noexcept
  { return flags >> 32; }

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  EventBackend::add(m_pid, to_native_flags(m_flags), // connect PID event to lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t lambda_flags) noexcept
                    {
                      switch(extract_filter(lambda_flags)) // switch by filtered event type
                      {
                        case Flags::Exec: // queue exec signal with PID
                          Object::enqueue_copy(execed, m_pid);
                          break;
                        case Flags::Exit: // queue exit signal with PID and exit code
                          Object::enqueue_copy(exited, m_pid, extract_flags<posix::error_t>(lambda_flags));
                          break;
                        case Flags::Fork: // queue fork signal with PID and child PID
                          Object::enqueue_copy(forked, m_pid, extract_flags<pid_t>(lambda_flags));
                          break;
                      }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_pid, to_native_flags(m_flags)); // disconnect PID with flags
}

#elif defined(__solaris__) /* Solaris */
# pragma message("No process event backend code exists in PUT for Solaris!  Please submit a patch!")
# define FALLBACK_ON_PROCESS_POLLING

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
# pragma message("No process event backend code exists in PUT for QNX!  Please submit a patch!")
# define FALLBACK_ON_PROCESS_POLLING

#else
# pragma message("No platform specific process event detection code! Falling back on polling /proc.")
# define FALLBACK_ON_PROCESS_POLLING
#endif

#if defined(FALLBACK_ON_PROCESS_POLLING)
#error TODO: implement process event detection code by polling /proc.
#endif
<commit_msg>ensure it doesn't exist already<commit_after>#include "processevent.h"

// PUT
#include <specialized/osdetect.h>
#include <specialized/eventbackend.h>

#if defined(FORCE_PROCESS_POLLING)
# pragma message("Forcing use of polling /proc to detect process events.")
# define FALLBACK_ON_PROCESS_POLLING

#elif defined(__linux__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,6,15) /* Linux 2.6.15+ */

// Linux
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>

// PUT
# include <cxxutils/posix_helpers.h>
# include <cxxutils/socket_helpers.h>
# include <cxxutils/vterm.h>

// process flags
static constexpr uint8_t from_native_flags(const native_flags_t flags) noexcept
{
  return
      (flags & proc_event::PROC_EVENT_EXEC ? ProcessEvent::Exec : 0) |
      (flags & proc_event::PROC_EVENT_EXIT ? ProcessEvent::Exit : 0) |
      (flags & proc_event::PROC_EVENT_FORK ? ProcessEvent::Fork : 0) ;
}
/*
static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? native_flags_t(proc_event::PROC_EVENT_EXEC) : 0) | // Process called exec*()
      (flags & ProcessEvent::Exit ? native_flags_t(proc_event::PROC_EVENT_EXIT) : 0) | // Process exited
      (flags & ProcessEvent::Fork ? native_flags_t(proc_event::PROC_EVENT_FORK) : 0) ; // Process forked
}
*/

struct ProcessEvent::platform_dependant // process notification (process events connector)
{
  enum {
    Read = 0,
    Write = 1,
  };

  bool permitted;
  posix::fd_t fd;
  struct eventinfo_t
  {
    posix::fd_t fd[2]; // two fds for pipe based communication
    Flags_t flags;
  };

  std::unordered_map<pid_t, eventinfo_t> events;

  platform_dependant(void) noexcept
  {
    fd = posix::socket(EDomain::netlink, EType::datagram, EProtocol::connector);
    flaw(fd == posix::invalid_descriptor,
         terminal::warning,,,
         "Unable to open a netlink socket for Process Events Connector: %s", posix::strerror(errno))

    sockaddr_nl sa_nl;
    sa_nl.nl_family = PF_NETLINK;
    sa_nl.nl_groups = CN_IDX_PROC;
    sa_nl.nl_pid = uint32_t(getpid());

    permitted = posix::bind(fd, reinterpret_cast<struct sockaddr *>(&sa_nl), sizeof(sa_nl));
    if(!permitted)
    {
      flaw(errno != std::errc::operation_not_permitted,
           terminal::warning,,,
           "Unable to bind socket for Process Events Connector: %s", posix::strerror(errno))
      errno = posix::success_response; // clear error
    }
    else
    {
#pragma pack(push, 1)
      struct alignas(NLMSG_ALIGNTO) procconn_t // 32-bit alignment
      {
        nlmsghdr header; // 16 bytes
        cn_msg message;
        proc_cn_mcast_op operation;
      } procconn;
#pragma pack(pop)
      static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_cn_mcast_op) == sizeof(procconn_t), "compiler needs to pack struct");

      posix::memset(&procconn, 0, sizeof(procconn));
      procconn.header.nlmsg_len = sizeof(procconn);
      procconn.header.nlmsg_pid = uint32_t(getpid());
      procconn.header.nlmsg_type = NLMSG_DONE;
      procconn.message.id.idx = CN_IDX_PROC;
      procconn.message.id.val = CN_VAL_PROC;
      procconn.message.len = sizeof(proc_cn_mcast_op);
      procconn.operation = PROC_CN_MCAST_LISTEN;

      flaw(posix::send(fd, &procconn, sizeof(procconn)) == posix::error_response,
           terminal::warning,,,
           "Failed to enable Process Events Connector notifications: %s", posix::strerror(errno));

      EventBackend::add(fd, EventBackend::SimplePollReadFlags,
                        [this](posix::fd_t lambda_fd, native_flags_t) noexcept { read(lambda_fd); });

      posix::fprintf(stderr, "%s%s\n", terminal::information, "Process Events Connector active");
    }
  }

  ~platform_dependant(void) noexcept
  {
    if(permitted)
      EventBackend::remove(fd, EventBackend::SimplePollReadFlags);
    posix::close(fd);
    fd = posix::invalid_descriptor;
  }

  posix::fd_t add(pid_t pid, Flags_t flags) noexcept
  {
    if(events.find(pid) != events.end())
      return events.find(pid)->second.fd[Read];

    eventinfo_t data;
    data.flags = flags;
    if(!permitted || !posix::pipe(data.fd))
      return posix::invalid_descriptor;

    posix::fcntl(data.fd[Read ], F_SETFD, FD_CLOEXEC); // close on exec*()
    posix::fcntl(data.fd[Write], F_SETFD, FD_CLOEXEC); // close on exec*()

    auto iter = events.emplace(pid, data);

    // add Berkeley Packet Filter installation code here

    return data.fd[Read];
  }

  bool remove(pid_t pid) noexcept
  {
    auto iter = events.find(pid);
    if(!permitted || iter == events.end())
      return false;

    // add Berkeley Packet Filter removal code here

    posix::close(iter->second.fd[Read]);
    posix::close(iter->second.fd[Write]);
    events.erase(iter);
    return true;
  }

  void read(posix::fd_t procfd) noexcept
  {
#pragma pack(push, 1)
    struct alignas(NLMSG_ALIGNTO) procmsg_t // 32-bit alignment
    {
      nlmsghdr header; // 16 bytes
      cn_msg message;
      proc_event event;
    } procmsg;
#pragma pack(pop)
    static_assert(sizeof(nlmsghdr) + sizeof(cn_msg) + sizeof(proc_event) == sizeof(procmsg_t), "compiler needs to pack struct");

    pollfd fds = { procfd, POLLIN, 0 };
    while(posix::poll(&fds, 1, 0) > 0 && // while there are messages AND
          posix::recv(procfd, reinterpret_cast<void*>(&procmsg), sizeof(procmsg_t), 0) > 0) // read process event message
    {
      auto iter = events.find(procmsg.event.event_data.id.process_pid); // find event info for this PID
      if(iter != events.end()) // if found...
        posix::write(iter->second.fd[Write], &procmsg.event, sizeof(procmsg.event)); // write process event info into the communications pipe
    }
  }
} ProcessEvent::s_platform;

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  m_fd = s_platform.add(m_pid, m_flags); // add PID to monitor and return communications pipe
  if(m_fd != posix::invalid_descriptor)
    EventBackend::add(m_fd, EventBackend::SimplePollReadFlags, // connect communications pipe to a lambda function
                      [this](posix::fd_t lambda_fd, native_flags_t) noexcept
                      {
                        proc_event data;
                        pollfd fds = { lambda_fd, POLLIN, 0 };
                        while(posix::poll(&fds, 1, 0) > 0 && // while there is another event to be read
                              posix::read(lambda_fd, &data, sizeof(data)) > 0) // read the event
                          switch(from_native_flags(data.what)) // find the type of event
                          {
                            case Flags::Exec: // queue exec signal with PID
                              Object::enqueue(execed,
                                              data.event_data.exec.process_pid);
                              break;
                            case Flags::Exit: // queue exit signal with PID and exit code
                              if(data.event_data.exit.exit_signal) // if killed by a signal
                                Object::enqueue(killed,
                                                data.event_data.exit.process_pid,
                                                *reinterpret_cast<posix::Signal::EId*>(&data.event_data.exit.exit_signal));
                              else // else exited by itself
                                Object::enqueue(exited,
                                                data.event_data.exit.process_pid,
                                                *reinterpret_cast<posix::error_t*>(&data.event_data.exit.exit_code));
                              break;
                            case Flags::Fork: // queue fork signal with PID and child PID
                              Object::enqueue(forked,
                                              data.event_data.fork.parent_pid,
                                              data.event_data.fork.child_pid);
                              break;
                          }
                      });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  if(m_fd != posix::invalid_descriptor)
  {
    EventBackend::remove(m_fd, EventBackend::SimplePollReadFlags);
    s_platform.remove(m_pid);
  }
}

#elif defined(__darwin__)     /* Darwin 7+     */ || \
      defined(__DragonFly__)  /* DragonFly BSD */ || \
      (defined(__FreeBSD__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(4,1,0))  /* FreeBSD 4.1+  */ || \
      (defined(__OpenBSD__) && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,9,0))  /* OpenBSD 2.9+  */ || \
      (defined(__NetBSD__)  && KERNEL_VERSION_CODE >= KERNEL_VERSION(2,0,0))  /* NetBSD 2+     */

#include <sys/event.h> // kqueue

static constexpr native_flags_t composite_flag(uint16_t actions, int16_t filters, uint32_t flags) noexcept
  { return native_flags_t(actions) | (native_flags_t(uint16_t(filters)) << 16) | (native_flags_t(flags) << 32); }

static constexpr bool flag_subset(native_flags_t flags, native_flags_t subset)
  { return (flags & subset) == subset; }

static constexpr native_flags_t to_native_flags(const uint8_t flags) noexcept
{
  return
      (flags & ProcessEvent::Exec ? composite_flag(0, EVFILT_PROC, NOTE_EXEC) : 0) |
      (flags & ProcessEvent::Exit ? composite_flag(0, EVFILT_PROC, NOTE_EXIT) : 0) |
      (flags & ProcessEvent::Fork ? composite_flag(0, EVFILT_PROC, NOTE_FORK) : 0) ;
}

static constexpr ushort extract_filter(native_flags_t flags) noexcept
  { return (flags >> 16) & 0xFFFF; }

template<typename rtype>
static constexpr rtype extract_flags(native_flags_t flags) noexcept
  { return flags >> 32; }

ProcessEvent::ProcessEvent(pid_t _pid, Flags_t _flags) noexcept
  : m_pid(_pid), m_flags(_flags), m_fd(posix::invalid_descriptor)
{
  EventBackend::add(m_pid, to_native_flags(m_flags), // connect PID event to lambda function
                    [this](posix::fd_t lambda_fd, native_flags_t lambda_flags) noexcept
                    {
                      switch(extract_filter(lambda_flags)) // switch by filtered event type
                      {
                        case Flags::Exec: // queue exec signal with PID
                          Object::enqueue_copy(execed, m_pid);
                          break;
                        case Flags::Exit: // queue exit signal with PID and exit code
                          Object::enqueue_copy(exited, m_pid, extract_flags<posix::error_t>(lambda_flags));
                          break;
                        case Flags::Fork: // queue fork signal with PID and child PID
                          Object::enqueue_copy(forked, m_pid, extract_flags<pid_t>(lambda_flags));
                          break;
                      }
                    });
}

ProcessEvent::~ProcessEvent(void) noexcept
{
  EventBackend::remove(m_pid, to_native_flags(m_flags)); // disconnect PID with flags
}

#elif defined(__solaris__) /* Solaris */
# pragma message("No process event backend code exists in PUT for Solaris!  Please submit a patch!")
# define FALLBACK_ON_PROCESS_POLLING

#elif defined(__QNX__) // QNX
// QNX docs: http://www.qnx.com/developers/docs/7.0.0/index.html#com.qnx.doc.neutrino.devctl/topic/about.html
# pragma message("No process event backend code exists in PUT for QNX!  Please submit a patch!")
# define FALLBACK_ON_PROCESS_POLLING

#else
# pragma message("No platform specific process event detection code! Falling back on polling /proc.")
# define FALLBACK_ON_PROCESS_POLLING
#endif

#if defined(FALLBACK_ON_PROCESS_POLLING)
#error TODO: implement process event detection code by polling /proc.
#endif
<|endoftext|>


<commit_before>#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

#define OCCUPIED_STATE                  0x0000
#define OCCUPIED_TO_UNOCCUPIED_DELAY    0x0010
#define HUE_SENSITIVITY                 0x0030
#define HUE_SENSITIVITY_MAX             0x0031

/*! Handle packets related to the ZCL occupancy sensing cluster.
    \param ind the APS level data indication containing the ZCL packet
    \param zclFrame the actual ZCL frame which holds the occupancy sensing cluster command or attribute
 */
void DeRestPluginPrivate::handleOccupancySensingClusterIndication(const deCONZ::ApsDataIndication &ind, const deCONZ::ZclFrame &zclFrame)
{
    if (zclFrame.isDefaultResponse())
    {
        return;
    }

    Sensor *sensor = getSensorNodeForAddressEndpointAndCluster(ind.srcAddress(), ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID );

    if (!sensor)
    {
        DBG_Printf(DBG_INFO, "No presence sensor found for 0x%016llX, endpoint: 0x%02X\n", ind.srcAddress().ext(), ind.srcEndpoint());
        return;
    }

    QDataStream stream(zclFrame.payload());
    stream.setByteOrder(QDataStream::LittleEndian);

    bool isReadAttr = false;
    bool isReporting = false;
    if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReadAttributesResponseId)
    {
        isReadAttr = true;
    }
    else if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReportAttributesId)
    {
        isReporting = true;
    }
    else
    {
        return; // neither ZCL Report nor ZCL Read Attributes Response
    }

    const NodeValue::UpdateType updateType = isReadAttr ? NodeValue::UpdateByZclRead : NodeValue::UpdateByZclReport;

    bool configUpdated = false;
    bool stateUpdated = false;

    while (!stream.atEnd())
    {
        quint16 attrId;
        quint8 attrTypeId;

        stream >> attrId;
        if (isReadAttr)
        {
            quint8 status;
            stream >> status;  // Read Attribute Response status
            if (status != deCONZ::ZclSuccessStatus)
            {
                continue;
            }
        }
        stream >> attrTypeId;

        deCONZ::ZclAttribute attr(attrId, attrTypeId, QLatin1String(""), deCONZ::ZclRead, false);

        if (!attr.readFromStream(stream))
        {
            continue;
        }

        ResourceItem *item = nullptr;

        switch (attrId)
        {
        case OCCUPIED_STATE:
        {
            quint8 occupancy = attr.numericValue().u8;
            item = sensor->item(RStatePresence);

            if (item)
            {
                item->setValue(occupancy);
                enqueueEvent(Event(RSensors, RStatePresence, sensor->id(), item));
                stateUpdated = true;

                const NodeValue &val = sensor->getZclValue(OCCUPANCY_SENSING_CLUSTER_ID, OCCUPIED_STATE);

                // prepare to automatically set presence to false
                if (item->toBool())
                {
                    if (val.maxInterval > 0 && updateType == NodeValue::UpdateByZclReport)
                    {
                        // prevent setting presence back to false, when report.maxInterval > config.duration
                        sensor->durationDue = item->lastSet().addSecs(val.maxInterval);
                    }
                    else
                    {
                        ResourceItem *item2 = sensor->item(RConfigDuration);
                        if (item2 && item2->toNumber() > 0)
                        {
                            sensor->durationDue = item->lastSet().addSecs(item2->toNumber());
                        }
                    }
                }
            }

            sensor->setZclValue(updateType, ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID, OCCUPIED_STATE, attr.numericValue());
        }
            break;

        case OCCUPIED_TO_UNOCCUPIED_DELAY:
        {
            if (sensor->modelId().startsWith(QLatin1String("FLS-NB")) || sensor->modelId() == QLatin1String("LG IP65 HMS"))
            {
                quint16 duration = attr.numericValue().u16;
                item = sensor->item(RConfigDuration);

                if (!item) { item = sensor->addItem(DataTypeUInt16, RConfigDuration); }

                if (item && item->toNumber() != duration)
                {
                    enqueueEvent(Event(RSensors, RConfigDuration, sensor->id(), item));

                    if (item->toNumber() <= 0)
                    {
                        DBG_Printf(DBG_INFO, "got occupied to unoccupied delay %u\n", duration);
                        item->setValue(duration);
                        configUpdated = true;
                    }
                    else
                    {
                        DBG_Printf(DBG_INFO, "occupied to unoccupied delay is %u should be %u, force rewrite\n", duration, (quint16)item->toNumber());
                        if (!sensor->mustRead(WRITE_OCCUPANCY_CONFIG))
                        {
                            sensor->enableRead(WRITE_OCCUPANCY_CONFIG);
                            sensor->setNextReadTime(WRITE_OCCUPANCY_CONFIG, queryTime);
                            queryTime = queryTime.addSecs(1);
                        }

                        if (!sensor->mustRead(READ_OCCUPANCY_CONFIG))
                        {
                            sensor->enableRead(READ_OCCUPANCY_CONFIG);
                            sensor->setNextReadTime(READ_OCCUPANCY_CONFIG, queryTime);
                            queryTime = queryTime.addSecs(5);
                        }
                        //Q_Q(DeRestPlugin);
                        //q->startZclAttributeTimer(q->checkZclAttributesDelay);

                        Q_Q(DeRestPlugin);
                        q->startZclAttributeTimer(checkZclAttributesDelay);
                    }
                }
            }
            else
            {
                quint16 delay = attr.numericValue().u16;
                item = sensor->item(RConfigDelay);

                if (item && item->toNumber() != delay)
                {
                    item->setValue(delay);
                    enqueueEvent(Event(RSensors, RConfigDelay, sensor->id(), item));
                    configUpdated = true;
                }

                if (sensor->mustRead(WRITE_DELAY))
                {
                    ResourceItem *item = sensor->item(RConfigPending);
                    if (item)
                    {
                        quint16 mask = item->toNumber();
                        mask &= ~R_PENDING_DELAY;
                        item->setValue(mask);
                        enqueueEvent(Event(RSensors, RConfigPending, sensor->id(), item));
                    }
                    sensor->clearRead(WRITE_DELAY);
                }
            }

            sensor->setZclValue(updateType, ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID, OCCUPIED_TO_UNOCCUPIED_DELAY, attr.numericValue());
        }
            break;

        case HUE_SENSITIVITY:
        {
            NodeValue &val = sensor->getZclValue(OCCUPANCY_SENSING_CLUSTER_ID, HUE_SENSITIVITY);
            // allow proper binding checks
            if (val.minInterval == 0 || val.maxInterval == 0)
            {
                val.minInterval = 5;      // value used by Hue bridge
                val.maxInterval = 7200;   // value used by Hue bridge
            }

            quint8 sensitivity = attr.numericValue().u8;
            item = sensor->item(RConfigSensitivity);

            if (item && item->toNumber() != sensitivity)
            {
                item->setValue(sensitivity);
                enqueueEvent(Event(RSensors, RConfigSensitivity, sensor->id(), item));
                configUpdated = true;
            }

            if (sensor->mustRead(WRITE_SENSITIVITY))
            {
                ResourceItem *item = sensor->item(RConfigPending);
                if (item)
                {
                    quint16 mask = item->toNumber();
                    mask &= ~R_PENDING_SENSITIVITY;
                    item->setValue(mask);
                    Event e(RSensors, RConfigPending, sensor->id(), item);
                    enqueueEvent(e);
                }
                sensor->clearRead(WRITE_SENSITIVITY);
            }

            sensor->setZclValue(updateType, ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID, HUE_SENSITIVITY, attr.numericValue());
        }
            break;

        case HUE_SENSITIVITY_MAX:
        {
            quint8 sensitivitymax = attr.numericValue().u8;
            item = sensor->item(RConfigSensitivityMax);

            if (item && item->toNumber() != sensitivitymax)
            {
                item->setValue(sensitivitymax);
                enqueueEvent(Event(RSensors, RConfigSensitivityMax, sensor->id(), item));
                configUpdated = true;
            }

            sensor->setZclValue(updateType, ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID, HUE_SENSITIVITY_MAX, attr.numericValue());
        }
            break;

        default:
            break;
        }
    }

    if (stateUpdated)
    {
        sensor->updateStateTimestamp();
        enqueueEvent(Event(RSensors, RStateLastUpdated, sensor->id()));
    }

    if (configUpdated || stateUpdated)
    {
        updateSensorEtag(&*sensor);
        sensor->setNeedSaveDatabase(true);
        queSaveDb(DB_SENSORS, DB_SHORT_SAVE_DELAY);
    }
}
<commit_msg>Followup #4983 remove unused isReporting variable; fix build<commit_after>#include "de_web_plugin.h"
#include "de_web_plugin_private.h"

#define OCCUPIED_STATE                  0x0000
#define OCCUPIED_TO_UNOCCUPIED_DELAY    0x0010
#define HUE_SENSITIVITY                 0x0030
#define HUE_SENSITIVITY_MAX             0x0031

/*! Handle packets related to the ZCL occupancy sensing cluster.
    \param ind the APS level data indication containing the ZCL packet
    \param zclFrame the actual ZCL frame which holds the occupancy sensing cluster command or attribute
 */
void DeRestPluginPrivate::handleOccupancySensingClusterIndication(const deCONZ::ApsDataIndication &ind, const deCONZ::ZclFrame &zclFrame)
{
    if (zclFrame.isDefaultResponse())
    {
        return;
    }

    Sensor *sensor = getSensorNodeForAddressEndpointAndCluster(ind.srcAddress(), ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID );

    if (!sensor)
    {
        DBG_Printf(DBG_INFO, "No presence sensor found for 0x%016llX, endpoint: 0x%02X\n", ind.srcAddress().ext(), ind.srcEndpoint());
        return;
    }

    QDataStream stream(zclFrame.payload());
    stream.setByteOrder(QDataStream::LittleEndian);

    bool isReadAttr = false;

    if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReadAttributesResponseId)
    {
        isReadAttr = true;
    }
    else if (zclFrame.isProfileWideCommand() && zclFrame.commandId() == deCONZ::ZclReportAttributesId)
    {
    }
    else
    {
        return; // neither ZCL Report nor ZCL Read Attributes Response
    }

    const NodeValue::UpdateType updateType = isReadAttr ? NodeValue::UpdateByZclRead : NodeValue::UpdateByZclReport;

    bool configUpdated = false;
    bool stateUpdated = false;

    while (!stream.atEnd())
    {
        quint16 attrId;
        quint8 attrTypeId;

        stream >> attrId;
        if (isReadAttr)
        {
            quint8 status;
            stream >> status;  // Read Attribute Response status
            if (status != deCONZ::ZclSuccessStatus)
            {
                continue;
            }
        }
        stream >> attrTypeId;

        deCONZ::ZclAttribute attr(attrId, attrTypeId, QLatin1String(""), deCONZ::ZclRead, false);

        if (!attr.readFromStream(stream))
        {
            continue;
        }

        ResourceItem *item = nullptr;

        switch (attrId)
        {
        case OCCUPIED_STATE:
        {
            quint8 occupancy = attr.numericValue().u8;
            item = sensor->item(RStatePresence);

            if (item)
            {
                item->setValue(occupancy);
                enqueueEvent(Event(RSensors, RStatePresence, sensor->id(), item));
                stateUpdated = true;

                const NodeValue &val = sensor->getZclValue(OCCUPANCY_SENSING_CLUSTER_ID, OCCUPIED_STATE);

                // prepare to automatically set presence to false
                if (item->toBool())
                {
                    if (val.maxInterval > 0 && updateType == NodeValue::UpdateByZclReport)
                    {
                        // prevent setting presence back to false, when report.maxInterval > config.duration
                        sensor->durationDue = item->lastSet().addSecs(val.maxInterval);
                    }
                    else
                    {
                        ResourceItem *item2 = sensor->item(RConfigDuration);
                        if (item2 && item2->toNumber() > 0)
                        {
                            sensor->durationDue = item->lastSet().addSecs(item2->toNumber());
                        }
                    }
                }
            }

            sensor->setZclValue(updateType, ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID, OCCUPIED_STATE, attr.numericValue());
        }
            break;

        case OCCUPIED_TO_UNOCCUPIED_DELAY:
        {
            if (sensor->modelId().startsWith(QLatin1String("FLS-NB")) || sensor->modelId() == QLatin1String("LG IP65 HMS"))
            {
                // TODO this if branch needs to be refactored or better removed later on
                quint16 duration = attr.numericValue().u16;
                item = sensor->item(RConfigDuration);

                if (!item) { item = sensor->addItem(DataTypeUInt16, RConfigDuration); }

                if (item && item->toNumber() != duration)
                {
                    enqueueEvent(Event(RSensors, RConfigDuration, sensor->id(), item));

                    if (item->toNumber() <= 0)
                    {
                        DBG_Printf(DBG_INFO, "got occupied to unoccupied delay %u\n", duration);
                        item->setValue(duration);
                        configUpdated = true;
                    }
                    else
                    {
                        DBG_Printf(DBG_INFO, "occupied to unoccupied delay is %u should be %u, force rewrite\n", duration, (quint16)item->toNumber());
                        if (!sensor->mustRead(WRITE_OCCUPANCY_CONFIG))
                        {
                            sensor->enableRead(WRITE_OCCUPANCY_CONFIG);
                            sensor->setNextReadTime(WRITE_OCCUPANCY_CONFIG, queryTime);
                            queryTime = queryTime.addSecs(1);
                        }

                        if (!sensor->mustRead(READ_OCCUPANCY_CONFIG))
                        {
                            sensor->enableRead(READ_OCCUPANCY_CONFIG);
                            sensor->setNextReadTime(READ_OCCUPANCY_CONFIG, queryTime);
                            queryTime = queryTime.addSecs(5);
                        }

                        Q_Q(DeRestPlugin);
                        q->startZclAttributeTimer(750);
                    }
                }
            }
            else
            {
                quint16 delay = attr.numericValue().u16;
                item = sensor->item(RConfigDelay);

                if (item && item->toNumber() != delay)
                {
                    item->setValue(delay);
                    enqueueEvent(Event(RSensors, RConfigDelay, sensor->id(), item));
                    configUpdated = true;
                }

                if (sensor->mustRead(WRITE_DELAY))
                {
                    ResourceItem *item = sensor->item(RConfigPending);
                    if (item)
                    {
                        quint16 mask = item->toNumber();
                        mask &= ~R_PENDING_DELAY;
                        item->setValue(mask);
                        enqueueEvent(Event(RSensors, RConfigPending, sensor->id(), item));
                    }
                    sensor->clearRead(WRITE_DELAY);
                }
            }

            sensor->setZclValue(updateType, ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID, OCCUPIED_TO_UNOCCUPIED_DELAY, attr.numericValue());
        }
            break;

        case HUE_SENSITIVITY:
        {
            NodeValue &val = sensor->getZclValue(OCCUPANCY_SENSING_CLUSTER_ID, HUE_SENSITIVITY);
            // allow proper binding checks
            if (val.minInterval == 0 || val.maxInterval == 0)
            {
                val.minInterval = 5;      // value used by Hue bridge
                val.maxInterval = 7200;   // value used by Hue bridge
            }

            quint8 sensitivity = attr.numericValue().u8;
            item = sensor->item(RConfigSensitivity);

            if (item && item->toNumber() != sensitivity)
            {
                item->setValue(sensitivity);
                enqueueEvent(Event(RSensors, RConfigSensitivity, sensor->id(), item));
                configUpdated = true;
            }

            if (sensor->mustRead(WRITE_SENSITIVITY))
            {
                ResourceItem *item = sensor->item(RConfigPending);
                if (item)
                {
                    quint16 mask = item->toNumber();
                    mask &= ~R_PENDING_SENSITIVITY;
                    item->setValue(mask);
                    Event e(RSensors, RConfigPending, sensor->id(), item);
                    enqueueEvent(e);
                }
                sensor->clearRead(WRITE_SENSITIVITY);
            }

            sensor->setZclValue(updateType, ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID, HUE_SENSITIVITY, attr.numericValue());
        }
            break;

        case HUE_SENSITIVITY_MAX:
        {
            quint8 sensitivitymax = attr.numericValue().u8;
            item = sensor->item(RConfigSensitivityMax);

            if (item && item->toNumber() != sensitivitymax)
            {
                item->setValue(sensitivitymax);
                enqueueEvent(Event(RSensors, RConfigSensitivityMax, sensor->id(), item));
                configUpdated = true;
            }

            sensor->setZclValue(updateType, ind.srcEndpoint(), OCCUPANCY_SENSING_CLUSTER_ID, HUE_SENSITIVITY_MAX, attr.numericValue());
        }
            break;

        default:
            break;
        }
    }

    if (stateUpdated)
    {
        sensor->updateStateTimestamp();
        enqueueEvent(Event(RSensors, RStateLastUpdated, sensor->id()));
    }

    if (configUpdated || stateUpdated)
    {
        updateSensorEtag(&*sensor);
        sensor->setNeedSaveDatabase(true);
        queSaveDb(DB_SENSORS, DB_SHORT_SAVE_DELAY);
    }
}
<|endoftext|>


<commit_before>/*  This file is part of the Vc library.

    Copyright (C) 2009 Matthias Kretz <kretz@kde.org>

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

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

    You should have received a copy of the GNU Lesser General Public
    License along with Vc.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <Vc/Vc>
#include "unittest.h"
#include <iostream>
#include <limits>

using namespace Vc;

template<typename Vec> void testZero()
{
    Vec a(Zero), b(Zero);
    COMPARE(a, b);
    Vec c, d(1);
    c.setZero();
    COMPARE(a, c);
    d.setZero();
    COMPARE(a, d);
    d = 0.;
    COMPARE(a, d);
    const typename Vec::EntryType zero = 0;
    COMPARE(a, Vec(zero));
    COMPARE(b, Vec(zero));
    COMPARE(c, Vec(zero));
    COMPARE(d, Vec(zero));
}

template<typename Vec> void testCmp()
{
    typedef typename Vec::EntryType T;
    Vec a(Zero), b(Zero);
    COMPARE(a, b);
    if (!(a != b).isEmpty()) {
        std::cerr << a << " != " << b << ", (a != b) = " << (a != b) << ", (a == b) = " << (a == b) << std::endl;
    }
    VERIFY((a != b).isEmpty());

    Vec c(1);
    VERIFY((a < c).isFull());
    VERIFY((c > a).isFull());
    VERIFY((a <= b).isFull());
    VERIFY((a <= c).isFull());
    VERIFY((b >= a).isFull());
    VERIFY((c >= a).isFull());

    {
        const T max = static_cast<T>(std::numeric_limits<T>::max() * 0.95);
        const T min = 0;
        const T step = max / 200;
        T j = min;
        VERIFY(Vec(Zero) == Vec(j));
        VERIFY(!(Vec(Zero) < Vec(j)));
        VERIFY(!(Vec(Zero) > Vec(j)));
        VERIFY(!(Vec(Zero) != Vec(j)));
        j += step;
        for (int i = 0; i < 200; ++i, j += step) {
            if(Vec(Zero) >= Vec(j)) {
                std::cout << j << " " << Vec(j) << " " << (Vec(Zero) >= Vec(j)) << std::endl;
            }
            VERIFY(Vec(Zero) < Vec(j));
            VERIFY(Vec(j) > Vec(Zero));
            VERIFY(!(Vec(Zero) >= Vec(j)));
            VERIFY(!(Vec(j) <= Vec(Zero)));
            VERIFY(!static_cast<bool>(Vec(Zero) >= Vec(j)));
            VERIFY(!static_cast<bool>(Vec(j) <= Vec(Zero)));
        }
    }
    if (std::numeric_limits<T>::min() <= 0) {
        const T min = static_cast<T>(std::numeric_limits<T>::min() * 0.95);
        if (min == 0) {
            return;
        }
        const T step = min / -201;
        T j = min;
        for (int i = 0; i < 200; ++i, j += step) {
            VERIFY(Vec(j) < Vec(Zero));
            VERIFY(Vec(Zero) > Vec(j));
            VERIFY(!(Vec(Zero) <= Vec(j)));
            VERIFY(!(Vec(j) >= Vec(Zero)));
        }
    }
}

template<typename Vec> void testIsMix()
{
    Vec a(IndexesFromZero);
    Vec b(Zero);
    Vec c(One);
    if (Vec::Size > 1) {
        VERIFY((a == b).isMix());
        VERIFY((a != b).isMix());
        VERIFY((a == c).isMix());
        VERIFY((a != c).isMix());
        VERIFY(!(a == a).isMix());
        VERIFY(!(a != a).isMix());
    } else { // masks of size 1 can never be a mix of 0 and 1
        VERIFY(!(a == b).isMix());
        VERIFY(!(a != b).isMix());
        VERIFY(!(a == c).isMix());
        VERIFY(!(a != c).isMix());
        VERIFY(!(a == a).isMix());
        VERIFY(!(a != a).isMix());
    }
}

template<typename Vec> void testAdd()
{
    Vec a(Zero), b(Zero);
    COMPARE(a, b);

    a += 1;
    Vec c(1);
    COMPARE(a, c);

    COMPARE(a, b + 1);
    COMPARE(a, b + c);
    Vec x(Zero);
}

template<typename Vec> void testSub()
{
    Vec a(2), b(2);
    COMPARE(a, b);

    a -= 1;
    Vec c(1);
    COMPARE(a, c);

    COMPARE(a, b - 1);
    COMPARE(a, b - c);
}

template<typename T> struct MulRangeHelper
{
    typedef int Iterator;
    static const Iterator Start;
    static const Iterator End;
};
template<> struct MulRangeHelper<unsigned int> {
    typedef unsigned int Iterator;
    static const Iterator Start;
    static const Iterator End;
};
template<> const int MulRangeHelper<float>::Start = -0xffffff;
template<> const int MulRangeHelper<float>::End   =  0xffffff - 133;
template<> const int MulRangeHelper<double>::Start = -0xffffff;
template<> const int MulRangeHelper<double>::End   =  0xffffff - 133;
template<> const int MulRangeHelper<int>::Start = -0x80000000;
template<> const int MulRangeHelper<int>::End   = 0x7fffffff - 110;
const unsigned int MulRangeHelper<unsigned int>::Start = 0;
const unsigned int MulRangeHelper<unsigned int>::End = 0xffffffff - 110;
template<> const int MulRangeHelper<short>::Start = -0x8000;
template<> const int MulRangeHelper<short>::End = 0x7fff - 50;
template<> const int MulRangeHelper<unsigned short>::Start = 0;
template<> const int MulRangeHelper<unsigned short>::End = 0xffff - 50;

template<typename Vec> void testMul()
{
    typedef typename Vec::EntryType T;
    typedef MulRangeHelper<T> Range;
    for (typename Range::Iterator i = Range::Start; i < Range::End; i += 0xef) {
        T i2 = static_cast<T>(i);
        Vec a(i2);
        i2 *= i2 - 9;

        COMPARE(a * (a - 9), Vec(i2));
    }
}

template<typename Vec> void testMulAdd()
{
    for (unsigned int i = 0; i < 0xffff; ++i) {
        const Vec i2(i * i + 1);
        Vec a(i);

        FUZZY_COMPARE(a * a + 1, i2);
    }
}

template<typename Vec> void testMulSub()
{
    for (unsigned int i = 0; i < 0xffff; ++i) {
        const Vec i2(i * i - i);
        Vec a(i);

        FUZZY_COMPARE(a * a - i, i2);
    }
}

template<typename Vec> void testDiv()
{
    typedef typename Vec::EntryType T;
    const T stepsize = std::max(T(1), T(std::numeric_limits<T>::max() / 1024));
    for (T divisor = 1; divisor < 5; ++divisor) {
        for (T scalar = std::numeric_limits<T>::min(); scalar < std::numeric_limits<T>::max(); scalar += stepsize) {
            Vec vector(scalar);
            Vec reference(scalar / divisor);

            COMPARE(vector / divisor, reference) << '\n' << vector << " / " << divisor;
            vector /= divisor;
            COMPARE(vector, reference);
        }
    }
}

template<typename Vec> void testAnd()
{
    Vec a(0x7fff);
    Vec b(0xf);
    COMPARE((a & 0xf), b);
    Vec c(IndexesFromZero);
    COMPARE(c, (c & 0xf));
    const typename Vec::EntryType zero = 0;
    COMPARE((c & 0x7ff0), Vec(zero));
}

template<typename Vec> void testShift()
{
    Vec a(1);
    Vec b(2);

    // left shifts
    COMPARE((a << 1), b);
    COMPARE((a << 2), (a << 2));
    COMPARE((a << 2), (b << 1));

    Vec shifts(IndexesFromZero);
    a <<= shifts;
    for (typename Vec::EntryType i = 0, x = 1; i < Vec::Size; ++i, x <<= 1) {
        COMPARE(a[i], x);
    }

    // right shifts
    a = Vec(4);
    COMPARE((a >> 1), b);
    COMPARE((a >> 2), (a >> 2));
    COMPARE((a >> 2), (b >> 1));

    a = Vec(16);
    a >>= shifts;
    for (typename Vec::EntryType i = 0, x = 16; i < Vec::Size; ++i, x >>= 1) {
        COMPARE(a[i], x);
    }
}

template<typename Vec> void testOnesComplement()
{
    Vec a(One);
    Vec b = ~a;
    COMPARE(~a, b);
    COMPARE(~b, a);
    COMPARE(~(a + b), Vec(Zero));
}

int main(int argc, char **argv)
{
    initTest(argc, argv);

    runTest(testZero<int_v>);
    runTest(testZero<uint_v>);
    runTest(testZero<float_v>);
    runTest(testZero<double_v>);
    runTest(testZero<short_v>);
    runTest(testZero<ushort_v>);
    runTest(testZero<sfloat_v>);

    runTest(testCmp<int_v>);
    runTest(testCmp<uint_v>);
    runTest(testCmp<float_v>);
    runTest(testCmp<double_v>);
    runTest(testCmp<short_v>);
    runTest(testCmp<ushort_v>);
    runTest(testCmp<sfloat_v>);

    runTest(testIsMix<int_v>);
    runTest(testIsMix<uint_v>);
    //runTest(testIsMix<float_v>);
    //runTest(testIsMix<double_v>);
    runTest(testIsMix<short_v>);
    runTest(testIsMix<ushort_v>);
    //runTest(testIsMix<sfloat_v>);

    runTest(testAdd<int_v>);
    runTest(testAdd<uint_v>);
    runTest(testAdd<float_v>);
    runTest(testAdd<double_v>);
    runTest(testAdd<short_v>);
    runTest(testAdd<ushort_v>);
    runTest(testAdd<sfloat_v>);

    runTest(testSub<int_v>);
    runTest(testSub<uint_v>);
    runTest(testSub<float_v>);
    runTest(testSub<double_v>);
    runTest(testSub<short_v>);
    runTest(testSub<ushort_v>);
    runTest(testSub<sfloat_v>);

    runTest(testMul<int_v>);
    runTest(testMul<uint_v>);
    runTest(testMul<float_v>);
    runTest(testMul<double_v>);
    runTest(testMul<short_v>);
    runTest(testMul<ushort_v>);
    runTest(testMul<sfloat_v>);

    runTest(testDiv<int_v>);
    runTest(testDiv<uint_v>);
    runTest(testDiv<float_v>);
    runTest(testDiv<double_v>);
    runTest(testDiv<short_v>);
    runTest(testDiv<ushort_v>);
    runTest(testDiv<sfloat_v>);

    runTest(testAnd<int_v>);
    runTest(testAnd<uint_v>);
    runTest(testAnd<short_v>);
    runTest(testAnd<ushort_v>);
    // no operator& for float/double

    runTest(testShift<int_v>);
    runTest(testShift<uint_v>);
    runTest(testShift<short_v>);
    runTest(testShift<ushort_v>);

    runTest(testMulAdd<int_v>);
    runTest(testMulAdd<uint_v>);
    runTest(testMulAdd<float_v>);
    runTest(testMulAdd<double_v>);
    runTest(testMulAdd<short_v>);
    runTest(testMulAdd<ushort_v>);
    runTest(testMulAdd<sfloat_v>);

    runTest(testMulSub<int_v>);
    runTest(testMulSub<uint_v>);
    runTest(testMulSub<float_v>);
    runTest(testMulSub<double_v>);
    runTest(testMulSub<short_v>);
    runTest(testMulSub<ushort_v>);
    runTest(testMulSub<sfloat_v>);

    runTest(testOnesComplement<int_v>);
    runTest(testOnesComplement<uint_v>);
    runTest(testOnesComplement<short_v>);
    runTest(testOnesComplement<ushort_v>);

    return 0;
}
<commit_msg>test operator-()<commit_after>/*  This file is part of the Vc library.

    Copyright (C) 2009 Matthias Kretz <kretz@kde.org>

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

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

    You should have received a copy of the GNU Lesser General Public
    License along with Vc.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <Vc/Vc>
#include "unittest.h"
#include <iostream>
#include <limits>

using namespace Vc;

template<typename Vec> void testZero()
{
    Vec a(Zero), b(Zero);
    COMPARE(a, b);
    Vec c, d(1);
    c.setZero();
    COMPARE(a, c);
    d.setZero();
    COMPARE(a, d);
    d = 0.;
    COMPARE(a, d);
    const typename Vec::EntryType zero = 0;
    COMPARE(a, Vec(zero));
    COMPARE(b, Vec(zero));
    COMPARE(c, Vec(zero));
    COMPARE(d, Vec(zero));
}

template<typename Vec> void testCmp()
{
    typedef typename Vec::EntryType T;
    Vec a(Zero), b(Zero);
    COMPARE(a, b);
    if (!(a != b).isEmpty()) {
        std::cerr << a << " != " << b << ", (a != b) = " << (a != b) << ", (a == b) = " << (a == b) << std::endl;
    }
    VERIFY((a != b).isEmpty());

    Vec c(1);
    VERIFY((a < c).isFull());
    VERIFY((c > a).isFull());
    VERIFY((a <= b).isFull());
    VERIFY((a <= c).isFull());
    VERIFY((b >= a).isFull());
    VERIFY((c >= a).isFull());

    {
        const T max = static_cast<T>(std::numeric_limits<T>::max() * 0.95);
        const T min = 0;
        const T step = max / 200;
        T j = min;
        VERIFY(Vec(Zero) == Vec(j));
        VERIFY(!(Vec(Zero) < Vec(j)));
        VERIFY(!(Vec(Zero) > Vec(j)));
        VERIFY(!(Vec(Zero) != Vec(j)));
        j += step;
        for (int i = 0; i < 200; ++i, j += step) {
            if(Vec(Zero) >= Vec(j)) {
                std::cout << j << " " << Vec(j) << " " << (Vec(Zero) >= Vec(j)) << std::endl;
            }
            VERIFY(Vec(Zero) < Vec(j));
            VERIFY(Vec(j) > Vec(Zero));
            VERIFY(!(Vec(Zero) >= Vec(j)));
            VERIFY(!(Vec(j) <= Vec(Zero)));
            VERIFY(!static_cast<bool>(Vec(Zero) >= Vec(j)));
            VERIFY(!static_cast<bool>(Vec(j) <= Vec(Zero)));
        }
    }
    if (std::numeric_limits<T>::min() <= 0) {
        const T min = static_cast<T>(std::numeric_limits<T>::min() * 0.95);
        if (min == 0) {
            return;
        }
        const T step = min / -201;
        T j = min;
        for (int i = 0; i < 200; ++i, j += step) {
            VERIFY(Vec(j) < Vec(Zero));
            VERIFY(Vec(Zero) > Vec(j));
            VERIFY(!(Vec(Zero) <= Vec(j)));
            VERIFY(!(Vec(j) >= Vec(Zero)));
        }
    }
}

template<typename Vec> void testIsMix()
{
    Vec a(IndexesFromZero);
    Vec b(Zero);
    Vec c(One);
    if (Vec::Size > 1) {
        VERIFY((a == b).isMix());
        VERIFY((a != b).isMix());
        VERIFY((a == c).isMix());
        VERIFY((a != c).isMix());
        VERIFY(!(a == a).isMix());
        VERIFY(!(a != a).isMix());
    } else { // masks of size 1 can never be a mix of 0 and 1
        VERIFY(!(a == b).isMix());
        VERIFY(!(a != b).isMix());
        VERIFY(!(a == c).isMix());
        VERIFY(!(a != c).isMix());
        VERIFY(!(a == a).isMix());
        VERIFY(!(a != a).isMix());
    }
}

template<typename Vec> void testAdd()
{
    Vec a(Zero), b(Zero);
    COMPARE(a, b);

    a += 1;
    Vec c(1);
    COMPARE(a, c);

    COMPARE(a, b + 1);
    COMPARE(a, b + c);
    Vec x(Zero);
}

template<typename Vec> void testSub()
{
    Vec a(2), b(2);
    COMPARE(a, b);

    a -= 1;
    Vec c(1);
    COMPARE(a, c);

    COMPARE(a, b - 1);
    COMPARE(a, b - c);
}

template<typename T> struct MulRangeHelper
{
    typedef int Iterator;
    static const Iterator Start;
    static const Iterator End;
};
template<> struct MulRangeHelper<unsigned int> {
    typedef unsigned int Iterator;
    static const Iterator Start;
    static const Iterator End;
};
template<> const int MulRangeHelper<float>::Start = -0xffffff;
template<> const int MulRangeHelper<float>::End   =  0xffffff - 133;
template<> const int MulRangeHelper<double>::Start = -0xffffff;
template<> const int MulRangeHelper<double>::End   =  0xffffff - 133;
template<> const int MulRangeHelper<int>::Start = -0x80000000;
template<> const int MulRangeHelper<int>::End   = 0x7fffffff - 110;
const unsigned int MulRangeHelper<unsigned int>::Start = 0;
const unsigned int MulRangeHelper<unsigned int>::End = 0xffffffff - 110;
template<> const int MulRangeHelper<short>::Start = -0x8000;
template<> const int MulRangeHelper<short>::End = 0x7fff - 50;
template<> const int MulRangeHelper<unsigned short>::Start = 0;
template<> const int MulRangeHelper<unsigned short>::End = 0xffff - 50;

template<typename Vec> void testMul()
{
    typedef typename Vec::EntryType T;
    typedef MulRangeHelper<T> Range;
    for (typename Range::Iterator i = Range::Start; i < Range::End; i += 0xef) {
        T i2 = static_cast<T>(i);
        Vec a(i2);
        i2 *= i2 - 9;

        COMPARE(a * (a - 9), Vec(i2));
    }
}

template<typename Vec> void testMulAdd()
{
    for (unsigned int i = 0; i < 0xffff; ++i) {
        const Vec i2(i * i + 1);
        Vec a(i);

        FUZZY_COMPARE(a * a + 1, i2);
    }
}

template<typename Vec> void testMulSub()
{
    for (unsigned int i = 0; i < 0xffff; ++i) {
        const Vec i2(i * i - i);
        Vec a(i);

        FUZZY_COMPARE(a * a - i, i2);
    }
}

template<typename Vec> void testDiv()
{
    typedef typename Vec::EntryType T;
    const T stepsize = std::max(T(1), T(std::numeric_limits<T>::max() / 1024));
    for (T divisor = 1; divisor < 5; ++divisor) {
        for (T scalar = std::numeric_limits<T>::min(); scalar < std::numeric_limits<T>::max(); scalar += stepsize) {
            Vec vector(scalar);
            Vec reference(scalar / divisor);

            COMPARE(vector / divisor, reference) << '\n' << vector << " / " << divisor;
            vector /= divisor;
            COMPARE(vector, reference);
        }
    }
}

template<typename Vec> void testAnd()
{
    Vec a(0x7fff);
    Vec b(0xf);
    COMPARE((a & 0xf), b);
    Vec c(IndexesFromZero);
    COMPARE(c, (c & 0xf));
    const typename Vec::EntryType zero = 0;
    COMPARE((c & 0x7ff0), Vec(zero));
}

template<typename Vec> void testShift()
{
    Vec a(1);
    Vec b(2);

    // left shifts
    COMPARE((a << 1), b);
    COMPARE((a << 2), (a << 2));
    COMPARE((a << 2), (b << 1));

    Vec shifts(IndexesFromZero);
    a <<= shifts;
    for (typename Vec::EntryType i = 0, x = 1; i < Vec::Size; ++i, x <<= 1) {
        COMPARE(a[i], x);
    }

    // right shifts
    a = Vec(4);
    COMPARE((a >> 1), b);
    COMPARE((a >> 2), (a >> 2));
    COMPARE((a >> 2), (b >> 1));

    a = Vec(16);
    a >>= shifts;
    for (typename Vec::EntryType i = 0, x = 16; i < Vec::Size; ++i, x >>= 1) {
        COMPARE(a[i], x);
    }
}

template<typename Vec> void testOnesComplement()
{
    Vec a(One);
    Vec b = ~a;
    COMPARE(~a, b);
    COMPARE(~b, a);
    COMPARE(~(a + b), Vec(Zero));
}

template<typename Vec> void testNegate()
{
    typedef typename Vec::EntryType T;
    typedef MulRangeHelper<T> Range;
    for (typename Range::Iterator i = Range::Start; i < Range::End; i += 0xef) {
        T i2 = static_cast<T>(i);
        Vec a(i2);

        COMPARE(-a, Vec(-i2));
    }
}

int main(int argc, char **argv)
{
    initTest(argc, argv);

    runTest(testZero<int_v>);
    runTest(testZero<uint_v>);
    runTest(testZero<float_v>);
    runTest(testZero<double_v>);
    runTest(testZero<short_v>);
    runTest(testZero<ushort_v>);
    runTest(testZero<sfloat_v>);

    runTest(testCmp<int_v>);
    runTest(testCmp<uint_v>);
    runTest(testCmp<float_v>);
    runTest(testCmp<double_v>);
    runTest(testCmp<short_v>);
    runTest(testCmp<ushort_v>);
    runTest(testCmp<sfloat_v>);

    runTest(testIsMix<int_v>);
    runTest(testIsMix<uint_v>);
    //runTest(testIsMix<float_v>);
    //runTest(testIsMix<double_v>);
    runTest(testIsMix<short_v>);
    runTest(testIsMix<ushort_v>);
    //runTest(testIsMix<sfloat_v>);

    runTest(testAdd<int_v>);
    runTest(testAdd<uint_v>);
    runTest(testAdd<float_v>);
    runTest(testAdd<double_v>);
    runTest(testAdd<short_v>);
    runTest(testAdd<ushort_v>);
    runTest(testAdd<sfloat_v>);

    runTest(testSub<int_v>);
    runTest(testSub<uint_v>);
    runTest(testSub<float_v>);
    runTest(testSub<double_v>);
    runTest(testSub<short_v>);
    runTest(testSub<ushort_v>);
    runTest(testSub<sfloat_v>);

    runTest(testMul<int_v>);
    runTest(testMul<uint_v>);
    runTest(testMul<float_v>);
    runTest(testMul<double_v>);
    runTest(testMul<short_v>);
    runTest(testMul<ushort_v>);
    runTest(testMul<sfloat_v>);

    runTest(testDiv<int_v>);
    runTest(testDiv<uint_v>);
    runTest(testDiv<float_v>);
    runTest(testDiv<double_v>);
    runTest(testDiv<short_v>);
    runTest(testDiv<ushort_v>);
    runTest(testDiv<sfloat_v>);

    runTest(testAnd<int_v>);
    runTest(testAnd<uint_v>);
    runTest(testAnd<short_v>);
    runTest(testAnd<ushort_v>);
    // no operator& for float/double

    runTest(testShift<int_v>);
    runTest(testShift<uint_v>);
    runTest(testShift<short_v>);
    runTest(testShift<ushort_v>);

    runTest(testMulAdd<int_v>);
    runTest(testMulAdd<uint_v>);
    runTest(testMulAdd<float_v>);
    runTest(testMulAdd<double_v>);
    runTest(testMulAdd<short_v>);
    runTest(testMulAdd<ushort_v>);
    runTest(testMulAdd<sfloat_v>);

    runTest(testMulSub<int_v>);
    runTest(testMulSub<uint_v>);
    runTest(testMulSub<float_v>);
    runTest(testMulSub<double_v>);
    runTest(testMulSub<short_v>);
    runTest(testMulSub<ushort_v>);
    runTest(testMulSub<sfloat_v>);

    runTest(testOnesComplement<int_v>);
    runTest(testOnesComplement<uint_v>);
    runTest(testOnesComplement<short_v>);
    runTest(testOnesComplement<ushort_v>);

    testAllTypes(testNegate);

    return 0;
}
<|endoftext|>


<commit_before>/*
    kopeteactivenotification.cpp - View Manager

    Kopete    (c) 2009 by the Kopete developers <kopete-devel@kde.org>

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

#include "kopeteactivenotification.h"

#include <klocale.h>
#include <knotification.h>

namespace Kopete
{
        ActiveNotification::ActiveNotification( KNotification *notification, const QString& id_, ActiveNotifications& notifications_, const QString& body_ )
          : QObject( notification ), id( id_ ),
            notifications( notifications_ ), nEventsSinceNotified( 0 ), body( body_ )
        {
            notifications.insert( id, this );
            static_cast<KNotification *>( parent())->setText("<qt>" + body + "</qt>" );
        }

        /**
         * Remove active notification from queue
         */
        ActiveNotification::~ActiveNotification() {
            notifications.remove( id );
        }

        /**
         * received another message from a sender with a notification
         */
        void ActiveNotification::incrementMessages() {
            KLocalizedString append = nEventsSinceNotified == 1 ? ki18n( "+ %1 more message") : ki18n( "+ %1 more messages");
            KNotification *aParent = static_cast<KNotification *>( parent() );
            aParent->setText( QString( "<qt>" ) + body + "
" + append.subs( ++nEventsSinceNotified ).toString() + "</qt>" );
        }
}
<commit_msg>i18n<commit_after>/*
    kopeteactivenotification.cpp - View Manager

    Kopete    (c) 2009 by the Kopete developers <kopete-devel@kde.org>

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

#include "kopeteactivenotification.h"

#include <klocale.h>
#include <knotification.h>

namespace Kopete
{
        ActiveNotification::ActiveNotification( KNotification *notification, const QString& id_, ActiveNotifications& notifications_, const QString& body_ )
          : QObject( notification ), id( id_ ),
            notifications( notifications_ ), nEventsSinceNotified( 0 ), body( body_ )
        {
            notifications.insert( id, this );
            static_cast<KNotification *>( parent())->setText("<qt>" + body + "</qt>" );
        }

        /**
         * Remove active notification from queue
         */
        ActiveNotification::~ActiveNotification() {
            notifications.remove( id );
        }

        /**
         * received another message from a sender with a notification
         */
        void ActiveNotification::incrementMessages() {
            KLocalizedString append = ki18np( "+ %1 more message", "+ %1 more messages");
            KNotification *aParent = static_cast<KNotification *>( parent() );
            aParent->setText( QString( "<qt>" ) + body + "
" + append.subs( ++nEventsSinceNotified ).toString() + "</qt>" );
        }
}
<|endoftext|>


<commit_before>#pragma once
#include <bts/db/level_map.hpp>

namespace bts { namespace db {

template<typename K, typename V>
class fast_level_map
{
    level_map<K, V>             _ldb;
    fc::optional<fc::path>      _ldb_path;
    bool                        _ldb_enabled = true;

    std::unordered_map<K, V>    _cache;

public:

    ~fast_level_map()
    {
        close();
    }

    void open( const fc::path& path )
    { try {
        FC_ASSERT( !_ldb_path.valid() );
        _ldb_path = path;
        _ldb.open( *_ldb_path );
        const size_t size = _ldb.size();
        if( size > 0 ) _cache.reserve( size );
        for( auto iter = _ldb.begin(); iter.valid(); ++iter )
            _cache.emplace( iter.key(), iter.value() );
    } FC_CAPTURE_AND_RETHROW( (path) ) }

    void close()
    { try {
        if( _ldb_path.valid() )
        {
            if( !_ldb_enabled ) toggle_leveldb( true );
            _ldb.close();
            _ldb_path = fc::optional<fc::path>();
        }
        _cache.clear();
    } FC_CAPTURE_AND_RETHROW() }

    void toggle_leveldb( const bool enabled )
    { try {
        FC_ASSERT( _ldb_path.valid() );
        if( enabled == _ldb_enabled )
            return;

        if( enabled )
        {
            _ldb.open( *_ldb_path );
            auto batch = _ldb.create_batch();
            for( const auto& item : _cache )
                batch.store( item.first, item.second );
            batch.commit();
        }
        else
        {
            _ldb.close();
            fc::remove_all( *_ldb_path );
        }

        _ldb_enabled = enabled;
    } FC_CAPTURE_AND_RETHROW( (enabled) ) }

    void store( const K& key, const V& value )
    { try {
        _cache[ key ] = value;
        if( _ldb_enabled )
            _ldb.store( key, value );
    } FC_CAPTURE_AND_RETHROW( (key)(value) ) }

    void remove( const K& key )
    { try {
        _cache.erase( key );
        if( _ldb_enabled )
            _ldb.remove( key );
    } FC_CAPTURE_AND_RETHROW( (key) ) }

    auto empty()const -> decltype( _cache.empty() )
    {
        return _cache.empty();
    }

    auto size()const -> decltype( _cache.size() )
    {
        return _cache.size();
    }

    auto count( const K& key )const -> decltype( _cache.count( key ) )
    {
        return _cache.count( key );
    }

    auto unordered_begin()const -> decltype( _cache.cbegin() )
    {
        return _cache.cbegin();
    }

    auto unordered_end()const -> decltype( _cache.cend() )
    {
        return _cache.cend();
    }

    auto unordered_find( const K& key )const -> decltype( _cache.find( key ) )
    {
        return _cache.find( key );
    }

    auto ordered_first()const -> decltype( _ldb.begin() )
    { try {
        return _ldb.begin();
    } FC_CAPTURE_AND_RETHROW() }

    auto ordered_last()const -> decltype( _ldb.last() )
    { try {
        return _ldb.last();
    } FC_CAPTURE_AND_RETHROW() }

    auto ordered_lower_bound( const K& key )const -> decltype( _ldb.lower_bound( key ) )
    { try {
        return _ldb.lower_bound( key );
    } FC_CAPTURE_AND_RETHROW( (key) ) }
};

} } // bts::db
<commit_msg>Don't reserve fast_level_map cache on startup to improve startup time<commit_after>#pragma once
#include <bts/db/level_map.hpp>

namespace bts { namespace db {

template<typename K, typename V>
class fast_level_map
{
    level_map<K, V>             _ldb;
    fc::optional<fc::path>      _ldb_path;
    bool                        _ldb_enabled = true;

    std::unordered_map<K, V>    _cache;

public:

    ~fast_level_map()
    {
        close();
    }

    void open( const fc::path& path )
    { try {
        FC_ASSERT( !_ldb_path.valid() );
        _ldb_path = path;
        _ldb.open( *_ldb_path );
        for( auto iter = _ldb.begin(); iter.valid(); ++iter )
            _cache.emplace( iter.key(), iter.value() );
    } FC_CAPTURE_AND_RETHROW( (path) ) }

    void close()
    { try {
        if( _ldb_path.valid() )
        {
            if( !_ldb_enabled ) toggle_leveldb( true );
            _ldb.close();
            _ldb_path = fc::optional<fc::path>();
        }
        _cache.clear();
    } FC_CAPTURE_AND_RETHROW() }

    void toggle_leveldb( const bool enabled )
    { try {
        FC_ASSERT( _ldb_path.valid() );
        if( enabled == _ldb_enabled )
            return;

        if( enabled )
        {
            _ldb.open( *_ldb_path );
            auto batch = _ldb.create_batch();
            for( const auto& item : _cache )
                batch.store( item.first, item.second );
            batch.commit();
        }
        else
        {
            _ldb.close();
            fc::remove_all( *_ldb_path );
        }

        _ldb_enabled = enabled;
    } FC_CAPTURE_AND_RETHROW( (enabled) ) }

    void store( const K& key, const V& value )
    { try {
        _cache[ key ] = value;
        if( _ldb_enabled )
            _ldb.store( key, value );
    } FC_CAPTURE_AND_RETHROW( (key)(value) ) }

    void remove( const K& key )
    { try {
        _cache.erase( key );
        if( _ldb_enabled )
            _ldb.remove( key );
    } FC_CAPTURE_AND_RETHROW( (key) ) }

    auto empty()const -> decltype( _cache.empty() )
    {
        return _cache.empty();
    }

    auto size()const -> decltype( _cache.size() )
    {
        return _cache.size();
    }

    auto count( const K& key )const -> decltype( _cache.count( key ) )
    {
        return _cache.count( key );
    }

    auto unordered_begin()const -> decltype( _cache.cbegin() )
    {
        return _cache.cbegin();
    }

    auto unordered_end()const -> decltype( _cache.cend() )
    {
        return _cache.cend();
    }

    auto unordered_find( const K& key )const -> decltype( _cache.find( key ) )
    {
        return _cache.find( key );
    }

    auto ordered_first()const -> decltype( _ldb.begin() )
    { try {
        return _ldb.begin();
    } FC_CAPTURE_AND_RETHROW() }

    auto ordered_last()const -> decltype( _ldb.last() )
    { try {
        return _ldb.last();
    } FC_CAPTURE_AND_RETHROW() }

    auto ordered_lower_bound( const K& key )const -> decltype( _ldb.lower_bound( key ) )
    { try {
        return _ldb.lower_bound( key );
    } FC_CAPTURE_AND_RETHROW( (key) ) }
};

} } // bts::db
<|endoftext|>