////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001-2026 Alexey Kuryakin daqgroup@mail.ru under MIT license //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// This file is part of the CRW-DAQ project by DaqGroup - component CRWLIB.   //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Purpose:                                                                   //
// ZeroMQ library binding.                                                    //
// Based on https://github.com/DJMaster/zeromq-fpc                            //
////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// History:                                                                   //
// 20260322 - Created by A.K. based on zeromq-fpc                             //
// 20260323 - Modified for dynamic loading, updated to libzmq-4.3.4.          //
////////////////////////////////////////////////////////////////////////////////

unit _crw_zmqlib; //  ZeroMQ library binding.

{$I _crw_sysdef.inc}

{$I _crw_sysmode.inc}

{-$DEFINE ZMQ_BUILD_DRAFT_API}

{$PACKRECORDS C}

interface

uses
 //////////////////////////////////////////////////////
 {$I _crw_uses_first.inc} // NB: MUST BE FIRST USES !!!
 //////////////////////////////////////////////////////
 sysutils, classes, math, ctypes,
 _crw_alloc, _crw_ef, _crw_str;

const
{$IFDEF WINDOWS}
  LIB_ZMQ              = 'libzmq.dll';
  LIB_ZMQ_TRY_EXT_LIST = '';
{$ELSE}
  LIB_ZMQ              = 'libzmq.so';
  LIB_ZMQ_TRY_EXT_LIST = '.0 .4 .5 .6';
{$ENDIF}

const
(*  Version macros for compile-time API version detection                     *)
  ZMQ_VERSION_MAJOR = 4;
  ZMQ_VERSION_MINOR = 3;
  ZMQ_VERSION_PATCH = 4;

const
  ZMQ_VERSION_H = ZMQ_VERSION_MAJOR*10000 + ZMQ_VERSION_MINOR*100 + ZMQ_VERSION_PATCH;

(******************************************************************************)
(*  0MQ errors.                                                               *)
(******************************************************************************)

const
(*  A number random enough not to collide with different errno ranges on      *)
(*  different OSes. The assumption is that error_t is at least 32-bit type.   *)
  ZMQ_HAUSNUMERO                                        = 156384712;

(*  On Windows platform some of the standard POSIX errnos are not defined.    *)
{$IFDEF WINDOWS}
  ENOTSUP                                               = (ZMQ_HAUSNUMERO + 1);
  EPROTONOSUPPORT                                       = (ZMQ_HAUSNUMERO + 2);
  ENOBUFS                                               = (ZMQ_HAUSNUMERO + 3);
  ENETDOWN                                              = (ZMQ_HAUSNUMERO + 4);
  EADDRINUSE                                            = (ZMQ_HAUSNUMERO + 5);
  EADDRNOTAVAIL                                         = (ZMQ_HAUSNUMERO + 6);
  ECONNREFUSED                                          = (ZMQ_HAUSNUMERO + 7);
  EINPROGRESS                                           = (ZMQ_HAUSNUMERO + 8);
  ENOTSOCK                                              = (ZMQ_HAUSNUMERO + 9);
  EMSGSIZE                                              = (ZMQ_HAUSNUMERO + 10);
  EAFNOSUPPORT                                          = (ZMQ_HAUSNUMERO + 11);
  ENETUNREACH                                           = (ZMQ_HAUSNUMERO + 12);
  ECONNABORTED                                          = (ZMQ_HAUSNUMERO + 13);
  ECONNRESET                                            = (ZMQ_HAUSNUMERO + 14);
  ENOTCONN                                              = (ZMQ_HAUSNUMERO + 15);
  ETIMEDOUT                                             = (ZMQ_HAUSNUMERO + 16);
  EHOSTUNREACH                                          = (ZMQ_HAUSNUMERO + 17);
  ENETRESET                                             = (ZMQ_HAUSNUMERO + 18);
{$ELSE}
  // POSIX errno (error codes)
  {$I _crw_errno.inc}
{$ENDIF}

(*  Native 0MQ error codes.                                                   *)
  EFSM                                                  = (ZMQ_HAUSNUMERO + 51);
  ENOCOMPATPROTO                                        = (ZMQ_HAUSNUMERO + 52);
  ETERM                                                 = (ZMQ_HAUSNUMERO + 53);
  EMTHREAD                                              = (ZMQ_HAUSNUMERO + 54);

(*  This function retrieves the errno as it is known to 0MQ library. The goal *)
(*  of this function is to make the code 100% portable, including where 0MQ   *)
(*  compiled with certain CRT library (on Windows) is linked to an            *)
(*  application that uses different CRT library.                              *)
function zmq_errno(): cint; cdecl;

(*  Resolves system errors and 0MQ errors to human-readable string.           *)
function zmq_strerror(errnum_: cint): pchar; cdecl;

(*  Run-time API version detection                                            *)
procedure zmq_version(major_: pcint; minor_: pcint; patch_: pcint); cdecl;

(******************************************************************************)
(*  0MQ infrastructure (a.k.a. context) initialisation & termination.         *)
(******************************************************************************)

(*  New API                                                                   *)
const
(*  Context options                                                           *)
  ZMQ_IO_THREADS                                        = 1;
  ZMQ_MAX_SOCKETS                                       = 2;
  ZMQ_SOCKET_LIMIT                                      = 3;
  ZMQ_THREAD_PRIORITY                                   = 3;
  ZMQ_THREAD_SCHED_POLICY                               = 4;
  ZMQ_MAX_MSGSZ                                         = 5;
  ZMQ_MSG_T_SIZE                                        = 6;
  ZMQ_THREAD_AFFINITY_CPU_ADD                           = 7;
  ZMQ_THREAD_AFFINITY_CPU_REMOVE                        = 8;
  ZMQ_THREAD_NAME_PREFIX                                = 9;

(*  Default for new contexts                                                  *)
  ZMQ_IO_THREADS_DFLT                                   = 1;
  ZMQ_MAX_SOCKETS_DFLT                                  = 1023;
  ZMQ_THREAD_PRIORITY_DFLT                              = -1;
  ZMQ_THREAD_SCHED_POLICY_DFLT                          = -1;

function zmq_ctx_new(): pointer; cdecl;
function zmq_ctx_term(context_: pointer): cint; cdecl;
function zmq_ctx_shutdown(context_: pointer): cint; cdecl;
function zmq_ctx_set(context_: pointer; option_: cint; optval_: cint): cint; cdecl;
function zmq_ctx_get(context_: pointer; option_: cint): cint; cdecl;

(*  Old (legacy) API                                                          *)
function zmq_init(io_threads_: cint): pointer; cdecl;
function zmq_term(context_: pointer): cint; cdecl;
function zmq_ctx_destroy(context_: pointer): cint; cdecl;

(******************************************************************************)
(*  0MQ message definition.                                                   *)
(******************************************************************************)

type
(* Some architectures, like sparc64 and some variants of aarch64, enforce pointer
 * alignment and raise sigbus on violations. Make sure applications allocate
 * zmq_msg_t on addresses aligned on a pointer-size boundary to avoid this issue.
 *)
  Pzmq_msg_t = ^zmq_msg_t;
  zmq_msg_t = record
    _: array[0..63] of cuchar;
  end;

  Pzmq_free_fn = ^zmq_free_fn;
  zmq_free_fn = procedure (data_: pointer; hint_: pointer); cdecl;

function zmq_msg_init(msg_: Pzmq_msg_t): cint; cdecl;
function zmq_msg_init_size(msg_: Pzmq_msg_t; size_: csize_t): cint; cdecl;
function zmq_msg_init_data(msg_: Pzmq_msg_t; data_: pointer; size_: csize_t; ffn_: Pzmq_free_fn; hint_: pointer): cint; cdecl;
function zmq_msg_send(msg_: Pzmq_msg_t; s_: pointer; flags_: cint): cint; cdecl;
function zmq_msg_recv(msg_: Pzmq_msg_t; s_: pointer; flags_: cint): cint; cdecl;
function zmq_msg_close(msg_: Pzmq_msg_t): cint; cdecl;
function zmq_msg_move(dest_: Pzmq_msg_t; src_: Pzmq_msg_t): cint; cdecl;
function zmq_msg_copy(dest_: Pzmq_msg_t; src_: Pzmq_msg_t): cint; cdecl;
function zmq_msg_data(msg_: Pzmq_msg_t): pointer; cdecl;
FUNCTION zmq_msg_size(msg_: Pzmq_msg_t): csize_t; cdecl;
function zmq_msg_more(msg_: Pzmq_msg_t): cint; cdecl;
function zmq_msg_get(msg_: Pzmq_msg_t; property_: cint): cint; cdecl;
function zmq_msg_set(msg_: Pzmq_msg_t; property_: cint; optval_: cint): cint; cdecl;
function zmq_msg_gets(msg_: Pzmq_msg_t; const property_: pchar): pchar; cdecl;

(******************************************************************************)
(*  0MQ socket definition.                                                    *)
(******************************************************************************)

const
(*  Socket types.                                                             *)
  ZMQ_PAIR                                              = 0;
  ZMQ_PUB                                               = 1;
  ZMQ_SUB                                               = 2;
  ZMQ_REQ                                               = 3;
  ZMQ_REP                                               = 4;
  ZMQ_DEALER                                            = 5;
  ZMQ_ROUTER                                            = 6;
  ZMQ_PULL                                              = 7;
  ZMQ_PUSH                                              = 8;
  ZMQ_XPUB                                              = 9;
  ZMQ_XSUB                                              = 10;
  ZMQ_STREAM                                            = 11;

(*  Deprecated aliases                                                        *)
  ZMQ_XREQ                                              = ZMQ_DEALER deprecated;
  ZMQ_XREP                                              = ZMQ_ROUTER deprecated;

