/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Copyright by The HDF Group.                                               *
 * Copyright by the Board of Trustees of the University of Illinois.         *
 * 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.                                                        *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* Programmer:  John Mainzer
 *              10/27/05
 *
 *        This file contains common #defines, type definitions, and
 *        externs for tests of the cache implemented in H5C.c
 */
#ifndef CACHE_COMMON_H
#define CACHE_COMMON_H

#define H5C_FRIEND /*suppress error about including H5Cpkg   */
#define H5F_FRIEND /*suppress error about including H5Fpkg      */

/* Include library header files */
#include "H5ACprivate.h"
#include "H5Cpkg.h"
#include "H5Fpkg.h"
#include "H5Iprivate.h"

/* Include test header files */
#include "h5test.h"

/* Macro to make error reporting easier */
#define CACHE_ERROR(s)                                                                                       \
    {                                                                                                        \
        failure_mssg = "Line #" H5_TOSTRING(__LINE__) ": " s;                                                \
        pass         = FALSE;                                                                                \
        goto done;                                                                                           \
    }

#define NO_CHANGE -1

/* with apologies for the abuse of terminology... */

#define PICO_ENTRY_TYPE     0
#define NANO_ENTRY_TYPE     1
#define MICRO_ENTRY_TYPE    2
#define TINY_ENTRY_TYPE     3
#define SMALL_ENTRY_TYPE    4
#define MEDIUM_ENTRY_TYPE   5
#define LARGE_ENTRY_TYPE    6
#define HUGE_ENTRY_TYPE     7
#define MONSTER_ENTRY_TYPE  8
#define VARIABLE_ENTRY_TYPE 9
#define NOTIFY_ENTRY_TYPE   10

#define NUMBER_OF_ENTRY_TYPES 11

#define PICO_ENTRY_SIZE     (size_t)1
#define NANO_ENTRY_SIZE     (size_t)4
#define MICRO_ENTRY_SIZE    (size_t)16
#define TINY_ENTRY_SIZE     (size_t)64
#define SMALL_ENTRY_SIZE    (size_t)256
#define MEDIUM_ENTRY_SIZE   (size_t)1024
#define LARGE_ENTRY_SIZE    (size_t)(4 * 1024)
#define HUGE_ENTRY_SIZE     (size_t)(16 * 1024)
#define MONSTER_ENTRY_SIZE  (size_t)(64 * 1024)
#define VARIABLE_ENTRY_SIZE (size_t)(10 * 1024)
#define NOTIFY_ENTRY_SIZE   (size_t)1

#define NUM_PICO_ENTRIES     (10 * 1024)
#define NUM_NANO_ENTRIES     (10 * 1024)
#define NUM_MICRO_ENTRIES    (10 * 1024)
#define NUM_TINY_ENTRIES     (10 * 1024)
#define NUM_SMALL_ENTRIES    (10 * 1024)
#define NUM_MEDIUM_ENTRIES   (10 * 1024)
#define NUM_LARGE_ENTRIES    (10 * 1024)
#define NUM_HUGE_ENTRIES     (10 * 1024)
#define NUM_MONSTER_ENTRIES  (10 * 1024)
#define NUM_VARIABLE_ENTRIES (10 * 1024)
#define NUM_NOTIFY_ENTRIES   (10 * 1024)

#define MAX_ENTRIES (10 * 1024)

/* The choice of the BASE_ADDR below is arbitrary -- it just has to be
 * larger than the superblock.
 */
#define BASE_ADDR          (haddr_t)1024
#define PICO_BASE_ADDR     BASE_ADDR
#define NANO_BASE_ADDR     (haddr_t)(PICO_BASE_ADDR + (PICO_ENTRY_SIZE * NUM_PICO_ENTRIES))
#define MICRO_BASE_ADDR    (haddr_t)(NANO_BASE_ADDR + (NANO_ENTRY_SIZE * NUM_NANO_ENTRIES))
#define TINY_BASE_ADDR     (haddr_t)(MICRO_BASE_ADDR + (MICRO_ENTRY_SIZE * NUM_MICRO_ENTRIES))
#define SMALL_BASE_ADDR    (haddr_t)(TINY_BASE_ADDR + (TINY_ENTRY_SIZE * NUM_TINY_ENTRIES))
#define MEDIUM_BASE_ADDR   (haddr_t)(SMALL_BASE_ADDR + (SMALL_ENTRY_SIZE * NUM_SMALL_ENTRIES))
#define LARGE_BASE_ADDR    (haddr_t)(MEDIUM_BASE_ADDR + (MEDIUM_ENTRY_SIZE * NUM_MEDIUM_ENTRIES))
#define HUGE_BASE_ADDR     (haddr_t)(LARGE_BASE_ADDR + (LARGE_ENTRY_SIZE * NUM_LARGE_ENTRIES))
#define MONSTER_BASE_ADDR  (haddr_t)(HUGE_BASE_ADDR + (HUGE_ENTRY_SIZE * NUM_HUGE_ENTRIES))
#define VARIABLE_BASE_ADDR (haddr_t)(MONSTER_BASE_ADDR + (MONSTER_ENTRY_SIZE * NUM_MONSTER_ENTRIES))
#define NOTIFY_BASE_ADDR   (haddr_t)(VARIABLE_BASE_ADDR + (VARIABLE_ENTRY_SIZE * NUM_VARIABLE_ENTRIES))

