text:
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/data/monomorphic.hpp>

#include <string>

#include <tao/pegtl.hpp>
#include <tao/pegtl/contrib/tracer.hpp>

#include "test_setup.hpp"

#include "../../include/grammar/tokens.hpp"

namespace data = boost::unit_test::data;

using tao::pegtl::parse;
using tao::pegtl::string_input;
namespace rg = rhea::grammar;

namespace {
    // Datasets

    std::string signed_integer_samples[] = {"0", "+1", "-2", "-1234", "0000004"};
    std::string hex_literal_samples[] = {"0x0", "0x1", "0xa", "0xff", "0x0000aaaa00005555"};
    std::string float_literal_samples[] {"0.01", "123.456", "-1e6", "+999E-999", "46.29_f"};
    std::string identifier_samples[] = {"a", "Abc", "_foo", "bar_42"};
    std::string integer_literal_samples[] = {"+7", "3_b", "42_u", "-98765_l", "1000000000_ul"};
    std::string symbol_samples[] = {"@foo", "@A", "@a_long_name", "@a1b2"};
    std::string fully_qualified_samples[] = { "id", "main:_id", "std:math:sin", "a_module:n123:CONSTANT"};
    std::string relative_samples[] = { ":foo", ":relative", ":a:b", ":submodule:abc:xyz"};

    BOOST_AUTO_TEST_SUITE(Integer)

    BOOST_DATA_TEST_CASE_F(fixture, signed_integer, data::make(signed_integer_samples))
    {
        BOOST_TEST_MESSAGE("Parsing " << sample);
        string_input<> in(sample, "test");

        BOOST_TEST(parse<
            simple_parser<rg::signed_integer>
        >(in) == true);
    }

    BOOST_AUTO_TEST_CASE(bad_signed_integer)
    {
        std::string invalid { "1abc" };

        BOOST_TEST_MESSAGE("Parsing invalid input " << invalid);
        string_input<> in(invalid, "test");
        BOOST_TEST(parse<
            simple_parser<rg::signed_integer>
        >(in) == false);
    }

    BOOST_DATA_TEST_CASE_F(fixture, hex_literals, data::make(hex_literal_samples))
    {
        BOOST_TEST_MESSAGE("Parsing " << sample);
        string_input<> in(sample, "test");

        BOOST_TEST(parse<
            simple_parser<rg::hex_literal>
        >(in) == true);
    }

    BOOST_AUTO_TEST_CASE(bad_hex_literal)
    {
        std::string invalid { "0x0g" };

        BOOST_TEST_MESSAGE("Parsing invalid input " << invalid);
        string_input<> in(invalid, "test");
        BOOST_TEST(parse<
            simple_parser<rg::hex_literal>
        >(in) == false);
    }

    BOOST_AUTO_TEST_CASE(incomplete_hex_literal)
    {
        std::string invalid { "0x" };

        BOOST_TEST_MESSAGE("Parsing invalid input " << invalid);
        string_input<> in(invalid, "test");
        BOOST_CHECK_THROW(parse<
            simple_parser<rg::hex_literal>
        >(in), tao::pegtl::parse_error);
    }

    BOOST_DATA_TEST_CASE_F(fixture, float_literals, data::make(float_literal_samples))
    {
        BOOST_TEST_MESSAGE("Parsing " << sample);
        string_input<> in(sample, "test");

        BOOST_TEST(parse<
            simple_parser<rg::float_literal>
        >(in) == true);
    }

    BOOST_AUTO_TEST_CASE(bad_float_literal)
    {
        std::string invalid { "0.55a" };

        BOOST_TEST_MESSAGE("Parsing invalid input " << invalid);
        string_input<> in(invalid, "test");
        BOOST_TEST(parse<
            simple_parser<rg::float_literal>
        >(in) == false);
    }

    BOOST_AUTO_TEST_CASE(boolean_literal)
    {
        std::string valid[] { "true", "false" };

        BOOST_TEST_MESSAGE("Parsing valid input " << valid[0]);
        string_input<> in_true(valid[0], "test");
        BOOST_TEST(parse<
            simple_parser<rg::boolean_literal>
        >(in_true) == true);

        BOOST_TEST_MESSAGE("Parsing valid input " << valid[1]);
        string_input<> in_false(valid[1], "test");
        BOOST_TEST(parse<
            simple_parser<rg::boolean_literal>
        >(in_false) == true);
    }

    BOOST_DATA_TEST_CASE_F(fixture, integer_literals, data::make(integer_literal_samples))
    {
        BOOST_TEST_MESSAGE("Parsing " << sample);
        string_input<> in(sample, "test");

        BOOST_TEST(parse<
            simple_parser<rg::integer_literal>
        >(in) == true);
    }

    BOOST_AUTO_TEST_SUITE_END()

    BOOST_AUTO_TEST_SUITE(Identifier)

    BOOST_DATA_TEST_CASE_F(fixture, identifier, data::make(identifier_samples))
    {
        BOOST_TEST_MESSAGE("Parsing " << sample);
        string_input<> in(sample, "test");

        BOOST_TEST(parse<
            simple_parser<rg::identifier>
        >(in) == true);
    }

    BOOST_AUTO_TEST_CASE(bad_identifier)
    {
        std::string invalid { "1abc" };

        BOOST_TEST_MESSAGE("Parsing invalid input " << invalid);
        string_input<> in(invalid, "test");
        BOOST_TEST(parse<
            simple_parser<rg::identifier>
        >(in) == false);
    }

    BOOST_DATA_TEST_CASE_F(fixture, symbol, data::make(symbol_samples))
    {
        BOOST_TEST_MESSAGE("Parsing " << sample);
        string_input<> in(sample, "test");

        BOOST_TEST(parse<
            simple_parser<rg::symbol_name>
        >(in) == true);
    }

    BOOST_AUTO_TEST_CASE(bad_symbol)
    {
        std::string invalid { "@@" };

        BOOST_TEST_MESSAGE("Parsing invalid input " << invalid);
        string_input<> in(invalid, "test");
        BOOST_CHECK_THROW(parse<
            simple_parser<rg::symbol_name>
        >(in), tao::pegtl::parse_error);
    }

    BOOST_DATA_TEST_CASE_F(fixture, fully_qualified_identifier, data::make(fully_qualified_samples))
    {
        BOOST_TEST_MESSAGE("Parsing " << sample);
        string_input<> in(sample, "test");

        BOOST_TEST(parse<
            simple_parser<rg::fully_qualified>
        >(in) == true);
    }

    BOOST_DATA_TEST_CASE_F(fixture, relative_identifier, data::make(relative_samples))
    {
        BOOST_TEST_MESSAGE("Parsing " << sample);
        string_input<> in(sample, "test");

        BOOST_TEST(parse<
            simple_parser<rg::relative_identifier>
        >(in) == true);
    }

    BOOST_AUTO_TEST_SUITE_END()

    BOOST_AUTO_TEST_SUITE(Comment)

    BOOST_AUTO_TEST_CASE(eol_comment)
    {
        std::string valid { "# some comment\n" };

        BOOST_TEST_MESSAGE("Parsing line comment");
        string_input<> in (valid, "test");
        BOOST_TEST(parse<
            simple_parser<rg::line_comment>
        >(in) == true);
    }

    BOOST_AUTO_TEST_CASE(eof_comment)
    {
        std::string valid { "# some comment" };

        BOOST_TEST_MESSAGE("Parsing line comment to EOF");
        string_input<> in (valid, "test");
        BOOST_TEST(parse<
            simple_parser<rg::line_comment>
        >(in) == true);
    }

    BOOST_AUTO_TEST_CASE(block_comment)
    {
        std::string valid { "#{some comment\n\tpossibly with blank lines\n#}" };

        BOOST_TEST_MESSAGE("Parsing block comment");
        string_input<> in (valid, "test");
        BOOST_TEST(parse<
            simple_parser<rg::block_comment>
        >(in) == true);
    }

    BOOST_AUTO_TEST_CASE(incomplete_block_comment)
    {
        std::string invalid { "#{this comment doesn't end before EOF" };

        BOOST_TEST_MESSAGE("Parsing invalid block comment");
        string_input<> in (invalid, "test");
        BOOST_CHECK_THROW(parse<
            simple_parser<rg::block_comment>
        >(in), tao::pegtl::parse_error);
    }

    BOOST_AUTO_TEST_SUITE_END()
}

max_stars_repo_name: momikey/rhea

max_issues_repo_name: momikey/rhea

max_forks_repo_name: momikey/rhea


text:
#pragma once

#include "boost_defs.hpp"

#include "apple_hid_usage_tables.hpp"
#include "cf_utility.hpp"
#include "connected_devices.hpp"
#include "core_configuration.hpp"
#include "event_queue.hpp"
#include "gcd_utility.hpp"
#include "iokit_utility.hpp"
#include "logger.hpp"
#include "spdlog_utility.hpp"
#include "types.hpp"
#include <IOKit/hid/IOHIDDevice.h>
#include <IOKit/hid/IOHIDElement.h>
#include <IOKit/hid/IOHIDQueue.h>
#include <IOKit/hid/IOHIDUsageTables.h>
#include <IOKit/hid/IOHIDValue.h>
#include <boost/algorithm/string.hpp>
#include <boost/optional.hpp>
#include <cstdint>
#include <functional>
#include <list>
#include <mach/mach_time.h>
#include <unordered_map>
#include <vector>

namespace krbn {
class human_interface_device final {
public:
  enum class grabbable_state {
    grabbable,
    ungrabbable_temporarily,
    ungrabbable_permanently,
  };

  typedef std::function<void(human_interface_device& device,
                             event_queue& event_queue)>
      value_callback;

  typedef std::function<void(human_interface_device& device,
                             IOHIDReportType type,
                             uint32_t report_id,
                             uint8_t* _Nonnull report,
                             CFIndex report_length)>
      report_callback;

  typedef std::function<grabbable_state(human_interface_device& device)> is_grabbable_callback;

  typedef std::function<void(human_interface_device& device)> grabbed_callback;
  typedef std::function<void(human_interface_device& device)> ungrabbed_callback;
  typedef std::function<void(human_interface_device& device)> disabled_callback;

  class keyboard_repeat_detector final {
  public:
    void set(hid_usage_page hid_usage_page,
             hid_usage hid_usage,
             event_type event_type) {
      switch (event_type) {
        case event_type::key_down:
          if (types::make_modifier_flag(hid_usage_page, hid_usage) != modifier_flag::zero) {
            repeating_key_ = boost::none;
          } else {
            repeating_key_ = std::make_pair(hid_usage_page, hid_usage);
          }
          break;

        case event_type::key_up:
          if (repeating_key_ &&
              repeating_key_->first == hid_usage_page &&
              repeating_key_->second == hid_usage) {
            repeating_key_ = boost::none;
          }
          break;

        case event_type::single:
          // Do nothing
          break;
      }
    }

    bool is_repeating(void) const {
      return repeating_key_ != boost::none;
    }

  private:
    boost::optional<std::pair<hid_usage_page, hid_usage>> repeating_key_;
  };

  human_interface_device(const human_interface_device&) = delete;

