/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * All rights reserved.                                                      *
 *                                                                           *
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
 * terms governing use, modification, and redistribution, is contained in    *
 * the COPYING file, which can be found at the root of the source code       *
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
 * If you do not have access to either file, you may request a copy from     *
 * help@hdfgroup.org.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/****************/
/* Module Setup */
/****************/
#include "H5module.h" /* This source code file is part of the H5 module */

/***********/
/* Headers */
/***********/
#include "H5private.h"   /* Generic Functions                        */
#include "H5ACprivate.h" /* Metadata cache                           */
#include "H5CXprivate.h" /* API Contexts                             */
#include "H5Dprivate.h"  /* Datasets                                 */
#include "H5Eprivate.h"  /* Error handling                           */
#include "H5FLprivate.h" /* Free lists                               */
#include "H5FSprivate.h" /* File free space                          */
#include "H5Lprivate.h"  /* Links                                    */
#include "H5MMprivate.h" /* Memory management                        */
#include "H5Pprivate.h"  /* Property lists                           */
#include "H5PLprivate.h" /* Plugins                                  */
#include "H5SLprivate.h" /* Skip lists                               */
#include "H5Tprivate.h"  /* Datatypes                                */

#include "H5FDsec2.h" /* for H5FD_sec2_init() */

/****************/
/* Local Macros */
/****************/

/******************/
/* Local Typedefs */
/******************/

/********************/
/* Package Typedefs */
/********************/

/* Node for list of 'atclose' routines to invoke at library shutdown */
typedef struct H5_atclose_node_t {
    H5_atclose_func_t         func; /* Function to invoke */
    void                     *ctx;  /* Context to pass to function */
    struct H5_atclose_node_t *next; /* Pointer to next node in list */
} H5_atclose_node_t;

/********************/
/* Local Prototypes */
/********************/
static void H5__debug_mask(const char *);
#ifdef H5_HAVE_PARALLEL
static int H5__mpi_delete_cb(MPI_Comm comm, int keyval, void *attr_val, int *flag);
#endif /*H5_HAVE_PARALLEL*/

/*********************/
/* Package Variables */
/*********************/

/*****************************/
/* Library Private Variables */
/*****************************/

/* Library incompatible release versions, develop releases are incompatible by design */
const unsigned VERS_RELEASE_EXCEPTIONS[]    = {0, 1, 2, 3, 4};
const unsigned VERS_RELEASE_EXCEPTIONS_SIZE = 5;

/* statically initialize block for pthread_once call used in initializing */
/* the first global mutex                                                 */
#ifdef H5_HAVE_THREADSAFE
H5_api_t H5_g;
#else
hbool_t H5_libinit_g = FALSE; /* Library hasn't been initialized */
hbool_t H5_libterm_g = FALSE; /* Library isn't being shutdown */
#endif

hbool_t H5_use_selection_io_g = FALSE;

char           H5_lib_vers_info_g[] = H5_VERS_INFO;
static hbool_t H5_dont_atexit_g     = FALSE;
H5_debug_t     H5_debug_g; /* debugging info */

/*******************/
/* Local Variables */
/*******************/

/* Linked list of registered 'atclose' functions to invoke at library shutdown */
static H5_atclose_node_t *H5_atclose_head = NULL;

/* Declare a free list to manage the H5_atclose_node_t struct */
H5FL_DEFINE_STATIC(H5_atclose_node_t);

/*-------------------------------------------------------------------------
 * Function:    H5_default_vfd_init
 *
 * Purpose:     Initialize the default VFD.
 *
 * Return:      Success:        non-negative
 *              Failure:        negative
 *-------------------------------------------------------------------------
 */
static herr_t
H5_default_vfd_init(void)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)
    /* Load the hid_t for the default VFD for the side effect
     * it has of initializing the default VFD.
     */
    if (H5FD_sec2_init() == H5I_INVALID_HID) {
        HGOTO_ERROR(H5E_FUNC, H5E_CANTINIT, FAIL, "unable to load default VFD ID")
    }
done:
    FUNC_LEAVE_NOAPI(ret_value)
}

/*--------------------------------------------------------------------------
 * NAME
 *   H5_init_library -- Initialize library-global information
 * USAGE
 *    herr_t H5_init_library()
 *
 * RETURNS
 *    Non-negative on success/Negative on failure
 *
 * DESCRIPTION
 *    Initializes any library-global data or routines.
 *
 *--------------------------------------------------------------------------
 */
