/*
 * Copyright (c) 1997-2024 Ross Cunniff
 *
 * 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.
 */

/* Header file for OADL system library interface.  An OADL system
 * library is a dynamically loaded library in system-dependent format
 * (e.g. a DLL on Win32, a .dynlib on MacOS, or a .so on Linux).  When
 * loaded (via a "using extern" statement) the library is first sought in a
 * system-specific location (although the OADLSYS environment variable
 * is widely recognized).  It is then dynamically loaded, and
 * the entry point "OADL_libname" is queried.  This entry point must
 * be of type OADL_SysInitProc, below.  Once that entry point is
 * determined, it is then called, passing in the OADL_FindProc routine.
 * The SysInitProc can then use the FindProc routine to find various
 * entry points required for operation.  These entry points
 * are documented below, but the most important one for a typical
 * system library is OADL_AddExtern which will define and register an
 * entry point of type OADL_SysCall - which all OADL extern procs
 * are.
 *
 * On shutdown / reload / restart, the OADL_SysTermProc for your
 * library is called. It is of type OADL_SysTermProc_fp, and is
 * named "OADL_libname_term" in your syslib shared library.
 */

#ifndef _OADL_SYS_INCLUDED // [
#define _OADL_SYS_INCLUDED 1

/* version number for interface purposes */
#define OADL_SYS_VER    101

/* Generic function pointer prototype, since C does not have a
 * function pointer equivalent to "void *"
 */
typedef int (*OADLproc)(void);

/* Used to find a named OADL procedure - see below for list.  "name"
 * is the name of the procedure (e.g. "OADL_AddExtern").  "version" is the
 * interface version - pass in OADL_SYS_VER.  The procedure
 * is returned in rProc - you will want to put that in the appropriately
 * typed pointer as defined below.  Returns 1 if found, 0 otherwise.
 * "ctx" should be passed the same context that SysInitProc was
 * called with.  Additionally, the "ctx" in all of these routines
 * refers to the "ctx" that the system procedure was called with.
 */
typedef int (*OADL_FindProc_fp)(void *ctx, const char *name,
                                int version, OADLproc *rProc);

/* This is the typedef of your system library initialization proc.
 * OADL queries the symbol from the dynamic library, as documented above.
 * "ctx" is an opaque OADL ctx, which needs to be passed back to
 * the various callback routines.
 */
typedef int (*OADL_SysInitProc_fp)(void *ctx, OADL_FindProc_fp findProc);

/* This is the typedef of your system library termination proc.
 * OADL queries the symbol from the dynamic library, as documented above.
 * "ctx" is an opaque OADL ctx, which needs to be passed back to
 * the various callback routines.
 */
typedef int (*OADL_SysTermProc_fp)(void *ctx);

/* You should make sure that your system library procedures are
 * compatible with this prototype.  Each syslib proc takes a pointer
 * to a result OadlVar "pRes", the number of arguments the OADL program
 * passed in "nargs", and the array of arguments "args".  Return
 * value is generally 1.  If return value is 0, some error
 * occurred, and the syslib proc probably has called ThrowError.
 * Returning 0 without calling ThrowError can cause OADL stack corruption.
 */
typedef int (*OADL_Extern_fp)(void *ctx, OadlVar *pRes,
                              int nargs, const OadlVar *args);

/* The routines that can be queried by FindProc.  To find these routines,
 * ask for the name without the "_fp" suffix.  names are case-sensitive.
 * Generally, if any of these routines return zero, it means an exception
 * has been raised and the calling system library function should
 * immediately return 0 (indicating aborted processing)
 */

/* OADL_AddExtern(ctx, name, proc)
 *      The heart of the system library implementation.  Tells OADL
 *      that the "proc" with the given "name" is implemented by this library.
 *      The "name" must include the namespace if it is intended to be part
 *      of that namespace - e.g. io::printf. Note that "name" may be a
 *      UTF-8 encoded wide string.
 */
