/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2009-2023. All Rights Reserved.
 *
 * 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.
 *
 * %CopyrightEnd%
 */

/* Include file for writers of Native Implemented Functions. 
*/

#ifndef __ERL_NIF_H__
#define __ERL_NIF_H__

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include "erl_drv_nif.h"

/* Version history:
** 0.1: R13B03
** 1.0: R13B04
** 2.0: R14A
** 2.1: R14B02 "vm_variant"
** 2.2: R14B03 enif_is_exception
** 2.3: R15 enif_make_reverse_list, enif_is_number
** 2.4: R16 enif_consume_timeslice
** 2.5: First experimental maps API additions (libs of this version is not compatible with any other VM)
** 2.5: R17 Maps API additions
** 2.6: R17 with maps
**      R17 dirty schedulers
** 2.7: 17.3 add enif_schedule_nif
**           remove enif_schedule_dirty_nif, enif_schedule_dirty_nif_finalizer, enif_dirty_nif_finalizer
**           add ErlNifEntry options
**           add ErlNifFunc flags
** 2.8: 18.0 add enif_has_pending_exception
** 2.9: 18.2 enif_getenv
** 2.10: Time API
** 2.11: 19.0 enif_snprintf 
** 2.12: 20.0 add enif_select, enif_open_resource_type_x
** 2.13: 20.1 add enif_ioq
** 2.14: 21.0 add enif_ioq_peek_head, enif_(mutex|cond|rwlock|thread)_name
**                enif_vfprintf, enif_vsnprintf, enif_make_map_from_arrays
** 2.15: 22.0 ERL_NIF_SELECT_CANCEL, enif_select_(read|write)
**            enif_term_type
** 2.16: 24.0 enif_init_resource_type, enif_dynamic_resource_call
** 2.17: 26.0 enif_set_option, enif_get_string_length, enif_make_new_atom,
**            enif_make_new_atom_len, ERL_NIF_UTF8
*/
#define ERL_NIF_MAJOR_VERSION 2
#define ERL_NIF_MINOR_VERSION 17

/*
 * WHEN CHANGING INTERFACE VERSION, also replace erts version below with
 * a ticket number e.g. "erts-@OTP-12345@". The syntax is the same as for
 * runtime dependencies so multiple tickets should be separated with ":", e.g.
 * "erts-@OTP-12345:OTP-54321@".
 *
 * If you're not on the OTP team, you should use a placeholder like
 * erts-@MyName@ instead.
 */
#define ERL_NIF_MIN_ERTS_VERSION "erts-14.0"

/*
 * The emulator will refuse to load a nif-lib with a major version
 * lower than ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD. The load
 * may however fail if user have not removed use of deprecated
 * symbols.
 *
 * The ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD have to allow
 * loading of nif-libs built at least two major OTP releases
 * ago.
 */
#define ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD 2

#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef ErlNapiUInt64 ErlNifUInt64;
typedef ErlNapiSInt64 ErlNifSInt64;
typedef ErlNapiUInt ErlNifUInt;
typedef ErlNapiSInt ErlNifSInt;

#define ERL_NIF_VM_VARIANT "beam.vanilla"
typedef ErlNifUInt ERL_NIF_TERM;

typedef ERL_NIF_TERM ERL_NIF_UINT;

typedef ErlNifSInt64 ErlNifTime;

#define ERL_NIF_TIME_ERROR ((ErlNifSInt64) ERTS_NAPI_TIME_ERROR__)

typedef enum {
    ERL_NIF_SEC    = ERTS_NAPI_SEC__,
    ERL_NIF_MSEC   = ERTS_NAPI_MSEC__,
    ERL_NIF_USEC   = ERTS_NAPI_USEC__,
    ERL_NIF_NSEC   = ERTS_NAPI_NSEC__
} ErlNifTimeUnit;

struct enif_environment_t;
typedef struct enif_environment_t ErlNifEnv;

typedef struct enif_func_t
{
    const char* name;
    unsigned arity;
    ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
    unsigned flags;
}ErlNifFunc;