(*  Socket options.                                                           *)
  ZMQ_AFFINITY                                          = 4;
  ZMQ_ROUTING_ID                                        = 5;
  ZMQ_SUBSCRIBE                                         = 6;
  ZMQ_UNSUBSCRIBE                                       = 7;
  ZMQ_RATE                                              = 8;
  ZMQ_RECOVERY_IVL                                      = 9;
  ZMQ_SNDBUF                                            = 11;
  ZMQ_RCVBUF                                            = 12;
  ZMQ_RCVMORE                                           = 13;
  ZMQ_FD                                                = 14;
  ZMQ_EVENTS                                            = 15;
  ZMQ_TYPE                                              = 16;
  ZMQ_LINGER                                            = 17;
  ZMQ_RECONNECT_IVL                                     = 18;
  ZMQ_BACKLOG                                           = 19;
  ZMQ_RECONNECT_IVL_MAX                                 = 21;
  ZMQ_MAXMSGSIZE                                        = 22;
  ZMQ_SNDHWM                                            = 23;
  ZMQ_RCVHWM                                            = 24;
  ZMQ_MULTICAST_HOPS                                    = 25;
  ZMQ_RCVTIMEO                                          = 27;
  ZMQ_SNDTIMEO                                          = 28;
  ZMQ_LAST_ENDPOINT                                     = 32;
  ZMQ_ROUTER_MANDATORY                                  = 33;
  ZMQ_TCP_KEEPALIVE                                     = 34;
  ZMQ_TCP_KEEPALIVE_CNT                                 = 35;
  ZMQ_TCP_KEEPALIVE_IDLE                                = 36;
  ZMQ_TCP_KEEPALIVE_INTVL                               = 37;
  ZMQ_IMMEDIATE                                         = 39;
  ZMQ_XPUB_VERBOSE                                      = 40;
  ZMQ_ROUTER_RAW                                        = 41;
  ZMQ_IPV6                                              = 42;
  ZMQ_MECHANISM                                         = 43;
  ZMQ_PLAIN_SERVER                                      = 44;
  ZMQ_PLAIN_USERNAME                                    = 45;
  ZMQ_PLAIN_PASSWORD                                    = 46;
  ZMQ_CURVE_SERVER                                      = 47;
  ZMQ_CURVE_PUBLICKEY                                   = 48;
  ZMQ_CURVE_SECRETKEY                                   = 49;
  ZMQ_CURVE_SERVERKEY                                   = 50;
  ZMQ_PROBE_ROUTER                                      = 51;
  ZMQ_REQ_CORRELATE                                     = 52;
  ZMQ_REQ_RELAXED                                       = 53;
  ZMQ_CONFLATE                                          = 54;
  ZMQ_ZAP_DOMAIN                                        = 55;
  ZMQ_ROUTER_HANDOVER                                   = 56;
  ZMQ_TOS                                               = 57;
  ZMQ_CONNECT_ROUTING_ID                                = 61;
  ZMQ_GSSAPI_SERVER                                     = 62;
  ZMQ_GSSAPI_PRINCIPAL                                  = 63;
  ZMQ_GSSAPI_SERVICE_PRINCIPAL                          = 64;
  ZMQ_GSSAPI_PLAINTEXT                                  = 65;
  ZMQ_HANDSHAKE_IVL                                     = 66;
  ZMQ_SOCKS_PROXY                                       = 68;
  ZMQ_XPUB_NODROP                                       = 69;
  ZMQ_BLOCKY                                            = 70;
  ZMQ_XPUB_MANUAL                                       = 71;
  ZMQ_XPUB_WELCOME_MSG                                  = 72;
  ZMQ_STREAM_NOTIFY                                     = 73;
  ZMQ_INVERT_MATCHING                                   = 74;
  ZMQ_HEARTBEAT_IVL                                     = 75;
  ZMQ_HEARTBEAT_TTL                                     = 76;
  ZMQ_HEARTBEAT_TIMEOUT                                 = 77;
  ZMQ_XPUB_VERBOSER                                     = 78;
  ZMQ_CONNECT_TIMEOUT                                   = 79;
  ZMQ_TCP_MAXRT                                         = 80;
  ZMQ_THREAD_SAFE                                       = 81;
  ZMQ_MULTICAST_MAXTPDU                                 = 84;
  ZMQ_VMCI_BUFFER_SIZE                                  = 85;
  ZMQ_VMCI_BUFFER_MIN_SIZE                              = 86;
  ZMQ_VMCI_BUFFER_MAX_SIZE                              = 87;
  ZMQ_VMCI_CONNECT_TIMEOUT                              = 88;
  ZMQ_USE_FD                                            = 89;
  ZMQ_GSSAPI_PRINCIPAL_NAMETYPE                         = 90;
  ZMQ_GSSAPI_SERVICE_PRINCIPAL_NAMETYPE                 = 91;
  ZMQ_BINDTODEVICE                                      = 92;

(*  Message options                                                           *)
  ZMQ_MORE                                              = 1;
  ZMQ_SHARED                                            = 3;

(*  Send/recv options.                                                        *)
  ZMQ_DONTWAIT                                          = 1;
  ZMQ_SNDMORE                                           = 2;

(*  Security mechanisms                                                       *)
  ZMQ_NULL                                              = 0;
  ZMQ_PLAIN                                             = 1;
  ZMQ_CURVE                                             = 2;
  ZMQ_GSSAPI                                            = 3;

(*  RADIO-DISH protocol                                                       *)
  ZMQ_GROUP_MAX_LENGTH                                  = 255;

(*  Deprecated options and aliases                                            *)
  ZMQ_IDENTITY                                          = ZMQ_ROUTING_ID deprecated;
  ZMQ_CONNECT_RID                                       = ZMQ_CONNECT_ROUTING_ID deprecated;
  ZMQ_TCP_ACCEPT_FILTER                                 = 38 deprecated;
  ZMQ_IPC_FILTER_PID                                    = 58 deprecated;
  ZMQ_IPC_FILTER_UID                                    = 59 deprecated;
  ZMQ_IPC_FILTER_GID                                    = 60 deprecated;
  ZMQ_IPV4ONLY                                          = 31 deprecated;
  ZMQ_DELAY_ATTACH_ON_CONNECT                           = ZMQ_IMMEDIATE deprecated;
  ZMQ_NOBLOCK                                           = ZMQ_DONTWAIT deprecated;
  ZMQ_FAIL_UNROUTABLE                                   = ZMQ_ROUTER_MANDATORY deprecated;
  ZMQ_ROUTER_BEHAVIOR                                   = ZMQ_ROUTER_MANDATORY deprecated;

(*  Deprecated Message options                                                *)
  ZMQ_SRCFD                                             = 2 deprecated;

(******************************************************************************)
(*  GSSAPI definitions                                                        *)
(******************************************************************************)

(*  GSSAPI principal name types                                               *)
  ZMQ_GSSAPI_NT_HOSTBASED                               = 0;
  ZMQ_GSSAPI_NT_USER_NAME                               = 1;
  ZMQ_GSSAPI_NT_KRB5_PRINCIPAL                          = 2;

(******************************************************************************)
(*  0MQ socket events and monitoring                                          *)
(******************************************************************************)

(*  Socket transport events (TCP, IPC and TIPC only)                          *)

  ZMQ_EVENT_CONNECTED                                   = $0001;
  ZMQ_EVENT_CONNECT_DELAYED                             = $0002;
  ZMQ_EVENT_CONNECT_RETRIED                             = $0004;
  ZMQ_EVENT_LISTENING                                   = $0008;
  ZMQ_EVENT_BIND_FAILED                                 = $0010;
  ZMQ_EVENT_ACCEPTED                                    = $0020;
  ZMQ_EVENT_ACCEPT_FAILED                               = $0040;
  ZMQ_EVENT_CLOSED                                      = $0080;
  ZMQ_EVENT_CLOSE_FAILED                                = $0100;
  ZMQ_EVENT_DISCONNECTED                                = $0200;
  ZMQ_EVENT_MONITOR_STOPPED                             = $0400;
  ZMQ_EVENT_ALL                                         = $FFFF;
(*  Unspecified system errors during handshake. Event value is an errno.      *)
  ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL                  = $0800;
(*  Handshake complete successfully with successful authentication (if        *
 *  enabled). Event value is unused.                                          *)
  ZMQ_EVENT_HANDSHAKE_SUCCEEDED                         = $1000;
(*  Protocol errors between ZMTP peers or between server and ZAP handler.     *
 *  Event value is one of ZMQ_PROTOCOL_ERROR_*                                *)
  ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL                   = $2000;
(*  Failed authentication requests. Event value is the numeric ZAP status     *
 *  code, i.e. 300, 400 or 500.                                               *)
  ZMQ_EVENT_HANDSHAKE_FAILED_AUTH                       = $4000;
  ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED                   = $10000000;
  ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND            = $10000001;
  ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE              = $10000002;
  ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE                  = $10000003;
  ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED = $10000011;
  ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE     = $10000012;
  ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO       = $10000013;
  ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE    = $10000014;
  ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR       = $10000015;
  ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY       = $10000016;
  ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME     = $10000017;
  ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA              = $10000018;
// the following two may be due to erroneous configuration of a peer
  ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC                 = $11000001;
  ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH            = $11000002;
  ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED                    = $20000000;
  ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY                = $20000001;
  ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID                 = $20000002;
  ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION                    = $20000003;
  ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE            = $20000004;
  ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA               = $20000005;
  ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED                     = $30000000;