typedef int (*OADL_AddExtern_fp)(void *ctx, const char *name,
                                 OADL_Extern_fp proc);

/* OADL_GetProp(ctx, pRes, propNum)
 *      Gets the (possibly private) property index number "propNum" of the
 *      current "self" object and returns its value in "*pRes"
 */
typedef int (*OADL_GetProp_fp)(void *ctx, OadlVar *pRes, OadlVar propNum);

/* OADL_SetProp(ctx, pRes, propNum, val)
 *      Sets the (possibly private) property index number "propNum" of the
 *      current "self" object to the given "val"
 */
typedef int (*OADL_SetProp_fp)(void *ctx, OadlVar propNum, OadlVar val);

/* OADL_GetIndex(ctx, pRes, v, nIdx, idx[])
 *      Returns "v[idx]" in "*pRes".  Always safe even in the face
 *      of asynchronous GC
 */
typedef int (*OADL_GetIndex_fp)(void *ctx, OadlVar *pRes, OadlVar v,
                                int nIdx, int idx[]);

/* OADL_SetIndex(ctx, pRes, v, nIdx, idx, val)
 *      Equivalent of "v[idx0,idx1,...] = val".  Always safe even in the face
 *      of asynchronous GC
 */
typedef int (*OADL_SetIndex_fp)(void *ctx, OadlVar v,
                                int nIdx, int idx[], OadlVar val);

/* OADL_WriteCopy(ctx, pRes, v)
 *      To write directly into arrData (i.e., instead of calling
 *      OADL_SetIndex), one must first call WriteCopy. The integer at
 *      *pRes will be changed to 1 if a write copy was created, 0 otherwise.
 *      It is not necessary to call OADL_WriteCopy on arrays created
 *      by the current extern call.  Note that OADL_WriteCopy can trigger
 *      a GC.
 */
typedef int (*OADL_WriteCopy_fp)(void *ctx, int *pRes, OadlVar v);

/* OADL_GetSubr(ctx, pDst, v, nargs, args )
 *      Returns "v[v0:v1...vn:vm]" in *pDst.  Note that this allocates memory
 *      and may result in an asynchronous GC which may invalidate any
 *      locally cached pointers to OADL heap objects.
 */
typedef int (*OADL_GetSubr_fp)(void *, OadlVar *, OadlVar, int, OadlVar *);

/* Structure for querying array information */
#define OADL_ARRAY_READONLY     0x01    /* Do not modify contents */
#define OADL_ARRAY_PERMANENT    0x02    /* Versus dyamic / GC memory */
#define OADL_ARRAY_USER_DATA    0x04    /* Points to user data, not OADL data */
#define OADL_ARRAY_ITERATOR     0x08    /* Iterator - only if not expanded */

/* If the array is an iterator and the caller requested the parameters,
 * the arrData is a pointer to an array of the following items (in the
 * type given by arrType):
 */
#define OADL_ARR_ITER_PARAM_FIRST       0
#define OADL_ARR_ITER_PARAM_LAST        1
#define OADL_ARR_ITER_PARAM_INCR        2
#define OADL_ARR_ITER_PARAM_COUNT       3 // Total size of iterator param array

typedef struct {
    uint32_t arrSize;                   /* Total size of array, in elements */
    uint8_t arrType;                    /* One of OADL_VT_* */
    uint8_t elemSize;                   /* size of a single element, in bytes */
    uint8_t arrRank;                    /* Rank - number of dimensions */
    uint8_t pad0;                       /* Pad, ignored */
    uint32_t arrFlags;                  /* flags from OADL_ARRAY_*, above */
    uint32_t arrShape[OADL_MAX_RANK];   /* Shape of the array */
    uint32_t arrStrides[OADL_MAX_RANK]; /* Dimension strides, in elements */
    OadlVar arrVar;                     /* Only if iter. expanded to heap */
    void *arrData;                      /* Pointer to array data or params */
    void *arrAlloc;                     /* Only if iter. expanded to malloc */
} OADL_ArrayInfo;

