/* block/gsl_block_ushort.h
 *
 * Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman, Brian Gough
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifndef __GSL_BLOCK_USHORT_H__
#define __GSL_BLOCK_USHORT_H__

#include <stdlib.h>
#include <gsl/gsl_errno.h>

#undef __BEGIN_DECLS
#undef __END_DECLS
#ifdef __cplusplus
# define __BEGIN_DECLS extern "C" {
# define __END_DECLS }
#else
# define __BEGIN_DECLS /* empty */
# define __END_DECLS /* empty */
#endif

__BEGIN_DECLS

struct gsl_block_ushort_struct
{
  size_t size;
  unsigned short *data;
};

typedef struct gsl_block_ushort_struct gsl_block_ushort;

gsl_block_ushort *gsl_block_ushort_alloc (const size_t n);
gsl_block_ushort *gsl_block_ushort_calloc (const size_t n);
void gsl_block_ushort_free (gsl_block_ushort * b);

int gsl_block_ushort_fread (FILE * stream, gsl_block_ushort * b);
int gsl_block_ushort_fwrite (FILE * stream, const gsl_block_ushort * b);
int gsl_block_ushort_fscanf (FILE * stream, gsl_block_ushort * b);
int gsl_block_ushort_fprintf (FILE * stream, const gsl_block_ushort * b, const char *format);

int gsl_block_ushort_raw_fread (FILE * stream, unsigned short * b, const size_t n, const size_t stride);
int gsl_block_ushort_raw_fwrite (FILE * stream, const unsigned short * b, const size_t n, const size_t stride);
int gsl_block_ushort_raw_fscanf (FILE * stream, unsigned short * b, const size_t n, const size_t stride);
int gsl_block_ushort_raw_fprintf (FILE * stream, const unsigned short * b, const size_t n, const size_t stride, const char *format);

size_t gsl_block_ushort_size (const gsl_block_ushort * b);
unsigned short * gsl_block_ushort_data (const gsl_block_ushort * b);

__END_DECLS

#endif /* __GSL_BLOCK_USHORT_H__ */


/*
  Copyright [2017-2019] [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 __MCAS_CLIENT_HANDLER_H__
#define __MCAS_CLIENT_HANDLER_H__

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Weffc++"

#include "fabric_transport.h"
#include "mcas_client_config.h"
#include "protocol.h"
#include "protocol_ostream.h"

#include <api/fabric_itf.h>
#include <api/mcas_itf.h>
#include <common/exceptions.h>
#include <common/utils.h>
#include <common/byte_buffer.h>
#include <common/string_view.h>
#include <gsl/pointers>
#include <sys/mman.h>
#include <sys/uio.h>
#include <unistd.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <gnutls/crypto.h>

#include <boost/numeric/conversion/cast.hpp>
#include <map>
#include <set>
#include <tuple>

/* Enable this to introduce locks to prevent re-entry by multiple
   threads.  The client is not re-entrant because of the state machine
   and multi-packet operations
*/
#define THREAD_SAFE_CLIENT

#define CAFILE "/etc/ssl/certs/ca-bundle.trust.crt"

#ifdef THREAD_SAFE_CLIENT
#define API_LOCK() std::lock_guard<std::mutex> g(_api_lock);
#else
#define API_LOCK()
#endif

namespace mcas
{

namespace client
{

struct TLS_transport
{
  static unsigned debug_level();
  static ssize_t gnutls_pull_func(gnutls_transport_ptr_t, void*, size_t);
  static ssize_t gnutls_vec_push_func(gnutls_transport_ptr_t, const giovec_t * , int );
  static int gnutls_pull_timeout_func(gnutls_transport_ptr_t, unsigned int);
};

struct async_buffer_set_t;
struct iob_free;

static const char *env_scbe = ::getenv("SHORT_CIRCUIT_BACKEND");

/* Adaptor point for other transports */
using Connection_base = mcas::client::Fabric_transport;

/**
 * Client side connection handler
 *
 */
class Connection_handler : public Connection_base {
  using iob_ptr        = std::unique_ptr<client::Fabric_transport::buffer_t, iob_free>;
  using locate_element = protocol::Message_IO_response::locate_element;

public:
  friend struct TLS_transport;

  using memory_region_t = typename Transport::memory_region_t;
  template <typename T>
    using basic_string_view = common::basic_string_view<T>;
  using byte = common::byte;

  /**
   * Constructor
   *
   * @param debug_level
   * @param connection
   * @param patience Time to wait (in seconds) for single fabric post to complete
   * @param other Additional configuration string
   *
   * @return
   */
  Connection_handler(const unsigned debug_level,
                     Connection_base::Transport *connection,
                     const unsigned patience,
                     const std::string other);

  ~Connection_handler();

private:
  enum State {
              INITIALIZE,
              HANDSHAKE_SEND,
              HANDSHAKE_GET_RESPONSE,
              SHUTDOWN,
              STOPPED,
              READY,
  };

  State _state = State::INITIALIZE;

  template <typename MT>
    status_t invoke_ado_common(
      const iob_ptr & iobs
      , const MT *msg
      , std::vector<component::IMCAS::ADO_response>& out_response
      , unsigned int flags
    );

public:
  using pool_t = uint64_t;

  void bootstrap()
  {
    set_state(INITIALIZE);
    while (tick() > 0)
      ;
  }

  void shutdown()
  {
    set_state(SHUTDOWN);
    while (tick() > 0) sleep(1);
  }

  pool_t open_pool(const std::string name,
                   const unsigned int flags,
                   const addr_t base);

  pool_t create_pool(const std::string  name,
                     const size_t       size,
                     const unsigned int flags,
                     const uint64_t     expected_obj_count,
                     const addr_t       base);

  status_t close_pool(const pool_t pool);

  status_t delete_pool(const std::string &name);

  status_t delete_pool(const pool_t pool);

  status_t configure_pool(const component::IKVStore::pool_t pool, const std::string &json);

  status_t put(const pool_t       pool,
               const std::string  key,
               const void *       value,
               const size_t       value_len,
               const unsigned int flags);

  status_t put(const pool_t       pool,
               const void *       key,
               const size_t       key_len,
               const void *       value,
               const size_t       value_len,
               const unsigned int flags);

  status_t put_direct(pool_t                               pool,
                      const void *                         key,
                      size_t                               key_len,
                      const void *                         value,
                      size_t                               value_len,
                      component::Registrar_memory_direct * rmd,
                      component::IKVStore::memory_handle_t handle,
                      unsigned int                         flags);

  status_t async_put(const pool_t                      pool,
                     const void *                      key,
                     const size_t                      key_len,
                     const void *                      value,
                     size_t                            value_len,
                     component::IMCAS::async_handle_t &out_handle,
                     unsigned int                      flags);

  status_t async_put_direct(const component::IMCAS::pool_t       pool,
                            const void *                         key,
                            size_t                               key_len,
                            const void *                         value,
                            size_t                               value_len,
                            component::IMCAS::async_handle_t &   out_handle,
                            component::Registrar_memory_direct * rmd,
                            component::IKVStore::memory_handle_t handle = component::IMCAS::MEMORY_HANDLE_NONE,
                            unsigned int                         flags  = component::IMCAS::FLAGS_NONE);

  status_t async_get_direct(const component::IMCAS::pool_t       pool,
                            const void *                         key,
                            size_t                               key_len,
                            void *                               value,
                            size_t &                             value_len,
                            component::IMCAS::async_handle_t &   out_handle,
                            component::Registrar_memory_direct * rmd,
                            component::IKVStore::memory_handle_t handle = component::IMCAS::MEMORY_HANDLE_NONE,
                            unsigned int                         flags  = component::IMCAS::FLAGS_NONE);

  status_t check_async_completion(component::IMCAS::async_handle_t &handle);

  status_t get(const pool_t pool, const std::string &key, std::string &value);

  status_t get(const pool_t pool, const std::string &key, void *&value, size_t &value_len);

  status_t get_direct(const pool_t                         pool,
                      const void *                         key,
                      size_t                               key_len,
                      void *                               value,
                      size_t &                             out_value_len,
                      component::Registrar_memory_direct * rmd,
                      component::IKVStore::memory_handle_t handle = component::IKVStore::HANDLE_NONE);

  status_t get_direct_offset(pool_t                              pool,
                             std::size_t                         offset,
                             std::size_t &                       length,
                             void *                              buffer,
                             component::Registrar_memory_direct *rmd,
                             component::IMCAS::memory_handle_t   handle);

  status_t async_get_direct_offset(pool_t                              pool,
                                   std::size_t                         offset,
                                   std::size_t &                       length,
                                   void *                              buffer,
                                   component::IMCAS::async_handle_t &  out_handle,
                                   component::Registrar_memory_direct *rmd,
                                   component::IMCAS::memory_handle_t   handle);

  status_t put_direct_offset(pool_t                              pool,
                             std::size_t                         offset,
                             std::size_t &                       length,
                             const void *                        buffer,
                             component::Registrar_memory_direct *rmd,
                             component::IMCAS::memory_handle_t   handle);

  status_t async_put_direct_offset(pool_t                              pool,
                                   std::size_t                         offset,
                                   std::size_t &                       length,
                                   const void *                        buffer,
                                   component::IMCAS::async_handle_t &  out_handle,
                                   component::Registrar_memory_direct *rmd,
                                   component::IMCAS::memory_handle_t   handle);

  status_t erase(const pool_t pool, const std::string &key);

  status_t async_erase(const component::IMCAS::pool_t    pool,
                       const std::string &               key,
                       component::IMCAS::async_handle_t &out_handle);

  uint64_t key_hash(const void *key, const size_t key_len);

  uint64_t auth_id() const
  {
    /* temporary */
    auto     env = getenv("MCAS_AUTH_ID");
    uint64_t auth_id;
    if (env) {
      auto id = std::strtoll(env, nullptr, 10);
      auth_id = boost::numeric_cast<uint64_t>(id);
    }
    else {
      auth_id = boost::numeric_cast<uint64_t>(getuid());
    }

    return auth_id;
  }

  size_t count(const pool_t pool);

  status_t get_attribute(const component::IKVStore::pool_t    pool,
                         const component::IKVStore::Attribute attr,
                         std::vector<uint64_t> &              out_attr,
                         const std::string *                  key);

  status_t get_statistics(component::IMCAS::Shard_stats &out_stats);

  status_t find(const component::IKVStore::pool_t pool,
                const std::string &               key_expression,
                const offset_t                    offset,
                offset_t &                        out_matched_offset,
                std::string &                     out_matched_key);