herr_t
H5_init_library(void)
{
    size_t i;
    char  *env_use_select_io = NULL;
    herr_t ret_value         = SUCCEED;

    FUNC_ENTER_NOAPI(FAIL)

    /* Run the library initialization routine, if it hasn't already run */
    if (H5_INIT_GLOBAL || H5_TERM_GLOBAL)
        HGOTO_DONE(SUCCEED)

    /* Set the 'library initialized' flag as early as possible, to avoid
     * possible re-entrancy.
     */
    H5_INIT_GLOBAL = TRUE;

#ifdef H5_HAVE_PARALLEL
    {
        int mpi_initialized;
        int mpi_finalized;
        int mpi_code;

        MPI_Initialized(&mpi_initialized);
        MPI_Finalized(&mpi_finalized);

        /* add an attribute on MPI_COMM_SELF to call H5_term_library
           when it is destroyed, i.e. on MPI_Finalize */
        if (mpi_initialized && !mpi_finalized) {
            int key_val;

            if (MPI_SUCCESS != (mpi_code = MPI_Comm_create_keyval(
                                    MPI_COMM_NULL_COPY_FN, (MPI_Comm_delete_attr_function *)H5__mpi_delete_cb,
                                    &key_val, NULL)))
                HMPI_GOTO_ERROR(FAIL, "MPI_Comm_create_keyval failed", mpi_code)

            if (MPI_SUCCESS != (mpi_code = MPI_Comm_set_attr(MPI_COMM_SELF, key_val, NULL)))
                HMPI_GOTO_ERROR(FAIL, "MPI_Comm_set_attr failed", mpi_code)

            if (MPI_SUCCESS != (mpi_code = MPI_Comm_free_keyval(&key_val)))
                HMPI_GOTO_ERROR(FAIL, "MPI_Comm_free_keyval failed", mpi_code)
        }
    }
#endif /*H5_HAVE_PARALLEL*/

    /*
     * Make sure the package information is updated.
     */
    HDmemset(&H5_debug_g, 0, sizeof H5_debug_g);
    H5_debug_g.pkg[H5_PKG_A].name  = "a";
    H5_debug_g.pkg[H5_PKG_AC].name = "ac";
    H5_debug_g.pkg[H5_PKG_B].name  = "b";
    H5_debug_g.pkg[H5_PKG_D].name  = "d";
    H5_debug_g.pkg[H5_PKG_E].name  = "e";
    H5_debug_g.pkg[H5_PKG_F].name  = "f";
    H5_debug_g.pkg[H5_PKG_G].name  = "g";
    H5_debug_g.pkg[H5_PKG_HG].name = "hg";
    H5_debug_g.pkg[H5_PKG_HL].name = "hl";
    H5_debug_g.pkg[H5_PKG_I].name  = "i";
    H5_debug_g.pkg[H5_PKG_M].name  = "m";
    H5_debug_g.pkg[H5_PKG_MF].name = "mf";
    H5_debug_g.pkg[H5_PKG_MM].name = "mm";
    H5_debug_g.pkg[H5_PKG_O].name  = "o";
    H5_debug_g.pkg[H5_PKG_P].name  = "p";
    H5_debug_g.pkg[H5_PKG_S].name  = "s";
    H5_debug_g.pkg[H5_PKG_T].name  = "t";
    H5_debug_g.pkg[H5_PKG_V].name  = "v";
    H5_debug_g.pkg[H5_PKG_VL].name = "vl";
    H5_debug_g.pkg[H5_PKG_Z].name  = "z";

    /*
     * Install atexit() library cleanup routines unless the H5dont_atexit()
     * has been called.  Once we add something to the atexit() list it stays
     * there permanently, so we set H5_dont_atexit_g after we add it to prevent
     * adding it again later if the library is closed and reopened.
     */
    if (!H5_dont_atexit_g) {

#if defined(H5_HAVE_THREADSAFE) && defined(H5_HAVE_WIN_THREADS)
        /* Clean up Win32 thread resources. Pthreads automatically cleans up.
         * This must be entered before the library cleanup code so it's
         * executed in LIFO order (i.e., last).
         */
        (void)HDatexit(H5TS_win32_process_exit);
#endif /* H5_HAVE_THREADSAFE && H5_HAVE_WIN_THREADS */

        /* Normal library termination code */
        (void)HDatexit(H5_term_library);

        H5_dont_atexit_g = TRUE;
    } /* end if */

    /*
     * Initialize interfaces that might not be able to initialize themselves
     * soon enough.  The file & dataset interfaces must be initialized because
     * calling H5P_create() might require the file/dataset property classes to be
     * initialized.  The property interface must be initialized before the file
     * & dataset interfaces though, in order to provide them with the proper
     * property classes.
     * The link interface needs to be initialized so that link property lists
     * have their properties registered.
     * The FS module needs to be initialized as a result of the fix for HDFFV-10160:
     *   It might not be initialized during normal file open.
     *   When the application does not close the file, routines in the module might
     *   be called via H5_term_library() when shutting down the file.
     * The dataspace interface needs to be initialized so that future IDs for
     *   dataspaces work.
     */
    /* clang-format off */
    struct {
        herr_t (*func)(void);
        const char *descr;
    } initializer[] = {
        {H5E_init, "error"}
    ,   {H5VL_init_phase1, "VOL"}
    ,   {H5SL_init, "skip lists"}
    ,   {H5FD_init, "VFD"}
    ,   {H5_default_vfd_init, "default VFD"}
    ,   {H5P_init_phase1, "property list"}
    ,   {H5AC_init, "metadata caching"}
    ,   {H5L_init, "link"}
    ,   {H5S_init, "dataspace"}
    ,   {H5PL_init, "plugins"}
    /* Finish initializing interfaces that depend on the interfaces above */
    ,   {H5P_init_phase2, "property list"}
    ,   {H5VL_init_phase2, "VOL"}
    };

    for (i = 0; i < NELMTS(initializer); i++) {
        if (initializer[i].func() < 0) {
            HGOTO_ERROR(H5E_FUNC, H5E_CANTINIT, FAIL,
                "unable to initialize %s interface", initializer[i].descr)
        }
    }
    /* clang-format on */

    /* Check for HDF5_USE_SELECTION_IO env variable */
    env_use_select_io = HDgetenv("HDF5_USE_SELECTION_IO");
    if (NULL != env_use_select_io && HDstrcmp(env_use_select_io, "") && HDstrcmp(env_use_select_io, "0") &&
        HDstrcmp(env_use_select_io, "no") && HDstrcmp(env_use_select_io, "No") &&
        HDstrcmp(env_use_select_io, "NO") && HDstrcmp(env_use_select_io, "false") &&
        HDstrcmp(env_use_select_io, "False") && HDstrcmp(env_use_select_io, "FALSE"))
        H5_use_selection_io_g = TRUE;

    /* Debugging? */
    H5__debug_mask("-all");
    H5__debug_mask(HDgetenv("HDF5_DEBUG"));

done:
    FUNC_LEAVE_NOAPI(ret_value)
} /* end H5_init_library() */

/*-------------------------------------------------------------------------
 * Function:    H5_term_library
 *
 * Purpose:    Terminate interfaces in a well-defined order due to
 *        dependencies among the interfaces, then terminate
 *        library-specific data.
 *
 * Return:    void
 *
 *-------------------------------------------------------------------------
 */
