/*
   Copyright [2017-2020] [IBM Corporation]
   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.
*/

#ifndef __API_FABRIC_ITF__
#define __API_FABRIC_ITF__

#include <component/base.h> /* component::IBase, DECLARE_COMPONENT_UUID, DECLARE_INTERFACE_UUID */
#include <common/byte_span.h>
#include <common/string_view.h>

#include <gsl/span>
#include <chrono>
#include <cstddef> /* size_t */
#include <cstdint> /* uint16_t, uint64_t */
#include <functional>
#include <stdexcept> /* runtime_error */
#include <vector>

#ifndef FABRIC_H
#define FI_READ (1ULL << 8)
#define FI_WRITE (1ULL << 9)
#define FI_RECV (1ULL << 10)
#define FI_SEND (1ULL << 11)
#define FI_TRANSMIT FI_SEND
#define FI_REMOTE_READ (1ULL << 12)
#define FI_REMOTE_WRITE (1ULL << 13)

#define FI_MULTI_RECV (1ULL << 16)
#define FI_REMOTE_CQ_DATA (1ULL << 17)
#define FI_MORE (1ULL << 18)
#define FI_PEEK (1ULL << 19)
#define FI_TRIGGER (1ULL << 20)
#define FI_FENCE (1ULL << 21)

#define FI_COMPLETION (1ULL << 24)
#define FI_EVENT FI_COMPLETION
#define FI_INJECT (1ULL << 25)
#define FI_INJECT_COMPLETE (1ULL << 26)
#define FI_TRANSMIT_COMPLETE (1ULL << 27)
#define FI_DELIVERY_COMPLETE (1ULL << 28)
#define FI_AFFINITY (1ULL << 29)
#define FI_COMMIT_COMPLETE (1ULL << 30)

#define FI_VARIABLE_MSG (1ULL << 48)
#define FI_RMA_PMEM (1ULL << 49)
#define FI_SOURCE_ERR (1ULL << 50)
#define FI_LOCAL_COMM (1ULL << 51)
#define FI_REMOTE_COMM (1ULL << 52)
#define FI_SHARED_AV (1ULL << 53)
#define FI_PROV_ATTR_ONLY (1ULL << 54)
#define FI_NUMERICHOST (1ULL << 55)
#define FI_RMA_EVENT (1ULL << 56)
#define FI_SOURCE (1ULL << 57)
#define FI_NAMED_RX_CTX (1ULL << 58)
#define FI_DIRECTED_RECV (1ULL << 59)
#endif

struct iovec; /* definition in <sys/uio.h> */

namespace component
{

class IFabric_runtime_error : public std::runtime_error {
 public:
  explicit IFabric_runtime_error(const common::string_view what_arg);
  /**
   * @return The libfabric error code (enumeration in <rdma/fi_errno.h>)
   */
  virtual unsigned id() const noexcept = 0;
};

/**
 * Fabric/RDMA-based network component
 *
 */
#pragma GCC diagnostic push
#if defined            __GNUC__ && 6 < __GNUC__
#pragma GCC diagnostic ignored "-Wnoexcept-type"
#endif

struct IFabric_memory_region;

class IFabric_op_completer {
 public:
  virtual ~IFabric_op_completer() {}

  /*
   * Functionality which probably belongs to a higher layer, but which landed
   * here. Callbacks which return DEFER are enqueued, to be retried after
   * later callbacks are given a first chance to run.
   */
  enum class cb_acceptance { ACCEPT, DEFER };

  /**
   * Poll completion events; service completion queues and store
   * events not belonging to group (stall them).  This method will
   * BOTH service the completion queues and service those events
   * stalled previously
   *
   *  completion_flags,
   * FI_{SEND,RECV,RMA,ATOMIC,MSG,TAGGED,MULTICAST,(REMOTE_)?READ,(REMOTE_)?WRITE,REMOTE_CQ_DATA,MULTI_RECV},
   *  are described in "man fi_cq" and defined in
   * libfabric/include/rdma/fabric.h
   *
   * @param completion_callback (context_t, completion_flags, std::size_t len,
   * status_t status, void* error_data)
   *
   * @return Number of completions processed
   */
  using complete_old = std::function<void(void *context, ::status_t)>;
  using complete_definite =
      std::function<void(void *context, ::status_t, std::uint64_t completion_flags, std::size_t len, void *error_data)>;
  using complete_tentative = std::function<
      cb_acceptance(void *context, ::status_t, std::uint64_t completion_flags, std::size_t len, void *error_data)>;
  using complete_param_definite = std::function<
      void(void *context, ::status_t, std::uint64_t completion_flags, std::size_t len, void *error_data, void *param)>;
  using complete_param_tentative              = std::function<cb_acceptance(void *context,
                                                               ::status_t,
                                                               std::uint64_t completion_flags,
                                                               std::size_t   len,
                                                               void *        error_data,
                                                               void *        param)>;
  using complete_param_definite_ptr_noexcept  = void (*)(void *context,
                                                        ::status_t,
                                                        std::uint64_t completion_flags,
                                                        std::size_t   len,
                                                        void *        error_data,
                                                        void *        param)
#if 201703L <= __cplusplus
	noexcept
#endif
    ;
  using complete_param_tentative_ptr_noexcept = cb_acceptance (*)(void *context,
                                                                  ::status_t,
                                                                  std::uint64_t completion_flags,
                                                                  std::size_t   len,
                                                                  void *        error_data,
                                                                  void *        param)
#if 201703L <= __cplusplus
	noexcept
#endif
	;

  /**
   * @throw IFabric_runtime_error - cq_read unhandled error
   * @throw std::logic_error - called on closed connection
   */
  virtual std::size_t poll_completions(const complete_old &completion_callback) = 0;
  /**
   * @throw IFabric_runtime_error - cq_read unhandled error
   * @throw std::logic_error - called on closed connection
   */
  virtual std::size_t poll_completions(const complete_definite &completion_callback) = 0;
  /**
   * @throw IFabric_runtime_error - cq_read unhandled error
   * @throw std::logic_error - called on closed connection
   */
  virtual std::size_t poll_completions_tentative(const complete_tentative &completion_callback) = 0;
  /**
   * @throw IFabric_runtime_error - cq_read unhandled error
   * @throw std::logic_error - called on closed connection
   */
  virtual std::size_t poll_completions(const complete_param_definite &completion_callback, void *callback_param) = 0;
  /**
   * @throw IFabric_runtime_error - cq_read unhandled error
   * @throw std::logic_error - called on closed connection
   */
  virtual std::size_t poll_completions_tentative(const complete_param_tentative &completion_callback,
                                                 void *                          callback_param) = 0;
  /**
   * @throw IFabric_runtime_error - cq_read unhandled error
   * @throw std::logic_error - called on closed connection
   */
  virtual std::size_t poll_completions(complete_param_definite_ptr_noexcept completion_callback,
                                       void *                               callback_param) = 0;
  /**
   * @throw IFabric_runtime_error - cq_read unhandled error
   * @throw std::logic_error - called on closed connection
   */
  virtual std::size_t poll_completions_tentative(complete_param_tentative_ptr_noexcept completion_callback,
                                                 void *                                callback_param) = 0;

  /**
   * Get count of stalled completions.
   *
   * @throw std::system_error, e.g. for locking
   */
  virtual std::size_t stalled_completion_count() = 0;

  /**
   * Block and wait for next completion.
   *
   * @param polls_limit Maximum number of polls
   *
   * @return (was next completion context. But poll_completions can retrieve
   * that)
   *
   * @throw IFabric_runtime_error - ::fi_control fail
   * @throw std::system_error - pselect fail
   */
  virtual void wait_for_next_completion(unsigned polls_limit = 0) = 0;
  /**
   * Block and wait for next completion.
   *
   * @param polls_limit Maximum time to wait
   *
   * @return (was next completion context. But poll_completions can retrieve
   * that)
   *
   * @throw IFabric_runtime_error - ::fi_control fail
   * @throw std::system_error - pselect fail
   */
  virtual void wait_for_next_completion(std::chrono::milliseconds timeout) = 0;

  /**
   * Unblock any threads waiting on completions
   *
   * @throw std::system_error, e.g. for locking
   */
  virtual void unblock_completions() = 0;

  /* Additional TODO:
     - support for completion and event counters
     - support for statistics collection
  */
};

class IFabric_memory_control {
 public:
  using const_byte_span = common::const_byte_span;
  virtual ~IFabric_memory_control() {}

  using memory_region_t = IFabric_memory_region *;
  /**
   * Register buffer for RDMA
   *
   * @param contig_addr Pointer to contiguous region
   * @param size Size of buffer in bytes
   * @param key Requested key for the remote memory. Note: if the fabric
   * provider uses the key (i.e., the fabric provider memory region attributes
   *            do not include the FI_MR_PROV_KEY bit), then the key must be
   *            unique among registered memory regions. As this API does not
   *            expose these attributes, the only safe strategy is to assume
   * that the key must be unique among registered memory regsions.
   * @param flags Flags e.g., FI_REMOTE_READ|FI_REMOTE_WRITE. Flag definitions
   * are in <rdma/fabric.h>
   *
   * @return Memory region handle
   *
   * @throw std::range_error - address already registered
   * @throw std::logic_error - inconsistent memory registry
   */
  virtual memory_region_t register_memory(const void *  contig_addr,
                                          std::size_t   size,
                                          std::uint64_t key,
                                          std::uint64_t flags)
  { return register_memory(common::make_const_byte_span(contig_addr, size), key, flags); }
  virtual memory_region_t register_memory(const_byte_span contig_,
                                          std::uint64_t key,
                                          std::uint64_t flags) = 0;