#define PICO_ALT_BASE_ADDR     (haddr_t)(NOTIFY_BASE_ADDR + (NOTIFY_ENTRY_SIZE * NUM_NOTIFY_ENTRIES))
#define NANO_ALT_BASE_ADDR     (haddr_t)(PICO_ALT_BASE_ADDR + (PICO_ENTRY_SIZE * NUM_PICO_ENTRIES))
#define MICRO_ALT_BASE_ADDR    (haddr_t)(NANO_ALT_BASE_ADDR + (NANO_ENTRY_SIZE * NUM_NANO_ENTRIES))
#define TINY_ALT_BASE_ADDR     (haddr_t)(MICRO_ALT_BASE_ADDR + (MICRO_ENTRY_SIZE * NUM_MICRO_ENTRIES))
#define SMALL_ALT_BASE_ADDR    (haddr_t)(TINY_ALT_BASE_ADDR + (TINY_ENTRY_SIZE * NUM_TINY_ENTRIES))
#define MEDIUM_ALT_BASE_ADDR   (haddr_t)(SMALL_ALT_BASE_ADDR + (SMALL_ENTRY_SIZE * NUM_SMALL_ENTRIES))
#define LARGE_ALT_BASE_ADDR    (haddr_t)(MEDIUM_ALT_BASE_ADDR + (MEDIUM_ENTRY_SIZE * NUM_MEDIUM_ENTRIES))
#define HUGE_ALT_BASE_ADDR     (haddr_t)(LARGE_ALT_BASE_ADDR + (LARGE_ENTRY_SIZE * NUM_LARGE_ENTRIES))
#define MONSTER_ALT_BASE_ADDR  (haddr_t)(HUGE_ALT_BASE_ADDR + (HUGE_ENTRY_SIZE * NUM_HUGE_ENTRIES))
#define VARIABLE_ALT_BASE_ADDR (haddr_t)(MONSTER_ALT_BASE_ADDR + (MONSTER_ENTRY_SIZE * NUM_MONSTER_ENTRIES))
#define NOTIFY_ALT_BASE_ADDR   (haddr_t)(VARIABLE_ALT_BASE_ADDR + (VARIABLE_ENTRY_SIZE * NUM_VARIABLE_ENTRIES))
#define MAX_ADDR               (haddr_t)(NOTIFY_ALT_BASE_ADDR + (NOTIFY_ENTRY_SIZE * NUM_NOTIFY_ENTRIES))
#define ADDR_SPACE_SIZE        (haddr_t)(MAX_ADDR - BASE_ADDR)

/***********************************************************************
 *
 * Macro:       H5C_FLUSH_CACHE
 *
 * Purpose:     Wrap a call to H5C_flush_cache() in calls to
 *              H5C_set_slist_enabled() to setup and take down the slist.
 *
 *              This is necessary, as H5C_flush_cache() needs the
 *              slist to be active.  Further, since it is called
 *              repeatedly during file flush, it would be inefficient
 *              for it to setup the slist on entry, and take it down
 *              on exit.
 *
 *              Note that the slist need not be empty if the flags
 *              indicate a partial flush (i.e.
 *              H5C__FLUSH_MARKED_ENTRIES_FLAG).  Compute clear_slist
 *              and pass it into H5C_set_slist_enabled as appropriate.
 *
 *              On error, set pass to FALSE, and set failure_mssg
 *              to the supplied error message.
 *
 * Return:      N/A
 *
 * Programmer:  John Mainzer
 *              5/14/20
 *
 * Changes:     None.
 *
 ***********************************************************************/