typedef struct enif_entry_t
{
    int major;
    int minor;
    const char* name;
    int num_of_funcs;
    ErlNifFunc* funcs;
    int  (*load)   (ErlNifEnv*, void** priv_data, ERL_NIF_TERM load_info);
    int  (*reload) (ErlNifEnv*, void** priv_data, ERL_NIF_TERM load_info);
    int  (*upgrade)(ErlNifEnv*, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info);
    void (*unload) (ErlNifEnv*, void* priv_data);

    /* Added in 2.1 */
    const char* vm_variant;

    /* Added in 2.7 */
    unsigned options;   /* Unused. Can be set to 0 or 1 (dirty sched config) */

    /* Added in 2.12 */
    size_t sizeof_ErlNifResourceTypeInit;

    /* Added in 2.14 */
    const char* min_erts;
}ErlNifEntry;


typedef struct
{
    size_t size;
    unsigned char* data;

    /* Internals (avert your eyes) */
    void* ref_bin;
    /* for future additions to be ABI compatible (same struct size) */
    void* __spare__[2];
}ErlNifBinary;

#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
typedef void* ErlNifEvent; /* FIXME: Use 'HANDLE' somehow without breaking existing source */
#else
typedef int ErlNifEvent;
#endif

/* Return bits from enif_select: */
#define ERL_NIF_SELECT_STOP_CALLED    (1 << 0)
#define ERL_NIF_SELECT_STOP_SCHEDULED (1 << 1)
#define ERL_NIF_SELECT_INVALID_EVENT  (1 << 2)
#define ERL_NIF_SELECT_FAILED         (1 << 3)
#define ERL_NIF_SELECT_READ_CANCELLED (1 << 4)
#define ERL_NIF_SELECT_WRITE_CANCELLED (1 << 5)
#define ERL_NIF_SELECT_ERROR_CANCELLED (1 << 6)
#define ERL_NIF_SELECT_NOTSUP          (1 << 7)

typedef enum
{
    ERL_NIF_RT_CREATE = 1,
    ERL_NIF_RT_TAKEOVER = 2
}ErlNifResourceFlags;

typedef enum
{
    ERL_NIF_LATIN1 = 1,
    ERL_NIF_UTF8 = 2,
}ErlNifCharEncoding;

typedef struct
{
    ERL_NIF_TERM pid;  /* internal, may change */
} ErlNifPid;

typedef struct
{
    ERL_NIF_TERM port_id;  /* internal, may change */
}ErlNifPort;

typedef ErlDrvMonitor ErlNifMonitor;

typedef void ErlNifOnHaltCallback(void *priv_data);

typedef struct enif_resource_type_t ErlNifResourceType;
typedef void ErlNifResourceDtor(ErlNifEnv*, void*);
typedef void ErlNifResourceStop(ErlNifEnv*, void*, ErlNifEvent, int is_direct_call);
typedef void ErlNifResourceDown(ErlNifEnv*, void*, ErlNifPid*, ErlNifMonitor*);
typedef void ErlNifResourceDynCall(ErlNifEnv*, void* obj, void* call_data);

typedef struct {
    ErlNifResourceDtor* dtor;
    ErlNifResourceStop* stop;  /* at ERL_NIF_SELECT_STOP event */
    ErlNifResourceDown* down;  /* enif_monitor_process */
    int members;
    ErlNifResourceDynCall* dyncall;
} ErlNifResourceTypeInit;

typedef ErlDrvSysInfo ErlNifSysInfo;

typedef struct ErlDrvTid_ *ErlNifTid;
typedef struct ErlDrvMutex_ ErlNifMutex;
typedef struct ErlDrvCond_ ErlNifCond;
typedef struct ErlDrvRWLock_ ErlNifRWLock;
typedef int ErlNifTSDKey;

typedef ErlDrvThreadOpts ErlNifThreadOpts;

typedef enum
{
    ERL_NIF_DIRTY_JOB_CPU_BOUND = ERL_DIRTY_JOB_CPU_BOUND,
    ERL_NIF_DIRTY_JOB_IO_BOUND  = ERL_DIRTY_JOB_IO_BOUND
}ErlNifDirtyTaskFlags;

typedef struct /* All fields all internal and may change */
{
    ERL_NIF_TERM map;
    ERL_NIF_UINT size;
    ERL_NIF_UINT idx;
    union {
        struct {
            ERL_NIF_TERM *ks;
            ERL_NIF_TERM *vs;
        }flat;
        struct {
            struct ErtsDynamicWStack_* wstack;
            ERL_NIF_TERM* kv;
        }hash;
    }u;
    void* __spare__[2]; /* for future additions to be ABI compatible (same struct size) */
} ErlNifMapIterator;