  /**
   * De-register memory region
   *
   * @param memory_region Memory region to de-register
   *
   * @throw std::range_error - address not registered
   * @throw std::logic_error - inconsistent memory registry
   */
  virtual void deregister_memory(memory_region_t memory_region) = 0;

  virtual std::uint64_t get_memory_remote_key(memory_region_t) const noexcept = 0;
  virtual void *        get_memory_descriptor(memory_region_t) const noexcept = 0;

  /**
   * Asynchronously post a buffer to receive data
   *
   * @param buffers Buffer span (containing regions should be registered)
   *
   * @return Work (context) identifier
   *
   * @throw IFabric_runtime_error - ::fi_recvv fail
   */
  virtual void post_recv(gsl::span<const ::iovec> buffers, void **descriptors, void *context) = 0;
  void post_recv(const ::iovec *first, const ::iovec *last, void **descriptors, void *context) { return post_recv( {first, last}, descriptors, context); }
  virtual void post_recv(gsl::span<const ::iovec> buffers, void *context)                           = 0;
};

class IFabric_client;
class IFabric_client_grouped;

/**
 * IFabric_initiator
 *  operations unique to a connected endpoint
 */
class IFabric_initiator
{
 public:
	virtual ~IFabric_initiator() {}
  /**
   * Asynchronously post a buffer to the connection
   *
   * @param buffers Buffer span (containing regions should be registered)
   *
   * @return Work (context) identifier
   *
   * @throw IFabric_runtime_error std::runtime_error - ::fi_sendv fail
   */
  virtual void post_send(gsl::span<const ::iovec> buffers, void **descriptors, void *context) = 0;
  void post_send(const ::iovec *first, const ::iovec *last, void **descriptors, void *context) { return post_send( {first, last}, descriptors, context); }
  virtual void post_send(gsl::span<const ::iovec> buffers, void *context)                     = 0;

  /**
   * Post RDMA read operation
   *
   * @param buffers Destination buffer span
   * @param remote_addr Remote address
   * @param key Key for remote address
   * @param out_context
   *
   * @throw IFabric_runtime_error - ::fi_readv fail
   *
   */
  virtual void post_read(gsl::span<const ::iovec> buffers,
                         void **        descriptors,
                         std::uint64_t  remote_addr,
                         std::uint64_t  key,
                         void *         context) = 0;

  void post_read(const ::iovec *first,
                         const ::iovec *last,
                         void **        descriptors,
                         std::uint64_t  remote_addr,
                         std::uint64_t  key,
                         void *         context) { return post_read( { first, last }, descriptors, remote_addr, key, context); }

  virtual void post_read(gsl::span<const ::iovec> buffers,
                         std::uint64_t               remote_addr,
                         std::uint64_t               key,
                         void *                      context) = 0;

  /**
   * Post RDMA write operation
   *
   * @param buffers Source buffer span
   * @param remote_addr Remote address
   * @param key Key for remote address
   * @param out_context
   *
   * @throw IFabric_runtime_error - ::fi_writev fail
   *
   */
  virtual void post_write(const gsl::span<const ::iovec> buffers,
                          void **        descriptors,
                          std::uint64_t  remote_addr,
                          std::uint64_t  key,
                          void *         context) = 0;

  void post_write(const ::iovec *first,
                          const ::iovec *last,
                          void **        descriptors,
                          std::uint64_t  remote_addr,
                          std::uint64_t  key,
                          void *         context)
  {
    return post_write(
      gsl::span<const ::iovec>(first, last), descriptors, remote_addr, key, context);
  }

  virtual void post_write(const gsl::span<const ::iovec> buffers,
                          std::uint64_t               remote_addr,
                          std::uint64_t               key,
                          void *                      context) = 0;

  /**
   * Send message without completion
   *
   * @param connection Connection to inject on
   * @param buf Data to send
   * @param buf Length of data to send (must not exceed
   * IFabric_connection::max_inject_size())
   *
   * @throw IFabric_runtime_error - ::fi_inject fail
   */
  virtual void inject_send(const void *buf, std::size_t len) = 0;

  /* TODO: atomic RMA operations
  */
};

/**
 * IFabric_endpoint_connected
 *  operations available to a connected endpoint
 */
class IFabric_endpoint_connected
	: public IFabric_memory_control
	, public IFabric_initiator
	, public IFabric_op_completer
{
  /* TODO: statistics collection
  */
};

class IFabric_endpoint_unconnected
	: public IFabric_memory_control
{
};

/**
 * Fabric/RDMA-based network component
 *  an acrive bit not connected endpoint
 */
class IFabric_endpoint_unconnected_client : public IFabric_endpoint_unconnected {
 public:

  /**
   * Open a fabric client (active endpoint) connection to a server. Active
   * endpoints usually correspond with hardware resources, e.g. verbs queue
   * pair. Options may not conflict with those specified for the fabric.
   *
   * @param json_configuration Configuration string in JSON
   * @param remote_endpoint The IP address (URL) of the server
   * @param port The IP port on the server
   *
   * @return the endpoint
   *
   * @throw std::bad_alloc - destination address alloc failed
   * @throw std::bad_alloc - libfabric out of memory
   * @throw std::bad_alloc - libfabric out of memory (creating a new server)
   * @throw std::bad_alloc - out of memory
   * @throw std::domain_error : json file parse-detected error
   * @throw std::logic_error : socket initialized with a negative value (from
   * ::socket) in Fd_control
   * @throw std::logic_error : unexpected event
   * @throw std::system_error : error receiving fabric server name
   * @throw std::system_error : pselect fail (expecting event)
   * @throw std::system_error : resolving address
   * @throw std::system_error : pselect fail
   * @throw std::system_error : read error on event pipe
   * @throw std::system_error - writing event pipe (normal callback)
   * @throw std::system_error - writing event pipe (readerr_eq)
   * @throw std::system_error - receiving data on socket
   * @throw IFabric_runtime_error - ::fi_domain fail
   * @throw IFabric_runtime_error - ::fi_connect fail
   * @throw IFabric_runtime_error - ::fi_ep_bind fail
   * @throw IFabric_runtime_error - ::fi_enable fail
   * @throw IFabric_runtime_error - ::fi_ep_bind fail (event registration)
   *
   */
  virtual IFabric_client *make_open_client() = 0;

  /**
   * Open a fabric endpoint for which communications are divided into smaller
   * entities (groups). Options may not conflict with those specified for the
   * fabric.
   *
   * @param json_configuration Configuration string in JSON
   * @param remote_endpoint The IP address (URL) of the server
   * @param port The IP port on the server
   *
   * @return the endpoint
   *
   * @throw std::bad_alloc - destination address alloc failed
   * @throw std::bad_alloc - libfabric out of memory
   * @throw std::bad_alloc - libfabric out of memory (creating a new server)
   * @throw std::bad_alloc - out of memory
   * @throw std::domain_error : json file parse-detected error
   * @throw std::logic_error : socket initialized with a negative value (from
   * ::socket) in Fd_control
   * @throw std::logic_error : unexpected event
   * @throw std::system_error : error receiving fabric server name
   * @throw std::system_error : pselect fail (expecting event)
   * @throw std::system_error : resolving address
   * @throw std::system_error : pselect fail
   * @throw std::system_error : read error on event pipe
   * @throw std::system_error - writing event pipe (normal callback)
   * @throw std::system_error - writing event pipe (readerr_eq)
   * @throw std::system_error - receiving data on socket
   * @throw IFabric_runtime_error - ::fi_domain fail
   * @throw IFabric_runtime_error - ::fi_connect fail
   * @throw IFabric_runtime_error - ::fi_ep_bind fail
   * @throw IFabric_runtime_error - ::fi_enable fail
   * @throw IFabric_runtime_error - ::fi_ep_bind fail (event registration)
   *
   */
  virtual IFabric_client_grouped *make_open_client_grouped() = 0;
};

class IFabric_connection {
 public:
  virtual ~IFabric_connection() {}

  /**
   * Get address of connected peer (taken from fi_getpeer during
   * connection instantiation). The fi_getpeer documentation says
   * "The address  must  be  in the same format as that specified using
   * fi_info: addr_format when the endpoint was created". Presumably
   * this means that the address returned by fi_getpeer *will* be in
   * that same form, and is thus dependent on the implementation of
   * endpoint creation.
   *
   *
   * @return Peer endpoint address
   * @throw std::bad_alloc, e.g.
   */
  virtual std::string get_peer_addr() = 0;

  /**
   * Get local address of connection (taken from fi_getname during
   * connection instantiation).
   *
   *
   * @return Local endpoint address
   * @throw std::bad_alloc, e.g.
   */
  virtual std::string get_local_addr() = 0;

  /**
   * Get the maximum message size for the provider
   *
   * @return Max message size in bytes
   */
  virtual std::size_t max_message_size() const noexcept = 0;

  /**
   * Get the maximum inject message size for the provider
   *
   * @return Max inject message size in bytes
   */
  virtual std::size_t max_inject_size() const noexcept = 0;