  human_interface_device(IOHIDDeviceRef _Nonnull device) : device_(device),
                                                           device_id_(types::make_new_device_id(iokit_utility::find_vendor_id(device),
                                                                                iokit_utility::find_product_id(device),
                                                                                iokit_utility::is_keyboard(device),
                                                                                iokit_utility::is_pointing_device(device))),
                                                           queue_(nullptr),
                                                           removed_(false),
                                                           observed_(false),
                                                           grabbed_(false),
                                                           disabled_(false) {
    // ----------------------------------------
    // Retain device_

    CFRetain(device_);

    // Set name_for_log_
    {
      std::stringstream stream;

      if (auto product_name = get_product()) {
        stream << boost::trim_copy(*product_name);
      } else {
        if (auto vendor_id = get_vendor_id()) {
          if (auto product_id = get_product_id()) {
            stream << std::hex
                   << "(vendor_id:0x" << static_cast<uint32_t>(*vendor_id)
                   << ", product_id:0x" << static_cast<uint32_t>(*product_id)
                   << ")"
                   << std::dec;
          }
        }
      }

      stream << " (device_id:" << static_cast<uint32_t>(device_id_) << ")";
      name_for_log_ = stream.str();
    }

    // Create connected_device_.
    {
      std::string manufacturer;
      std::string product;
      if (auto m = iokit_utility::get_manufacturer(device_)) {
        manufacturer = *m;
      }
      if (auto p = iokit_utility::get_product(device_)) {
        product = *p;
      }
      connected_devices::device::descriptions descriptions(manufacturer, product);

      auto vendor_id = iokit_utility::find_vendor_id(device_);
      auto product_id = iokit_utility::find_product_id(device_);
      bool is_keyboard = IOHIDDeviceConformsTo(device_, kHIDPage_GenericDesktop, kHIDUsage_GD_Keyboard);
      bool is_pointing_device = IOHIDDeviceConformsTo(device_, kHIDPage_GenericDesktop, kHIDUsage_GD_Pointer) ||
                                IOHIDDeviceConformsTo(device_, kHIDPage_GenericDesktop, kHIDUsage_GD_Mouse);
      device_identifiers identifiers(vendor_id,
                                     product_id,
                                     is_keyboard,
                                     is_pointing_device);

      bool is_built_in_keyboard = false;
      if (is_keyboard &&
          !is_pointing_device &&
          descriptions.get_product().find("Apple Internal ") != std::string::npos) {
        is_built_in_keyboard = true;
      }

      connected_device_ = std::make_unique<connected_devices::device>(descriptions,
                                                                      identifiers,
                                                                      is_built_in_keyboard);
    }

    // ----------------------------------------
    // Setup elements_

    if (auto elements = IOHIDDeviceCopyMatchingElements(device_, nullptr, kIOHIDOptionsTypeNone)) {
      for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i) {
        // Add to elements_.
        auto element = cf_utility::get_value<IOHIDElementRef>(elements, i);
        auto usage_page = hid_usage_page(IOHIDElementGetUsagePage(element));
        auto usage = hid_usage(IOHIDElementGetUsage(element));

        auto key = elements_key(usage_page, usage);
        if (elements_.find(key) == elements_.end()) {
          CFRetain(element);
          elements_[key] = element;
        }
      }
      CFRelease(elements);
    }

    // ----------------------------------------
    // setup queue_

    const CFIndex depth = 1024;
    queue_ = IOHIDQueueCreate(kCFAllocatorDefault, device_, depth, kIOHIDOptionsTypeNone);
    if (!queue_) {
      logger::get_logger().error("IOHIDQueueCreate error @ {0}", __PRETTY_FUNCTION__);
    } else {
      // Add elements into queue_.
      for (const auto& it : elements_) {
        IOHIDQueueAddElement(queue_, it.second);
      }
      IOHIDQueueRegisterValueAvailableCallback(queue_, static_queue_value_available_callback, this);
    }
  }

  ~human_interface_device(void) {
    // Release device_ and queue_ in main thread to avoid callback invocations after object has been destroyed.
    gcd_utility::dispatch_sync_in_main_queue(^{
      // Unregister all callbacks.
      unschedule();
      unregister_report_callback();
      queue_stop();
      close();

      types::detach_device_id(device_id_);

      // ----------------------------------------
      // Release queue_

      if (queue_) {
        CFRelease(queue_);
        queue_ = nullptr;
      }

      // ----------------------------------------
      // Release elements_

      for (const auto& it : elements_) {
        CFRelease(it.second);
      }
      elements_.clear();

      // ----------------------------------------
      // Release device_

      CFRelease(device_);
    });
  }

  device_id get_device_id(void) const {
    return device_id_;
  }

  IOReturn open(IOOptionBits options = kIOHIDOptionsTypeNone) {
    IOReturn __block r;
    gcd_utility::dispatch_sync_in_main_queue(^{
      r = IOHIDDeviceOpen(device_, options);
    });
    return r;
  }

  IOReturn close(void) {
    IOReturn __block r;
    gcd_utility::dispatch_sync_in_main_queue(^{
      r = IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone);
    });
    return r;
  }

  void schedule(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      IOHIDDeviceScheduleWithRunLoop(device_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
      if (queue_) {
        IOHIDQueueScheduleWithRunLoop(queue_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
      }
    });
  }

  void unschedule(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (queue_) {
        IOHIDQueueUnscheduleFromRunLoop(queue_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
      }
      IOHIDDeviceUnscheduleFromRunLoop(device_, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
    });
  }

  void register_report_callback(const report_callback& callback) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      report_callback_ = callback;

      resize_report_buffer();
      IOHIDDeviceRegisterInputReportCallback(device_, &(report_buffer_[0]), report_buffer_.size(), static_input_report_callback, this);
    });
  }

  void unregister_report_callback(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      resize_report_buffer();
      IOHIDDeviceRegisterInputReportCallback(device_, &(report_buffer_[0]), report_buffer_.size(), nullptr, this);

      report_callback_ = nullptr;
    });
  }

  void queue_start(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (queue_) {
        IOHIDQueueStart(queue_);
      }
    });
  }

  void queue_stop(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (queue_) {
        IOHIDQueueStop(queue_);
      }
    });
  }

  void set_removed(void) {
    removed_ = true;
  }

  // High-level utility method.
  void observe(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (removed_) {
        return;
      }

      if (is_pqrs_device()) {
        return;
      }

      if (observed_) {
        return;
      }

      auto r = open();
      if (r != kIOReturnSuccess) {
        logger::get_logger().error("IOHIDDeviceOpen error: {0} ({1}) {2}", iokit_utility::get_error_name(r), r, name_for_log_);
        return;
      }

      queue_start();
      schedule();

      observed_ = true;
    });
  }

  // High-level utility method.
  void unobserve(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (is_pqrs_device()) {
        return;
      }

      if (!observed_) {
        return;
      }

      unschedule();
      queue_stop();
      close();

      observed_ = false;
    });
  }

  // High-level utility method.
  void grab(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (removed_) {
        return;
      }

      if (is_pqrs_device()) {
        return;
      }

      if (grabbed_) {
        return;
      }

      cancel_grab_timer();

      is_grabbable_callback_log_reducer_.reset();

      grab_timer_ = std::make_unique<gcd_utility::main_queue_timer>(
          // We have to set an initial wait since OS X will lost the device if we called IOHIDDeviceOpen(kIOHIDOptionsTypeSeizeDevice) in device_matching_callback.
          // (The device will be unusable after karabiner_grabber is quitted if we don't wait here.)
          dispatch_time(DISPATCH_TIME_NOW, 100 * NSEC_PER_MSEC),
          100 * NSEC_PER_MSEC,
          0,
          ^{
            switch (is_grabbable()) {
              case grabbable_state::grabbable:
                break;

              case grabbable_state::ungrabbable_temporarily:
                return;

              case grabbable_state::ungrabbable_permanently:
                cancel_grab_timer();
                return;
            }

            // ----------------------------------------
            grabbed_ = true;

            unobserve();

            // ----------------------------------------
            auto r = open(kIOHIDOptionsTypeSeizeDevice);
            if (r != kIOReturnSuccess) {
              logger::get_logger().error("IOHIDDeviceOpen error: {0} ({1}) {2}", iokit_utility::get_error_name(r), r, name_for_log_);
              return;
            }

            if (grabbed_callback_) {
              grabbed_callback_(*this);
            }

            queue_start();
            schedule();

            // ----------------------------------------
            logger::get_logger().info("{0} is grabbed", get_name_for_log());

            cancel_grab_timer();
          });
    });
  }

  // High-level utility method.
  void ungrab(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (is_pqrs_device()) {
        return;
      }

      if (!grabbed_) {
        return;
      }

      grabbed_ = false;

      cancel_grab_timer();

      unschedule();
      queue_stop();
      close();

      if (ungrabbed_callback_) {
        ungrabbed_callback_(*this);
      }

      observe();
    });
  }

  bool get_disabled(void) const {
    return disabled_;
  }

  void disable(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (is_pqrs_device()) {
        return;
      }

      if (disabled_) {
        return;
      }

      disabled_ = true;

      if (disabled_callback_) {
        disabled_callback_(*this);
      }

      logger::get_logger().info("{0} is disabled", get_name_for_log());
    });
  }

  void enable(void) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      if (is_pqrs_device()) {
        return;
      }

      if (!disabled_) {
        return;
      }

      disabled_ = false;

      logger::get_logger().info("{0} is enabled", get_name_for_log());
    });
  }

  boost::optional<long> get_max_input_report_size(void) const {
    return iokit_utility::get_max_input_report_size(device_);
  }

  boost::optional<vendor_id> get_vendor_id(void) const {
    return iokit_utility::find_vendor_id(device_);
  }

  boost::optional<product_id> get_product_id(void) const {
    return iokit_utility::find_product_id(device_);
  }

  boost::optional<location_id> get_location_id(void) const {
    return iokit_utility::get_location_id(device_);
  }

  boost::optional<std::string> get_manufacturer(void) const {
    return iokit_utility::get_manufacturer(device_);
  }

  boost::optional<std::string> get_product(void) const {
    return iokit_utility::get_product(device_);
  }

  boost::optional<std::string> get_serial_number(void) const {
    return iokit_utility::get_serial_number(device_);
  }

  boost::optional<std::string> get_transport(void) const {
    return iokit_utility::get_transport(device_);
  }

  std::string get_name_for_log(void) const {
    return name_for_log_;
  }

  void set_is_grabbable_callback(const is_grabbable_callback& callback) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      is_grabbable_callback_ = callback;
    });
  }

  void set_grabbed_callback(const grabbed_callback& callback) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      grabbed_callback_ = callback;
    });
  }

  void set_ungrabbed_callback(const ungrabbed_callback& callback) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      ungrabbed_callback_ = callback;
    });
  }

  void set_disabled_callback(const disabled_callback& callback) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      disabled_callback_ = callback;
    });
  }

  void set_value_callback(const value_callback& callback) {
    gcd_utility::dispatch_sync_in_main_queue(^{
      value_callback_ = callback;
    });
  }

  grabbable_state is_grabbable(void) {
    if (is_grabbable_callback_) {
      auto state = is_grabbable_callback_(*this);
      if (state != grabbable_state::grabbable) {
        return state;
      }
    }

    // ----------------------------------------
    // Ungrabbable while key repeating

    if (keyboard_repeat_detector_.is_repeating()) {
      is_grabbable_callback_log_reducer_.warn(std::string("We cannot grab ") + get_name_for_log() + " while a key is repeating.");
      return human_interface_device::grabbable_state::ungrabbable_temporarily;
    }

    // ----------------------------------------
    // Ungrabbable while pointing button is pressed.

    if (!pressed_pointing_buttons_.empty()) {
      // We should not grab the device while a button is pressed since we cannot release the button.
      // (To release the button, we have to send a hid report to the device. But we cannot do it.)

      is_grabbable_callback_log_reducer_.warn(std::string("We cannot grab ") + get_name_for_log() + " while mouse buttons are pressed.");
      return human_interface_device::grabbable_state::ungrabbable_temporarily;
    }

    // ----------------------------------------

    return grabbable_state::grabbable;
  }

  bool is_grabbed(void) const {
    bool __block r = false;
    gcd_utility::dispatch_sync_in_main_queue(^{
      r = grabbed_;
    });
    return r;
  }