  status_t invoke_ado(const component::IMCAS::pool_t               pool,
                      basic_string_view<byte>                      key,
                      basic_string_view<byte>                      request,
                      const unsigned int                           flags,
                      std::vector<component::IMCAS::ADO_response> &out_response,
                      const size_t                                 value_size);

  status_t invoke_ado_async(const component::IMCAS::pool_t               pool,
                            basic_string_view<byte>                      key,
                            basic_string_view<byte>                      request,
                            const component::IMCAS::ado_flags_t          flags,
                            std::vector<component::IMCAS::ADO_response> &out_response,
                            component::IMCAS::async_handle_t &           out_async_handle,
                            const size_t                                 value_size);

  status_t invoke_put_ado(const component::IMCAS::pool_t               pool,
                          basic_string_view<byte>                      key,
                          basic_string_view<byte>                      request,
                          basic_string_view<byte>                      value,
                          size_t                                       root_len,
                          const unsigned int                           flags,
                          std::vector<component::IMCAS::ADO_response> &out_response);

  status_t invoke_put_ado_async(const component::IMCAS::pool_t                  pool,
                                const basic_string_view<byte>                   key,
                                const basic_string_view<byte>                   request,
                                const basic_string_view<byte>                   value,
                                const size_t                                    root_len,
                                const component::IMCAS::ado_flags_t             flags,
                                std::vector<component::IMCAS::ADO_response>&    out_response,
                                component::IMCAS::async_handle_t&               out_async_handle);

  bool check_message_size(size_t size) const { return size > _max_message_size; }

  status_t receive_and_process_ado_response(
    const iob_ptr & iobr
    , std::vector<component::IMCAS::ADO_response> & out_response
  );

private:
  /**
   * FSM tick call
   *
   */
  int tick();

  /**
   * FSM state change
   *
   * @param s State to change to
   */
  inline void set_state(State s)
  {
    if (2 < debug_level()) {
      static const std::map<State, const char *> m{
                                                   {INITIALIZE, "INITIALIZE"},
                                                   {HANDSHAKE_SEND, "HANDSHAKE_SEND"},
                                                   {HANDSHAKE_GET_RESPONSE, "HANDSHAKE_GET_RESPONSE"},
                                                   {SHUTDOWN, "SHUTDOWN"},
                                                   {STOPPED, "STOPPED"},
                                                   {READY, "READY"},
      };
      PLOG("Client state %s -> %s", m.find(_state)->second, m.find(s)->second);
    }
    _state = s;
  } /* we could add transition checking later */

  void start_tls();

  template <typename MT>
  void msg_send_log(const MT *m, const void *context, const char *desc) { msg_send_log(1, m, context, desc); }

  template <typename MT>
  void msg_send_log(const unsigned level, const MT *msg, const void *context, const char *desc)
  {
    msg_log(level, msg, context, desc, "SEND");
  }

  template <typename MT>
  void msg_log(const unsigned level_, const MT *msg_, const void *context_, const char *desc_, const char *direction_)
  {
    if ( level_ < debug_level() )
      {
        std::ostringstream m;
        m << *msg_;
        PLOG("%s (%p) (%s) %s", direction_, context_, desc_, m.str().c_str());
      }
  }

  template <typename MT>
  void msg_recv_log(const MT *m, const void *context, const char *desc) { msg_recv_log(1, m, context, desc); }

  template <typename MT>
  void msg_recv_log(const unsigned level, const MT *msg, const void *context, const char *desc)
  {
    msg_log(level, msg, context, desc, "RECV");
  }

public:

  template <typename MT>
  gsl::not_null<const MT *>msg_recv(const buffer_t *iob, const char *desc)
  {
    /*
     * First, cast the response buffer to Message (checking version).
     * Second, cast the Message to a specific message type (checking message type).
     */
    const auto *const msg = mcas::protocol::message_cast(iob->base());
    const auto *const response_msg = msg->ptr_cast<MT>();
    msg_recv_log(response_msg, iob, desc);
    return response_msg;
  }

  template <typename MT>
  void sync_inject_send(buffer_t *iob, const MT *msg, std::size_t size, const char *desc)
  {
    msg_send_log(msg, iob, desc);
    Connection_base::sync_inject_send(iob, size);
  }

  template <typename MT>
  void sync_inject_send(buffer_t *iob, const MT *msg, const char *desc)
  {
    sync_inject_send(iob, msg, msg->msg_len(), desc);
  }

  template <typename MT>
  void sync_send(buffer_t *iob, const MT *msg, const char *desc)
  {
    msg_send_log(msg, iob, desc);
    Connection_base::sync_send(iob);
  }

  void sync_inject_send(buffer_t *iob, std::size_t size)
  {
    Connection_base::sync_inject_send(iob, size);
  }

private:
  /* unused */
#if 0
  void post_send(buffer_t *iob, const protocol::Message_IO_request *msg, buffer_external *iob_extra, const char *desc)
  {
    msg_send_log(msg, iob, desc);
    Connection_base::post_send(iob, iob_extra);
  }
#endif
  template <typename MT>
  void post_send(buffer_t *iob, const MT *msg, const char *desc)
  {
    msg_send_log(2, msg, iob, desc);
    Connection_base::post_send(iob);
  }

  template <typename MT>
  void post_send(const ::iovec *first, const ::iovec *last, void **descriptors, void *context, const MT *msg, const char *desc)
  {
    msg_send_log(msg, context, desc);
    Connection_base::post_send(first, last, descriptors, context);
  }

  /**
   * Put used when the value exceeds the size of the basic
   * IO buffer (e.g., 2MB).  This version of put will perform
   * a two-stage exchange, advance notice and then value
   *
   * @param pool Pool identifier
   * @param key Key
   * @param key_len Key length
   * @param value_len Value length
   * @param flags ignored?
   *
   * @return
   */
  std::tuple<uint64_t, uint64_t> put_locate(const pool_t   pool,
                                            const void *   key,
                                            const size_t   key_len,
                                            const size_t   value_len,
                                            const unsigned flags);

  component::IMCAS::async_handle_t put_locate_async(pool_t                              pool,
                                                    const void *                        key,
                                                    size_t                              key_len,
                                                    const void *                        value,
                                                    size_t                              value_len,
                                                    component::Registrar_memory_direct *rmd,
                                                    void *                              desc,
                                                    unsigned                            flags);

  std::tuple<uint64_t, uint64_t, std::size_t> get_locate(const pool_t   pool,
                                                         const void *   key,
                                                         const size_t   key_len,
                                                         const unsigned flags);

  component::IMCAS::async_handle_t get_locate_async(pool_t                              pool,
                                                    const void *                        key,
                                                    size_t                              key_len,
                                                    void *                              value,
                                                    size_t &                            value_len,
                                                    component::Registrar_memory_direct *rmd,
                                                    void *                              desc,
                                                    unsigned                            flags);
public:
  iob_ptr make_iob_ptr(buffer_t::completion_t);
  iob_ptr make_iob_ptr_recv();
  iob_ptr make_iob_ptr_send();
  iob_ptr make_iob_ptr_write();
  iob_ptr make_iob_ptr_read();

private:
  static void send_complete(void *, buffer_t *iob);
  static void recv_complete(void *, buffer_t *iob);
  static void write_complete(void *, buffer_t *iob);
  static void read_complete(void *, buffer_t *iob);

  std::tuple<uint64_t, std::vector<locate_element>> locate(pool_t pool, std::size_t offset, std::size_t size);

  component::IMCAS::async_handle_t get_direct_offset_async(pool_t                              pool,
                                                           std::size_t                         offset,
                                                           void *                              buffer,
                                                           std::size_t &                       length,
                                                           component::Registrar_memory_direct *rmd,
                                                           void *                              desc);

  component::IMCAS::async_handle_t put_direct_offset_async(pool_t                              pool,
                                                           std::size_t                         offset,
                                                           const void *                        buffer,
                                                           std::size_t &                       length,
                                                           component::Registrar_memory_direct *rmd,
                                                           void *                              desc);

private:
#ifdef THREAD_SAFE_CLIENT
  std::mutex _api_lock;
#endif

  bool     _exit;
  uint64_t _request_id;

public: /* for async "move_along" processing */
  uint64_t request_id() { return ++_request_id; }

private:
  size_t _max_message_size;
  size_t _max_inject_size;

  struct options_s {
    bool short_circuit_backend;
    unsigned tls   : 1;
    unsigned hmac : 1;

    options_s()
      : short_circuit_backend(env_scbe && env_scbe[0] == '1'), tls(0), hmac(0)
    {}
  };

  options_s _options;

  gnutls_certificate_credentials_t _xcred;
  gnutls_priority_t                _priority;
  gnutls_session_t                 _session;
  common::Byte_buffer              _tls_buffer;

};

}  // namespace client
}  // namespace mcas

#pragma GCC diagnostic pop
#endif


/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
 *     Copyright 2017 Couchbase, 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 <mcbp/protocol/opcode.h>
#include <mcbp/protocol/request.h>
#include <mcbp/protocol/response.h>
#include <mcbp/protocol/status.h>

#include <gsl/gsl>

namespace cb {
namespace mcbp {

/**
 * FrameBuilder allows you to build up a request / response frame in
 * a provided memory area. It provides helper methods which makes sure
 * that the fields is formatted in the correct byte order etc.
 */
template <typename T>
class FrameBuilder {
public:
    FrameBuilder(cb::byte_buffer backing) : buffer(backing) {
        checkSize(sizeof(T));
        std::fill(backing.begin(), backing.begin() + sizeof(T), 0);
    }

    T* getFrame() {
        return reinterpret_cast<T*>(buffer.data());
    }

    void setMagic(Magic magic) {
        getFrame()->setMagic(magic);
    }

    void setOpcode(ClientOpcode opcode) {
        getFrame()->setOpcode(opcode);
    }

    void setOpcode(ServerOpcode opcode) {
        getFrame()->setOpcode(opcode);
    }

    void setDatatype(Datatype datatype) {
        getFrame()->setDatatype(datatype);
    }

    void setVBucket(Vbid value) {
        getFrame()->setVBucket(value);
    }

    void setOpaque(uint32_t opaque) {
        getFrame()->setOpaque(opaque);
    }

    void setCas(uint64_t val) {
        getFrame()->setCas(val);
    }