#define H5C_FLUSH_CACHE(file, flags, fail_mssg)                                                              \
    {                                                                                                        \
        hbool_t clear_slist;                                                                                 \
        herr_t  rslt;                                                                                        \
                                                                                                             \
        clear_slist = ((flags & H5C__FLUSH_MARKED_ENTRIES_FLAG) != 0);                                       \
                                                                                                             \
        rslt = H5C_set_slist_enabled((file)->shared->cache, TRUE, FALSE);                                    \
                                                                                                             \
        if (rslt >= 0) {                                                                                     \
                                                                                                             \
            rslt = H5C_flush_cache((file), (flags));                                                         \
        }                                                                                                    \
                                                                                                             \
        if (rslt >= 0) {                                                                                     \
                                                                                                             \
            rslt = H5C_set_slist_enabled((file)->shared->cache, FALSE, clear_slist);                         \
        }                                                                                                    \
                                                                                                             \
        if (rslt < 0) {                                                                                      \
                                                                                                             \
            pass         = FALSE;                                                                            \
            failure_mssg = (fail_mssg);                                                                      \
        }                                                                                                    \
    } /* H5C_FLUSH_CACHE */

#define MAX_PINS                                                                                             \
    8 /* Maximum number of entries that can be                                                               \
       * directly pinned by a single entry.                                                                  \
       */

#define FLUSH_OP__NO_OP          0
#define FLUSH_OP__DIRTY          1
#define FLUSH_OP__RESIZE         2
#define FLUSH_OP__MOVE           3
#define FLUSH_OP__ORDER          4
#define FLUSH_OP__EXPUNGE        5
#define FLUSH_OP__DEST_FLUSH_DEP 6
#define FLUSH_OP__MAX_OP         6

#define MAX_FLUSH_OPS                                                                                        \
    10 /* Maximum number of flush operations                                                                 \
        * that can be associated with a                                                                      \
        * cache entry.                                                                                       \
        */

#define MAX_FLUSH_DEP_PARS                                                                                   \
    8 /* Maximum number of flush dependency                                                                  \
       * parents in the test */

typedef struct flush_op {
    int op_code;         /* integer op code indicating the
                          * operation to be performed.  At
                          * present it must be one of:
                          *
                          *   FLUSH_OP__NO_OP
                          *   FLUSH_OP__DIRTY
                          *   FLUSH_OP__RESIZE
                          *   FLUSH_OP__MOVE
                          *   FLUSH_OP__ORDER
                          */
    int type;            /* type code of the cache entry that
                          * is the target of the operation.
                          * This value is passed into the
                          * function implementing the flush
                          * operation.
                          */
    int idx;             /* index of the cache entry that
                          * is the target of the operation.
                          * This value is passed into the
                          * function implementing the flush
                          * operation.
                          */
    hbool_t flag;        /* boolean flag passed into the
                          * function implementing the flush
                          * operation.  The meaning of the
                          * flag is dependent upon the flush
                          * operation:
                          *
                          * FLUSH_OP__DIRTY: TRUE iff the
                          *   target is pinned, and is to
                          *   be dirtied via the
                          *   H5C_mark_entry_dirty()
                          *   call.
                          *
                          * FLUSH_OP__RESIZE: TRUE iff the
                          *   target is pinned, and is to
                          *   be resized via the
                          *   H5C_resize_entry()
                          *   call.
                          *
                          * FLUSH_OP__MOVE: TRUE iff the
                          *    target is to be moved to
                          *    its main address.
                          */
    size_t size;         /* New target size in the
                          * FLUSH_OP__MOVE operation.
                          * Unused elsewhere.
                          */
    unsigned *order_ptr; /* Pointer to outside counter for
                          * recording the order of entries
                          * flushed.
                          */
} flush_op;

typedef enum test_entry_action_t {
    TEST_ENTRY_ACTION_NUL = 0, /* No action on entry */
    TEST_ENTRY_ACTION_MOVE     /* Entry is beging moved */
} test_entry_action_t;

