/*******************************************************************************
* This file is part of SWIFT.
* Copyright (c) 2019 James Willis (james.s.willis@durham.ac.uk)
* Pedro Gonnet (pedro.gonnet@gmail.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see .
*
******************************************************************************/
/*
* Generic hashmap manipulation functions
*
* Originally by Elliot C Back -
* http://elliottback.com/wp/hashmap-implementation-in-c/
*
* Modified by Pete Warden to fix a serious performance problem, support strings
* as keys and removed thread synchronization - http://petewarden.typepad.com
*/
#ifndef SWIFT_HASHMAP_H
#define SWIFT_HASHMAP_H
/* Some standard headers. */
#include
#include
/* Local headers. */
#include "align.h"
// Type used for chunk bitmasks.
typedef size_t hashmap_mask_t;
#define HASHMAP_BITS_PER_MASK ((int)sizeof(hashmap_mask_t) * 8)
// Type used for the hashmap keys (must have a valid '==' operation).
#ifndef hashmap_key_t
#define hashmap_key_t size_t
#endif
// Type used for the hashmap values (must have a valid '==' operation).
#ifndef hashmap_value_t
typedef struct _hashmap_struct {
long long value_st;
long long value_ll;
float value_flt;
double value_dbl;
double value_array_dbl[3];
double value_array2_dbl[3];
} hashmap_struct_t;
#define hashmap_value_t hashmap_struct_t
#endif
/* We need to keep keys and values */
typedef struct _hashmap_element {
hashmap_key_t key;
hashmap_value_t value;
} hashmap_element_t;
/* Make sure a chunk fits in a given size. */
#define HASHMAP_TARGET_CHUNK_BYTES (80 * 1024)
#define HASHMAP_BITS_PER_ELEMENT ((int)sizeof(hashmap_element_t) * 8 + 1)
#define HASHMAP_ELEMENTS_PER_CHUNK \
((HASHMAP_TARGET_CHUNK_BYTES * 8) / HASHMAP_BITS_PER_ELEMENT)
#define HASHMAP_MASKS_PER_CHUNK \
((HASHMAP_ELEMENTS_PER_CHUNK + HASHMAP_BITS_PER_MASK - 1) / \
HASHMAP_BITS_PER_MASK)
#define HASHMAP_ALLOCS_INITIAL_SIZE (80 * 1024)
#define HASHMAP_ALLOC_SIZE_FRACTION (0.1)
#define HASHMAP_MAX_CHAIN_LENGTH (HASHMAP_ELEMENTS_PER_CHUNK / 8)
#ifndef HASHMAP_DEBUG_OUTPUT
#define HASHMAP_DEBUG_OUTPUT (0)
#endif // HASHMAP_DEBUG_OUTPUT
/* A chunk of hashmap_element, with the corresponding bitmask. */
typedef struct _hashmap_chunk {
union {
hashmap_mask_t masks[HASHMAP_MASKS_PER_CHUNK];
void *next;
};
hashmap_element_t data[HASHMAP_ELEMENTS_PER_CHUNK];
} SWIFT_STRUCT_ALIGN hashmap_chunk_t;
/* A hashmap has some maximum size and current size,
* as well as the data to hold. */
typedef struct _hashmap {
size_t table_size;
size_t size;
size_t nr_chunks;
hashmap_chunk_t *
*chunks; // Pointer to chunks in use, but not densely populated.
hashmap_chunk_t
*graveyard; // Pointer to allocated, but currently unused chunks.
void **allocs; // Pointers to allocated blocks of chunks.
size_t allocs_size; // Size of the allocs array.
size_t allocs_count; // Number of elements in the allocs array.
#if HASHMAP_DEBUG_OUTPUT
/* Chain lengths, used for debugging only. */
size_t chain_length_counts[HASHMAP_MAX_CHAIN_LENGTH];
#endif
} hashmap_t;
/**
* Pointer to a function that can take a key, a pointer to a value, and a
* void pointer extra data payload.
*/
typedef void (*hashmap_mapper_t)(hashmap_key_t, hashmap_value_t *, void *);
/**
* @brief Initialize a hashmap.
*/
void hashmap_init(hashmap_t *m);
/**
* @brief Re-size the hashmap.
*
* Note that the hashmap size does not necessarily correspond to its
* capacity, since it will grow if too many collisions occur. As a rule
* of thumb, allocate twice as many elements as you think you will need.
*
* @param m The hasmmap to grow.
* @param new_size New table size. If zero, the current size will be increase
* by a fixed rate.
*/
void hashmap_grow(hashmap_t *m, size_t new_size);
/**
* @brief Add a key/value pair to the hashmap, overwriting whatever was
* previously there.
*/
extern void hashmap_put(hashmap_t *m, hashmap_key_t key, hashmap_value_t value);
/**
* @brief Get the value for a given key. If no value exists a new one will be
* created.
*
* Note that the returned pointer is volatile and will be invalidated if the
* hashmap is re-hashed!
*/
extern hashmap_value_t *hashmap_get(hashmap_t *m, hashmap_key_t key);
/**
* @brief Get the value for a given key. If no value exists a new one will be
* created. Return a flag indicating whether a new element has been added.
*
* Note that the returned pointer is volatile and will be invalidated if the
* hashmap is re-hashed!
*/
extern hashmap_value_t *hashmap_get_new(hashmap_t *m, hashmap_key_t key,
int *created_new_element);
/**
* @brief Look for the given key and return a pointer to its value or NULL if
* it is not in the hashmap.
*
* Note that the returned pointer is volatile and will be invalidated if the
* hashmap is re-hashed!
*/
extern hashmap_value_t *hashmap_lookup(hashmap_t *m, hashmap_key_t key);
/**
* @brief Iterate the function parameter over each element in the hashmap.
*
* The function `f` takes three arguments, the first and second are the element
* key and a pointer to the correspondig value, respectively, while the third
* is the `void *data` argument.
*/
extern void hashmap_iterate(hashmap_t *m, hashmap_mapper_t f, void *data);
/**
* @brief De-allocate memory associated with this hashmap, clears all the
* entries.
*
* After a call to `hashmap_free`, the hashmap cna be re-initialized with
* `hashmap_init`.
*/
extern void hashmap_free(hashmap_t *m);
/**
* Get the current size of a hashmap
*/
extern size_t hashmap_size(hashmap_t *m);
/**
* @brief Print all sorts of stats on the given hashmap.
*/
void hashmap_print_stats(hashmap_t *m);
#endif /* SWIFT_HASHMAP_H */