    /**
     * Insert the provided extras in the appropriate location (right after
     * the header) and updates the extlen and bodylen field.
     *
     * @param extras the extra field to insert
     */
    void setExtras(cb::const_byte_buffer extras) {
        auto* req = getFrame();

        // can we fit the extras?
        checkSize(req->getExtlen(), extras.size());

        // Start by moving key and body to where it belongs
        const auto old_body_size = req->getBodylen() - req->getExtlen();
        auto* start = buffer.data() + sizeof(*req);
        uint8_t* old_start = start + req->getExtlen();
        uint8_t* new_start = start + extras.size();
        memmove(new_start, old_start, old_body_size);

        // Insert the new extras:
        std::copy(extras.begin(), extras.end(), start);
        req->setExtlen(uint8_t(extras.size()));
        req->setBodylen(gsl::narrow<uint32_t>(old_body_size + extras.size()));
    }

    /**
     * Insert the provided key in the appropriate location (right after
     * the extras field) and updates the keylen and bodylen field.
     *
     * @param key the key field to insert
     */
    void setKey(cb::const_byte_buffer key) {
        auto* req = getFrame();
        checkSize(req->getKeylen(), key.size());

        // Start by moving the body to where it belongs
        const auto old_body_size =
                req->getBodylen() - req->getKeylen() - req->getExtlen();
        auto* start = buffer.data() + sizeof(*req) + req->getExtlen();
        uint8_t* old_start = start + req->getKeylen();
        uint8_t* new_start = start + key.size();
        memmove(new_start, old_start, old_body_size);

        // Insert the new key:
        std::copy(key.begin(), key.end(), start);
        req->setKeylen(uint16_t(key.size()));
        req->setBodylen(gsl::narrow<uint32_t>(old_body_size + req->getExtlen() +
                                              key.size()));
    }

    /**
     * Insert the provided value in the appropriate location (right after
     * the key field) and update the bodylen field.
     *
     * @param value the value to insert into the body
     */
    void setValue(cb::const_byte_buffer value) {
        auto* req = getFrame();

        const auto old_size =
                req->getBodylen() - req->getKeylen() - req->getExtlen();
        checkSize(old_size, value.size());

        auto* start = buffer.data() + sizeof(*req) + req->getExtlen() +
                      req->getKeylen();
        std::copy(value.begin(), value.end(), start);
        req->setBodylen(gsl::narrow<uint32_t>(value.size() + req->getKeylen() +
                                              req->getExtlen()));
    }

    /**
     * Try to validate the underlying packet
     */
    void validate() {
        getFrame()->isValid();
    }

protected:
    /**
     * check that the requested size fits into the buffer
     *
     * @param size The total requested size (including header)
     */
    void checkSize(size_t size) {
        if (size > buffer.size()) {
            throw std::logic_error(
                    "FrameBuilder::checkSize: too big to fit in buffer");
        }
    }

    /**
     * check if there is room in the buffer to replace the field with
     * one with a different size
     *
     * @param oldfield
     * @param newfield
     */
    void checkSize(size_t oldfield, size_t newfield) {
        checkSize(sizeof(T) + getFrame()->getBodylen() - oldfield + newfield);
    }

    cb::byte_buffer buffer;
};

/**
 * Specialized class to build a Request
 */
class RequestBuilder : public FrameBuilder<Request> {
public:
    RequestBuilder(cb::byte_buffer backing) : FrameBuilder<Request>(backing) {
    }
    void setVbucket(Vbid vbucket) {
        getFrame()->setVBucket(vbucket);
    }
};

/**
 * Specialized class to build a response
 */
class ResponseBuilder : public FrameBuilder<Response> {
public:
    ResponseBuilder(cb::byte_buffer backing) : FrameBuilder<Response>(backing) {
    }
    void setStatus(Status status) {
        getFrame()->setStatus(status);
    }
};

} // namespace mcbp
} // namespace cb


#ifndef __GSL_BLOCK_H__
#define __GSL_BLOCK_H__

#include <gsl/block/gsl_block_double.h>

#endif /* __GSL_BLOCK_H__ */


#pragma once

#include <gsl/gsl>

#include <mutex>
#include <condition_variable>

#include <arcana/threading/affinity.h>

namespace Babylon
{
    class SafeTimespanGuarantor
    {
    public:
        SafeTimespanGuarantor();

        void BeginSafeTimespan();
        void EndSafeTimespan();

        using SafetyGuarantee = gsl::final_action<std::function<void()>>;
        SafetyGuarantee GetSafetyGuarantee();

    private:
        arcana::affinity m_affinity{};
        uint32_t m_count{};
        std::mutex m_mutex{};
        std::unique_lock<std::mutex> m_lock{};
        std::condition_variable m_condition{};
    };
}


// Copyright 2021 atframework
// Created by owent on

#pragma once

#include <stdint.h>
#include <cstddef>
#include <cstring>
#include <memory>
#include <string>
#include <type_traits>

#include <config/compiler/protobuf_prefix.h>

#include <rapidjson/document.h>

#include <config/compiler/protobuf_suffix.h>

#include <gsl/select-gsl.h>

#include <log/log_wrapper.h>

#include <libatbus.h>

#include "atframe/atapp_config.h"

namespace google {
namespace protobuf {
class Message;
class Timestamp;
class Duration;
}  // namespace protobuf
}  // namespace google

namespace atapp {

struct LIBATAPP_MACRO_API_HEAD_ONLY rapidsjon_loader_string_mode {
  enum type {
    RAW = 0,
    URI,
    URI_COMPONENT,
  };
};

struct LIBATAPP_MACRO_API_HEAD_ONLY rapidsjon_loader_load_options {
  bool reserve_empty;
  bool convert_large_number_to_string;  // it's friendly to JSON.parse(...) in javascript
  rapidsjon_loader_string_mode::type string_mode;

  inline rapidsjon_loader_load_options()
      : reserve_empty(false), convert_large_number_to_string(false), string_mode(rapidsjon_loader_string_mode::RAW) {}
};

struct LIBATAPP_MACRO_API_HEAD_ONLY rapidsjon_loader_dump_options {
  rapidsjon_loader_string_mode::type string_mode;
  bool convert_number_from_string;  // it's friendly to JSON.parse(...) in javascript
  inline rapidsjon_loader_dump_options()
      : string_mode(rapidsjon_loader_string_mode::RAW), convert_number_from_string(true) {}
};

LIBATAPP_MACRO_API std::string rapidsjon_loader_stringify(const rapidjson::Document &doc, size_t more_reserve_size = 0);
LIBATAPP_MACRO_API bool rapidsjon_loader_unstringify(rapidjson::Document &doc, const std::string &json);
LIBATAPP_MACRO_API const char *rapidsjon_loader_get_type_name(rapidjson::Type t);

LIBATAPP_MACRO_API std::string rapidsjon_loader_stringify(
    const ATBUS_MACRO_PROTOBUF_NAMESPACE_ID::Message &src,
    const rapidsjon_loader_load_options &options = rapidsjon_loader_load_options());
LIBATAPP_MACRO_API bool rapidsjon_loader_parse(
    ATBUS_MACRO_PROTOBUF_NAMESPACE_ID::Message &dst, const std::string &src,
    const rapidsjon_loader_dump_options &options = rapidsjon_loader_dump_options());

LIBATAPP_MACRO_API void rapidsjon_loader_mutable_set_member(rapidjson::Value &parent, gsl::string_view key,
                                                            rapidjson::Value &&val, rapidjson::Document &doc);
LIBATAPP_MACRO_API void rapidsjon_loader_mutable_set_member(rapidjson::Value &parent, gsl::string_view key,
                                                            const rapidjson::Value &val, rapidjson::Document &doc);
LIBATAPP_MACRO_API void rapidsjon_loader_mutable_set_member(rapidjson::Value &parent, gsl::string_view key,
                                                            gsl::string_view val, rapidjson::Document &doc);

LIBATAPP_MACRO_API void rapidsjon_loader_append_to_list(rapidjson::Value &list_parent, gsl::string_view val,
                                                        rapidjson::Document &doc);

LIBATAPP_MACRO_API void rapidsjon_loader_dump_to(
    const rapidjson::Document &src, ATBUS_MACRO_PROTOBUF_NAMESPACE_ID::Message &dst,
    const rapidsjon_loader_dump_options &options = rapidsjon_loader_dump_options());

LIBATAPP_MACRO_API void rapidsjon_loader_load_from(
    rapidjson::Document &dst, const ATBUS_MACRO_PROTOBUF_NAMESPACE_ID::Message &src,
    const rapidsjon_loader_load_options &options = rapidsjon_loader_load_options());

LIBATAPP_MACRO_API void rapidsjon_loader_dump_to(
    const rapidjson::Value &src, ATBUS_MACRO_PROTOBUF_NAMESPACE_ID::Message &dst,
    const rapidsjon_loader_dump_options &options = rapidsjon_loader_dump_options());

LIBATAPP_MACRO_API void rapidsjon_loader_load_from(
    rapidjson::Value &dst, rapidjson::Document &doc, const ATBUS_MACRO_PROTOBUF_NAMESPACE_ID::Message &src,
    const rapidsjon_loader_load_options &options = rapidsjon_loader_load_options());

// ============ template implement ============

template <class TVAL, bool>
struct rapidsjon_loader_mutable_member_helper;

template <class TVAL>
struct rapidsjon_loader_mutable_member_helper<TVAL, true> {
  static LIBATAPP_MACRO_API_HEAD_ONLY inline void set(rapidjson::Value &parent, gsl::string_view key, TVAL &&val,
                                                      rapidjson::Document &doc) {
    rapidsjon_loader_mutable_set_member(parent, key, gsl::string_view(std::forward<TVAL>(val)), doc);
  }

  static LIBATAPP_MACRO_API_HEAD_ONLY inline void append(rapidjson::Value &parent, TVAL &&val,
                                                         rapidjson::Document &doc) {
    rapidsjon_loader_append_to_list(parent, gsl::string_view(std::forward<TVAL>(val)), doc);
  }
};

template <class TVAL>
struct rapidsjon_loader_mutable_member_helper<TVAL, false> {
  static LIBATAPP_MACRO_API_HEAD_ONLY inline void set(rapidjson::Value &parent, gsl::string_view key, TVAL &&val,
                                                      rapidjson::Document &doc) {
    if (parent.IsNull()) {
      parent.SetObject();
    }

    if (!parent.IsObject()) {
      FWLOGERROR("parent should be a object, but we got {}.", rapidsjon_loader_get_type_name(parent.GetType()));
      return;
    }

    rapidjson::Value jkey;
    jkey.SetString(rapidjson::StringRef(key.data(), static_cast<rapidjson::SizeType>(key.size())));
    rapidjson::Value::MemberIterator iter = parent.FindMember(jkey);
    if (iter != parent.MemberEnd()) {
      iter->value.Set(std::forward<TVAL>(val), doc.GetAllocator());
    } else {
      rapidjson::Value k;
      k.SetString(key.data(), static_cast<rapidjson::SizeType>(key.size()), doc.GetAllocator());
      parent.AddMember(k, std::forward<TVAL>(val), doc.GetAllocator());
    }
  }