function zmq_socket(context_: pointer; type_: cint): pointer; cdecl;
function zmq_close(s_: pointer): cint; cdecl;
function zmq_setsockopt(s_: pointer; option_: cint; const optval_: pointer; optvallen_: csize_t): cint; cdecl;
function zmq_getsockopt(s_: pointer; option_: cint; optval_: pointer; optvallen_: pcsize_t): cint; cdecl;
function zmq_bind(s_: pointer; const addr_: pchar): cint; cdecl;
function zmq_connect(s_: pointer; const addr_: pchar): cint; cdecl;
function zmq_unbind(s_: pointer; const addr_: pchar): cint; cdecl;
function zmq_disconnect(s_: pointer; const addr_: pchar): cint; cdecl;
function zmq_send(s_: pointer; const buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
function zmq_send_const(s_: pointer; const buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
function zmq_recv(s_: pointer; buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
function zmq_socket_monitor(s_: pointer; const addr_: pchar; events_: cint): cint; cdecl;

(******************************************************************************)
(*  Hide socket fd type; this was before zmq_poller_event_t typedef below     *)
(******************************************************************************)

{$IFDEF WINDOWS}
// Windows uses a pointer-sized unsigned integer to store the socket fd.
{$IFDEF WIN64}
type zmq_fd_t = cuint64;
{$ELSE}
type zmq_fd_t = cuint;
{$ENDIF}
{$ELSE}
type zmq_fd_t = cint;
{$ENDIF}

type Pzmq_fd_t = ^zmq_fd_t;

(******************************************************************************)
(*  Deprecated I/O multiplexing. Prefer using zmq_poller API                  *)
(******************************************************************************)
const
  ZMQ_POLLIN                                            = 1;
  ZMQ_POLLOUT                                           = 2;
  ZMQ_POLLERR                                           = 4;
  ZMQ_POLLPRI                                           = 8;

type
  Pzmq_pollitem_t = ^zmq_pollitem_t;
  zmq_pollitem_t = record
    socket: pointer;
    fd: zmq_fd_t;
    events: cshort ;
    revents: cshort;
  end;

const
  ZMQ_POLLITEMS_DFLT                                    = 16;

function zmq_poll(items_: Pzmq_pollitem_t; nitems_: cint; timeout_: clong): cint; cdecl;

(******************************************************************************)
(*  Message proxying                                                          *)
(******************************************************************************)

function zmq_proxy(frontend_: pointer; backend_: pointer; capture_: pointer): cint; cdecl;
function zmq_proxy_steerable(frontend_: pointer; backend_: pointer; capture_: pointer; control_: pointer): cint; cdecl;

(******************************************************************************)
(*  Probe library capabilities                                                *)
(******************************************************************************)

const
  ZMQ_HAS_CAPABILITIES                                  = 1;

function zmq_has(const capability_: pchar): cint; cdecl;

const
(*  Deprecated aliases *)
  ZMQ_STREAMER                                          = 1 deprecated;
  ZMQ_FORWARDER                                         = 2 deprecated;
  ZMQ_QUEUE                                             = 3 deprecated;

(*  Deprecated methods *)
function zmq_device(type_: cint; frontend_: pointer; backend_: pointer): cint; cdecl; deprecated;
function zmq_sendmsg(s_: pointer; msg_: Pzmq_msg_t; flags_: cint): cint; cdecl; deprecated;
function zmq_recvmsg(s_: pointer; msg_: Pzmq_msg_t; flags_: cint): cint; cdecl; deprecated;

type
  Piovec = ^iovec;
  iovec = record
  end;
  
function zmq_sendiov (s_: pointer; iov_: Piovec; count_: csize_t; flags_: cint): cint; cdecl; deprecated;
function zmq_recviov (s_: pointer; iov_: Piovec; count_: pcsize_t; flags_: cint): cint; cdecl; deprecated;

(******************************************************************************)
(*  Encryption functions                                                      *)
(******************************************************************************)

(*  Encode data with Z85 encoding. Returns encoded data                       *)
function zmq_z85_encode(dest_: pchar; const data_: pcuint8; size_: csize_t): pchar; cdecl;

(*  Decode data with Z85 encoding. Returns decoded data                       *)
function zmq_z85_decode(dest_: pcuint8; const string_: pchar): pcuint8; cdecl;

(*  Generate z85-encoded public and private keypair with tweetnacl/libsodium. *)
(*  Returns 0 on success.                                                     *)
function zmq_curve_keypair(z85_public_key_: pchar; z85_secret_key_: pchar): cint; cdecl;

(*  Derive the z85-encoded public key from the z85-encoded secret key.        *)
(*  Returns 0 on success.                                                     *)
function zmq_curve_public (z85_public_key_: pcchar; const z85_secret_key_: pcchar): cint; cdecl;

(******************************************************************************)
(*  Atomic utility methods                                                    *)
(******************************************************************************)

function zmq_atomic_counter_new(): pointer; cdecl;
procedure zmq_atomic_counter_set(counter_: pointer; value_: cint); cdecl;
function zmq_atomic_counter_inc(counter_: pointer): cint; cdecl;
function zmq_atomic_counter_dec(counter_: pointer): cint; cdecl;
function zmq_atomic_counter_value(counter_: pointer): cint; cdecl;
procedure zmq_atomic_counter_destroy (counter_p_: pointer); cdecl;

(******************************************************************************)
(*  Scheduling timers                                                         *)
(******************************************************************************)

{$define ZMQ_HAVE_TIMERS}

type
  zmq_timer_fn = procedure (timer_id: cint; arg: pointer); cdecl;

function zmq_timers_new (): pointer; cdecl;
function zmq_timers_destroy (timers_p_: pointer): cint; cdecl;
function zmq_timers_add (timers_: pointer; interval_: csize_t; handler_: zmq_timer_fn; arg_: pointer): cint; cdecl;
function zmq_timers_cancel (timers_: pointer; timer_id_: cint): cint; cdecl;
function zmq_timers_set_interval (timers_: pointer; timer_id_: cint; interval_: csize_t): cint; cdecl;
function zmq_timers_reset (timers_: pointer; timer_id_: cint): cint; cdecl;
function zmq_timers_timeout (timers_: pointer): clong; cdecl;
function zmq_timers_execute (timers_: pointer): cint; cdecl;

(******************************************************************************)
(*  These functions are not documented by man pages -- use at your own risk.  *)
(*  If you need these to be part of the formal ZMQ API, then (a) write a man  *)
(*  page, and (b) write a test case in tests.                                 *)
(******************************************************************************)

(*  Helper functions are used by perf tests so that they don't have to care   *)
(*  about minutiae of time-related functions on different OS platforms.       *)

(*  Starts the stopwatch. Returns the handle to the watch.                    *)
function zmq_stopwatch_start(): pointer; cdecl;

(*  Returns the number of microseconds elapsed since the stopwatch was        *)
(*  started, but does not stop or deallocate the stopwatch.                   *)
function zmq_stopwatch_intermediate(watch_: pointer): culong; cdecl;

(*  Stops the stopwatch. Returns the number of microseconds elapsed since     *)
(*  the stopwatch was started, and deallocates that watch.                    *)
function zmq_stopwatch_stop(watch_: pointer): culong; cdecl;

(*  Sleeps for specified number of seconds.                                   *)
procedure zmq_sleep(seconds_: cint); cdecl;

type
  Pzmq_thread_fn = ^zmq_thread_fn;
  zmq_thread_fn = procedure(data_: pointer); cdecl;

(* Start a thread. Returns a handle to the thread.                            *)
function zmq_threadstart(func_: Pzmq_thread_fn; arg_: pointer): pointer; cdecl;

(* Wait for thread to complete then free up resources.                        *)
procedure zmq_threadclose(thread_: pointer); cdecl;

(******************************************************************************)
(*  These functions are DRAFT and disabled in stable releases, and subject to *)
(*  change at ANY time until declared stable.                                 *)
(******************************************************************************)

{$IFDEF ZMQ_BUILD_DRAFT_API}

const
(*  DRAFT Socket types.                                                       *)
  ZMQ_SERVER                                            = 12;
  ZMQ_CLIENT                                            = 13;
  ZMQ_RADIO                                             = 14;
  ZMQ_DISH                                              = 15;
  ZMQ_GATHER                                            = 16;
  ZMQ_SCATTER                                           = 17;
  ZMQ_DGRAM                                             = 18;
  ZMQ_PEER                                              = 19;
  ZMQ_CHANNEL                                           = 20;

(*  DRAFT Socket options.                                                     *)
  ZMQ_ZAP_ENFORCE_DOMAIN                                = 93;
  ZMQ_LOOPBACK_FASTPATH                                 = 94;
  ZMQ_METADATA                                          = 95;
  ZMQ_MULTICAST_LOOP                                    = 96;
  ZMQ_ROUTER_NOTIFY                                     = 97;
  ZMQ_XPUB_MANUAL_LAST_VALUE                            = 98;
  ZMQ_SOCKS_USERNAME                                    = 99;
  ZMQ_SOCKS_PASSWORD                                    = 100;
  ZMQ_IN_BATCH_SIZE                                     = 101;
  ZMQ_OUT_BATCH_SIZE                                    = 102;
  ZMQ_WSS_KEY_PEM                                       = 103;
  ZMQ_WSS_CERT_PEM                                      = 104;
  ZMQ_WSS_TRUST_PEM                                     = 105;
  ZMQ_WSS_HOSTNAME                                      = 106;
  ZMQ_WSS_TRUST_SYSTEM                                  = 107;
  ZMQ_ONLY_FIRST_SUBSCRIBE                              = 108;
  ZMQ_RECONNECT_STOP                                    = 109;
  ZMQ_HELLO_MSG                                         = 110;
  ZMQ_DISCONNECT_MSG                                    = 111;
  ZMQ_PRIORITY                                          = 112;

  (*  DRAFT ZMQ_RECONNECT_STOP options                                        *)
  ZMQ_RECONNECT_STOP_CONN_REFUSED                       = $1;
  ZMQ_RECONNECT_STOP_HANDSHAKE_FAILED                   = $2;
  ZMQ_RECONNECT_STOP_AFTER_DISCONNECT                   = $3;

(*  DRAFT Context options                                                     *)
  ZMQ_ZERO_COPY_RECV                                    = 10;

(*  DRAFT Context methods.                                                    *)
function zmq_ctx_set_ext(context_: pointer; option_: cint; const optval_: pointer; optvallen_: csize_t): cint; cdecl;
function zmq_ctx_get_ext(context_: pointer; option_: cint; optval_: pointer; optvallen_: pcsize_t): cint; cdecl;

(*  DRAFT Socket methods.                                                     *)
function zmq_join(s: pointer; const group: pcchar): cint; cdecl;
function zmq_leave(s: pointer; const group: pcchar): cint; cdecl;
function zmq_connect_peer(s_: pointer; const addr_: pcchar): cuint32; cdecl;

(*  DRAFT Msg methods.                                                        *)
function zmq_msg_set_routing_id(msg: Pzmq_msg_t; routing_id: cuint32): cint; cdecl;
function zmq_msg_routing_id(msg: Pzmq_msg_t): cuint32; cdecl;
function zmq_msg_set_group(msg: Pzmq_msg_t; const group: pcchar): cint; cdecl;
function zmq_msg_group(msg: Pzmq_msg_t): pcchar; cdecl;
function zmq_msg_init_buffer(msg_: Pzmq_msg_t; const buf_: pointer;  size_: csize_t): cint; cdecl;

const
(*  DRAFT Msg property names.                                                 *)
  ZMQ_MSG_PROPERTY_ROUTING_ID                           = 'Routing-Id';
  ZMQ_MSG_PROPERTY_SOCKET_TYPE                          = 'Socket-Type';
  ZMQ_MSG_PROPERTY_USER_ID                              = 'User-Id';
  ZMQ_MSG_PROPERTY_PEER_ADDRESS                         = 'Peer-Address';

(*  Router notify options                                                     *)
  ZMQ_NOTIFY_CONNECT                                    = 1;
  ZMQ_NOTIFY_DISCONNECT                                 = 2;

(******************************************************************************)
(*  Poller polling on sockets,fd and thread-safe sockets                      *)
(******************************************************************************)

{$DEFINE ZMQ_HAVE_POLLER}

type
  Pzmq_poller_event_t = ^zmq_poller_event_t;
  zmq_poller_event_t = record
    socket: pointer;
    fd: zmq_fd_t;
    user_data: pointer;
    events: cshort;
  end;

function zmq_poller_new(): pointer; cdecl;
function zmq_poller_destroy(poller_p: pointer): cint; cdecl;
function zmq_poller_size(poller: pointer): cint; cdecl;
function zmq_poller_add(poller: pointer; socket: pointer; user_data: pointer; events: cshort): cint; cdecl;
function zmq_poller_modify(poller: pointer; socket: pointer; events: cshort): cint; cdecl;
function zmq_poller_remove(poller: pointer; socket: pointer): cint; cdecl;
function zmq_poller_wait(poller: pointer; event: Pzmq_poller_event_t; timeout: clong): cint; cdecl;
function zmq_poller_wait_all(poller: pointer; events: Pzmq_poller_event_t; n_events: cint; timeout: clong): cint; cdecl;
function zmq_poller_fd(poller: pointer; fd: Pzmq_fd_t): cint; cdecl;

function zmq_poller_add_fd(poller: pointer; fd: zmq_fd_t; user_data: pointer; events: cshort): cint; cdecl;
function zmq_poller_modify_fd(poller: pointer; fd: zmq_fd_t; events: cshort): cint; cdecl;
function zmq_poller_remove_fd(poller: pointer; fd: zmq_fd_t): cint; cdecl;

function zmq_socket_get_peer_state(socket: pointer; const routing_id: pointer; routing_id_size: csize_t): cint; cdecl;

(*  DRAFT Socket monitoring events                                            *)
const
  ZMQ_EVENT_PIPES_STATS                                 = $10000;

  ZMQ_CURRENT_EVENT_VERSION                             = 1;
  ZMQ_CURRENT_EVENT_VERSION_DRAFT                       = 2;

  ZMQ_EVENT_ALL_V1                                      = ZMQ_EVENT_ALL;
  ZMQ_EVENT_ALL_V2                                      = ZMQ_EVENT_ALL_V1 or ZMQ_EVENT_PIPES_STATS;

function zmq_socket_monitor_versioned(s_: pointer; const addr_: pcchar;  events_: cuint64;  event_version_: cint; type_: cint): cint; cdecl;
function zmq_socket_monitor_pipes_stats(s: pointer): cint; cdecl;

{$ENDIF}

type // ZeroMQ exception
 EZmqFail = class(Exception);

 /////////////////
 // ZeroMQ loading
 /////////////////
procedure CheckLibZmqDll;
function ValidLibZmq:Boolean;
function LibZmqDllName(Def:LongString=''):LongString;
function LoadLibZmqDll(const Lib:LongString=LIB_ZMQ):Boolean;
function ListLibZmqApi(const Delim:LongString=EOL; Mode:Integer=0):LongString;
function BriefLibZmqDllState(Check:Boolean=True):LongString;

 ///////////////////////////
 // ZeroMQ utility functions
 ///////////////////////////
function zmq_version_int:Integer;
function zmq_version_str:LongString;

function zmq_send_cstring(Socket:Pointer; const S:LongString; Flags:Integer=ZMQ_DONTWAIT):Integer;
function zmq_send_string(Socket:Pointer; const S:LongString; Flags:Integer=ZMQ_DONTWAIT; ZTerm:Boolean=False):Integer;

function zmq_recv_cstring(Socket:Pointer; Flags:Integer=ZMQ_DONTWAIT):LongString;
function zmq_recv_string(Socket:Pointer; Flags:Integer=ZMQ_DONTWAIT; ZTerm:Boolean=False):LongString;

function zmq_recv_stream(Socket:Pointer; S:TMemoryStream; Flags:Integer=ZMQ_DONTWAIT):Integer;

implementation

/////////////////////////////
// ZeroMQ interface functions
/////////////////////////////
type
 TZmqApiRec = record
  zmq_errno : function(): cint; cdecl;
  zmq_strerror : function(errnum_: cint): pchar; cdecl;
  zmq_version : procedure(major_: pcint; minor_: pcint; patch_: pcint); cdecl;
  zmq_ctx_new : function(): pointer; cdecl;
  zmq_ctx_term : function(context_: pointer): cint; cdecl;
  zmq_ctx_shutdown : function(context_: pointer): cint; cdecl;
  zmq_ctx_set : function(context_: pointer; option_: cint; optval_: cint): cint; cdecl;
  zmq_ctx_get : function(context_: pointer; option_: cint): cint; cdecl;
  zmq_init : function(io_threads_: cint): pointer; cdecl;
  zmq_term : function(context_: pointer): cint; cdecl;
  zmq_ctx_destroy : function(context_: pointer): cint; cdecl;
  zmq_msg_init : function(msg_: Pzmq_msg_t): cint; cdecl;
  zmq_msg_init_size : function(msg_: Pzmq_msg_t; size_: csize_t): cint; cdecl;
  zmq_msg_init_data : function(msg_: Pzmq_msg_t; data_: pointer; size_: csize_t; ffn_: Pzmq_free_fn; hint_: pointer): cint; cdecl;
  zmq_msg_send : function(msg_: Pzmq_msg_t; s_: pointer; flags_: cint): cint; cdecl;
  zmq_msg_recv : function(msg_: Pzmq_msg_t; s_: pointer; flags_: cint): cint; cdecl;
  zmq_msg_close : function(msg_: Pzmq_msg_t): cint; cdecl;
  zmq_msg_move : function(dest_: Pzmq_msg_t; src_: Pzmq_msg_t): cint; cdecl;
  zmq_msg_copy : function(dest_: Pzmq_msg_t; src_: Pzmq_msg_t): cint; cdecl;
  zmq_msg_data : function(msg_: Pzmq_msg_t): pointer; cdecl;
  zmq_msg_size : FUNCTION(msg_: Pzmq_msg_t): csize_t; cdecl;
  zmq_msg_more : function(msg_: Pzmq_msg_t): cint; cdecl;
  zmq_msg_get : function(msg_: Pzmq_msg_t; property_: cint): cint; cdecl;
  zmq_msg_set : function(msg_: Pzmq_msg_t; property_: cint; optval_: cint): cint; cdecl;
  zmq_msg_gets : function(msg_: Pzmq_msg_t; const property_: pchar): pchar; cdecl;
  zmq_socket : function(context_: pointer; type_: cint): pointer; cdecl;
  zmq_close : function(s_: pointer): cint; cdecl;
  zmq_setsockopt : function(s_: pointer; option_: cint; const optval_: pointer; optvallen_: csize_t): cint; cdecl;
  zmq_getsockopt : function(s_: pointer; option_: cint; optval_: pointer; optvallen_: pcsize_t): cint; cdecl;
  zmq_bind : function(s_: pointer; const addr_: pchar): cint; cdecl;
  zmq_connect : function(s_: pointer; const addr_: pchar): cint; cdecl;
  zmq_unbind : function(s_: pointer; const addr_: pchar): cint; cdecl;
  zmq_disconnect : function(s_: pointer; const addr_: pchar): cint; cdecl;
  zmq_send : function(s_: pointer; const buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
  zmq_send_const : function(s_: pointer; const buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
  zmq_recv : function(s_: pointer; buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
  zmq_socket_monitor : function(s_: pointer; const addr_: pchar; events_: cint): cint; cdecl;
  zmq_poll : function(items_: Pzmq_pollitem_t; nitems_: cint; timeout_: clong): cint; cdecl;
  zmq_proxy : function(frontend_: pointer; backend_: pointer; capture_: pointer): cint; cdecl;
  zmq_proxy_steerable : function(frontend_: pointer; backend_: pointer; capture_: pointer; control_: pointer): cint; cdecl;
  zmq_has : function(const capability_: pchar): cint; cdecl;
  zmq_device : function(type_: cint; frontend_: pointer; backend_: pointer): cint; cdecl;
  zmq_sendmsg : function(s_: pointer; msg_: Pzmq_msg_t; flags_: cint): cint; cdecl;
  zmq_recvmsg : function(s_: pointer; msg_: Pzmq_msg_t; flags_: cint): cint; cdecl;
  zmq_sendiov : function(s_: pointer; iov_: Piovec; count_: csize_t; flags_: cint): cint; cdecl;
  zmq_recviov : function(s_: pointer; iov_: Piovec; count_: pcsize_t; flags_: cint): cint; cdecl;
  zmq_z85_encode : function(dest_: pchar; const data_: pcuint8; size_: csize_t): pchar; cdecl;
  zmq_z85_decode : function(dest_: pcuint8; const string_: pchar): pcuint8; cdecl;
  zmq_curve_keypair : function(z85_public_key_: pchar; z85_secret_key_: pchar): cint; cdecl;
  zmq_curve_public : function(z85_public_key_: pcchar; const z85_secret_key_: pcchar): cint; cdecl;
  zmq_atomic_counter_new : function(): pointer; cdecl;
  zmq_atomic_counter_set : procedure(counter_: pointer; value_: cint); cdecl;
  zmq_atomic_counter_inc : function(counter_: pointer): cint; cdecl;
  zmq_atomic_counter_dec : function(counter_: pointer): cint; cdecl;
  zmq_atomic_counter_value : function(counter_: pointer): cint; cdecl;
  zmq_atomic_counter_destroy : procedure(counter_p_: pointer); cdecl;
  zmq_timers_new : function(): pointer; cdecl;
  zmq_timers_destroy : function(timers_p_: pointer): cint; cdecl;
  zmq_timers_add : function(timers_: pointer; interval_: csize_t; handler_: zmq_timer_fn; arg_: pointer): cint; cdecl;
  zmq_timers_cancel : function(timers_: pointer; timer_id_: cint): cint; cdecl;
  zmq_timers_set_interval : function(timers_: pointer; timer_id_: cint; interval_: csize_t): cint; cdecl;
  zmq_timers_reset : function(timers_: pointer; timer_id_: cint): cint; cdecl;
  zmq_timers_timeout : function(timers_: pointer): clong; cdecl;
  zmq_timers_execute : function(timers_: pointer): cint; cdecl;
  zmq_stopwatch_start : function(): pointer; cdecl;
  zmq_stopwatch_intermediate : function(watch_: pointer): culong; cdecl;
  zmq_stopwatch_stop : function(watch_: pointer): culong; cdecl;
  zmq_sleep : procedure(seconds_: cint); cdecl;
  zmq_threadstart : function(func_: Pzmq_thread_fn; arg_: pointer): pointer; cdecl;
  zmq_threadclose : procedure(thread_: pointer); cdecl;
  {$IFDEF ZMQ_BUILD_DRAFT_API}
  zmq_ctx_set_ext : function(context_: pointer; option_: cint; const optval_: pointer; optvallen_: csize_t): cint; cdecl;
  zmq_ctx_get_ext : function(context_: pointer; option_: cint; optval_: pointer; optvallen_: pcsize_t): cint; cdecl;
  zmq_join : function(s: pointer; const group: pcchar): cint; cdecl;
  zmq_leave : function(s: pointer; const group: pcchar): cint; cdecl;
  zmq_connect_peer : function(s_: pointer; const addr_: pcchar): cuint32; cdecl;
  zmq_msg_set_routing_id : function(msg: Pzmq_msg_t; routing_id: cuint32): cint; cdecl;
  zmq_msg_routing_id : function(msg: Pzmq_msg_t): cuint32; cdecl;
  zmq_msg_set_group : function(msg: Pzmq_msg_t; const group: pcchar): cint; cdecl;
  zmq_msg_group : function(msg: Pzmq_msg_t): pcchar; cdecl;
  zmq_msg_init_buffer : function(msg_: Pzmq_msg_t; const buf_: pointer;  size_: csize_t): cint; cdecl;
  zmq_poller_new : function(): pointer; cdecl;
  zmq_poller_destroy : function(poller_p: pointer): cint; cdecl;
  zmq_poller_size : function(poller: pointer): cint; cdecl;
  zmq_poller_add : function(poller: pointer; socket: pointer; user_data: pointer; events: cshort): cint; cdecl;
  zmq_poller_modify : function(poller: pointer; socket: pointer; events: cshort): cint; cdecl;
  zmq_poller_remove : function(poller: pointer; socket: pointer): cint; cdecl;
  zmq_poller_wait : function(poller: pointer; event: Pzmq_poller_event_t; timeout: clong): cint; cdecl;
  zmq_poller_wait_all : function(poller: pointer; events: Pzmq_poller_event_t; n_events: cint; timeout: clong): cint; cdecl;
  zmq_poller_fd : function(poller: pointer; fd: Pzmq_fd_t): cint; cdecl;
  zmq_poller_add_fd : function(poller: pointer; fd: zmq_fd_t; user_data: pointer; events: cshort): cint; cdecl;
  zmq_poller_modify_fd : function(poller: pointer; fd: zmq_fd_t; events: cshort): cint; cdecl;
  zmq_poller_remove_fd : function(poller: pointer; fd: zmq_fd_t): cint; cdecl;
  zmq_socket_get_peer_state : function(socket: pointer; const routing_id: pointer; routing_id_size: csize_t): cint; cdecl;
  zmq_socket_monitor_versioned : function(s_: pointer; const addr_: pcchar;  events_: cuint64;  event_version_: cint; type_: cint): cint; cdecl;
  zmq_socket_monitor_pipes_stats : function(s: pointer): cint; cdecl;
  {$ENDIF}
 end;

var ZmqApiRec:TZmqApiRec;

procedure ClearZmqApiRec;
begin
 ZmqApiRec:=Default(TZmqApiRec);
end;

//////////////////////////
// ZeroMQ functions loader
//////////////////////////
const
 sFunErr:LongString='';
 sFunSuc:LongString='';

function InitZmqApi(hLib:THandle; var Z:TZmqApiRec):Boolean;
 function AssignProc(id:String):Pointer;
 begin
  Result:=nil;
  if (hLib<>0) and (hLib<>THandle(-1))
  then Result:=System.GetProcAddress(hLib,id);
  if (Result<>nil) then sFunSuc+=(id+EOL) else sFunErr+=(id+EOL);
 end;
begin
 Z:=Default(TZmqApiRec);
 sFunErr:=''; sFunSuc:='';
 if (hLib=0) then Exit(False);
 Z.zmq_errno:=AssignProc('zmq_errno');
 Z.zmq_strerror:=AssignProc('zmq_strerror');
 Z.zmq_version:=AssignProc('zmq_version');
 Z.zmq_ctx_new:=AssignProc('zmq_ctx_new');
 Z.zmq_ctx_term:=AssignProc('zmq_ctx_term');
 Z.zmq_ctx_shutdown:=AssignProc('zmq_ctx_shutdown');
 Z.zmq_ctx_set:=AssignProc('zmq_ctx_set');
 Z.zmq_ctx_get:=AssignProc('zmq_ctx_get');
 Z.zmq_init:=AssignProc('zmq_init');
 Z.zmq_term:=AssignProc('zmq_term');
 Z.zmq_ctx_destroy:=AssignProc('zmq_ctx_destroy');
 Z.zmq_msg_init:=AssignProc('zmq_msg_init');
 Z.zmq_msg_init_size:=AssignProc('zmq_msg_init_size');
 Z.zmq_msg_init_data:=AssignProc('zmq_msg_init_data');
 Z.zmq_msg_send:=AssignProc('zmq_msg_send');
 Z.zmq_msg_recv:=AssignProc('zmq_msg_recv');
 Z.zmq_msg_close:=AssignProc('zmq_msg_close');
 Z.zmq_msg_move:=AssignProc('zmq_msg_move');
 Z.zmq_msg_copy:=AssignProc('zmq_msg_copy');
 Z.zmq_msg_data:=AssignProc('zmq_msg_data');
 Z.zmq_msg_size:=AssignProc('zmq_msg_size');
 Z.zmq_msg_more:=AssignProc('zmq_msg_more');
 Z.zmq_msg_get:=AssignProc('zmq_msg_get');
 Z.zmq_msg_set:=AssignProc('zmq_msg_set');
 Z.zmq_msg_gets:=AssignProc('zmq_msg_gets');
 Z.zmq_socket:=AssignProc('zmq_socket');
 Z.zmq_close:=AssignProc('zmq_close');
 Z.zmq_setsockopt:=AssignProc('zmq_setsockopt');
 Z.zmq_getsockopt:=AssignProc('zmq_getsockopt');
 Z.zmq_bind:=AssignProc('zmq_bind');
 Z.zmq_connect:=AssignProc('zmq_connect');
 Z.zmq_unbind:=AssignProc('zmq_unbind');
 Z.zmq_disconnect:=AssignProc('zmq_disconnect');
 Z.zmq_send:=AssignProc('zmq_send');
 Z.zmq_send_const:=AssignProc('zmq_send_const');
 Z.zmq_recv:=AssignProc('zmq_recv');
 Z.zmq_socket_monitor:=AssignProc('zmq_socket_monitor');
 Z.zmq_poll:=AssignProc('zmq_poll');
 Z.zmq_proxy:=AssignProc('zmq_proxy');
 Z.zmq_proxy_steerable:=AssignProc('zmq_proxy_steerable');
 Z.zmq_has:=AssignProc('zmq_has');
 Z.zmq_device:=AssignProc('zmq_device');
 Z.zmq_sendmsg:=AssignProc('zmq_sendmsg');
 Z.zmq_recvmsg:=AssignProc('zmq_recvmsg');
 Z.zmq_sendiov:=AssignProc('zmq_sendiov');
 Z.zmq_recviov:=AssignProc('zmq_recviov');
 Z.zmq_z85_encode:=AssignProc('zmq_z85_encode');
 Z.zmq_z85_decode:=AssignProc('zmq_z85_decode');
 Z.zmq_curve_keypair:=AssignProc('zmq_curve_keypair');
 Z.zmq_curve_public:=AssignProc('zmq_curve_public');
 Z.zmq_atomic_counter_new:=AssignProc('zmq_atomic_counter_new');
 Z.zmq_atomic_counter_set:=AssignProc('zmq_atomic_counter_set');
 Z.zmq_atomic_counter_inc:=AssignProc('zmq_atomic_counter_inc');
 Z.zmq_atomic_counter_dec:=AssignProc('zmq_atomic_counter_dec');
 Z.zmq_atomic_counter_value:=AssignProc('zmq_atomic_counter_value');
 Z.zmq_atomic_counter_destroy:=AssignProc('zmq_atomic_counter_destroy');
 Z.zmq_timers_new:=AssignProc('zmq_timers_new');
 Z.zmq_timers_destroy:=AssignProc('zmq_timers_destroy');
 Z.zmq_timers_add:=AssignProc('zmq_timers_add');
 Z.zmq_timers_cancel:=AssignProc('zmq_timers_cancel');
 Z.zmq_timers_set_interval:=AssignProc('zmq_timers_set_interval');
 Z.zmq_timers_reset:=AssignProc('zmq_timers_reset');
 Z.zmq_timers_timeout:=AssignProc('zmq_timers_timeout');
 Z.zmq_timers_execute:=AssignProc('zmq_timers_execute');
 Z.zmq_stopwatch_start:=AssignProc('zmq_stopwatch_start');
 Z.zmq_stopwatch_intermediate:=AssignProc('zmq_stopwatch_intermediate');
 Z.zmq_stopwatch_stop:=AssignProc('zmq_stopwatch_stop');
 Z.zmq_sleep:=AssignProc('zmq_sleep');
 Z.zmq_threadstart:=AssignProc('zmq_threadstart');
 Z.zmq_threadclose:=AssignProc('zmq_threadclose');
 {$IFDEF ZMQ_BUILD_DRAFT_API}
 Z.zmq_ctx_set_ext:=AssignProc('zmq_ctx_set_ext');
 Z.zmq_ctx_get_ext:=AssignProc('zmq_ctx_get_ext');
 Z.zmq_join:=AssignProc('zmq_join');
 Z.zmq_leave:=AssignProc('zmq_leave');
 Z.zmq_connect_peer:=AssignProc('zmq_connect_peer');
 Z.zmq_msg_set_routing_id:=AssignProc('zmq_msg_set_routing_id');
 Z.zmq_msg_routing_id:=AssignProc('zmq_msg_routing_id');
 Z.zmq_msg_set_group:=AssignProc('zmq_msg_set_group');
 Z.zmq_msg_group:=AssignProc('zmq_msg_group');
 Z.zmq_msg_init_buffer:=AssignProc('zmq_msg_init_buffer');
 Z.zmq_poller_new:=AssignProc('zmq_poller_new');
 Z.zmq_poller_destroy:=AssignProc('zmq_poller_destroy');
 Z.zmq_poller_size:=AssignProc('zmq_poller_size');
 Z.zmq_poller_add:=AssignProc('zmq_poller_add');
 Z.zmq_poller_modify:=AssignProc('zmq_poller_modify');
 Z.zmq_poller_remove:=AssignProc('zmq_poller_remove');
 Z.zmq_poller_wait:=AssignProc('zmq_poller_wait');
 Z.zmq_poller_wait_all:=AssignProc('zmq_poller_wait_all');
 Z.zmq_poller_fd:=AssignProc('zmq_poller_fd');
 Z.zmq_poller_add_fd:=AssignProc('zmq_poller_add_fd');
 Z.zmq_poller_modify_fd:=AssignProc('zmq_poller_modify_fd');
 Z.zmq_poller_remove_fd:=AssignProc('zmq_poller_remove_fd');
 Z.zmq_socket_get_peer_state:=AssignProc('zmq_socket_get_peer_state');
 Z.zmq_socket_monitor_versioned:=AssignProc('zmq_socket_monitor_versioned');
 Z.zmq_socket_monitor_pipes_stats:=AssignProc('zmq_socket_monitor_pipes_stats');
 {$ENDIF}
 Result:=(sFunErr='') and (sFunSuc<>'');
end;

function ListLibZmqApi(const Delim:LongString=EOL; Mode:Integer=0):LongString;
begin
 if HasFlags(Mode,1) then Result:=sFunErr else Result:=sFunSuc;
 if (Delim<>'') and (Delim<>EOL)
 then Result:=StringReplace(Trim(Result),EOL,Delim,[rfReplaceAll]);
end;

const
 hLib : TLibHandle = 0;
 sLib : LongString = '';

function ValidLibZmq:Boolean;
begin
 Result:=(hLib<>0) and Assigned(ZmqApiRec.zmq_errno);
end;

function LibZmqDllName(Def:LongString=''):LongString;
begin
 Result:=TrimDef(sLib,Def);
end;

procedure FreeLibZmqDll;
begin
 ClearZmqApiRec;
 if (hLib<>0) then begin
  FreeLibrary(hLib);
  hLib:=0;
 end;
 sLib:='';
 sFunErr:='';
 sFunSuc:='';
end;

function LoadLibZmqDll(const Lib:LongString=LIB_ZMQ):Boolean;
const Delims=[#0..' ',','];
var i:Integer;
begin
 Result:=False;
 if (hLib=0) then begin
  sLib:=Lib;
  hLib:=SafeLoadLibrary(sLib);
  if (hLib=0) then begin // Try also libzmq.so,libzmq.so.0 etc
   for i:=0 to WordCount(LIB_ZMQ_TRY_EXT_LIST,Delims) do begin
    sLib:=LIB_ZMQ+ExtractWord(i,LIB_ZMQ_TRY_EXT_LIST,Delims);
    hLib:=SafeLoadLibrary(sLib);
    if (hLib<>0) then break;
    sLib:='';
   end;
  end;
 end;
 if not ValidLibZmq then begin
  if not InitZmqApi(hLib,ZmqApiRec)
  then ClearZmqApiRec;
 end;
 if ValidLibZmq then Result:=True else ClearZmqApiRec;
end;

procedure CheckLibZmqDll;
begin
 if (hLib=0) then LoadLibZmqDll;
end;

procedure ErrorUnassigned(const who:LongString);
begin
 raise EZmqFail.Create(Format('Unassigned ZMQ function %s.',[who]));
end;

function BriefLibZmqDllState(Check:Boolean=True):LongString;
var nsuc,nerr:Integer;
begin
 Result:='';
 if Check then CheckLibZmqDll;
 nsuc:=WordCount(sFunSuc,ScanSpaces);
 nerr:=WordCount(sFunErr,ScanSpaces);
 if ValidLibZmq then begin
  Result:='Valid '+LibZmqDllName(LIB_ZMQ)+' version '+zmq_version_str;
  Result+=', API loaded:'+IntToStr(nsuc)+', missed:'+IntToStr(nerr);
 end else begin
  Result:='Failed '+LibZmqDllName(LIB_ZMQ);
  Result+=', API loaded:'+IntToStr(nsuc)+', missed:'+IntToStr(nerr);
 end;
end;

////////////////////////////////////
// Safe binding for ZeroMQ functions
////////////////////////////////////

function zmq_errno(): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_errno) then ErrorUnassigned('zmq_errno');
 Result:=ZmqApiRec.zmq_errno();
end;

function zmq_strerror(errnum_: cint): pchar; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_strerror) then ErrorUnassigned('zmq_strerror');
 Result:=ZmqApiRec.zmq_strerror(errnum_);
end;

procedure zmq_version(major_: pcint; minor_: pcint; patch_: pcint); cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_version) then ErrorUnassigned('zmq_version');
 ZmqApiRec.zmq_version(major_, minor_, patch_);
end;

function zmq_ctx_new(): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_ctx_new) then ErrorUnassigned('zmq_ctx_new');
 Result:=ZmqApiRec.zmq_ctx_new();
end;

function zmq_ctx_term(context_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_ctx_term) then ErrorUnassigned('zmq_ctx_term');
 Result:=ZmqApiRec.zmq_ctx_term(context_);
end;

function zmq_ctx_shutdown(context_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_ctx_shutdown) then ErrorUnassigned('zmq_ctx_shutdown');
 Result:=ZmqApiRec.zmq_ctx_shutdown(context_);
end;

function zmq_ctx_set(context_: pointer; option_: cint; optval_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_ctx_set) then ErrorUnassigned('zmq_ctx_set');
 Result:=ZmqApiRec.zmq_ctx_set(context_, option_, optval_);
end;

function zmq_ctx_get(context_: pointer; option_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_ctx_get) then ErrorUnassigned('zmq_ctx_get');
 Result:=ZmqApiRec.zmq_ctx_get(context_, option_);
end;

function zmq_init(io_threads_: cint): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_init) then ErrorUnassigned('zmq_init');
 Result:=ZmqApiRec.zmq_init(io_threads_);
end;

function zmq_term(context_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_term) then ErrorUnassigned('zmq_term');
 Result:=ZmqApiRec.zmq_term(context_);
end;

function zmq_ctx_destroy(context_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_ctx_destroy) then ErrorUnassigned('zmq_ctx_destroy');
 Result:=ZmqApiRec.zmq_ctx_destroy(context_);
end;

function zmq_msg_init(msg_: Pzmq_msg_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_init) then ErrorUnassigned('zmq_msg_init');
 Result:=ZmqApiRec.zmq_msg_init(msg_);
end;

function zmq_msg_init_size(msg_: Pzmq_msg_t; size_: csize_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_init_size) then ErrorUnassigned('zmq_msg_init_size');
 Result:=ZmqApiRec.zmq_msg_init_size(msg_, size_);
end;

function zmq_msg_init_data(msg_: Pzmq_msg_t; data_: pointer; size_: csize_t; ffn_: Pzmq_free_fn; hint_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_init_data) then ErrorUnassigned('zmq_msg_init_data');
 Result:=ZmqApiRec.zmq_msg_init_data(msg_, data_, size_, ffn_, hint_);
end;

function zmq_msg_send(msg_: Pzmq_msg_t; s_: pointer; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_send) then ErrorUnassigned('zmq_msg_send');
 Result:=ZmqApiRec.zmq_msg_send(msg_, s_, flags_);
end;

function zmq_msg_recv(msg_: Pzmq_msg_t; s_: pointer; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_recv) then ErrorUnassigned('zmq_msg_recv');
 Result:=ZmqApiRec.zmq_msg_recv(msg_, s_, flags_);
end;

function zmq_msg_close(msg_: Pzmq_msg_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_close) then ErrorUnassigned('zmq_msg_close');
 Result:=ZmqApiRec.zmq_msg_close(msg_);
end;

function zmq_msg_move(dest_: Pzmq_msg_t; src_: Pzmq_msg_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_move) then ErrorUnassigned('zmq_msg_move');
 Result:=ZmqApiRec.zmq_msg_move(dest_, src_);
end;

function zmq_msg_copy(dest_: Pzmq_msg_t; src_: Pzmq_msg_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_copy) then ErrorUnassigned('zmq_msg_copy');
 Result:=ZmqApiRec.zmq_msg_copy(dest_, src_);
end;

function zmq_msg_data(msg_: Pzmq_msg_t): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_data) then ErrorUnassigned('zmq_msg_data');
 Result:=ZmqApiRec.zmq_msg_data(msg_);
end;

function zmq_msg_size(msg_: Pzmq_msg_t): csize_t; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_size) then ErrorUnassigned('zmq_msg_size');
 Result:=ZmqApiRec.zmq_msg_size(msg_);
end;

function zmq_msg_more(msg_: Pzmq_msg_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_more) then ErrorUnassigned('zmq_msg_more');
 Result:=ZmqApiRec.zmq_msg_more(msg_);
end;

function zmq_msg_get(msg_: Pzmq_msg_t; property_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_get) then ErrorUnassigned('zmq_msg_get');
 Result:=ZmqApiRec.zmq_msg_get(msg_, property_);
end;

function zmq_msg_set(msg_: Pzmq_msg_t; property_: cint; optval_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_set) then ErrorUnassigned('zmq_msg_set');
 Result:=ZmqApiRec.zmq_msg_set(msg_, property_, optval_);
end;

function zmq_msg_gets(msg_: Pzmq_msg_t; const property_: pchar): pchar; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_gets) then ErrorUnassigned('zmq_msg_gets');
 Result:=ZmqApiRec.zmq_msg_gets(msg_, property_);
