text:
#define BOOST_TEST_DYN_LINK
#include <canard/net/ofp/v13/message/async_config.hpp>
#include <boost/test/unit_test.hpp>

#include <utility>
#include "../../test_utility.hpp"

namespace ofp = canard::net::ofp;
namespace v13 = ofp::v13;
namespace protocol = v13::protocol;
namespace msg = v13::messages;

namespace {
  struct get_async_request_fixture {
    std::uint32_t xid = 0x12345678;
    msg::get_async_request sut{xid};
    std::vector<unsigned char> bin
      = "\x04\x1a\x00\x08\x12\x34\x56\x78"_bin;
  };
  using mask_type = msg::get_async_reply::mask_array_type;
  struct get_async_reply_parameter {
    std::uint32_t packet_in_master_mask = 0x01;
    std::uint32_t packet_in_slave_mask = 0x02;
    mask_type packet_in_mask{{
      packet_in_master_mask, packet_in_slave_mask
    }};
    std::uint32_t port_status_master_mask = 0x03;
    std::uint32_t port_status_slave_mask = 0x04;
    mask_type port_status_mask{{
      port_status_master_mask, port_status_slave_mask
    }};
    std::uint32_t flow_removed_master_mask = 0x05;
    std::uint32_t flow_removed_slave_mask = 0x06;
    mask_type flow_removed_mask{{
      flow_removed_master_mask, flow_removed_slave_mask
    }};
    std::uint32_t xid = 0x12345678;
  };
  struct get_async_reply_fixture : get_async_reply_parameter {
    msg::get_async_reply sut{
      packet_in_mask, port_status_mask, flow_removed_mask, xid
    };
    std::vector<unsigned char> bin
      = "\x04\x1b\x00\x20\x12\x34\x56\x78""\x00\x00\x00\x01\x00\x00\x00\x02"
        "\x00\x00\x00\x03\x00\x00\x00\x04""\x00\x00\x00\x05\x00\x00\x00\x06"
        ""_bin;
  };
  struct set_async_parameter {
    std::uint32_t packet_in_master_mask = 0x01020304;
    std::uint32_t packet_in_slave_mask = 0x10203040;
    mask_type packet_in_mask{{
      packet_in_master_mask, packet_in_slave_mask
    }};
    std::uint32_t port_status_master_mask = 0x05060708;
    std::uint32_t port_status_slave_mask = 0x50607080;
    mask_type port_status_mask{{
      port_status_master_mask, port_status_slave_mask
    }};
    std::uint32_t flow_removed_master_mask = 0x0a0b0c0d;
    std::uint32_t flow_removed_slave_mask = 0xa0b0c0d0;
    mask_type flow_removed_mask{{
      flow_removed_master_mask, flow_removed_slave_mask
    }};
    std::uint32_t xid = 0x12345678;
  };
  struct set_async_fixture : set_async_parameter {
    msg::set_async sut{
      packet_in_mask, port_status_mask, flow_removed_mask, xid
    };
    std::vector<unsigned char> bin
      = "\x04\x1c\x00\x20\x12\x34\x56\x78""\x01\x02\x03\x04\x10\x20\x30\x40"
        "\x05\x06\x07\x08\x50\x60\x70\x80""\x0a\x0b\x0c\x0d\xa0\xb0\xc0\xd0"
        ""_bin;
  };
}

BOOST_AUTO_TEST_SUITE(message_test)