  static LIBATAPP_MACRO_API_HEAD_ONLY inline void append(rapidjson::Value &parent, TVAL &&val,
                                                         rapidjson::Document &doc) {
    if (parent.IsNull()) {
      parent.SetArray();
    }

    if (!parent.IsArray()) {
      FWLOGERROR("parent should be a array, but we got {}.", rapidsjon_loader_get_type_name(parent.GetType()));
      return;
    }

    parent.PushBack(std::forward<TVAL>(val), doc.GetAllocator());
  }
};

template <class TVAL, class = typename std::enable_if<
                          !std::is_same<typename std::decay<TVAL>::type, gsl::string_view>::value>::type>
LIBATAPP_MACRO_API_HEAD_ONLY void rapidsjon_loader_mutable_set_member(rapidjson::Value &parent, gsl::string_view key,
                                                                      TVAL &&val, rapidjson::Document &doc) {
  rapidsjon_loader_mutable_member_helper<TVAL, std::is_convertible<TVAL, gsl::string_view>::value>::set(
      parent, key, std::forward<TVAL>(val), doc);
}

template <class TVAL, class = typename std::enable_if<
                          !std::is_same<typename std::decay<TVAL>::type, gsl::string_view>::value>::type>
LIBATAPP_MACRO_API_HEAD_ONLY void rapidsjon_loader_append_to_list(rapidjson::Value &parent, TVAL &&val,
                                                                  rapidjson::Document &doc) {
  rapidsjon_loader_mutable_member_helper<TVAL, std::is_convertible<TVAL, gsl::string_view>::value>::append(
      parent, std::forward<TVAL>(val), doc);
}
}  // namespace atapp


#pragma once
#include "parser.h"
#include "format.h"
#include "nameutils.h"
#include "utils.h"
#include <sfun/string_utils.h>
#include <cmdlime/errors.h>
#include <gsl/gsl>
#include <algorithm>
#include <sstream>
#include <iomanip>
#include <functional>
#include <optional>

namespace cmdlime::detail{
namespace str = sfun::string_utils;

template <FormatType formatType>
class GNUParser : public Parser<formatType>
{
    using Parser<formatType>::Parser;
    using FindMode = typename Parser<formatType>::FindMode;

    void preProcess() override
    {
        checkNames();
        foundParam_.clear();
        foundParamPrefix_.clear();
    }

    void process(const std::string& token) override
    {
        if (str::startsWith(token, "--") && token.size() > 2)
            processCommand(token);
        else if (str::startsWith(token, "-") && token.size() > 1)
            processShortCommand(token);
        else if (!foundParam_.empty()){
            this->readParam(foundParam_, token);
            foundParam_.clear();
        }
        else
            this->readArg(token);
    }

    void postProcess() override
    {
        if (!foundParam_.empty())
            throw ParsingError{"Parameter '" + foundParamPrefix_ + foundParam_ + "' value can't be empty"};
    }

    void processCommand(std::string command)
    {
        command = str::after(command, "--");
        auto paramValue = std::optional<std::string>{};
        if (command.find('=') != std::string::npos){
            paramValue = str::after(command, "=");
            command = str::before(command, "=");
        }
        if (isParamOrFlag(command) &&
            !foundParam_.empty() &&
            this->readMode_ != Parser<formatType>::ReadMode::ExitFlagsAndCommands)
            throw ParsingError{"Parameter '" + foundParamPrefix_ + foundParam_ + "' value can't be empty"};
        if (this->findParam(command, FindMode::Name) || this->findParamList(command, FindMode::Name)){
            if (paramValue.has_value())
                this->readParam(command, paramValue.value());
            else{
                foundParam_ = command;
                foundParamPrefix_ = "--";
            }
        }
        else if (this->findFlag(command, FindMode::Name))
            this->readFlag(command);
        else if (this->readMode_ != Parser<formatType>::ReadMode::ExitFlagsAndCommands)
            throw ParsingError{"Encountered unknown parameter or flag '--" + command + "'"};
    }

    void processShortCommand(std::string command)
    {
        auto possibleNumberArg = command;
        command = str::after(command, "-");
        if (isShortParamOrFlag(command)){
            if (!foundParam_.empty() && this->readMode_ != Parser<formatType>::ReadMode::ExitFlagsAndCommands)
                throw ParsingError{"Parameter '" + foundParamPrefix_ + foundParam_ + "' value can't be empty"};
            parseShortCommand(command);
        }
        else if (isNumber(possibleNumberArg))
            this->readArg(possibleNumberArg);
        else if (this->readMode_ != Parser<formatType>::ReadMode::ExitFlagsAndCommands)
            throw ParsingError{"Encountered unknown parameter or flag '-" + command + "'"};
    }

    void parseShortCommand(const std::string& command)
    {
        if (command.empty())
            throw ParsingError{"Flags and parameters must have a name"};
        auto paramValue = std::string{};
        for(auto ch : command){
            auto opt = std::string{ch};
            if (!foundParam_.empty())
                paramValue += opt;
            else if (this->findFlag(opt, FindMode::ShortName))
                this->readFlag(opt);
            else if (this->findParam(opt, FindMode::ShortName)){
                foundParam_ = opt;
                foundParamPrefix_ = "-";
            }
            else if (this->findParamList(opt, FindMode::ShortName)){
                foundParam_ = opt;
                foundParamPrefix_ = "-";
            }
            else if (this->readMode_ != Parser<formatType>::ReadMode::ExitFlagsAndCommands)
                throw ParsingError{"Unknown option '" + opt + "' in command '-" + command + "'"};
        }
        if (!foundParam_.empty() && !paramValue.empty()){
            this->readParam(foundParam_, paramValue);
            foundParam_.clear();
        }
    }

    void checkLongNames()
    {
        auto check = [](const OptionInfo& var, const std::string& varType){
            if (!std::isalpha(var.name().front()))
                throw ConfigError{varType + "'s name '" + var.name() + "' must start with an alphabet character"};
            if (var.name().size() > 1){
                auto nonSupportedCharIt = std::find_if(var.name().begin() + 1, var.name().end(), [](char ch){return !std::isalnum(ch) && ch != '-';});
                if (nonSupportedCharIt != var.name().end())
                    throw ConfigError{varType + "'s name '" + var.name() + "' must consist of alphanumeric characters and hyphens"};
            }
        };
        this->forEachParamInfo([check](const OptionInfo& var){
            check(var, "Parameter");
        });
        this->forEachParamListInfo([check](const OptionInfo& var){
            check(var, "Parameter");
        });
        this->forEachFlagInfo([check](const OptionInfo& var){
            check(var, "Flag");
        });
    }

    void checkShortNames()
    {
        auto check = [](const OptionInfo& var, const std::string& varType){
            if (var.shortName().empty())
                return;
            if (var.shortName().size() != 1)
                throw ConfigError{varType + "'s short name '" + var.shortName() + "' can't have more than one symbol"};
            if (!std::isalnum(var.shortName().front()))
                throw ConfigError{varType + "'s short name '" + var.shortName() + "' must be an alphanumeric character"};
        };
        this->forEachParamInfo([check](const OptionInfo& var){
            check(var, "Parameter");
        });
        this->forEachParamListInfo([check](const OptionInfo& var){
            check(var, "Parameter");
        });
        this->forEachFlagInfo([check](const OptionInfo& var){
            check(var, "Flag");
        });
    }

    void checkNames()
    {
        checkLongNames();
        checkShortNames();
    }

    bool isParamOrFlag(const std::string& str)
    {
        if (str.empty())
            return false;
        return this->findFlag(str, FindMode::Name) ||
               this->findParam(str, FindMode::Name) ||
               this->findParamList(str, FindMode::Name);
    }

    bool isShortParamOrFlag(const std::string& str)
    {
        if (str.empty())
            return false;
        auto opt = str.substr(0,1);
        return this->findFlag(opt, FindMode::ShortName) ||
               this->findParam(opt, FindMode::ShortName) ||
               this->findParamList(opt, FindMode::ShortName);
    }

private:
    std::string foundParam_;
    std::string foundParamPrefix_;
};

class GNUNameProvider{
public:
    static std::string name(const std::string& optionName)
    {
        Expects(!optionName.empty());
        return toKebabCase(optionName);
    }

    static std::string shortName(const std::string& optionName)
    {
        Expects(!optionName.empty());
        return toLowerCase(optionName.substr(0, 1));
    }

    static std::string fullName(const std::string& optionName)
    {
        Expects(!optionName.empty());
        return toKebabCase(optionName);
    }

    static std::string valueName(const std::string& typeName)
    {
        Expects(!typeName.empty());
        return toKebabCase(templateType(typeNameWithoutNamespace(typeName)));
    }
};

class GNUOutputFormatter{
public:
    static std::string paramUsageName(const IParam& param)
    {
        auto stream = std::stringstream{};
        if (param.isOptional())
            stream << "[" << paramPrefix() << param.info().name() << " <" << param.info().valueName() << ">]";
        else
            stream << paramPrefix() << param.info().name() << " <" << param.info().valueName() << ">";
        return stream.str();
    }

    static std::string paramListUsageName(const IParamList& param)
    {
        auto stream = std::stringstream{};
        if (param.isOptional())
            stream << "[" << paramPrefix() << param.info().name() << " <" << param.info().valueName() << ">...]";
        else
            stream << paramPrefix() << param.info().name() << " <" << param.info().valueName() << ">...";
        return stream.str();
    }

    static std::string paramDescriptionName(const IParam& param, int indent = 0)
    {
        auto stream = std::stringstream{};
        stream << std::setw(indent);
        if (!param.info().shortName().empty())
            stream << "-" << param.info().shortName() << ", ";
        else
            stream << " " << "   ";
        stream << "--" << param.info().name() << " <" << param.info().valueName() << ">";
        return stream.str();
    }

    static std::string paramListDescriptionName(const IParamList& param, int indent = 0)
    {
        auto stream = std::stringstream{};
        stream << std::setw(indent);
        if (!param.info().shortName().empty())
            stream << "-" << param.info().shortName() << ", ";
        else
            stream << " " << "   ";
        stream << "--" << param.info().name() << " <" << param.info().valueName() << ">";
        return stream.str();
    }