void
H5_term_library(void)
{
    int         pending, ntries   = 0;
    char        loop[1024], *next = loop;
    size_t      i;
    size_t      nleft = sizeof(loop);
    int         nprinted;
    H5E_auto2_t func;

#ifdef H5_HAVE_THREADSAFE
    /* explicit locking of the API */
    H5_FIRST_THREAD_INIT
    H5_API_LOCK
#endif

    /* Don't do anything if the library is already closed */
    if (!(H5_INIT_GLOBAL))
        goto done;

    /* Indicate that the library is being shut down */
    H5_TERM_GLOBAL = TRUE;

    /* Push the API context without checking for errors */
    H5CX_push_special();

    /* Check if we should display error output */
    (void)H5Eget_auto2(H5E_DEFAULT, &func, NULL);

    /* Iterate over the list of 'atclose' callbacks that have been registered */
    if (H5_atclose_head) {
        H5_atclose_node_t *curr_atclose; /* Current 'atclose' node */

        /* Iterate over all 'atclose' nodes, making callbacks */
        curr_atclose = H5_atclose_head;
        while (curr_atclose) {
            H5_atclose_node_t *tmp_atclose; /* Temporary pointer to 'atclose' node */

            /* Invoke callback, providing context */
            (*curr_atclose->func)(curr_atclose->ctx);

            /* Advance to next node and free this one */
            tmp_atclose  = curr_atclose;
            curr_atclose = curr_atclose->next;
            H5FL_FREE(H5_atclose_node_t, tmp_atclose);
        } /* end while */

        /* Reset list head, in case library is re-initialized */
        H5_atclose_head = NULL;
    } /* end if */

    /* clang-format off */

    /*
     * Terminate each interface. The termination functions return a positive
     * value if they do something that might affect some other interface in a
     * way that would necessitate some cleanup work in the other interface.
     */

#define TERMINATOR(module, wait) {      \
      .func = H5##module##_term_package \
    , .name = #module                   \
    , .completed = false                \
    , .await_prior = wait               \
    }

    /*
     * Termination is ordered by the `terminator` table so the "higher" level
     * packages are shut down before "lower" level packages that they
     * rely on:
     */
    struct {
        int (*func)(void);       /* function to terminate the module; returns 0
                                  * on success, >0 if termination was not
                                  * completed and we should try to terminate
                                  * some dependent modules, first.
                                  */
        const char *name;        /* name of the module */
        hbool_t     completed;   /* true iff this terminator was already
                                  * completed
                                  */
        const hbool_t await_prior;  /* true iff all prior terminators in the
                                     * list must complete before this
                                     * terminator is attempted
                                     */
    } terminator[] = {
        /* Close the event sets first, so that all asynchronous operations
         * complete before anything else attempts to shut down.
         */
        TERMINATOR(ES, false)
        /* Do not attempt to close down package L until after event sets
         * have finished closing down.
         */
    ,   TERMINATOR(L, true)
        /* Close the "top" of various interfaces (IDs, etc) but don't shut
         * down the whole interface yet, so that the object header messages
         * get serialized correctly for entries in the metadata cache and the
         * symbol table entry in the superblock gets serialized correctly, etc.
         * all of which is performed in the 'F' shutdown.
         *
         * The tops of packages A, D, G, M, S, T do not need to wait for L
         * or previous packages to finish closing down.
         */
    ,   TERMINATOR(A_top, false)
    ,   TERMINATOR(D_top, false)
    ,   TERMINATOR(G_top, false)
    ,   TERMINATOR(M_top, false)
    ,   TERMINATOR(S_top, false)
    ,   TERMINATOR(T_top, false)
        /* Don't shut down the file code until objects in files are shut down */
    ,   TERMINATOR(F, true)
        /* Don't shut down the property list code until all objects that might
         * use property lists are shut down
         */
    ,   TERMINATOR(P, true)
        /* Wait to shut down the "bottom" of various interfaces until the
         * files are closed, so pieces of the file can be serialized
         * correctly.
         *
         * Shut down the "bottom" of the attribute, dataset, group,
         * reference, dataspace, and datatype interfaces, fully closing
         * out the interfaces now.
         */
    ,   TERMINATOR(A, true)
    ,   TERMINATOR(D, false)
    ,   TERMINATOR(G, false)
    ,   TERMINATOR(M, false)
    ,   TERMINATOR(S, false)
    ,   TERMINATOR(T, false)
        /* Wait to shut down low-level packages like AC until after
         * the preceding high-level packages have shut down.  This prevents
         * low-level objects from closing "out from underneath" their
         * reliant high-level objects.
         */
    ,   TERMINATOR(AC, true)
        /* Shut down the "pluggable" interfaces, before the plugin framework */
    ,   TERMINATOR(Z, false)
    ,   TERMINATOR(FD, false)
    ,   TERMINATOR(VL, false)
        /* Don't shut down the plugin code until all "pluggable" interfaces
         * (Z, FD, PL) are shut down
         */
    ,   TERMINATOR(PL, true)
        /* Shut down the following packages in strictly the order given
         * by the table.
         */
    ,   TERMINATOR(E, true)
    ,   TERMINATOR(I, true)
    ,   TERMINATOR(SL, true)
    ,   TERMINATOR(FL, true)
    ,   TERMINATOR(CX, true)
    };

    do {
        pending = 0;
        for (i = 0; i < NELMTS(terminator); i++) {
            if (terminator[i].completed)
                continue;
            if (pending != 0 && terminator[i].await_prior)
                break;
            if (terminator[i].func() == 0) {
                terminator[i].completed = true;
                continue;
            }

            /* log a package when its terminator needs to be retried */
            pending++;
            nprinted = HDsnprintf(next, nleft, "%s%s",
                (next != loop) ? "," : "", terminator[i].name);
            if (nprinted < 0)
                continue;
            if ((size_t)nprinted >= nleft)
                nprinted = HDsnprintf(next, nleft, "...");
            if (nprinted < 0 || (size_t)nprinted >= nleft)
                continue;
            nleft -= (size_t)nprinted;
            next += nprinted;
        }
    } while (pending && ntries++ < 100);

    /* clang-format on */

    if (pending) {
        /* Only display the error message if the user is interested in them. */
        if (func) {
            HDfprintf(stderr, "HDF5: infinite loop closing library\n");
            HDfprintf(stderr, "      %s\n", loop);
#ifndef NDEBUG
            HDabort();
#endif    /* NDEBUG */
        } /* end if */
    }     /* end if */

    /* Free open debugging streams */
    while (H5_debug_g.open_stream) {
        H5_debug_open_stream_t *tmp_open_stream;

        tmp_open_stream = H5_debug_g.open_stream;
        (void)HDfclose(H5_debug_g.open_stream->stream);
        H5_debug_g.open_stream = H5_debug_g.open_stream->next;
        (void)H5MM_free(tmp_open_stream);
    } /* end while */

    /* Reset flag indicating that the library is being shut down */
    H5_TERM_GLOBAL = FALSE;

    /* Mark library as closed */
    H5_INIT_GLOBAL = FALSE;

    /* Don't pop the API context (i.e. H5CX_pop), since it's been shut down already */