end;

function zmq_socket(context_: pointer; type_: cint): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_socket) then ErrorUnassigned('zmq_socket');
 Result:=ZmqApiRec.zmq_socket(context_, type_);
end;

function zmq_close(s_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_close) then ErrorUnassigned('zmq_close');
 Result:=ZmqApiRec.zmq_close(s_);
end;

function zmq_setsockopt(s_: pointer; option_: cint; const optval_: pointer; optvallen_: csize_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_setsockopt) then ErrorUnassigned('zmq_setsockopt');
 Result:=ZmqApiRec.zmq_setsockopt(s_, option_, optval_, optvallen_);
end;

function zmq_getsockopt(s_: pointer; option_: cint; optval_: pointer; optvallen_: pcsize_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_getsockopt) then ErrorUnassigned('zmq_getsockopt');
 Result:=ZmqApiRec.zmq_getsockopt(s_, option_, optval_, optvallen_);
end;

function zmq_bind(s_: pointer; const addr_: pchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_bind) then ErrorUnassigned('zmq_bind');
 Result:=ZmqApiRec.zmq_bind(s_, addr_);
end;

function zmq_connect(s_: pointer; const addr_: pchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_connect) then ErrorUnassigned('zmq_connect');
 Result:=ZmqApiRec.zmq_connect(s_, addr_);