#pragma mark - usage specific utilities

  // This method requires root privilege to use IOHIDDeviceGetValue for kHIDPage_LEDs usage.
  boost::optional<led_state> get_caps_lock_led_state(void) const {
    boost::optional<led_state> __block state = boost::none;

    gcd_utility::dispatch_sync_in_main_queue(^{
      if (auto element = get_element(hid_usage_page::leds, hid_usage::led_caps_lock)) {
        auto max = IOHIDElementGetLogicalMax(element);

        IOHIDValueRef value;
        auto r = IOHIDDeviceGetValue(device_, element, &value);
        if (r != kIOReturnSuccess) {
          logger::get_logger().error("IOHIDDeviceGetValue error: {1} @ {0}", __PRETTY_FUNCTION__, r);
        } else {
          auto integer_value = IOHIDValueGetIntegerValue(value);
          if (integer_value == max) {
            state = led_state::on;
          } else {
            state = led_state::off;
          }
        }
      }
    });

    return state;
  }

  // This method requires root privilege to use IOHIDDeviceSetValue for kHIDPage_LEDs usage.
  IOReturn set_caps_lock_led_state(led_state state) {
    // `IOHIDDeviceSetValue` will block forever with some buggy devices. (eg. Bit Touch)
    // This, we use a blacklist.
    if (auto v = get_vendor_id()) {
      if (auto p = get_product_id()) {
        if ((*v == vendor_id(0x22ea) && *p == product_id(0xf)) /* Bit Touch (Bit Trade One LTD.) */ ||
            false) {
          return kIOReturnSuccess;
        }
      }
    }

    IOReturn __block r = kIOReturnError;

    gcd_utility::dispatch_sync_in_main_queue(^{
      if (auto element = get_element(hid_usage_page::leds, hid_usage::led_caps_lock)) {
        CFIndex integer_value = 0;
        if (state == led_state::on) {
          integer_value = IOHIDElementGetLogicalMax(element);
        } else {
          integer_value = IOHIDElementGetLogicalMin(element);
        }

        if (auto value = IOHIDValueCreateWithIntegerValue(kCFAllocatorDefault, element, mach_absolute_time(), integer_value)) {
          r = IOHIDDeviceSetValue(device_, element, value);

          if (r != kIOReturnSuccess) {
            logger::get_logger().error("IOHIDDeviceSetValue error {1} for {2} @ {0}", __PRETTY_FUNCTION__, r, get_name_for_log());
          }

          CFRelease(value);

        } else {
          logger::get_logger().error("IOHIDValueCreateWithIntegerValue error @ {0}", __PRETTY_FUNCTION__);
        }
      }
    });

    return r;
  }

  const connected_devices::device get_connected_device(void) const {
    return *connected_device_;
  }

  bool is_keyboard(void) const {
    return connected_device_->get_identifiers().get_is_keyboard();
  }

  bool is_pointing_device(void) const {
    return connected_device_->get_identifiers().get_is_pointing_device();
  }

  bool is_built_in_keyboard(void) const {
    return connected_device_->get_is_built_in_keyboard();
  }

  bool is_pqrs_device(void) const {
    if (auto manufacturer = get_manufacturer()) {
      if (*manufacturer == "pqrs.org") {
        return true;
      }
    }

    return false;
  }

  bool is_pqrs_virtual_hid_keyboard(void) const {
    if (auto manufacturer = get_manufacturer()) {
      if (auto product = get_product()) {
        if (*manufacturer == "pqrs.org" &&
            *product == "Karabiner VirtualHIDKeyboard") {
          return true;
        }
      }
    }

    return false;
  }

private:
  static void static_queue_value_available_callback(void* _Nullable context, IOReturn result, void* _Nullable sender) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<human_interface_device*>(context);
    if (!self) {
      return;
    }

    self->queue_value_available_callback();
  }

  void queue_value_available_callback(void) {
    while (true) {
      if (auto value = IOHIDQueueCopyNextValueWithTimeout(queue_, 0.)) {
        auto element = IOHIDValueGetElement(value);
        if (element) {
          auto time_stamp = IOHIDValueGetTimeStamp(value);
          auto usage_page = hid_usage_page(IOHIDElementGetUsagePage(element));
          auto usage = hid_usage(IOHIDElementGetUsage(element));
          auto integer_value = IOHIDValueGetIntegerValue(value);

          if (input_event_queue_.emplace_back_event(device_id_, time_stamp, usage_page, usage, integer_value)) {
            // We need to check whether event is emplaced into `input_event_queue_` since
            // `emplace_back_event` does not add an event in some usage_page and usage.

            auto& element_event = input_event_queue_.get_events().back();

            if (element_event.get_event().get_key_code() ||
                element_event.get_event().get_consumer_key_code()) {
              // Update keyboard_repeat_detector_

              keyboard_repeat_detector_.set(usage_page, usage, element_event.get_event_type());

              // Send `device_keys_are_released` event if needed.

              if (integer_value) {
                pressed_keys_.insert(elements_key(usage_page, usage));
              } else {
                size_t size = pressed_keys_.size();
                pressed_keys_.erase(elements_key(usage_page, usage));
                if (size > 0 && pressed_keys_.empty()) {
                  auto event = event_queue::queued_event::event::make_device_keys_are_released_event();
                  input_event_queue_.emplace_back_event(device_id_,
                                                        time_stamp,
                                                        event,
                                                        event_type::key_down,
                                                        event);
                }
              }
            }

            if (auto pointing_button = element_event.get_event().get_pointing_button()) {
              // Send `device_pointing_buttons_are_released` event if needed.

              if (integer_value) {
                pressed_pointing_buttons_.insert(elements_key(usage_page, usage));
              } else {
                size_t size = pressed_pointing_buttons_.size();
                pressed_pointing_buttons_.erase(elements_key(usage_page, usage));
                if (size > 0 && pressed_pointing_buttons_.empty()) {
                  auto event = event_queue::queued_event::event::make_device_pointing_buttons_are_released_event();
                  input_event_queue_.emplace_back_event(device_id_,
                                                        time_stamp,
                                                        event,
                                                        event_type::key_down,
                                                        event);
                }
              }
            }
          }
        }

        CFRelease(value);

      } else {
        break;
      }
    }

    // Call value_callback_.
    if (value_callback_) {
      value_callback_(*this, input_event_queue_);
    }

    input_event_queue_.clear_events();
  }

  static void static_input_report_callback(void* _Nullable context,
                                           IOReturn result,
                                           void* _Nullable sender,
                                           IOHIDReportType type,
                                           uint32_t report_id,
                                           uint8_t* _Nullable report,
                                           CFIndex report_length) {
    if (result != kIOReturnSuccess) {
      return;
    }

    auto self = static_cast<human_interface_device*>(context);
    if (!self) {
      return;
    }

    self->input_report_callback(type, report_id, report, report_length);
  }

  void input_report_callback(IOHIDReportType type,
                             uint32_t report_id,
                             uint8_t* _Nullable report,
                             CFIndex report_length) {
    if (report_callback_) {
      report_callback_(*this, type, report_id, report, report_length);
    }
  }

  uint64_t elements_key(hid_usage_page usage_page, hid_usage usage) const {
    return ((static_cast<uint64_t>(usage_page) << 32) | static_cast<uint32_t>(usage));
  }

  IOHIDElementRef _Nullable get_element(hid_usage_page usage_page, hid_usage usage) const {
    auto key = elements_key(usage_page, usage);
    auto it = elements_.find(key);
    if (it == elements_.end()) {
      return nullptr;
    } else {
      return it->second;
    }
  }

  void resize_report_buffer(void) {
    size_t buffer_size = 32; // use this provisional value if we cannot get max input report size from device.
    if (auto size = get_max_input_report_size()) {
      buffer_size = *size;
    }

    report_buffer_.resize(buffer_size);
  }

  void cancel_grab_timer(void) {
    grab_timer_ = nullptr;
  }

  IOHIDDeviceRef _Nonnull device_;
  device_id device_id_;
  IOHIDQueueRef _Nullable queue_;
  std::unordered_map<uint64_t, IOHIDElementRef> elements_;

  std::string name_for_log_;

  event_queue input_event_queue_;

  value_callback value_callback_;
  report_callback report_callback_;
  std::vector<uint8_t> report_buffer_;

  is_grabbable_callback is_grabbable_callback_;
  spdlog_utility::log_reducer is_grabbable_callback_log_reducer_;

  grabbed_callback grabbed_callback_;
  ungrabbed_callback ungrabbed_callback_;
  disabled_callback disabled_callback_;
  std::unique_ptr<gcd_utility::main_queue_timer> grab_timer_;
  bool removed_;
  bool observed_;
  bool grabbed_;
  // `disabled_` is ignoring input events from this device.
  // (== `grabbed_` and does not call `value_callback_`)
  bool disabled_;

  std::unique_ptr<connected_devices::device> connected_device_;

  keyboard_repeat_detector keyboard_repeat_detector_;

  std::unordered_set<uint64_t> pressed_keys_;
  std::unordered_set<uint64_t> pressed_pointing_buttons_;
};
} // namespace krbn

ext: hpp

max_stars_repo_name: rcmdnk/Karabiner-Elements

max_issues_repo_name: rcmdnk/Karabiner-Elements

max_forks_repo_name: rcmdnk/Karabiner-Elements


text:
//
// Copyright (c) 2018-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/vinniefalco/BeastLounge
//

#ifndef LOUNGE_RPC_HPP
#define LOUNGE_RPC_HPP

#include "config.hpp"
#include <boost/beast/core/error.hpp>
#include <boost/beast/core/string.hpp>
#include <boost/json/value.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/optional.hpp>
#include <stdexcept>
#include <utility>

class user;

/// Codes used in JSON-RPC error responses
enum class rpc_code
{
    parse_error      = -32700,
    invalid_request  = -32600,
    method_not_found = -32601,
    invalid_params   = -32602,
    internal_error   = -32603,

    /// Expected object in JSON-RPC request
    expected_object = 1,

    /// Expected string version in JSON-RPC request
    expected_string_version,

    /// Uknown version in JSON-RPC request
    unknown_version,

    /// Invalid null id in JSON-RPC request
    invalid_null_id,

    /// Expected string or number id in JSON-RPC request
    expected_strnum_id,

    /// Missing id in JSON-RPC request version 1
    expected_id,

    /// Missing method in JSON-RPC request
    missing_method,

    /// Expected string method in JSON-RPC request
    expected_string_method,

    /// Expected structured params in JSON-RPC request version 2
    expected_structured_params,

    /// Missing params in JSON-RPC request version 1
    missing_params,

    /// Expected array params in JSON-RPC request version 1
    expected_array_params
};

namespace boost {
namespace system {
template<>
struct is_error_code_enum<rpc_code>
{
    static bool constexpr value = true;
};
} // system
} // boost

beast::error_code
make_error_code(rpc_code e);

//------------------------------------------------------------------------------

class rpc_error
    : public std::exception
{
    int code_;
    std::string msg_;

public:
    rpc_error()
        : rpc_error(
            rpc_code::internal_error)
    {
    }

    rpc_error(
        rpc_code ev)
        : rpc_error(ev,
            beast::error_code(ev).message())
    {
    }

    rpc_error(
        beast::string_view msg)
        : rpc_error(
            rpc_code::invalid_params,
            msg)
    {
    }

    rpc_error(
        rpc_code ev,
        beast::string_view msg)
        : code_(static_cast<int>(ev))
        , msg_(msg)
    {
    }

    rpc_error(
        beast::error_code const& ec)
        : code_(static_cast<int>(ec.value()))
        , msg_(ec.message())
    {
    }

    json::value
    to_json(
        boost::optional<json::value> const&
            id = boost::none) const;
};

//------------------------------------------------------------------------------

/** Represents a JSON-RPC request
*/
class rpc_call
{
    /** The request id

        If set, this will be string, number, or null
    */
    boost::optional<json::value> id_;

public:
    /// The user submitting the request
    boost::shared_ptr<user> u;

    /// Version of the request (1 or 2)
    int version = 2;

    /// The request method
    json::string method;

    /** The request parameters

        This will be object, array, or null
    */
    json::value params;

    /** The response result

        This value will be sent as the result of
        a successful operation.
    */
    json::value result;

public:
    rpc_call(rpc_call&&) = default;
    rpc_call& operator=(rpc_call&&) = delete;

    /** Construct an empty request using the specified storage.

        The method, params, and id will be null,
        and version will be 2.
    */
    rpc_call(
        ::user& u,
        json::storage_ptr sp = {});

    /** Extract a JSON-RPC request or return an error.
    */
    void
    extract(
        json::value&& jv,
        beast::error_code& ec);

    /** Complete the RPC request with a success.

        This function sends the user originating the request
        a JSON-RPC response object containing the result.
    */
    void
    complete();

    /** Complete the RPC request with an error.

        This function sends the user originating the request
        a JSON-RPC response containing an error object.
    */
    void
    complete(rpc_error const& e);

    /** Respond to a request with an error.

        This function will throw an `rpc_error`
        constructed from the argument list.
    */
    template<class... Args>
    [[noreturn]]
    void
    fail(Args&&... args)
    {
        throw rpc_error(
            std::forward<Args>(args)...);
    }
};