/* Iterator flags for OADL_GetArrayInfo */
#define OADL_AIF_ITERATOR_EXPANSION     0x00000007 // Mask for AIF_ITERATOR*
#define OADL_AIF_ITERATOR_HEAP          0x00000001 // ...expand into heap
#define OADL_AIF_ITERATOR_MALLOC        0x00000002 // ...expand into malloc
#define OADL_AIF_ITERATOR_SCRATCH       0x00000003 // ...expand into scratch
#define OADL_AIF_ITERATOR_PARAMS        0x00000004 // ...raw params in arrData
                                                   //    (i.e. not expanded)

/* OADL_GetArrayInfo(ctx, pArr, a)
 *      Returns info about a in OADL_ArrayInfo *pArr. flags are from
 *      OADL_AIF_*, above.
 *
 *      If the OADL_AIF_ITERATOR_EXPANSION flags are set, one of the
 *      following actions are performed:
 *
 *          OADL_AIF_ITERATOR_HEAP
 *              Allocates a new array from the heap and returns the
 *              newly created array expansion
 *
 *          OADL_AIF_ITERATOR_MALLOC
 *              Allocates a new pseudo-array via OADL_malloc and
 *              returns the newly created array expansion. The caller
 *              must OADL_free the returned arrAlloc pointer
 *
 *          OADL_AIF_ITERATOR_SCRATCH
 *              Allocates a new pseudo-array via OADL_AllocScratch and
 *              returns the newly created array expansion.
 */
typedef int (*OADL_GetArrayInfo_fp)(void *ctx, OADL_ArrayInfo *pArr, OadlVar a,
                                               int flags);

/* OADL_GetGcCounter(ctx)
 *      Returns the GC counter - if it changes a GC has happened
 *      and any local pointers to array/objects will be invalid
 */
typedef int (*OADL_GetGcCounter_fp)(void *ctx);

/* OADL_Writable(ctx, pRes, v)
 *      Returns "1" in *pRes if "v" is writable, and "0" if it is
 *      read-only.
 */
typedef int (*OADL_Writeable_fp)(void *ctx, int *pRes, OadlVar v);

/* OADL_TypeOf(ctx, pRes, v)
 *      Returns "typeof(v)" in "*pRes"
 */
typedef int (*OADL_TypeOf_fp)(void *ctx, int *pRes, OadlVar v);

/* OADL_FatalError(ctx, ErrNum)
 *      Raises fatal exception "ErrNum" from oadlerr.h
 */
typedef int (*OADL_FatalError_fp)(void *ctx, int ErrNum);

/* OADL_ThrowError(ctx, ErrNum)
 *      Throws exception "ErrNum" from oadlerr.h
 */
typedef int (*OADL_ThrowError_fp)(void *ctx, int ErrNum);

/* OADL_FindPublic(ctx, pRes, name)
 *      Find the public named "name" and returns its value in *pRes
 *      Note that "name" may be a UTF-8 encoded wide string.
 */
typedef int (*OADL_FindPublic_fp)(void *ctx, OadlVar *pRes,
                                  const char *name);

/* OADL_FindExtern(ctx, pRes, name)
 *      Find the extern named "name" and returns its value in *pRes
 *      Note that "name" may be a UTF-8 encoded wide string.
 */
typedef int (*OADL_FindExtern_fp)(void *ctx, OadlVar *pRes,
                                  const char *name);

/* OADL_FindObject(ctx, pRes, name)
 *      Find the object named "name" and returns its value in *pRes
 *      Note that "name" may be a UTF-8 encoded wide string.
 */
typedef int (*OADL_FindObject_fp)(void *ctx, OadlVar *pRes,
                                  const char *name);

/* OADL_FindClass(ctx, pRes, name)
 *      Find the object named "name" and returns its value in *pRes
 *      Note that "name" may be a UTF-8 encoded wide string.
 */
typedef int (*OADL_FindClass_fp)(void *ctx, OadlVar *pRes,
                                  const char *name);