  /* Additional TODO:
     - support for atomic RMA operations
     - support for completion and event counters
     - support for statistics collection
  */
};

/**
 * An endpoint with a communication channel. Distinguished from a connection
 * which can provide grouped communications channels, but does not of itself
 * provide communications.
 *
 */
class IFabric_endpoint_comm
    : public IFabric_connection
    , public IFabric_endpoint_connected {
};

/**
 * An endpoint established as a server.
 *
 */
class IFabric_server : public IFabric_endpoint_comm {
};

/**
 * An endpoint established as a client.
 *
 */
class IFabric_client : public IFabric_endpoint_comm {
};

/* A group: memory control, commands, and completions */
class IFabric_group
	: public IFabric_endpoint_connected
{
};

/**
 * An endpoint "grouped", without a communication channel.
 * Can provide communications channels, but does not initiate operations
 *
 */
class IFabric_endpoint_grouped
    : public IFabric_connection
    , public IFabric_memory_control
    , public IFabric_op_completer
{
 public:
  /**
   * Allocate group (for partitioned completion handling)
   *
   * @throw std::system_error, e.g. for locking
   * @throw std::bad_alloc, e.g.
   */
  virtual IFabric_group *allocate_group() = 0;
};

/**
 * A client endpoint which cannot initiate commands but which
 * can allocate "commuicators" which can initiate commands.
 *
 */
class IFabric_client_grouped : public IFabric_endpoint_grouped {
};

/**
 * A server endpoint which cannot initiate commands but which
 * can allocate "commuicators" which can initiate commands.
 *
 */
class IFabric_server_grouped : public IFabric_endpoint_grouped {
};

/**
 * Fabric passive endpoint. Instantiation of this interface normally creates
 * an active thread that uses the fabric connection manager to handle
 * connections (see man fi_cm).  On release of the interface, the endpoint
 * is destroyed.
 */
class IFabric_passive_endpoint {
 public:
  virtual ~IFabric_passive_endpoint() {}

  /**
   * Get the maximum message size for the provider
   *
   * @return Max message size in bytes
   */
  virtual std::size_t max_message_size() const noexcept = 0;

  /**
   * Get provider name
   *
   *
   * @return Provider name
   *
   * @throw std::bad_alloc, e.g.
   */
  virtual std::string get_provider_name() const = 0;
};

class IFabric_endpoint_unconnected_server
	: public IFabric_endpoint_unconnected
{
};

/**
 * Fabric passive endpoint providing servers with ordinary (not grouped)
 * communicators.
 */
class IFabric_server_factory : public IFabric_passive_endpoint {
 public:
  /**
   * Server/accept side handling of new connections (handled by the
   * active thread) are queued so that they can be taken by a polling
   * thread and integrated into the processing loop.  This method is
   * normally invoked until NULL is returned.
   *
   * @return New connection, or NULL if no new connection.
   *
   * @throw std::system_error, e.g. for locking
   * @throw std::logic_error : unexpected event
   * @throw std::system_error : read error on event pipe
   * @throw std::system_error : failure to listen for connections
   */
	virtual IFabric_endpoint_unconnected_server *get_new_endpoint_unconnected() = 0;
	virtual IFabric_server *open_connection(IFabric_endpoint_unconnected_server *) = 0;

  /**
   * Close connection and release any associated resources
   *
   * @param connection
   *
   * @throw std::system_error, e.g. for locking
   */
  virtual void close_connection(IFabric_server *connection) = 0;

  /**
   * Used to get a vector of active connection belonging to this
   * end point.
   *
   * @return Vector of active connections
   *
   * @throw std::bad_alloc, e.g.
   */
  virtual std::vector<IFabric_server *> connections() = 0;

  /**
   * Get provider name
   *
   *
   * @return Provider name
   *
   * @throw std::bad_alloc, e.g.
   */
  virtual std::string get_provider_name() const = 0;
};

/* Fabric passive endpoint providing servers with grouped communicators.
 */
class IFabric_server_grouped_factory : public IFabric_passive_endpoint {
 public:
  /**
   * Server/accept side handling of new connections (handled by the
   * active thread) are queued so that they can be taken by a polling
   * thread an integrated into the processing loop.  This method is
   * normally invoked until NULL is returned.
   *
   *
   * @return New connection, or NULL if no new connection.
   *
   * @throw std::logic_error : unexpected event
   * @throw std::system_error, e.g. for locking
   * @throw std::system_error : read error on event pipe
   */
	virtual IFabric_endpoint_unconnected_server *get_new_endpoint_unconnected() = 0;
	virtual IFabric_server_grouped *open_connection(IFabric_endpoint_unconnected_server *) = 0;

  /**
   * Close connection and release any associated resources
   *
   * @param connection
   *
   * @throw std::system_error, e.g. for locking
   */
  virtual void close_connection(IFabric_server_grouped *connection) = 0;

  /**
   * Used to get a vector of active connection belonging to this
   * end point.
   *
   * @return Vector of active connections
   * @throw std::bad_alloc, e.g.
   */
  virtual std::vector<IFabric_server_grouped *> connections() = 0;
};

class IFabric {
 public:
  using memory_region_t = IFabric_memory_region *;

  DECLARE_INTERFACE_UUID(0xc373d083, 0xe629, 0x46c9, 0x86fa, 0x6f, 0x96, 0x40, 0x61, 0x10, 0xdf);
  virtual ~IFabric() {}
  /**
   * Open a fabric server factory. Endpoints usually correspond with hardware
   * resources, e.g. verbs queue pair. Options may not conflict with those
   * specified for the fabric.
   *
   * @param json_configuration Configuration string in JSON
   *
   * @return the endpoint
   *
   * @throw std::domain_error : json file parse-detected error
   * @throw IFabric_runtime_error - ::fi_passive_ep fail
   * @throw IFabric_runtime_error - ::fi_pep_bind fail
   * @throw IFabric_runtime_error - ::fi_listen fail
   */
  virtual IFabric_server_factory *open_server_factory(const common::string_view json_configuration, std::uint16_t port) = 0;
  /**
   * Open a fabric "server grouped" factory. Endpoints usually correspond with
   * hardware resources, e.g. verbs queue pair. Options may not conflict with
   * those specified for the fabric. "Server groups" are servers in which each
   * operation is assigned to a "communication group." Each "completion group"
   * may be separately polled for completions.
   *
   * @param json_configuration Configuration string in JSON
   *
   * @return the endpoint
   *
   * @throw std::domain_error : json file parse-detected error
   * @throw IFabric_runtime_error - ::fi_passive_ep fail
   * @throw IFabric_runtime_error - ::fi_pep_bind fail
   * @throw IFabric_runtime_error - ::fi_listen fail
   */
  virtual IFabric_server_grouped_factory *open_server_grouped_factory(const common::string_view json_configuration,
                                                                      std::uint16_t      port) = 0;

  virtual IFabric_endpoint_unconnected_client *make_endpoint(const common::string_view json_configuration, common::string_view remote_endpoint, std::uint16_t port) = 0;

  /*
   * provider name in the fabric.
   */
  virtual const char *prov_name() const noexcept = 0;
};

class IFabric_factory : public component::IBase {
 public:
  DECLARE_INTERFACE_UUID(0xfac3d083, 0xe629, 0x46c9, 0x86fa, 0x6f, 0x96, 0x40, 0x61, 0x10, 0xdf);

  /**
   * Open a fabric endpoint.  Endpoints usually correspond with hardware
   * resources, e.g. verbs queue pair Options should include provider,
   * capabilities, active thread core.
   *
   * @param json_configuration Configuration string in JSON
   * form. e.g. { "caps":["FI_MSG","FI_RMA"], "preferred_provider" : "verbs"}
   * @return
   */
  virtual ~IFabric_factory() {}
  /**
   * @throw std::bad_alloc - out of memory
   * @throw std::domain_error : json file parse-detected error
   * @throw IFabric_runtime_error - ::fi_control fail
   */
  virtual IFabric *make_fabric(const common::string_view json_configuration) = 0;
};

}  // namespace component

#endif  // __API_FABRIC_ITF__


/***
 * Copyright 2020 The Katla Authors
 *
 * 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.
 */
#ifndef KATLA_SQLITE_DATABASE_H
#define KATLA_SQLITE_DATABASE_H

#include "katla/core/core.h"
#include "katla/core/error.h"

#include "sqlite3.h"

#include <memory>
#include <vector>
#include <optional>
#include <gsl/span>

namespace katla {

struct SqliteTableData {
    int nrOfColumns {};
    std::vector<std::string> columnNames;
    std::vector<std::string> data;
};

struct SqliteQueryResult {
    std::optional<SqliteTableData> queryResult;
};

class SqliteDatabase {
public:
    SqliteDatabase();
    virtual ~SqliteDatabase();

    outcome::result<void, Error> init();

    outcome::result<void, Error> open();
    outcome::result<void, Error> close();

    outcome::result<void, Error> create(std::string path);

    outcome::result<SqliteQueryResult, Error> exec(std::string sql);

    outcome::result<SqliteQueryResult, Error> insert(std::string table,  gsl::span<std::pair<std::string, std::string>> values);

    void setPath(std::string path) {
        m_path = path;
    }

    bool isOpen () {
        return m_handle != nullptr;
    }

private:
    sqlite3* m_handle {};
    std::string m_path {};
};

}