//------------------------------------------------------------------------------

extern
json::object&
checked_object(json::value& jv);

extern
json::array&
checked_array(json::value& jv);

extern
json::string&
checked_string(json::value& jv);

extern
std::uint64_t&
checked_uint64(json::value& jv);

extern
bool&
checked_bool(json::value& jv);

extern
void
checked_null(json::value& jv);

extern
json::value&
checked_value(
    json::value& jv,
    beast::string_view key);

extern
json::object&
checked_object(
    json::value& jv,
    beast::string_view key);

extern
json::array&
checked_array(
    json::value& jv,
    beast::string_view key);

extern
json::string&
checked_string(
    json::value& jv,
    beast::string_view key);

extern
std::uint64_t&
checked_uint64(
    json::value& jv,
    beast::string_view key);

extern
bool&
checked_bool(
    json::value& jv,
    beast::string_view key);

extern
void
checked_null(
    json::value& jv,
    beast::string_view key);

#endif

max_stars_repo_name: ReddyArunreddy/BeastLounge

max_issues_repo_name: ReddyArunreddy/BeastLounge

max_forks_repo_name: ReddyArunreddy/BeastLounge


text:
#ifdef _MSC_VER
#include "targetver.h"
#endif

#include <boost/asio.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>

#include "client.hpp"

namespace http
{
	namespace client
	{
		bool client(const std::string& addr, const std::string& port, const request& req, reply& reply)
		{
			using boost::asio::ip::tcp;
			boost::asio::io_service io_service;

			// Get a list of endpoints corresponding to the server name.
			tcp::resolver resolver(io_service);
			tcp::resolver::query query(addr, port);
			tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
			tcp::resolver::iterator end;

			// Try each endpoint until we successfully establish a connection.
			tcp::socket socket(io_service);
			boost::system::error_code error = boost::asio::error::host_not_found;
			while(error && endpoint_iterator != end) {
				socket.close();
				socket.connect(*endpoint_iterator++, error);
			}
			if(error) {
				throw boost::system::system_error(error);
			}

			// Form the request. We specify the "Connection: close" header so that the
			// server will close the socket after transmitting the response. This will
			// allow us to treat all data up until the EOF as the content.
			boost::asio::streambuf request;
			std::ostream request_stream(&request);
			request_stream << "POST /tbs" << " HTTP/1.0\r\n";
			request_stream << "Host: " << addr << "\r\n";
			request_stream << "Accept: */*\r\n";
#ifdef BOOST_NO_CXX11_RANGE_BASED_FOR
			BOOST_FOREACH(auto it, req.headers) {
#else
			for(auto it : req.headers) {
#endif
				request_stream << it.name << ": " << it.value << "\r\n";
			}
			if(!req.body.empty()) {
				request_stream << "Content-Length: " << req.body.length() << "\r\n";
			}
			request_stream << "Connection: close\r\n\r\n";
			if(!req.body.empty()) {
				request_stream << req.body;
			}

			// Send the request.
			boost::asio::write(socket, request);

			// Read the response status line.
			boost::asio::streambuf response;
			boost::asio::read_until(socket, response, "\r\n");

			// Check that response is OK.
			std::istream response_stream(&response);
			std::string http_version;
			response_stream >> http_version;
			response_stream >> reply.status_code;
			std::string status_message;
			std::getline(response_stream, status_message);
			if(!response_stream || http_version.substr(0, 5) != "HTTP/") {
				std::cerr << "Invalid response\n";
				return false;
			}
			if(reply.status_code != 200) {
				std::cerr << "Response returned with status code " << reply.status_code << "\n";
				return false;
			}
			size_t pos = http_version.substr(5).find('.');
			reply.http_version_major = boost::lexical_cast<int>(http_version.substr(5,pos));
			reply.http_version_minor = boost::lexical_cast<int>(http_version.substr(pos+6));

			// Read the response headers, which are terminated by a blank line.
			boost::asio::read_until(socket, response, "\r\n\r\n");

			// Process the response headers.
			std::string header;
			while(std::getline(response_stream, header) && header != "\r") {
				http::server::header h;
				size_t n = header.find(':');
				if(n == std::string::npos) {
					std::cerr << "Bad header: " << header;
					return false;
				}
				h.name = header.substr(0, n);
				h.value = header.substr(n+1);
				boost::algorithm::trim(h.value);
				reply.headers.push_back(h);
			}

			reply.body.clear();
			reply.content_length = 0;

			if(response.size() > 0) {
				std::stringstream str;
				str << &response;
				reply.body += str.str();
				reply.content_length += reply.body.length();
			}

			// Read until EOF, writing data to output as we go.
			while(boost::asio::read(socket, response, boost::asio::transfer_at_least(1), error)) {
				// Dump the rest into the reply body and set content length.
				std::stringstream str;
				str << &response;
				reply.body += str.str();
				reply.content_length += reply.body.length();
			}
			if (error != boost::asio::error::eof) {
				return false;
				//throw boost::system::system_error(error);
			}

			return true;
		}
	}
}

ext: cpp

max_stars_repo_name: sweetkristas/anura

max_issues_repo_name: sweetkristas/anura

max_forks_repo_name: sweetkristas/anura


text:
#include <iostream>
#include <fstream>
#include <deque>
#include "map"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include <sstream>
#include <iomanip>
#include <zlib.h>
#include <stdint.h>
#include <boost/iostreams/filtering_streambuf.hpp>
#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>

using namespace std;
//ofstream OutFile;
FILE * trace;
/* ===================================================================== */
/* Main                                                                  */
/* ===================================================================== */

int main(int argc, char * argv[]) {
  ifstream inFile(argv[1]);
  string line;
  long int line_cntr=0;
  long int prev_addr=0;

  ifstream file(argv[1], ios_base::in | ios_base::binary);
  boost::iostreams::filtering_istream in;
  in.push(boost::iostreams::gzip_decompressor());
  in.push(file);

  int bubble = 3;
  uint64_t counter=0;

  gzFile fi0 = gzopen("cifar_ramulator_cpu.gz","wb");

  for(std::string str; std::getline(in, str);) {
    string str_addr;
    uint64_t addr;
    string rw;
    string op;
    long int pc;
    int cntr;
    istringstream iss(str);
    char *pch;
    pch = strtok(&str[0]," \n");
    cntr=0;
    while(pch != NULL) {
      if(cntr == 0) {
        rw = pch;
      }
      //if(cntr == 2)
        //bubble = stol(pch, NULL);
      else if(cntr == 1) {
        str_addr = pch;
	str_addr.erase(0,2);
      }
      pch = strtok(NULL," \n");
      cntr++;
    }

    addr = strtoul(&str_addr[0], NULL, 16);

    gzprintf(fi0, &((to_string(bubble))[0]));
    gzprintf(fi0, " ");
    gzprintf(fi0, &((to_string(addr))[0]));
    gzprintf(fi0, " ");
    gzprintf(fi0, &rw[0]);
    gzprintf(fi0, "\n");
    //break;
    counter++;
    if(counter>2689381744)
      break;
  }
  gzclose(fi0);
}


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

#include <map_nodelet.h>

#include <random>

#include <pluginlib/class_list_macros.h>
#include <boost/concept_check.hpp>
#include <pcl/common/time.h>
#include <eigen_conversions/eigen_msg.h>

namespace mrs_laser_mapping
{
MapNodelet::MapNodelet()
  : running_(true)
  , first_scan_(true)
  , scan_number_(0)

  , map_size_(32)
  , map_levels_(2)
  , map_cell_capacity_(400)
  , map_resolution_(20.0)

  , map_downsampled_size_(8)
  , map_downsampled_levels_(4)
  , map_downsampled_cell_capacity_(100)
  , map_downsampled_resolution_(20)

  , scan_assembler_frame_id_("/odom")
  , sensor_frame_id_("laser_scanner_origin")

  , num_scans_registration_(0)
  , num_scans_for_map_publishing_(5)
  , add_new_points_(true)
  , transform_wait_duration_(0.2)
  , multiresolution_map_(new MapType(map_size_, map_resolution_, map_levels_, map_cell_capacity_, map_frame_id_))
  , last_scan_stamp_(0)
  , last_scan_received_stamp_(0)
  , scan_stamp_delta_(0)
  , decrease_rate_(0.08)
  , decrease_once_(false)
  , param_update_occupancy_("occ_update", true)
  , param_clamping_thresh_min_("occ_clamping_thresh_min", -10.f, 0.1f, 10.f, -2.f)
  , param_clamping_thresh_max_("occ_clamping_thresh_max", -10.f, 0.1f, 10.f, 3.5f)
  , param_prob_hit_("occ_prob_hit", -10.f, 0.1f, 10.f, 0.85f)
  , param_prob_miss_("occ_prob_miss", -10.f, 0.1f, 10.f, -0.4f)
  , add_scans_when_torso_rotated_(true)
  , last_torso_yaw_(0.0)
  , scan_buffer_(10)
{
}

MapNodelet::~MapNodelet()
{
  if (running_)
  {
    printf("stopping threads\n");
    fflush(stdout);

    running_ = false;
    tf_broadcaster_thread_->join();
  }

  printf("exiting\n");
  fflush(stdout);
}

void MapNodelet::onInit()
{
  NODELET_INFO("MapNodelet::onInit nodelet...");

  last_base_link_transform_.setIdentity();
  last_base_link_oriented_transform_.setIdentity();

  correction_transform_ = Eigen::Matrix4d::Identity();
  map_orientation_ = Eigen::Matrix4d::Identity();

  ros::NodeHandle& ph = getMTPrivateNodeHandle();
  ros::NodeHandle& phMT = getMTPrivateNodeHandle();

  // get parameters
  ph.getParam("scan_assembler_frame_id", scan_assembler_frame_id_);
  ph.getParam("min_scans_registration", num_scans_registration_);
  ph.getParam("min_scans_for_map_publishing", num_scans_for_map_publishing_);
  ph.getParam("transform_wait_duration", transform_wait_duration_);
  ph.getParam("sensor_frame_id", sensor_frame_id_);

   // parameters for registration
  mrs_laser_maps::RegistrationParameters params;
  ph.param<int>("registration_max_iterations", params.max_iterations_, 100);
  ph.param<double>("registration_prior_prob", params.prior_prob_, 0.25);
  ph.param<double>("registration_soft_assoc_c1", params.soft_assoc_c1_, 0.9);
  ph.param<double>("registration_soft_assoc_c2", params.soft_assoc_c2_, 10.0);
  ph.param<double>("registration_sigma_size_factor", params.sigma_size_factor_, 0.45);
  surfel_registration_.setRegistrationParameters(params);

  // parameters for map
  ph.getParam("map_size", map_size_);
  ph.getParam("map_resolution", map_resolution_);
  ph.getParam("map_levels", map_levels_);
  ph.getParam("map_cell_capacity", map_cell_capacity_);

  ph.getParam("map_downsampled_size", map_downsampled_size_);
  ph.getParam("map_downsampled_resolution", map_downsampled_resolution_);
  ph.getParam("map_downsampled_levels", map_downsampled_levels_);
  ph.getParam("map_downsampled_cell_capacity", map_downsampled_cell_capacity_);

  ph.param<bool>("add_scans_when_torso_rotated", add_scans_when_torso_rotated_, true);
  // ph.param<double>("decrease_rate", m_decreaseRate, 0.0);

  multiresolution_map_ = boost::make_shared<MapType>(map_size_, map_resolution_, map_levels_,
                                                                                map_cell_capacity_, map_frame_id_);
  // subscribe laserscanlines
  sub_cloud_.subscribe(phMT, "input", 3);
  cloud_message_filter_.reset(
      new tf::MessageFilter<sensor_msgs::PointCloud2>(sub_cloud_, tf_listener_, scan_assembler_frame_id_, 100, phMT));
  cloud_message_filter_->registerCallback(boost::bind(&MapNodelet::receivedCloud, this, _1));

  service_clear_map_ = ph.advertiseService("/surfel_map/clear_map", &MapNodelet::clearMapServiceCall, this);
  service_reset_ = ph.advertiseService("/surfel_map/reset", &MapNodelet::resetServiceCall, this);
  service_decrease_once_ = ph.advertiseService("/surfel_map/decrease_once", &MapNodelet::decreaseOnceServiceCall, this);

  service_add_points_ =
      ph.advertiseService("/surfel_map/add_points_to_map", &MapNodelet::addPointsToMapServiceCall, this);

  tf_broadcaster_thread_ =
      boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&MapNodelet::broadcastTf, this)));
  process_scan_thread_ =
      boost::shared_ptr<boost::thread>(new boost::thread(boost::bind(&MapNodelet::processScans, this)));

}