/* OADL_GetSelf(ctx, pRes)
 *      Returns the value of "self" in *pRes
 */
typedef int (*OADL_GetSelf_fp)(void *ctx, OadlVar *pSelf);

/* OADL_GetPublic(ctx, pRes, obj, pub)
 *      Returns "obj.pub" in *pRes
 */
typedef int (*OADL_GetPublic_fp)(void *ctx, OadlVar *pRes,
                                 OadlVar obj, OadlVar pub);

/* Return values from OADL_SetPublic */
#define PUBLIC_OK       0       // Public was set OK
#define PUBLIC_ERROR    1       // Can't set a read-only constant
#define PUBLIC_OVERLOAD 2       // Needs a call to := operator
#define PUBLIC_DO_TC    3       // Needs intrinsic type conversion

/* OADL_SetPublic(ctx, obj, pub, v)
 *      Equivalent of "obj.pub = v"
 *      Returns one of PUBLIC_*, above
 */
typedef int (*OADL_SetPublic_fp)(void *ctx, OadlVar obj,
                                 OadlVar pub, OadlVar v);


/* OADL_FindGlobal(ctx, pRes, name)
 *      Returns the index of the global variable named "name" in *pRes
 *      Note that "name" may be a UTF-8 encoded wide string.
 */
typedef int (*OADL_FindGlobal_fp)(void *ctx, int *pRes,
                                  const char *name);

/* OADL_GetGlobal(ctx, pRes, idx)
 *      Gets the global variable at index idx and puts it in pRes
 */
typedef int (*OADL_GetGlobal_fp)(void *ctx, OadlVar *pRes, int idx);

/* OADL_SetGlobal(ctx, idx, val)
 *      Sets the global variable at index idx to val
 */
typedef int (*OADL_SetGlobal_fp)(void *ctx, int idx, OadlVar val);

/* OADL_AllocMach(ctx, pRes, stackAlloc)
 *      Creates an OADL Machine capable of calling procedures
 *      immediately.  It allocates "stackAlloc" entries
 *      for the execution stack.
 */
typedef int (*OADL_AllocMach_fp)(void *ctx, void **pRes, int stackAlloc);

/* OADL_FreeMach(ctx, pRes, stackAlloc)
 *      Frees a machine previously allocated by OADL_AllocMach()
 */
typedef int (*OADL_FreeMach_fp)(void *ctx, void *ptr);

/* OADL_CallProc(ctx, typ, pRes, mach, obj, proc, nargs, args)
 *      Makes an immediate or deferred call to the OADL procedure
 *      whose value is "proc", or to the method whose value
 *      is "obj.proc" if obj is not nil (proc must be of type
 *      Public in that case).
 *
 *      The "typ" parameter defines how the call will be executed:
 *
 *              OADL_CALL_IMMEDIATE
 *                  Make an immediate call. The "mach" parameter must
 *                  be allocated by OADL_AllocMach() OADL_CallProc will
 *                  not return until the OADL procedure has completed
 *                  execution. The return value of the immediate call
 *                  will be placed in the OadlVar pointed to by the
 *                  "pRes" parameter. Most OADL procedures allocate
 *                  heap objects. This may trigger a GC cycle which
 *                  will invalidate any cached heap pointers. The
 *                  calling routine must revalidate any pointers to
 *                  heap objects after OADL_CallProc returns.
 *
 *              OADL_CALL_DEFERRED
 *                  Make a deferred call (a call placed on the current
 *                  executing mach stack). OADL_CallProc returns
 *                  immediately. Note that deferred calls do not return
 *                  any value to the calling context. This allows
 *                  multiple deferred calls to be scheduled
 *                  simultaneously without overflowing the stack with
 *                  unused results. Since the call is deferred, no heap
 *                  allocations will occur at the time of the
 *                  OADL_CallProc call. The extern that defers one
 *                  or more calls must return OADL_CALL_DEFERRED to
 *                  OADL.
 *
 *              OADL_CALL_CHAINED
 *                  Make a chained call. Chained calls are also
 *                  deferred; however, their results *are* returned to
 *                  the calling context. The extern that chains a call
 *                  must return OADL_CALL_CHAINED to OADL.
 *
 *
 * OADL_CallProc returns 1 on success and 0 on failure.
 */