end;

function zmq_unbind(s_: pointer; const addr_: pchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_unbind) then ErrorUnassigned('zmq_unbind');
 Result:=ZmqApiRec.zmq_unbind(s_, addr_);
end;

function zmq_disconnect(s_: pointer; const addr_: pchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_disconnect) then ErrorUnassigned('zmq_disconnect');
 Result:=ZmqApiRec.zmq_disconnect(s_, addr_);
end;

function zmq_send(s_: pointer; const buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_send) then ErrorUnassigned('zmq_send');
 Result:=ZmqApiRec.zmq_send(s_, buf_, len_, flags_);
end;

function zmq_send_const(s_: pointer; const buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_send_const) then ErrorUnassigned('zmq_send_const');
 Result:=ZmqApiRec.zmq_send_const(s_, buf_, len_, flags_);
end;

function zmq_recv(s_: pointer; buf_: pointer; len_: csize_t; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_recv) then ErrorUnassigned('zmq_recv');
 Result:=ZmqApiRec.zmq_recv(s_, buf_, len_, flags_);
end;

function zmq_socket_monitor(s_: pointer; const addr_: pchar; events_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_socket_monitor) then ErrorUnassigned('zmq_socket_monitor');
 Result:=ZmqApiRec.zmq_socket_monitor(s_, addr_, events_);