#endif


/**
 *  Copyright (C) 2021 FISCO BCOS.
 *  SPDX-License-Identifier: Apache-2.0
 *  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.
 *
 * @brief implementation for BlockHeader
 * @file BlockHeaderImpl.h
 * @author: ancelmo
 * @date 2021-04-20
 */

#pragma once
#include "bcos-tars-protocol/Common.h"
#include "bcos-tars-protocol/tars/Block.h"
#include <bcos-framework/interfaces/crypto/CommonType.h>
#include <bcos-framework/interfaces/crypto/CryptoSuite.h>
#include <bcos-framework/interfaces/protocol/BlockHeader.h>
#include <bcos-framework/interfaces/protocol/ProtocolTypeDef.h>
#include <gsl/span>

namespace bcostars
{
namespace protocol
{
class BlockHeaderImpl : public bcos::protocol::BlockHeader
{
public:
    virtual ~BlockHeaderImpl() {}

    BlockHeaderImpl() = delete;

    BlockHeaderImpl(
        bcos::crypto::CryptoSuite::Ptr cryptoSuite, std::function<bcostars::BlockHeader*()> inner)
      : bcos::protocol::BlockHeader(cryptoSuite), m_inner(inner)
    {}

    void decode(bcos::bytesConstRef _data) override;
    void encode(bcos::bytes& _encodeData) const override;
    bcos::crypto::HashType hash() const override;

    void clear() override;

    int32_t version() const override { return m_inner()->data.version; }
    gsl::span<const bcos::protocol::ParentInfo> parentInfo() const override;

    bcos::crypto::HashType txsRoot() const override;
    bcos::crypto::HashType stateRoot() const override;
    bcos::crypto::HashType receiptsRoot() const override;
    bcos::protocol::BlockNumber number() const override { return m_inner()->data.blockNumber; }
    bcos::u256 gasUsed() const override;
    int64_t timestamp() const override { return m_inner()->data.timestamp; }
    int64_t sealer() const override { return m_inner()->data.sealer; }

    gsl::span<const bcos::bytes> sealerList() const override
    {
        return gsl::span(reinterpret_cast<const bcos::bytes*>(m_inner()->data.sealerList.data()),
            m_inner()->data.sealerList.size());
    }
    bcos::bytesConstRef extraData() const override
    {
        return bcos::bytesConstRef(
            reinterpret_cast<const bcos::byte*>(m_inner()->data.extraData.data()),
            m_inner()->data.extraData.size());
    }
    gsl::span<const bcos::protocol::Signature> signatureList() const override
    {
        return gsl::span(
            reinterpret_cast<const bcos::protocol::Signature*>(m_inner()->signatureList.data()),
            m_inner()->signatureList.size());
    }

    gsl::span<const uint64_t> consensusWeights() const override
    {
        return gsl::span(reinterpret_cast<const uint64_t*>(m_inner()->data.consensusWeights.data()),
            m_inner()->data.consensusWeights.size());
    }

    void setVersion(int32_t _version) override { m_inner()->data.version = _version; }

    void setParentInfo(gsl::span<const bcos::protocol::ParentInfo> const& _parentInfo) override;

    void setParentInfo(bcos::protocol::ParentInfoList&& _parentInfo) override
    {
        setParentInfo(gsl::span(_parentInfo.data(), _parentInfo.size()));
    }

    void setTxsRoot(bcos::crypto::HashType _txsRoot) override
    {
        m_inner()->data.txsRoot.assign(_txsRoot.begin(), _txsRoot.end());
    }
    void setReceiptsRoot(bcos::crypto::HashType _receiptsRoot) override
    {
        m_inner()->data.receiptRoot.assign(_receiptsRoot.begin(), _receiptsRoot.end());
    }
    void setStateRoot(bcos::crypto::HashType _stateRoot) override
    {
        m_inner()->data.stateRoot.assign(_stateRoot.begin(), _stateRoot.end());
    }
    void setNumber(bcos::protocol::BlockNumber _blockNumber) override
    {
        m_inner()->data.blockNumber = _blockNumber;
    }
    void setGasUsed(bcos::u256 _gasUsed) override
    {
        m_inner()->data.gasUsed = boost::lexical_cast<std::string>(_gasUsed);
    }
    void setTimestamp(int64_t _timestamp) override { m_inner()->data.timestamp = _timestamp; }
    void setSealer(int64_t _sealerId) override { m_inner()->data.sealer = _sealerId; }
    void setSealerList(gsl::span<const bcos::bytes> const& _sealerList) override;
    void setSealerList(std::vector<bcos::bytes>&& _sealerList) override
    {
        setSealerList(gsl::span(_sealerList.data(), _sealerList.size()));
    }

    void setConsensusWeights(gsl::span<const uint64_t> const& _weightList) override
    {
        m_inner()->data.consensusWeights.assign(_weightList.begin(), _weightList.end());
    }

    void setConsensusWeights(std::vector<uint64_t>&& _weightList) override
    {
        setConsensusWeights(gsl::span(_weightList.data(), _weightList.size()));
    }

    void setExtraData(bcos::bytes const& _extraData) override
    {
        m_inner()->data.extraData.assign(_extraData.begin(), _extraData.end());
    }
    void setExtraData(bcos::bytes&& _extraData) override
    {
        m_inner()->data.extraData.assign(_extraData.begin(), _extraData.end());
    }
    void setSignatureList(
        gsl::span<const bcos::protocol::Signature> const& _signatureList) override;

    void setSignatureList(bcos::protocol::SignatureList&& _signatureList) override
    {
        setSignatureList(gsl::span(_signatureList.data(), _signatureList.size()));
    }

    const bcostars::BlockHeader& inner() const { return *m_inner(); }

    void setInner(const bcostars::BlockHeader& blockHeader) { *m_inner() = blockHeader; }
    void setInner(bcostars::BlockHeader&& blockHeader) { *m_inner() = std::move(blockHeader); }

private:
    std::function<bcostars::BlockHeader*()> m_inner;
    mutable std::vector<bcos::protocol::ParentInfo> m_parentInfo;
};
}  // namespace protocol
}  // namespace bcostars


/** ***************************************************************************
*
*  Copyright 2018 Jose Fernando Lopez Fernandez - All Rights Reserved
*  See the file AUTHORS included with the application distribution for
*  specific information.
*
*  Redistribution and use in source and binary forms, with or without
*  modification, are permitted provided that the following
*  conditions are met:
*
*   * Redistributions of source code must retain the above copyright
*     notice, this list of conditions and the following disclaimer.
*
*   * Redistributions in binary form must reproduce the above
*     copyright notice, this list of conditions and the following
*     disclaimer in the documentation and/or other materials provided
*     with the distribution.
*
*     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
*     CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
*     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
*     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
*     DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
*     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
*     SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
*     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
*     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
*     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
*     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
*     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*  ***************************************************************************
*
*  @author Jose Fernando Lopez Fernandez
*  @date 15-June-2018
*  @brief This is the main keygen driver file
*
*  Tasks:
*      1. @todo Add file details
*      2. @todo Add file details
*
*  **************************************************************************/

#ifndef KEYGEN_INCLUDE_KEYGEN_H_
#define KEYGEN_INCLUDE_KEYGEN_H_

#define TRUE  1
#define FALSE 0

/** @def _CRT_SECURE_NO_WARNINGS
*  @brief This preprocessor definition disables Visual Studio's warnings
*  when not using the safe string functions provided by the MSVC runtime.
*
*  Disable the safe string library due to its lack of compatibility with
*  *nix systems.
*
*/

#define _CRT_SECURE_NO_WARNINGS

#ifndef _CRT_SECURE_NO_WARNINGS
#error "The safe string library is not compatible with *nix systems"
#endif

/** Include Microsoft's Guideline Support Library (GSL).
*
*  The Guideline Support Library (GSL) contains functions and types that are
*  suggested for use by the C++ Core Guidelines maintained by the Standard C++
*  Foundation. This repo contains Microsoft's implementation of GSL.
*
*  The library includes types like span<T>, string_span, owner<> and others.
*
*  The entire implementation is provided inline in the headers under the gsl
*  directory. The implementation generally assumes a platform that implements
*  C++14 support. There are specific workarounds to support MSVC 2015.
*
*  While some types have been broken out into their own headers
*  (e.g. gsl/span), it is simplest to just include gsl/gsl and gain access to
*  the entire library.
*
*/

#include <gsl/gsl>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstddef>
#include <cstdint>
#include <cerrno>
#include <csignal>
#include <cmath>
#include <ctime>

/** C++ Standard Library Headers
*
*/

#include <algorithm>
#include <chrono>
#include <iostream>
#include <limits>
#include <ratio>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include <utility>

/** @file config.h
 *  @brief This file contains customization settings and project metadata such
 *  as the current release version, compilation settings, etc.
 *
 */

#include "config.h"
#include "password.h"

#endif // KEYGEN_KEYGEN_H_


/**
* Copyright 2016 BitTorrent 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.
*/
#pragma once

#include <scraps/config.h>

#if SCRAPS_APPLE
    #include <scraps/apple/HMAC.h>
    namespace scraps { using HMACSHA256 = apple::HMAC<apple::HMACAlgorithmType::SHA256>; }
