src/cx/map.h

Wed, 08 Jan 2025 20:06:37 +0100

author
Mike Becker <universe@uap-core.de>
date
Wed, 08 Jan 2025 20:06:37 +0100
changeset 1115
6db21dee4929
parent 1114
ad5eeb256242
permissions
-rw-r--r--

create specialized map iterators - fixes #550

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. 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.
 *
 * 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 THE COPYRIGHT HOLDER 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.
 */
/**
 * @file map.h
 * @brief Interface for map implementations.
 * @author Mike Becker
 * @author Olaf Wintermann
 * @copyright 2-Clause BSD License
 */

#ifndef UCX_MAP_H
#define UCX_MAP_H

#include "common.h"
#include "collection.h"
#include "string.h"
#include "hash_key.h"

#ifdef    __cplusplus
extern "C" {
#endif

/** Type for the UCX map. */
typedef struct cx_map_s CxMap;

/** Type for a map entry. */
typedef struct cx_map_entry_s CxMapEntry;

/** Type for a map iterator. */
typedef struct cx_map_iterator_s CxMapIterator;

/** Type for map class definitions. */
typedef struct cx_map_class_s cx_map_class;

/** Structure for the UCX map. */
struct cx_map_s {
    /**
     * Base attributes.
     */
    CX_COLLECTION_BASE;
    /** The map class definition. */
    cx_map_class *cl;
};

/**
 * A map entry.
 */
struct cx_map_entry_s {
    /**
     * A pointer to the key.
     */
    const CxHashKey *key;
    /**
     * A pointer to the value.
     */
    void *value;
};

/**
 * The type of iterator for a map.
 */
enum cx_map_iterator_type {
    /**
     * Iterates over key/value pairs.
     */
    CX_MAP_ITERATOR_PAIRS,
    /**
     * Iterates over keys only.
     */
    CX_MAP_ITERATOR_KEYS,
    /**
     * Iterates over values only.
     */
    CX_MAP_ITERATOR_VALUES
};

/**
 * Internal iterator struct - use CxMapIterator.
 */
struct cx_map_iterator_s {
    /**
     * Inherited common data for all iterators.
     */
    CX_ITERATOR_BASE;

    /**
     * Handle for the source map.
     */
    union {
        /**
         * Access for mutating iterators.
         */
        CxMap *m;
        /**
         * Access for normal iterators.
         */
        const CxMap *c;
    } map;

    /**
     * Handle for the current element.
     *
     * @attention Depends on the map implementation, do not assume a type (better: do not use!).
     */
    void *elem;

    /**
     * Reserved memory for a map entry.
     *
     * If a map implementation uses an incompatible layout, the iterator needs something
     * to point to during iteration which @em is compatible.
     */
    CxMapEntry entry;

    /**
     * Field for storing the current slot number.
     *
     * (Used internally)
     */
    size_t slot;

    /**
     * Counts the elements successfully.
     * It usually does not denote a stable index within the map as it would be for arrays.
     */
    size_t index;

    /**
     * The size of a value stored in this map.
     */
    size_t elem_size;

    /**
     * May contain the total number of elements, if known.
     * Set to @c SIZE_MAX when the total number is unknown during iteration.
     *
     * @remark The UCX implementations of #CxMap always know the number of elements they store.
     */
    size_t elem_count;

    /**
     * The type of this iterator.
     */
    enum cx_map_iterator_type type;
};

/**
 * The class definition for arbitrary maps.
 */
struct cx_map_class_s {
    /**
     * Deallocates the entire memory.
     */
    void (*deallocate)(struct cx_map_s *map);

    /**
     * Removes all elements.
     */
    void (*clear)(struct cx_map_s *map);

    /**
     * Add or overwrite an element.
     */
    int (*put)(
            CxMap *map,
            CxHashKey key,
            void *value
    );

    /**
     * Returns an element.
     */
    void *(*get)(
            const CxMap *map,
            CxHashKey key
    );

    /**
     * Removes an element.
     *
     * Implementations SHALL check if @p targetbuf is set and copy the elements
     * to the buffer without invoking any destructor.
     * When @p targetbuf is not set, the destructors SHALL be invoked.
     *
     * The function SHALL return zero when the @p key was found and
     * non-zero, otherwise. 
     */
    int (*remove)(
            CxMap *map,
            CxHashKey key,
            void *targetbuf
    );

    /**
     * Creates an iterator for this map.
     */
    CxMapIterator (*iterator)(const CxMap *map, enum cx_map_iterator_type type);
};

/**
 * A shared instance of an empty map.
 *
 * Writing to that map is not allowed.
 *
 * You can use this is a placeholder for initializing CxMap pointers
 * for which you do not want to reserve memory right from the beginning.
 */
extern CxMap *const cxEmptyMap;

/**
 * Deallocates the memory of the specified map.
 *
 * Also calls the content destructor functions for each element, if specified.
 *
 * @param map the map to be freed
 */
void cxMapFree(CxMap *map);


/**
 * Clears a map by removing all elements.
 *
 * Also calls the content destructor functions for each element, if specified.
 *
 * @param map the map to be cleared
 */
cx_attr_nonnull
static inline void cxMapClear(CxMap *map) {
    map->cl->clear(map);
}

/**
 * Returns the number of elements in this map.
 *
 * @param map the map
 * @return the number of stored elements
 */
cx_attr_nonnull
static inline size_t cxMapSize(const CxMap *map) {
    return map->collection.size;
}

/**
 * Creates a value iterator for a map.
 *
 * When the map is storing pointers, those pointers are returned.
 * Otherwise, the iterator iterates over pointers to the memory within the map where the
 * respective elements are stored.
 *
 * @note An iterator iterates over all elements successively. Therefore, the order
 * highly depends on the map implementation and may change arbitrarily when the contents change.
 *
 * @param map the map to create the iterator for
 * @return an iterator for the currently stored values
 */
cx_attr_nonnull
cx_attr_nodiscard
static inline CxMapIterator cxMapIteratorValues(const CxMap *map) {
    return map->cl->iterator(map, CX_MAP_ITERATOR_VALUES);
}

/**
 * Creates a key iterator for a map.
 *
 * The elements of the iterator are keys of type CxHashKey and the pointer returned
 * during iterator shall be treated as @c const @c CxHashKey* .
 *
 * @note An iterator iterates over all elements successively. Therefore, the order
 * highly depends on the map implementation and may change arbitrarily when the contents change.
 *
 * @param map the map to create the iterator for
 * @return an iterator for the currently stored keys
 */
cx_attr_nonnull
cx_attr_nodiscard
static inline CxMapIterator cxMapIteratorKeys(const CxMap *map) {
    return map->cl->iterator(map, CX_MAP_ITERATOR_KEYS);
}

/**
 * Creates an iterator for a map.
 *
 * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned
 * during iterator shall be treated as @c const @c CxMapEntry* .
 *
 * @note An iterator iterates over all elements successively. Therefore, the order
 * highly depends on the map implementation and may change arbitrarily when the contents change.
 *
 * @param map the map to create the iterator for
 * @return an iterator for the currently stored entries
 * @see cxMapIteratorKeys()
 * @see cxMapIteratorValues()
 */
cx_attr_nonnull
cx_attr_nodiscard
static inline CxMapIterator cxMapIterator(const CxMap *map) {
    return map->cl->iterator(map, CX_MAP_ITERATOR_PAIRS);
}


/**
 * Creates a mutating iterator over the values of a map.
 *
 * When the map is storing pointers, those pointers are returned.
 * Otherwise, the iterator iterates over pointers to the memory within the map where the
 * respective elements are stored.
 *
 * @note An iterator iterates over all elements successively. Therefore, the order
 * highly depends on the map implementation and may change arbitrarily when the contents change.
 *
 * @param map the map to create the iterator for
 * @return an iterator for the currently stored values
 */
cx_attr_nonnull
cx_attr_nodiscard
CxMapIterator cxMapMutIteratorValues(CxMap *map);

/**
 * Creates a mutating iterator over the keys of a map.
 *
 * The elements of the iterator are keys of type CxHashKey and the pointer returned
 * during iterator shall be treated as @c const @c CxHashKey* .
 *
 * @note An iterator iterates over all elements successively. Therefore, the order
 * highly depends on the map implementation and may change arbitrarily when the contents change.
 *
 * @param map the map to create the iterator for
 * @return an iterator for the currently stored keys
 */
cx_attr_nonnull
cx_attr_nodiscard
CxMapIterator cxMapMutIteratorKeys(CxMap *map);

/**
 * Creates a mutating iterator for a map.
 *
 * The elements of the iterator are key/value pairs of type CxMapEntry and the pointer returned
 * during iterator shall be treated as @c const @c CxMapEntry* .
 *
 * @note An iterator iterates over all elements successively. Therefore, the order
 * highly depends on the map implementation and may change arbitrarily when the contents change.
 *
 * @param map the map to create the iterator for
 * @return an iterator for the currently stored entries
 * @see cxMapMutIteratorKeys()
 * @see cxMapMutIteratorValues()
 */
cx_attr_nonnull
cx_attr_nodiscard
CxMapIterator cxMapMutIterator(CxMap *map);

#ifdef __cplusplus
} // end the extern "C" block here, because we want to start overloading
cx_attr_nonnull
static inline int cxMapPut(
        CxMap *map,
        CxHashKey const &key,
        void *value
) {
    return map->cl->put(map, key, value);
}