typedef struct test_entry_t {
    H5C_cache_entry_t header;     /* entry data used by the cache
                                   * -- must be first
                                   */
    struct test_entry_t *self;    /* pointer to this entry -- used for
                                   * sanity checking.
                                   */
    test_entry_action_t action;   /* Action being performed on a test entry */
    H5F_t *             file_ptr; /* pointer to the file in which the
                                   * entry resides, or NULL if the entry
                                   * is not in a file.
                                   */
    H5C_t *cache_ptr;             /* pointer to the cache in which
                                   * the entry resides, or NULL if the
                                   * entry is not in cache.
                                   */
    hbool_t written_to_main_addr;
    /* Flag indicating whether an image
     * of the entry has been written to
     * its main address.  Since we no
     * longer have a flush callback, we
     * set this field to true whenever the
     * entry is serialized while at its
     * main address.
     */
    hbool_t written_to_alt_addr;
    /* Flag indicating whether an image
     * of the entry has been written to
     * its alternate address.  Since we no
     * longer have a flush callback, we
     * set this field to true whenever the
     * entry is serialized while at its
     * alternate address.
     */
    haddr_t addr;                                    /* where the cache thinks this entry
                                                      * is located
                                                      */
    hbool_t at_main_addr;                            /* boolean flag indicating whether
                                                      * the entry is supposed to be at
                                                      * either its main or alternate
                                                      * address.
                                                      */
    haddr_t main_addr;                               /* initial location of the entry
                                                      */
    haddr_t alt_addr;                                /* location to which the entry
                                                      * can be relocated or "moved"
                                                      */
    size_t size;                                     /* how big the cache thinks this
                                                      * entry is
                                                      */
    int32_t type;                                    /* indicates which entry array this
                                                      * entry is in
                                                      */
    int32_t index;                                   /* index in its entry array
                                                      */
    int32_t serializes;                              /* number of times this entry has
                                                      * been serialized.
                                                      */
    int32_t deserializes;                            /* number of times this entry has
                                                      * been deserialized
                                                      */
    hbool_t is_dirty;                                /* entry has been modified since
                                                      * last write
                                                      */
    hbool_t is_protected;                            /* entry should currently be on
                                                      * the cache's protected list.
                                                      */
    hbool_t is_read_only;                            /* TRUE iff the entry should be
                                                      * protected read only.
                                                      */
    int ro_ref_count;                                /* Number of outstanding read only
                                                      * protects on the entry.
                                                      */
    hbool_t is_pinned;                               /* entry is currently pinned in
                                                      * the cache.
                                                      */
    haddr_t tag;                                     /* the base_addr as tag for corking entries */
    hbool_t is_corked;                               /* entry is currently corked or not */
    int     pinning_ref_count;                       /* Number of entries that
                                                      * pin this entry in the cache.
                                                      * When this count drops to zero,
                                                      * this entry should be unpinned.
                                                      */
    int num_pins;                                    /* Number of entries that this
                                                      * entry pins in the cache.  This
                                                      * value must be in the range
                                                      * [0, MAX_PINS].
                                                      */
    int pin_type[MAX_PINS];                          /* array of the types of entries
                                                      * pinned by this entry.
                                                      */
    int pin_idx[MAX_PINS];                           /* array of the indicies of
                                                      * entries pinned by this entry.
                                                      */
    int num_flush_ops;                               /* integer field containing the
                                                      * number of flush operations to
                                                      * be executed when the entry is
                                                      * flushed.  This value must lie in
                                                      * the closed interval
                                                      * [0, MAX_FLUSH_OPS].
                                                      */
    struct flush_op flush_ops[MAX_FLUSH_OPS];        /* Array of instances
                                                      * of struct flush_op detailing the
                                                      * flush operations (if any) that
                                                      * are to be executed when the entry
                                                      * is flushed from the cache.
                                                      *
                                                      * num_flush_ops contains the number
                                                      * of valid entries in this array.
                                                      */
    hbool_t flush_op_self_resize_in_progress;        /* Boolean flag
                                                      * that is set to TRUE iff this
                                                      * entry is being flushed, it has
                                                      * been resized by a resize flush
                                                      * op, and the flush function has
                                                      * not yet returned,  This field is
                                                      * used to turn off overactive santity
                                                      * checking code that would otherwise
                                                      * cause a false test failure.
                                                      */
    hbool_t deserialized;                            /* entry has been deserialized since
                                                      * the last time it was reset.
                                                      */
    hbool_t serialized;                              /* entry has been serialized since the
                                                      * last time it was reset.
                                                      */
    hbool_t destroyed;                               /* entry has been destroyed since the
                                                      * last time it was reset.
                                                      */
    hbool_t expunged;                                /* entry has been expunged since the
                                                      * last time it was reset.
                                                      */
    int      flush_dep_par_type[MAX_FLUSH_DEP_PARS]; /* Entry types of flush dependency parents */
    int      flush_dep_par_idx[MAX_FLUSH_DEP_PARS];  /* Indices of flush dependency parents */
    unsigned flush_dep_npar;                         /* Number of flush dependency parents */
    unsigned flush_dep_nchd;                         /* Number of flush dependency children */
    unsigned
             flush_dep_ndirty_chd; /* Number of dirty flush dependency children (including granchildren, etc.) */
    hbool_t  pinned_from_client; /* entry was pinned by client call */
    hbool_t  pinned_from_cache;  /* entry was pinned by cache internally */
    unsigned flush_order;        /* Order that entry was flushed in */

    unsigned notify_after_insert_count; /* Count of times that entry was inserted in cache */
    unsigned notify_before_evict_count; /* Count of times that entry was removed in cache */
    size_t   actual_len;                /* Simulate the entry's actual size for a speculative load */
    unsigned max_verify_ct;             /* Maximum # of times to verify an entry's checksum */
    unsigned verify_ct;                 /* Count the # of checksum verification for an entry */
} test_entry_t;