done:
#ifdef H5_HAVE_THREADSAFE
    H5_API_UNLOCK
#endif /* H5_HAVE_THREADSAFE */

    return;
} /* end H5_term_library() */

/*-------------------------------------------------------------------------
 * Function:    H5dont_atexit
 *
 * Purpose:    Indicates that the library is not to clean up after itself
 *        when the application exits by calling exit() or returning
 *        from main().  This function must be called before any other
 *        HDF5 function or constant is used or it will have no effect.
 *
 *        If this function is used then certain memory buffers will not
 *        be de-allocated nor will open files be flushed automatically.
 *        The application may still call H5close() explicitly to
 *        accomplish these things.
 *
 * Return:    Success:    non-negative
 *
 *        Failure:    negative if this function is called more than
 *                once or if it is called too late.
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5dont_atexit(void)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API_NOINIT_NOERR_NOFS
    H5TRACE0("e", "");

    if (H5_dont_atexit_g)
        ret_value = FAIL;
    else
        H5_dont_atexit_g = TRUE;

    FUNC_LEAVE_API_NOFS(ret_value)
} /* end H5dont_atexit() */

/*-------------------------------------------------------------------------
 * Function:    H5garbage_collect
 *
 * Purpose:    Walks through all the garbage collection routines for the
 *        library, which are supposed to free any unused memory they have
 *        allocated.
 *
 *      These should probably be registered dynamically in a linked list of
 *          functions to call, but there aren't that many right now, so we
 *          hard-wire them...
 *
 * Return:    Success:    non-negative
 *
 *        Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5garbage_collect(void)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_API(FAIL)
    H5TRACE0("e", "");

    /* Call the garbage collection routines in the library */
    if (H5FL_garbage_coll() < 0)
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "can't garbage collect objects")

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5garbage_collect() */

/*-------------------------------------------------------------------------
 * Function:    H5set_free_list_limits
 *
 * Purpose:    Sets limits on the different kinds of free lists.  Setting a value
 *      of -1 for a limit means no limit of that type.  These limits are global
 *      for the entire library.  Each "global" limit only applies to free lists
 *      of that type, so if an application sets a limit of 1 MB on each of the
 *      global lists, up to 3 MB of total storage might be allocated (1MB on
 *      each of regular, array and block type lists).
 *
 *      The settings for block free lists are duplicated to factory free lists.
 *      Factory free list limits cannot be set independently currently.
 *
 * Parameters:
 *  int reg_global_lim;  IN: The limit on all "regular" free list memory used
 *  int reg_list_lim;    IN: The limit on memory used in each "regular" free list
 *  int arr_global_lim;  IN: The limit on all "array" free list memory used
 *  int arr_list_lim;    IN: The limit on memory used in each "array" free list
 *  int blk_global_lim;  IN: The limit on all "block" free list memory used
 *  int blk_list_lim;    IN: The limit on memory used in each "block" free list
 *
 * Return:    Success:    non-negative
 *
 *        Failure:    negative
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5set_free_list_limits(int reg_global_lim, int reg_list_lim, int arr_global_lim, int arr_list_lim,
                       int blk_global_lim, int blk_list_lim)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_API(FAIL)
    H5TRACE6("e", "IsIsIsIsIsIs", reg_global_lim, reg_list_lim, arr_global_lim, arr_list_lim, blk_global_lim,
             blk_list_lim);

    /* Call the free list function to actually set the limits */
    if (H5FL_set_free_list_limits(reg_global_lim, reg_list_lim, arr_global_lim, arr_list_lim, blk_global_lim,
                                  blk_list_lim, blk_global_lim, blk_list_lim) < 0)
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTSET, FAIL, "can't set garbage collection limits")

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5set_free_list_limits() */

/*-------------------------------------------------------------------------
 * Function:    H5get_free_list_sizes
 *
 * Purpose:    Gets the current size of the different kinds of free lists that
 *    the library uses to manage memory.  The free list sizes can be set with
 *    H5set_free_list_limits and garbage collected with H5garbage_collect.
 *      These lists are global for the entire library.
 *
 * Parameters:
 *  size_t *reg_size;    OUT: The current size of all "regular" free list memory used
 *  size_t *arr_size;    OUT: The current size of all "array" free list memory used
 *  size_t *blk_size;    OUT: The current size of all "block" free list memory used
 *  size_t *fac_size;    OUT: The current size of all "factory" free list memory used
 *
 * Return:    Success:    non-negative
 *        Failure:    negative
 *
 * Programmer:  Quincey Koziol
 *              Friday, March 6, 2020
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5get_free_list_sizes(size_t *reg_size /*out*/, size_t *arr_size /*out*/, size_t *blk_size /*out*/,
                      size_t *fac_size /*out*/)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE4("e", "xxxx", reg_size, arr_size, blk_size, fac_size);

    /* Call the free list function to actually get the sizes */
    if (H5FL_get_free_list_sizes(reg_size, arr_size, blk_size, fac_size) < 0)
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGET, FAIL, "can't get garbage collection sizes")

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5get_free_list_sizes() */