void MapNodelet::broadcastTf()
{
  ros::Rate loopRate(50);
  while (running_)
  {
    updateTransforms(ros::Time::now());
    loopRate.sleep();
  }
}

bool MapNodelet::clearMapServiceCall(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
{
  boost::unique_lock<boost::mutex> lock(mutex_map_);
  clearMap();

  return true;
}

bool MapNodelet::resetServiceCall(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
{
  boost::unique_lock<boost::mutex> lock(mutex_map_);
  clearMap();

  last_base_link_transform_.setIdentity();
  last_base_link_oriented_transform_.setIdentity();

  boost::unique_lock<boost::mutex> correctionLock(mutex_correction_transform_);
  correction_transform_ = Eigen::Matrix4d::Identity();
  map_orientation_ = Eigen::Matrix4d::Identity();

  return true;
}

void MapNodelet::clearMap()
{
  multiresolution_map_ = boost::make_shared<MapType>(map_size_, map_resolution_, map_levels_, map_cell_capacity_, map_frame_id_);

  first_scan_ = true;
  scan_number_ = 0;
  last_scan_stamp_ = ros::Time(0);

  add_new_points_ = true;
}

bool MapNodelet::decreaseOnceServiceCall(std_srvs::Empty::Request& req, std_srvs::Empty::Response& res)
{
  decrease_once_ = true;

  return true;
}

bool MapNodelet::addPointsToMapServiceCall(AddPointsToMapRequest& req, AddPointsToMapResponse& res)
{
  if (req.addPoints == 0)
  {
    add_new_points_ = false;
  }
  else
  {
    add_new_points_ = true;
  }
  return true;
}

void MapNodelet::receivedCloud(const sensor_msgs::PointCloud2ConstPtr& msg)
{
  NODELET_DEBUG_STREAM("received map from timestamp " << msg->header.stamp << " at time " << ros::Time::now()
                                                      << " diff: " <<  (ros::Time::now() - msg->header.stamp));
  try
  {
    pcl::PointCloud<InputPointType>::Ptr pcl_cloud(new pcl::PointCloud<InputPointType>); // TODO
    pcl::fromROSMsg<InputPointType>(*msg, *pcl_cloud);
    scan_buffer_.push_front(pcl_cloud);
  }
  catch (pcl::PCLException& ex)
  {
    NODELET_ERROR("Failed to convert a message to a pcl type, dropping observation: %s", ex.what());
  }

  if (msg->header.frame_id != scan_assembler_frame_id_)
  {
    NODELET_ERROR_THROTTLE(10.0, "input frame is different from m_scanAssemblerFrameId! This could lead to transform "
                                 "problems.");
  }

  // determine average delta between two scans
  if ( last_scan_received_stamp_ > ros::Time(0) )
  {
    ros::Duration current_scan_stamp_delta (msg->header.stamp - last_scan_received_stamp_);

    if ( scan_stamp_delta_ > ros::Duration(0) )
      scan_stamp_delta_ = current_scan_stamp_delta * 0.1 + scan_stamp_delta_ * 0.9;
    else
      scan_stamp_delta_ = current_scan_stamp_delta;
    NODELET_DEBUG_STREAM("scan stamp delta: " << current_scan_stamp_delta.toSec() << " " << scan_stamp_delta_.toSec());
  }

  last_scan_received_stamp_ = msg->header.stamp;
}

void MapNodelet::processScans()
{
  while (running_)
  {
    pcl::PointCloud<InputPointType>::Ptr cloud;

    while (scan_buffer_.size() > 1)
    {
      NODELET_ERROR_STREAM("Too many messages in scan buffer. Dropping scans." );
      scan_buffer_.pop_back(&cloud);
    }

    scan_buffer_.pop_back(&cloud);
    registerScan(cloud);
  }
}

// register the current pointcloud to the one before/the map
void MapNodelet::registerScan(pcl::PointCloud<InputPointType>::Ptr cloud) //TODO
{
  pcl::StopWatch callback_timer;
  callback_timer.reset();

  boost::unique_lock<boost::mutex> lock(mutex_map_);

  ros::Time scan_stamp;

  tf::StampedTransform base_link_transform;
  tf::StampedTransform base_link_oriented_transform;
  tf::StampedTransform scan_transform;

  base_link_transform.setIdentity();
  base_link_oriented_transform.setIdentity();
  scan_transform.setIdentity();

  NODELET_DEBUG_STREAM("registerScans thread:: received scan from " << cloud->header.stamp << " frame "
                                                                    << cloud->header.frame_id << " with "
                                                                    << cloud->size() << " points");

  scan_stamp = pcl_conversions::fromPCL(cloud->header.stamp);

  if (!first_scan_ && decrease_rate_ > 0.0 && decrease_once_)
  {
    float decrease_per_scan = decrease_rate_ * static_cast<float>((scan_stamp - last_scan_stamp_).toSec());
    multiresolution_map_->lock();
    multiresolution_map_->decreaseAll(decrease_per_scan);
    multiresolution_map_->unlock();
    NODELET_DEBUG_STREAM("decreasing with: " << decrease_per_scan);
    decrease_once_ = false;
  }

  if ( !getTranform(scan_assembler_frame_id_, "/base_link", scan_stamp, base_link_transform) )
    return;

  if (first_scan_)
  {
    // intialize map orientation with orientation between odometry and /base_link
    Eigen::Affine3d odom_rotation;
    tf::transformTFToEigen(tf::Transform(base_link_transform.getRotation().inverse()), odom_rotation);
    map_orientation_ = odom_rotation.matrix();
  }
  else
  {
    // update map orientation with delta between previous and current odometry orientation
    Eigen::Affine3d last_odom_rotation;
    tf::transformTFToEigen(tf::Transform(last_base_link_transform_.getRotation().inverse()), last_odom_rotation);
    Eigen::Affine3d odom_rotation;
    tf::transformTFToEigen(tf::Transform(base_link_transform.getRotation().inverse()), odom_rotation);
    map_orientation_ = (odom_rotation * last_odom_rotation.inverse()).matrix() * map_orientation_;

    // translate map with odometry delta
    tf::Vector3 delta_translation = base_link_transform.getOrigin() - last_base_link_transform_.getOrigin();
    Eigen::Vector3d delta_translation_eigen;
    tf::vectorTFToEigen(delta_translation, delta_translation_eigen);
    multiresolution_map_->lock();
    multiresolution_map_->translateMap(delta_translation_eigen.cast<float>());
    multiresolution_map_->unlock();
    NODELET_DEBUG_STREAM("deltaTranslation: " << delta_translation_eigen.transpose());
  }
  updateTransforms(scan_stamp);

  if ( !getTranform(scan_assembler_frame_id_, map_frame_id_, scan_stamp, base_link_oriented_transform) )
    return;

  Eigen::Affine3d scan_transform_eigen;
  tf::transformTFToEigen(base_link_oriented_transform.inverse(), scan_transform_eigen);

  pcl::PointCloud<InputPointType>::Ptr assembled_cloud(new pcl::PointCloud<InputPointType>());

  // transform pointcloud from fixed frame of the scanAssembler to the same frame the map is in (e.g. base_link)
  pcl::transformPointCloud(*cloud, *assembled_cloud, scan_transform_eigen.cast<float>());
  // correct the frame_id
  assembled_cloud->header.frame_id = multiresolution_map_->getFrameId();

  pcl::PointCloud<MapPointType>::Ptr assembled_cloud_rgb(new pcl::PointCloud<MapPointType>()); //TODO
  pcl::copyPointCloud(*assembled_cloud, *assembled_cloud_rgb);

  Eigen::Matrix4d transform = Eigen::Matrix4d::Identity();

  if (!first_scan_ && scan_number_ >= num_scans_registration_)
  {
    pcl::StopWatch registration_timer;
    registration_timer.reset();

    MapType mapScene(map_size_, map_resolution_, map_levels_, map_cell_capacity_, map_frame_id_);

    mapScene.addCloud(assembled_cloud_rgb);
    mapScene.evaluateAll();

    multiresolution_map_->lock();
    multiresolution_map_->evaluateAll(); // TODO: necessary??
    surfel_registration_.estimateTransformationLevenbergMarquardt(multiresolution_map_.get(), &mapScene, transform );
    multiresolution_map_->unlock();

    NODELET_DEBUG_STREAM("estimateTransformationLevenbergMarquardt took: " << registration_timer.getTime() << " ms");
    NODELET_DEBUG_STREAM("transform \n" << transform);
  }
  NODELET_DEBUG_STREAM("after registration: " << callback_timer.getTime() << " ms");

  pcl::PointCloud<MapPointType>::Ptr assembled_cloud_reg(new pcl::PointCloud<MapPointType>); // TODO
  pcl::transformPointCloud(*assembled_cloud_rgb, *assembled_cloud_reg, transform.cast<float>());

  if (add_new_points_ && !checkTorsoRotation(scan_stamp))
  {
    multiresolution_map_->setOccupancyParameters(param_clamping_thresh_min_(), param_clamping_thresh_max_(), param_prob_hit_(), param_prob_miss_());

    Eigen::Matrix4f sensorTransform = Eigen::Matrix4f::Identity();
    getTranform(sensor_frame_id_, "/base_link", scan_stamp, sensorTransform);

    // add current cloud to map
    multiresolution_map_->addCloud(assembled_cloud_reg, param_update_occupancy_(), sensorTransform.inverse());
  }
  else
  {
    NODELET_DEBUG_STREAM("not adding scan to map");
    if (first_scan_)
    {
      return;
    }
  }

  NODELET_DEBUG_STREAM("after adding to map: " << callback_timer.getTime() << " ms");

  // translate the map to keep it egocentric
  multiresolution_map_->lock();
  multiresolution_map_->setLastUpdateTimestamp(scan_stamp);
  multiresolution_map_->translateMap(transform.block<3, 1>(0, 3).cast<float>());
  multiresolution_map_->unlock();

  Eigen::Affine3d base_link_transform_eigen;
  tf::transformTFToEigen(base_link_transform, base_link_transform_eigen);
  Eigen::Matrix4d base_link_oriented_in_odom_before_update = base_link_transform_eigen * map_orientation_;

  // update correction transforms for /world_corrected and /base_link_oriented frames
  {
    boost::unique_lock<boost::mutex> lock(mutex_correction_transform_);
    map_orientation_.block<3, 3>(0, 0) = map_orientation_.block<3, 3>(0, 0) * transform.inverse().block<3, 3>(0, 0);

    Eigen::Affine3d base_link_oriented_transform_eigen;
    tf::transformTFToEigen(base_link_oriented_transform, base_link_oriented_transform_eigen);

    correction_transform_ *= (base_link_oriented_in_odom_before_update * transform * base_link_oriented_in_odom_before_update.inverse());

  }
  updateTransforms(scan_stamp);

  // publish point clouds for visualization
  pcl::PointCloud<pcl::PointXYZ>::Ptr assembled_cloud_reg_XYZ(new pcl::PointCloud<pcl::PointXYZ>);
  pcl::copyPointCloud(*assembled_cloud_reg, *assembled_cloud_reg_XYZ);
  mrs_laser_mapping::SurfelMapPublisher::getInstance()->publishTransformedScenePointCloud(assembled_cloud_reg_XYZ);
  pcl::PointCloud<pcl::PointXYZ>::Ptr assembled_cloud_XYZ(new pcl::PointCloud<pcl::PointXYZ>);
  pcl::copyPointCloud(*assembled_cloud_rgb, *assembled_cloud_XYZ);
  mrs_laser_mapping::SurfelMapPublisher::getInstance()->publishScenePointCloud(assembled_cloud_XYZ);

  mrs_laser_mapping::SurfelMapPublisher::getInstance()->publishSurfelMarkers<MapPointType, MapType>(multiresolution_map_);
  mrs_laser_mapping::SurfelMapPublisher::getInstance()->publishPointCloud<MapPointType, MapType>(multiresolution_map_);
  mrs_laser_mapping::SurfelMapPublisher::getInstance()->publishDownsampledPointCloud<MapPointType, MapType>(
      multiresolution_map_, map_downsampled_size_, map_downsampled_resolution_, map_downsampled_levels_, map_downsampled_cell_capacity_);
  mrs_laser_mapping::MapPublisher::getInstance()->publishOccupiedCells<MapPointType, MapType>(multiresolution_map_);
  mrs_laser_mapping::MapPublisher::getInstance()->publishMapLevelColor<MapPointType, MapType>(multiresolution_map_);
  mrs_laser_mapping::MapPublisher::getInstance()->publishMapScanColor<MapPointType, MapType>(multiresolution_map_);

  // publish local map for slam nodelet
  if (scan_number_ >= num_scans_for_map_publishing_)
  {
    mrs_laser_mapping::MapPublisher::getInstance()->publishMap<MapPointType, MapType>( multiresolution_map_ );
  }

  NODELET_DEBUG_STREAM("finished processing of scan: " << scan_number_);
  NODELET_DEBUG_STREAM("one callback took: " << callback_timer.getTime() << " ms");

  if ( scan_stamp_delta_ > ros::Duration(0) && callback_timer.getTimeSeconds() > scan_stamp_delta_.toSec() )
    NODELET_WARN_STREAM("Processing scan took: " << callback_timer.getTimeSeconds() << "s. Average scan time is " << scan_stamp_delta_.toSec());

  scan_number_++;
  last_base_link_transform_ = base_link_transform;
  last_base_link_oriented_transform_ = base_link_oriented_transform;
  last_scan_stamp_ = scan_stamp;
  first_scan_ = false;

}

void MapNodelet::updateTransforms(ros::Time time)
{
  boost::unique_lock<boost::mutex> lock(mutex_correction_transform_);
  // update /base_link_oriented
  tf::Transform back_rotation;
  tf::transformEigenToTF(Eigen::Affine3d(map_orientation_), back_rotation);
  tf_broadcaster_.sendTransform(tf::StampedTransform(back_rotation, time, "/base_link", "/base_link_oriented"));
  tf_listener_.setTransform(tf::StampedTransform(back_rotation, time, "/base_link", "/base_link_oriented"));

  // update /world_corrected
  tf::StampedTransform transform_tf;
  transform_tf.setIdentity();
  tf::transformEigenToTF(Eigen::Affine3d(correction_transform_).inverse(), transform_tf);
  tf_broadcaster_.sendTransform(
        tf::StampedTransform(transform_tf, time, scan_assembler_frame_id_, "/world_corrected"));
  tf_listener_.setTransform(
      tf::StampedTransform(transform_tf, time, scan_assembler_frame_id_, "/world_corrected"));
}

bool MapNodelet::checkTorsoRotation(ros::Time time)
{
  /*
   * check for torso yaw angle and discard scans when torso is or was rotated (during scan)
   */
  double torso_roll, torso_pitch, torso_yaw;
  bool torso_rotated = false;
  if (!add_scans_when_torso_rotated_)
  {
    try
    {
      tf::StampedTransform torsoTransform;
      if (tf_listener_.waitForTransform("/torso_link", "/base_link", time,
                                        ros::Duration(transform_wait_duration_)))
      {
        tf_listener_.lookupTransform("/torso_link", "/base_link", time, torsoTransform);

        torsoTransform.getBasis().getRPY(torso_roll, torso_pitch, torso_yaw);
      }
      else
      {
        NODELET_ERROR("registerScans:: could not wait for torso transform");
        return false;
      }
    }
    catch (tf::TransformException ex)
    {
      NODELET_ERROR("registerScans:: could not lookup torso transform: %s", ex.what());
      return false;
    }

    // since torso rotation is versy slow we can check the angle at the last and current scan time
    if ((fabs(torso_yaw) > TORSO_YAW_THRESH) || (fabs(last_torso_yaw_) > TORSO_YAW_THRESH))
    {
      torso_rotated = true;
    }
    last_torso_yaw_ = torso_yaw;
  }
  return torso_rotated;
}

bool MapNodelet::getTranform(const std::string& target_frame, const std::string& source_frame, ros::Time time, Eigen::Matrix4f& transform)
{
  tf::StampedTransform transform_tf;
  if ( getTranform(target_frame, source_frame, time, transform_tf) )
  {
    Eigen::Affine3d transform_eigen;
    tf::transformTFToEigen(transform_tf, transform_eigen);
    transform = transform_eigen.matrix().cast<float>();
    return true;
  }
  else
    return false;
}

bool MapNodelet::getTranform(const std::string& target_frame, const std::string& source_frame, ros::Time time, tf::StampedTransform& transform)
{
  try
  {
    if (tf_listener_.waitForTransform(target_frame, source_frame, time,
    ros::Duration(transform_wait_duration_)))
    {
      tf_listener_.lookupTransform(target_frame, source_frame, time, transform);
    }
    else
    {
      NODELET_ERROR_STREAM("getTranform: could not wait for transform. target_frame: " << target_frame << " source_frame: " << source_frame );
      return false;
    }
  }
  catch (tf::TransformException ex)
  {
    NODELET_ERROR("getTranform: could not lookup transform: %s", ex.what());
    return false;
  }
  return true;
}

}

PLUGINLIB_EXPORT_CLASS(mrs_laser_mapping::MapNodelet, nodelet::Nodelet)

max_stars_repo_name: AIS-Bonn/MRSLaserMap

max_issues_repo_name: AIS-Bonn/MRSLaserMap

max_forks_repo_name: AIS-Bonn/MRSLaserMap


text:
//  (C) Copyright Gennadiy Rozental 2001-2008.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.
//
//  File        : $RCSfile$
//
//  Version     : $Revision$
//
//  Description : minimal testing unit test
// ***************************************************************************

// Boost.Test
#include <boost/test/minimal.hpp>

//____________________________________________________________________________//

struct bool_convertible1 {
    bool_convertible1( bool v ) : v_( v ) {}
    operator bool() { return v_; }

    bool v_;
};

//____________________________________________________________________________//

struct bool_convertible2 {
    bool_convertible2( int v ) : v_( v ) {}
    operator int() { return v_; }

    int v_;
};

//____________________________________________________________________________//

struct bool_convertible3 {
    bool_convertible3( void* v ) : v_( v ) {}

    struct Tester {};
    operator Tester*() { return (Tester*)v_; }

    void* v_;
};

//____________________________________________________________________________//

int
test_main( int /*argc*/, char* /*argv*/[] )
{
    int i = 1;

    BOOST_CHECK( i == 1 );
    BOOST_CHECK( i == 2 );

    BOOST_CHECK( bool_convertible1( true  ) );
    BOOST_CHECK( bool_convertible1( false ) );

    BOOST_CHECK( bool_convertible2( 1 ) );
    BOOST_CHECK( bool_convertible2( 0 ) );

    BOOST_CHECK( bool_convertible3( (void*)1 ) );
    BOOST_CHECK( bool_convertible3( NULL ) );

    BOOST_ERROR( "Some error" );

    BOOST_REQUIRE( i == 4 );

    return 0;
}

//____________________________________________________________________________//

// EOF

max_stars_repo_name: Abce/boost


text:
/*
 * token_types.hpp
 *
 *  Created on: 19 апр. 2016 г.
 *      Author: sergey.fedorov
 */

#ifndef WIRE_IDL_TOKEN_TYPES_HPP_
#define WIRE_IDL_TOKEN_TYPES_HPP_

#include <boost/spirit/include/lex_lexertl.hpp>

namespace wire {
namespace idl {
namespace lexer {

//----------------------------------------------------------------------------
/**
 * Token types
 */
enum token_type {
    token_locn    = ::boost::spirit::lex::min_token_id,
    //@{
    /** @name Keywords */
    token_ns,
    token_struct,
    token_class,
    token_interface,
    token_exception,
    token_enum,
    token_const,
    token_using,
    token_throw,
    //@}
    //@{
    /** @name Punctuation */
    token_comma,
    token_colon,
    token_scope_resolution,
    token_semicolon,
    token_assign,
    token_asterisk,
    token_brace_open,
    token_brace_close,
    token_block_start,
    token_block_end,
    token_angled_open,
    token_angled_close,
    token_attrib_start,
    token_attrib_end,
    //@}
    token_identifier,
    token_number,
    token_oct_number,
    token_hex_number,
    token_float_literal,
    token_quoted_string,

    token_c_comment,
    token_cpp_comment,

    token_eol,
    token_whitespace,
    token_any
};

template <typename Attribute = ::boost::spirit::lex::unused_type,
        typename Char = char>
using token_def = ::boost::spirit::lex::token_def<Attribute, Char, token_type>;

template <typename Iterator = char const*,
        typename AttributeTypes = ::boost::mpl::vector0<>,
        typename HasState = ::boost::mpl::true_ >
using token = ::boost::spirit::lex::lexertl::token<Iterator, AttributeTypes, HasState, token_type>;

}  /* namespace lexer */
}  /* namespace idl */
}  /* namespace wire */

#endif /* WIRE_IDL_TOKEN_TYPES_HPP_ */

max_stars_repo_name: zmij/wire

max_issues_repo_name: zmij/wire

max_forks_repo_name: zmij/wire


text:
//
// stream-recorder.hpp
//
//  Created by Peter Gusev on 9 October 2018.
//  Copyright 2013-2018 Regents of the University of California
//

#ifndef __stream_recorder_hpp__
#define __stream_recorder_hpp__

#include <boost/shared_ptr.hpp>

namespace ndn {
    class Face;
    class KeyChain;
}

namespace ndnlog {
    namespace new_api {
        class Logger;
    }
}

namespace ndnrtc {
    class MetaFetcher;
    class NamespaceInfo;
    class StorageEngine;
    class StreamRecorderImpl;

    /**
     * This class fetches (live) ndnrtc stream and stores all associated data packets
     * into persistent storage for later retrieval.
     * StreamRecorder may operate in several modes:
     *  -- ffwd: fetch stream starting from the most recent data, until stopped
     *  -- bckwd: fetch stream startging from the most recent data moving backwards
     *  -- ffwd && bckwd: fetch stream strarting from the most recent data, in
     *      both directions (forward and backwards)
     * StreamRecorder can be initialized with a seed frame, which will be
     * used to as a starting frame for fetching (instead of using most recent
     * frame).
     * StreamRecroder can be intialized for fetching N frames. In this case, only
     * data packets associated with N frames (Key and Delta) will be fetched.
     */
    class StreamRecorder {
        public:

        typedef enum _FetchDirection {
            Forward = 1 << 0,
            Backward = 1 << 1
        } FetchDirection;

        typedef struct _FetchSettings {
            uint16_t lifetime_;
            uint8_t direction_;
            uint32_t seedFrame_;
            uint32_t recordLength_;
            uint8_t pipelineSize_;
            uint32_t streamMetaFetchInterval_, threadMetaFetchInterval_;
        } FetchSettings;

        typedef struct _Stats {
            size_t manifestsStored_;
            size_t streamMetaStored_, threadMetaStored_;
            size_t keyStored_, deltaStored_;
            uint32_t latestKeyFetched_, latestDeltaFetched_;
            uint32_t latestKeyRequested_, latestDeltaRequested_;
            uint64_t totalSegmentsStored_;
            size_t deltaFailed_, keyFailed_;
            size_t pendingFrames_;
        } Stats;

        static const FetchSettings Default;

        /**
         * Construcor:
         * @param io io_service which will be used for asynchronous Interest/Data processing.
         * @param storageEngine Persisten storage engine to store data in.
         * @param streamPrefix ndnrtc stream prefix.
         * @param face Face object used for expressing interests.
         * @param keyChain KeyChain object used to verify incoming data. If ommitted,
         *                  a default keychain with NoVerify policy will be used.
         */
        StreamRecorder(const boost::shared_ptr<StorageEngine>& storageEngine,
                        const NamespaceInfo& ninfo,
                        const boost::shared_ptr<ndn::Face>& face,
                        const boost::shared_ptr<ndn::KeyChain> keyChain);
        ~StreamRecorder(){ pimpl_.reset(); }

        /**
         * Start stream fetching.
         * @param directionMask Defines direction for retrieving data. If ommitted, will fetch newly generated frames.
         * @param seedFrame Initial frame to start fetching from. If ommitted, start from the most recent frame.
         * @param nFrames Number of frames to fetch. If ommitted, will keep fetching until stopped.
         */
        void start(const FetchSettings& settings = Default);
        void stop();

        bool isFetching();
        const std::string getStreamPrefix() const;
        const std::string getThreadName() const;
        void setLogger(const boost::shared_ptr<ndnlog::new_api::Logger>& logger);
        const Stats& getCurrentStats() const;

        private:
        boost::shared_ptr<StreamRecorderImpl> pimpl_;
    };
}

#endif

max_stars_repo_name: luckiday/ndnrtc

max_issues_repo_name: luckiday/ndnrtc

max_forks_repo_name: luckiday/ndnrtc


text:
/*
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or copy at
 * http://www.boost.org/LICENSE_1_0.txt)
 *
 * Copyright (c) 2011 Helge Bahmann
 * Copyright (c) 2013-2014 Andrey Semashev
 */
/*!
 * \file   lockpool.cpp
 *
 * This file contains implementation of the lockpool used to emulate atomic ops.
 */

#include <boost/assert.hpp>
#include <boost/atomic/capabilities.hpp>
#include <boost/config.hpp>
#include <boost/memory_order.hpp>
#include <cstddef>

#if BOOST_ATOMIC_FLAG_LOCK_FREE == 2

#include <boost/atomic/detail/operations_lockfree.hpp>

#elif !defined(BOOST_HAS_PTHREADS)
#error Boost.Atomic: Unsupported target platform, POSIX threads are required when native atomic operations are not available
#else
#include <pthread.h>
#define BOOST_ATOMIC_USE_PTHREAD
#endif

#include <boost/atomic/detail/lockpool.hpp>
#include <boost/atomic/detail/pause.hpp>

namespace mars_boost {}
namespace boost = mars_boost;
namespace mars_boost {
namespace atomics {
namespace detail {

namespace {

// This seems to be the maximum across all modern CPUs
// NOTE: This constant is made as a macro because some compilers (gcc 4.4 for
// one) don't allow enums or namespace scope constants in alignment attributes
#define BOOST_ATOMIC_CACHE_LINE_SIZE 64

#if defined(BOOST_ATOMIC_USE_PTHREAD)
typedef pthread_mutex_t lock_type;
#else
typedef atomics::detail::operations<1u, false> lock_operations;
typedef lock_operations::storage_type lock_type;
#endif

enum {
  padding_size = (sizeof(lock_type) <= BOOST_ATOMIC_CACHE_LINE_SIZE
                      ? (BOOST_ATOMIC_CACHE_LINE_SIZE - sizeof(lock_type))
                      : (BOOST_ATOMIC_CACHE_LINE_SIZE -
                         sizeof(lock_type) % BOOST_ATOMIC_CACHE_LINE_SIZE))
};

template <unsigned int PaddingSize>
struct BOOST_ALIGNMENT(BOOST_ATOMIC_CACHE_LINE_SIZE) padded_lock {
  lock_type lock;
  // The additional padding is needed to avoid false sharing between locks
  char padding[PaddingSize];
};

template <>
struct BOOST_ALIGNMENT(BOOST_ATOMIC_CACHE_LINE_SIZE) padded_lock<0u> {
  lock_type lock;
};

typedef padded_lock<padding_size> padded_lock_t;

static padded_lock_t g_lock_pool[41]
#if defined(BOOST_ATOMIC_USE_PTHREAD)
    = {{PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}, {PTHREAD_MUTEX_INITIALIZER},
       {PTHREAD_MUTEX_INITIALIZER}}
#endif
;

}  // namespace

#if !defined(BOOST_ATOMIC_USE_PTHREAD)

// NOTE: This function must NOT be inline. Otherwise MSVC 9 will sometimes
// generate broken code for modulus operation which result in crashes.
BOOST_ATOMIC_DECL lockpool::scoped_lock::scoped_lock(const volatile void* addr)
    BOOST_NOEXCEPT
    : m_lock(&g_lock_pool[reinterpret_cast<std::size_t>(addr) %
                          (sizeof(g_lock_pool) / sizeof(*g_lock_pool))]
                  .lock) {
  while (lock_operations::test_and_set(*static_cast<lock_type*>(m_lock),
                                       memory_order_acquire)) {
    do {
      atomics::detail::pause();
    } while (!!lock_operations::load(*static_cast<lock_type*>(m_lock),
                                     memory_order_relaxed));
  }
}

BOOST_ATOMIC_DECL lockpool::scoped_lock::~scoped_lock() BOOST_NOEXCEPT {
  lock_operations::clear(*static_cast<lock_type*>(m_lock),
                         memory_order_release);
}

BOOST_ATOMIC_DECL void signal_fence() BOOST_NOEXCEPT;

#else  // !defined(BOOST_ATOMIC_USE_PTHREAD)

BOOST_ATOMIC_DECL lockpool::scoped_lock::scoped_lock(const volatile void* addr)
    BOOST_NOEXCEPT
    : m_lock(&g_lock_pool[reinterpret_cast<std::size_t>(addr) %
                          (sizeof(g_lock_pool) / sizeof(*g_lock_pool))]
                  .lock) {
  BOOST_VERIFY(pthread_mutex_lock(static_cast<pthread_mutex_t*>(m_lock)) == 0);
}

BOOST_ATOMIC_DECL lockpool::scoped_lock::~scoped_lock() BOOST_NOEXCEPT {
  BOOST_VERIFY(pthread_mutex_unlock(static_cast<pthread_mutex_t*>(m_lock)) ==
               0);
}

#endif  // !defined(BOOST_ATOMIC_USE_PTHREAD)

BOOST_ATOMIC_DECL void lockpool::thread_fence() BOOST_NOEXCEPT {
#if BOOST_ATOMIC_THREAD_FENCE > 0
  atomics::detail::thread_fence(memory_order_seq_cst);
#else
  // Emulate full fence by locking/unlocking a mutex
  scoped_lock lock(0);
#endif
}

BOOST_ATOMIC_DECL void lockpool::signal_fence() BOOST_NOEXCEPT {
  // This function is intentionally non-inline, even if empty. This forces the
  // compiler to treat its call as a compiler barrier.
#if BOOST_ATOMIC_SIGNAL_FENCE > 0
  atomics::detail::signal_fence(memory_order_seq_cst);
#endif
}

}  // namespace detail
}  // namespace atomics
}  // namespace mars_boost

max_stars_repo_name: habbyge/mars

max_issues_repo_name: habbyge/mars

max_forks_repo_name: habbyge/mars


text:
#pragma once

#include <bunsan/error.hpp>

#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/if.hpp>
#include <boost/preprocessor/stringize.hpp>

#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/to_tuple.hpp>
#include <boost/preprocessor/tuple/to_seq.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/tuple/enum.hpp>
#include <boost/preprocessor/tuple/size.hpp>

#include <istream>
#include <ostream>
#include <string>

#include <cstdint>

namespace bunsan {
namespace stream_enum {

/// Base exception class for stream_enum errors.
struct invalid_value_error : virtual error {};

/// String representation is invalid.
struct invalid_string_representation_error : virtual invalid_value_error {
  using string_value = boost::error_info<struct tag_string_value, std::string>;
};

/// Enum representation is invalid.
struct invalid_enum_representation_error : virtual invalid_value_error {
  using enum_value = boost::error_info<struct tag_enum_value, std::intmax_t>;
};

}  // namespace stream_enum
}  // namespace bunsan

/*!
 * \brief Macro repeats a macro for each element in a tuple.
 *
 * macro(r, data, item) for each item in tuple
 */
#define BOOST_PP_TUPLE_FOR_EACH(macro, data, tuple) \
  BOOST_PP_SEQ_FOR_EACH(macro, data, BOOST_PP_TUPLE_TO_SEQ(tuple))

// ******************************** SITEM ******************************** //
// item name
#define BUNSAN_STREAM_ENUM_ITEM_NAME_DEFAULT(ITEM) ITEM

#define BUNSAN_STREAM_ENUM_ITEM_NAME_INITIALIZED(ITEM_VALUE) \
  BOOST_PP_TUPLE_ELEM(0, ITEM_VALUE)

// item
#define BUNSAN_STREAM_ENUM_ITEM_DEFAULT(ITEM) ITEM

#define BUNSAN_STREAM_ENUM_ITEM_INITIALIZED_1(ITEM) BOOST_PP_TUPLE_ELEM(0, ITEM)
#define BUNSAN_STREAM_ENUM_ITEM_INITIALIZED_2(ITEM_VALUE) \
  BOOST_PP_TUPLE_ELEM(0, ITEM_VALUE) = BOOST_PP_TUPLE_ELEM(1, ITEM_VALUE)

#define BUNSAN_STREAM_ENUM_ITEM_INITIALIZED(ITEM)    \
  BOOST_PP_CAT(BUNSAN_STREAM_ENUM_ITEM_INITIALIZED_, \
               BOOST_PP_TUPLE_SIZE(ITEM))(ITEM)

// qualified item
#define BUNSAN_STREAM_ENUM_QITEM_DEFAULT(ENUM, ITEM) ITEM

#define BUNSAN_STREAM_ENUM_QITEM_CLASS(ENUM, ITEM) ENUM::ITEM

// sitem = (raw item name, qualified item name, declaration item name, string)
#define BUNSAN_STREAM_ENUM_SITEM_CREATE(ENUM, CLASS_TYPE, ITEM, DECL_TYPE)   \
  ((BOOST_PP_CAT(BUNSAN_STREAM_ENUM_ITEM_NAME_, DECL_TYPE)(ITEM),            \
    BOOST_PP_CAT(BUNSAN_STREAM_ENUM_QITEM_, CLASS_TYPE)(                     \
        ENUM, BOOST_PP_CAT(BUNSAN_STREAM_ENUM_ITEM_NAME_, DECL_TYPE)(ITEM)), \
    BOOST_PP_CAT(BUNSAN_STREAM_ENUM_ITEM_, DECL_TYPE)(ITEM),                 \
    BOOST_PP_STRINGIZE(                                                      \
        BOOST_PP_CAT(BUNSAN_STREAM_ENUM_ITEM_NAME_, DECL_TYPE)(ITEM))))

#define BUNSAN_STREAM_ENUM_SITEMS_CREATE_PART(r, ENUM_CLASS_DECL, ITEM)    \
  BUNSAN_STREAM_ENUM_SITEM_CREATE(BOOST_PP_TUPLE_ELEM(0, ENUM_CLASS_DECL), \
                                  BOOST_PP_TUPLE_ELEM(1, ENUM_CLASS_DECL), \
                                  ITEM,                                    \
                                  BOOST_PP_TUPLE_ELEM(2, ENUM_CLASS_DECL))

#define BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS_TYPE, ITEMS, DECL_TYPE) \
  BOOST_PP_SEQ_TO_TUPLE(                                                     \
      BOOST_PP_TUPLE_FOR_EACH(BUNSAN_STREAM_ENUM_SITEMS_CREATE_PART,         \
                              (ENUM, CLASS_TYPE, DECL_TYPE), ITEMS))

#define BUNSAN_STREAM_ENUM_SITEM_NAME(SITEM) BOOST_PP_TUPLE_ELEM(0, SITEM)
#define BUNSAN_STREAM_ENUM_SITEM_QNAME(SITEM) BOOST_PP_TUPLE_ELEM(1, SITEM)
#define BUNSAN_STREAM_ENUM_SITEM_DECLNAME(SITEM) BOOST_PP_TUPLE_ELEM(2, SITEM)
#define BUNSAN_STREAM_ENUM_SITEM_SNAME(SITEM) BOOST_PP_TUPLE_ELEM(3, SITEM)

// ******************************** SITEM ******************************** //

// ******************************** ERRORS ******************************** //

/// Error name for exception derived from
/// ::bunsan::stream_enum::invalid_value_error.
#define BUNSAN_STREAM_ENUM_ERROR(ENUM) BOOST_PP_CAT(ENUM, _invalid_value_error)

/// Error name for exception derived from
/// ::bunsan::stream_enum::invalid_string_representation_error.
#define BUNSAN_STREAM_ENUM_ERROR_STRING(ENUM) \
  BOOST_PP_CAT(ENUM, _invalid_string_representation_error)

/// Error name for exception derived from
/// ::bunsan::stream_enum::invalid_enum_representation_error.
#define BUNSAN_STREAM_ENUM_ERROR_ENUM(ENUM) \
  BOOST_PP_CAT(ENUM, _invalid_enum_representation_error)

/*!
 * \brief Define exceptions derived from appropriate
 * in ::bunsan::stream_enum namespace.
 *
 * \param ENUM enum name
 */
#define BUNSAN_STREAM_ENUM_ERRORS(ENUM)                                        \
  struct BUNSAN_STREAM_ENUM_ERROR(ENUM)                                        \
      : virtual ::bunsan::stream_enum::invalid_value_error {};                 \
  struct BUNSAN_STREAM_ENUM_ERROR_STRING(ENUM)                                 \
      : virtual BUNSAN_STREAM_ENUM_ERROR(ENUM),                                \
        virtual ::bunsan::stream_enum::invalid_string_representation_error {}; \
  struct BUNSAN_STREAM_ENUM_ERROR_ENUM(ENUM)                                   \
      : virtual BUNSAN_STREAM_ENUM_ERROR(ENUM),                                \
        virtual ::bunsan::stream_enum::invalid_enum_representation_error {};

// ******************************** ERRORS ******************************** //

// *************************** IMPLEMENTATION  *************************** //

#define BUNSAN_STREAM_ENUM_STREAM_FREE inline

#define BUNSAN_STREAM_ENUM_STREAM_INCLASS friend inline

#define BUNSAN_STREAM_ENUM_ISTREAM_PART(r, data, SITEM) \
  if (val == BUNSAN_STREAM_ENUM_SITEM_SNAME(SITEM))     \
    item = BUNSAN_STREAM_ENUM_SITEM_QNAME(SITEM);       \
  else

/// Define std::istream &operator>>(...).
#define BUNSAN_STREAM_ENUM_ISTREAM(SCOPE, ENUM, SITEMS)                        \
  BUNSAN_STREAM_ENUM_STREAM_##SCOPE std::istream &operator>>(std::istream &in, \
                                                             ENUM &item) {     \
    std::string val;                                                           \
    in >> val;                                                                 \
    BOOST_PP_TUPLE_FOR_EACH(BUNSAN_STREAM_ENUM_ISTREAM_PART, UNUSED, SITEMS)   \
    BOOST_THROW_EXCEPTION(                                                     \
        BUNSAN_STREAM_ENUM_ERROR_STRING(ENUM)() << ::bunsan::stream_enum::     \
            invalid_string_representation_error::string_value(val));           \
    return in;                                                                 \
  }