cx_attr_nonnull
static inline int cxMapPut(
        CxMap *map,
        cxstring const &key,
        void *value
) {
    return map->cl->put(map, cx_hash_key_cxstr(key), value);
}

cx_attr_nonnull
static inline int cxMapPut(
        CxMap *map,
        cxmutstr const &key,
        void *value
) {
    return map->cl->put(map, cx_hash_key_cxstr(key), value);
}

cx_attr_nonnull
cx_attr_cstr_arg(2)
static inline int cxMapPut(
        CxMap *map,
        const char *key,
        void *value
) {
    return map->cl->put(map, cx_hash_key_str(key), value);
}

cx_attr_nonnull
cx_attr_nodiscard
static inline void *cxMapGet(
        const CxMap *map,
        CxHashKey const &key
) {
    return map->cl->get(map, key);
}

cx_attr_nonnull
cx_attr_nodiscard
static inline void *cxMapGet(
        const CxMap *map,
        cxstring const &key
) {
    return map->cl->get(map, cx_hash_key_cxstr(key));
}

cx_attr_nonnull
cx_attr_nodiscard
static inline void *cxMapGet(
        const CxMap *map,
        cxmutstr const &key
) {
    return map->cl->get(map, cx_hash_key_cxstr(key));
}

cx_attr_nonnull
cx_attr_nodiscard
cx_attr_cstr_arg(2)
static inline void *cxMapGet(
        const CxMap *map,
        const char *key
) {
    return map->cl->get(map, cx_hash_key_str(key));
}