    static std::string paramPrefix()
    {
        return "--";
    }

    static std::string flagUsageName(const IFlag& flag)
    {
        auto stream = std::stringstream{};
        stream << "[" << flagPrefix() << flag.info().name() << "]";
        return stream.str();
    }

    static std::string flagDescriptionName(const IFlag& flag, int indent = 0)
    {
        auto stream = std::stringstream{};
        stream << std::setw(indent) ;
        if (!flag.info().shortName().empty())
            stream << "-" << flag.info().shortName() << ", ";
        else
            stream << " " << "   ";
        stream << "--" << flag.info().name();
        return stream.str();
    }

    static std::string flagPrefix()
    {
        return "--";
    }

    static std::string argUsageName(const IArg& arg)
    {
        auto stream = std::stringstream{};
        stream << "<" << arg.info().name() << ">";
        return stream.str();
    }

    static std::string argDescriptionName(const IArg& arg, int indent = 0)
    {
        auto stream = std::stringstream{};
        if (indent)
            stream << std::setw(indent) << " ";
        stream << "<" << arg.info().name() << "> (" << arg.info().valueName() << ")";
        return stream.str();
    }

    static std::string argListUsageName(const IArgList& argList)
    {
        auto stream = std::stringstream{};
        if (argList.isOptional())
            stream << "[" << argList.info().name() << "...]";
        else
            stream << "<" << argList.info().name() << "...>";
        return stream.str();
    }

    static std::string argListDescriptionName(const IArgList& argList, int indent = 0)
    {
        auto stream = std::stringstream{};
        if (indent)
            stream << std::setw(indent) << " ";
        stream << "<" << argList.info().name() << "> (" << argList.info().valueName() << ")";
        return stream.str();
    }

};

template<>
struct Format<FormatType::GNU>
{
    using parser = GNUParser<FormatType::GNU>;
    using nameProvider = GNUNameProvider;
    using outputFormatter = GNUOutputFormatter;
    static constexpr bool shortNamesEnabled = true;
};

}


/**
 *  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 interface for Ledger
 * @file LedgerInterface.h
 * @author: kyonRay
 * @date: 2021-04-07
 */

#pragma once

#include "../../interfaces/crypto/CommonType.h"
#include "../../interfaces/protocol/Block.h"
#include "../../interfaces/protocol/BlockHeader.h"
#include "../../interfaces/protocol/Transaction.h"
#include "../../interfaces/protocol/TransactionReceipt.h"
#include "../../interfaces/storage/StorageInterface.h"
#include "LedgerConfig.h"
#include "LedgerTypeDef.h"
#include "bcos-utilities/Error.h"
#include <gsl/span>
#include <map>

namespace bcos::ledger
{
class LedgerInterface
{
public:
    using Ptr = std::shared_ptr<LedgerInterface>;
    LedgerInterface() = default;
    virtual ~LedgerInterface() {}

    /**
     * @brief async prewrite a block in scheduler module
     * @param block the block to commit
     * @param callback trigger this callback when write is finished
     */
    virtual void asyncPrewriteBlock(bcos::storage::StorageInterface::Ptr storage,
        bcos::protocol::Block::ConstPtr block, std::function<void(Error::Ptr&&)> callback) = 0;

    /**
     * @brief async store txs in block when tx pool verify
     * @param _txToStore tx bytes data list
     * @param _txHashList tx hash list
     * @param _onTxsStored callback
     */
    virtual void asyncStoreTransactions(std::shared_ptr<std::vector<bytesConstPtr>> _txToStore,
        crypto::HashListPtr _txHashList, std::function<void(Error::Ptr)> _onTxStored) = 0;

    /**
     * @brief async get block by blockNumber
     * @param _blockNumber number of block
     * @param _blockFlag flag bit of what the block be callback contains,
     *                   you can checkout all flags in LedgerTypeDef.h
     * @param _onGetBlock
     *
     * @example
     * asyncGetBlockDataByNumber(10, HEADER|TRANSACTIONS, [](error, block){ doSomething(); });
     */
    virtual void asyncGetBlockDataByNumber(protocol::BlockNumber _blockNumber, int32_t _blockFlag,
        std::function<void(Error::Ptr, protocol::Block::Ptr)> _onGetBlock) = 0;

    /**
     * @brief async get latest block number
     * @param _onGetBlock
     */
    virtual void asyncGetBlockNumber(
        std::function<void(Error::Ptr, protocol::BlockNumber)> _onGetBlock) = 0;

    /**
     * @brief async get block hash by block number
     * @param _blockNumber the number of block to get
     * @param _onGetBlock
     */
    virtual void asyncGetBlockHashByNumber(protocol::BlockNumber _blockNumber,
        std::function<void(Error::Ptr, crypto::HashType)> _onGetBlock) = 0;

    /**
     * @brief async get block number by block hash
     * @param _blockHash the hash of block to get
     * @param _onGetBlock
     */
    virtual void asyncGetBlockNumberByHash(crypto::HashType const& _blockHash,
        std::function<void(Error::Ptr, protocol::BlockNumber)> _onGetBlock) = 0;

    /**
     * @brief async get a batch of transaction by transaction hash list
     * @param _txHashList transaction hash list, hash should be hex
     * @param _withProof if true then it will callback MerkleProofPtr map in _onGetTx
     *                   if false then MerkleProofPtr map will be nullptr
     * @param _onGetTx return <error, [tx data in bytes], map<txHash, merkleProof>
     */
    virtual void asyncGetBatchTxsByHashList(crypto::HashListPtr _txHashList, bool _withProof,
        std::function<void(Error::Ptr, bcos::protocol::TransactionsPtr,
            std::shared_ptr<std::map<std::string, MerkleProofPtr>>)>
            _onGetTx) = 0;

    /**
     * @brief async get a transaction receipt by tx hash
     * @param _txHash hash of transaction
     * @param _withProof if true then it will callback MerkleProofPtr in _onGetTx
     *                   if false then MerkleProofPtr will be nullptr
     * @param _onGetTx
     */
    virtual void asyncGetTransactionReceiptByHash(crypto::HashType const& _txHash, bool _withProof,
        std::function<void(Error::Ptr, protocol::TransactionReceipt::ConstPtr, MerkleProofPtr)>
            _onGetTx) = 0;

    /**
     * @brief async get total transaction count and latest block number
     * @param _callback callback totalTxCount, totalFailedTxCount, and latest block number
     */
    virtual void asyncGetTotalTransactionCount(std::function<void(Error::Ptr, int64_t _totalTxCount,
            int64_t _failedTxCount, protocol::BlockNumber _latestBlockNumber)>
            _callback) = 0;

    /**
     * @brief async get system config by table key
     * @param _key the key of row, you can checkout all key in LedgerTypeDef.h
     * @param _onGetConfig callback when get config, <value, latest block number>
     */
    virtual void asyncGetSystemConfigByKey(std::string const& _key,
        std::function<void(Error::Ptr, std::string, protocol::BlockNumber)> _onGetConfig) = 0;

    /**
     * @brief async get node list by type, can be sealer or observer
     * @param _type the type of node, CONSENSUS_SEALER or CONSENSUS_OBSERVER
     * @param _onGetConfig
     */
    virtual void asyncGetNodeListByType(std::string const& _type,
        std::function<void(Error::Ptr, consensus::ConsensusNodeListPtr)> _onGetConfig) = 0;

    /**
     * @brief async get a batch of nonce lists in blocks
     * @param _startNumber start block number
     * @param _offset batch offset, if batch is 0, then callback nonce list in start block number;
     * if (_startNumber + _offset) > latest block number, then callback nonce lists in
     * [_startNumber, latest number]
     * @param _onGetList
     */
    virtual void asyncGetNonceList(protocol::BlockNumber _startNumber, int64_t _offset,
        std::function<void(
            Error::Ptr, std::shared_ptr<std::map<protocol::BlockNumber, protocol::NonceListPtr>>)>
            _onGetList) = 0;
};
}  // namespace bcos::ledger


/* -----------------------------------------------------------------------------
 * Copyright 2021 Jonathan Haigh
 * SPDX-License-Identifier: MIT
 * ---------------------------------------------------------------------------*/

#ifndef SQ_INCLUDE_GUARD_system_linux_SqFieldSchemaImpl_h_
#define SQ_INCLUDE_GUARD_system_linux_SqFieldSchemaImpl_h_

#include "core/typeutil.h"
#include "system/SqFieldSchema.gen.h"
#include "system/schema.h"

#include <gsl/gsl>

namespace sq::system::linux {

class SqFieldSchemaImpl : public SqFieldSchema<SqFieldSchemaImpl> {
public:
  explicit SqFieldSchemaImpl(const FieldSchema &field_schema);

  SQ_ND Result get_name() const;
  SQ_ND Result get_doc() const;
  SQ_ND Result get_params() const;
  SQ_ND Result get_return_type() const;
  SQ_ND Result get_return_list() const;
  SQ_ND Result get_null() const;

  SQ_ND Primitive to_primitive() const override;

private:
  gsl::not_null<const FieldSchema *> field_schema_;
};

} // namespace sq::system::linux

#endif // SQ_INCLUDE_GUARD_system_linux_SqFieldSchemaImpl_h_


#pragma once
#include "iflag.h"
#include "optioninfo.h"
#include "configaccess.h"
#include "format.h"
#include <gsl/gsl>
#include <cmdlime/customnames.h>
#include <memory>
#include <functional>
#include <utility>

namespace cmdlime::detail{

class Flag : public IFlag{
public:
    enum class Type{
        Normal,
        Exit
    };

    Flag(std::string name,
         std::string shortName,
         std::function<bool&()> flagGetter,
         Type type)
        : info_(std::move(name), std::move(shortName), {})
        , flagGetter_(std::move(flagGetter))
        , type_(type)
    {
    }

    OptionInfo& info() override
    {
        return info_;
    }

    const OptionInfo& info() const override
    {
        return info_;
    }

private:
    void set() override
    {
        flagGetter_() = true;
    }

    bool isSet() const override
    {
        return flagGetter_();
    }