/*-------------------------------------------------------------------------
 * Function:    H5__debug_mask
 *
 * Purpose:     Set runtime debugging flags according to the string S.  The
 *              string should contain file numbers and package names
 *              separated by other characters. A file number applies to all
 *              following package names up to the next file number. The
 *              initial file number is `2' (the standard error stream). Each
 *              package name can be preceded by a `+' or `-' to add or remove
 *              the package from the debugging list (`+' is the default). The
 *              special name `all' means all packages.
 *
 *              The name `trace' indicates that API tracing is to be turned
 *              on or off.
 *
 *              The name 'ttop' indicates that only top-level API calls
 *              should be shown. This also turns on tracing as if the
 *              'trace' word was shown.
 *
 * Return:      void
 *
 *-------------------------------------------------------------------------
 */
static void
H5__debug_mask(const char *s)
{
    FILE   *stream = stderr;
    char    pkg_name[32], *rest;
    size_t  i;
    hbool_t clear;

    while (s && *s) {

        if (HDisalpha(*s) || '-' == *s || '+' == *s) {

            /* Enable or Disable debugging? */
            if ('-' == *s) {
                clear = TRUE;
                s++;
            }
            else if ('+' == *s) {
                clear = FALSE;
                s++;
            }
            else {
                clear = FALSE;
            } /* end if */

            /* Get the name */
            for (i = 0; HDisalpha(*s); i++, s++)
                if (i < sizeof pkg_name)
                    pkg_name[i] = *s;
            pkg_name[MIN(sizeof(pkg_name) - 1, i)] = '\0';

            /* Trace, all, or one? */
            if (!HDstrcmp(pkg_name, "trace")) {
                H5_debug_g.trace = clear ? NULL : stream;
            }
            else if (!HDstrcmp(pkg_name, "ttop")) {
                H5_debug_g.trace = stream;
                H5_debug_g.ttop  = (hbool_t)!clear;
            }
            else if (!HDstrcmp(pkg_name, "ttimes")) {
                H5_debug_g.trace  = stream;
                H5_debug_g.ttimes = (hbool_t)!clear;
            }
            else if (!HDstrcmp(pkg_name, "all")) {
                for (i = 0; i < (size_t)H5_NPKGS; i++)
                    H5_debug_g.pkg[i].stream = clear ? NULL : stream;
            }
            else {
                for (i = 0; i < (size_t)H5_NPKGS; i++) {
                    if (!HDstrcmp(H5_debug_g.pkg[i].name, pkg_name)) {
                        H5_debug_g.pkg[i].stream = clear ? NULL : stream;
                        break;
                    } /* end if */
                }     /* end for */
                if (i >= (size_t)H5_NPKGS)
                    HDfprintf(stderr, "HDF5_DEBUG: ignored %s\n", pkg_name);
            } /* end if-else */
        }
        else if (HDisdigit(*s)) {
            int                     fd = (int)HDstrtol(s, &rest, 0);
            H5_debug_open_stream_t *open_stream;

            if ((stream = HDfdopen(fd, "w")) != NULL) {
                (void)HDsetvbuf(stream, NULL, _IOLBF, (size_t)0);

                if (NULL ==
                    (open_stream = (H5_debug_open_stream_t *)H5MM_malloc(sizeof(H5_debug_open_stream_t)))) {
                    (void)HDfclose(stream);
                    return;
                } /* end if */

                open_stream->stream    = stream;
                open_stream->next      = H5_debug_g.open_stream;
                H5_debug_g.open_stream = open_stream;
            } /* end if */

            s = rest;
        }
        else {
            s++;
        } /* end if-else */
    }     /* end while */
} /* end H5__debug_mask() */

#ifdef H5_HAVE_PARALLEL

/*-------------------------------------------------------------------------
 * Function:    H5__mpi_delete_cb
 *
 * Purpose:    Callback attribute on MPI_COMM_SELF to terminate the HDF5
 *              library when the communicator is destroyed, i.e. on MPI_Finalize.
 *
 * Return:    MPI_SUCCESS
 *
 *-------------------------------------------------------------------------
 */
static int
H5__mpi_delete_cb(MPI_Comm H5_ATTR_UNUSED comm, int H5_ATTR_UNUSED keyval, void H5_ATTR_UNUSED *attr_val,
                  int H5_ATTR_UNUSED *flag)
{
    H5_term_library();
    return MPI_SUCCESS;
}
#endif /*H5_HAVE_PARALLEL*/