cx_attr_nonnull
static inline int cxMapRemove(
        CxMap *map,
        CxHashKey const &key
) {
    return map->cl->remove(map, key, nullptr);
}

cx_attr_nonnull
static inline int cxMapRemove(
        CxMap *map,
        cxstring const &key
) {
    return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr);
}

cx_attr_nonnull
static inline int cxMapRemove(
        CxMap *map,
        cxmutstr const &key
) {
    return map->cl->remove(map, cx_hash_key_cxstr(key), nullptr);
}

cx_attr_nonnull
cx_attr_cstr_arg(2)
static inline int cxMapRemove(
        CxMap *map,
        const char *key
) {
    return map->cl->remove(map, cx_hash_key_str(key), nullptr);
}

cx_attr_nonnull
cx_attr_access_w(3)
static inline int cxMapRemoveAndGet(
        CxMap *map,
        CxHashKey key,
        void *targetbuf
) {
    return map->cl->remove(map, key, targetbuf);
}

cx_attr_nonnull
cx_attr_access_w(3)
static inline int cxMapRemoveAndGet(
        CxMap *map,
        cxstring key,
        void *targetbuf
) {
    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
}

cx_attr_nonnull
cx_attr_access_w(3)
static inline int cxMapRemoveAndGet(
        CxMap *map,
        cxmutstr key,
        void *targetbuf
) {
    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
}