    bool isExitFlag() const override
    {
        return type_ == Type::Exit;
    }

private:
    OptionInfo info_;
    std::function<bool&()> flagGetter_;
    Type type_;
};

template <typename TConfig>
class FlagCreator{
    using NameProvider = typename Format<ConfigAccess<TConfig>::format()>::nameProvider;

public:
    FlagCreator(TConfig& cfg,
                const std::string& varName,
                std::function<bool&()> flagGetter,
                Flag::Type flagType = Flag::Type::Normal)
        : cfg_(cfg)
    {
        Expects(!varName.empty());
        flag_ = std::make_unique<Flag>(NameProvider::name(varName),
                                       NameProvider::shortName(varName),
                                       std::move(flagGetter),
                                       flagType);
    }

    FlagCreator& operator<<(const std::string& info)
    {
        flag_->info().addDescription(info);
        return *this;
    }

    FlagCreator& operator<<(const Name& customName)
    {
        flag_->info().resetName(customName.value());
        return *this;
    }

    FlagCreator& operator<<(const ShortName& customName)
    {
        static_assert(Format<ConfigAccess<TConfig>::format()>::shortNamesEnabled,
                      "Current command line format doesn't support short names");
        flag_->info().resetShortName(customName.value());
        return *this;
    }

    FlagCreator& operator<<(const WithoutShortName&)
    {
        static_assert(Format<ConfigAccess<TConfig>::format()>::shortNamesEnabled,
                      "Current command line format doesn't support short names");
        flag_->info().resetShortName({});
        return *this;
    }

    operator bool()
    {
        ConfigAccess<TConfig>{cfg_}.addFlag(std::move(flag_));
        return false;
    }

private:
    std::unique_ptr<Flag> flag_;
    TConfig& cfg_;
};
}


/// @file Span.h
/// @author Braxton Salyer <braxtonsalyer@gmail.com>
/// @brief A thin wrapper around `gsl::span`, providing `at` as a member function instead of a free
/// function
/// @version 0.1
/// @date 2021-10-15
///
/// MIT License
/// @copyright Copyright (c) 2021 Braxton Salyer <braxtonsalyer@gmail.com>
///
/// 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

#include <Hyperion/BasicTypes.h>
#include <Hyperion/HyperionDef.h>
#include <array>
#include <gsl/gsl>
#include <gsl/span>

namespace hyperion {

	/// @brief Thin wrapper around `gsl::span`
	///
	/// @tparam T - The type contained in the `Span`
	/// @tparam Size - The number of elements for constexpr sizes, or gsl::dynamic_extent (default)
	/// for run-time sizes
	/// @ingroup utils
	/// @headerfile "Hyperion/Span.h"
	template<typename T, usize Size = gsl::dynamic_extent>
	class Span {
	  public:
		using Iterator = typename gsl::span<T, Size>::iterator;
		using ReverseIterator = typename gsl::span<T, Size>::reverse_iterator;
		template<usize Offset, usize Count>
		using SubSpan = typename gsl::details::calculate_subspan_type<T, Size, Offset, Count>::type;

		Span() noexcept = default;

		/// @brief Constructs a `Span` from a `gsl::span`
		///
		/// @param span - The `gsl::span` to wrap
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		explicit constexpr Span(gsl::span<T, Size> span) noexcept : m_span_internal(span) {
		}

		/// @brief Copy constructs a `Span` from the given one
		///
		/// @param span - The `Span` to copy
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		constexpr Span(const Span<T, Size>& span) noexcept = default;

		/// @brief Move constructs the given `Span`
		///
		/// @param span - The `Span` to move
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		constexpr Span(Span<T, Size>&& span) noexcept = default;
		~Span() noexcept = default;

		/// @brief Returns the element at the given `index`
		///
		/// @param index - The index of the desired element
		/// @return - The element at `index`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto at(usize index) noexcept -> T& {
			return gsl::at(m_span_internal, static_cast<gsl::index>(index));
		}

		/// @brief Returns the first `Count` elements in the `Span`
		///
		/// @tparam Count - The number of elements to get
		/// @tparam size - The size of the `Span`, this should always be left defaulted
		/// @return - The first `Count` elements in the `Span`, as a `Span`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		template<usize Count, usize size = Size>
		requires(size != gsl::dynamic_extent)
			[[nodiscard]] inline constexpr auto first() const noexcept -> Span<T, Count> {
			return Span(m_span_internal.template first<Count>());
		}

		/// @brief Returns the first `count` elements in the `Span`
		///
		/// @tparam size - The size of the `Span`, this should always be left defaulted
		/// @param count - The number of elements to get
		/// @return - The first `count` elements in the `Span`, as a `Span`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		template<usize size = Size>
		requires(size == gsl::dynamic_extent)
			[[nodiscard]] inline constexpr auto first(usize count) const noexcept -> Span<T, Size> {
			return Span(m_span_internal.first(count));
		}

		/// @brief Returns the last `Count` elements in the `Span`
		///
		/// @tparam Count - The number of elements to get
		/// @tparam size - The size of the `Span`, this should always be left defaulted
		/// @return - The last `Count` elements in the `Span`, as a `Span`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		template<usize Count, usize size = Size>
		requires(size != gsl::dynamic_extent)
			[[nodiscard]] inline constexpr auto last() const noexcept -> Span<T, Count> {
			return Span(m_span_internal.template last<Count>());
		}

		/// @brief Returns the last `count` elements in the `Span`
		///
		/// @tparam size - The size of the `Span`, this should always be left defaulted
		/// @param count - The number of elements to get
		/// @return- The last `count` elements in the `Span` as a `Span`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		template<usize size = Size>
		requires(size == gsl::dynamic_extent)
			[[nodiscard]] inline constexpr auto last(usize count) const noexcept -> Span<T, Size> {
			return Span(m_span_internal.last(count));
		}

		/// @brief Returns a subspan starting at the given `Offset` with `Count` elements
		///
		/// @tparam Offset - The offset to start the subspan at
		/// @tparam Count - The number of elements to get in the subspan
		/// @return - The subspan starting at `Offset` of size `Count`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		template<usize Offset, usize Count = gsl::dynamic_extent>
		[[nodiscard]] inline constexpr auto subspan() const noexcept -> SubSpan<Offset, Count> {
			return Span(m_span_internal.template subspan<Offset, Count>());
		}

		/// @brief Returns a subspan starting at the given `offset` with `count` elements
		///
		/// @param offset - The offset to start the subspan at
		/// @param count - The number of elements to get in the subspan
		/// @return - The subspan starting at `offset` of size `count`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto
		subspan(usize offset, usize count = gsl::dynamic_extent) const noexcept
			-> Span<T, gsl::dynamic_extent> {
			return Span(m_span_internal.subspan(offset, count));
		}

		/// @brief Returns the number of elements in the `Span`
		///
		/// @return - The number of elements in the `Span`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto size() const noexcept -> usize {
			return m_span_internal.size();
		}

		/// @brief Returns the size of the `Span` in bytes
		///
		/// @return - The size of the `Span` in bytes
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto size_bytes() const noexcept -> usize {
			return m_span_internal.size_bytes();
		}

		/// @brief Returns whether the `Span` is empty
		///
		/// @return - If the `Span` is empty
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto is_empty() const noexcept -> bool {
			return size() == 0;
		}

		/// @brief A pointer to the data contained in the `Span`
		///
		/// @return - A pointer to the data
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto data() const noexcept -> T* {
			return m_span_internal.data();
		}

		/// @brief Returns the first element in the `Span`
		///
		/// @return - The first element
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto front() const noexcept -> T& {
			return m_span_internal.front();
		}

		/// @brief Returns the last element in the `Span`
		///
		/// @return - The last element
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto back() const noexcept -> T& {
			return m_span_internal.back();
		}

		/// @brief Returns an iterator at the beginning of the span
		///
		/// @return an iterator at the beginning of the span
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto begin() noexcept -> Iterator {
			return m_span_internal.begin();
		}

		/// @brief Returns an iterator at the beginning of the span
		///
		/// @return an iterator at the beginning of the span
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto begin() const noexcept -> Iterator {
			return m_span_internal.begin();
		}

		/// @brief Returns an iterator at the end of the span
		///
		/// @return an iterator at the end of the span
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto end() noexcept -> Iterator {
			return m_span_internal.end();
		}

		/// @brief Returns an iterator at the end of the span
		///
		/// @return an iterator at the end of the span
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto end() const noexcept -> Iterator {
			return m_span_internal.end();
		}

		/// @brief Returns an iterator at the beginning of the reversed iteration of the span
		///
		/// @return an iterator at the beginning of the reversed iteration of the span
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto rbegin() noexcept -> ReverseIterator {
			return m_span_internal.rbegin();
		}

		/// @brief Returns an iterator at the beginning of the reversed iteration of the span
		///
		/// @return an iterator at the beginning of the reversed iteration of the span
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto rbegin() const noexcept -> ReverseIterator {
			return m_span_internal.rbegin();
		}

		/// @brief Returns an iterator at the end of the reversed iteration of the span
		///
		/// @return an iterator at the end of the reversed iteration of the span
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto rend() noexcept -> ReverseIterator {
			return m_span_internal.rend();
		}

		/// @brief Returns an iterator at the end of the reversed iteration of the span
		///
		/// @return an iterator at the end of the reversed iteration of the span
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto rend() const noexcept -> ReverseIterator {
			return m_span_internal.rend();
		}

#if HYPERION_PLATFORM_COMPILER_MSVC
		[[nodiscard]] inline constexpr auto _Unchecked_begin() const noexcept -> T* {
			return m_span_internal._Unchecked_begin();
		}

		[[nodiscard]] inline constexpr auto _Unchecked_end() const noexcept -> T* {
			return m_span_internal._Unchecked_end();
		}