BOOST_AUTO_TEST_SUITE(get_async_request)

  BOOST_AUTO_TEST_SUITE(type_definition)
    BOOST_AUTO_TEST_CASE(type_is_get_async_request)
    {
      using sut = msg::get_async_request;

      constexpr auto type = sut::type();

      BOOST_TEST(type == protocol::OFPT_GET_ASYNC_REQUEST);
    }
    BOOST_AUTO_TEST_CASE(min_length_is_size_of_ofp_header)
    {
      using sut = msg::get_async_request;

      constexpr auto min_length = sut::min_length();

      BOOST_TEST(min_length == sizeof(protocol::ofp_header));
    }
  BOOST_AUTO_TEST_SUITE_END() // type_definition

  BOOST_AUTO_TEST_SUITE(constructor)
    BOOST_AUTO_TEST_CASE(constructible_from_xid)
    {
      auto const xid = std::uint32_t{123};

      msg::get_async_request const sut{xid};

      BOOST_TEST(sut.xid() == xid);
      BOOST_TEST(sut.length() == sizeof(protocol::ofp_header));
    }
    BOOST_AUTO_TEST_CASE(constructible_without_xid)
    {
      msg::get_async_request const sut{};

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_header));
    }
    BOOST_FIXTURE_TEST_CASE(copy_constructible, get_async_request_fixture)
    {
      auto const& const_get_async_request = sut;

      auto const copy = const_get_async_request;

      BOOST_TEST((copy == sut));
    }
    BOOST_FIXTURE_TEST_CASE(move_constructible, get_async_request_fixture)
    {
      auto const& moved = sut;

      auto const copy = std::move(moved);

      BOOST_TEST((copy == sut));
    }
  BOOST_AUTO_TEST_SUITE_END() // constructor

  BOOST_AUTO_TEST_SUITE(equality)
    BOOST_AUTO_TEST_CASE(true_if_same_object)
    {
      auto const sut = msg::get_async_request{0x1234};

      BOOST_TEST((sut == sut));
    }
    BOOST_AUTO_TEST_CASE(true_if_xid_is_equal)
    {
      auto const xid = std::uint32_t{0x12121212};

      BOOST_TEST(
          (msg::get_async_request{xid}
        == msg::get_async_request{xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_xid_is_not_equal)
    {
      BOOST_TEST(
          (msg::get_async_request{0x1234}
        != msg::get_async_request{0x1235}));
    }
  BOOST_AUTO_TEST_SUITE_END() // equality

  BOOST_AUTO_TEST_SUITE(encode)
    BOOST_FIXTURE_TEST_CASE(generate_binary, get_async_request_fixture)
    {
      auto buffer = std::vector<unsigned char>{};

      sut.encode(buffer);

      BOOST_TEST(buffer.size() == sut.byte_length());
      BOOST_TEST(buffer == bin, boost::test_tools::per_element{});
    }
  BOOST_AUTO_TEST_SUITE_END() // encode

  BOOST_AUTO_TEST_SUITE(decode)
    BOOST_FIXTURE_TEST_CASE(construct_from_binary, get_async_request_fixture)
    {
      auto it = bin.begin();

      auto const get_async_request
        = msg::get_async_request::decode(it, bin.end());

      BOOST_TEST((it == bin.end()));
      BOOST_TEST((get_async_request == sut));
    }
  BOOST_AUTO_TEST_SUITE_END() // decode

BOOST_AUTO_TEST_SUITE_END() // get_async_request

BOOST_AUTO_TEST_SUITE(get_async_reply)

  BOOST_AUTO_TEST_SUITE(type_definition)
    BOOST_AUTO_TEST_CASE(type_is_get_async_reply)
    {
      using sut = msg::get_async_reply;

      constexpr auto type = sut::type();

      BOOST_TEST(type == protocol::OFPT_GET_ASYNC_REPLY);
    }
    BOOST_AUTO_TEST_CASE(min_length_is_size_of_ofp_async_config)
    {
      using sut = msg::get_async_reply;

      constexpr auto min_length = sut::min_length();

      BOOST_TEST(min_length == sizeof(protocol::ofp_async_config));
    }
  BOOST_AUTO_TEST_SUITE_END() // type_definition

  BOOST_AUTO_TEST_SUITE(constructor)
    BOOST_AUTO_TEST_CASE(constructible_from_each_mask_and_xid)
    {
      auto const packet_in_master_mask = 0x01;
      auto const packet_in_slave_mask = 0x02;
      auto const port_status_master_mask = 0x03;
      auto const port_status_slave_mask = 0x04;
      auto const flow_removed_master_mask = 0x05;
      auto const flow_removed_slave_mask = 0x06;
      auto const xid = std::uint32_t{0x12345678};

      msg::get_async_reply const sut{
          packet_in_master_mask, packet_in_slave_mask
        , port_status_master_mask, port_status_slave_mask
        , flow_removed_master_mask ,flow_removed_slave_mask
        , xid
      };

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_async_config));
      BOOST_TEST(sut.xid() == xid);
      BOOST_TEST(sut.packet_in_master_mask() == packet_in_master_mask);
      BOOST_TEST(sut.packet_in_slave_mask() == packet_in_slave_mask);
      BOOST_TEST(
             sut.packet_in_mask()
          == (mask_type{{ packet_in_master_mask, packet_in_slave_mask }})
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.port_status_master_mask() == port_status_master_mask);
      BOOST_TEST(sut.port_status_slave_mask() == port_status_slave_mask);
      BOOST_TEST(
             sut.port_status_mask()
          == (mask_type{{ port_status_master_mask, port_status_slave_mask }})
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.flow_removed_master_mask() == flow_removed_master_mask);
      BOOST_TEST(sut.flow_removed_slave_mask() == flow_removed_slave_mask);
      BOOST_TEST(
             sut.flow_removed_mask()
          == (mask_type{{ flow_removed_master_mask, flow_removed_slave_mask }})
          , boost::test_tools::per_element{});
    }
    BOOST_AUTO_TEST_CASE(constructible_from_each_mask_without_xid)
    {
      auto const packet_in_master_mask = 0x10;
      auto const packet_in_slave_mask = 0x20;
      auto const port_status_master_mask = 0x30;
      auto const port_status_slave_mask = 0x40;
      auto const flow_removed_master_mask = 0x50;
      auto const flow_removed_slave_mask = 0x60;

      msg::get_async_reply const sut{
          packet_in_master_mask, packet_in_slave_mask
        , port_status_master_mask, port_status_slave_mask
        , flow_removed_master_mask ,flow_removed_slave_mask
      };

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_async_config));
      BOOST_TEST(sut.packet_in_master_mask() == packet_in_master_mask);
      BOOST_TEST(sut.packet_in_slave_mask() == packet_in_slave_mask);
      BOOST_TEST(
             sut.packet_in_mask()
          == (mask_type{{ packet_in_master_mask, packet_in_slave_mask }})
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.port_status_master_mask() == port_status_master_mask);
      BOOST_TEST(sut.port_status_slave_mask() == port_status_slave_mask);
      BOOST_TEST(
             sut.port_status_mask()
          == (mask_type{{ port_status_master_mask, port_status_slave_mask }})
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.flow_removed_master_mask() == flow_removed_master_mask);
      BOOST_TEST(sut.flow_removed_slave_mask() == flow_removed_slave_mask);
      BOOST_TEST(
             sut.flow_removed_mask()
          == (mask_type{{ flow_removed_master_mask, flow_removed_slave_mask }})
          , boost::test_tools::per_element{});
    }
    BOOST_AUTO_TEST_CASE(constructible_from_mask_and_xid)
    {
      auto const packet_in_mask = mask_type{{0x12, 0x34}};
      auto const port_status_mask = mask_type{{0x56, 0x78}};
      auto const flow_removed_mask = mask_type{{0x11, 0x22}};
      auto const xid = std::uint32_t{0xa1b2c3d4};

      msg::get_async_reply const sut{
        packet_in_mask, port_status_mask, flow_removed_mask, xid
      };

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_async_config));
      BOOST_TEST(sut.xid() == xid);
      BOOST_TEST(sut.packet_in_master_mask() == packet_in_mask[0]);
      BOOST_TEST(sut.packet_in_slave_mask() == packet_in_mask[1]);
      BOOST_TEST(
            sut.packet_in_mask() == packet_in_mask
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.port_status_master_mask() == port_status_mask[0]);
      BOOST_TEST(sut.port_status_slave_mask() == port_status_mask[1]);
      BOOST_TEST(
            sut.port_status_mask() == port_status_mask
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.flow_removed_master_mask() == flow_removed_mask[0]);
      BOOST_TEST(sut.flow_removed_slave_mask() == flow_removed_mask[1]);
      BOOST_TEST(
            sut.flow_removed_mask() == flow_removed_mask
          , boost::test_tools::per_element{});
    }
    BOOST_AUTO_TEST_CASE(constructible_from_mask_without_xid)
    {
      auto const packet_in_mask = mask_type{{0x21, 0x43}};
      auto const port_status_mask = mask_type{{0x65, 0x87}};
      auto const flow_removed_mask = mask_type{{0x33, 0x44}};

      msg::get_async_reply const sut{
        packet_in_mask, port_status_mask, flow_removed_mask
      };

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_async_config));
      BOOST_TEST(sut.packet_in_master_mask() == packet_in_mask[0]);
      BOOST_TEST(sut.packet_in_slave_mask() == packet_in_mask[1]);
      BOOST_TEST(
            sut.packet_in_mask() == packet_in_mask
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.port_status_master_mask() == port_status_mask[0]);
      BOOST_TEST(sut.port_status_slave_mask() == port_status_mask[1]);
      BOOST_TEST(
            sut.port_status_mask() == port_status_mask
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.flow_removed_master_mask() == flow_removed_mask[0]);
      BOOST_TEST(sut.flow_removed_slave_mask() == flow_removed_mask[1]);
      BOOST_TEST(
            sut.flow_removed_mask() == flow_removed_mask
          , boost::test_tools::per_element{});
    }
    BOOST_FIXTURE_TEST_CASE(copy_constructible, get_async_reply_fixture)
    {
      auto const& const_get_async_reply = sut;

      auto const copy = const_get_async_reply;

      BOOST_TEST((copy == sut));
    }
    BOOST_FIXTURE_TEST_CASE(move_constructible, get_async_reply_fixture)
    {
      auto moved = sut;

      auto const copy = std::move(moved);

      BOOST_TEST((copy == sut));
    }
  BOOST_AUTO_TEST_SUITE_END() // constructor

  BOOST_FIXTURE_TEST_SUITE(equality, get_async_reply_fixture)
    BOOST_AUTO_TEST_CASE(true_if_same_object)
    {
      auto const sut = msg::get_async_reply{
        packet_in_mask, port_status_mask, flow_removed_mask, xid
      };

      BOOST_TEST((sut == sut));
    }
    BOOST_AUTO_TEST_CASE(true_if_all_masks_and_xid_are_equal)
    {
      BOOST_TEST(
          (msg::get_async_reply{
              packet_in_mask, port_status_mask, flow_removed_mask, xid}
        == msg::get_async_reply{
              packet_in_mask, port_status_mask, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_packet_in_master_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::get_async_reply{
              {{1, 2}}, port_status_mask, flow_removed_mask, xid}
        != msg::get_async_reply{
              {{2, 2}}, port_status_mask, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_packet_in_slave_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::get_async_reply{
              {{1, 1}}, port_status_mask, flow_removed_mask, xid}
        != msg::get_async_reply{
              {{1, 2}}, port_status_mask, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_port_status_master_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::get_async_reply{
              packet_in_mask, {{1, 2}}, flow_removed_mask, xid}
        != msg::get_async_reply{
              packet_in_mask, {{2, 2}}, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_port_status_slave_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::get_async_reply{
              packet_in_mask, {{1, 1}}, flow_removed_mask, xid}
        != msg::get_async_reply{
              packet_in_mask, {{1, 2}}, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_flow_removed_master_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::get_async_reply{
              packet_in_mask, port_status_mask, {{1, 2}}, xid}
        != msg::get_async_reply{
              packet_in_mask, port_status_mask, {{2, 2}}, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_flow_removed_slave_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::get_async_reply{
              packet_in_mask, port_status_mask, {{1, 1}}, xid}
        != msg::get_async_reply{
              packet_in_mask, port_status_mask, {{1, 2}}, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_xid_is_not_equal)
    {
      BOOST_TEST(
          (msg::get_async_reply{
              packet_in_mask, port_status_mask, flow_removed_mask, 1}
        != msg::get_async_reply{
              packet_in_mask, port_status_mask, flow_removed_mask, 2}));
    }
  BOOST_AUTO_TEST_SUITE_END() // equality

  BOOST_AUTO_TEST_SUITE(encode)
    BOOST_FIXTURE_TEST_CASE(generate_binary, get_async_reply_fixture)
    {
      auto buffer = std::vector<unsigned char>{};

      sut.encode(buffer);

      BOOST_TEST(buffer.size() == sut.byte_length());
      BOOST_TEST(buffer == bin, boost::test_tools::per_element{});
    }
  BOOST_AUTO_TEST_SUITE_END() // encode

  BOOST_AUTO_TEST_SUITE(decode)
    BOOST_FIXTURE_TEST_CASE(construct_from_binary, get_async_reply_fixture)
    {
      auto it = bin.begin();

      auto const get_async_reply = msg::get_async_reply::decode(it, bin.end());

      BOOST_TEST((it == bin.end()));
      BOOST_TEST((get_async_reply == sut));
    }
  BOOST_AUTO_TEST_SUITE_END() // decode

BOOST_AUTO_TEST_SUITE_END() // get_async_reply

BOOST_AUTO_TEST_SUITE(set_async)

  BOOST_AUTO_TEST_SUITE(type_definition)
    BOOST_AUTO_TEST_CASE(type_is_set_async)
    {
      using sut = msg::set_async;

      constexpr auto type = sut::type();

      BOOST_TEST(type == protocol::OFPT_SET_ASYNC);
    }
    BOOST_AUTO_TEST_CASE(min_length_is_size_of_ofp_async_config)
    {
      using sut = msg::set_async;

      constexpr auto min_length = sut::min_length();

      BOOST_TEST(min_length == sizeof(protocol::ofp_async_config));
    }
  BOOST_AUTO_TEST_SUITE_END() // type_definition

  BOOST_AUTO_TEST_SUITE(constructor)
    BOOST_AUTO_TEST_CASE(constructible_from_each_mask_and_xid)
    {
      auto const packet_in_master_mask = 0x01;
      auto const packet_in_slave_mask = 0x02;
      auto const port_status_master_mask = 0x03;
      auto const port_status_slave_mask = 0x04;
      auto const flow_removed_master_mask = 0x05;
      auto const flow_removed_slave_mask = 0x06;
      auto const xid = std::uint32_t{0x12345678};

      msg::set_async const sut{
          packet_in_master_mask, packet_in_slave_mask
        , port_status_master_mask, port_status_slave_mask
        , flow_removed_master_mask ,flow_removed_slave_mask
        , xid
      };

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_async_config));
      BOOST_TEST(sut.xid() == xid);
      BOOST_TEST(sut.packet_in_master_mask() == packet_in_master_mask);
      BOOST_TEST(sut.packet_in_slave_mask() == packet_in_slave_mask);
      BOOST_TEST(
             sut.packet_in_mask()
          == (mask_type{{ packet_in_master_mask, packet_in_slave_mask }})
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.port_status_master_mask() == port_status_master_mask);
      BOOST_TEST(sut.port_status_slave_mask() == port_status_slave_mask);
      BOOST_TEST(
             sut.port_status_mask()
          == (mask_type{{ port_status_master_mask, port_status_slave_mask }})
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.flow_removed_master_mask() == flow_removed_master_mask);
      BOOST_TEST(sut.flow_removed_slave_mask() == flow_removed_slave_mask);
      BOOST_TEST(
             sut.flow_removed_mask()
          == (mask_type{{ flow_removed_master_mask, flow_removed_slave_mask }})
          , boost::test_tools::per_element{});
    }
    BOOST_AUTO_TEST_CASE(constructible_from_each_mask_without_xid)
    {
      auto const packet_in_master_mask = 0x10;
      auto const packet_in_slave_mask = 0x20;
      auto const port_status_master_mask = 0x30;
      auto const port_status_slave_mask = 0x40;
      auto const flow_removed_master_mask = 0x50;
      auto const flow_removed_slave_mask = 0x60;

      msg::set_async const sut{
          packet_in_master_mask, packet_in_slave_mask
        , port_status_master_mask, port_status_slave_mask
        , flow_removed_master_mask ,flow_removed_slave_mask
      };

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_async_config));
      BOOST_TEST(sut.packet_in_master_mask() == packet_in_master_mask);
      BOOST_TEST(sut.packet_in_slave_mask() == packet_in_slave_mask);
      BOOST_TEST(
             sut.packet_in_mask()
          == (mask_type{{ packet_in_master_mask, packet_in_slave_mask }})
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.port_status_master_mask() == port_status_master_mask);
      BOOST_TEST(sut.port_status_slave_mask() == port_status_slave_mask);
      BOOST_TEST(
             sut.port_status_mask()
          == (mask_type{{ port_status_master_mask, port_status_slave_mask }})
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.flow_removed_master_mask() == flow_removed_master_mask);
      BOOST_TEST(sut.flow_removed_slave_mask() == flow_removed_slave_mask);
      BOOST_TEST(
             sut.flow_removed_mask()
          == (mask_type{{ flow_removed_master_mask, flow_removed_slave_mask }})
          , boost::test_tools::per_element{});
    }
    BOOST_AUTO_TEST_CASE(constructible_from_mask_and_xid)
    {
      auto const packet_in_mask = mask_type{{0x12, 0x34}};
      auto const port_status_mask = mask_type{{0x56, 0x78}};
      auto const flow_removed_mask = mask_type{{0x11, 0x22}};
      auto const xid = std::uint32_t{0xa1b2c3d4};

      msg::set_async const sut{
        packet_in_mask, port_status_mask, flow_removed_mask, xid
      };

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_async_config));
      BOOST_TEST(sut.xid() == xid);
      BOOST_TEST(sut.packet_in_master_mask() == packet_in_mask[0]);
      BOOST_TEST(sut.packet_in_slave_mask() == packet_in_mask[1]);
      BOOST_TEST(
            sut.packet_in_mask() == packet_in_mask
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.port_status_master_mask() == port_status_mask[0]);
      BOOST_TEST(sut.port_status_slave_mask() == port_status_mask[1]);
      BOOST_TEST(
            sut.port_status_mask() == port_status_mask
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.flow_removed_master_mask() == flow_removed_mask[0]);
      BOOST_TEST(sut.flow_removed_slave_mask() == flow_removed_mask[1]);
      BOOST_TEST(
            sut.flow_removed_mask() == flow_removed_mask
          , boost::test_tools::per_element{});
    }
    BOOST_AUTO_TEST_CASE(constructible_from_mask_without_xid)
    {
      auto const packet_in_mask = mask_type{{0x21, 0x43}};
      auto const port_status_mask = mask_type{{0x65, 0x87}};
      auto const flow_removed_mask = mask_type{{0x33, 0x44}};

      msg::set_async const sut{
        packet_in_mask, port_status_mask, flow_removed_mask
      };

      BOOST_TEST(sut.length() == sizeof(protocol::ofp_async_config));
      BOOST_TEST(sut.packet_in_master_mask() == packet_in_mask[0]);
      BOOST_TEST(sut.packet_in_slave_mask() == packet_in_mask[1]);
      BOOST_TEST(
            sut.packet_in_mask() == packet_in_mask
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.port_status_master_mask() == port_status_mask[0]);
      BOOST_TEST(sut.port_status_slave_mask() == port_status_mask[1]);
      BOOST_TEST(
            sut.port_status_mask() == port_status_mask
          , boost::test_tools::per_element{});
      BOOST_TEST(sut.flow_removed_master_mask() == flow_removed_mask[0]);
      BOOST_TEST(sut.flow_removed_slave_mask() == flow_removed_mask[1]);
      BOOST_TEST(
            sut.flow_removed_mask() == flow_removed_mask
          , boost::test_tools::per_element{});
    }
    BOOST_FIXTURE_TEST_CASE(copy_constructible, set_async_fixture)
    {
      auto const& const_set_async = sut;

      auto const copy = const_set_async;

      BOOST_TEST((copy == sut));
    }
    BOOST_FIXTURE_TEST_CASE(move_constructible, set_async_fixture)
    {
      auto moved = sut;

      auto const copy = std::move(moved);

      BOOST_TEST((copy == sut));
    }
  BOOST_AUTO_TEST_SUITE_END() // constructor

  BOOST_FIXTURE_TEST_SUITE(equality, set_async_fixture)
    BOOST_AUTO_TEST_CASE(true_if_same_object)
    {
      auto const sut = msg::set_async{
        packet_in_mask, port_status_mask, flow_removed_mask, xid
      };

      BOOST_TEST((sut == sut));
    }
    BOOST_AUTO_TEST_CASE(true_if_all_masks_and_xid_are_equal)
    {
      BOOST_TEST(
          (msg::set_async{
              packet_in_mask, port_status_mask, flow_removed_mask, xid}
        == msg::set_async{
              packet_in_mask, port_status_mask, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_packet_in_master_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::set_async{
              {{1, 2}}, port_status_mask, flow_removed_mask, xid}
        != msg::set_async{
              {{2, 2}}, port_status_mask, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_packet_in_slave_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::set_async{
              {{1, 1}}, port_status_mask, flow_removed_mask, xid}
        != msg::set_async{
              {{1, 2}}, port_status_mask, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_port_status_master_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::set_async{
              packet_in_mask, {{1, 2}}, flow_removed_mask, xid}
        != msg::set_async{
              packet_in_mask, {{2, 2}}, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_port_status_slave_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::set_async{
              packet_in_mask, {{1, 1}}, flow_removed_mask, xid}
        != msg::set_async{
              packet_in_mask, {{1, 2}}, flow_removed_mask, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_flow_removed_master_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::set_async{
              packet_in_mask, port_status_mask, {{1, 2}}, xid}
        != msg::set_async{
              packet_in_mask, port_status_mask, {{2, 2}}, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_flow_removed_slave_mask_is_not_equal)
    {
      BOOST_TEST(
          (msg::set_async{
              packet_in_mask, port_status_mask, {{1, 1}}, xid}
        != msg::set_async{
              packet_in_mask, port_status_mask, {{1, 2}}, xid}));
    }
    BOOST_AUTO_TEST_CASE(false_if_xid_is_not_equal)
    {
      BOOST_TEST(
          (msg::set_async{
              packet_in_mask, port_status_mask, flow_removed_mask, 1}
        != msg::set_async{
              packet_in_mask, port_status_mask, flow_removed_mask, 2}));
    }
  BOOST_AUTO_TEST_SUITE_END() // equality

  BOOST_AUTO_TEST_SUITE(encode)
    BOOST_FIXTURE_TEST_CASE(generate_binary, set_async_fixture)
    {
      auto buffer = std::vector<unsigned char>{};

      sut.encode(buffer);

      BOOST_TEST(buffer.size() == sut.byte_length());
      BOOST_TEST(buffer == bin, boost::test_tools::per_element{});
    }
  BOOST_AUTO_TEST_SUITE_END() // encode

  BOOST_AUTO_TEST_SUITE(decode)
    BOOST_FIXTURE_TEST_CASE(construct_from_binary, set_async_fixture)
    {
      auto it = bin.begin();

      auto const set_async = msg::set_async::decode(it, bin.end());

      BOOST_TEST((it == bin.end()));
      BOOST_TEST((set_async == sut));
    }
  BOOST_AUTO_TEST_SUITE_END() // decode

BOOST_AUTO_TEST_SUITE_END() // set_async

BOOST_AUTO_TEST_SUITE_END() // message_test


text:
/**
 * @file qot_peer_service.cpp
 * @brief Peer Clock Delay/Offset Calculation Service main file
 * @author Anon D'Anon
 *
 * Copyright (c) Anon, 2018. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *      1. Redistributions of source code must retain the above copyright notice,
 *     this list of conditions and the following disclaimer.
 *  2. 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.
 *
 * 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 HOLDER 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.
 *
 *
 */

// Boost includes
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/program_options.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>

// C++ Standard Library Headers
#include <iostream>
#include <map>
#include <string>

// Peer timestamping headers
#include "sync/huygens/PeerTSserver.hpp"
#include "sync/huygens/PeerTSclient.hpp"
#include "sync/huygens/PeerTSreceiver.hpp"

using namespace qot;

// Maximum Clients
#define MAX_CLIENTS 1

// Select Timeout (seconds)
#define TIMEOUT 5

// Default NATS Server
#define NATS_SERVER "nats://localhost:4222"

// Running Flag
int peer_service_running = 1;

// Exit Handler to terminate the program on Ctrl+C
static void exit_handler(int s)
{
    std::cout << "Exit requested " << std::endl;
    peer_service_running = 0;
}

// Catch Signal Handler function
static void sigpipe_handler(int signum){

    printf("Caught signal SIGPIPE %d\n",signum);
}

// Generate a random string
std::string RandomString(uint32_t length)
{
	auto randchar = []() -> char
	{
		const char charset[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		const size_t max_index = (sizeof(charset) - 1);
		return charset[ rand() % max_index ];
	};
	std::string str(length,0);
	std::generate_n( str.begin(), length, randchar );
	return str;
}

/* Timeline Service Main Function */
int main(int argc , char *argv[])
{
    // Seed the random number generated with a nanosecond count
	struct timespec t={0,0};
	clock_gettime(CLOCK_REALTIME, &t);
	srand(t.tv_nsec);
	int mode_flag;
	int timestamping_flag = 2; /* Timestamping flag 2 - HWTS, 0 - SWTS */

	// Parse command line options
	boost::program_options::options_description desc("Allowed options");
	desc.add_options()
		("help,h",       "produce help message")
		("verbose,v",    "print verbose debug messages")
		("iface,i",      boost::program_options::value<std::string>()->default_value("eth0"), "PTP-compliant interface")
		("name,n",       boost::program_options::value<std::string>()->default_value(RandomString(32)), "name of this node")
        ("peerport,p",  boost::program_options::value<int>()->default_value(0), "port on which the peer to peer rtt measurement server listens")
		("timelineid,d", boost::program_options::value<int>()->default_value(0), "timeline id")
        ("addr,a",  boost::program_options::value<std::string>()->default_value("0"), "peer IP address")
        ("tx_period_ns,t",  boost::program_options::value<uint64_t>()->default_value(1000000000ULL), "peer IP")
        ("natsserver,m",  boost::program_options::value<std::string>()->default_value(NATS_SERVER), "NATS server(s) which to connect to for Peer Sync")
        ("discipline,d",  boost::program_options::value<bool>()->default_value(false), "Flag indicating if the PHC corresponding to the interface should be disciplined")
        ("mode,o",  boost::program_options::value<int>()->default_value(0), "Flag indicating which mode to launch in: 0-normal, 1-client only, 2-server only")
		("timestamping,x",  boost::program_options::value<int>()->default_value(2), "Flag indicating which timestamps to use: 0-SWTS, 2-HWTS")
	;
	boost::program_options::variables_map vm;
	boost::program_options::store(
		boost::program_options::parse_command_line(argc, argv, desc), vm);
	boost::program_options::notify(vm);

	// Set logging level
	if (vm.count("verbose") > 0)
	{
		boost::log::core::get()->set_filter
	    (
	        boost::log::trivial::severity >= boost::log::trivial::info
	    );
	}
	else
	{
		boost::log::core::get()->set_filter
	    (
	        boost::log::trivial::severity >= boost::log::trivial::warning
	    );
	}

	// Print some help with arguments
	if (vm.count("help") > 0)
	{
		std::cout << desc << "\n";
		return 0;
	}

	// Get operating mode
	mode_flag = 0;
	if (vm["mode"].as<int>() >= 0 & vm["mode"].as<int>() < 3)
		mode_flag = vm["mode"].as<int>();

	// Get timestamping mode
	if (vm["timestamping"].as<int>() >= 0 & vm["timestamping"].as<int>() < 3)
		timestamping_flag = vm["timestamping"].as<int>();

	// Some friendly debug
	BOOST_LOG_TRIVIAL(info) << "Node unique name is " << vm["name"].as<std::string>();
	BOOST_LOG_TRIVIAL(info) << "Performing synchronization over interface " << vm["iface"].as<std::string>();
	BOOST_LOG_TRIVIAL(info)	<< "Peer IP address is " << vm["addr"].as<std::string>();

	// Exclusion Set and Multicast Map for Peer Service
	std::set<std::string> exclusion_set;
	std::map<std::string, std::string> multicast_map;

	// Clock Parameters
	tl_translation_t clk_params;
	clk_params.u_nsec = 0;
	clk_params.l_nsec = 0;
	clk_params.u_mult = 0;
	clk_params.l_mult = 0;

	// Initialize Multicast Map
	// multicast_map[std::string("192.168.1.110")] = std::string("224.0.1.129");
	// multicast_map[std::string("192.168.1.111")] = std::string("224.0.1.130");
	// multicast_map[std::string("192.168.1.112")] = std::string("224.0.1.131");
	// multicast_map[std::string("192.168.1.113")] = std::string("224.0.1.132");
	// exclusion_set.insert(std::string("192.168.1.111"));

    // Spawn threads for the peer-delay client and server
    PeerTSclient peerclient(vm["addr"].as<std::string>(), vm["peerport"].as<int>(), vm["iface"].as<std::string>(), vm["natsserver"].as<std::string>(), timestamping_flag);
    PeerTSserver peerserver(vm["peerport"].as<int>(), vm["iface"].as<std::string>(), 0, timestamping_flag, exclusion_set, multicast_map);

    // Setup the receiver to get the offset
    PeerTSreceiver peerreceiver(vm["name"].as<std::string>(), vm["natsserver"].as<std::string>(), vm["iface"].as<std::string>(), vm["discipline"].as<bool>());

    if (mode_flag != 1)
    	peerserver.Start(vm["name"].as<std::string>());

    if (mode_flag != 2)
    {
    	peerreceiver.SetClkParamVar(&clk_params);
    	peerclient.Start(vm["name"].as<std::string>(), vm["tx_period_ns"].as<uint64_t>());
    	peerreceiver.Start(2000000000);
    }

    // Install SIGINT Signal Handler for exit
    signal(SIGINT, exit_handler);

    // Catch Signal Handler SIGPIPE
    signal(SIGPIPE, sigpipe_handler);

    // Main Loop
    while(peer_service_running)
    {
        sleep(1);
        // Check error status of peer server
        if (peerserver.GetErrorStatus() && mode_flag != 1)
        {
        	peerserver.Stop();
        	peerserver.Start(vm["name"].as<std::string>());
        }

        // Check error status of peer server
        if (peerclient.GetErrorStatus() && mode_flag != 2)
        {
        	peerclient.Stop();
        	peerclient.Start(vm["name"].as<std::string>(), vm["tx_period_ns"].as<uint64_t>());
        }
    }

    // Stop the server and client threads
    if (mode_flag != 2)
    {
    	peerclient.Stop();
    	// Stop the peer receiver
    	peerreceiver.Stop();
    }
    if (mode_flag != 1)
    	peerserver.Stop();

    return 0;
}

ext: cpp

max_stars_repo_name: quartz-roseline/quartz

max_issues_repo_name: quartz-roseline/quartz

max_forks_repo_name: quartz-roseline/quartz


text:
#include "hir.hpp"
#include "hir_inlines.hpp"
#include "lir.hpp"

#include <iostream>
#include <map>

#include <boost/range/adaptor/reversed.hpp>

namespace algrad {
namespace compiler {

std::vector<lir::RegClass>
computeRegisterClasses(hir::Program& program)
{
    std::vector<lir::RegClass> regClasses(program.defIdCount(), lir::RegClass::sgpr);
    regClasses[program.params()[0]->id()] = lir::RegClass::sgpr;
    regClasses[program.params()[1]->id()] = lir::RegClass::vgpr;
    regClasses[program.params()[2]->id()] = lir::RegClass::vgpr;
    for (auto& bb : program.basicBlocks()) {
        for (auto& insn : bb->instructions()) {
            if (insn.type() == &voidType)
                continue;

            if (insn.isVarying())
                regClasses[insn.id()] =
                  insn.type()->kind() == TypeKind::boolean ? lir::RegClass::sgpr : lir::RegClass::vgpr;
            else
                regClasses[insn.id()] =
                  insn.type()->kind() == TypeKind::boolean ? lir::RegClass::scc : lir::RegClass::sgpr;

            switch (insn.opCode()) {
                default: {
                    auto operandCount = insn.operandCount();
                    for (std::size_t i = 0; i < operandCount; ++i) {
                        if (regClasses[insn.getOperand(i)->id()] == lir::RegClass::vgpr)
                            regClasses[insn.id()] =
                              insn.type()->kind() == TypeKind::boolean ? lir::RegClass::sgpr : lir::RegClass::vgpr;
                    }
                }
            }
        }
    }
    return regClasses;
}

struct SelectionContext
{
    std::vector<lir::RegClass> regClasses;
    std::vector<lir::Temp_id> regMap;
    lir::Program* lprog;

    std::map<std::pair<lir::Block*, lir::Block*>, unsigned> controlFlowVars;
};

lir::Temp_id
getReg(SelectionContext& ctx, hir::Def& def)
{
    lir::RegClass rc = ctx.regClasses[def.id()];
    unsigned size = 4;
    if (ctx.regMap[def.id()] == ~0U) {
        ctx.regMap[def.id()] = ctx.lprog->allocate_temp(rc, size);
    }

    return ctx.regMap[def.id()];
}

lir::Temp_id
getReg(SelectionContext& ctx, hir::Def& def, lir::RegClass rc, unsigned size)
{
    return getReg(ctx, def);
}

lir::Temp_id
getSingleSGPR(SelectionContext& ctx, hir::Def& def)
{
    return getReg(ctx, def, lir::RegClass::sgpr, 4);
}

lir::Temp_id
getSingleVGPR(SelectionContext& ctx, hir::Def& def)
{
    return getReg(ctx, def, lir::RegClass::vgpr, 4);
}

void
createStartInstruction(SelectionContext& ctx, lir::Program& lprog, hir::Program& program)
{
    auto newInst = std::make_unique<lir::Inst>(lir::OpCode::start, 3, 0);
    newInst->getDefinition(0) = lir::Arg{getReg(ctx, *program.params()[0], lir::RegClass::sgpr, 4), lir::PhysReg{16 * 4}};
    newInst->getDefinition(1) = lir::Arg{getSingleVGPR(ctx, *program.params()[1]), lir::PhysReg{(0 + 256) * 4}};
    newInst->getDefinition(2) = lir::Arg{getSingleVGPR(ctx, *program.params()[2]), lir::PhysReg{(1 + 256) * 4}};

    lprog.blocks().front()->instructions().push_back(std::move(newInst));
}

void
createVectorCompare(SelectionContext& ctx, lir::OpCode opCode, hir::Inst& inst, lir::Block& lbb)
{
    auto newInst = std::make_unique<lir::Inst>(opCode, 1, 2);
    newInst->getOperand(0) = lir::Arg{getReg(ctx, *inst.getOperand(0))};
    newInst->getOperand(1) = lir::Arg{getReg(ctx, *inst.getOperand(1))};
    newInst->getDefinition(0) = lir::Arg{getReg(ctx, inst), lir::PhysReg{106 * 4}};

    lbb.instructions().push_back(std::move(newInst));
}

void
createLogicalCondBranch(SelectionContext& ctx, hir::Inst& inst, lir::Block& lbb)
{
    auto newInst = std::make_unique<lir::Inst>(lir::OpCode::logical_cond_branch, 2, 1);
    newInst->getOperand(0) = lir::Arg{getReg(ctx, *inst.getOperand(0))};

    newInst->getDefinition(0) =
      lir::Arg{lir::Temp_id{ctx.controlFlowVars.find({&lbb, lbb.logicalSuccessors()[0]})->second}};
    newInst->getDefinition(1) =
      lir::Arg{lir::Temp_id{ctx.controlFlowVars.find({&lbb, lbb.logicalSuccessors()[1]})->second}};
    lbb.instructions().push_back(std::move(newInst));
}

void
createLogicalBranch(SelectionContext& ctx, hir::Inst& inst, lir::Block& lbb)
{
    auto newInst = std::make_unique<lir::Inst>(lir::OpCode::logical_branch, 1, 0);

    newInst->getDefinition(0) =
      lir::Arg{lir::Temp_id{ctx.controlFlowVars.find({&lbb, lbb.logicalSuccessors()[0]})->second}};
    lbb.instructions().push_back(std::move(newInst));
}

void
createVectorPhi(SelectionContext& ctx, hir::Inst& inst, lir::Block& lbb)
{
    auto newInst = std::make_unique<lir::Inst>(lir::OpCode::phi, 1, lbb.logicalPredecessors().size());
    for (unsigned i = 0; i < lbb.logicalPredecessors().size(); ++i) {
        newInst->getOperand(i) = lir::Arg{getReg(ctx, *inst.getOperand(i))};
    }
    newInst->getDefinition(0) = lir::Arg{getReg(ctx, inst)};

    lbb.instructions().push_back(std::move(newInst));
}

void
createBlockStart(SelectionContext& ctx, lir::Block& lbb, hir::Program& program)
{
    if (lbb.linearizedPredecessors().empty()) {
        createStartInstruction(ctx, *ctx.lprog, program);
        return;
    }

    auto newInst = std::make_unique<lir::Inst>(lir::OpCode::start_block, 0, lbb.logicalPredecessors().size());
    for (unsigned i = 0; i < lbb.logicalPredecessors().size(); ++i) {
        newInst->getOperand(i) = lir::Arg{
          lir::Temp_id{ctx.controlFlowVars.find({lbb.logicalPredecessors()[i], &lbb})->second}};
    }

    lbb.instructions().push_back(std::move(newInst));
}
std::unique_ptr<lir::Program>
selectInstructions(hir::Program& program)
{
    SelectionContext ctx;
    ctx.regClasses = computeRegisterClasses(program);
    ctx.regMap.resize(program.defIdCount(), ~0U);

    auto lprog = std::make_unique<lir::Program>();
    ctx.lprog = lprog.get();

    for (auto& bb : program.basicBlocks()) {
        ctx.lprog->blocks().push_back(std::make_unique<lir::Block>(bb->id()));
    }
    for (int i = program.basicBlocks().size() - 1; i >= 0; --i) {
        auto& bb = *program.basicBlocks()[i];
        auto& lbb = *ctx.lprog->blocks()[i];

        if (i + 1 < program.basicBlocks().size()) {
            lir::findOrInsertBlock(lbb.linearizedSuccessors(), ctx.lprog->blocks()[i + 1].get());
            lir::findOrInsertBlock(ctx.lprog->blocks()[i + 1]->linearizedPredecessors(), &lbb);
        }

        for (auto pred : bb.predecessors())
            lbb.logicalPredecessors().push_back(ctx.lprog->blocks()[pred->id()].get());

        for (auto succ : bb.successors()) {
            lbb.logicalSuccessors().push_back(ctx.lprog->blocks()[succ->id()].get());
            ctx.controlFlowVars.insert({{&lbb, ctx.lprog->blocks()[succ->id()].get()}, ctx.lprog->allocate_temp(lir::RegClass::sgpr, 8)});
        }
    }

    for (int i = program.basicBlocks().size() - 1; i >= 0; --i) {
        auto& bb = *program.basicBlocks()[i];
        auto& lbb = *ctx.lprog->blocks()[i];

        bool emittedBlockStart = false;
        for (auto& insn : boost::adaptors::reverse(bb.instructions())) {
            switch (insn.opCode()) {
                case hir::OpCode::ret:
                    lbb.instructions().push_back(std::make_unique<lir::Inst>(lir::OpCode::s_endpgm, 0, 0));
                    break;
                case hir::OpCode::gcnInterpolate: {
                    auto attribute = static_cast<hir::ScalarConstant*>(insn.getOperand(3))->integerValue();
                    auto component = static_cast<hir::ScalarConstant*>(insn.getOperand(4))->integerValue();
                    auto p1 = std::make_unique<lir::Inst>(lir::OpCode::v_interp_p1_f32, 1, 2); //(attribute, component);
                    auto p2 = std::make_unique<lir::Inst>(lir::OpCode::v_interp_p2_f32, 1, 3); //(attribute, component);

                    lir::Temp_id tmp = lprog->allocate_temp(lir::RegClass::vgpr, 4);
                    p1->getDefinition(0) = lir::Arg{tmp};
                    p1->getOperand(0) = lir::Arg{getSingleVGPR(ctx, *insn.getOperand(1))};
                    p1->getOperand(1) = lir::Arg{getSingleSGPR(ctx, *insn.getOperand(0)), lir::PhysReg{124 * 4}};
                    p1->aux().vintrp.attribute = attribute;
                    p1->aux().vintrp.channel = component;

                    p2->getDefinition(0) = lir::Arg{getReg(ctx, insn, lir::RegClass::vgpr, 4)};
                    p2->getOperand(0) = lir::Arg{tmp};
                    p2->getOperand(1) = lir::Arg{getSingleVGPR(ctx, *insn.getOperand(2))};
                    p2->getOperand(2) = lir::Arg{getSingleSGPR(ctx, *insn.getOperand(0)), lir::PhysReg{124 * 4}};
                    p2->aux().vintrp.attribute = attribute;
                    p2->aux().vintrp.channel = component;

                    lbb.instructions().emplace_back(std::move(p2));
                    lbb.instructions().emplace_back(std::move(p1));
                } break;
                case hir::OpCode::gcnExport: {
                    auto exp = std::make_unique<lir::Inst>(lir::OpCode::exp, 0, 4);

                    exp->getOperand(0) = lir::Arg{getSingleVGPR(ctx, *insn.getOperand(3))};
                    exp->getOperand(1) = lir::Arg{getSingleVGPR(ctx, *insn.getOperand(4))};
                    exp->getOperand(2) = lir::Arg{getSingleVGPR(ctx, *insn.getOperand(5))};
                    exp->getOperand(3) = lir::Arg{getSingleVGPR(ctx, *insn.getOperand(6))};
                    exp->aux().exp.enable = static_cast<hir::ScalarConstant*>(insn.getOperand(0))->integerValue();
                    exp->aux().exp.target = static_cast<hir::ScalarConstant*>(insn.getOperand(1))->integerValue();
                    exp->aux().exp.compressed = static_cast<hir::ScalarConstant*>(insn.getOperand(2))->integerValue();
                    exp->aux().exp.done = true;
                    exp->aux().exp.validMask = true;

                    lbb.instructions().emplace_back(std::move(exp));
                } break;
                case hir::OpCode::orderedLessThan:
                    createVectorCompare(ctx, lir::OpCode::v_cmp_lt_f32, insn, lbb);
                    break;
                case hir::OpCode::phi: {
                    if (!emittedBlockStart) {
                        createBlockStart(ctx, lbb, program);
                        emittedBlockStart = true;
                    }
                    if (ctx.regClasses[insn.id()] == lir::RegClass::vgpr)
                        createVectorPhi(ctx, insn, lbb);
                    else
                        std::terminate();
                } break;
                case hir::OpCode::condBranch: {
                    createLogicalCondBranch(ctx, insn, lbb);
                } break;
                case hir::OpCode::branch:
                    createLogicalBranch(ctx, insn, lbb);
                    break;
                default:
                    std::terminate();
            }
        }
        if (!emittedBlockStart) {
            createBlockStart(ctx, lbb, program);
            emittedBlockStart = true;
        }
    }

    for (auto& b : ctx.lprog->blocks())
        std::reverse(b->instructions().begin(), b->instructions().end());
    return lprog;
}
}
}

max_stars_repo_name: BNieuwenhuizen/algrad

max_issues_repo_name: BNieuwenhuizen/algrad

max_forks_repo_name: BNieuwenhuizen/algrad


text:
/*
 * Copyright 2017-present Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/portability/PThread.h>

#if !FOLLY_HAVE_PTHREAD && _WIN32
#include <boost/thread/tss.hpp> // @manual

#include <errno.h>

#include <chrono>
#include <condition_variable>
#include <exception>
#include <limits>
#include <mutex>
#include <shared_mutex>
#include <thread>

#include <folly/lang/Assume.h>
#include <folly/portability/Windows.h>

namespace folly {
namespace portability {
namespace pthread {

int pthread_attr_init(pthread_attr_t* attr) {
  if (attr == nullptr) {
    errno = EINVAL;
    return -1;
  }
  attr->stackSize = 0;
  attr->detached = false;
  return 0;
}

int pthread_attr_setdetachstate(pthread_attr_t* attr, int state) {
  if (attr == nullptr) {
    errno = EINVAL;
    return -1;
  }
  attr->detached = state == PTHREAD_CREATE_DETACHED ? true : false;
  return 0;
}

int pthread_attr_setstacksize(pthread_attr_t* attr, size_t kb) {
  if (attr == nullptr) {
    errno = EINVAL;
    return -1;
  }
  attr->stackSize = kb;
  return 0;
}

namespace pthread_detail {
pthread_t::~pthread_t() noexcept {
  if (handle != INVALID_HANDLE_VALUE && !detached) {
    CloseHandle(handle);
  }
}
} // namespace pthread_detail

int pthread_equal(pthread_t threadA, pthread_t threadB) {
  if (threadA == threadB) {
    return 1;
  }

  // Note that, in the presence of detached threads, it is in theory possible
  // for two different pthread_t handles to be compared as the same due to
  // Windows HANDLE and Thread ID re-use. If you're doing anything useful with
  // a detached thread, you're probably doing it wrong, but I felt like leaving
  // this note here anyways.
  if (threadA->handle == threadB->handle &&
      threadA->threadID == threadB->threadID) {
    return 1;
  }
  return 0;
}

namespace {
thread_local pthread_t current_thread_self;
struct pthread_startup_info {
  pthread_t thread;
  void* (*startupFunction)(void*);
  void* startupArgument;
};

DWORD internal_pthread_thread_start(void* arg) {
  // We are now in the new thread.
  auto startupInfo = reinterpret_cast<pthread_startup_info*>(arg);
  current_thread_self = startupInfo->thread;
  auto ret = startupInfo->startupFunction(startupInfo->startupArgument);
  if /* constexpr */ (sizeof(void*) != sizeof(DWORD)) {
    auto tmp = reinterpret_cast<uintptr_t>(ret);
    if (tmp > std::numeric_limits<DWORD>::max()) {
      throw std::out_of_range(
          "Exit code of the pthread is outside the range representable on Windows");
    }
  }

  delete startupInfo;
  return static_cast<DWORD>(reinterpret_cast<uintptr_t>(ret));
}
} // namespace

int pthread_create(
    pthread_t* thread,
    const pthread_attr_t* attr,
    void* (*start_routine)(void*),
    void* arg) {
  if (thread == nullptr) {
    errno = EINVAL;
    return -1;
  }

  size_t stackSize = attr != nullptr ? attr->stackSize : 0;
  bool detach = attr != nullptr ? attr->detached : false;

  // Note that the start routine passed into pthread returns a void* and the
  // windows API expects DWORD's, so we need to stub around that.
  auto startupInfo = new pthread_startup_info();
  startupInfo->startupFunction = start_routine;
  startupInfo->startupArgument = arg;
  startupInfo->thread = std::make_shared<pthread_detail::pthread_t>();
  // We create the thread suspended so we can assign the handle and thread id
  // in the pthread_t.
  startupInfo->thread->handle = CreateThread(
      nullptr,
      stackSize,
      internal_pthread_thread_start,
      startupInfo,
      CREATE_SUSPENDED,
      &startupInfo->thread->threadID);
  ResumeThread(startupInfo->thread->handle);

  if (detach) {
    *thread = std::make_shared<pthread_detail::pthread_t>();
    (*thread)->detached = true;
    (*thread)->handle = startupInfo->thread->handle;
    (*thread)->threadID = startupInfo->thread->threadID;
  } else {
    *thread = startupInfo->thread;
  }
  return 0;
}

pthread_t pthread_self() {
  // Not possible to race :)
  if (current_thread_self == nullptr) {
    current_thread_self = std::make_shared<pthread_detail::pthread_t>();
    current_thread_self->threadID = GetCurrentThreadId();
    // The handle returned by GetCurrentThread is a pseudo-handle and needs to
    // be swapped out for a real handle to be useful anywhere other than this
    // thread.
    DuplicateHandle(
        GetCurrentProcess(),
        GetCurrentThread(),
        GetCurrentProcess(),
        &current_thread_self->handle,
        DUPLICATE_SAME_ACCESS,
        TRUE,
        0);
  }

  return current_thread_self;
}

int pthread_join(pthread_t thread, void** exitCode) {
  if (thread->detached) {
    errno = EINVAL;
    return -1;
  }

  if (WaitForSingleObjectEx(thread->handle, INFINITE, FALSE) == WAIT_FAILED) {
    return -1;
  }

  if (exitCode != nullptr) {
    DWORD e;
    if (!GetExitCodeThread(thread->handle, &e)) {
      return -1;
    }
    *exitCode = reinterpret_cast<void*>(static_cast<uintptr_t>(e));
  }

  return 0;
}

HANDLE pthread_getw32threadhandle_np(pthread_t thread) {
  return thread->handle;
}

DWORD pthread_getw32threadid_np(pthread_t thread) {
  return thread->threadID;
}

int pthread_setschedparam(
    pthread_t thread,
    int policy,
    const sched_param* param) {
  if (thread->detached) {
    errno = EINVAL;
    return -1;
  }

  auto newPrior = param->sched_priority;
  if (newPrior > THREAD_PRIORITY_TIME_CRITICAL ||
      newPrior < THREAD_PRIORITY_IDLE) {
    errno = EINVAL;
    return -1;
  }
  if (GetPriorityClass(GetCurrentProcess()) != REALTIME_PRIORITY_CLASS) {
    if (newPrior > THREAD_PRIORITY_IDLE && newPrior < THREAD_PRIORITY_LOWEST) {
      // The values between IDLE and LOWEST are invalid unless the process is
      // running as realtime.
      newPrior = THREAD_PRIORITY_LOWEST;
    } else if (
        newPrior < THREAD_PRIORITY_TIME_CRITICAL &&
        newPrior > THREAD_PRIORITY_HIGHEST) {
      // Same as above.
      newPrior = THREAD_PRIORITY_HIGHEST;
    }
  }
  if (!SetThreadPriority(thread->handle, newPrior)) {
    return -1;
  }
  return 0;
}

int pthread_mutexattr_init(pthread_mutexattr_t* attr) {
  if (attr == nullptr) {
    return EINVAL;
  }

  attr->type = PTHREAD_MUTEX_DEFAULT;
  return 0;
}

int pthread_mutexattr_destroy(pthread_mutexattr_t* attr) {
  if (attr == nullptr) {
    return EINVAL;
  }

  return 0;
}

int pthread_mutexattr_settype(pthread_mutexattr_t* attr, int type) {
  if (attr == nullptr) {
    return EINVAL;
  }

  if (type != PTHREAD_MUTEX_DEFAULT && type != PTHREAD_MUTEX_RECURSIVE) {
    return EINVAL;
  }

  attr->type = type;
  return 0;
}

struct pthread_mutex_t_ {
 private:
  int type;
  union {
    std::timed_mutex timed_mtx;
    std::recursive_timed_mutex recursive_timed_mtx;
  };

 public:
  pthread_mutex_t_(int mutex_type) : type(mutex_type) {
    switch (type) {
      case PTHREAD_MUTEX_NORMAL:
        new (&timed_mtx) std::timed_mutex();
        break;
      case PTHREAD_MUTEX_RECURSIVE:
        new (&recursive_timed_mtx) std::recursive_timed_mutex();
        break;
    }
  }

  ~pthread_mutex_t_() noexcept {
    switch (type) {
      case PTHREAD_MUTEX_NORMAL:
        timed_mtx.~timed_mutex();
        break;
      case PTHREAD_MUTEX_RECURSIVE:
        recursive_timed_mtx.~recursive_timed_mutex();
        break;
    }
  }

  void lock() {
    switch (type) {
      case PTHREAD_MUTEX_NORMAL:
        timed_mtx.lock();
        break;
      case PTHREAD_MUTEX_RECURSIVE:
        recursive_timed_mtx.lock();
        break;
    }
  }

  bool try_lock() {
    switch (type) {
      case PTHREAD_MUTEX_NORMAL:
        return timed_mtx.try_lock();
      case PTHREAD_MUTEX_RECURSIVE:
        return recursive_timed_mtx.try_lock();
    }
    folly::assume_unreachable();
  }

  bool timed_try_lock(std::chrono::system_clock::time_point until) {
    switch (type) {
      case PTHREAD_MUTEX_NORMAL:
        return timed_mtx.try_lock_until(until);
      case PTHREAD_MUTEX_RECURSIVE:
        return recursive_timed_mtx.try_lock_until(until);
    }
    folly::assume_unreachable();
  }

  void unlock() {
    switch (type) {
      case PTHREAD_MUTEX_NORMAL:
        timed_mtx.unlock();
        break;
      case PTHREAD_MUTEX_RECURSIVE:
        recursive_timed_mtx.unlock();
        break;
    }
  }

  void condition_wait(std::condition_variable_any& cond) {
    switch (type) {
      case PTHREAD_MUTEX_NORMAL: {
        std::unique_lock<std::timed_mutex> lock(timed_mtx);
        cond.wait(lock);
        break;
      }
      case PTHREAD_MUTEX_RECURSIVE: {
        std::unique_lock<std::recursive_timed_mutex> lock(recursive_timed_mtx);
        cond.wait(lock);
        break;
      }
    }
  }

  bool condition_timed_wait(
      std::condition_variable_any& cond,
      std::chrono::system_clock::time_point until) {
    switch (type) {
      case PTHREAD_MUTEX_NORMAL: {
        std::unique_lock<std::timed_mutex> lock(timed_mtx);
        return cond.wait_until(lock, until) == std::cv_status::no_timeout;
      }
      case PTHREAD_MUTEX_RECURSIVE: {
        std::unique_lock<std::recursive_timed_mutex> lock(recursive_timed_mtx);
        return cond.wait_until(lock, until) == std::cv_status::no_timeout;
      }
    }
    folly::assume_unreachable();
  }
};

int pthread_mutex_init(
    pthread_mutex_t* mutex,
    const pthread_mutexattr_t* attr) {
  if (mutex == nullptr) {
    return EINVAL;
  }

  auto type = attr != nullptr ? attr->type : PTHREAD_MUTEX_DEFAULT;
  auto ret = new pthread_mutex_t_(type);
  *mutex = ret;
  return 0;
}

int pthread_mutex_destroy(pthread_mutex_t* mutex) {
  if (mutex == nullptr) {
    return EINVAL;
  }

  delete *mutex;
  *mutex = nullptr;
  return 0;
}

int pthread_mutex_lock(pthread_mutex_t* mutex) {
  if (mutex == nullptr) {
    return EINVAL;
  }

  // This implementation does not implement deadlock detection, as the
  // STL mutexes we're wrapping don't either.
  (*mutex)->lock();
  return 0;
}

int pthread_mutex_trylock(pthread_mutex_t* mutex) {
  if (mutex == nullptr) {
    return EINVAL;
  }

  if ((*mutex)->try_lock()) {
    return 0;
  } else {
    return EBUSY;
  }
}

static std::chrono::system_clock::time_point timespec_to_time_point(
    const timespec* t) {
  using time_point = std::chrono::system_clock::time_point;
  auto ns =
      std::chrono::seconds(t->tv_sec) + std::chrono::nanoseconds(t->tv_nsec);
  return time_point(std::chrono::duration_cast<time_point::duration>(ns));
}

int pthread_mutex_timedlock(
    pthread_mutex_t* mutex,
    const timespec* abs_timeout) {
  if (mutex == nullptr || abs_timeout == nullptr) {
    return EINVAL;
  }

  auto time = timespec_to_time_point(abs_timeout);
  if ((*mutex)->timed_try_lock(time)) {
    return 0;
  } else {
    return ETIMEDOUT;
  }
}

int pthread_mutex_unlock(pthread_mutex_t* mutex) {
  if (mutex == nullptr) {
    return EINVAL;
  }

  // This implementation allows other threads to unlock it,
  // as the STL containers also do.
  (*mutex)->unlock();
  return 0;
}

struct pthread_rwlock_t_ {
  std::shared_timed_mutex mtx;
  std::atomic<bool> writing{false};
};

int pthread_rwlock_init(pthread_rwlock_t* rwlock, const void* attr) {
  if (attr != nullptr) {
    return EINVAL;
  }
  if (rwlock == nullptr) {
    return EINVAL;
  }

  *rwlock = new pthread_rwlock_t_();
  return 0;
}

int pthread_rwlock_destroy(pthread_rwlock_t* rwlock) {
  if (rwlock == nullptr) {
    return EINVAL;
  }

  delete *rwlock;
  *rwlock = nullptr;
  return 0;
}

int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock) {
  if (rwlock == nullptr) {
    return EINVAL;
  }

  (*rwlock)->mtx.lock_shared();
  return 0;
}

int pthread_rwlock_tryrdlock(pthread_rwlock_t* rwlock) {
  if (rwlock == nullptr) {
    return EINVAL;
  }

  if ((*rwlock)->mtx.try_lock_shared()) {
    return 0;
  } else {
    return EBUSY;
  }
}

int pthread_rwlock_timedrdlock(
    pthread_rwlock_t* rwlock,
    const timespec* abs_timeout) {
  if (rwlock == nullptr) {
    return EINVAL;
  }

  auto time = timespec_to_time_point(abs_timeout);
  if ((*rwlock)->mtx.try_lock_shared_until(time)) {
    return 0;
  } else {
    return ETIMEDOUT;
  }
}

int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock) {
  if (rwlock == nullptr) {
    return EINVAL;
  }

  (*rwlock)->mtx.lock();
  (*rwlock)->writing = true;
  return 0;
}

// Note: As far as I can tell, rwlock is technically supposed to
// be an upgradable lock, but we don't implement it that way.
int pthread_rwlock_trywrlock(pthread_rwlock_t* rwlock) {
  if (rwlock == nullptr) {
    return EINVAL;
  }

  if ((*rwlock)->mtx.try_lock()) {
    (*rwlock)->writing = true;
    return 0;
  } else {
    return EBUSY;
  }
}

int pthread_rwlock_timedwrlock(
    pthread_rwlock_t* rwlock,
    const timespec* abs_timeout) {
  if (rwlock == nullptr) {
    return EINVAL;
  }

  auto time = timespec_to_time_point(abs_timeout);
  if ((*rwlock)->mtx.try_lock_until(time)) {
    (*rwlock)->writing = true;
    return 0;
  } else {
    return ETIMEDOUT;
  }
}

int pthread_rwlock_unlock(pthread_rwlock_t* rwlock) {
  if (rwlock == nullptr) {
    return EINVAL;
  }

  // Note: We don't have any checking to ensure we have actually
  // locked things first, so you'll actually be in undefined behavior
  // territory if you do attempt to unlock things you haven't locked.
  if ((*rwlock)->writing) {
    (*rwlock)->mtx.unlock();
    // If we fail, then another thread has already immediately acquired
    // the write lock, so this should stay as true :)
    bool dump = true;
    (void)(*rwlock)->writing.compare_exchange_strong(dump, false);
  } else {
    (*rwlock)->mtx.unlock_shared();
  }
  return 0;
}

struct pthread_cond_t_ {
  // pthread_mutex_t is backed by timed
  // mutexes, so no basic condition variable for
  // us :(
  std::condition_variable_any cond;
};

int pthread_cond_init(pthread_cond_t* cond, const void* attr) {
  if (attr != nullptr) {
    return EINVAL;
  }
  if (cond == nullptr) {
    return EINVAL;
  }

  *cond = new pthread_cond_t_();
  return 0;
}

int pthread_cond_destroy(pthread_cond_t* cond) {
  if (cond == nullptr) {
    return EINVAL;
  }

  delete *cond;
  *cond = nullptr;
  return 0;
}

int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex) {
  if (cond == nullptr || mutex == nullptr) {
    return EINVAL;
  }

  (*mutex)->condition_wait((*cond)->cond);
  return 0;
}

int pthread_cond_timedwait(
    pthread_cond_t* cond,
    pthread_mutex_t* mutex,
    const timespec* abstime) {
  if (cond == nullptr || mutex == nullptr || abstime == nullptr) {
    return EINVAL;
  }

  auto time = timespec_to_time_point(abstime);
  if ((*mutex)->condition_timed_wait((*cond)->cond, time)) {
    return 0;
  } else {
    return ETIMEDOUT;
  }
}

int pthread_cond_signal(pthread_cond_t* cond) {
  if (cond == nullptr) {
    return EINVAL;
  }

  (*cond)->cond.notify_one();
  return 0;
}

int pthread_cond_broadcast(pthread_cond_t* cond) {
  if (cond == nullptr) {
    return EINVAL;
  }

  (*cond)->cond.notify_all();
  return 0;
}

int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) {
  try {
    auto newKey = new boost::thread_specific_ptr<void>(destructor);
    *key = newKey;
    return 0;
  } catch (boost::thread_resource_error) {
    return -1;
  }
}

int pthread_key_delete(pthread_key_t key) {
  try {
    auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key);
    delete realKey;
    return 0;
  } catch (boost::thread_resource_error) {
    return -1;
  }
}

void* pthread_getspecific(pthread_key_t key) {
  auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key);
  // This can't throw as-per the documentation.
  return realKey->get();
}

int pthread_setspecific(pthread_key_t key, const void* value) {
  try {
    auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key);
    // We can't just call reset here because that would invoke the cleanup
    // function, which we don't want to do.
    boost::detail::set_tss_data(
        realKey,
        boost::shared_ptr<boost::detail::tss_cleanup_function>(),
        const_cast<void*>(value),
        false);
    return 0;
  } catch (boost::thread_resource_error) {
    return -1;
  }
}
} // namespace pthread
} // namespace portability
} // namespace folly
#endif