/* The following are cut down test versions of the hash table manipulation
 * macros from H5Cpkg.h, which have been further modified to avoid references
 * to the error reporting macros.  Needless to say, these macros must be
 * updated as necessary.
 */

#define H5C__HASH_MASK ((size_t)(H5C__HASH_TABLE_LEN - 1) << 3)

#define H5C_TEST__PRE_HT_SEARCH_SC(cache_ptr, Addr)                                                          \
    if (((cache_ptr) == NULL) || ((cache_ptr)->magic != H5C__H5C_T_MAGIC) ||                                 \
        ((cache_ptr)->index_size != ((cache_ptr)->clean_index_size + (cache_ptr)->dirty_index_size)) ||      \
        (!H5F_addr_defined(Addr)) || (H5C__HASH_FCN(Addr) < 0) ||                                            \
        (H5C__HASH_FCN(Addr) >= H5C__HASH_TABLE_LEN)) {                                                      \
        HDfprintf(stdout, "Pre HT search SC failed.\n");                                                     \
    }

#define H5C_TEST__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, k)                                             \
    if (((cache_ptr) == NULL) || ((cache_ptr)->magic != H5C__H5C_T_MAGIC) || ((cache_ptr)->index_len < 1) || \
        ((entry_ptr) == NULL) || ((cache_ptr)->index_size < (entry_ptr)->size) ||                            \
        ((cache_ptr)->index_size != ((cache_ptr)->clean_index_size + (cache_ptr)->dirty_index_size)) ||      \
        ((entry_ptr)->size <= 0) || (((cache_ptr)->index)[k] == NULL) ||                                     \
        ((((cache_ptr)->index)[k] != (entry_ptr)) && ((entry_ptr)->ht_prev == NULL)) ||                      \
        ((((cache_ptr)->index)[k] == (entry_ptr)) && ((entry_ptr)->ht_prev != NULL)) ||                      \
        (((entry_ptr)->ht_prev != NULL) && ((entry_ptr)->ht_prev->ht_next != (entry_ptr))) ||                \
        (((entry_ptr)->ht_next != NULL) && ((entry_ptr)->ht_next->ht_prev != (entry_ptr)))) {                \
        HDfprintf(stdout, "Post successful HT search SC failed.\n");                                         \
    }

#define H5C_TEST__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k)                                            \
    if (((cache_ptr) == NULL) || (((cache_ptr)->index)[k] != (entry_ptr)) ||                                 \
        ((entry_ptr)->ht_prev != NULL)) {                                                                    \
        HDfprintf(stdout, "Post HT shift to front failed.\n");                                               \
    }

#define H5C_TEST__SEARCH_INDEX(cache_ptr, Addr, entry_ptr)                                                   \
    {                                                                                                        \
        int k;                                                                                               \
        H5C_TEST__PRE_HT_SEARCH_SC(cache_ptr, Addr)                                                          \
        k         = H5C__HASH_FCN(Addr);                                                                     \
        entry_ptr = ((cache_ptr)->index)[k];                                                                 \
        while (entry_ptr) {                                                                                  \
            if (H5F_addr_eq(Addr, (entry_ptr)->addr)) {                                                      \
                H5C_TEST__POST_SUC_HT_SEARCH_SC(cache_ptr, entry_ptr, k)                                     \
                if (entry_ptr != ((cache_ptr)->index)[k]) {                                                  \
                    if ((entry_ptr)->ht_next)                                                                \
                        (entry_ptr)->ht_next->ht_prev = (entry_ptr)->ht_prev;                                \
                    HDassert((entry_ptr)->ht_prev != NULL);                                                  \
                    (entry_ptr)->ht_prev->ht_next    = (entry_ptr)->ht_next;                                 \
                    ((cache_ptr)->index)[k]->ht_prev = (entry_ptr);                                          \
                    (entry_ptr)->ht_next             = ((cache_ptr)->index)[k];                              \
                    (entry_ptr)->ht_prev             = NULL;                                                 \
                    ((cache_ptr)->index)[k]          = (entry_ptr);                                          \
                    H5C_TEST__POST_HT_SHIFT_TO_FRONT(cache_ptr, entry_ptr, k)                                \
                }                                                                                            \
                break;                                                                                       \
            }                                                                                                \
            (entry_ptr) = (entry_ptr)->ht_next;                                                              \
        }                                                                                                    \
    }

