/* **********************************************************
 * Copyright (c) 2011-2022 Google, Inc.  All rights reserved.
 * Copyright (c) 2007-2010 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of VMware, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

/* Containers DynamoRIO Extension: Hashtable */

#ifndef _HASHTABLE_H_
#define _HASHTABLE_H_ 1

/**
 * @file hashtable.h
 * @brief Header for DynamoRIO Hashtable Extension
 */

#ifdef __cplusplus
extern "C" {
#endif

/***************************************************************************
 * HASHTABLE
 */

/**
 * \addtogroup drcontainers Container Data Structures
 */
/**@{*/ /* begin doxygen group */

/** The type of hash key */
typedef enum {
    HASH_INTPTR,        /**< A pointer-sized integer or pointer */
    HASH_STRING,        /**< A case-sensitive string */
    HASH_STRING_NOCASE, /**< A case-insensitive string */
    /**
     * A custom key.  Hash and compare operations must be provided
     * in hashtable_init_ex().  The hash operation can return a full
     * uint, as its result will be truncated via a mod of the
     * hash key bit size.  This allows for resizing the table
     * without changing the hash operation.
     */
    HASH_CUSTOM,
} hash_type_t;

typedef struct _hash_entry_t {
    void *key;
    void *payload;
    struct _hash_entry_t *next;
} hash_entry_t;

/** Configuration parameters for a hashtable. */
typedef struct _hashtable_config_t {
    size_t size;           /**< The size of the hashtable_config_t struct used */
    bool resizable;        /**< Whether the table should be resized */
    uint resize_threshold; /**< Resize the table at this % full */
    /**
     * Called whenever an entry is removed, with the key passed in.  If "str_dup" is set
     * to true in hashtable_init() or hashtable_init_ex(), this field is ignored.
     */
    void (*free_key_func)(void *);
} hashtable_config_t;

typedef struct _hashtable_t {
    hash_entry_t **table;
    hash_type_t hashtype;
    bool str_dup;
    void *lock;
    uint table_bits;
    bool synch;
    void (*free_payload_func)(void *);
    uint (*hash_key_func)(void *);
    bool (*cmp_key_func)(void *, void *);
    uint entries;
    hashtable_config_t config;
    uint persist_count;
} hashtable_t;

/* should move back to utils.c once have iterator and alloc_exit
 * doesn't need this macro
 */
#define HASHTABLE_SIZE(num_bits) (1U << (num_bits))

/** Caseless string compare */
bool
stri_eq(const char *s1, const char *s2);

/**
 * The hashtable has parametrized heap and assert routines for flexibility.
 * This routine must be called BEFORE any other hashtable_ routine; else,
 * the defaults will be used.
 */
void
hashtable_global_config(void *(*alloc_func)(size_t), void (*free_func)(void *, size_t),
                        void (*assert_fail_func)(const char *));

/**
 * Initializes a hashtable with the given size, hash type, and whether to
 * duplicate string keys.  All operations are synchronized by default.
 */
void
hashtable_init(hashtable_t *table, uint num_bits, hash_type_t hashtype, bool str_dup);

/**
 * Initializes a hashtable with the given parameters.
 *
 * @param[out] table     The hashtable to be initialized.
 * @param[in]  num_bits  The initial number of bits to use for the hash key
 *   which determines the initial size of the table itself.  The result of the
 *   hash function will be truncated to this size.  This size will be
 *   increased when the table is resized (resizing always doubles the size).
 * @param[in]  hashtype  The type of hash to perform.
 * @param[in]  str_dup   Whether to duplicate string keys.
 * @param[in]  synch     Whether to synchronize each operation.
 *   Even when \p synch is false, the hashtable's lock is initialized and can
 *   be used via hashtable_lock() and hashtable_unlock(), allowing the caller
 *   to extend synchronization beyond just the operation in question, to
 *   include accessing a looked-up payload, e.g.
 * @param[in]  free_payload_func   A callback for freeing each payload.
 *   Leave it NULL if no callback is needed.
 * @param[in]  hash_key_func       A callback for hashing a key.
 *   Leave it NULL if no callback is needed and the default is to be used.
 *   For HASH_CUSTOM, a callback must be provided.
 *   The hash operation can return a full uint, as its result will be
 *   truncated via a mod of the hash key bit size.  This allows for resizing
 *   the table without changing the hash operation.
 * @param[in]  cmp_key_func        A callback for comparing two keys.
 *   Leave it NULL if no callback is needed and the default is to be used.
 *   For HASH_CUSTOM, a callback must be provided.
 *
 * This hashtable uses closed addressing.
 * For an open-address hashtable, consider dr_hashtable_create().
 */
void
hashtable_init_ex(hashtable_t *table, uint num_bits, hash_type_t hashtype, bool str_dup,
                  bool synch, void (*free_payload_func)(void *),
                  uint (*hash_key_func)(void *), bool (*cmp_key_func)(void *, void *));

/** Configures optional parameters of hashtable operation. */
void
hashtable_configure(hashtable_t *table, hashtable_config_t *config);

/** Returns the payload for the given key, or NULL if the key is not found */
void *
hashtable_lookup(hashtable_t *table, void *key);

/**
 * Adds a new entry.  Returns false if an entry for \p key already exists.
 * \note Never use NULL as a payload as that is used for a lookup failure.
 */
bool
hashtable_add(hashtable_t *table, void *key, void *payload);

/**
 * Adds a new entry, replacing an existing entry if any.
 * Returns the old payload, or NULL if there was no existing entry.
 * \note Never use NULL as a payload as that is used for a lookup failure.
 */
void *
hashtable_add_replace(hashtable_t *table, void *key, void *payload);

/**
 * Removes the entry for key.  If free_payload_func was specified calls it
 * for the payload being removed.  Returns false if no such entry
 * exists.
 */
bool
hashtable_remove(hashtable_t *table, void *key);

/**
 * Removes all entries with key in [start..end).  If free_payload_func
 * was specified calls it for each payload being removed.  Returns
 * false if no such entry exists.
 */
bool
hashtable_remove_range(hashtable_t *table, void *start, void *end);

/**
 * Calls the \p apply_func for each payload.
 * @param table The hashtable to apply the function.
 * @param apply_func A pointer to a function that is called for all payloads
 * stored in the map.
 */
void
hashtable_apply_to_all_payloads(hashtable_t *table, void (*apply_func)(void *payload));

/**
 * Calls the \p apply_func for each payload with user data. Similar to
 * hashtable_apply_to_all_payloads().
 * @param table The hashtable to apply the function.
 * @param apply_func A pointer to a function that is called for all payloads
 * stored in the map. It also takes user data as a parameter.
 * @param user_data User data that is available when iterating through payloads.
 */
void
hashtable_apply_to_all_payloads_user_data(hashtable_t *table,
                                          void (*apply_func)(void *payload,
                                                             void *user_data),
                                          void *user_data);

/**
 * Removes all entries from the table.  If free_payload_func was specified
 * calls it for each payload.
 */
void
hashtable_clear(hashtable_t *table);

/**
 * Destroys all storage for the table, including all entries and the
 * table itself.  If free_payload_func was specified calls it for each
 * payload.
 */
void
hashtable_delete(hashtable_t *table);

/** Acquires the hashtable lock. */
void
hashtable_lock(hashtable_t *table);

/** Releases the hashtable lock. */
void
hashtable_unlock(hashtable_t *table);

/**
 * Returns true iff the hashtable lock is owned by the calling thread.
 * This routine is only available in debug builds.
 * In release builds it always returns true.
 */
bool
hashtable_lock_self_owns(hashtable_t *table);

/* DR_API EXPORT BEGIN */
/** Flags to control hashtable persistence */
typedef enum {
    /**
     * Valid for hashtable_persist() and hashtable_resurrect() and the
     * same value must be passed to both.  Treats payloads as pointers
     * to allocated memory.  By default payloads are treated as
     * inlined values if this flag is not set.
     */
    DR_HASHPERS_PAYLOAD_IS_POINTER = 0x0001,
    /**
     * Valid for hashtable_resurrect().  Only applies if
     * DR_HASHPERS_KEY_IS_POINTER.  Performs a shallow clone of the
     * payload upon resurrection.  If this flag is not set, the
     * payloads will remain pointing into the mapped file.
     */
    DR_HASHPERS_CLONE_PAYLOAD = 0x0002,
    /**
     * Valid for hashtable_persist_size(), hashtable_persist(), and
     * hashtable_resurrect(), and the same value must be passed to all.
     * Only applies if keys are of type HASH_INTPTR.  Adjusts each key by
     * the difference in the persist-time start address of the persisted
     * code region and the resurrected start address.  The value of this
     * flag must match across all three calls hashtable_persist_size(),
     * hashtable_persist(), and hashtable_resurrect().
     */
    DR_HASHPERS_REBASE_KEY = 0x0004,
    /**
     * Valid for hashtable_persist_size() and hashtable_persist() and
     * the same value must be passed to both.  Only applies if keys
     * are of type HASH_INTPTR.  Only persists entries whose key is
     * in the address range being persisted.
     */
    DR_HASHPERS_ONLY_IN_RANGE = 0x0008,
    /**
     * Valid for hashtable_persist_size() and hashtable_persist() and
     * the same value must be passed to both.  Only applies if keys
     * are of type HASH_INTPTR.  Only persists entries for which
     * dr_fragment_persistable() returns true.
     */
    DR_HASHPERS_ONLY_PERSISTED = 0x0010,
} hasthable_persist_flags_t;
/* DR_API EXPORT END */

/**
 * For use persisting a table of single-alloc entries (i.e., via a
 * shallow copy) for loading into a live table later.
 *
 * These routines assume that the caller is synchronizing across the
 * call to hashtable_persist_size() and hashtable_persist().  If these
 * are called using DR's persistence interface, DR guarantees
 * synchronization.
 *
 * @param[in] drcontext   The opaque DR context
 * @param[in] table       The table to persist
 * @param[in] entry_size  The size of each table entry payload
 * @param[in] perscxt     The opaque persistence context from DR's persist events
 * @param[in] flags       Controls various aspects of the persistence
 */
size_t
hashtable_persist_size(void *drcontext, hashtable_t *table, size_t entry_size,
                       void *perscxt, hasthable_persist_flags_t flags);

/**
 * For use persisting a table of single-alloc entries (i.e., via a
 * shallow copy) for loading into a live table later.
 *
 * These routines assume that the caller is synchronizing across the
 * call to hashtable_persist_size() and hashtable_persist().  If these
 * are called using DR's persistence interface, DR guarantees
 * synchronization.
 *
 * hashtable_persist_size() must be called immediately prior to
 * calling hashtable_persist().
 *
 * @param[in] drcontext   The opaque DR context
 * @param[in] table       The table to persist
 * @param[in] entry_size  The size of each table entry payload
 * @param[in] fd          The target persisted file handle
 * @param[in] perscxt     The opaque persistence context from DR's persist events
 * @param[in] flags       Controls various aspects of the persistence
 */
bool
hashtable_persist(void *drcontext, hashtable_t *table, size_t entry_size, file_t fd,
                  void *perscxt, hasthable_persist_flags_t flags);

/**
 * For use persisting a table of single-alloc entries (i.e., via a
 * shallow copy) for loading into a live table later.
 *
 * Reads in entries from disk and adds them to the live table.
 *
 * @param[in] drcontext   The opaque DR context
 * @param[in] map         The mapped-in persisted file, pointing at the
 *   data written by hashtable_persist()
 * @param[in] table       The live table to add to
 * @param[in] entry_size  The size of each table entry payload
 * @param[in] perscxt     The opaque persistence context from DR's persist events
 * @param[in] flags       Controls various aspects of the persistence
 * @param[in] process_payload  If non-NULL, calls process_payload instead of
 *   hashtable_add.  process_payload can then adjust the paylod and if
 *   it wishes invoke hashtable_add.
 */
bool
hashtable_resurrect(void *drcontext, byte **map /*INOUT*/, hashtable_t *table,
                    size_t entry_size, void *perscxt, hasthable_persist_flags_t flags,
                    bool (*process_payload)(void *key, void *payload, ptr_int_t shift));

/**@}*/ /* end doxygen group */

#ifdef __cplusplus
}
#endif

#endif /* _HASHTABLE_H_ */