#else
    #include <scraps/sodium/HMACSHA256.h>
    namespace scraps { using HMACSHA256 = sodium::HMACSHA256; }
#endif

#include <gsl.h>

namespace scraps {

template <typename BaseByteType>
struct HMACSHA256ByteTag {};

template <typename BaseByteType>
using HMACSHA256Byte = StrongByte<HMACSHA256ByteTag<BaseByteType>>;

template <typename KeyByteType, std::ptrdiff_t KeySize, typename ByteT, std::ptrdiff_t DataSize>
std::array<HMACSHA256Byte<std::remove_const_t<ByteT>>, HMACSHA256::kResultSize>
GetHMACSHA256(gsl::span<KeyByteType, KeySize> key, gsl::span<ByteT, DataSize> data) {
    std::array<HMACSHA256Byte<std::remove_const_t<ByteT>>, HMACSHA256::kResultSize> ret;

    HMACSHA256 hmac(key.data(), key.size());
    hmac.update(data.data(), data.size());
    hmac.finish(ret.data());

    return ret;
}

} // namespace scraps


/**
 * This file is part of the "libterminal" project
 *   Copyright (c) 2019-2020 Christian Parpart <christian@parpart.family>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * 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.
 */
#pragma once

#include <terminal/Color.h>
#include <terminal/RenderBuffer.h>
#include <terminal/Screen.h>

#include <terminal_renderer/Atlas.h>
#include <terminal_renderer/BoxDrawingRenderer.h>
#include <terminal_renderer/RenderTarget.h>

#include <text_shaper/font.h>
#include <text_shaper/shaper.h>

#include <crispy/FNV.h>
#include <crispy/LRUCache.h>
#include <crispy/point.h>
#include <crispy/size.h>

#include <unicode/convert.h>
#include <unicode/run_segmenter.h>

#include <gsl/span>
#include <gsl/span_ext>

#include <functional>
#include <list>
#include <memory>
#include <unordered_map>
#include <vector>

namespace terminal::renderer
{

enum class TextStyle
{
    Invalid = 0x00,
    Regular = 0x10,
    Bold = 0x11,
    Italic = 0x12,
    BoldItalic = 0x13,
};

constexpr TextStyle operator|(TextStyle a, TextStyle b) noexcept
{
    return static_cast<TextStyle>(static_cast<unsigned>(a) | static_cast<unsigned>(b));
}

constexpr bool operator<(TextStyle a, TextStyle b) noexcept
{
    return static_cast<unsigned>(a) < static_cast<unsigned>(b);
}

struct TextCacheKey
{
    std::u32string_view text;
    TextStyle style;

    constexpr bool operator<(TextCacheKey const& _rhs) const noexcept
    {
        if (text < _rhs.text)
            return true;

        return text < _rhs.text || style < _rhs.style;
    }

    constexpr bool operator==(TextCacheKey const& _rhs) const noexcept
    {
        return text == _rhs.text && style == _rhs.style;
    }

    constexpr bool operator!=(TextCacheKey const& _rhs) const noexcept { return !(*this == _rhs); }
};

} // end namespace terminal::renderer

namespace std
{
template <>
struct hash<terminal::renderer::TextCacheKey>
{
    size_t operator()(terminal::renderer::TextCacheKey const& _key) const noexcept
    {
        auto fnv = crispy::FNV<char32_t> {};
        return static_cast<size_t>(fnv(fnv.basis(), _key.text, static_cast<char32_t>(_key.style)));
    }
};
} // namespace std

namespace terminal::renderer
{

struct GridMetrics;

enum class TextShapingEngine
{
    OpenShaper, //!< Uses open-source implementation: harfbuzz/freetype/fontconfig
    DWrite,     //!< native platform support: Windows
    CoreText,   //!< native platform support: OS/X
};

enum class FontLocatorEngine
{
    Mock,       //!< mock font locator API
    FontConfig, //!< platform independant font locator API
    DWrite,     //!< native platform support: Windows
    CoreText,   //!< native font locator on OS/X
};

std::unique_ptr<text::font_locator> createFontLocator(FontLocatorEngine _engine);

struct FontDescriptions
{
    double dpiScale = 1.0;
    crispy::Point dpi = { 0, 0 }; // 0 => auto-fill with defaults
    text::font_size size;
    text::font_description regular;
    text::font_description bold;
    text::font_description italic;
    text::font_description boldItalic;
    text::font_description emoji;
    text::render_mode renderMode;
    TextShapingEngine textShapingEngine = TextShapingEngine::OpenShaper;
    FontLocatorEngine fontLocator = FontLocatorEngine::FontConfig;
    bool builtinBoxDrawing = true;
};

inline bool operator==(FontDescriptions const& a, FontDescriptions const& b) noexcept
{
    return a.size.pt == b.size.pt && a.regular == b.regular && a.bold == b.bold && a.italic == b.italic
           && a.boldItalic == b.boldItalic && a.emoji == b.emoji && a.renderMode == b.renderMode;
}

inline bool operator!=(FontDescriptions const& a, FontDescriptions const& b) noexcept
{
    return !(a == b);
}

struct FontKeys
{
    text::font_key regular;
    text::font_key bold;
    text::font_key italic;
    text::font_key boldItalic;
    text::font_key emoji;
};

/// Text Rendering Pipeline
class TextRenderer: public Renderable
{
  public:
    TextRenderer(GridMetrics const& _gridMetrics,
                 text::shaper& _textShaper,
                 FontDescriptions& _fontDescriptions,
                 FontKeys const& _fontKeys);

    void setRenderTarget(RenderTarget& _renderTarget) override;

    void debugCache(std::ostream& _textOutput) const;
    void clearCache() override;

    void updateFontMetrics();

    void setPressure(bool _pressure) noexcept { pressure_ = _pressure; }

    /// Must be invoked before a new terminal frame is rendered.
    void beginFrame();

    /// Renders a given terminal's grid cell that has been
    /// transformed into a RenderCell.
    void renderCell(RenderCell const& _cell);

    /// Must be invoked when rendering the terminal's text has finished for this frame.
    void endFrame();

  private:
    /// Puts a sequence of codepoints that belong to the same grid cell at @p _pos
    /// at the end of the currently filled line.
    void appendCell(gsl::span<char32_t const> _codepoints, TextStyle _style, RGBColor _color);
    text::shape_result const& cachedGlyphPositions();
    text::shape_result requestGlyphPositions();
    text::shape_result shapeRun(unicode::run_segmenter::range const& _run);
    void endSequence();

    void renderRun(crispy::Point _startPos,
                   gsl::span<text::glyph_position const> _glyphPositions,
                   RGBColor _color);

    /// Renders an arbitrary texture.
    void renderTexture(crispy::Point const& _pos,
                       RGBAColor const& _color,
                       atlas::TextureInfo const& _textureInfo);

    // rendering
    //
    struct GlyphMetrics
    {
        ImageSize bitmapSize;  // glyph size in pixels
        crispy::Point bearing; // offset baseline and left to top and left of the glyph's bitmap
    };

    using TextureAtlas = atlas::MetadataTextureAtlas<text::glyph_key, GlyphMetrics>;
    using DataRef = TextureAtlas::DataRef;

    std::optional<DataRef> getTextureInfo(text::glyph_key const& _id,
                                          unicode::PresentationStyle _presentation);

    void renderTexture(crispy::Point const& _pos,
                       RGBAColor const& _color,
                       atlas::TextureInfo const& _textureInfo,
                       GlyphMetrics const& _glyphMetrics,
                       text::glyph_position const& _gpos);

    TextureAtlas* atlasForBitmapFormat(text::bitmap_format _format) noexcept
    {
        switch (_format)
        {
        case text::bitmap_format::alpha_mask: return monochromeAtlas_.get();
        case text::bitmap_format::rgba: return colorAtlas_.get();
        case text::bitmap_format::rgb: return lcdAtlas_.get();
        default: return nullptr; // Should NEVER EVER happen.
        }
    }

    // general properties
    //
    GridMetrics const& gridMetrics_;
    FontDescriptions& fontDescriptions_;
    FontKeys const& fonts_;

    // performance optimizations
    //
    bool pressure_ = false;
    std::unordered_map<text::glyph_key, text::bitmap_format> glyphToTextureMapping_;
    std::list<std::u32string> cacheKeyStorage_;
    crispy::LRUCache<TextCacheKey, text::shape_result> cache_;

    // target surface rendering
    //
    text::shaper& textShaper_;
    std::unique_ptr<TextureAtlas> monochromeAtlas_;
    std::unique_ptr<TextureAtlas> colorAtlas_;
    std::unique_ptr<TextureAtlas> lcdAtlas_;

    // sub-renderer
    //
    BoxDrawingRenderer boxDrawingRenderer_;

    // render states
    TextStyle style_ = TextStyle::Invalid;
    RGBColor color_ {};

    crispy::Point textPosition_;
    std::vector<char32_t> codepoints_;
    std::vector<unsigned> clusters_;
    unsigned cellCount_ = 0;
    bool textStartFound_ = false;
    bool forceCellGroupSplit_ = false;

    // output fields
    //
    std::vector<text::shape_result> shapedLines_;
};

} // namespace terminal::renderer