/* Macros used in H5AC level tests */

#define CACHE_CONFIGS_EQUAL(a, b, cmp_set_init, cmp_init_size)                                               \
    (((a).version == (b).version) && ((a).rpt_fcn_enabled == (b).rpt_fcn_enabled) &&                         \
     ((a).open_trace_file == (b).open_trace_file) && ((a).close_trace_file == (b).close_trace_file) &&       \
     (((a).open_trace_file == FALSE) || (strcmp((a).trace_file_name, (b).trace_file_name) == 0)) &&          \
     ((a).evictions_enabled == (b).evictions_enabled) &&                                                     \
     ((!cmp_set_init) || ((a).set_initial_size == (b).set_initial_size)) &&                                  \
     ((!cmp_init_size) || ((a).initial_size == (b).initial_size)) &&                                         \
     (H5_DBL_ABS_EQUAL((a).min_clean_fraction, (b).min_clean_fraction)) && ((a).max_size == (b).max_size) && \
     ((a).min_size == (b).min_size) && ((a).epoch_length == (b).epoch_length) &&                             \
     ((a).incr_mode == (b).incr_mode) &&                                                                     \
     (H5_DBL_ABS_EQUAL((a).lower_hr_threshold, (b).lower_hr_threshold)) &&                                   \
     (H5_DBL_ABS_EQUAL((a).increment, (b).increment)) &&                                                     \
     ((a).apply_max_increment == (b).apply_max_increment) && ((a).max_increment == (b).max_increment) &&     \
     ((a).flash_incr_mode == (b).flash_incr_mode) &&                                                         \
     (H5_DBL_ABS_EQUAL((a).flash_multiple, (b).flash_multiple)) &&                                           \
     (H5_DBL_ABS_EQUAL((a).flash_threshold, (b).flash_threshold)) && ((a).decr_mode == (b).decr_mode) &&     \
     (H5_DBL_ABS_EQUAL((a).upper_hr_threshold, (b).upper_hr_threshold)) &&                                   \
     (H5_DBL_ABS_EQUAL((a).decrement, (b).decrement)) &&                                                     \
     ((a).apply_max_decrement == (b).apply_max_decrement) && ((a).max_decrement == (b).max_decrement) &&     \
     ((a).epochs_before_eviction == (b).epochs_before_eviction) &&                                           \
     ((a).apply_empty_reserve == (b).apply_empty_reserve) &&                                                 \
     (H5_DBL_ABS_EQUAL((a).empty_reserve, (b).empty_reserve)) &&                                             \
     ((a).dirty_bytes_threshold == (b).dirty_bytes_threshold) &&                                             \
     ((a).metadata_write_strategy == (b).metadata_write_strategy))

#define XLATE_EXT_TO_INT_MDC_CONFIG(i, e)                                                                    \
    {                                                                                                        \
        (i).version = H5C__CURR_AUTO_SIZE_CTL_VER;                                                           \
        if ((e).rpt_fcn_enabled)                                                                             \
            (i).rpt_fcn = H5C_def_auto_resize_rpt_fcn;                                                       \
        else                                                                                                 \
            (i).rpt_fcn = NULL;                                                                              \
        (i).set_initial_size       = (e).set_initial_size;                                                   \
        (i).initial_size           = (e).initial_size;                                                       \
        (i).min_clean_fraction     = (e).min_clean_fraction;                                                 \
        (i).max_size               = (e).max_size;                                                           \
        (i).min_size               = (e).min_size;                                                           \
        (i).epoch_length           = (long int)((e).epoch_length);                                           \
        (i).incr_mode              = (e).incr_mode;                                                          \
        (i).lower_hr_threshold     = (e).lower_hr_threshold;                                                 \
        (i).increment              = (e).increment;                                                          \
        (i).apply_max_increment    = (e).apply_max_increment;                                                \
        (i).max_increment          = (e).max_increment;                                                      \
        (i).flash_incr_mode        = (e).flash_incr_mode;                                                    \
        (i).flash_multiple         = (e).flash_multiple;                                                     \
        (i).flash_threshold        = (e).flash_threshold;                                                    \
        (i).decr_mode              = (e).decr_mode;                                                          \
        (i).upper_hr_threshold     = (e).upper_hr_threshold;                                                 \
        (i).decrement              = (e).decrement;                                                          \
        (i).apply_max_decrement    = (e).apply_max_decrement;                                                \
        (i).max_decrement          = (e).max_decrement;                                                      \
        (i).epochs_before_eviction = (int)((e).epochs_before_eviction);                                      \
        (i).apply_empty_reserve    = (e).apply_empty_reserve;                                                \
        (i).empty_reserve          = (e).empty_reserve;                                                      \
    }