cx_attr_nonnull
cx_attr_access_w(3)
cx_attr_cstr_arg(2)
static inline int cxMapRemoveAndGet(
        CxMap *map,
        const char *key,
        void *targetbuf
) {
    return map->cl->remove(map, cx_hash_key_str(key), targetbuf);
}

#else // __cplusplus

/**
 * @copydoc cxMapPut()
 */
cx_attr_nonnull
static inline int cx_map_put(
        CxMap *map,
        CxHashKey key,
        void *value
) {
    return map->cl->put(map, key, value);
}

/**
 * @copydoc cxMapPut()
 */
cx_attr_nonnull
static inline int cx_map_put_cxstr(
        CxMap *map,
        cxstring key,
        void *value
) {
    return map->cl->put(map, cx_hash_key_cxstr(key), value);
}

/**
 * @copydoc cxMapPut()
 */
cx_attr_nonnull
static inline int cx_map_put_mustr(
        CxMap *map,
        cxmutstr key,
        void *value
) {
    return map->cl->put(map, cx_hash_key_cxstr(key), value);
}

/**
 * @copydoc cxMapPut()
 */
cx_attr_nonnull
cx_attr_cstr_arg(2)
static inline int cx_map_put_str(
        CxMap *map,
        const char *key,
        void *value
) {
    return map->cl->put(map, cx_hash_key_str(key), value);
}

/**
 * Puts a key/value-pair into the map.
 *
 * A possible existing value will be overwritten.
 * If destructor functions are specified, they are called for
 * the overwritten element.
 *
 * If this map is storing pointers, the @p value pointer is written
 * to the map. Otherwise, the memory is copied from @p value with
 * memcpy().
 *
 * The @p key is always copied.
 *
 * @param map (@c CxMap*) the map
 * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
 * @param value (@c void*) the value
 * @retval zero success
 * @retval non-zero value on memory allocation failure
 */
#define cxMapPut(map, key, value) _Generic((key), \
    CxHashKey: cx_map_put,                        \
    cxstring: cx_map_put_cxstr,                   \
    cxmutstr: cx_map_put_mustr,                   \
    char*: cx_map_put_str,                        \
    const char*: cx_map_put_str)                  \
    (map, key, value)

/**
 * @copydoc cxMapGet()
 */
cx_attr_nonnull
cx_attr_nodiscard
static inline void *cx_map_get(
        const CxMap *map,
        CxHashKey key
) {
    return map->cl->get(map, key);
}

/**
 * @copydoc cxMapGet()
 */
cx_attr_nonnull
cx_attr_nodiscard
static inline void *cx_map_get_cxstr(
        const CxMap *map,
        cxstring key
) {
    return map->cl->get(map, cx_hash_key_cxstr(key));
}

/**
 * @copydoc cxMapGet()
 */
cx_attr_nonnull
cx_attr_nodiscard
static inline void *cx_map_get_mustr(
        const CxMap *map,
        cxmutstr key
) {
    return map->cl->get(map, cx_hash_key_cxstr(key));
}

/**
 * @copydoc cxMapGet()
 */
cx_attr_nonnull
cx_attr_nodiscard
cx_attr_cstr_arg(2)
static inline void *cx_map_get_str(
        const CxMap *map,
        const char *key
) {
    return map->cl->get(map, cx_hash_key_str(key));
}

/**
 * Retrieves a value by using a key.
 *
 * If this map is storing pointers, the stored pointer is returned.
 * Otherwise, a pointer to the element within the map's memory
 * is returned (which is valid as long as the element stays in the map).
 *
 * @param map (@c CxMap*) the map
 * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
 * @return (@c void*) the value
 */
#define cxMapGet(map, key) _Generic((key), \
    CxHashKey: cx_map_get,                 \
    cxstring: cx_map_get_cxstr,            \
    cxmutstr: cx_map_get_mustr,            \
    char*: cx_map_get_str,                 \
    const char*: cx_map_get_str)           \
    (map, key)

/**
 * @copydoc cxMapRemove()
 */