// These start at "2" so normal extern return values of 0 and 1 don't
// interfere
#define OADL_CALL_IMMEDIATE     2
#define OADL_CALL_DEFERRED      3
#define OADL_CALL_CHAINED       4

typedef int (*OADL_CallProc_fp)(void *ctx, OadlVar *pRes, int typ, void *mach,
                                OadlVar obj, OadlVar proc,
                                int nargs, const OadlVar *args);

/* OADL_CreateDict(ctx, pRes, pList, nList)
 *      Creates a Dictionary holding exactly the key-value pairs found
 *      in the given list. nList must be a multiple of two.
 */

typedef int (*OADL_CreateDict_fp)(void *ctx, OadlVar *pRes,
                                  OadlVar *pList, int nList);
/* OADL_CreateArray(ctx, pRes, pArr)
 *      Creates an array with parameters taken from OADL_ArrayInfo *pArr.
 *      Returns the resulting array in *pRes.  This does allocate
 *      heap memory which could trigger a GC and invalidate any
 *      cached heap pointers.
 *
 *      Note that if pArr->arrData and pArr->arrStrides are ignored
 *      unless OADL_ARRAY_USER_DATA is specified in pArr->arrFlags.
 *
 *      pArr->arrSize and pArr->elemSize are always ignored, as they
 *      are calculated from the input in pArr->arrType and pArr->arrShape.
 *
 *      The pArr structure is updated with the actual array created.
 */
typedef int (*OADL_CreateArray_fp)(void *ctx, OadlVar *pRes,
                                   OADL_ArrayInfo *pArr);

/* OADL_CreateObject(ctx, pRes, vClass)
 *      Creates a new instance of class vClass. Note that neither
 *      the create() nor the complete() method of the class will
 *      be invoked; this must be done manually by the syslib.
 */
typedef int (*OADL_CreateObject_fp)(void *ctx, OadlVar *pRes,
                                    OadlVar vClass);

/* OADL_MakePointer(ctx, pRes, ptr)
 *      Registers the given C pointer as an OADL pointer, returning
 *      the result in *pRes.
 */
typedef int (*OADL_MakePointer_fp)(void *ctx, OadlVar *pRes, void *ptr);

/* OADL_DelPointer(ctx, v)
 *      De-registers the given OADL pointer.
 */
typedef int (*OADL_DelPointer_fp)(void *ctx, OadlVar v);

/* OADL_FindPointer(pRes, v)
 *      Finds the C pointer referred to by OADL pointer v and returns
 *      it in *pRes
 */
typedef int (*OADL_FindPointer_fp)(void *ctx, void **pRes, OadlVar v);

/* OADL_GetPubName(ctx, pRes, v)
 *      Returns the name of the public v.  Same as "pubname(v)"
 *      Note that "*pRes" will be UTF-8 encoded if the result is
 *      a wide string.
 */
typedef int (*OADL_GetPubName_fp)(void *ctx, char **pRes, OadlVar v);

/* OADL_GetExtName(ctx, pRes, v)
 *      Returns the name of the extern v.
 *      Note that "*pRes" will be UTF-8 encoded if the result is
 *      a wide string.
 */
typedef int (*OADL_GetExtName_fp)(void *ctx, char **pRes, OadlVar v);

/* OADL_GetObjName(ctx, pRes, v)
 *      Returns the name of the object v.  Same as "objname(v)"
 *      Note that "*pRes" will be UTF-8 encoded if the result is
 *      a wide string.
 */
typedef int (*OADL_GetObjName_fp)(void *ctx, char **pRes, OadlVar v);