/*-------------------------------------------------------------------------
 * Function:    H5get_libversion
 *
 * Purpose:    Returns the library version numbers through arguments. MAJNUM
 *        will be the major revision number of the library, MINNUM the
 *        minor revision number, and RELNUM the release revision number.
 *
 * Note:    When printing an HDF5 version number it should be printed as
 *
 *         printf("%u.%u.%u", maj, min, rel)        or
 *        printf("version %u.%u release %u", maj, min, rel)
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5get_libversion(unsigned *majnum /*out*/, unsigned *minnum /*out*/, unsigned *relnum /*out*/)
{
    herr_t ret_value = SUCCEED;

    FUNC_ENTER_API(FAIL)
    H5TRACE3("e", "xxx", majnum, minnum, relnum);

    /* Set the version information */
    if (majnum)
        *majnum = H5_VERS_MAJOR;
    if (minnum)
        *minnum = H5_VERS_MINOR;
    if (relnum)
        *relnum = H5_VERS_RELEASE;

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5get_libversion() */

/*-------------------------------------------------------------------------
 * Function:    H5check_version
 *
 * Purpose:    Verifies that the arguments match the version numbers
 *        compiled into the library.  This function is intended to be
 *        called from user to verify that the versions of header files
 *        compiled into the application match the version of the hdf5
 *        library.
 *        Within major.minor.release version, the expectation
 *        is that all release versions are compatible, exceptions to
 *        this rule must be added to the VERS_RELEASE_EXCEPTIONS list.
 *
 * Return:    Success:    SUCCEED
 *
 *        Failure:    abort()
 *
 *-------------------------------------------------------------------------
 */
#define VERSION_MISMATCH_WARNING                                                                             \
    "Warning! ***HDF5 library version mismatched error***\n"                                                 \
    "The HDF5 header files used to compile this application do not match\n"                                  \
    "the version used by the HDF5 library to which this application is linked.\n"                            \
    "Data corruption or segmentation faults may occur if the application continues.\n"                       \
    "This can happen when an application was compiled by one version of HDF5 but\n"                          \
    "linked with a different version of static or shared HDF5 library.\n"                                    \
    "You should recompile the application or check your shared library related\n"                            \
    "settings such as 'LD_LIBRARY_PATH'.\n"
#define RELEASE_MISMATCH_WARNING                                                                             \
    "Warning! ***HDF5 library release mismatched error***\n"                                                 \
    "The HDF5 header files used to compile this application are not compatible with\n"                       \
    "the version used by the HDF5 library to which this application is linked.\n"                            \
    "Data corruption or segmentation faults may occur if the application continues.\n"                       \
    "This can happen when an application was compiled by one version of HDF5 but\n"                          \
    "linked with an incompatible version of static or shared HDF5 library.\n"                                \
    "You should recompile the application or check your shared library related\n"                            \
    "settings such as 'LD_LIBRARY_PATH'.\n"

herr_t
H5check_version(unsigned majnum, unsigned minnum, unsigned relnum)
{
    char                lib_str[256];
    char                substr[]                 = H5_VERS_SUBRELEASE;
    static int          checked                  = 0; /* If we've already checked the version info */
    static unsigned int disable_version_check    = 0; /* Set if the version check should be disabled */
    static const char  *version_mismatch_warning = VERSION_MISMATCH_WARNING;
    static const char  *release_mismatch_warning = RELEASE_MISMATCH_WARNING;
    herr_t              ret_value                = SUCCEED; /* Return value */

    FUNC_ENTER_API_NOINIT_NOERR_NOFS
    H5TRACE3("e", "IuIuIu", majnum, minnum, relnum);

    /* Don't check again, if we already have */
    if (checked)
        HGOTO_DONE(SUCCEED)

    {
        const char *s; /* Environment string for disabling version check */

        /* Allow different versions of the header files and library? */
        s = HDgetenv("HDF5_DISABLE_VERSION_CHECK");

        if (s && HDisdigit(*s))
            disable_version_check = (unsigned int)HDstrtol(s, NULL, 0);
    }

    /* H5_VERS_MAJOR and H5_VERS_MINOR must match */
    if (H5_VERS_MAJOR != majnum || H5_VERS_MINOR != minnum) {
        switch (disable_version_check) {
            case 0:
                HDfprintf(stderr, "%s%s", version_mismatch_warning,
                          "You can, at your own risk, disable this warning by setting the environment\n"
                          "variable 'HDF5_DISABLE_VERSION_CHECK' to a value of '1'.\n"
                          "Setting it to 2 or higher will suppress the warning messages totally.\n");
                /* Mention the versions we are referring to */
                HDfprintf(stderr, "Headers are %u.%u.%u, library is %u.%u.%u\n", majnum, minnum, relnum,
                          (unsigned)H5_VERS_MAJOR, (unsigned)H5_VERS_MINOR, (unsigned)H5_VERS_RELEASE);
                /* Show library settings if available */
                HDfprintf(stderr, "%s", H5libhdf5_settings);

                /* Bail out now. */
                HDfputs("Bye...\n", stderr);
                HDabort();
            case 1:
                /* continue with a warning */
                /* Note that the warning message is embedded in the format string.*/
                HDfprintf(stderr,
                          "%s'HDF5_DISABLE_VERSION_CHECK' "
                          "environment variable is set to %d, application will\n"
                          "continue at your own risk.\n",
                          version_mismatch_warning, disable_version_check);
                /* Mention the versions we are referring to */
                HDfprintf(stderr, "Headers are %u.%u.%u, library is %u.%u.%u\n", majnum, minnum, relnum,
                          (unsigned)H5_VERS_MAJOR, (unsigned)H5_VERS_MINOR, (unsigned)H5_VERS_RELEASE);
                /* Show library settings if available */
                HDfprintf(stderr, "%s", H5libhdf5_settings);
                break;
            default:
                /* 2 or higher: continue silently */
                break;
        } /* end switch */

    } /* end if (H5_VERS_MAJOR != majnum || H5_VERS_MINOR != minnum) */

    /* H5_VERS_RELEASE should be compatible, we will only add checks for exceptions */
    /* Library develop release versions are incompatible by design */
    if (H5_VERS_RELEASE != relnum) {
        for (unsigned i = 0; i < VERS_RELEASE_EXCEPTIONS_SIZE; i++) {
            /* Check for incompatible headers or incompatible library */
            if (VERS_RELEASE_EXCEPTIONS[i] == relnum || VERS_RELEASE_EXCEPTIONS[i] == H5_VERS_RELEASE) {
                switch (disable_version_check) {
                    case 0:
                        HDfprintf(
                            stderr, "%s%s", release_mismatch_warning,
                            "You can, at your own risk, disable this warning by setting the environment\n"
                            "variable 'HDF5_DISABLE_VERSION_CHECK' to a value of '1'.\n"
                            "Setting it to 2 or higher will suppress the warning messages totally.\n");
                        /* Mention the versions we are referring to */
                        HDfprintf(stderr, "Headers are %u.%u.%u, library is %u.%u.%u\n", majnum, minnum,
                                  relnum, (unsigned)H5_VERS_MAJOR, (unsigned)H5_VERS_MINOR,
                                  (unsigned)H5_VERS_RELEASE);

                        /* Bail out now. */
                        HDfputs("Bye...\n", stderr);
                        HDabort();
                    case 1:
                        /* continue with a warning */
                        /* Note that the warning message is embedded in the format string.*/
                        HDfprintf(stderr,
                                  "%s'HDF5_DISABLE_VERSION_CHECK' "
                                  "environment variable is set to %d, application will\n"
                                  "continue at your own risk.\n",
                                  release_mismatch_warning, disable_version_check);
                        /* Mention the versions we are referring to */
                        HDfprintf(stderr, "Headers are %u.%u.%u, library is %u.%u.%u\n", majnum, minnum,
                                  relnum, (unsigned)H5_VERS_MAJOR, (unsigned)H5_VERS_MINOR,
                                  (unsigned)H5_VERS_RELEASE);
                        break;
                    default:
                        /* 2 or higher: continue silently */
                        break;
                } /* end switch */

            } /* end if */

        } /* end for */

    } /* end if (H5_VERS_RELEASE != relnum) */

    /* Indicate that the version check has been performed */
    checked = 1;

    if (!disable_version_check) {
        /*
         * Verify if H5_VERS_INFO is consistent with the other version information.
         * Check only the first sizeof(lib_str) char.  Assume the information
         * will fit within this size or enough significance.
         */
        HDsnprintf(lib_str, sizeof(lib_str), "HDF5 library version: %d.%d.%d%s%s", H5_VERS_MAJOR,
                   H5_VERS_MINOR, H5_VERS_RELEASE, (*substr ? "-" : ""), substr);

        if (HDstrcmp(lib_str, H5_lib_vers_info_g) != 0) {
            HDfputs("Warning!  Library version information error.\n"
                    "The HDF5 library version information are not "
                    "consistent in its source code.\nThis is NOT a fatal error "
                    "but should be corrected.  Setting the environment\n"
                    "variable 'HDF5_DISABLE_VERSION_CHECK' to a value of 1 "
                    "will suppress\nthis warning.\n",
                    stderr);
            HDfprintf(stderr,
                      "Library version information are:\n"
                      "H5_VERS_MAJOR=%d, H5_VERS_MINOR=%d, H5_VERS_RELEASE=%d, "
                      "H5_VERS_SUBRELEASE=%s,\nH5_VERS_INFO=%s\n",
                      H5_VERS_MAJOR, H5_VERS_MINOR, H5_VERS_RELEASE, H5_VERS_SUBRELEASE, H5_VERS_INFO);
        } /* end if */
    }

done:
    FUNC_LEAVE_API_NOFS(ret_value)
} /* end H5check_version() */

/*-------------------------------------------------------------------------
 * Function:    H5open
 *
 * Purpose:     Initialize the library.  This is normally called
 *              automatically, but if you find that an HDF5 library function
 *              is failing inexplicably, then try calling this function
 *              first.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5open(void)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API_NOPUSH(FAIL)
    /*NO TRACE*/

    /* all work is done by FUNC_ENTER() */

done:
    FUNC_LEAVE_API_NOPUSH(ret_value)
} /* end H5open() */