#endif // HYPERION_PLATFORM_COMPILER_MSVC

		/// @brief Returns the element at the given `index`
		///
		/// @param index - The index of the desired element
		/// @return - The element at `index`
		/// @ingroup utils
		/// @headerfile "Hyperion/Span.h"
		[[nodiscard]] inline constexpr auto operator[](usize index) noexcept -> T& {
			return this->at(index);
		}

		constexpr auto operator=(const Span<T, Size>& span) noexcept -> Span<T, Size>& = default;
		constexpr auto operator=(Span<T, Size>&& span) noexcept -> Span<T, Size>& = default;

	  private:
		gsl::span<T, Size> m_span_internal = gsl::span<T, Size>();
	};

	IGNORE_UNUSED_TEMPLATES_START

	/// @brief Creates a `Span` from the given arguments
	///
	/// @tparam T - The type stored in the array
	///
	/// @param array - The array to get a `Span` over
	/// @param size - The size of the array
	///
	/// @return a `Span` over the given array
	/// @ingroup utils
	/// @headerfile "Hyperion/Span.h"
	template<typename T>
	[[nodiscard]] inline static constexpr auto make_span(T* array, usize size) noexcept -> Span<T> {
		return Span(gsl::make_span(array, static_cast<typename gsl::span<T>::size_type>(size)));
	}

	/// @brief Creates a `Span` from the given arguments
	///
	/// @tparam T - The type stored in the array
	///
	/// @param first - Pointer to the first element in the array
	/// @param last - Pointer to the last element in the array
	///
	/// @return a `Span` over the given array
	/// @ingroup utils
	/// @headerfile "Hyperion/Span.h"
	template<typename T>
	[[nodiscard]] inline static constexpr auto make_span(T* first, T* last) noexcept -> Span<T> {
		return Span(gsl::make_span(first, last));
	}

	/// @brief Creates a `Span` from the given arguments
	///
	/// @tparam T - The type stored in the array
	/// @tparam Size - The size of the array
	///
	/// @param array - The array to get a `Span` over
	///
	/// @return a `Span` over the given array
	/// @ingroup utils
	/// @headerfile "Hyperion/Span.h"
	template<typename T, usize Size>
	[[nodiscard]] inline static constexpr auto
	make_span(T (&array)[Size]) noexcept -> Span<T, Size> { // NOLINT
		return Span(gsl::make_span(array));
	}

	/// @brief Creates a `Span` from the given arguments
	///
	/// @tparam Collection - The collection type to get a `Span` over
	///
	/// @param collection - The collection to get a `Span` over
	///
	/// @return a `Span` over the given collection
	/// @ingroup utils
	/// @headerfile "Hyperion/Span.h"
	template<typename Collection>
	[[nodiscard]] inline static constexpr auto
	make_span(Collection& collection) noexcept -> Span<typename Collection::value_type> {
		return Span(gsl::make_span(collection));
	}

	/// @brief Creates a `Span` from the given arguments
	///
	/// @tparam Collection - The collection type to get a `Span` over
	///
	/// @param collection - The collection to get a `Span` over
	///
	/// @return a `Span` over the given collection
	/// @ingroup utils
	/// @headerfile "Hyperion/Span.h"
	template<typename Collection>
	[[nodiscard]] inline static constexpr auto make_span(const Collection& collection) noexcept
		-> Span<const typename Collection::value_type> {
		return Span(gsl::make_span(collection));
	}

	/// @brief Creates a `Span` from the given arguments
	///
	/// @tparam SmartPtr - The smart pointer type wrapping the array
	///
	/// @param array - The smart pointer wrapping the array to get a `Span` over
	/// @param size - The size of the array
	///
	/// @return a `Span` over the given array
	/// @ingroup utils
	/// @headerfile "Hyperion/Span.h"
	template<typename SmartPtr>
	[[nodiscard]] inline static constexpr auto
	make_span(SmartPtr& array, usize size) noexcept -> Span<typename SmartPtr::element_type> {
		return Span(gsl::make_span(array, size));
	}

	/// @brief Creates a `Span` from the given arguments
	///
	/// @tparam T - The type stored in the array
	/// @tparam Size - The size of the array
	///
	/// @param array - The array to get a `Span` over
	///
	/// @return a `Span` over the given array
	/// @ingroup utils
	/// @headerfile "Hyperion/Span.h"
	template<typename T, usize Size>
	[[nodiscard]] inline static constexpr auto
	make_span(std::array<T, Size> array) noexcept -> Span<T, Size> {
		return Span(gsl::make_span(array));
	}

	IGNORE_UNUSED_TEMPLATES_STOP
} // namespace hyperion


#pragma once

#include <gsl.h>

#include <QString>

namespace gsl {
    inline QString to_QString(gsl::cstring_span<> view) { return QString::fromLatin1(view.data(), view.size()); }
}


#pragma once

#include "arcana/expected.h"
#include "arcana/functional/inplace_function.h"
#include "arcana/iterators.h"
#include "arcana/type_traits.h"

#include "cancellation.h"

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

#include <atomic>

namespace arcana
{
    template<typename ResultT, typename ErrorT>
    class task_completion_source;

    //
    // A scheduler that will invoke the continuation inline
    // right after the previous task.
    //
    namespace
    {
        constexpr auto inline_scheduler = [](auto&& callable)
        {
            callable();
        };
    }
}

#include "internal/internal_task.h"

namespace arcana
{
    //
    //  Generic task system to run work with continuations on a generic scheduler.
    //
    //  The scheduler on which tasks are queued must satisfy this contract:
    //
    //      struct scheduler
    //      {
    //          template<CallableT>
    //          void queue(CallableT&& callable)
    //          {
    //              callable must be usable like this: callable();
    //          }
    //      };
    //
    template<typename ResultT, typename ErrorT>
    class task
    {
        using payload_t = internal::task_payload_with_return<ResultT, ErrorT>;
        using payload_ptr = std::shared_ptr<payload_t>;

        static_assert(std::is_same<typename as_expected<ResultT, ErrorT>::value_type, ResultT>::value,
            "task can't be of expected<T>");

    public:
        using result_type = ResultT;
        using error_type = ErrorT;

        task() = default;

        task(const task& other) = default;
        task(task&& other) = default;

        task(const task_completion_source<ResultT, ErrorT>& source)
            : m_payload{ source.m_payload }
        {}

        task(task_completion_source<ResultT, ErrorT>&& source)
            : m_payload{ std::move(source.m_payload) }
        {}

        task& operator=(task&& other) = default;
        task& operator=(const task& other) = default;

        bool operator==(const task& other)
        {
            return m_payload == other.m_payload;
        }

        //
        // Executes a callable on this scheduler once this task is finished and
        // returns a task that represents the callable.
        //
        // Calling .then() on the returned task will queue a task to run after the
        // callable is run.
        //
        template<typename SchedulerT, typename CallableT>
        auto then(SchedulerT& scheduler, cancellation& token, CallableT&& callable)
        {
            using traits = internal::callable_traits<CallableT, result_type>;
            using wrapper = internal::input_output_wrapper<result_type, error_type, traits::handles_expected::value>;

            static_assert(error_priority<error_type>::value <= error_priority<typename traits::error_propagation_type>::value,
                "The error of a parent task needs to be convertible to the error of a child task.");

            static_assert(std::is_same_v<typename expected_error_or<typename traits::input_type, error_type>::type, error_type>,
                "Continuation expected input parameter needs to use the same error type as the parent task");

            auto factory{ internal::make_task_factory(
                internal::make_work_payload<typename traits::expected_return_type::value_type, typename traits::error_propagation_type>(
                    [callable = wrapper::wrap_callable(std::forward<CallableT>(callable), token)]
                    (internal::base_task_payload* self) mutable noexcept
                    {
                        return callable(*static_cast<payload_t*>(self)->Result);
                    })
            ) };

            m_payload->create_continuation([&scheduler](auto&& c)
            {
                scheduler(std::forward<decltype(c)>(c));
            }, m_payload, std::move(factory.to_run.m_payload));

            return factory.to_return;
        }

    private:
        explicit task(payload_ptr payload)
            : m_payload{ std::move(payload) }
        {}

        template<typename OtherResultT, typename OtherErrorT>
        friend class task;

        friend class task_completion_source<ResultT, ErrorT>;

        template<typename OtherErrorT, typename OtherResultT>
        friend struct internal::task_factory;

        template<typename SchedulerT, typename CallableT>
        friend auto make_task(SchedulerT& scheduler, cancellation& token, CallableT&& callable)
            -> typename internal::task_factory<
                            typename internal::callable_traits<CallableT, void>::error_propagation_type,
                            typename internal::callable_traits<CallableT, void>::expected_return_type::value_type>::task_t;

        payload_ptr m_payload;
    };
}

namespace arcana
{
    template<typename ResultT, typename ErrorT>
    class task_completion_source
    {
        using payload_t = internal::task_payload_with_return<ResultT, ErrorT>;
        using payload_ptr = std::shared_ptr<payload_t>;

    public:
        using result_type = ResultT;
        using error_type = ErrorT;

        task_completion_source()
            : m_payload{ std::make_shared<payload_t>() }
        {}

        //
        // Completes the task this source represents.
        //
        void complete()
        {
            static_assert(std::is_same<ResultT, void>::value,
                "complete with no arguments can only be used with a void completion source");
            m_payload->complete(basic_expected<void, ErrorT>::make_valid());
        }

        //
        // Completes the task this source represents.
        //
        template<typename ValueT>
        void complete(ValueT&& value)
        {
            m_payload->complete(std::forward<ValueT>(value));
        }

        //
        // Returns whether or not the current source has already been completed.
        //
        bool completed() const
        {
            return m_payload->completed();
        }

        //
        // Converts this task_completion_source to a task object for consumers to use.
        //
        task<ResultT, ErrorT> as_task() const &
        {
            return task<ResultT, ErrorT>{ m_payload };
        }

        task<ResultT, ErrorT> as_task() &&
        {
            return task<ResultT, ErrorT>{ std::move(m_payload) };
        }

    private:
        explicit task_completion_source(std::shared_ptr<payload_t> payload)
            : m_payload{ std::move(payload) }
        {}

        friend class task<ResultT, ErrorT>;
        friend class abstract_task_completion_source;

        template<typename E, typename R>
        friend struct internal::task_factory;

        payload_ptr m_payload;
    };

    //
    // a type erased version of task_completion_source.
    //
    class abstract_task_completion_source
    {
        using payload_t = internal::base_task_payload;
        using payload_ptr = std::shared_ptr<payload_t>;

    public:
        abstract_task_completion_source()
            : m_payload{}
        {}

        template<typename T, typename E>
        explicit abstract_task_completion_source(const task_completion_source<T, E>& other)
            : m_payload{ other.m_payload }
        {}

        template<typename T, typename E>
        explicit abstract_task_completion_source(task_completion_source<T, E>&& other)
            : m_payload{ std::move(other.m_payload) }
        {}

        //
        // Returns whether or not the current source has already been completed.
        //
        bool completed() const
        {
            return m_payload->completed();
        }

        template<typename T, typename E>
        bool operator==(const task_completion_source<T, E>& other)
        {
            return m_payload == other.m_payload;
        }

        template<typename T, typename E>
        task_completion_source<T, E> unsafe_cast()
        {
            return task_completion_source<T, E>{ std::static_pointer_cast<internal::task_payload_with_return<T, E>>(m_payload) };
        }

    private:
        payload_ptr m_payload;
    };