/* OADL_fgets(ctx, pRes, f)
 *      Reads a string from the UTF8 stream f returning the resulting OADL
 *      String or WideString object in *pRes
 */
typedef int (*OADL_fgets_fp)(void *ctx, OadlVar *pRes, OadlVar f);

/* OADL_fgetc(ctx, f)
 *      Reads a UTF8 character from the UTF8 stream f and returns
 *      the character as *pRes (note - returns the int, not the OadlVar)
 */
typedef int (*OADL_fgetc_fp)(void *ctx, int *pRes, OadlVar f);

/* OADL_fputc(ctx, f, c)
 *      Puts the (possibly wide) character c into UTF8 stream f
 */
typedef int (*OADL_fputc_fp)(void *ctx, OadlVar f, int c);

/* OADL_fputs(ctx, f, s)
 *      Puts the 8-bit character string "s" into UTF8 stream f
 */
typedef int (*OADL_fputs_fp)(void *ctx, OadlVar f, const char *s, int len);

/* OADL_wfputs(ctx, f, s)
 *      Puts the 32-bit character string "s" into UTF8 stream f
 */
typedef int (*OADL_wfputs_fp)(void *ctx, OadlVar f, const OADL_WCH *s,
                              int len);

/* OADL_ungetc(ctx, f, c)
 *      Pushes the character "c" back onto UTF8 stream f to be
 *      returned by subsequent UTF8 reads
 */
typedef int (*OADL_ungetc_fp)(void *ctx, OadlVar f, int c);

/* OADL_fopen(ctx, pRes, name, access)
 *      Opens a UTF8 stream and returns the File result in *pRes
 */
typedef int (*OADL_fopen_fp)(void *ctx, OadlVar *pRes,
                             const char *name, const char *access);

/* OADL_fclose(ctx, f)
 *      Close the UTF8 stream "f"
 */
typedef int (*OADL_fclose_fp)(void *ctx, OadlVar f);

/* Other OADL equivalents to stdio routines.  If you use
 * OADL_fopen, you need to use these.
 */
typedef int (*OADL_fflush_fp)(void *ctx, OadlVar f);
typedef int64_t (*OADL_fseek_fp)(void *ctx, OadlVar, int64_t, int);
typedef int64_t (*OADL_ftell_fp)(void *ctx, OadlVar);
typedef int (*OADL_fread_fp)(void *ctx, void *p, int n, int s, OadlVar f);
typedef int (*OADL_fwrite_fp)(void *ctx, void *p, int n, int s, OadlVar f);
typedef int (*OADL_feof_fp)(void *ctx, OadlVar f);
typedef int (*OADL_ferror_fp)(void *ctx, OadlVar f);
typedef int (*OADL_clearerr_fp)(void *ctx, OadlVar f);

/* Determines the Unicode Character Type from a given widechar value
 * between 0 and 0x110000. Returns -1 on error. See unicode.h for
 * possible character types.
 */
typedef int (*OADL_ucCharType_fp)(int);

/* Converts a widechar value between 0 and 0x110000. Returns the
 * original character if no single-character conversion is found.
 */
typedef int (*OADL_ucToUpper_fp)(int);
typedef int (*OADL_ucToLower_fp)(int);

/* OADL_utf8_bytelen(src, n)
 *      Returns the number of bytes that would be required to encode
 *      the 32-bit string src
 */
typedef int (*OADL_utf8_bytelen_fp)(OADL_WCH *src, int n);

/* OADL_utf8_wcharLen(src, n)
 *      Returns the number of WCHARS that would be required to decode
 *      the UTF-8 encoded string "src[n]"
 */
typedef int (*OADL_utf8_wcharlen_fp)(uint8_t *src, int n);

/* OADL_utf8_encode(ctx, dst, src, n)
 *      Copies the 32-bit string "src[n]" into the UTF8 string
 *      "dst" which must have enough space allocated to hold the
 *      complete string. Returns a pointer to the byte after
 *      the last byte placed.
 */