max_stars_repo_name: hasridha/react-native

max_issues_repo_name: hasridha/react-native

max_forks_repo_name: hasridha/react-native


text:
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include "helpers.hpp"
#include <Eigen/Dense>
#include <celerite2/celerite2.h>

using namespace celerite2::test;
using namespace celerite2::core;

TEMPLATE_LIST_TEST_CASE("check the results of factor", "[factor]", TestKernels) {
  SETUP_TEST(50);

  Vector d;
  LowRank W;
  Matrix K, S;
  to_dense(x, c, a, U, V, K);

  // Brute force the Cholesky factorization
  Eigen::LDLT<Matrix> LDLT(K);
  Eigen::MatrixXd matrixL = LDLT.matrixL();

  SECTION("general") {
    // Do the Cholesky using celerite
    int flag = factor(x, c, a, U, V, d, W, S);
    REQUIRE(flag == 0);

    // Reconstruct the L matrix
    Matrix UWT;
    to_dense(x, c, Eigen::VectorXd::Ones(N), U, W, UWT);
    UWT.triangularView<Eigen::StrictlyUpper>().setConstant(0.0);

    // Check that the lower triangle is correct
    double resid = (matrixL - UWT).array().abs().maxCoeff();
    REQUIRE(resid < 1e-12);

    // Check that the diagonal is correct
    double diag_resid = (LDLT.vectorD() - d).array().abs().maxCoeff();
    REQUIRE(diag_resid < 1e-12);
  }

  SECTION("no grad") {
    // Do the Cholesky using celerite
    int flag = factor(x, c, a, U, V, d, W);
    REQUIRE(flag == 0);

    // Reconstruct the L matrix
    Matrix UWT;
    to_dense(x, c, Eigen::VectorXd::Ones(N), U, W, UWT);
    UWT.triangularView<Eigen::StrictlyUpper>().setConstant(0.0);

    // Check that the lower triangle is correct
    double resid = (matrixL - UWT).array().abs().maxCoeff();
    REQUIRE(resid < 1e-12);

    // Check that the diagonal is correct
    double diag_resid = (LDLT.vectorD() - d).array().abs().maxCoeff();
    REQUIRE(diag_resid < 1e-12);
  }

  SECTION("inplace") {
    // Do the Cholesky using celerite
    int flag = factor(x, c, a, U, V, a, V, S);
    REQUIRE(flag == 0);

    // Reconstruct the L matrix
    Matrix UWT;
    to_dense(x, c, Eigen::VectorXd::Ones(N), U, V, UWT);
    UWT.triangularView<Eigen::StrictlyUpper>().setConstant(0.0);

    // Check that the lower triangle is correct
    double resid = (matrixL - UWT).array().abs().maxCoeff();
    REQUIRE(resid < 1e-12);

    // Check that the diagonal is correct
    double diag_resid = (LDLT.vectorD() - a).array().abs().maxCoeff();
    REQUIRE(diag_resid < 1e-12);
  }
}