#define BUNSAN_STREAM_ENUM_OSTREAM_PART(r, data, SITEM) \
  case BUNSAN_STREAM_ENUM_SITEM_QNAME(SITEM):           \
    out << BUNSAN_STREAM_ENUM_SITEM_SNAME(SITEM);       \
    break;

/// Define std::ostream &operator<<(...).
#define BUNSAN_STREAM_ENUM_OSTREAM(SCOPE, ENUM, SITEMS)                        \
  BUNSAN_STREAM_ENUM_STREAM_##SCOPE std::ostream &operator<<(                  \
      std::ostream &out, const ENUM &item) {                                   \
    switch (item) {                                                            \
      BOOST_PP_TUPLE_FOR_EACH(BUNSAN_STREAM_ENUM_OSTREAM_PART, UNUSED, SITEMS) \
      default:                                                                 \
        BOOST_THROW_EXCEPTION(                                                 \
            BUNSAN_STREAM_ENUM_ERROR_ENUM(ENUM)() << ::bunsan::stream_enum::   \
                invalid_enum_representation_error::enum_value(static_cast<     \
                    ::bunsan::stream_enum::invalid_enum_representation_error:: \
                        enum_value::value_type>(item)));                       \
    }                                                                          \
    return out;                                                                \
  }

#define BUNSAN_STREAM_ENUM_SITEM_DECLNAME_ARG(r, data, SITEM) \
  (BUNSAN_STREAM_ENUM_SITEM_DECLNAME(SITEM))