typedef uint8_t *(*OADL_utf8_encode_fp)(uint8_t *dst,
                                        uint32_t *src, int n);

/* OADL_utf8_decode(dst, src, len)
 *      Decodes a UTF-8 encoded string "src[n]" into the
 *      32-bit string "dst" which must have enough space allocated
 *      to hold the complete string. Returns a pointer to the
 *      uint32 after the last uint32 placed.
 */
typedef uint32_t *(*OADL_utf8_decode_fp)(uint32_t *dst,
                                         uint8_t *src, int n);
/* OADL_PushIntrinsic(ctx)
 *      Increments the intrinsic depth. Returns 1 on success, 0 if
 *      an error occured.
 */
typedef int (*OADL_PushIntrinsic_fp)(void *ctx);

/* OADL_PopIntrinsic(ctx)
 *      Decrements the intrinsic depth. Returns 1 on success, 0 if
 *      an error occured.
 */
typedef int (*OADL_PopIntrinsic_fp)(void *ctx);

/* OADL_EnumDict(ctx, dict, arg, func)
 *      Enumerates the contents of a dictionary, passing the
 *      arg to the func with each key/value pair. Note that
 *      a "nil" key or value indicates an undefined entry.
 *      Func should return 1 to terminate the enumeration
 *      early, and 0 otherwise.
 *
 *      Returns 1 if an invocation of func returned 1,
 *      and 0 otherwise (this facilitates the search
 *      for a specific set of keys).
 *
 *      func MUST NOT provoke a garbage collection cycle
 */
typedef int (*OADL_EnumDict_fp)(void *ctx, OadlVar dict,
                                void *arg,
                                int (*func)(void *, OadlVar, OadlVar));

/* OADL_AddProc(ctx, pVar, instrs, numInst)
 *      Adds a new proc based on the numInst instructions
 *      in instrs. The instrs/numInst are typically based on
 *      a cvtintr process. Result is returned in *pVar.
 */
 typedef int (*OADL_AddProc_fp)(void *ctx, OadlVar *pVar,
                                const uint8_t instrs[], int numInst);

/* OADL_NewPublic(ctx, pVar, vName)
 *      Creates a new public with name vName. Returns result in *pVar.
 */
typedef int (*OADL_NewPublic_fp)(void *ctx, OadlVar *pVar, OadlVar vName);

/* OADL_NewClass(ctx, pVar, vName, vPublics)
 *      Creates a new class with name vName (which can be nil)
 *      and initial key-value pairs vPublics (which must be a List).
 *      Returns result in *pVar.
 */
typedef int (*OADL_NewClass_fp)(void *ctx, OadlVar *pVar,
                                OadlVar vName, OadlVar vPublics);

/* OADL_RegComp(ctx, pRE, pattStr)
 *      Compiles the PCRE pattern "pattStr" into *pRE for subsequent
 *      use with OADL_RegEx().  Options are standard PCRE options,
 *      found in oadlpcre.h
 */
typedef int (*OADL_RegComp_fp)(void *ctx, void **pRE, char *pattStr,
                               int options);

/* OADL_RegEx(ctx, pRes, re, str, first, len, options, patOffs, patOffSize)
 *      Executes the PCRE reg exp. machine with compiled expression "RE".
 *      The matches are returned in patOffs[].  patOffs will be
 *      an array of [begin,end+1] pairs indicating the captured
 *      substrings, with patOffs[0]/patOffs[1] indicating the entire
 *      match.  first/len indicate the substring which is to be matched.
 *      Options are PCRE options, from the list found in oadlpcre.h.
 *      The number of pairs is returned in *pRes.
 */
typedef int (*OADL_RegEx_fp)(void *ctx, int *pRes, void *re,
                             char *str, int first, int len, int options,
                              int patOffs[], int patOffSize);

/* OADL_RegFree(ctx, re)
 *      Frees a PCRE regular expression previously created by OADL_RegComp()
 */