/* misc type definitions */

struct expected_entry_status {
    int           entry_type;
    int           entry_index;
    size_t        size;
    hbool_t       in_cache;
    hbool_t       at_main_addr;
    hbool_t       is_dirty;
    hbool_t       is_protected;
    hbool_t       is_pinned;
    hbool_t       deserialized;
    hbool_t       serialized;
    hbool_t       destroyed;
    int           flush_dep_par_type[MAX_FLUSH_DEP_PARS]; /* Entry types of flush dependency parents */
    int           flush_dep_par_idx[MAX_FLUSH_DEP_PARS];  /* Indices of flush dependency parents */
    unsigned      flush_dep_npar;                         /* Number of flush dependency parents */
    unsigned      flush_dep_nchd;                         /* Number of flush dependency children */
    unsigned      flush_dep_ndirty_chd;                   /* Number of dirty flush dependency children */
    int           flush_order;                            /* flush order of entry */
    unsigned char is_corked;                              /* cork status of entry */
};

/* global variable externs: */
H5TEST_DLLVAR hbool_t     pass; /* set to false on error */
H5TEST_DLLVAR const char *failure_mssg;

H5TEST_DLLVAR test_entry_t *entries[NUMBER_OF_ENTRY_TYPES];
H5TEST_DLLVAR const int32_t max_indices[NUMBER_OF_ENTRY_TYPES];
H5TEST_DLLVAR const size_t  entry_sizes[NUMBER_OF_ENTRY_TYPES];
H5TEST_DLLVAR const haddr_t base_addrs[NUMBER_OF_ENTRY_TYPES];
H5TEST_DLLVAR const haddr_t alt_base_addrs[NUMBER_OF_ENTRY_TYPES];

/* callback table extern */

H5TEST_DLLVAR const H5C_class_t *types[NUMBER_OF_ENTRY_TYPES];