typedef enum {
    ERL_NIF_MAP_ITERATOR_FIRST = 1,
    ERL_NIF_MAP_ITERATOR_LAST = 2,

    /* deprecated synonyms (undocumented in 17 and 18-rc) */
    ERL_NIF_MAP_ITERATOR_HEAD = ERL_NIF_MAP_ITERATOR_FIRST,
    ERL_NIF_MAP_ITERATOR_TAIL = ERL_NIF_MAP_ITERATOR_LAST
} ErlNifMapIteratorEntry;

typedef enum {
    ERL_NIF_UNIQUE_POSITIVE = (1 << 0),
    ERL_NIF_UNIQUE_MONOTONIC = (1 << 1)
} ErlNifUniqueInteger;

typedef enum {
    ERL_NIF_BIN2TERM_SAFE = 0x20000000
} ErlNifBinaryToTerm;

typedef enum {
    ERL_NIF_INTERNAL_HASH = 1,
    ERL_NIF_PHASH2 = 2
} ErlNifHash;

#define ERL_NIF_IOVEC_SIZE 16

typedef struct erl_nif_io_vec {
    int iovcnt;  /* length of vectors */
    size_t size; /* total size in bytes */
    SysIOVec *iov;

    /* internals (avert your eyes) */
    void **ref_bins; /* Binary[] */
    int flags;

    /* Used when stack allocating the io vec */
    SysIOVec small_iov[ERL_NIF_IOVEC_SIZE];
    void *small_ref_bin[ERL_NIF_IOVEC_SIZE];
} ErlNifIOVec;

typedef struct erts_io_queue ErlNifIOQueue;

typedef enum {
    ERL_NIF_IOQ_NORMAL = 1
} ErlNifIOQueueOpts;

typedef enum {
    ERL_NIF_TERM_TYPE_ATOM = 1,
    ERL_NIF_TERM_TYPE_BITSTRING = 2,
    ERL_NIF_TERM_TYPE_FLOAT = 3,
    ERL_NIF_TERM_TYPE_FUN = 4,
    ERL_NIF_TERM_TYPE_INTEGER = 5,
    ERL_NIF_TERM_TYPE_LIST = 6,
    ERL_NIF_TERM_TYPE_MAP = 7,
    ERL_NIF_TERM_TYPE_PID = 8,
    ERL_NIF_TERM_TYPE_PORT = 9,
    ERL_NIF_TERM_TYPE_REFERENCE = 10,
    ERL_NIF_TERM_TYPE_TUPLE = 11,

    /* This is a dummy value intended to coax the compiler into warning about
     * unhandled values in a switch even if all the above values have been
     * handled. We can add new entries at any time so the user must always
     * have a default case. */
    ERL_NIF_TERM_TYPE__MISSING_DEFAULT_CASE__READ_THE_MANUAL = -1
} ErlNifTermType;

/*
 * Return values from enif_thread_type(). Negative values
 * reserved for specific types of non-scheduler threads.
 * Positive values reserved for scheduler thread types.
 */

#define ERL_NIF_THR_UNDEFINED 0
#define ERL_NIF_THR_NORMAL_SCHEDULER 1
#define ERL_NIF_THR_DIRTY_CPU_SCHEDULER 2
#define ERL_NIF_THR_DIRTY_IO_SCHEDULER 3

typedef enum {
    ERL_NIF_OPT_DELAY_HALT = 1,
    ERL_NIF_OPT_ON_HALT = 2
} ErlNifOption;

#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
#  define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS
typedef struct {
#  include "erl_nif_api_funcs.h"
   void* erts_alc_test;
} TWinDynNifCallbacks;
extern TWinDynNifCallbacks WinDynNifCallbacks;
#  undef ERL_NIF_API_FUNC_DECL
#endif

#ifdef STATIC_ERLANG_NIF_LIBNAME
#  define STATIC_ERLANG_NIF
#endif

#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) && !defined(STATIC_ERLANG_DRIVER) && !defined(STATIC_ERLANG_NIF)
#  define ERL_NIF_API_FUNC_MACRO(NAME) (WinDynNifCallbacks.NAME)
#  include "erl_nif_api_funcs.h"
/* note that we have to keep ERL_NIF_API_FUNC_MACRO defined */

#else /* non windows or included from emulator itself */