cx_attr_nonnull
static inline int cx_map_remove(
        CxMap *map,
        CxHashKey key
) {
    return map->cl->remove(map, key, NULL);
}

/**
 * @copydoc cxMapRemove()
 */
cx_attr_nonnull
static inline int cx_map_remove_cxstr(
        CxMap *map,
        cxstring key
) {
    return map->cl->remove(map, cx_hash_key_cxstr(key), NULL);
}

/**
 * @copydoc cxMapRemove()
 */
cx_attr_nonnull
static inline int cx_map_remove_mustr(
        CxMap *map,
        cxmutstr key
) {
    return map->cl->remove(map, cx_hash_key_cxstr(key), NULL);
}

/**
 * @copydoc cxMapRemove()
 */
cx_attr_nonnull
cx_attr_cstr_arg(2)
static inline int cx_map_remove_str(
        CxMap *map,
        const char *key
) {
    return map->cl->remove(map, cx_hash_key_str(key), NULL);
}

/**
 * Removes a key/value-pair from the map by using the key.
 *
 * Always invokes the destructors functions, if any, on the removed element.
 *
 * @param map (@c CxMap*) the map
 * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
 * @retval zero success
 * @retval non-zero the key was not found
 * 
 * @see cxMapRemoveAndGet()
 */
#define cxMapRemove(map, key) _Generic((key), \
    CxHashKey: cx_map_remove,                 \
    cxstring: cx_map_remove_cxstr,            \
    cxmutstr: cx_map_remove_mustr,            \
    char*: cx_map_remove_str,                 \
    const char*: cx_map_remove_str)           \
    (map, key)

/**
 * @copydoc cxMapRemoveAndGet()
 */
cx_attr_nonnull
cx_attr_access_w(3)
static inline int cx_map_remove_and_get(
        CxMap *map,
        CxHashKey key,
        void *targetbuf
) {
    return map->cl->remove(map, key, targetbuf);
}

/**
 * @copydoc cxMapRemoveAndGet()
 */
cx_attr_nonnull
cx_attr_access_w(3)
static inline int cx_map_remove_and_get_cxstr(
        CxMap *map,
        cxstring key,
        void *targetbuf
) {
    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
}

/**
 * @copydoc cxMapRemoveAndGet()
 */
cx_attr_nonnull
cx_attr_access_w(3)
static inline int cx_map_remove_and_get_mustr(
        CxMap *map,
        cxmutstr key,
        void *targetbuf
) {
    return map->cl->remove(map, cx_hash_key_cxstr(key), targetbuf);
}

/**
 * @copydoc cxMapRemoveAndGet()
 */
cx_attr_nonnull
cx_attr_access_w(3)
cx_attr_cstr_arg(2)
static inline int cx_map_remove_and_get_str(
        CxMap *map,
        const char *key,
        void *targetbuf
) {
    return map->cl->remove(map, cx_hash_key_str(key), targetbuf);
}

/**
 * Removes a key/value-pair from the map by using the key.
 *
 * This function will copy the contents of the removed element
 * to the target buffer, which must be guaranteed to be large enough
 * to hold the element (the map's element size).
 * The destructor functions, if any, will @em not be called.
 *
 * If this map is storing pointers, the element is the pointer itself
 * and not the object it points to.
 *
 * @param map (@c CxMap*) the map
 * @param key (@c CxHashKey, @c char*, @c cxstring, or @c cxmutstr) the key
 * @param targetbuf (@c void*) the buffer where the element shall be copied to
 * @retval zero success
 * @retval non-zero the key was not found
 *
 * @see cxMapRemove()
 */
#define cxMapRemoveAndGet(map, key, targetbuf) _Generic((key), \
    CxHashKey: cx_map_remove_and_get,               \
    cxstring: cx_map_remove_and_get_cxstr,          \
    cxmutstr: cx_map_remove_and_get_mustr,          \
    char*: cx_map_remove_and_get_str,               \
    const char*: cx_map_remove_and_get_str)         \
    (map, key, targetbuf)

#endif // __cplusplus

#endif // UCX_MAP_H

mercurial