#ifdef __cplusplus
extern "C" {
#endif

/* function declarations: */

H5TEST_DLL void add_flush_op(int target_type, int target_idx, int op_code, int type, int idx, hbool_t flag,
                             size_t size, unsigned *order);

H5TEST_DLL void addr_to_type_and_index(haddr_t addr, int32_t *type_ptr, int32_t *index_ptr);

#if 0 /* keep this for a while -- it may be useful */
H5TEST_DLL haddr_t type_and_index_to_addr(int32_t type,
                               int32_t idx);
#endif

H5TEST_DLL void dirty_entry(H5F_t *file_ptr, int32_t type, int32_t idx, hbool_t dirty_pin);

H5TEST_DLL void expunge_entry(H5F_t *file_ptr, int32_t type, int32_t idx);

H5TEST_DLL void insert_entry(H5F_t *file_ptr, int32_t type, int32_t idx, unsigned int flags);

H5TEST_DLL void mark_entry_dirty(int32_t type, int32_t idx);

H5TEST_DLL void move_entry(H5C_t *cache_ptr, int32_t type, int32_t idx, hbool_t main_addr);

H5TEST_DLL void protect_entry(H5F_t *file_ptr, int32_t type, int32_t idx);

H5TEST_DLL void protect_entry_ro(H5F_t *file_ptr, int32_t type, int32_t idx);

H5TEST_DLL void pin_entry(int32_t type, int32_t idx);

H5TEST_DLL hbool_t entry_in_cache(H5C_t *cache_ptr, int32_t type, int32_t idx);

H5TEST_DLL void create_pinned_entry_dependency(H5F_t *file_ptr, int pinning_type, int pinning_idx,
                                               int pinned_type, int pinned_idx);

H5TEST_DLL herr_t create_entry_arrays(void);

H5TEST_DLL void free_entry_arrays(void);

H5TEST_DLL void reset_entries(void);

H5TEST_DLL void cork_entry_type(H5F_t *file_ptr, int32_t type);

H5TEST_DLL void uncork_entry_type(H5F_t *file_ptr, int32_t type);

H5TEST_DLL void resize_entry(H5F_t *file_ptr, int32_t type, int32_t idx, size_t new_size, hbool_t in_cache);

H5TEST_DLL void row_major_scan_forward(H5F_t *file_ptr, int32_t max_index, int32_t lag, hbool_t verbose,
                                       hbool_t reset_stats, hbool_t display_stats,
                                       hbool_t display_detailed_stats, hbool_t do_inserts, hbool_t do_moves,
                                       hbool_t move_to_main_addr, hbool_t do_destroys,
                                       hbool_t do_mult_ro_protects, int dirty_destroys, int dirty_unprotects);

H5TEST_DLL void hl_row_major_scan_forward(H5F_t *file_ptr, int32_t max_index, hbool_t verbose,
                                          hbool_t reset_stats, hbool_t display_stats,
                                          hbool_t display_detailed_stats, hbool_t do_inserts);

H5TEST_DLL void row_major_scan_backward(H5F_t *file_ptr, int32_t max_index, int32_t lag, hbool_t verbose,
                                        hbool_t reset_stats, hbool_t display_stats,
                                        hbool_t display_detailed_stats, hbool_t do_inserts, hbool_t do_moves,
                                        hbool_t move_to_main_addr, hbool_t do_destroys,
                                        hbool_t do_mult_ro_protects, int dirty_destroys,
                                        int dirty_unprotects);

H5TEST_DLL void hl_row_major_scan_backward(H5F_t *file_ptr, int32_t max_index, hbool_t verbose,
                                           hbool_t reset_stats, hbool_t display_stats,
                                           hbool_t display_detailed_stats, hbool_t do_inserts);

H5TEST_DLL void col_major_scan_forward(H5F_t *file_ptr, int32_t max_index, int32_t lag, hbool_t verbose,
                                       hbool_t reset_stats, hbool_t display_stats,
                                       hbool_t display_detailed_stats, hbool_t do_inserts,
                                       int dirty_unprotects);

H5TEST_DLL void hl_col_major_scan_forward(H5F_t *file_ptr, int32_t max_index, hbool_t verbose,
                                          hbool_t reset_stats, hbool_t display_stats,
                                          hbool_t display_detailed_stats, hbool_t do_inserts,
                                          int dirty_unprotects);

H5TEST_DLL void col_major_scan_backward(H5F_t *file_ptr, int32_t max_index, int32_t lag, hbool_t verbose,
                                        hbool_t reset_stats, hbool_t display_stats,
                                        hbool_t display_detailed_stats, hbool_t do_inserts,
                                        int dirty_unprotects);

H5TEST_DLL void hl_col_major_scan_backward(H5F_t *file_ptr, int32_t max_index, hbool_t verbose,
                                           hbool_t reset_stats, hbool_t display_stats,
                                           hbool_t display_detailed_stats, hbool_t do_inserts,
                                           int dirty_unprotects);

H5TEST_DLL void flush_cache(H5F_t *file_ptr, hbool_t destroy_entries, hbool_t dump_stats,
                            hbool_t dump_detailed_stats);

H5TEST_DLL void unpin_entry(int32_t type, int32_t idx);

H5TEST_DLL void unprotect_entry(H5F_t *file_ptr, int32_t type, int32_t idx, unsigned int flags);

H5TEST_DLL void verify_clean(void);

H5TEST_DLL void verify_entry_status(H5C_t *cache_ptr, int tag, int num_entries,
                                    struct expected_entry_status expected[]);

H5TEST_DLL void verify_unprotected(void);

H5TEST_DLL void create_flush_dependency(int32_t parent_type, int32_t parent_idx, int32_t child_type,
                                        int32_t child_idx);

H5TEST_DLL void destroy_flush_dependency(int32_t parent_type, int32_t parent_idx, int32_t child_type,
                                         int32_t child_idx);

/*** H5AC level utility functions ***/

H5TEST_DLL hbool_t resize_configs_are_equal(const H5C_auto_size_ctl_t *a, const H5C_auto_size_ctl_t *b,
                                            hbool_t compare_init);

H5TEST_DLL void check_and_validate_cache_hit_rate(hid_t file_id, double *hit_rate_ptr, hbool_t dump_data,
                                                  int64_t min_accesses, double min_hit_rate);

H5TEST_DLL void check_and_validate_cache_size(hid_t file_id, size_t *max_size_ptr, size_t *min_clean_size_ptr,
                                              size_t *cur_size_ptr, int32_t *cur_num_entries_ptr,
                                              hbool_t dump_data);

H5TEST_DLL void validate_mdc_config(hid_t file_id, H5AC_cache_config_t *ext_config_ptr, hbool_t compare_init,
                                    int test_num);

/** Debugging functions -- normally commented out ***/
#if 0
H5TEST_DLL void dump_LRU(H5F_t * file_ptr);
#endif

#ifdef __cplusplus
}
#endif

#endif /* CACHE_COMMON_H */