/*-------------------------------------------------------------------------
 * Function:    H5atclose
 *
 * Purpose:    Register a callback for the library to invoke when it's
 *        closing.  Callbacks are invoked in LIFO order.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5atclose(H5_atclose_func_t func, void *ctx)
{
    H5_atclose_node_t *new_atclose;         /* New 'atclose' node */
    herr_t             ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API(FAIL)
    H5TRACE2("e", "Hc*x", func, ctx);

    /* Check arguments */
    if (NULL == func)
        HGOTO_ERROR(H5E_ARGS, H5E_BADVALUE, FAIL, "NULL func pointer")

    /* Allocate space for the 'atclose' node */
    if (NULL == (new_atclose = H5FL_MALLOC(H5_atclose_node_t)))
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, FAIL, "can't allocate 'atclose' node")

    /* Set up 'atclose' node */
    new_atclose->func = func;
    new_atclose->ctx  = ctx;

    /* Connector to linked-list of 'atclose' nodes */
    new_atclose->next = H5_atclose_head;
    H5_atclose_head   = new_atclose;

done:
    FUNC_LEAVE_API(ret_value)
} /* end H5atclose() */

/*-------------------------------------------------------------------------
 * Function:    H5close
 *
 * Purpose:    Terminate the library and release all resources.
 *
 * Return:    Non-negative on success/Negative on failure
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5close(void)
{
    /*
     * Don't call normal FUNC_ENTER() since we don't want to initialize the
     * whole library just to release it all right away.  It is safe to call
     * this function for an uninitialized library.
     */
    FUNC_ENTER_API_NOINIT_NOERR_NOFS
    H5TRACE0("e", "");

    H5_term_library();

    FUNC_LEAVE_API_NOFS(SUCCEED)
} /* end H5close() */

/*-------------------------------------------------------------------------
 * Function:    H5allocate_memory
 *
 * Purpose:        Allocate a memory buffer with the semantics of malloc().
 *
 *              NOTE: This function is intended for use with filter
 *              plugins so that all allocation and free operations
 *              use the same memory allocator. It is not intended for
 *              use as a general memory allocator in applications.
 *
 * Parameters:
 *
 *      size:   The size of the buffer.
 *
 *      clear:  Whether or not to memset the buffer to 0.
 *
 * Return:
 *
 *      Success:    A pointer to the allocated buffer or NULL if the size
 *                  parameter is zero.
 *
 *      Failure:    NULL (but may also be NULL w/ size 0!)
 *
 *-------------------------------------------------------------------------
 */