namespace fmt
{ // {{{
template <>
struct formatter<terminal::renderer::FontDescriptions>
{
    template <typename ParseContext>
    constexpr auto parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template <typename FormatContext>
    auto format(terminal::renderer::FontDescriptions const& fd, FormatContext& ctx)
    {
        return format_to(ctx.out(),
                         "({}, {}, {}, {}, {}, {})",
                         fd.size,
                         fd.regular,
                         fd.bold,
                         fd.italic,
                         fd.boldItalic,
                         fd.emoji,
                         fd.renderMode);
    }
};

template <>
struct formatter<terminal::renderer::FontLocatorEngine>
{
    template <typename ParseContext>
    constexpr auto parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template <typename FormatContext>
    auto format(terminal::renderer::FontLocatorEngine value, FormatContext& ctx)
    {
        using terminal::renderer::FontLocatorEngine;
        switch (value)
        {
        case FontLocatorEngine::Mock: return format_to(ctx.out(), "Mock");
        case FontLocatorEngine::FontConfig: return format_to(ctx.out(), "FontConfig");
        case FontLocatorEngine::DWrite: return format_to(ctx.out(), "DirectWrite");
        case FontLocatorEngine::CoreText: return format_to(ctx.out(), "CoreText");
        }
        return format_to(ctx.out(), "UNKNOWN");
    }
};

template <>
struct formatter<terminal::renderer::TextShapingEngine>
{
    template <typename ParseContext>
    constexpr auto parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template <typename FormatContext>
    auto format(terminal::renderer::TextShapingEngine value, FormatContext& ctx)
    {
        using terminal::renderer::TextShapingEngine;
        switch (value)
        {
        case TextShapingEngine::OpenShaper: return format_to(ctx.out(), "OpenShaper");
        case TextShapingEngine::DWrite: return format_to(ctx.out(), "DirectWrite");
        case TextShapingEngine::CoreText: return format_to(ctx.out(), "CoreText");
        }
        return format_to(ctx.out(), "UNKNOWN");
    }
};

template <>
struct formatter<terminal::renderer::TextCacheKey>
{
    template <typename ParseContext>
    constexpr auto parse(ParseContext& ctx)
    {
        return ctx.begin();
    }
    template <typename FormatContext>
    auto format(terminal::renderer::TextCacheKey value, FormatContext& ctx)
    {
        return format_to(ctx.out(), "({}, \"{}\")", value.style, unicode::convert_to<char>(value.text));
    }
};

} // namespace fmt


/****************************************************************************
 *                                                                          *
 *  Author : lukasz.iwaszkiewicz@gmail.com                                  *
 *  ~~~~~~~~                                                                *
 *  License : see COPYING file for details.                                 *
 *  ~~~~~~~~~                                                               *
 ****************************************************************************/

#pragma once
#include <etl/cstring.h>
// #include <fmt/core.h>
// #include <fmt/format.h>
#include <gsl/gsl>

namespace logging {

/// Line size including tick number and task name.
static constexpr size_t MAX_LINE_SIZE = 256;

/// Have to be initialized.
void init ();

/**
 * Add a log. Do not call from an ISR.
 */
bool log (gsl::czstring<> str);

// template <typename Format, typename... Args> bool log2 (Format &&format, Args &&... args)
// {
//         etl::string<MAX_LINE_SIZE> buf;
//         fmt::format_to (std::back_inserter (buf), std::forward<Format> (format), std::forward<Args> (args)...);
//         log (buf.c_str ());
//         return true;
// }

} // namespace logging


#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <valgrind/callgrind.h>
#include <gsl/gsl_histogram.h>
#include <gsl/gsl_sort.h>
#include <gsl/gsl_statistics.h>

#include "betree.h"
#include "debug.h"
#include "hashmap.h"
#include "helper.h"
#include "printer.h"
#include "tree.h"
#include "utils.h"

#define MAX_EXPRS 10000
#define MAX_EVENTS 5000
#define DEFAULT_SEARCH_COUNT 10

// wc -L filename
#define MAX_EVENT_CHARACTERS 20000
#define MAX_EXPR_CHARACTERS 17000
#define MAX_CONSTANT_CHARACTERS 20

const char* EXPRS_FILE = "data/betree_exprs";
const char* CONSTANTS_FILE = "data/betree_constants";
const char* EVENTS_FILE = "data/betree_events";

struct betree_events {
    size_t count;
    char** events;
};

void add_event(char* event, struct betree_events* events)
{
    if(events->count == 0) {
        events->events = calloc(1, sizeof(*events->events));
        if(events == NULL) {
            fprintf(stderr, "%s calloc failed", __func__);
            abort();
        }
    }
    else {
        char** new_events
            = realloc(events->events, sizeof(*new_events) * ((events->count) + 1));
        if(new_events == NULL) {
            fprintf(stderr, "%s realloc failed", __func__);
            abort();
        }
        events->events = new_events;
    }
    events->events[events->count] = strdup(event);
    events->count++;
}

char* strip_chars(const char* string, const char* chars)
{
    char* new_string = malloc(strlen(string) + 1);
    int counter = 0;

    for(; *string; string++) {
        if(!strchr(chars, *string)) {
            new_string[counter] = *string;
            ++counter;
        }
    }
    new_string[counter] = 0;
    return new_string;
}

int event_parse(const char* text, struct betree_event** event);

size_t read_betree_events(struct betree_events* events)
{
    FILE* f = fopen(EVENTS_FILE, "r");
    size_t count = 0;

    char line[MAX_EVENT_CHARACTERS]; // Arbitrary from what I've seen
    while(fgets(line, sizeof(line), f)) {
        if(MAX_EVENTS != 0 && events->count == MAX_EVENTS) {
            break;
        }

        add_event(line, events);
        count++;
    }
    fclose(f);
    return count;
}

size_t read_betree_exprs(struct betree* tree)
{

    FILE* f = fopen(EXPRS_FILE, "r");
    FILE* constants_f = fopen(CONSTANTS_FILE, "r");

    //char* lines[MAX_EXPRS];
    char line[MAX_EXPR_CHARACTERS]; // Arbitrary from what I've seen
    char constants_line[MAX_CONSTANT_CHARACTERS];
    size_t count = 0;
    const struct betree_sub* subs[MAX_EXPRS];

    enum e { constant_count = 3 };
    while(fgets(line, sizeof(line), f)) {
        char* ignore = fgets(constants_line, sizeof(constants_line), constants_f);
        (void)ignore;
        char* copy = strdup(constants_line);
        char* rest = copy;
        int64_t campaign_id = strtoll(strtok_r(rest, ",", &rest), NULL, 10);
        int64_t advertiser_id = strtoll(strtok_r(rest, ",", &rest), NULL, 10);
        int64_t flight_id = strtoll(strtok_r(rest, "\n", &rest), NULL, 10);
        const struct betree_constant* constants[constant_count] = {
            betree_make_integer_constant("campaign_id", campaign_id),
            betree_make_integer_constant("advertiser_id", advertiser_id),
            betree_make_integer_constant("flight_id", flight_id),
        };

        const struct betree_sub* sub = betree_make_sub(tree, count, constant_count, constants, line);
        subs[count] = sub;
        count++;
        betree_free_constants(constant_count, (struct betree_constant**) constants);
        free(copy);
        if(MAX_EXPRS != 0 && count == MAX_EXPRS) {
            break;
        }
    }
    /*for(size_t i = 0; i < tree->config->attr_domain_count; i++) {*/
        /*const struct attr_domain* attr_domain = tree->config->attr_domains[i];*/
        /*print_attr_domain(attr_domain);*/
    /*}*/
    fclose(f);
    fclose(constants_f);

    for(size_t i = 0; i < count; i++) {
        const struct betree_sub* sub = subs[i];
        if(!betree_insert_sub(tree, sub)) {
            printf("Can't insert expr %zu\n", i);
            abort();
        }
    }

    return count;
}

void read_betree_defs(struct betree* tree)
{
    FILE* f = fopen("data/betree_defs", "r");

    char line[LINE_MAX];
    while(fgets(line, sizeof(line), f)) {
        add_variable_from_string(tree, line);
    }

    fclose(f);
}

int compare_int( const void* a, const void* b )
{
    if( *(int*)a == *(int*)b ) return 0;
    return *(int*)a < *(int*)b ? -1 : 1;
}