#define BUNSAN_STREAM_ENUM_HEADER_NAME_DEFAULT(ENUM) enum ENUM

#define BUNSAN_STREAM_ENUM_HEADER_NAME_CLASS(ENUM) enum class ENUM

#define BUNSAN_STREAM_ENUM_HEADER_DEFAULT(ENUM, CLASS_TYPE, TYPE) \
  BUNSAN_STREAM_ENUM_HEADER_NAME_##CLASS_TYPE(ENUM)

#define BUNSAN_STREAM_ENUM_HEADER_TYPED(ENUM, CLASS_TYPE, TYPE) \
  BUNSAN_STREAM_ENUM_HEADER_NAME_##CLASS_TYPE(ENUM) : TYPE

/*!
 * \brief Define enum with operator<<() and operator>>() defined.
 *
 * This enum is suitable for boost::lexical_cast<NAME>(std::string)
 * and boost::lexical_cast<std::string>(NAME).
 *
 * \param SCOPE where enum is defined: DEFAULT or INCLASS
 * \param ENUM enum name
 * \param CLASS_TYPE DEFAULT for old-style enum and CLASS for scoped enum
 * \param TYPED_TYPE TYPED to override underlying type DEFAULT otherwise
 * \param TYPE if TYPED_TYPE is TYPED used as underlying type otherwise ignored
 * \param SITEMS tuple of sitem structures
 */