text:
// Copyright 2019 Josh Pieper, jjp@pobox.com.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#if defined(TARGET_STM32F4)
#include "stm32f4xx.h"
#else
#error "Unsupported target"
#endif

#include <string_view>

#include <boost/crc.hpp>

#include "mjlib/base/buffer_stream.h"
#include "mjlib/base/tokenizer.h"
#include "mjlib/multiplex/format.h"
#include "mjlib/multiplex/stream.h"

namespace {
using mjlib::multiplex::Format;

template <typename T>
uint32_t u32(T value) {
  return static_cast<uint32_t>(value);
}

// Copied from stm32f446_async_uart
struct Dma {
  DMA_Stream_TypeDef* stream;
  uint32_t channel;
  volatile uint32_t* status_clear;
  volatile uint32_t* status_register;
  uint32_t status_tcif;
  uint32_t status_htif;
  uint32_t status_teif;
  uint32_t status_dmeif;
  uint32_t status_feif;
  IRQn_Type irq;

  uint32_t all_status() const {
    return status_tcif |
        status_htif |
        status_teif |
        status_dmeif |
        status_feif;
  }
};

struct DmaPair {
  Dma tx;
  Dma rx;
};

void uint8_hex(uint8_t value, char* buffer) {
  constexpr char digits[] = "0123456789ABCDEF";
  buffer[0] = digits[value >> 4];
  buffer[1] = digits[value & 0x0f];
  buffer[2] = 0;
}

void uint32_hex(uint32_t value, char* buffer) {
  for (size_t i = 0; i < 4; i++) {
    uint8_hex(value >> (3 - i) * 8, &buffer[i * 2]);
  }
}

// I was going to just use strtol, but for some reason linking it in
// caused boost::crc to stop working?  Oh well, this is easy enough
// for now.
uint32_t hex_to_i(const std::string_view& str) {
  uint32_t result = 0;
  for (char c : str) {
    result <<= 4;
    result |= [&]() {
      if (c >= '0' && c <= '9') { return c - '0'; }
      if (c >= 'a' && c <= 'f') { return c - 'a' + 0x0a; }
      if (c >= 'A' && c <= 'F') { return c - 'A' + 0x0a; }
      return 0;
    }();
  }
  return result;
}

#define MAKE_UART(DmaNumber, StreamNumber, ChannelNumber, StatusRegister) \
  Dma {                                                                 \
    DmaNumber ## _Stream ## StreamNumber,                               \
        (ChannelNumber) << DMA_SxCR_CHSEL_Pos,                          \
        & ( DmaNumber -> StatusRegister ## FCR ),                       \
        & ( DmaNumber -> StatusRegister ## SR ),                        \
        DMA_ ## StatusRegister ## SR_TCIF ## StreamNumber,              \
        DMA_ ## StatusRegister ## SR_HTIF ## StreamNumber,              \
        DMA_ ## StatusRegister ## SR_TEIF ## StreamNumber,              \
        DMA_ ## StatusRegister ## SR_DMEIF ## StreamNumber,             \
        DMA_ ## StatusRegister ## SR_FEIF ## StreamNumber,              \
        DmaNumber ## _Stream ## StreamNumber ## _IRQn,                  \
        }

DmaPair MakeDma(USART_TypeDef* uart) {
  if (uart == USART1) {
      return { MAKE_UART(DMA2, 7, 4, HI), MAKE_UART(DMA2, 2, 4, LI), };
  } else if (uart == USART2) {
    return { MAKE_UART(DMA1, 6, 4, HI), MAKE_UART(DMA1, 5, 4, HI), };
  } else if (uart == USART3) {
      return { MAKE_UART(DMA1, 3, 4, LI), MAKE_UART(DMA1, 1, 4, LI), };
  } else if (uart == UART4) {
    return { MAKE_UART(DMA1, 4, 4, HI), MAKE_UART(DMA1, 2, 4, LI), };
  } else if (uart == UART5) {
    return { MAKE_UART(DMA1, 7, 4, HI), MAKE_UART(DMA1, 0, 4, LI), };
  } else if (uart == USART6) {
    return { MAKE_UART(DMA2, 6, 5, HI), MAKE_UART(DMA2, 1, 5, LI), };
  }
  return {};
}

class DigitalOut {
 public:
  DigitalOut(GPIO_TypeDef* port, int pin) : port_(port), pin_(pin) {}

  void write(int value) {
    if (value) {
      port_->BSRR |= (1 << pin_);
    } else {
      port_->BSRR |= (1 << (pin_ + 16));
    }
  }

 private:
  GPIO_TypeDef* const port_;
  const int pin_;
};

class MillisecondTimer {
 public:
  uint32_t read_ms() {
    return TIM5->CNT / 1000;
  }

  uint32_t read_us() {
    return TIM5->CNT;
  }

  void wait_ms(uint32_t delay_ms) {
    wait_us(delay_ms * 1000);
  }

  void wait_us(uint32_t delay_us) {
    uint32_t current = TIM5->CNT;
    uint32_t elapsed = 0;
    while (true) {
      const uint32_t next = TIM5->CNT;
      elapsed += next - current;
      if (elapsed >= (delay_us + 1)) { return; }
      current = next;
    }
  }
};

template <typename T>
struct Buffer {
  T data[256] = {};
  size_t pos = 0;

  std::string_view view() const {
    return {reinterpret_cast<const char*>(data), pos};
  }

  size_t capacity() const {
    return sizeof(data) / sizeof(*data);
  }

  mjlib::base::BufferWriteStream writer() {
    return mjlib::base::BufferWriteStream(
        mjlib::base::string_span(&data[pos], capacity() - pos));
  }
};

class BootloaderServer {
 public:
  BootloaderServer(uint8_t id,
                   USART_TypeDef* uart, GPIO_TypeDef* dir_port, int dir_pin)
      : id_(id),
        uart_(uart),
        dir_(dir_port, dir_pin) {
    // We reconfigure the DMA entirely.  We need to re-point it at our
    // buffers in any event, and setting everything else up from
    // scratch is easy enough.
    const auto dma_pair = MakeDma(uart);
    dma_tx_ = dma_pair.tx;
    dma_rx_ = dma_pair.rx;

    // Make the tx DMA write to the uart.
    dma_tx_.stream->PAR = reinterpret_cast<uint32_t>(&(uart_->DR));
    dma_tx_.stream->CR =
        dma_tx_.channel |
        DMA_SxCR_MINC |
        DMA_MEMORY_TO_PERIPH;

    // Configure the RX to go into our buffer.  Notably, we enable no
    // interrupts, because we are going to poll.
    for (auto& item : rx_.data) { item = 0xffff; }

    dma_rx_.stream->PAR = reinterpret_cast<uint32_t>(&(uart_->DR));
    dma_rx_.stream -> CR =
        dma_rx_.channel |
        DMA_SxCR_MINC |
        DMA_PERIPH_TO_MEMORY |
        (0x1 << DMA_SxCR_MSIZE_Pos) | // 16-bit memory
        (0x1 << DMA_SxCR_PSIZE_Pos) | // 16-bit peripheral
        DMA_SxCR_CIRC;

    // Start reading into the circular buffer.
    dma_rx_.stream->M0AR = reinterpret_cast<uint32_t>(rx_.data);
    *dma_rx_.status_clear |= dma_rx_.all_status();
    dma_rx_.stream->NDTR = rx_.capacity();
    dma_rx_.stream->CR |= DMA_SxCR_EN;

    // Finally, enable receiving.
    uart_->CR3 |= USART_CR3_DMAR;

    // Send out our startup message.
    auto writer = response_.writer();
    writer.write("multiplex bootloader protocol 1\r\n");
    response_.pos = writer.offset();
  }

  void Run() {
    while (true) {
      // Read command.
      ReadCommand();

      // Do action.
      RunCommand();
    }
  }

  void WriteResponse(uint8_t id, int max_bytes) {
    // Formulate our out frame.
    out_frame_.pos = 0;
    auto buffer_stream = out_frame_.writer();
    mjlib::multiplex::WriteStream write_stream(buffer_stream);

    const size_t bytes_to_write =
        max_bytes >= 0 ?
        std::min<size_t>(max_bytes, response_.pos) :
        response_.pos;

    write_stream.Write(Format::kHeader);
    write_stream.Write<uint8_t>(id_);
    write_stream.Write<uint8_t>(id);
    write_stream.WriteVaruint(
        mjlib::multiplex::GetVaruintSize(u32(Format::Subframe::kServerToClient)) +
        mjlib::multiplex::GetVaruintSize(1) + // channel number
        mjlib::multiplex::GetVaruintSize(bytes_to_write) +
        bytes_to_write);

    write_stream.WriteVaruint(u32(Format::Subframe::kServerToClient));
    write_stream.WriteVaruint(1);
    write_stream.WriteVaruint(bytes_to_write);
    buffer_stream.write(response_.view().substr(0, bytes_to_write));

    // Calculate the CRC and write it out.
    boost::crc_ccitt_type crc;
    crc.process_bytes(out_frame_.data, buffer_stream.offset());
    write_stream.Write<uint16_t>(crc.checksum());

    out_frame_.pos = buffer_stream.offset();

    // Mark that we've written out all we have.
    std::memmove(&response_.data[0], &response_.data[bytes_to_write],
                 response_.pos - bytes_to_write);
    response_.pos -= bytes_to_write;

    // Now queue up the DMA transfer and wait for it to finish.

    // Set direction bit.
    dir_.write(1);

    *dma_tx_.status_clear |= dma_tx_.all_status();
    uart_->SR &= ~USART_SR_TC;

    dma_tx_.stream->NDTR = out_frame_.pos;
    dma_tx_.stream->M0AR = reinterpret_cast<uint32_t>(out_frame_.data);
    dma_tx_.stream->CR |= DMA_SxCR_EN;

    uart_->CR3 |= USART_CR3_DMAT;

    // Wait for transmission to complete.
    while (((*dma_tx_.status_register) & dma_tx_.status_tcif) == 0);
    while ((uart_->SR & USART_SR_TC) == 0);

    // Delay a bit.
    timer_.wait_us(2);

    // Clear the direction bit.
    dir_.write(0);
  }

  void ReadCommand() {
    while (true) {
      ReadFrame();

      if (command_.view().find_first_of("\r\n") != std::string_view::npos) {
        return;
      }
    }
  }

  void ReadFrame() {
    // Read until we get a start of frame.
    while (true) {
      frame_.pos = 0;
      const uint8_t hdr1 = ReadFrameByte();
      if (hdr1 != 0x54) { continue; }

      const uint8_t hdr2 = ReadFrameByte();
      if (hdr2 != 0xab) { continue; }

      break;
    }

    ReadFrameByte(); // source id
    ReadFrameByte(); // dest id
    const uint32_t size = ReadFrameVaruint();
    if (size > (frame_.capacity() - frame_.pos)) {
      // We'll just ignore this, since it is larger than we can
      // handle.
      return;
    }

    // Read the whole frame plus the checksum.
    for (uint32_t i = 0; i < size + 2; i++) {
      ReadFrameByte();
    }

    // Verify the checksum.
    boost::crc_ccitt_type crc;
    crc.process_bytes(frame_.data, frame_.pos - 2);
    const uint16_t expected_crc = crc.checksum();

    {
      mjlib::base::BufferReadStream buffer_stream{
        std::string_view{frame_.data + frame_.pos - 2, 2}};
      mjlib::multiplex::ReadStream read_stream{buffer_stream};
      const auto maybe_actual_crc = read_stream.ReadScalar<uint16_t>();
      if (!maybe_actual_crc) { return; }
      if (*maybe_actual_crc != expected_crc) { return; }
    }

    mjlib::base::BufferReadStream buffer_stream{
      std::string_view{frame_.data, frame_.pos - 2}};
    mjlib::multiplex::ReadStream read_stream{buffer_stream};
    read_stream.ReadScalar<uint16_t>();  // ignore frame header
    const auto maybe_source_id = read_stream.ReadScalar<uint8_t>();
    const auto maybe_dest_id = read_stream.ReadScalar<uint8_t>();

    if (!maybe_source_id || !maybe_dest_id) { return; }

    // Verify it is addressed to us.
    if (*maybe_dest_id != id_) { return; }

    read_stream.ReadVaruint();  // ignore the payload size

    // Look for the subframe we know about.
    const auto maybe_subframe_id = read_stream.ReadVaruint();
    if (!maybe_subframe_id) { return; }

    if (*maybe_subframe_id != u32(Format::Subframe::kClientToServer) &&
        *maybe_subframe_id != u32(Format::Subframe::kClientPollServer)) {
      return;
    }

    const bool poll_only =
        (*maybe_subframe_id == u32(Format::Subframe::kClientPollServer));

    const bool query = (*maybe_source_id & 0x80) != 0;

    const auto maybe_channel = read_stream.ReadVaruint();
    if (!maybe_channel) { return; }
    if (*maybe_channel != 1) { return; }

    const auto maybe_bytes = read_stream.ReadVaruint();
    if (!maybe_bytes) { return; }

    if (!poll_only) {
      if (*maybe_bytes > static_cast<size_t>(buffer_stream.remaining())) {
        return;
      }
      if (command_.pos + *maybe_bytes > command_.capacity()) {
        // We would have overrun our command buffer.  Just empty it out
        // and discard this one too.
        command_.pos = 0;
        return;
      }

      // Great, we have some bytes, move the data into the command buffer.
      std::memcpy(&command_.data[command_.pos], frame_.data + buffer_stream.offset(),
                  *maybe_bytes);
      command_.pos += *maybe_bytes;
    }

    if (query) {
      // Write out anything we've got after a short delay to give the
      // master a chance to switch back to receive mode.
      constexpr int kResponseDelayUs = 100;
      timer_.wait_us(kResponseDelayUs);

      WriteResponse(*maybe_source_id & 0x7f, poll_only ? *maybe_bytes : -1);
    }
  }

  uint32_t ReadFrameVaruint() {
    uint32_t result = 0;
    int pos = 0;
    for (int i = 0; i < 5; i++) {
      const auto this_byte = ReadFrameByte();
      result |= (this_byte & 0x7f) << pos;
      pos += 7;
      if ((this_byte & 0x80) == 0) { return result; }
    }
    return std::numeric_limits<uint32_t>::max();
  }

  uint8_t ReadFrameByte() {
    const uint8_t result = GetNextByte();
    frame_.data[frame_.pos] = result;
    frame_.pos++;
    return result;
  }

  uint8_t GetNextByte() {
    volatile uint16_t* data = rx_.data;

    int reset_count = 0;

    // Block until the DMA has populated something.
    while (data[rx_.pos] == 0xffff) {
      // Check to see if we perhaps got out of sync with the DMA.
      const auto write_position = rx_.capacity() - dma_rx_.stream->NDTR;
      if (write_position != rx_.pos) {
        reset_count++;
        if (reset_count > 1000) {
          // Wipe out everything and start over.
          std::memset(rx_.data, 0xff, sizeof(rx_.data));
          rx_.pos = write_position;
        }
      } else {
        reset_count = 0;
      }
    }

    const uint8_t result = data[rx_.pos];
    data[rx_.pos] = 0xffff;
    rx_.pos = (rx_.pos + 1) % rx_.capacity();
    return result;
  }

  void RunCommand() {
    auto writer = response_.writer();

    auto command_end = command_.view().find_first_of("\r\n");

    mjlib::base::Tokenizer tokenizer(command_.view(), " \r\n");
    const auto next = tokenizer.next();
    if (next == std::string_view()) {
      // Empty line, just ignore.
    } else if (next == "echo") {
      // We will just echo back the remainder.
      writer.write(tokenizer.remaining());
    } else if (next == "unlock") {
      Unlock();
      writer.write("OK\r\n");
    } else if (next == "lock") {
      Lock();
      writer.write("OK\r\n");
    } else if (next == "w") {
      const auto address = tokenizer.next();
      const auto data = tokenizer.next();
      if (address.empty() || data.empty()) {
        writer.write("malformed write\r\n");
      } else {
        WriteFlash(address, data, writer);
      }
    } else if (next == "r") {
      const auto address = tokenizer.next();
      const auto size = tokenizer.next();
      if (address.empty() || size.empty()) {
        writer.write("malformed read\r\n");
      } else {
        ReadFlash(address, size, writer);
      }
    } else if (next == "reset") {
      // Make sure flash is back in the locked state before resetting.
      Lock();
      NVIC_SystemReset();
    } else if (next == "fault") {
      uint32_t* const value = reinterpret_cast<uint32_t*>(0x00200002);
      *value = 1;
    } else {
      writer.write("unknown command\r\n");
    }

    response_.pos += writer.offset();

    const auto to_consume = command_end + 1;
    std::memmove(command_.data, command_.data + to_consume,
                 command_.capacity() - to_consume);
    command_.pos -= to_consume;
  }

  void Unlock() {
    if (!locked_) { return; }

    FLASH->SR |= FLASH_SR_PGSERR;
    FLASH->SR |= FLASH_SR_PGPERR;
    FLASH->KEYR = 0x45670123;
    FLASH->KEYR = 0xCDEF89AB;
    locked_ = false;
  }

  void Lock() {
    if (locked_) { return; }

    FLASH->CR |= FLASH_CR_LOCK;
    locked_ = true;
  }

  void ReadFlash(const std::string_view& address_str,
                 const std::string_view& size_str,
                 mjlib::base::WriteStream& writer) {
    char buf[10] = {};

    const uint32_t start_address = hex_to_i(address_str);
    const uint32_t size = hex_to_i(size_str);
    if (size > 32) {
      writer.write("size too big\r\n");
      return;
    }
    uint32_hex(start_address, buf);
    writer.write(buf);
    writer.write(" ");

    for (uint32_t i = 0; i < size; i++) {
      const uint8_t* const value = reinterpret_cast<uint8_t*>(start_address + i);
      uint8_hex(*value, buf);
      writer.write(buf);
    }
    writer.write("\r\n");
  }

  void WriteFlash(const std::string_view& address_str,
                  const std::string_view& data_str,
                  mjlib::base::WriteStream& writer) {
    const auto start = timer_.read_us();
    if (data_str.size() % 2 != 0) {
      writer.write("odd data size\r\n");
      return;
    }
    const uint32_t start_address = hex_to_i(address_str);
    const uint32_t bytes = data_str.size() / 2;
    for (uint32_t i = 0; i < bytes; i++) {
      const uint8_t this_byte =
          hex_to_i(std::string_view(data_str.data() + i * 2, 2));
      if (!WriteByte(start_address + i, this_byte, writer)) {
        return;
      }
    }
    const auto end = timer_.read_us();
    writer.write("OK ");
    char buf[16] = {};
    uint32_hex((end - start), buf);
    writer.write(buf);
    writer.write("\r\n");
  }

  bool WriteByte(uint32_t address, uint8_t byte, mjlib::base::WriteStream& writer) {
    // What sector are we in.
    Sector* sector = [&]() -> Sector* {
      for (auto& sector : sectors_) {
        if (address >= sector.start &&
            address < (sector.start + sector.size)) {
          return &sector;
        }
      }
      return nullptr;
    }();

    if (!sector) {
      writer.write("address not in flash\r\n");
      return false;
    }

    if (!sector->programmable) {
      writer.write("sector not writeable\r\n");
      return false;
    }

    if (!sector->erased) {
      // Before we can write to this, we need to erase it.
      const uint32_t error = EraseSector(sector->number);
      if (error) {
        writer.write("erase error ");
        char buf[10] = {};
        uint32_hex(error, buf);
        writer.write(buf);
        writer.write("\r\n");
        return false;
      }
      sector->erased = true;
    }

    const uint32_t error = ProgramByte(sector->number, address, byte);
    if (error) {
      writer.write("program error ");
      char buf[10] = {};
      uint32_hex(error, buf);
      writer.write(buf);
      writer.write("\r\n");
      return false;
    }
    return true;
  }

  uint32_t EraseSector(uint32_t number) {
    while (FLASH->SR & FLASH_SR_BSY);
    FLASH->CR =
        // PSIZE = 0, so single byte
        (number << FLASH_CR_SNB_Pos) |
        FLASH_CR_SER;
    FLASH->CR |= FLASH_CR_STRT;

    while (FLASH->SR & FLASH_SR_BSY);

    return FlashError();
  }

  uint32_t ProgramByte(uint32_t sector, uint32_t address, uint8_t byte) {
    while (FLASH->SR & FLASH_SR_BSY);
    FLASH->CR =
        (sector << FLASH_CR_SNB_Pos) |
        FLASH_CR_PG;

    *reinterpret_cast<volatile uint8_t*>(address) = byte;

    while (FLASH->SR & FLASH_SR_BSY);

    return FlashError();
  }

  uint32_t FlashError() {
    const uint32_t result = FLASH->SR & (
        FLASH_SR_WRPERR |
        FLASH_SR_PGAERR |
        FLASH_SR_PGPERR |
        FLASH_SR_PGSERR |
        FLASH_SR_RDERR);
    FLASH->SR = ~0;
    return result;
  }

 private:
  const uint8_t id_;
  USART_TypeDef* const uart_;
  DigitalOut dir_;

  MillisecondTimer timer_;
  Dma dma_tx_;
  Dma dma_rx_;

  // Data that the DMA is populating.
  Buffer<uint16_t> rx_;

  // The contents of one multiplex frame.
  Buffer<char> frame_;

  // The current command line that is being received.
  Buffer<char> command_;

  // The thing we are going to send back.
  Buffer<char> response_;

  Buffer<char> out_frame_;

  bool locked_ = true;

  struct Sector {
    uint32_t number;
    uint32_t start;
    uint32_t size;
    bool programmable;
    bool erased;
  };

  Sector sectors_[8] = {
    { 0, 0x8000000, 0x4000, true, false }, // The ISRs
    { 1, 0x8004000, 0x4000, false, false }, // PersistentConfig
    { 2, 0x8008000, 0x4000, true, false }, // unused
    { 3, 0x800c000, 0x4000, false, false }, // where we are located!
    { 4, 0x8010000, 0x10000, true, false },
    { 5, 0x8020000, 0x20000, true, false },
    { 6, 0x8040000, 0x20000, true, false },
    { 7, 0x8060000, 0x20000, true, false },
  };
};

void BadInterrupt() {
  while (true);
}

}

extern "C" {

extern uint8_t __bss_start__;
extern uint8_t __bss_end__;

void __attribute__((__section__(".multiplex_bootloader")))
MultiplexBootloader(uint8_t source_id,
                    USART_TypeDef* uart,
                    GPIO_TypeDef* direction_port,
                    int direction_pin) {
  // While we are bootloading, we want no interrupts whatsoever.
  __disable_irq();

  // Manually zero out our BSS.
  ::memset(&__bss_start__, 0, &__bss_end__ - &__bss_start__);

  // We don't want any handlers to go into the original application
  // code, so point everything to a noop.
  for (int i = -14; i <= 96; i++) {
    const auto irq = static_cast<IRQn_Type>(i);

    if (irq == DebugMonitor_IRQn) { continue; }
    NVIC_SetVector(irq, reinterpret_cast<uint32_t>(&BadInterrupt));
  }

  BootloaderServer server(source_id, uart, direction_port, direction_pin);
  server.Run();
}

void abort() {
  while (true) {}
}

}

namespace mjlib {
namespace base {

void assertion_failed(const char* expression, const char* filename, int line) {
  while (true);
}

}
}

ext: cc

max_stars_repo_name: annhan/moteus

max_issues_repo_name: annhan/moteus

max_forks_repo_name: annhan/moteus


text:
/*
 *  Author(s):
 *  - Joël Lamotte <jlamotte@aldebaran.com>
 *
 *  Copyright (c) 2014 Aldebaran. All rights reserved.
 */

#include <gtest/gtest.h>
#include <boost/filesystem/fstream.hpp>
#include <boost/filesystem.hpp>
#include <atomic>

#include <qicore/file.hpp>
#include <qi/path.hpp>
#include <qi/application.hpp>
#include <qi/type/dynamicobjectbuilder.hpp>
#include <testsession/testsessionpair.hpp>
#include <testsession/testsession.hpp>

qiLogCategory("qimessaging.testFile");

namespace
{
struct TemporaryDir
{
  const qi::Path PATH;
  TemporaryDir()
    : PATH(qi::os::mktmpdir("qiCoreTestFile"))
  {
  }
  ~TemporaryDir()
  {
    boost::system::error_code err;
    const auto path = boost::filesystem::system_complete(PATH);
    boost::filesystem::remove_all(path, err);
    if (err)
    {
      qiLogError() << "Failed to remove temporary directory '" << PATH << "' : " << err.message();
    }
  }
} const TEMPORARY_DIR;

const qi::Path SMALL_TEST_FILE_PATH{TEMPORARY_DIR.PATH / "\xED\x95\x9C/testfile.data"};
qi::Path BIG_TEST_FILE_PATH;

const std::string TESTFILE_CONTENT = "abcdefghijklmnopqrstuvwxyz";
const std::streamoff TESTFILE_PARTIAL_BEGIN_POSITION = 23;
const std::streamoff TESTFILE_PARTIAL_SIZE = 3;

const std::streamoff TESTFILE_MIDDLE_BEGIN_POSITION = TESTFILE_CONTENT.size() / 3;
const std::streamoff TESTFILE_MIDDLE_SIZE = TESTFILE_CONTENT.size() / 3;

void makeSmallTestFile()
{
  boost::filesystem::remove(SMALL_TEST_FILE_PATH);
  boost::filesystem::create_directories(SMALL_TEST_FILE_PATH.parent());
  boost::filesystem::ofstream fileOutput(SMALL_TEST_FILE_PATH, std::ios::out | std::ios::binary);
  assert(fileOutput.is_open());
  fileOutput << TESTFILE_CONTENT;
  fileOutput.flush();
}

void checkIsTestFileContent(const qi::Buffer& buffer, std::streamoff beginOffset, std::streamsize bytesCount)
{
  EXPECT_EQ(static_cast<std::streamsize>(buffer.size()), bytesCount);
  for (size_t idx = 0; idx < buffer.totalSize(); ++idx)
  {
    EXPECT_EQ(static_cast<const char*>(buffer.data())[idx], TESTFILE_CONTENT[beginOffset + idx]);
  }
}

void checkIsTestFileMiddleContent(const qi::Buffer& buffer)
{
  return checkIsTestFileContent(buffer, TESTFILE_MIDDLE_BEGIN_POSITION, TESTFILE_MIDDLE_SIZE);
}

void checkIsTestFilePartialContent(const qi::Buffer& buffer)
{
  return checkIsTestFileContent(buffer, TESTFILE_PARTIAL_BEGIN_POSITION, TESTFILE_PARTIAL_SIZE);
}

void checkIsTestFileContent(const qi::Buffer& buffer)
{
  for (size_t idx = 0; idx < buffer.totalSize(); ++idx)
  {
    EXPECT_EQ(static_cast<const char*>(buffer.data())[idx], TESTFILE_CONTENT[idx]);
  }
}

void checkIsTestFileContent(qi::File& file)
{
  ASSERT_TRUE(file.isOpen());
  EXPECT_EQ(static_cast<std::streamsize>(TESTFILE_CONTENT.size()), file.size());
  file.seek(0);
  const qi::Buffer allFileData = file.read(file.size());
  EXPECT_EQ(TESTFILE_CONTENT.size(), allFileData.totalSize());
  checkIsTestFileContent(allFileData);
}

// Compare contents of the files and report any difference explicitly
void checkSameFilesContent(qi::File& leftFile, qi::File& rightFile)
{
  ASSERT_TRUE(leftFile.isOpen());
  ASSERT_TRUE(rightFile.isOpen());
  ASSERT_EQ(leftFile.size(), rightFile.size());
  static const std::streamsize BYTES_STEP = 1024 * 64;

  for (std::streamoff byteOffset = 0;
       byteOffset < static_cast<std::streamoff>(rightFile.size());
       byteOffset += BYTES_STEP)
  {
    qi::Buffer leftBytes = leftFile.read(byteOffset, BYTES_STEP);
    qi::Buffer rightBytes = rightFile.read(byteOffset, BYTES_STEP);
    EXPECT_GE(BYTES_STEP, static_cast<std::streamsize>(leftBytes.totalSize()));
    EXPECT_GE(BYTES_STEP, static_cast<std::streamsize>(rightBytes.totalSize()));
    EXPECT_EQ(leftBytes.totalSize(), rightBytes.totalSize());

    ASSERT_TRUE(std::equal(static_cast<char*>(leftBytes.data()),
                           static_cast<char*>(leftBytes.data()) + leftBytes.totalSize(),
                           static_cast<char*>(rightBytes.data())));
  }
}
}

TEST(TestFile, cannotReadUnknownFile)
{
  EXPECT_THROW(
      {
        qi::FilePtr file = qi::openLocalFile("file/that/doesnt/exists.atall");
      },
      std::runtime_error);
}

TEST(TestFile, cannotReadClosedFile)
{
  qi::FilePtr file = qi::openLocalFile(SMALL_TEST_FILE_PATH);
  EXPECT_TRUE(file->isOpen());

  file->close();
  EXPECT_FALSE(file->isOpen());
  EXPECT_EQ(0, file->size());
  EXPECT_THROW(
      {
        file->read(42);
      },
      std::runtime_error);
  EXPECT_THROW(
      {
        file->seek(42);
      },
      std::runtime_error);
}

TEST(TestFile, readLocalFile)
{
  qi::FilePtr testFile = qi::openLocalFile(SMALL_TEST_FILE_PATH);
  EXPECT_TRUE(testFile->isOpen());
  EXPECT_EQ(std::streamsize(TESTFILE_CONTENT.size()), testFile->size());

  static const size_t COUNT_BYTES_TO_READ = 20;

  qi::Buffer buffer = testFile->read(COUNT_BYTES_TO_READ);
  ASSERT_EQ(COUNT_BYTES_TO_READ, buffer.totalSize());
  checkIsTestFileContent(buffer);
}

TEST(TestFile, cannotReadPastEnd)
{
  qi::FilePtr testFile = qi::openLocalFile(SMALL_TEST_FILE_PATH);
  EXPECT_TRUE(testFile->isOpen());
  EXPECT_EQ(std::streamsize(TESTFILE_CONTENT.size()), testFile->size());

  testFile->read(testFile->size());
  qi::Buffer buffer = testFile->read(1);
  EXPECT_EQ(0u, buffer.totalSize());
}

TEST(TestFile, localCopy)
{
  qi::FilePtr testFile = qi::openLocalFile(SMALL_TEST_FILE_PATH);
  EXPECT_TRUE(testFile->isOpen());
  EXPECT_EQ(std::streamsize(TESTFILE_CONTENT.size()), testFile->size());

  static const qi::Path LOCAL_COPY_PATH("if_this_is_not_removed_test_file_failed.txt");
  boost::filesystem::remove(LOCAL_COPY_PATH);
  {
    copyToLocal(testFile, LOCAL_COPY_PATH);

    qi::FilePtr copiedFile = qi::openLocalFile(SMALL_TEST_FILE_PATH);
    EXPECT_TRUE(copiedFile->isOpen());
    checkSameFilesContent(*testFile, *copiedFile);
  }
  boost::filesystem::remove(LOCAL_COPY_PATH);
}

namespace
{
qi::FilePtr getTestFile(const qi::Path& filePath)
{
  qi::FilePtr testFile = qi::openLocalFile(filePath);
  EXPECT_TRUE(testFile->isOpen());
  return testFile;
}

std::atomic<bool> printProgressHaveBeenCalled(false);
void printTranferProgress(double progress)
{
  printProgressHaveBeenCalled = true;
  qiLogInfo() << "#### File Transfer Progress = " << (progress * 100.0) << "%";
}

class Test_ReadRemoteFile : public ::testing::Test
{
public:
  void SetUp()
  {
    qi::DynamicObjectBuilder objectBuilder;
    objectBuilder.advertiseMethod("getTestFile", &getTestFile);
    service = objectBuilder.object();

    qi::SessionPtr serverSession = sessionPair.server();
    serverSession->registerService("service", service);
  }

  qi::FilePtr clientAcquireTestFile(const qi::Path& path)
  {
    qi::AnyObject service = sessionPair.client()->service("service").value();
    qi::FilePtr testFile = service.call<qi::FilePtr>("getTestFile", path);
    EXPECT_TRUE(testFile->isRemote());
    return testFile;
  }

private:
  TestSessionPair sessionPair;
  qi::AnyObject service;
};
}

TEST_F(Test_ReadRemoteFile, isLocalOrRemote)
{
  {
    qi::FilePtr testFile = openLocalFile(SMALL_TEST_FILE_PATH);
    EXPECT_FALSE(testFile->isRemote());
  }
  {
    qi::FilePtr testFile = clientAcquireTestFile(SMALL_TEST_FILE_PATH);
    EXPECT_TRUE(testFile->isRemote());
  }
}

TEST_F(Test_ReadRemoteFile, someReading)
{
  qi::FilePtr testFile = clientAcquireTestFile(SMALL_TEST_FILE_PATH);

  static const size_t COUNT_BYTES_TO_READ = 20;

  qi::Buffer buffer = testFile->read(COUNT_BYTES_TO_READ);
  ASSERT_EQ(COUNT_BYTES_TO_READ, buffer.totalSize());
  checkIsTestFileContent(buffer);
}

TEST_F(Test_ReadRemoteFile, readAll)
{
  qi::FilePtr testFile = clientAcquireTestFile(SMALL_TEST_FILE_PATH);

  static const size_t COUNT_BYTES_TO_READ_PER_CYCLE = 10;

  qi::Buffer buffer;
  qi::Buffer cycleBuffer;
  while (true)
  {
    cycleBuffer = testFile->read(COUNT_BYTES_TO_READ_PER_CYCLE);
    buffer.write(cycleBuffer.data(), cycleBuffer.totalSize());
    if (cycleBuffer.totalSize() < COUNT_BYTES_TO_READ_PER_CYCLE)
      break;
  }

  EXPECT_EQ(TESTFILE_CONTENT.size(), buffer.totalSize());
  checkIsTestFileContent(buffer);
}

TEST_F(Test_ReadRemoteFile, readAllOnce)
{
  qi::FilePtr testFile = clientAcquireTestFile(SMALL_TEST_FILE_PATH);

  const std::streamsize fileSize = testFile->size();
  EXPECT_EQ(std::streamsize(TESTFILE_CONTENT.size()), fileSize);

  qi::Buffer buffer = testFile->read(fileSize);
  EXPECT_EQ(TESTFILE_CONTENT.size(), buffer.totalSize());
  checkIsTestFileContent(buffer);
}

TEST_F(Test_ReadRemoteFile, filetransfert)
{
  static const qi::Path LOCAL_PATH_TO_RECEIVE_FILE_IN = TEMPORARY_DIR.PATH / "file.data";
  boost::filesystem::remove(LOCAL_PATH_TO_RECEIVE_FILE_IN);

  {
    qi::FilePtr testFile = clientAcquireTestFile(SMALL_TEST_FILE_PATH);

    copyToLocal(testFile, LOCAL_PATH_TO_RECEIVE_FILE_IN);
  }
  {
    qi::FilePtr localFileCopy = qi::openLocalFile(LOCAL_PATH_TO_RECEIVE_FILE_IN);
    EXPECT_TRUE(localFileCopy->isOpen());
    checkIsTestFileContent(*localFileCopy);
  }

  boost::filesystem::remove(LOCAL_PATH_TO_RECEIVE_FILE_IN);
}

TEST_F(Test_ReadRemoteFile, readInTheMiddle)
{
  qi::FilePtr testFile = clientAcquireTestFile(SMALL_TEST_FILE_PATH);

  qi::Buffer bufferPartial = testFile->read(TESTFILE_PARTIAL_BEGIN_POSITION, TESTFILE_PARTIAL_SIZE);
  checkIsTestFilePartialContent(bufferPartial);

  qi::Buffer bufferMiddle = testFile->read(TESTFILE_MIDDLE_BEGIN_POSITION, TESTFILE_MIDDLE_SIZE);
  checkIsTestFileMiddleContent(bufferMiddle);
}

TEST_F(Test_ReadRemoteFile, bigFiletransfert)
{
  static const qi::Path LOCAL_PATH_TO_RECEIVE_FILE_IN = TEMPORARY_DIR.PATH / "bigfile.data";
  boost::filesystem::remove(LOCAL_PATH_TO_RECEIVE_FILE_IN);

  {
    qi::FilePtr testFile = clientAcquireTestFile(BIG_TEST_FILE_PATH);

    qi::FileCopyToLocal fileCopy{testFile, LOCAL_PATH_TO_RECEIVE_FILE_IN};
    fileCopy.notifier()->progress.connect(&printTranferProgress);
    fileCopy.start().wait();

    EXPECT_TRUE(printProgressHaveBeenCalled.load());
  }
  {
    qi::FilePtr originalFile = qi::openLocalFile(BIG_TEST_FILE_PATH);
    qi::FilePtr localFileCopy = qi::openLocalFile(LOCAL_PATH_TO_RECEIVE_FILE_IN);
    checkSameFilesContent(*originalFile, *localFileCopy);
  }

  boost::filesystem::remove(LOCAL_PATH_TO_RECEIVE_FILE_IN);
}

TEST_F(Test_ReadRemoteFile, cancelFileTransfer)
{
  static const qi::Path LOCAL_PATH_TO_RECEIVE_FILE_IN = TEMPORARY_DIR.PATH / "bigfile.data";
  boost::filesystem::remove(LOCAL_PATH_TO_RECEIVE_FILE_IN);

  {
    qi::FilePtr testFile = clientAcquireTestFile(BIG_TEST_FILE_PATH);

    qi::FileCopyToLocal fileOp{ testFile, LOCAL_PATH_TO_RECEIVE_FILE_IN };
    auto fileOpNotifier = fileOp.notifier();
    fileOpNotifier->status.connect([&](qi::ProgressNotifier::Status status){
      if (status == qi::ProgressNotifier::Status_Running)
      {
        fileOpNotifier->waitForFinished().cancel();
      }
    });
    qi::Future<void> copyOpFt = fileOp.start();
    copyOpFt.wait();
    EXPECT_TRUE(copyOpFt.isCanceled());
  }

  EXPECT_FALSE(boost::filesystem::exists(LOCAL_PATH_TO_RECEIVE_FILE_IN));
  boost::filesystem::remove(LOCAL_PATH_TO_RECEIVE_FILE_IN);
}

int main(int argc, char** argv)
{
  ::TestMode::forceTestMode(TestMode::Mode_SD);
  ::testing::InitGoogleTest(&argc, argv);
  qi::Application app(argc, argv);
  BIG_TEST_FILE_PATH = qi::path::findLib("qi");
  makeSmallTestFile();
  const int result = RUN_ALL_TESTS();
  return result;
}


text:
// Boost.Function library

// Copyright (C) 2002-2003 Doug Gregor (gregod@cs.rpi.edu)
//
// Permission to copy, use, sell and distribute this software is granted
// provided this copyright notice appears in all copies.
// Permission to modify the code and to distribute modified code is granted
// provided this copyright notice appears in all copies, and a notice
// that the code was modified is included with the copyright notice.
//
// This software is provided "as is" without express or implied warranty,
// and with no claim as to its suitability for any purpose.

// For more information, see http://www.boost.org

// Make sure we don't try to redefine function2
#include <boost/function/function2.hpp>

// Define all Boost.Function class templates up to 30 arguments
#define BOOST_FUNCTION_MAX_ARGS 30
#include <boost/function.hpp>

int main()
{
  boost::function0<float> f0;

  boost::function30<float, int, int, int, int, int, int, int, int, int, int,
                    int, int, int, int, int, int, int, int, int, int,
                    int, int, int, int, int, int, int, int, int, int> f30;
  return 0;
}