/* Redundant declaration of deallocator functions as they are referred to by
 * __attribute__(malloc) of allocator functions and we cannot change
 * the declaration order in erl_nif_api_funcs.h due to Windows. */
extern void enif_free(void* ptr);
extern void enif_mutex_destroy(ErlNifMutex *mtx);
extern void enif_cond_destroy(ErlNifCond *cnd);
extern void enif_rwlock_destroy(ErlNifRWLock *rwlck);
extern void enif_thread_opts_destroy(ErlNifThreadOpts *opts);
extern void enif_ioq_destroy(ErlNifIOQueue *q);

#  define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) extern RET_TYPE NAME ARGS
#  include "erl_nif_api_funcs.h"
#  undef ERL_NIF_API_FUNC_DECL
#endif

#if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_))
#  define ERL_NIF_INIT_GLOB TWinDynNifCallbacks WinDynNifCallbacks;
#  define ERL_NIF_INIT_ARGS TWinDynNifCallbacks* callbacks
#  define ERL_NIF_INIT_BODY memcpy(&WinDynNifCallbacks,callbacks,sizeof(TWinDynNifCallbacks))
#  define ERL_NIF_INIT_EXPORT __declspec(dllexport)
#else 
#  define ERL_NIF_INIT_GLOB
#  define ERL_NIF_INIT_ARGS void
#  define ERL_NIF_INIT_BODY
#  if defined(__GNUC__) && __GNUC__ >= 4
#    define ERL_NIF_INIT_EXPORT __attribute__ ((visibility("default")))
#  elif defined (__SUNPRO_C) && (__SUNPRO_C >= 0x550)
#    define ERL_NIF_INIT_EXPORT __global
#  else
#    define ERL_NIF_INIT_EXPORT
#  endif
#endif


#ifdef STATIC_ERLANG_NIF
#  ifdef STATIC_ERLANG_NIF_LIBNAME
#    define ERL_NIF_INIT_NAME(MODNAME) ERL_NIF_INIT_NAME2(STATIC_ERLANG_NIF_LIBNAME)
#    define ERL_NIF_INIT_NAME2(LIB) ERL_NIF_INIT_NAME3(LIB)
#    define ERL_NIF_INIT_NAME3(LIB) LIB ## _nif_init
#  else
#    define ERL_NIF_INIT_NAME(MODNAME) MODNAME ## _nif_init
#  endif
#  define ERL_NIF_INIT_DECL(MODNAME) \
          ErlNifEntry* ERL_NIF_INIT_NAME(MODNAME)(ERL_NIF_INIT_ARGS)
#else
#  define ERL_NIF_INIT_DECL(MODNAME) \
          ERL_NIF_INIT_EXPORT ErlNifEntry* nif_init(ERL_NIF_INIT_ARGS)
#endif


#ifdef __cplusplus
}
#  define ERL_NIF_INIT_PROLOGUE extern "C" {
#  define ERL_NIF_INIT_EPILOGUE }
#else
#  define ERL_NIF_INIT_PROLOGUE
#  define ERL_NIF_INIT_EPILOGUE
#endif


#define ERL_NIF_INIT(NAME, FUNCS, LOAD, RELOAD, UPGRADE, UNLOAD) \
ERL_NIF_INIT_PROLOGUE                   \
ERL_NIF_INIT_GLOB                       \
ERL_NIF_INIT_DECL(NAME);		\
ERL_NIF_INIT_DECL(NAME)			\
{					\
    static ErlNifEntry entry = 		\
    {					\
	ERL_NIF_MAJOR_VERSION,		\
	ERL_NIF_MINOR_VERSION,		\
	#NAME,				\
	sizeof(FUNCS) / sizeof(*FUNCS),	\
	FUNCS,				\
	LOAD, RELOAD, UPGRADE, UNLOAD,	\
	ERL_NIF_VM_VARIANT,		\
        1,                              \
        sizeof(ErlNifResourceTypeInit), \
        ERL_NIF_MIN_ERTS_VERSION        \
    };                                  \
    ERL_NIF_INIT_BODY;                  \
    return &entry;			\
}                                       \
ERL_NIF_INIT_EPILOGUE

#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP))
#define HAVE_USE_DTRACE 1
#endif

#ifdef HAVE_USE_DTRACE
ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
ERL_NIF_TERM erl_nif_user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
#endif

#endif /* __ERL_NIF_H__ */