end;

function zmq_poll(items_: Pzmq_pollitem_t; nitems_: cint; timeout_: clong): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poll) then ErrorUnassigned('zmq_poll');
 Result:=ZmqApiRec.zmq_poll(items_, nitems_, timeout_);
end;

function zmq_proxy(frontend_: pointer; backend_: pointer; capture_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_proxy) then ErrorUnassigned('zmq_proxy');
 Result:=ZmqApiRec.zmq_proxy(frontend_, backend_, capture_);
end;

function zmq_proxy_steerable(frontend_: pointer; backend_: pointer; capture_: pointer; control_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_proxy_steerable) then ErrorUnassigned('zmq_proxy_steerable');
 Result:=ZmqApiRec.zmq_proxy_steerable(frontend_, backend_, capture_, control_);
end;

function zmq_has(const capability_: pchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_has) then ErrorUnassigned('zmq_has');
 Result:=ZmqApiRec.zmq_has(capability_);
end;

function zmq_device(type_: cint; frontend_: pointer; backend_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_device) then ErrorUnassigned('zmq_device');
 Result:=ZmqApiRec.zmq_device(type_, frontend_, backend_);
end;

function zmq_sendmsg(s_: pointer; msg_: Pzmq_msg_t; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_sendmsg) then ErrorUnassigned('zmq_sendmsg');
 Result:=ZmqApiRec.zmq_sendmsg(s_, msg_, flags_);