#define BUNSAN_STREAM_ENUM_IMPLEMENTATION(SCOPE, ENUM, CLASS_TYPE, TYPED_TYPE, \
                                          TYPE, SITEMS)                        \
  BUNSAN_STREAM_ENUM_HEADER_##TYPED_TYPE(                                      \
      ENUM, CLASS_TYPE, TYPE){BOOST_PP_SEQ_ENUM(BOOST_PP_TUPLE_FOR_EACH(       \
      BUNSAN_STREAM_ENUM_SITEM_DECLNAME_ARG, UNUSED, SITEMS))};                \
  BUNSAN_STREAM_ENUM_ERRORS(ENUM)                                              \
  BUNSAN_STREAM_ENUM_ISTREAM(SCOPE, ENUM, SITEMS)                              \
  BUNSAN_STREAM_ENUM_OSTREAM(SCOPE, ENUM, SITEMS)

// *************************** IMPLEMENTATION  *************************** //

// ******************************** PUBLIC ******************************** //

/*!
 * \note naming:
 * BUNSAN[_INCLASS][_TYPED]_STREAM_ENUM[_CLASS][_INITIALIZED](ENUM[, TYPE],
 *ITEMS)
 * \param INCLASS if declared inside class (or struct) scope FREE otherwise
 * \param TYPED argument after enum name is enum underlying type
 * \param CLASS if enum is scoped
 * \param INITIALIZED to override enum values,
 * (item_name) or (item_name, item_value) should be used
 *
 * Examples:
 *
 * \code{.cpp}
 * BUNSAN_STREAM_ENUM(my_enum, (
 *   my_value_1,
 *   my_value_2
 * ))
 *
 * enum my_enum {
 *   my_value_1,
 *   my_value_2
 * };
 * \endcode
 *
 * \code{.cpp}
 * struct my_struct
 * {
 *   BUNSAN_INCLASS_STREAM_ENUM(my_enum, (
 *     my_value_1,
 *     my_value_2
 *   ))
 * };
 *
 * struct my_struct {
 *   enum my_enum {
 *     my_value_1,
 *     my_value_2
 *   };
 * };
 * \endcode
 *
 * \code{.cpp}
 * BUNSAN_STREAM_ENUM_INITIALIZED(my_enum, (
 *   (my_value_1),
 *   (my_value_2, 10)
 *   (my_value_3)
 * ))
 *
 * enum my_enum {
 *   my_value_1,
 *   my_value_2 = 10,
 *   my_value_3
 * };
 * \endcode
 *
 * \code{.cpp}
 * BUNSAN_TYPED_STREAM_ENUM(my_enum, char, (
 *   my_value_1,
 *   my_value_2
 * ))
 *
 * enum my_enum: char {
 *   my_value_1,
 *   my_value_2
 * };
 * \endcode
 */

#define BUNSAN_STREAM_ENUM(ENUM, ITEMS)     \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(        \
      FREE, ENUM, DEFAULT, DEFAULT, UNUSED, \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, DEFAULT, ITEMS, DEFAULT))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_TYPED_STREAM_ENUM(ENUM, TYPE, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                \
      FREE, ENUM, DEFAULT, TYPED, TYPE,             \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, DEFAULT, ITEMS, DEFAULT))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_STREAM_ENUM_INITIALIZED(ENUM, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                \
      FREE, ENUM, DEFAULT, DEFAULT, UNUSED,         \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, DEFAULT, ITEMS, INITIALIZED))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_TYPED_STREAM_ENUM_INITIALIZED(ENUM, TYPE, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                            \
      FREE, ENUM, DEFAULT, TYPED, TYPE,                         \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, DEFAULT, ITEMS, INITIALIZED))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_INCLASS_STREAM_ENUM(ENUM, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(            \
      INCLASS, ENUM, DEFAULT, DEFAULT, UNUSED,  \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, DEFAULT, ITEMS, DEFAULT))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_INCLASS_TYPED_STREAM_ENUM(ENUM, TYPE, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                        \
      INCLASS, ENUM, DEFAULT, TYPED, TYPE,                  \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, DEFAULT, ITEMS, DEFAULT))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_INCLASS_STREAM_ENUM_INITIALIZED(ENUM, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                        \
      INCLASS, ENUM, DEFAULT, DEFAULT, UNUSED,              \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, DEFAULT, ITEMS, INITIALIZED))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_INCLASS_TYPED_STREAM_ENUM_INITIALIZED(ENUM, TYPE, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                                    \
      INCLASS, ENUM, DEFAULT, TYPED, TYPE,                              \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, DEFAULT, ITEMS, INITIALIZED))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_STREAM_ENUM_CLASS(ENUM, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(          \
      FREE, ENUM, CLASS, DEFAULT, UNUSED,     \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS, ITEMS, DEFAULT))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_TYPED_STREAM_ENUM_CLASS(ENUM, TYPE, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                      \
      FREE, ENUM, CLASS, TYPED, TYPE,                     \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS, ITEMS, DEFAULT))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_STREAM_ENUM_CLASS_INITIALIZED(ENUM, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                      \
      FREE, ENUM, CLASS, DEFAULT, UNUSED,                 \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS, ITEMS, INITIALIZED))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_TYPED_STREAM_ENUM_CLASS_INITIALIZED(ENUM, TYPE, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                                  \
      FREE, ENUM, CLASS, TYPED, TYPE,                                 \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS, ITEMS, INITIALIZED))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_INCLASS_STREAM_ENUM_CLASS(ENUM, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                  \
      INCLASS, ENUM, CLASS, DEFAULT, UNUSED,          \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS, ITEMS, DEFAULT))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_INCLASS_TYPED_STREAM_ENUM_CLASS(ENUM, TYPE, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                              \
      INCLASS, ENUM, CLASS, TYPED, TYPE,                          \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS, ITEMS, DEFAULT))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_INCLASS_STREAM_ENUM_CLASS_INITIALIZED(ENUM, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                              \
      INCLASS, ENUM, CLASS, DEFAULT, UNUSED,                      \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS, ITEMS, INITIALIZED))

/// \copydoc BUNSAN_STREAM_ENUM
#define BUNSAN_INCLASS_TYPED_STREAM_ENUM_CLASS_INITIALIZED(ENUM, TYPE, ITEMS) \
  BUNSAN_STREAM_ENUM_IMPLEMENTATION(                                          \
      INCLASS, ENUM, CLASS, TYPED, TYPE,                                      \
      BUNSAN_STREAM_ENUM_SITEMS_CREATE(ENUM, CLASS, ITEMS, INITIALIZED))

// ******************************** PUBLIC ******************************** //