void *
H5allocate_memory(size_t size, hbool_t clear)
{
    void *ret_value = NULL;

    FUNC_ENTER_API_NOINIT
    H5TRACE2("*x", "zb", size, clear);

    if (0 == size)
        return NULL;

    if (clear)
        ret_value = H5MM_calloc(size);
    else
        ret_value = H5MM_malloc(size);

    FUNC_LEAVE_API_NOINIT(ret_value)
} /* end H5allocate_memory() */

/*-------------------------------------------------------------------------
 * Function:    H5resize_memory
 *
 * Purpose:        Resize a memory buffer with the semantics of realloc().
 *
 *              NOTE: This function is intended for use with filter
 *              plugins so that all allocation and free operations
 *              use the same memory allocator. It is not intended for
 *              use as a general memory allocator in applications.
 *
 * Parameters:
 *
 *      mem:    The buffer to be resized.
 *
 *      size:   The size of the buffer.
 *
 * Return:
 *
 *      Success:    A pointer to the resized buffer.
 *
 *      Failure:    NULL (the input buffer will be unchanged)
 *
 *-------------------------------------------------------------------------
 */
void *
H5resize_memory(void *mem, size_t size)
{
    void *ret_value = NULL;

    FUNC_ENTER_API_NOINIT
    H5TRACE2("*x", "*xz", mem, size);

    ret_value = H5MM_realloc(mem, size);

    FUNC_LEAVE_API_NOINIT(ret_value)
} /* end H5resize_memory() */

/*-------------------------------------------------------------------------
 * Function:    H5free_memory
 *
 * Purpose:        Frees memory allocated by the library that it is the user's
 *              responsibility to free.  Ensures that the same library
 *              that was used to allocate the memory frees it.  Passing
 *              NULL pointers is allowed.
 *
 * Return:        SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5free_memory(void *mem)
{
    FUNC_ENTER_API_NOINIT
    H5TRACE1("e", "*x", mem);

    /* At this time, it is impossible for this to fail. */
    H5MM_xfree(mem);

    FUNC_LEAVE_API_NOINIT(SUCCEED)
} /* end H5free_memory() */

/*-------------------------------------------------------------------------
 * Function:    H5is_library_threadsafe
 *
 * Purpose:        Checks to see if the library was built with thread-safety
 *              enabled.
 *
 * Return:        SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5is_library_threadsafe(hbool_t *is_ts /*out*/)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API_NOINIT
    H5TRACE1("e", "x", is_ts);

    if (is_ts) {
#ifdef H5_HAVE_THREADSAFE
        *is_ts = TRUE;
#else  /* H5_HAVE_THREADSAFE */
        *is_ts = FALSE;
#endif /* H5_HAVE_THREADSAFE */
    }
    else
        ret_value = FAIL;

    FUNC_LEAVE_API_NOINIT(ret_value)
} /* end H5is_library_threadsafe() */

/*-------------------------------------------------------------------------
 * Function:    H5is_library_terminating
 *
 * Purpose:    Checks to see if the library is shutting down.
 *
 * Note:    Useful for plugins to detect when the library is terminating.
 *        For example, a VOL connector could check if a "file close"
 *        callback was the result of the library shutdown process, or
 *        an API action from the application.
 *
 * Return:    SUCCEED/FAIL
 *
 *-------------------------------------------------------------------------
 */
herr_t
H5is_library_terminating(hbool_t *is_terminating /*out*/)
{
    herr_t ret_value = SUCCEED; /* Return value */

    FUNC_ENTER_API_NOINIT
    H5TRACE1("e", "x", is_terminating);

    HDassert(is_terminating);

    if (is_terminating)
        *is_terminating = H5_TERM_GLOBAL;
    else
        ret_value = FAIL;

    FUNC_LEAVE_API_NOINIT(ret_value)
} /* end H5is_library_terminating() */

#if defined(H5_HAVE_THREADSAFE) && defined(H5_BUILT_AS_DYNAMIC_LIB) && defined(H5_HAVE_WIN32_API) &&         \
    defined(H5_HAVE_WIN_THREADS)
/*-------------------------------------------------------------------------
 * Function:    DllMain
 *
 * Purpose:     Handles various conditions in the library on Windows.
 *
 *    NOTE:     The main purpose of this is for handling Win32 thread cleanup
 *              on thread/process detach.
 *
 *              Only enabled when the shared Windows library is built with
 *              thread safety enabled.
 *
 * Return:      TRUE on success, FALSE on failure
 *
 *-------------------------------------------------------------------------
 */
BOOL WINAPI
DllMain(_In_ HINSTANCE hinstDLL, _In_ DWORD fdwReason, _In_ LPVOID lpvReserved)
{
    /* Don't add our function enter/leave macros since this function will be
     * called before the library is initialized.
     *
     * NOTE: Do NOT call any CRT functions in DllMain!
     * This includes any functions that are called by from here!
     */

    BOOL fOkay = TRUE;

    switch (fdwReason) {
        case DLL_PROCESS_ATTACH:
            break;

        case DLL_PROCESS_DETACH:
            break;

        case DLL_THREAD_ATTACH:
#ifdef H5_HAVE_WIN_THREADS
            if (H5TS_win32_thread_enter() < 0)
                fOkay = FALSE;
#endif /* H5_HAVE_WIN_THREADS */
            break;

        case DLL_THREAD_DETACH:
#ifdef H5_HAVE_WIN_THREADS
            if (H5TS_win32_thread_exit() < 0)
                fOkay = FALSE;
#endif /* H5_HAVE_WIN_THREADS */
            break;

        default:
            /* Shouldn't get here */
            fOkay = FALSE;
            break;
    }

    return fOkay;
}
#endif /* H5_HAVE_WIN32_API && H5_BUILT_AS_DYNAMIC_LIB && H5_HAVE_WIN_THREADS && H5_HAVE_THREADSAFE*/