int main(int argc, char** argv)
{
    size_t search_count = DEFAULT_SEARCH_COUNT;
    if(argc > 1) {
        search_count = atoi(argv[1]);
    }
    if(access("data/betree_defs", F_OK) == -1 || access("data/betree_events", F_OK) == -1
        || access("data/betree_exprs", F_OK) == -1 || access("data/betree_constants", F_OK) == -1) {
        fprintf(stderr, "Missing files, skipping the tests");
        return 0;
    }
    struct timespec start, insert_done, gen_event_done, search_done;

    // Init
    struct betree* tree = betree_make();
    read_betree_defs(tree);

    clock_gettime(CLOCK_MONOTONIC_RAW, &start);

    // Insert
    size_t expr_count = read_betree_exprs(tree);

    clock_gettime(CLOCK_MONOTONIC_RAW, &insert_done);
    uint64_t insert_us = (insert_done.tv_sec - start.tv_sec) * 1000000
        + (insert_done.tv_nsec - start.tv_nsec) / 1000;
    printf("    Insert took %" PRIu64 "\n", insert_us);

    struct betree_events events = { .count = 0, .events = NULL };
    size_t event_count = read_betree_events(&events);

    uint64_t evaluated_sum = 0;
    uint64_t matched_sum = 0;
    uint64_t memoized_sum = 0;
    uint64_t shorted_sum = 0;

    const size_t search_us_count = search_count * events.count;
    double search_us_data[search_us_count];

    CALLGRIND_START_INSTRUMENTATION;

    size_t search_us_i = 0;

    for(size_t j = 0; j < search_count; j++) {
        for(size_t i = 0; i < events.count; i++) {
            clock_gettime(CLOCK_MONOTONIC_RAW, &gen_event_done);

            char* event = events.events[i];
            struct report* report = make_report();
            if(betree_search(tree, event, report) == false) {
                fprintf(stderr, "Failed to search with event\n");
                abort();
            }

            clock_gettime(CLOCK_MONOTONIC_RAW, &search_done);

            uint64_t search_us = (search_done.tv_sec - gen_event_done.tv_sec) * 1000000
                + (search_done.tv_nsec - gen_event_done.tv_nsec) / 1000;

            search_us_data[search_us_i] = (double)search_us;

            evaluated_sum += report->evaluated;
            matched_sum += report->matched;
            memoized_sum += report->memoized;
            shorted_sum += report->shorted;
            free_report(report);
            search_us_i++;
        }
        printf("Finished run %zu/%zu\n", j, search_count);
    }

    CALLGRIND_STOP_INSTRUMENTATION;
    CALLGRIND_DUMP_STATS;

    for(size_t i = 0; i < events.count; i++) {
        free(events.events[i]);
    }

    double evaluated_average = (double)evaluated_sum / (double)MAX_EVENTS;
    double matched_average = (double)matched_sum / (double)MAX_EVENTS;
    double memoized_average = (double)memoized_sum / (double)MAX_EVENTS;
    double shorted_average = (double)shorted_sum / (double)MAX_EVENTS;
    printf("%zu searches, %zu expressions, %zu events, %zu preds, %zu memoize preds. Evaluated %.2f, matched %.2f, memoized %.2f, shorted %.2f\n",
        search_us_count,
        expr_count,
        event_count,
        tree->config->pred_map->pred_count,
        tree->config->pred_map->memoize_count,
        evaluated_average / search_count,
        matched_average / search_count,
        memoized_average / search_count,
        shorted_average / search_count);

    double search_us_min = gsl_stats_min(search_us_data, 1, search_us_count);
    double search_us_max = gsl_stats_max(search_us_data, 1, search_us_count);
    double search_us_mean = gsl_stats_mean(search_us_data, 1, search_us_count);
    gsl_sort(search_us_data, 1, search_us_count);
    double search_us_90 = gsl_stats_quantile_from_sorted_data(search_us_data, 1, search_us_count, 0.90);
    double search_us_95 = gsl_stats_quantile_from_sorted_data(search_us_data, 1, search_us_count, 0.95);
    double search_us_99 = gsl_stats_quantile_from_sorted_data(search_us_data, 1, search_us_count, 0.99);

    printf("Min: %.1f, Mean: %.1f, Max: %.1f, 90: %.1f, 95: %.1f, 99: %.1f\n", search_us_min, search_us_mean, search_us_max, search_us_90, search_us_95, search_us_99);

    printf("| %lu | %.1f | %.1f | %.1f | %.1f | %.1f | %.1f | |\n", insert_us, search_us_min, search_us_mean, search_us_max, search_us_90, search_us_95, search_us_99);

    // DEBUG
    write_dot_file(tree);
    // DEBUG

    free(events.events);
    betree_free(tree);
    return 0;
}


#pragma once

#include <memory>
#include <gsl/gsl>

/// Holds code related to computer memory.
namespace MEMORY
{
    /// A type alias to improve readability for non-null raw pointers.
    /// @tparam Type - The type of the underyling item being pointed to.
    template <typename Type>
    using NonNullRawPointer = gsl::strict_not_null<Type*>;

    /// A type alias to improve readability for non-null unique pointers.
    /// @tparam Type - The type of the underlying item in the pointer.
    template <typename Type>
    using NonNullUniquePointer = gsl::strict_not_null<std::unique_ptr<Type>>;

    /// A type alias to improve readability for non-null shared pointers.
    /// @tparam Type - The type of the underlying item in the pointer.
    template <typename Type>
    using NonNullSharedPointer = gsl::strict_not_null<std::shared_ptr<Type>>;
}


/*
 * Copyright (c) 2016-2021 lymastee, All rights reserved.
 * Contact: lymastee@hotmail.com
 *
 * This file is part of the gslib project.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#pragma once

#ifndef dvt_31727ef2_2dfa_4e64_b93d_deab9c280036_h
#define dvt_31727ef2_2dfa_4e64_b93d_deab9c280036_h

#include <gslib/config.h>
#include <gslib/type.h>
#include <gslib/std.h>

__gslib_begin__

template<class _targetfn>
inline uint method_address(_targetfn fn)
{
    uint r;
    memcpy(&r, &fn, sizeof(r));
    return r;
}

extern uint dsm_get_address(uint addr, uint pthis);
extern void dvt_recover_vtable(void* pthis, void* ovt, int vtsize);

class dvt_bridge_code;

template<class _cls>
class vtable_ops:
    public _cls
{
public:
    typedef vtable_ops<_cls> myref;

public:
    myref() {}
    template<class _arg1>
    myref(_arg1 a1): _cls(a1) {}
    template<class _arg1, class _arg2>
    myref(_arg1 a1, _arg2 a2): _cls(a1, a2) {}
    template<class _arg1, class _arg2, class _arg3>
    myref(_arg1 a1, _arg2 a2, _arg3 a3): _cls(a1, a2, a3) {}
    virtual void end_of_vtable() { __asm { nop } }      /* add some real stuff in this function to prevent optimization in release. */

public:
    int get_virtual_method_index(uint m) const
    {
        uint mv = dsm_final_address(m);
        uint eov = dsm_final_address(method_address(&myref::end_of_vtable));
        uint* ovt = *(uint**)this;
        int i = 0;
        for(;; ++ i) {
            uint cv = dsm_final_address(ovt[i]);
            if(cv == mv)
                return i;
            if(cv == eov) {
                assert(!"get index failed.");
                return i;
            }
        }
        return -1;
    }
    int size_of_vtable() const
    {
        return get_virtual_method_index(
            dsm_final_address(method_address(&myref::end_of_vtable))
            );
    }
    void* create_per_instance_vtable(_cls* p, dvt_bridge_code& bridges)
    {
        assert(p);
        int count = size_of_vtable();
        byte* ovt = *(byte**)p;
        void** pvt = (void**)VirtualAlloc(nullptr, count * 4, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
        assert(pvt);
        bridges.install(ovt, count);
        for(int i = 0; i < count; i ++)
            pvt[i] = bridges.get_bridge(i);
        memcpy(p, &pvt, 4);
        DWORD oldpro;
        VirtualProtect(pvt, count * 4, PAGE_EXECUTE_READ, &oldpro);
        return ovt;
    }
    void destroy_per_instance_vtable(_cls* p, void* ovt)
    {
        assert(p && ovt);
        dvt_recover_vtable(p, ovt, size_of_vtable());
    }
    template<class _func>
    uint replace_vtable_method(_cls* p, int index, _func func)
    {
        uint* vt = *(uint**)p;
        vt += index;
        uint r = *vt;
        DWORD oldpro;
        VirtualProtect(vt, 4, PAGE_EXECUTE_READWRITE, &oldpro);
        *vt = (uint)func;
        VirtualProtect(vt, 4, oldpro, &oldpro);
        return r;
    }

private:
    uint dsm_final_address(uint addr) const
    {
        return dsm_get_address(
            addr, (uint)((void*)this)
        );
    }
};

class dvt_detour_code
{
    typedef unsigned long ulong;

public:
    enum detour_type
    {
        detour_notify,
        detour_reflect,
    };

public:
    dvt_detour_code(detour_type dty, int argsize);
    ~dvt_detour_code();
    void finalize(uint old_func, uint host, uint action);
    uint get_code_address() const { return (uint)_ptr; }

private:
    detour_type     _type;
    int             _argsize;
    byte*           _ptr;
    int             _len;
    ulong           _oldpro;
};

/*
 * The two kind of DVT callings:
 * 1.notify:    the original function would be called first, and then the notify function will be called after, you MUST specify the size of the argument table.
 * 2.reflect:   the original function would NOT be called, simply jump to the reflect function, and the argument table of the reflect function SHOULD be exactly the same with the original function.
 */
#define connect_typed_detour(targettype, target, trigger, host, action, dty, argsize) { \
    auto& vo = vtable_ops<targettype>(nullptr); \
    (target)->ensure_dvt_available<targettype>(); \
    auto* detour = (target)->add_detour(dty, argsize); \
    assert(detour); \
    uint old_func = vo.replace_vtable_method(target, vo.get_virtual_method_index(method_address(trigger)), detour->get_code_address()); \
    detour->finalize(old_func, (uint)host, method_address(action)); \
}

#define connect_typed_notify(targettype, target, trigger, host, action, argsize) \
    connect_typed_detour(targettype, target, trigger, host, action, dvt_detour_code::detour_notify, argsize)