typedef int (*OADL_RegFree_fp)(void *ctx, void *re);

/* OADL_malloc / OADL_realloc / OADL_free
 *      Just like libc malloc/realloc/free but with OADL
 *      debugging hooks.  Also more likely to work independent
 *      of how malloc pools are implemented with shared libraries.
 */
typedef int (*OADL_malloc_fp)(void *ctx, void **pRes, int nBytes);
typedef int (*OADL_realloc_fp)(void *ctx, void **pRes, void *ptr, int nBytes);
typedef int (*OADL_free_fp)(void *ctx, void *ptr);

/* OADL_AllocScratch - allocates scratch space that is only valid
 *      until the next OADL_AllocScratch. No need to free this memory;
 *      it is recycled by client of OADL_AllocScratch
 */
typedef int (*OADL_AllocScratch_fp)(void *ctx, void **pRes, int nBytes);

/* If the extern library wants to hook/intercept say() and read(), it should
 * call this routine with the three entry points specified. Note that
 * the OADL_GetChar, OADL_SayChar, OADL_SayStr, and OADL_SayLStr functions
 * are NOT queryable via the normal OADL_FindProc mechanism; use the
 * OADL_GetIoProcs() function to get them instead.
 */
typedef int (*OADL_GetChar_fp)(void *ctx, OadlVar vf);
typedef void (*OADL_SayChar_fp)(void *ctx, int wch);
typedef void (*OADL_SayStr_fp)(void *ctx, char *s, int n);
typedef void (*OADL_SayLStr_fp)(void *ctx, OADL_WCH *s, int n);
typedef int (*OADL_IoProcs_fp)(void *ctx,
                               OADL_GetChar_fp getChar,
                               OADL_SayChar_fp sayChar,
                               OADL_SayStr_fp sayStr,
                               OADL_SayLStr_fp sayLStr);
/* Retrieve the current IO procs */
typedef int (*OADL_GetIoProcs_fp)(void *ctx,
                                  OADL_GetChar_fp *pGetChar,
                                  OADL_SayChar_fp *pSayChar,
                                  OADL_SayStr_fp *pSayStr,
                                  OADL_SayLStr_fp *pSayLStr);

/* OADL_ArrayIncr
 *      Increment a multidimensional array index and return the
 *      new offset to the current element. Note that a scalar may
 *      be aliased as an array by using a rank of 0. Note also
 *      that the proper offset to the next element (based on the
 *      contents of index[]) is computed if an offs of 0 is
 *      consistently provided in a loop over all elements.
 *
 *      Inputs:
 *              offs            Current offset (start at 0)
 *              rank            Rank (dimensionality) of array
 *              strides[]       Array of strides, from OADL_ArrayInfo
 *              shape[]         Shape of array, from OADL_ArrayInfo
 *              index[]         Current indices of array (start at {0})
 *      Result:
 *              Updated offs and index[] array
 */
static inline int OADL_ArrayIncr(int offs,
                                 int rank,
                                 const uint32_t strides[],
                                 const uint32_t shape[],
                                 uint32_t index[])
{
    int i;
    for (i = rank-1; i >= 0; i--) {
        /* Increment to next element in this rank */
        if (strides) offs += strides[i];
        if (shape[i] > ++index[i]) {
            /* We are done rolling up the odometer at this point */
            break;
        }
        /* Roll over the odometer and continue with the next higher index */
        index[i] = 0;
        /* Decrement back to beginning of this rank */
        if (strides) offs -= shape[i]*strides[i];
    }
    return offs;
}

/* OADL_IdxOffset
 *      Calculates the offset to an array element given an index, rank
 *      and strides. It is presumed that the index has been range-checked!
 */

static inline int OADL_IdxOffset(int rank,
                                 const uint32_t index[],
                                 const uint32_t strides[])
{
    int offs = 0;

    for (int i = 0; i < rank; i++) {
        offs += index[i] * strides[i];
    }

    return offs;
}

#endif // ] _OADL_SYS_INCLUDED