    //
    // creates a task and queues it to run on the given scheduler
    //
    template<typename SchedulerT, typename CallableT>
    inline auto make_task(SchedulerT& scheduler, cancellation& token, CallableT&& callable)
        -> typename internal::task_factory<
                            typename internal::callable_traits<CallableT, void>::error_propagation_type,
                            typename internal::callable_traits<CallableT, void>::expected_return_type::value_type>::task_t
    {
        using traits = internal::callable_traits<CallableT, void>;
        using wrapper = internal::input_output_wrapper<void, typename traits::error_propagation_type, false>;

        auto factory{ internal::make_task_factory(
            internal::make_work_payload<typename traits::expected_return_type::value_type, typename traits::error_propagation_type>(
                [callable = wrapper::wrap_callable(std::forward<CallableT>(callable), token)]
                (internal::base_task_payload*) mutable noexcept
                {
                    return callable(basic_expected<void, typename traits::error_propagation_type>::make_valid());
                })
        ) };

        scheduler([to_run = std::move(factory.to_run)]
        {
            to_run.m_payload->run(nullptr);
        });

        return factory.to_return;
    }

    //
    // creates a completed task from the given result
    //
    template<typename ErrorT, typename ResultT>
    inline task<typename as_expected<ResultT, ErrorT>::value_type, ErrorT> task_from_result(ResultT&& value)
    {
        task_completion_source<typename as_expected<ResultT, ErrorT>::value_type, ErrorT> result;
        result.complete(std::forward<ResultT>(value));
        return std::move(result);
    }

    template<typename ErrorT>
    inline task<void, ErrorT> task_from_result()
    {
        task_completion_source<void, ErrorT> result;
        result.complete();
        return std::move(result);
    }

    template<typename ResultT, typename ErrorT>
    inline task<ResultT, std::error_code> task_from_error(const ErrorT& error)
    {
        task_completion_source<ResultT, std::error_code> result;
        result.complete(make_unexpected(make_error_code(error)));
        return std::move(result);
    }

    template<typename ResultT>
    inline task<ResultT, std::error_code> task_from_error(const std::error_code& error)
    {
        task_completion_source<ResultT, std::error_code> result;
        result.complete(make_unexpected(error));
        return std::move(result);
    }

    template<typename ResultT>
    inline task<ResultT, std::exception_ptr> task_from_error(const std::exception_ptr& error)
    {
        task_completion_source<ResultT, std::exception_ptr> result;
        result.complete(make_unexpected(error));
        return std::move(result);
    }

    template<typename ErrorT>
    inline task<void, ErrorT> when_all(gsl::span<task<void, ErrorT>> tasks)
    {
        if (tasks.empty())
        {
            return task_from_result<ErrorT>();
        }

        struct when_all_data
        {
            std::mutex mutex;
            size_t pendingCount;
            std::error_code error;
        };

        task_completion_source<void, ErrorT> result;
        auto data = std::make_shared<when_all_data>();
        data->pendingCount = tasks.size();

        for (task<void, ErrorT>& task : tasks)
        {
            task.then(inline_scheduler, cancellation::none(), [data, result](const basic_expected<void, ErrorT>& exp) mutable noexcept {
                bool last = false;
                {
                    std::lock_guard<std::mutex> guard{ data->mutex };

                    data->pendingCount -= 1;
                    last = data->pendingCount == 0;

                    if (exp.has_error() && !data->error)
                    {
                        // set the first error, as it might have cascaded
                        data->error = exp.error();
                    }
                }

                if (last) // we were the last task to complete
                {
                    if (data->error)
                    {
                        result.complete(make_unexpected(data->error));
                    }
                    else
                    {
                        result.complete();
                    }
                }
            });
        }

        return std::move(result);
    }

    template<typename T, typename ErrorT>
    inline task<std::vector<T>, ErrorT> when_all(gsl::span<task<T, ErrorT>> tasks)
    {
        if (tasks.empty())
        {
            return task_from_result<ErrorT, std::vector<T>>(std::vector<T>());
        }

        struct when_all_data
        {
            std::mutex mutex;
            size_t pendingCount;
            std::error_code error;
            std::vector<T> results;
        };

        task_completion_source<std::vector<T>, ErrorT> result;
        auto data = std::make_shared<when_all_data>();
        data->pendingCount = tasks.size();
        data->results.resize(tasks.size());

        //using forloop with index to be able to keep proper order of results
        for (auto idx = 0U; idx < data->results.size(); idx++)
        {
            tasks[idx].then(arcana::inline_scheduler, cancellation::none(), [data, result, idx](const basic_expected<T, ErrorT>& exp) mutable noexcept {
                bool last = false;
                {
                    std::lock_guard<std::mutex> guard{ data->mutex };

                    data->pendingCount -= 1;
                    last = data->pendingCount == 0;

                    if (exp.has_error() && !data->error)
                    {
                        // set the first error, as it might have cascaded
                        data->error = exp.error();
                    }
                    if (exp.has_value())
                    {
                        data->results[idx] = exp.value();
                    }
                }

                if (last) // we were the last task to complete
                {
                    if (data->error)
                    {
                        result.complete(make_unexpected(data->error));
                    }
                    else
                    {
                        result.complete(data->results);
                    }
                }
            });
        }

        return std::move(result);
    }

    template<typename ErrorT, typename... ArgTs>
    inline task<std::tuple<typename arcana::void_passthrough<ArgTs>::type...>, ErrorT> when_all(task<ArgTs, ErrorT>... tasks)
    {
        using void_passthrough_tuple = std::tuple<typename arcana::void_passthrough<ArgTs>::type...>;

        struct when_all_data
        {
            std::mutex mutex;
            int pending;
            ErrorT error;
            void_passthrough_tuple results;
        };

        task_completion_source<void_passthrough_tuple, ErrorT> result;

        auto data = std::make_shared<when_all_data>();
        data->pending = std::tuple_size<void_passthrough_tuple>::value;

        std::tuple<task<ArgTs, ErrorT>&...> taskrefs = std::make_tuple(std::ref(tasks)...);

        iterate_tuple(taskrefs, [&](auto& task, auto idx) {
            using task_t = std::remove_reference_t<decltype(task)>;

            task.then(inline_scheduler,
                cancellation::none(),
                [data, result](const basic_expected<typename task_t::result_type, ErrorT>& exp) mutable noexcept {
                bool last = false;
                {
                    std::lock_guard<std::mutex> guard{ data->mutex };

                    data->pending -= 1;
                    last = data->pending == 0;

                    internal::write_expected_to_tuple<decltype(idx)::value, ErrorT>(data->results, exp);

                    if (exp.has_error() && !data->error)
                    {
                        // set the first error, as it might have cascaded
                        data->error = exp.error();
                    }
                }

                if (last) // we were the last task to complete
                {
                    if (data->error)
                    {
                        result.complete(make_unexpected(data->error));
                    }
                    else
                    {
                        result.complete(std::move(data->results));
                    }
                }

                return basic_expected<void, ErrorT>::make_valid();
            });
        });
        return std::move(result);
    }
}


#ifndef COMMON_STRUCTURES_H_R5TFSBX6
#define COMMON_STRUCTURES_H_R5TFSBX6

#include <gsl/gsl>
#include <string_view>

namespace sens_loc::util {

/// This data is common for all functionality that reads in files in
/// batch-processing mode.
struct processing_input {
    std::string_view input_pattern;
    int              start;
    int              end;

    processing_input(std::string_view input_pattern,
                     int              start,
                     int              end) noexcept
        : input_pattern{input_pattern}
        , start{start}
        , end{end} {
        Expects(!input_pattern.empty());
        Expects(start <= end);
    }
};

}  // namespace sens_loc::util

#endif /* end of include guard: COMMON_STRUCTURES_H_R5TFSBX6 */


#pragma once

#include "halley/text/halleystring.h"
#include "halley/utils/utils.h"
#include <gsl/span>

namespace Halley
{
	class Path
	{
	public:
		Path();
		Path(const char* name);
		Path(const std::string& name);
		Path(const String& name);

		Path(const Path& other) = default;
		Path(Path&& other) noexcept = default;
		Path& operator=(const Path& other) = default;
		Path& operator=(Path&& other) noexcept = default;

		Path& operator=(const std::string& other);
		Path& operator=(const String& other);

		Path getRoot() const;
		Path getFront(size_t n) const;
		Path getFilename() const;
		Path getStem() const;
		String getExtension() const;
		String getString() const;
		String getNativeString() const;
		String toString() const;

		size_t getNumberPaths() const;

		Path dropFront(int numberFolders) const;

		Path parentPath() const;
		Path replaceExtension(String newExtension) const;

		Path operator/(const char* other) const;
		Path operator/(const Path& other) const;
		Path operator/(const String& other) const;
		Path operator/(const std::string& other) const;

		bool operator==(const char* other) const;
		bool operator==(const String& other) const;
		bool operator==(const Path& other) const;

		bool operator!=(const Path& other) const;
		bool operator<(const Path& other) const;

		std::string string() const;

		static void writeFile(const Path& path, gsl::span<const gsl::byte> data);
		static void writeFile(const Path& path, const Bytes& data);
		static void writeFile(const Path& path, const String& data);
		static Bytes readFile(const Path& path);
		static void removeFile(const Path& path);

		Path makeRelativeTo(const Path& path) const;
		Path changeRelativeRoot(const Path& currentParent, const Path& newParent) const;

		bool isDirectory() const;
		bool isFile() const;
		bool isAbsolute() const;
		bool isEmpty() const;

	private:
		Vector<String> pathParts;
		void normalise();
		void setPath(const String& value);

		explicit Path(Vector<String> parts);
	};

	using TimestampedPath = std::pair<Path, int64_t>;
}


// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#pragma once

#include "core/common/inlined_containers_fwd.h"
#include "core/framework/kernel_registry.h"
#include "core/graph/graph_viewer.h"
#include <gsl/gsl>

namespace onnxruntime {

/**
  Returns a list of nodes that are preferred on CPU.
  They are commonly shape-related computation subgraphs.
  @param graph Graph viewer
  @param provider_type The target execution provider type
  @param kernel_registries Kernel registries for the target EP
  @param tentative_nodes Nodes that are tentative to be placed on on target EP
  */
InlinedHashSet<NodeIndex> GetCpuPreferredNodes(const GraphViewer& graph,
                                               const std::string& provider_type,
                                               gsl::span<const KernelRegistry* const> kernel_registries,
                                               gsl::span<const NodeIndex> tentative_nodes);

}  // namespace onnxruntime