end;

function zmq_recvmsg(s_: pointer; msg_: Pzmq_msg_t; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_recvmsg) then ErrorUnassigned('zmq_recvmsg');
 Result:=ZmqApiRec.zmq_recvmsg(s_, msg_, flags_);
end;

function zmq_sendiov(s_: pointer; iov_: Piovec; count_: csize_t; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_sendiov) then ErrorUnassigned('zmq_sendiov');
 Result:=ZmqApiRec.zmq_sendiov(s_, iov_, count_, flags_);
end;

function zmq_recviov(s_: pointer; iov_: Piovec; count_: pcsize_t; flags_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_recviov) then ErrorUnassigned('zmq_recviov');
 Result:=ZmqApiRec.zmq_recviov(s_, iov_, count_, flags_);
end;

function zmq_z85_encode(dest_: pchar; const data_: pcuint8; size_: csize_t): pchar; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_z85_encode) then ErrorUnassigned('zmq_z85_encode');
 Result:=ZmqApiRec.zmq_z85_encode(dest_, data_, size_);
end;

function zmq_z85_decode(dest_: pcuint8; const string_: pchar): pcuint8; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_z85_decode) then ErrorUnassigned('zmq_z85_decode');
 Result:=ZmqApiRec.zmq_z85_decode(dest_, string_);
end;

function zmq_curve_keypair(z85_public_key_: pchar; z85_secret_key_: pchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_curve_keypair) then ErrorUnassigned('zmq_curve_keypair');
 Result:=ZmqApiRec.zmq_curve_keypair(z85_public_key_, z85_secret_key_);
end;

function zmq_curve_public(z85_public_key_: pcchar; const z85_secret_key_: pcchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_curve_public) then ErrorUnassigned('zmq_curve_public');
 Result:=ZmqApiRec.zmq_curve_public(z85_public_key_, z85_secret_key_);
end;

function zmq_atomic_counter_new(): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_atomic_counter_new) then ErrorUnassigned('zmq_atomic_counter_new');
 Result:=ZmqApiRec.zmq_atomic_counter_new();
end;

procedure zmq_atomic_counter_set(counter_: pointer; value_: cint);
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_atomic_counter_set) then ErrorUnassigned('zmq_atomic_counter_set');
 ZmqApiRec.zmq_atomic_counter_set(counter_, value_);
end;

function zmq_atomic_counter_inc(counter_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_atomic_counter_inc) then ErrorUnassigned('zmq_atomic_counter_inc');
 Result:=ZmqApiRec.zmq_atomic_counter_inc(counter_);
end;

function zmq_atomic_counter_dec(counter_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_atomic_counter_dec) then ErrorUnassigned('zmq_atomic_counter_dec');
 Result:=ZmqApiRec.zmq_atomic_counter_dec(counter_);
end;

function zmq_atomic_counter_value(counter_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_atomic_counter_value) then ErrorUnassigned('zmq_atomic_counter_value');
 Result:=ZmqApiRec.zmq_atomic_counter_value(counter_);
end;

procedure zmq_atomic_counter_destroy(counter_p_: pointer);
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_atomic_counter_destroy) then ErrorUnassigned('zmq_atomic_counter_destroy');
 ZmqApiRec.zmq_atomic_counter_destroy(counter_p_);
end;

function zmq_timers_new(): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_timers_new) then ErrorUnassigned('zmq_timers_new');
 Result:=ZmqApiRec.zmq_timers_new();
end;

function zmq_timers_destroy(timers_p_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_timers_destroy) then ErrorUnassigned('zmq_timers_destroy');
 Result:=ZmqApiRec.zmq_timers_destroy(timers_p_);
end;

function zmq_timers_add(timers_: pointer; interval_: csize_t; handler_: zmq_timer_fn; arg_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_timers_add) then ErrorUnassigned('zmq_timers_add');
 Result:=ZmqApiRec.zmq_timers_add(timers_, interval_, handler_, arg_);
end;

function zmq_timers_cancel(timers_: pointer; timer_id_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_timers_cancel) then ErrorUnassigned('zmq_timers_cancel');
 Result:=ZmqApiRec.zmq_timers_cancel(timers_, timer_id_);
end;

function zmq_timers_set_interval(timers_: pointer; timer_id_: cint; interval_: csize_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_timers_set_interval) then ErrorUnassigned('zmq_timers_set_interval');
 Result:=ZmqApiRec.zmq_timers_set_interval(timers_, timer_id_, interval_);
end;

function zmq_timers_reset(timers_: pointer; timer_id_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_timers_reset) then ErrorUnassigned('zmq_timers_reset');
 Result:=ZmqApiRec.zmq_timers_reset(timers_, timer_id_);
end;

function zmq_timers_timeout(timers_: pointer): clong; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_timers_timeout) then ErrorUnassigned('zmq_timers_timeout');
 Result:=ZmqApiRec.zmq_timers_timeout(timers_);
end;

function zmq_timers_execute(timers_: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_timers_execute) then ErrorUnassigned('zmq_timers_execute');
 Result:=ZmqApiRec.zmq_timers_execute(timers_);
end;

function zmq_stopwatch_start(): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_stopwatch_start) then ErrorUnassigned('zmq_stopwatch_start');
 Result:=ZmqApiRec.zmq_stopwatch_start();
end;

function zmq_stopwatch_intermediate(watch_: pointer): culong; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_stopwatch_intermediate) then ErrorUnassigned('zmq_stopwatch_intermediate');
 Result:=ZmqApiRec.zmq_stopwatch_intermediate(watch_);
end;

function zmq_stopwatch_stop(watch_: pointer): culong; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_stopwatch_stop) then ErrorUnassigned('zmq_stopwatch_stop');
 Result:=ZmqApiRec.zmq_stopwatch_stop(watch_);
end;

procedure zmq_sleep(seconds_: cint);
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_sleep) then ErrorUnassigned('zmq_sleep');
 ZmqApiRec.zmq_sleep(seconds_);
end;

function zmq_threadstart(func_: Pzmq_thread_fn; arg_: pointer): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_threadstart) then ErrorUnassigned('zmq_threadstart');
 Result:=ZmqApiRec.zmq_threadstart(func_, arg_);
end;

procedure zmq_threadclose(thread_: pointer);
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_threadclose) then ErrorUnassigned('zmq_threadclose');
 ZmqApiRec.zmq_threadclose(thread_);
end;

{$IFDEF ZMQ_BUILD_DRAFT_API}

function zmq_ctx_set_ext(context_: pointer; option_: cint; const optval_: pointer; optvallen_: csize_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_ctx_set_ext) then ErrorUnassigned('zmq_ctx_set_ext');
 Result:=ZmqApiRec.zmq_ctx_set_ext(context_, option_, optval_, optvallen_);