#define connect_notify(target, trigger, host, action, argsize) \
    connect_typed_notify(std::remove_reference_t<decltype(*target)>, target, trigger, host, action, argsize)

#define connect_typed_reflect(targettype, target, trigger, host, action) \
    connect_typed_detour(targettype, target, trigger, host, action, dvt_detour_code::detour_reflect, 0)

#define connect_reflect(target, trigger, host, action) \
    connect_typed_reflect(std::remove_reference_t<decltype(*target)>, target, trigger, host, action)

/*
 * Bridge to jump to the original vtable
 * The reason why we can't simply copy the original vtable was that it's hard to determine how long the vtable actually was.
 * If the target class had a complex derivations, missing a part of the vtable would make you unable to call __super::function.
 */
class dvt_bridge_code
{
public:
    dvt_bridge_code();
    ~dvt_bridge_code();
    void install(void* vt, int count);
    void* get_bridge(int index) const;

protected:
    byte*           _bridges;
    int             _bridge_size;       /* in bytes */
    int             _jt_stride;         /* jump table stride in bytes */
};

class dvt_holder
{
public:
    typedef vector<dvt_detour_code*> detour_list;

public:
    dvt_holder();
    virtual ~dvt_holder();

private:
    void*           _backvt;
    int             _backvtsize;
    void*           _switchvt;
    detour_list     _detours;
    bool            _delete_later;
    dvt_bridge_code _bridges;

public:
    template<class _cls>
    void ensure_dvt_available()
    {
        if(_backvt)
            return;
        auto& vo = vtable_ops<_cls>(nullptr);
        _backvtsize = vo.size_of_vtable();
        _backvt = vo.create_per_instance_vtable(static_cast<_cls*>(this), _bridges);
    }
    dvt_detour_code* add_detour(dvt_detour_code::detour_type dty, int argsize);
    dvt_holder* switch_to_ovt();        /* switch to original vtable */
    dvt_holder* switch_to_dvt();
    /*
     * In case the holder would be tagged more than once by the garbage collector, the cause might be
     * a message re-entrant of the msg callback function. So here we tag it.
     */
    void set_delete_later() { _delete_later = true; }
    bool is_delete_later() const { return _delete_later; }
};

class dvt_collector
{
public:
    typedef vector<dvt_holder*> holder_list;

private:
    holder_list     _holders;
    dvt_collector() {}

public:
    static dvt_collector* get_singleton_ptr()
    {
        static dvt_collector inst;
        return &inst;
    }
    ~dvt_collector() { cleanup(); }
    bool set_delete_later(dvt_holder* holder);
    void cleanup();
};

__gslib_end__

#endif


// SPDX-License-Identifier: MIT
// The MIT License (MIT)
//
// Copyright (c) 2014-2018, Institute for Software & Systems Engineering
// Copyright (c) 2018-2019, Johannes Leupolz
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#ifndef PEMC_BASIC_RAW_MEMORY_H_
#define PEMC_BASIC_RAW_MEMORY_H_

#include <gsl/gsl_byte>
#include <memory>

namespace pemc {

using deleter_t = std::function<void(void*)>;

using unique_void_ptr = std::unique_ptr<void, deleter_t>;

// The following method can be used to reserve some bulk memory.
// The unique_void_ptr keeps track of the memory to avoid leaks.
// unique_ptr are not used, because they do not support void.
// Example of usage:
// > unique_void_ptr stateMemory = unique_void(::operator new(amount of memory )
// ); > gsl::byte* p_stateMemory = static_cast<gsl::byte*>(stateMemory.get());
unique_void_ptr unique_void(void* ptr);

/// <summary>
///   Compares the two buffers <paramref name="buffer1" /> and <paramref
///   name="buffer2" />, returning <c>true</c> when the buffers are equivalent.
/// </summary>
/// <param name="buffer1">The first buffer of memory to compare.</param>
/// <param name="buffer2">The second buffer of memory to compare.</param>
/// <param name="sizeInBytes">The size of the buffers in bytes.</param>
bool areBuffersEqual(gsl::byte* buffer1,
                     gsl::byte* buffer2,
                     size_t sizeInBytes);

/// <summary>
///   Copies <paramref name="sizeInBytes" />-many bytes from <paramref
///   name="source" /> to <see cref="destination" />.
/// </summary>
/// <param name="source">The first buffer of memory to compare.</param>
/// <param name="destination">The second buffer of memory to compare.</param>
/// <param name="sizeInBytes">The size of the buffers in bytes.</param>
void copyBuffers(gsl::byte* source, gsl::byte* destination, size_t sizeInBytes);

/// <summary>
///   Hashes the <paramref name="buffer" />.
/// </summary>
/// <param name="buffer">The buffer of memory that should be hashed.</param>
/// <param name="sizeInBytes">The size of the buffer in bytes.</param>
/// <param name="seed">The seed value for the hash.</param>
/// <remarks>See also https://en.wikipedia.org/wiki/MurmurHash (MurmurHash3
/// implementation)</remarks>
uint32_t hashBuffer(gsl::byte* buffer, size_t sizeInBytes, int32_t seed);

}  // namespace pemc

#endif  // PEMC_BASIC_RAW_MEMORY_H_


#ifndef _GENMAP_QUALITY_H_
#define _GENMAP_QUALITY_H_

#include <gslib.h>

void printPartStat(long long *vtx, int nel, int nv, comm_ext ce);

#endif


#pragma once
#include "utility/types.h"
#include "utility/c++std/optional.h"
#include "utility/c++std/string_view.h"
#include <fmt/format.h>
#include <gsl/gsl>

namespace cws80 {

class NkScreen;
class NativeUI;
struct Bank;
struct Program;

///
class UIController {
public:
    virtual ~UIController() {}

    virtual NkScreen &screen() const = 0;
    virtual NativeUI &native_ui() const = 0;
    virtual const Program &program() const = 0;
    virtual uint bank_number() const = 0;
    virtual uint program_number() const = 0;
    virtual gsl::span<const std::string> program_names() const = 0;
    virtual i32 get_parameter(uint idx) const = 0;
    virtual bool set_parameter(uint idx, i32 val) = 0;
    virtual f32 get_f32_parameter(uint idx) const = 0;
    virtual bool set_f32_parameter(uint idx, f32 val) = 0;
    virtual void currently_editing_parameter(cxx::optional<uint> idx) = 0;
    virtual void request_next_bank() = 0;
    virtual void request_bank(uint num) = 0;
    virtual void request_program(uint num) = 0;
    virtual void request_rename_program(cxx::string_view name) = 0;
    virtual void request_init_program() = 0;
    virtual void request_write_program() = 0;
    virtual void dlg_rename_program() = 0;
    virtual void dlg_load_bank() = 0;
    virtual void dlg_save_bank() = 0;
    virtual void send_piano_events(const i8 events[128]) = 0;
    virtual const std::string &led_message() = 0;
    virtual void led_message(std::string msg) = 0;
    virtual void led_priority_message(f64 timeout, std::string msg) = 0;
    virtual const std::string &status_message() = 0;
    virtual void status_message(std::string msg) = 0;
    //
    template <class A, class... As>
    void led_fmt(const char *fmt, const A &x, const As &... xs)
    {
        led_message(fmt::format(fmt, x, xs...));
    }
    template <class A, class... As>
    void led_priority_fmt(f64 timeout, const char *fmt, const A &x, const As &... xs)
    {
        led_priority_message(timeout, fmt::format(fmt, x, xs...));
    }
    //
    template <class A, class... As>
    void status_fmt(const char *fmt, const A &x, const As &... xs)
    {
        status_message(fmt::format(fmt, x, xs...));
    }
    //
    enum {
        debugging_input = true,
        debugging_logic = true,
    };
    inline void debug_input(std::string str)
    {
        if (debugging_input)
            status_message(std::move(str));
    }
    inline void debug_logic(std::string str)
    {
        if (debugging_logic)
            status_message(std::move(str));
    }
    template <class A, class... As>
    void debug_input_fmt(const char *fmt, const A &x, const As &... xs)
    {
        if (debugging_input)
            status_fmt(fmt, x, xs...);
    }
    template <class A, class... As>
    void debug_logic_fmt(const char *fmt, const A &x, const As &... xs)
    {
        if (debugging_logic)
            status_fmt(fmt, x, xs...);
    }
};

}  // namespace cws80


#pragma once

#include <Windows.h>

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstddef>
#include <cassert>
#include <cstdarg>
#include <climits>
#include <cinttypes>
#include <cmath>
#include <ctime>

#include <algorithm>
#include <any>
#include <array>
#include <chrono>
#include <complex>
#include <deque>
#include <exception>
#include <filesystem>
#include <functional>
#include <iomanip>
#include <iostream>
#include <iterator>
#include <list>
#include <locale>
#include <memory>
#include <mutex>
#include <new>
#include <numeric>
#include <optional>
#include <random>
#include <ratio>
#include <sstream>
#include <stack>
#include <string>
#include <string_view>
#include <system_error>
#include <thread>
#include <tuple>
#include <type_traits>
#include <typeinfo>
#include <unordered_set>
#include <utility>
#include <valarray>
#include <vector>

#include <gsl/gsl>


#define MPICH_SKIP_MPICXX 1
#define OMPI_SKIP_MPICXX 1
#include <Python.h>
#include <petsc.h>
#include "libpetsc4py/libpetsc4py.c"