end;

function zmq_ctx_get_ext(context_: pointer; option_: cint; optval_: pointer; optvallen_: pcsize_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_ctx_get_ext) then ErrorUnassigned('zmq_ctx_get_ext');
 Result:=ZmqApiRec.zmq_ctx_get_ext(context_, option_, optval_, optvallen_);
end;

function zmq_join(s: pointer; const group: pcchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_join) then ErrorUnassigned('zmq_join');
 Result:=ZmqApiRec.zmq_join(s, group);
end;

function zmq_leave(s: pointer; const group: pcchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_leave) then ErrorUnassigned('zmq_leave');
 Result:=ZmqApiRec.zmq_leave(s, group);
end;

function zmq_connect_peer(s_: pointer; const addr_: pcchar): cuint32; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_connect_peer) then ErrorUnassigned('zmq_connect_peer');
 Result:=ZmqApiRec.zmq_connect_peer(s_, addr_);
end;

function zmq_msg_set_routing_id(msg: Pzmq_msg_t; routing_id: cuint32): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_set_routing_id) then ErrorUnassigned('zmq_msg_set_routing_id');
 Result:=ZmqApiRec.zmq_msg_set_routing_id(msg, routing_id);
end;

function zmq_msg_routing_id(msg: Pzmq_msg_t): cuint32; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_routing_id) then ErrorUnassigned('zmq_msg_routing_id');
 Result:=ZmqApiRec.zmq_msg_routing_id(msg);
end;

function zmq_msg_set_group(msg: Pzmq_msg_t; const group: pcchar): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_set_group) then ErrorUnassigned('zmq_msg_set_group');
 Result:=ZmqApiRec.zmq_msg_set_group(msg, group);
end;

function zmq_msg_group(msg: Pzmq_msg_t): pcchar; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_group) then ErrorUnassigned('zmq_msg_group');
 Result:=ZmqApiRec.zmq_msg_group(msg);
end;

function zmq_msg_init_buffer(msg_: Pzmq_msg_t; const buf_: pointer;  size_: csize_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_msg_init_buffer) then ErrorUnassigned('zmq_msg_init_buffer');
 Result:=ZmqApiRec.zmq_msg_init_buffer(msg_, buf_, size_);
end;

function zmq_poller_new(): pointer; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_new) then ErrorUnassigned('zmq_poller_new');
 Result:=ZmqApiRec.zmq_poller_new();
end;

function zmq_poller_destroy(poller_p: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_destroy) then ErrorUnassigned('zmq_poller_destroy');
 Result:=ZmqApiRec.zmq_poller_destroy(poller_p);
end;

function zmq_poller_size(poller: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_size) then ErrorUnassigned('zmq_poller_size');
 Result:=ZmqApiRec.zmq_poller_size(poller);
end;

function zmq_poller_add(poller: pointer; socket: pointer; user_data: pointer; events: cshort): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_add) then ErrorUnassigned('zmq_poller_add');
 Result:=ZmqApiRec.zmq_poller_add(poller, socket, user_data, events);
end;

function zmq_poller_modify(poller: pointer; socket: pointer; events: cshort): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_modify) then ErrorUnassigned('zmq_poller_modify');
 Result:=ZmqApiRec.zmq_poller_modify(poller, socket, events);
end;

function zmq_poller_remove(poller: pointer; socket: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_remove) then ErrorUnassigned('zmq_poller_remove');
 Result:=ZmqApiRec.zmq_poller_remove(poller, socket);
end;

function zmq_poller_wait(poller: pointer; event: Pzmq_poller_event_t; timeout: clong): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_wait) then ErrorUnassigned('zmq_poller_wait');
 Result:=ZmqApiRec.zmq_poller_wait(poller, event, timeout);
end;

function zmq_poller_wait_all(poller: pointer; events: Pzmq_poller_event_t; n_events: cint; timeout: clong): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_wait_all) then ErrorUnassigned('zmq_poller_wait_all');
 Result:=ZmqApiRec.zmq_poller_wait_all(poller, events, n_events, timeout);
end;

function zmq_poller_fd(poller: pointer; fd: Pzmq_fd_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_fd) then ErrorUnassigned('zmq_poller_fd');
 Result:=ZmqApiRec.zmq_poller_fd(poller, fd);
end;

function zmq_poller_add_fd(poller: pointer; fd: zmq_fd_t; user_data: pointer; events: cshort): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_add_fd) then ErrorUnassigned('zmq_poller_add_fd');
 Result:=ZmqApiRec.zmq_poller_add_fd(poller, fd, user_data, events);
end;

function zmq_poller_modify_fd(poller: pointer; fd: zmq_fd_t; events: cshort): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_modify_fd) then ErrorUnassigned('zmq_poller_modify_fd');
 Result:=ZmqApiRec.zmq_poller_modify_fd(poller, fd, events);
end;

function zmq_poller_remove_fd(poller: pointer; fd: zmq_fd_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_poller_remove_fd) then ErrorUnassigned('zmq_poller_remove_fd');
 Result:=ZmqApiRec.zmq_poller_remove_fd(poller, fd);
end;

function zmq_socket_get_peer_state(socket: pointer; const routing_id: pointer; routing_id_size: csize_t): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_socket_get_peer_state) then ErrorUnassigned('zmq_socket_get_peer_state');
 Result:=ZmqApiRec.zmq_socket_get_peer_state(socket, routing_id, routing_id_size);
end;

function zmq_socket_monitor_versioned(s_: pointer; const addr_: pcchar;  events_: cuint64;  event_version_: cint; type_: cint): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_socket_monitor_versioned) then ErrorUnassigned('zmq_socket_monitor_versioned');
 Result:=ZmqApiRec.zmq_socket_monitor_versioned(s_, addr_, events_, event_version_,type_);
end;

function zmq_socket_monitor_pipes_stats(s: pointer): cint; cdecl;
begin
 CheckLibZmqDll;
 if not Assigned(ZmqApiRec.zmq_socket_monitor_pipes_stats) then ErrorUnassigned('zmq_socket_monitor_pipes_stats');
 Result:=ZmqApiRec.zmq_socket_monitor_pipes_stats(s);
end;

{$ENDIF}

///////////////////////////
// ZeroMQ utility functions
///////////////////////////

function zmq_version_int:Integer;
var major,minor,patch:cint;
begin
 Result:=0;
 try
  zmq_version(@major,@minor,@patch);
  Result:=major*10000+minor*100+patch;
 except
  on E:Exception do BugReport(E,nil,'zmq_version_int');
 end;
end;

function zmq_version_str:LongString;
var major,minor,patch:cint;
begin
 Result:='';
 try
  zmq_version(@major,@minor,@patch);
  Result:=Format('%u.%u.%u',[major,minor,patch]);
 except
  on E:Exception do BugReport(E,nil,'zmq_version_str');
 end;
end;

function zmq_send_cstring(Socket:Pointer; const S:LongString; Flags:Integer=ZMQ_DONTWAIT):Integer;
begin
 Result:=zmq_send_string(Socket,S,Flags,True);
end;

function zmq_send_string(Socket:Pointer; const S:LongString; Flags:Integer=ZMQ_DONTWAIT; ZTerm:Boolean=False):Integer;
var rc:Integer; Msg:zmq_msg_t; Buff,Data:Pointer; Size:SizeInt;
begin
 Result:=0;
 if (Socket<>nil) and (S<>'') then
 try
  rc:=0;
  Buff:=Pointer(S); // Add zero terminator?
  Size:=Length(S); if ZTerm then Inc(Size);
  if (zmq_msg_init_size(@Msg,Size)=0) then
  try
   Data:=zmq_msg_data(@Msg);
   if (Data<>nil) then begin
    SafeMove(Buff^,Data^,Size);
    rc:=zmq_msg_send(@Msg,Socket,Flags);
    Result:=Max(0,rc);
   end;
  finally
   if (rc=-1) then zmq_msg_close(@Msg);
  end;
 except
  on E:Exception do BugReport(E,nil,'zmq_send_string');
 end;
end;

function zmq_recv_cstring(Socket:Pointer; Flags:Integer=ZMQ_DONTWAIT):LongString;
begin
 Result:=zmq_recv_string(Socket,Flags,True);
end;

function zmq_recv_string(Socket:Pointer; Flags:Integer=ZMQ_DONTWAIT; ZTerm:Boolean=False):LongString;
var Msg:zmq_msg_t; Data:Pointer; Size,N:Integer;
begin
 Result:='';
 if (Socket<>nil) then
 try
  if (zmq_msg_init(@Msg)=0) then
  try
   Size:=zmq_msg_recv(@Msg,Socket,Flags);
   Data:=zmq_msg_data(@Msg);
   if (Size>0) and (Data<>nil) then begin
    Result:=StringBuffer(Data,Size);
    if ZTerm then begin
     N:=StrLLen(PChar(Result),Length(Result));
     if (N<>Length(Result)) then Result:=Copy(Result,1,N);
    end;
   end;
  finally
   zmq_msg_close(@Msg);
  end;
 except
  on E:Exception do BugReport(E,nil,'zmq_send_string');
 end;
end;

function zmq_recv_stream(Socket:Pointer; S:TMemoryStream; Flags:Integer=ZMQ_DONTWAIT):Integer;
var Msg:zmq_msg_t; Data:Pointer; Size:Integer;
begin
 Result:=0;
 if (Socket<>nil) and (S<>nil) then
 try
  if (zmq_msg_init(@Msg)=0) then
  try
   Size:=zmq_msg_recv(@Msg,Socket,Flags);
   Data:=zmq_msg_data(@Msg);
   if (Size>0) and (Data<>nil) then begin
    S.Write(Data^,Size);
    Result:=Size;
   end;
  finally
   zmq_msg_close(@Msg);
  end;
 except
  on E:Exception do BugReport(E,nil,'zmq_recv_stream');
 end;
end;

///////////////////////////////////////
// Unit initialization and finalization
///////////////////////////////////////

procedure Init_crw_zmqlib;
begin
 ClearZmqApiRec;
 CheckLibZmqDll;
end;

procedure Free_crw_zmqlib;
begin
 FreeLibZmqDll;
end;

initialization

 Init_crw_zmqlib;

finalization

 Free_crw_zmqlib;

end.

//////////////
// END OF FILE
//////////////

